};
 #endif
 
+struct bmc_device
+{
+       struct platform_device *dev;
+       struct ipmi_device_id  id;
+       unsigned char          guid[16];
+       int                    guid_set;
+
+       struct kref            refcount;
+
+       /* bmc device attributes */
+       struct device_attribute device_id_attr;
+       struct device_attribute provides_dev_sdrs_attr;
+       struct device_attribute revision_attr;
+       struct device_attribute firmware_rev_attr;
+       struct device_attribute version_attr;
+       struct device_attribute add_dev_support_attr;
+       struct device_attribute manufacturer_id_attr;
+       struct device_attribute product_id_attr;
+       struct device_attribute guid_attr;
+       struct device_attribute aux_firmware_rev_attr;
+};
+
 #define IPMI_IPMB_NUM_SEQ      64
 #define IPMI_MAX_CHANNELS       16
 struct ipmi_smi
        /* Used for wake ups at startup. */
        wait_queue_head_t waitq;
 
-       /* The IPMI version of the BMC on the other end. */
-       unsigned char       version_major;
-       unsigned char       version_minor;
+       struct bmc_device *bmc;
+       char *my_dev_name;
 
        /* This is the lower-layer's sender routine. */
        struct ipmi_smi_handlers *handlers;
        struct ipmi_proc_entry *proc_entries;
 #endif
 
+       /* Driver-model device for the system interface. */
+       struct device          *si_dev;
+
        /* A table of sequence numbers for this interface.  We use the
            sequence numbers for IPMB messages that go out of the
            interface to match them up with their responses.  A routine
        /* Events that were received with the proper format. */
        unsigned int events;
 };
+#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev)
 
 /* Used to mark an interface entry that cannot be used but is not a
  * free entry, either, primarily used at creation and deletion time so
 #define IPMI_INVALID_INTERFACE(i) (((i) == NULL) \
                                   || (i == IPMI_INVALID_INTERFACE_ENTRY))
 
+/**
+ * The driver model view of the IPMI messaging driver.
+ */
+static struct device_driver ipmidriver = {
+       .name = "ipmi",
+       .bus = &platform_bus_type
+};
+static DEFINE_MUTEX(ipmidriver_mutex);
+
 #define MAX_IPMI_INTERFACES 4
 static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES];
 
                if (IPMI_INVALID_INTERFACE(intf))
                        continue;
                spin_unlock_irqrestore(&interfaces_lock, flags);
-               watcher->new_smi(i);
+               watcher->new_smi(i, intf->si_dev);
                spin_lock_irqsave(&interfaces_lock, flags);
        }
        spin_unlock_irqrestore(&interfaces_lock, flags);
 }
 
 static void
-call_smi_watchers(int i)
+call_smi_watchers(int i, struct device *dev)
 {
        struct ipmi_smi_watcher *w;
 
        down_read(&smi_watchers_sem);
        list_for_each_entry(w, &smi_watchers, link) {
                if (try_module_get(w->owner)) {
-                       w->new_smi(i);
+                       w->new_smi(i, dev);
                        module_put(w->owner);
                }
        }
                      unsigned char *major,
                      unsigned char *minor)
 {
-       *major = user->intf->version_major;
-       *minor = user->intf->version_minor;
+       *major = ipmi_version_major(&user->intf->bmc->id);
+       *minor = ipmi_version_minor(&user->intf->bmc->id);
 }
 
 int ipmi_set_my_address(ipmi_user_t   user,
        ipmi_smi_t intf = data;
 
        return sprintf(out, "%d.%d\n",
-                      intf->version_major, intf->version_minor);
+                      ipmi_version_major(&intf->bmc->id),
+                      ipmi_version_minor(&intf->bmc->id));
 }
 
 static int stat_file_read_proc(char *page, char **start, off_t off,
 #endif /* CONFIG_PROC_FS */
 }
 
+static int __find_bmc_guid(struct device *dev, void *data)
+{
+       unsigned char *id = data;
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+       return memcmp(bmc->guid, id, 16) == 0;
+}
+
+static struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv,
+                                            unsigned char *guid)
+{
+       struct device *dev;
+
+       dev = driver_find_device(drv, NULL, guid, __find_bmc_guid);
+       if (dev)
+               return dev_get_drvdata(dev);
+       else
+               return NULL;
+}
+
+struct prod_dev_id {
+       unsigned int  product_id;
+       unsigned char device_id;
+};
+
+static int __find_bmc_prod_dev_id(struct device *dev, void *data)
+{
+       struct prod_dev_id *id = data;
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+
+       return (bmc->id.product_id == id->product_id
+               && bmc->id.product_id == id->product_id
+               && bmc->id.device_id == id->device_id);
+}
+
+static struct bmc_device *ipmi_find_bmc_prod_dev_id(
+       struct device_driver *drv,
+       unsigned char product_id, unsigned char device_id)
+{
+       struct prod_dev_id id = {
+               .product_id = product_id,
+               .device_id = device_id,
+       };
+       struct device *dev;
+
+       dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id);
+       if (dev)
+               return dev_get_drvdata(dev);
+       else
+               return NULL;
+}
+
+static ssize_t device_id_show(struct device *dev,
+                             struct device_attribute *attr,
+                             char *buf)
+{
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+
+       return snprintf(buf, 10, "%u\n", bmc->id.device_id);
+}
+
+static ssize_t provides_dev_sdrs_show(struct device *dev,
+                                     struct device_attribute *attr,
+                                     char *buf)
+{
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+
+       return snprintf(buf, 10, "%u\n",
+                       bmc->id.device_revision && 0x80 >> 7);
+}
+
+static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+
+       return snprintf(buf, 20, "%u\n",
+                       bmc->id.device_revision && 0x0F);
+}
+
+static ssize_t firmware_rev_show(struct device *dev,
+                                struct device_attribute *attr,
+                                char *buf)
+{
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+
+       return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_revision_1,
+                       bmc->id.firmware_revision_2);
+}
+
+static ssize_t ipmi_version_show(struct device *dev,
+                                struct device_attribute *attr,
+                                char *buf)
+{
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+
+       return snprintf(buf, 20, "%u.%u\n",
+                       ipmi_version_major(&bmc->id),
+                       ipmi_version_minor(&bmc->id));
+}
+
+static ssize_t add_dev_support_show(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+
+       return snprintf(buf, 10, "0x%02x\n",
+                       bmc->id.additional_device_support);
+}
+
+static ssize_t manufacturer_id_show(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+
+       return snprintf(buf, 20, "0x%6.6x\n", bmc->id.manufacturer_id);
+}
+
+static ssize_t product_id_show(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+
+       return snprintf(buf, 10, "0x%4.4x\n", bmc->id.product_id);
+}
+
+static ssize_t aux_firmware_rev_show(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf)
+{
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+
+       return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n",
+                       bmc->id.aux_firmware_revision[3],
+                       bmc->id.aux_firmware_revision[2],
+                       bmc->id.aux_firmware_revision[1],
+                       bmc->id.aux_firmware_revision[0]);
+}
+
+static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct bmc_device *bmc = dev_get_drvdata(dev);
+
+       return snprintf(buf, 100, "%Lx%Lx\n",
+                       (long long) bmc->guid[0],
+                       (long long) bmc->guid[8]);
+}
+
+static void
+cleanup_bmc_device(struct kref *ref)
+{
+       struct bmc_device *bmc;
+
+       bmc = container_of(ref, struct bmc_device, refcount);
+
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->device_id_attr);
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->provides_dev_sdrs_attr);
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->revision_attr);
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->firmware_rev_attr);
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->version_attr);
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->add_dev_support_attr);
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->manufacturer_id_attr);
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->product_id_attr);
+       if (bmc->id.aux_firmware_revision_set)
+               device_remove_file(&bmc->dev->dev,
+                                  &bmc->aux_firmware_rev_attr);
+       if (bmc->guid_set)
+               device_remove_file(&bmc->dev->dev,
+                                  &bmc->guid_attr);
+       platform_device_unregister(bmc->dev);
+       kfree(bmc);
+}
+
+static void ipmi_bmc_unregister(ipmi_smi_t intf)
+{
+       struct bmc_device *bmc = intf->bmc;
+
+       sysfs_remove_link(&intf->si_dev->kobj, "bmc");
+       if (intf->my_dev_name) {
+               sysfs_remove_link(&bmc->dev->dev.kobj, intf->my_dev_name);
+               kfree(intf->my_dev_name);
+               intf->my_dev_name = NULL;
+       }
+
+       mutex_lock(&ipmidriver_mutex);
+       kref_put(&bmc->refcount, cleanup_bmc_device);
+       mutex_unlock(&ipmidriver_mutex);
+}
+
+static int ipmi_bmc_register(ipmi_smi_t intf)
+{
+       int               rv;
+       struct bmc_device *bmc = intf->bmc;
+       struct bmc_device *old_bmc;
+       int               size;
+       char              dummy[1];
+
+       mutex_lock(&ipmidriver_mutex);
+
+       /*
+        * Try to find if there is an bmc_device struct
+        * representing the interfaced BMC already
+        */
+       if (bmc->guid_set)
+               old_bmc = ipmi_find_bmc_guid(&ipmidriver, bmc->guid);
+       else
+               old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver,
+                                                   bmc->id.product_id,
+                                                   bmc->id.device_id);
+
+       /*
+        * If there is already an bmc_device, free the new one,
+        * otherwise register the new BMC device
+        */
+       if (old_bmc) {
+               kfree(bmc);
+               intf->bmc = old_bmc;
+               bmc = old_bmc;
+
+               kref_get(&bmc->refcount);
+               mutex_unlock(&ipmidriver_mutex);
+
+               printk(KERN_INFO
+                      "ipmi: interfacing existing BMC (man_id: 0x%6.6x,"
+                      " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
+                      bmc->id.manufacturer_id,
+                      bmc->id.product_id,
+                      bmc->id.device_id);
+       } else {
+               bmc->dev = platform_device_alloc("ipmi_bmc",
+                                                bmc->id.device_id);
+               if (! bmc->dev) {
+                       printk(KERN_ERR
+                              "ipmi_msghandler:"
+                              " Unable to allocate platform device\n");
+                       return -ENOMEM;
+               }
+               bmc->dev->dev.driver = &ipmidriver;
+               dev_set_drvdata(&bmc->dev->dev, bmc);
+               kref_init(&bmc->refcount);
+
+               rv = platform_device_register(bmc->dev);
+               mutex_unlock(&ipmidriver_mutex);
+               if (rv) {
+                       printk(KERN_ERR
+                              "ipmi_msghandler:"
+                              " Unable to register bmc device: %d\n",
+                              rv);
+                       /* Don't go to out_err, you can only do that if
+                          the device is registered already. */
+                       return rv;
+               }
+
+               bmc->device_id_attr.attr.name = "device_id";
+               bmc->device_id_attr.attr.owner = THIS_MODULE;
+               bmc->device_id_attr.attr.mode = S_IRUGO;
+               bmc->device_id_attr.show = device_id_show;
+
+               bmc->provides_dev_sdrs_attr.attr.name = "provides_device_sdrs";
+               bmc->provides_dev_sdrs_attr.attr.owner = THIS_MODULE;
+               bmc->provides_dev_sdrs_attr.attr.mode = S_IRUGO;
+               bmc->provides_dev_sdrs_attr.show = provides_dev_sdrs_show;
+
+
+               bmc->revision_attr.attr.name = "revision";
+               bmc->revision_attr.attr.owner = THIS_MODULE;
+               bmc->revision_attr.attr.mode = S_IRUGO;
+               bmc->revision_attr.show = revision_show;
+
+               bmc->firmware_rev_attr.attr.name = "firmware_revision";
+               bmc->firmware_rev_attr.attr.owner = THIS_MODULE;
+               bmc->firmware_rev_attr.attr.mode = S_IRUGO;
+               bmc->firmware_rev_attr.show = firmware_rev_show;
+
+               bmc->version_attr.attr.name = "ipmi_version";
+               bmc->version_attr.attr.owner = THIS_MODULE;
+               bmc->version_attr.attr.mode = S_IRUGO;
+               bmc->version_attr.show = ipmi_version_show;
+
+               bmc->add_dev_support_attr.attr.name
+                       = "additional_device_support";
+               bmc->add_dev_support_attr.attr.owner = THIS_MODULE;
+               bmc->add_dev_support_attr.attr.mode = S_IRUGO;
+               bmc->add_dev_support_attr.show = add_dev_support_show;
+
+               bmc->manufacturer_id_attr.attr.name = "manufacturer_id";
+               bmc->manufacturer_id_attr.attr.owner = THIS_MODULE;
+               bmc->manufacturer_id_attr.attr.mode = S_IRUGO;
+               bmc->manufacturer_id_attr.show = manufacturer_id_show;
+
+               bmc->product_id_attr.attr.name = "product_id";
+               bmc->product_id_attr.attr.owner = THIS_MODULE;
+               bmc->product_id_attr.attr.mode = S_IRUGO;
+               bmc->product_id_attr.show = product_id_show;
+
+               bmc->guid_attr.attr.name = "guid";
+               bmc->guid_attr.attr.owner = THIS_MODULE;
+               bmc->guid_attr.attr.mode = S_IRUGO;
+               bmc->guid_attr.show = guid_show;
+
+               bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision";
+               bmc->aux_firmware_rev_attr.attr.owner = THIS_MODULE;
+               bmc->aux_firmware_rev_attr.attr.mode = S_IRUGO;
+               bmc->aux_firmware_rev_attr.show = aux_firmware_rev_show;
+
+               device_create_file(&bmc->dev->dev,
+                                  &bmc->device_id_attr);
+               device_create_file(&bmc->dev->dev,
+                                  &bmc->provides_dev_sdrs_attr);
+               device_create_file(&bmc->dev->dev,
+                                  &bmc->revision_attr);
+               device_create_file(&bmc->dev->dev,
+                                  &bmc->firmware_rev_attr);
+               device_create_file(&bmc->dev->dev,
+                                  &bmc->version_attr);
+               device_create_file(&bmc->dev->dev,
+                                  &bmc->add_dev_support_attr);
+               device_create_file(&bmc->dev->dev,
+                                  &bmc->manufacturer_id_attr);
+               device_create_file(&bmc->dev->dev,
+                                  &bmc->product_id_attr);
+               if (bmc->id.aux_firmware_revision_set)
+                       device_create_file(&bmc->dev->dev,
+                                          &bmc->aux_firmware_rev_attr);
+               if (bmc->guid_set)
+                       device_create_file(&bmc->dev->dev,
+                                          &bmc->guid_attr);
+
+               printk(KERN_INFO
+                      "ipmi: Found new BMC (man_id: 0x%6.6x, "
+                      " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
+                      bmc->id.manufacturer_id,
+                      bmc->id.product_id,
+                      bmc->id.device_id);
+       }
+
+       /*
+        * create symlink from system interface device to bmc device
+        * and back.
+        */
+       rv = sysfs_create_link(&intf->si_dev->kobj,
+                              &bmc->dev->dev.kobj, "bmc");
+       if (rv) {
+               printk(KERN_ERR
+                      "ipmi_msghandler: Unable to create bmc symlink: %d\n",
+                      rv);
+               goto out_err;
+       }
+
+       size = snprintf(dummy, 0, "ipmi%d", intf->intf_num);
+       intf->my_dev_name = kmalloc(size+1, GFP_KERNEL);
+       if (!intf->my_dev_name) {
+               rv = -ENOMEM;
+               printk(KERN_ERR
+                      "ipmi_msghandler: allocate link from BMC: %d\n",
+                      rv);
+               goto out_err;
+       }
+       snprintf(intf->my_dev_name, size+1, "ipmi%d", intf->intf_num);
+
+       rv = sysfs_create_link(&bmc->dev->dev.kobj, &intf->si_dev->kobj,
+                              intf->my_dev_name);
+       if (rv) {
+               kfree(intf->my_dev_name);
+               intf->my_dev_name = NULL;
+               printk(KERN_ERR
+                      "ipmi_msghandler:"
+                      " Unable to create symlink to bmc: %d\n",
+                      rv);
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       ipmi_bmc_unregister(intf);
+       return rv;
+}
+
+static int
+send_guid_cmd(ipmi_smi_t intf, int chan)
+{
+       struct kernel_ipmi_msg            msg;
+       struct ipmi_system_interface_addr si;
+
+       si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+       si.channel = IPMI_BMC_CHANNEL;
+       si.lun = 0;
+
+       msg.netfn = IPMI_NETFN_APP_REQUEST;
+       msg.cmd = IPMI_GET_DEVICE_GUID_CMD;
+       msg.data = NULL;
+       msg.data_len = 0;
+       return i_ipmi_request(NULL,
+                             intf,
+                             (struct ipmi_addr *) &si,
+                             0,
+                             &msg,
+                             intf,
+                             NULL,
+                             NULL,
+                             0,
+                             intf->channels[0].address,
+                             intf->channels[0].lun,
+                             -1, 0);
+}
+
+static void
+guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
+{
+       if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
+           || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE)
+           || (msg->msg.cmd != IPMI_GET_DEVICE_GUID_CMD))
+               /* Not for me */
+               return;
+
+       if (msg->msg.data[0] != 0) {
+               /* Error from getting the GUID, the BMC doesn't have one. */
+               intf->bmc->guid_set = 0;
+               goto out;
+       }
+
+       if (msg->msg.data_len < 17) {
+               intf->bmc->guid_set = 0;
+               printk(KERN_WARNING PFX
+                      "guid_handler: The GUID response from the BMC was too"
+                      " short, it was %d but should have been 17.  Assuming"
+                      " GUID is not available.\n",
+                      msg->msg.data_len);
+               goto out;
+       }
+
+       memcpy(intf->bmc->guid, msg->msg.data, 16);
+       intf->bmc->guid_set = 1;
+ out:
+       wake_up(&intf->waitq);
+}
+
+static void
+get_guid(ipmi_smi_t intf)
+{
+       int rv;
+
+       intf->bmc->guid_set = 0x2;
+       intf->null_user_handler = guid_handler;
+       rv = send_guid_cmd(intf, 0);
+       if (rv)
+               /* Send failed, no GUID available. */
+               intf->bmc->guid_set = 0;
+       wait_event(intf->waitq, intf->bmc->guid_set != 2);
+       intf->null_user_handler = NULL;
+}
+
 static int
 send_channel_info_cmd(ipmi_smi_t intf, int chan)
 {
 
 int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
                      void                     *send_info,
-                     unsigned char            version_major,
-                     unsigned char            version_minor,
+                     struct ipmi_device_id    *device_id,
+                     struct device            *si_dev,
                      unsigned char            slave_addr,
                      ipmi_smi_t               *new_intf)
 {
        int              rv;
        ipmi_smi_t       intf;
        unsigned long    flags;
+       int              version_major;
+       int              version_minor;
 
+       version_major = ipmi_version_major(device_id);
+       version_minor = ipmi_version_minor(device_id);
 
        /* Make sure the driver is actually initialized, this handles
           problems with initialization order. */
        if (!intf)
                return -ENOMEM;
        memset(intf, 0, sizeof(*intf));
+       intf->bmc = kzalloc(sizeof(*intf->bmc), GFP_KERNEL);
+       if (!intf->bmc) {
+               kfree(intf);
+               return -ENOMEM;
+       }
        intf->intf_num = -1;
        kref_init(&intf->refcount);
-       intf->version_major = version_major;
-       intf->version_minor = version_minor;
+       intf->bmc->id = *device_id;
+       intf->si_dev = si_dev;
        for (j = 0; j < IPMI_MAX_CHANNELS; j++) {
                intf->channels[j].address = IPMI_BMC_SLAVE_ADDR;
                intf->channels[j].lun = 2;
           caller before sending any messages with it. */
        *new_intf = intf;
 
+       get_guid(intf);
+
        if ((version_major > 1)
            || ((version_major == 1) && (version_minor >= 5)))
        {
                /* Wait for the channel info to be read. */
                wait_event(intf->waitq,
                           intf->curr_channel >= IPMI_MAX_CHANNELS);
+               intf->null_user_handler = NULL;
        } else {
                /* Assume a single IPMB channel at zero. */
                intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
        if (rv == 0)
                rv = add_proc_entries(intf, i);
 
+       rv = ipmi_bmc_register(intf);
+
  out:
        if (rv) {
                if (intf->proc_dir)
                spin_lock_irqsave(&interfaces_lock, flags);
                ipmi_interfaces[i] = intf;
                spin_unlock_irqrestore(&interfaces_lock, flags);
-               call_smi_watchers(i);
+               call_smi_watchers(i, intf->si_dev);
        }
 
        return rv;
        struct ipmi_smi_watcher *w;
        unsigned long           flags;
 
+       ipmi_bmc_unregister(intf);
+
        spin_lock_irqsave(&interfaces_lock, flags);
        for (i = 0; i < MAX_IPMI_INTERFACES; i++) {
                if (ipmi_interfaces[i] == intf) {
 static int ipmi_init_msghandler(void)
 {
        int i;
+       int rv;
 
        if (initialized)
                return 0;
 
+       rv = driver_register(&ipmidriver);
+       if (rv) {
+               printk(KERN_ERR PFX "Could not register IPMI driver\n");
+               return rv;
+       }
+
        printk(KERN_INFO "ipmi message handler version "
               IPMI_DRIVER_VERSION "\n");
 
        remove_proc_entry(proc_ipmi_root->name, &proc_root);
 #endif /* CONFIG_PROC_FS */
 
+       driver_unregister(&ipmidriver);
+
        initialized = 0;
 
        /* Check for buffer leaks. */
 
 };
 static char *si_to_str[] = { "KCS", "SMIC", "BT" };
 
-struct ipmi_device_id {
-       unsigned char device_id;
-       unsigned char device_revision;
-       unsigned char firmware_revision_1;
-       unsigned char firmware_revision_2;
-       unsigned char ipmi_version;
-       unsigned char additional_device_support;
-       unsigned char manufacturer_id[3];
-       unsigned char product_id[2];
-       unsigned char aux_firmware_revision[4];
-} __attribute__((packed));
-
-#define ipmi_version_major(v) ((v)->ipmi_version & 0xf)
-#define ipmi_version_minor(v) ((v)->ipmi_version >> 4)
+#define DEVICE_NAME "ipmi_si"
+
+static struct device_driver ipmi_driver =
+{
+       .name = DEVICE_NAME,
+       .bus = &platform_bus_type
+};
 
 struct smi_info
 {
           interrupts. */
        int interrupt_disabled;
 
+       /* From the get device id response... */
        struct ipmi_device_id device_id;
 
+       /* Driver model stuff. */
+       struct device *dev;
+       struct platform_device *pdev;
+
+        /* True if we allocated the device, false if it came from
+         * someplace else (like PCI). */
+       int dev_registered;
+
        /* Slave address, could be reported from DMI. */
        unsigned char slave_addr;
 
 static DECLARE_MUTEX(smi_infos_lock);
 static int smi_num; /* Used to sequence the SMIs */
 
-#define DEVICE_NAME "ipmi_si"
-
 #define DEFAULT_REGSPACING     1
 
 static int           si_trydefaults = 1;
 
                release_region (addr, mapsize);
        }
-       kfree(info);
 }
 
 static int port_setup(struct smi_info *info)
 
                release_mem_region(addr, mapsize);
        }
-       kfree(info);
 }
 
 static int mem_setup(struct smi_info *info)
        if (info->irq)
                info->irq_setup = std_irq_setup;
 
+       info->dev = &pdev->dev;
+
        return try_smi_init(info);
 }
 
 
 static int try_get_dev_id(struct smi_info *smi_info)
 {
-       unsigned char      msg[2];
-       unsigned char      *resp;
-       unsigned long      resp_len;
-       enum si_sm_result smi_result;
-       int               rv = 0;
+       unsigned char         msg[2];
+       unsigned char         *resp;
+       unsigned long         resp_len;
+       enum si_sm_result     smi_result;
+       int                   rv = 0;
 
        resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
        if (!resp)
        /* Otherwise, we got some data. */
        resp_len = smi_info->handlers->get_result(smi_info->si_sm,
                                                  resp, IPMI_MAX_MSG_LENGTH);
-       if (resp_len < 6) {
+       if (resp_len < 14) {
                /* That's odd, it should be longer. */
                rv = -EINVAL;
                goto out;
        }
 
        /* Record info from the get device id, in case we need it. */
-       memcpy(&smi_info->device_id, &resp[3],
-              min_t(unsigned long, resp_len-3, sizeof(smi_info->device_id)));
+       ipmi_demangle_device_id(resp+3, resp_len-3, &smi_info->device_id);
 
  out:
        kfree(resp);
 #define DELL_POWEREDGE_8G_BMC_DEVICE_ID  0x20
 #define DELL_POWEREDGE_8G_BMC_DEVICE_REV 0x80
 #define DELL_POWEREDGE_8G_BMC_IPMI_VERSION 0x51
-#define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00}
+#define DELL_IANA_MFR_ID 0x0002a2
 static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info)
 {
        struct ipmi_device_id *id = &smi_info->device_id;
-       const char mfr[3]=DELL_IANA_MFR_ID;
-       if (!memcmp(mfr, id->manufacturer_id, sizeof(mfr))) {
+       if (id->manufacturer_id == DELL_IANA_MFR_ID) {
                if (id->device_id       == DELL_POWEREDGE_8G_BMC_DEVICE_ID  &&
                    id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV &&
-                   id->ipmi_version    == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) {
+                   id->ipmi_version   == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) {
                        smi_info->oem_data_avail_handler =
                                oem_data_avail_to_receive_msg_avail;
                }
 setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info)
 {
        struct ipmi_device_id *id = &smi_info->device_id;
-       const char mfr[3]=DELL_IANA_MFR_ID;
-       if (!memcmp(mfr, id->manufacturer_id, sizeof(mfr)) &&
+       if (id->manufacturer_id == DELL_IANA_MFR_ID &&
            smi_info->si_type == SI_BT)
                register_xaction_notifier(&dell_poweredge_bt_xaction_notifier);
 }
                new_smi->thread = kthread_run(ipmi_thread, new_smi,
                                              "kipmi%d", new_smi->intf_num);
 
+       if (!new_smi->dev) {
+               /* If we don't already have a device from something
+                * else (like PCI), then register a new one. */
+               new_smi->pdev = platform_device_alloc("ipmi_si",
+                                                     new_smi->intf_num);
+               if (rv) {
+                       printk(KERN_ERR
+                              "ipmi_si_intf:"
+                              " Unable to allocate platform device\n");
+                       goto out_err_stop_timer;
+               }
+               new_smi->dev = &new_smi->pdev->dev;
+               new_smi->dev->driver = &ipmi_driver;
+
+               rv = platform_device_register(new_smi->pdev);
+               if (rv) {
+                       printk(KERN_ERR
+                              "ipmi_si_intf:"
+                              " Unable to register system interface device:"
+                              " %d\n",
+                              rv);
+                       goto out_err_stop_timer;
+               }
+               new_smi->dev_registered = 1;
+       }
+
        rv = ipmi_register_smi(&handlers,
                               new_smi,
-                              ipmi_version_major(&new_smi->device_id),
-                              ipmi_version_minor(&new_smi->device_id),
+                              &new_smi->device_id,
+                              new_smi->dev,
                               new_smi->slave_addr,
                               &(new_smi->intf));
        if (rv) {
        if (new_smi->io_cleanup)
                new_smi->io_cleanup(new_smi);
 
+       if (new_smi->dev_registered)
+               platform_device_unregister(new_smi->pdev);
+
+       kfree(new_smi);
+
        up(&smi_infos_lock);
 
        return rv;
 {
        int  i;
        char *str;
+       int  rv;
 
        if (initialized)
                return 0;
        initialized = 1;
 
+       /* Register the device drivers. */
+       rv = driver_register(&ipmi_driver);
+       if (rv) {
+               printk(KERN_ERR
+                      "init_ipmi_si: Unable to register driver: %d\n",
+                      rv);
+               return rv;
+       }
+
+
        /* Parse out the si_type string into its components. */
        str = si_type_str;
        if (*str != '\0') {
                to_clean->addr_source_cleanup(to_clean);
        if (to_clean->io_cleanup)
                to_clean->io_cleanup(to_clean);
+
+       if (to_clean->dev_registered)
+               platform_device_unregister(to_clean->pdev);
+
+       kfree(to_clean);
 }
 
 static __exit void cleanup_ipmi_si(void)
        list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
                cleanup_one_si(e);
        up(&smi_infos_lock);
+
+       driver_unregister(&ipmi_driver);
 }
 module_exit(cleanup_ipmi_si);