Re: [PATCH] IPMI: driver model and sysfs support

From: Yani Ioannou
Date: Sat Nov 26 2005 - 07:08:32 EST


Hi Corey,

On 8/30/05, Yani Ioannou <yani.ioannou@xxxxxxxxx> wrote:
> On 8/30/05, Corey Minyard <minyard@xxxxxxx> wrote:
> > This is very good. I believe the structure is correct, but I'm not a
> > sysfs expert.
> >
> > There are a few things we need to deal with, though.
<snip>

Sorry it has been quite a while .. anyway I finally got around to
updating the patch with the changes you proposed, and attached is a
patch against the latest git (2.6.15-rc2-git6).

Thanks,
Yani

---

drivers/char/ipmi/ipmi_bt_sm.c | 13 +
drivers/char/ipmi/ipmi_devintf.c | 11 -
drivers/char/ipmi/ipmi_kcs_sm.c | 13 +
drivers/char/ipmi/ipmi_msghandler.c | 374 +++++++++++++++++++++++++++++++++++
drivers/char/ipmi/ipmi_si_intf.c | 200 ++++++++++++++++---
drivers/char/ipmi/ipmi_si_sm.h | 9 +
drivers/char/ipmi/ipmi_smic_sm.c | 13 +
include/linux/ipmi.h | 127 ++++++++++++
include/linux/ipmi_msgdefs.h | 1
include/linux/ipmi_smi.h | 1
10 files changed, 723 insertions(+), 39 deletions(-)

applies-to: 1f3b06d516dac26c976cedfc59cd48781b96f86d
93ace52e69e5cc5258abb14a8c6c8786ca7d209b
diff --git a/drivers/char/ipmi/ipmi_bt_sm.c b/drivers/char/ipmi/ipmi_bt_sm.c
index 58dcdee..648ab0b 100644
--- a/drivers/char/ipmi/ipmi_bt_sm.c
+++ b/drivers/char/ipmi/ipmi_bt_sm.c
@@ -28,6 +28,7 @@

#include <linux/kernel.h> /* For printk. */
#include <linux/string.h>
+#include <linux/ipmi.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ipmi_msgdefs.h> /* for completion codes */
@@ -105,6 +106,17 @@ struct si_sm_data {
#define BT_INTMASK_R bt->io->inputb(bt->io, 2)
#define BT_INTMASK_W(x) bt->io->outputb(bt->io, 2, x)

+/**
+ * The driver model view of the IPMI system interface state machine.
+ */
+static struct ipmi_si_driver bt_si_driver = {
+ .owner = THIS_MODULE,
+ .driver = {
+ .name = "ipmi_bt_sm",
+ .bus = &platform_bus_type
+ },
+};
+
/* Convenience routines for debugging. These are not multi-open safe!
Note the macros have hardcoded variables in them. */

@@ -513,6 +525,7 @@ static int bt_size(void)

struct si_sm_handlers bt_smi_handlers =
{
+ .driver = &bt_si_driver,
.init_data = bt_init_data,
.start_transaction = bt_start_transaction,
.get_result = bt_get_result,
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index 7c0684d..c0e3cc5 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -789,7 +789,7 @@ MODULE_PARM_DESC(ipmi_major, "Sets the m
" interface. Other values will set the major device number"
" to that value.");

-static struct class *ipmi_class;
+extern struct class *ipmi_class;

static void ipmi_new_smi(int if_num)
{
@@ -823,15 +823,8 @@ static __init int init_ipmi_devintf(void

printk(KERN_INFO "ipmi device interface\n");

- ipmi_class = class_create(THIS_MODULE, "ipmi");
- if (IS_ERR(ipmi_class)) {
- printk(KERN_ERR "ipmi: can't register device class\n");
- return PTR_ERR(ipmi_class);
- }
-
rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
if (rv < 0) {
- class_destroy(ipmi_class);
printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major);
return rv;
}
@@ -845,7 +838,6 @@ static __init int init_ipmi_devintf(void
rv = ipmi_smi_watcher_register(&smi_watcher);
if (rv) {
unregister_chrdev(ipmi_major, DEVICE_NAME);
- class_destroy(ipmi_class);
printk(KERN_WARNING "ipmi: can't register smi watcher\n");
return rv;
}
@@ -856,7 +848,6 @@ module_init(init_ipmi_devintf);

static __exit void cleanup_ipmi(void)
{
- class_destroy(ipmi_class);
ipmi_smi_watcher_unregister(&smi_watcher);
devfs_remove(DEVICE_NAME);
unregister_chrdev(ipmi_major, DEVICE_NAME);
diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c
index da15541..6e71315 100644
--- a/drivers/char/ipmi/ipmi_kcs_sm.c
+++ b/drivers/char/ipmi/ipmi_kcs_sm.c
@@ -41,6 +41,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
+#include <linux/ipmi.h>
#include <linux/jiffies.h>
#include <linux/ipmi_msgdefs.h> /* for completion codes */
#include "ipmi_si_sm.h"
@@ -120,6 +121,17 @@ struct si_sm_data
unsigned long error0_timeout;
};

+/**
+ * The driver model view of the IPMI system interface state machine.
+ */
+static struct ipmi_si_driver kcs_si_driver = {
+ .owner = THIS_MODULE,
+ .driver = {
+ .name = "ipmi_kcs_sm",
+ .bus = &platform_bus_type
+ },
+};
+
static unsigned int init_kcs_data(struct si_sm_data *kcs,
struct si_sm_io *io)
{
@@ -509,6 +521,7 @@ static void kcs_cleanup(struct si_sm_dat

struct si_sm_handlers kcs_smi_handlers =
{
+ .driver = &kcs_si_driver,
.init_data = init_kcs_data,
.start_transaction = start_kcs_transaction,
.get_result = get_kcs_result,
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 6b302a9..1b4c9b8 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -30,6 +30,10 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+/*
+ * IPMI driver model/sysfs support.
+ * Copyright (C) 2005 Yani Ioannou <yani.ioannou@xxxxxxxxx>
+ */

#include <linux/config.h>
#include <linux/module.h>
@@ -66,6 +70,13 @@ struct proc_dir_entry *proc_ipmi_root =
#define MAX_MSG_TIMEOUT 60000


+#define IPMI_DEVICE_MANUFACTURER_ID(man_id) \
+ ((long unsigned int) (man_id[2] << 16 | man_id[1] << 8 | man_id[0]))
+
+#define IPMI_DEVICE_PRODUCT_ID(product_id) \
+ ((long unsigned int) (product_id[1] << 8 | product_id[0]))
+
+
/*
* The main "user" data structure.
*/
@@ -319,6 +330,19 @@ struct ipmi_smi
#define IPMI_INVALID_INTERFACE(i) (((i) == NULL) \
|| (i == IPMI_INVALID_INTERFACE_ENTRY))

+struct class *ipmi_class;
+
+/**
+ * The driver model view of the IPMI messaging driver.
+ */
+static struct ipmi_driver ipmidriver = {
+ .owner = THIS_MODULE,
+ .driver = {
+ .name = "ipmi_msghandler",
+ .bus = &platform_bus_type
+ }
+};
+
#define MAX_IPMI_INTERFACES 4
static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES];

@@ -378,6 +402,330 @@ static void intf_free(struct kref *ref)
kfree(intf);
}

+static int __find_bmc_guid(struct device *dev, void *data)
+{
+ unsigned char *id = (unsigned char *) data;
+ struct ipmi_bmc_device *bmc =
+ to_ipmi_bmc_device(to_platform_device(dev));
+
+ return memcmp(bmc->guid, id, 16) == 0;
+}
+
+struct ipmi_bmc_device * ipmi_find_bmc_guid(struct ipmi_driver *drv,
+ unsigned char* guid)
+{
+ struct device *dev = driver_find_device(&drv->driver, NULL, guid,
+ __find_bmc_guid);
+ return (dev?to_ipmi_bmc_device(to_platform_device(dev)):NULL);
+}
+
+struct prod_dev_id{
+ unsigned char product_id[2];
+ unsigned char device_id;
+};
+
+static int __find_bmc_prod_dev_id(struct device *dev, void *data)
+{
+ struct prod_dev_id *id = (struct prod_dev_id *) data;
+ struct ipmi_bmc_device *bmc =
+ to_ipmi_bmc_device(to_platform_device(dev));
+
+ return bmc->id.product_id[0] == id->product_id[0]
+ && bmc->id.product_id[1] == id->product_id[1]
+ && bmc->id.device_id == id->device_id;
+}
+
+struct ipmi_bmc_device * ipmi_find_bmc_prod_dev_id(struct ipmi_driver *drv,
+ unsigned char *product_id, unsigned char *device_id)
+{
+ struct prod_dev_id id = {
+ .product_id = {product_id[0], product_id[1]},
+ .device_id = *device_id,
+ };
+
+ struct device *dev = driver_find_device(&drv->driver, NULL, &id,
+ __find_bmc_prod_dev_id);
+ return (dev?to_ipmi_bmc_device(to_platform_device(dev)):NULL);
+}
+
+static void ipmi_bmc_release(struct device *dev)
+{
+ printk(KERN_DEBUG "ipmi_bmc release\n");
+}
+
+static ssize_t device_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipmi_bmc_device *bmc = to_ipmi_bmc_device(to_platform_device(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 ipmi_bmc_device *bmc = to_ipmi_bmc_device(to_platform_device(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 ipmi_bmc_device *bmc = to_ipmi_bmc_device(to_platform_device(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 ipmi_bmc_device *bmc = to_ipmi_bmc_device(to_platform_device(dev));
+ return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_rev_1,
+ bmc->id.firmware_rev_2);
+}
+
+static ssize_t ipmi_version_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipmi_bmc_device *bmc = to_ipmi_bmc_device(to_platform_device(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 ipmi_bmc_device *bmc = to_ipmi_bmc_device(to_platform_device(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 ipmi_bmc_device *bmc = to_ipmi_bmc_device(to_platform_device(dev));
+ return snprintf(buf, 20, "%lu\n",
+ IPMI_DEVICE_MANUFACTURER_ID(bmc->id.manufacturer_id));
+}
+
+static ssize_t product_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipmi_bmc_device *bmc = to_ipmi_bmc_device(to_platform_device(dev));
+ return snprintf(buf, 10, "%lu\n",
+ IPMI_DEVICE_PRODUCT_ID(bmc->id.product_id));
+}
+
+static ssize_t aux_firmware_rev_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipmi_bmc_device *bmc = to_ipmi_bmc_device(to_platform_device(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 ipmi_bmc_device *bmc = to_ipmi_bmc_device(to_platform_device(dev));
+ return snprintf(buf, 100, "%Lx%Lx\n",
+ (long long) bmc->guid[0],
+ (long long) bmc->guid[8]);
+}
+
+void ipmi_bmc_unregister(struct ipmi_si_device *si,
+ struct ipmi_bmc_device *bmc)
+{
+ if(--bmc->interfaces == 0){
+ class_device_unregister(bmc->class_dev);
+ 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);
+ device_remove_file(&bmc->dev.dev,
+ &bmc->aux_firmware_rev_attr);
+ if(bmc->guid_present)
+ device_remove_file(&bmc->dev.dev,
+ &bmc->guid_attr);
+ platform_device_unregister(&bmc->dev);
+ }
+}
+
+int ipmi_bmc_register(struct ipmi_si_device *si,
+ struct ipmi_bmc_device **bmcref)
+{
+ int rv = 0;
+ struct ipmi_bmc_device *bmc = *bmcref;
+ struct ipmi_bmc_device *new_bmc;
+
+ /* Try to find if there is an ipmi_bmc_device struct
+ * representing the interfaced BMC already
+ */
+ if(bmc->guid_present){
+ new_bmc = ipmi_find_bmc_guid(&ipmidriver, bmc->guid);
+ }else{
+ new_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver,
+ bmc->id.product_id,
+ &bmc->id.device_id);
+ }
+ /* If there is already an ipmi_bmc_device, free the new one,
+ * otherwise register the new BMC device
+ */
+ if(new_bmc){
+ kfree(bmc);
+ *bmcref = new_bmc;
+ bmc = new_bmc;
+
+ bmc->interfaces++;
+
+ printk(KERN_INFO
+ "ipmi_si: interfacing existing BMC (man_id: %lu, \
+prod_id: %lu, dev_id: %u)\n",
+ IPMI_DEVICE_MANUFACTURER_ID(bmc->id.manufacturer_id),
+ IPMI_DEVICE_PRODUCT_ID(bmc->id.product_id),
+ bmc->id.device_id);
+ }else{
+ bmc->dev.name = "ipmi_bmc";
+ bmc->dev.id = bmc->id.device_id;
+ bmc->dev.dev.driver = &ipmidriver.driver;
+ bmc->dev.dev.release = ipmi_bmc_release;
+ bmc->interfaces = 1;
+
+ rv = platform_device_register(&bmc->dev);
+ if (rv) {
+ printk(KERN_ERR
+ "ipmi_si: Unable to register bmc device: %d\n",
+ rv);
+ goto out_err;
+ }
+
+ 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);
+ device_create_file(&bmc->dev.dev,
+ &bmc->aux_firmware_rev_attr);
+
+ if(bmc->guid_present)
+ device_create_file(&bmc->dev.dev,
+ &bmc->guid_attr);
+
+ bmc->class_dev =
+ kmalloc(sizeof(struct class_device), GFP_KERNEL);
+ if (!bmc->class_dev) {
+ printk(KERN_ERR "ipmi_msghandler: Could not allocate class_device memory\n");
+ rv = -ENOMEM;
+ goto out_err;
+ }
+ memset(bmc->class_dev, 0, sizeof(struct class_device));
+
+
+ bmc->class_dev->class = ipmi_class;
+ bmc->class_dev->dev = &bmc->dev.dev;
+ snprintf(bmc->class_dev->class_id, BUS_ID_SIZE, "bmc%d",
+ bmc->id.device_id);
+
+ class_device_register(bmc->class_dev);
+
+ printk(KERN_INFO
+ "ipmi_si: Found new BMC (man_id: %lu, prod_id: %lu, \
+dev_id: %u)\n",
+ IPMI_DEVICE_MANUFACTURER_ID(bmc->id.manufacturer_id),
+ IPMI_DEVICE_PRODUCT_ID(bmc->id.product_id),
+ bmc->id.device_id);
+ }
+
+ /* create symlink from system interface device to bmc device */
+ rv = sysfs_create_link(&si->dev.dev.kobj,
+ &bmc->dev.dev.kobj, "bmc");
+ if (rv) {
+ printk(KERN_ERR
+ "ipmi_si: Unable to create bmc symlink: %d\n",
+ rv);
+ goto out_err;
+ }
+out_err:
+ return rv;
+}
+
int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
{
int i;
@@ -3194,11 +3542,23 @@ static struct notifier_block panic_block

static int ipmi_init_msghandler(void)
{
- int i;
+ int i, retval;

if (initialized)
return 0;
-
+
+ ipmi_class = class_create(THIS_MODULE, "ipmi");
+ if (IS_ERR(ipmi_class)) {
+ printk(KERN_ERR "ipmi: can't register device class\n");
+ return PTR_ERR(ipmi_class);
+ }
+
+ retval = driver_register(&ipmidriver.driver);
+ if (retval) {
+ printk(KERN_ERR PFX "Could'nt register IPMI driver\n");
+ return retval;
+ }
+
printk(KERN_INFO "ipmi message handler version "
IPMI_DRIVER_VERSION "\n");

@@ -3255,6 +3615,10 @@ static __exit void cleanup_ipmi(void)
remove_proc_entry(proc_ipmi_root->name, &proc_root);
#endif /* CONFIG_PROC_FS */

+ driver_unregister(&ipmidriver.driver);
+
+ class_destroy(ipmi_class);
+
initialized = 0;

/* Check for buffer leaks. */
@@ -3300,3 +3664,9 @@ EXPORT_SYMBOL(ipmi_smi_add_proc_entry);
EXPORT_SYMBOL(proc_ipmi_root);
EXPORT_SYMBOL(ipmi_user_set_run_to_completion);
EXPORT_SYMBOL(ipmi_free_recv_msg);
+EXPORT_SYMBOL(ipmidriver);
+EXPORT_SYMBOL(ipmi_find_bmc_guid);
+EXPORT_SYMBOL(ipmi_find_bmc_prod_dev_id);
+EXPORT_SYMBOL(ipmi_bmc_register);
+EXPORT_SYMBOL(ipmi_bmc_unregister);
+EXPORT_SYMBOL(ipmi_class);
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 01a1f6b..d824597 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -38,6 +38,11 @@
* and drives the real SMI state machine.
*/

+/*
+ * IPMI driver model/sysfs support.
+ * Copyright (C) 2005 Yani Ioannou <yani.ioannou@xxxxxxxxx>
+ */
+
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -110,25 +115,11 @@ enum si_type {
SI_KCS, SI_SMIC, SI_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)
-
struct smi_info
{
int intf_num;
ipmi_smi_t intf;
+ struct ipmi_si_device *dev;
struct si_sm_data *si_sm;
struct si_sm_handlers *handlers;
enum si_type si_type;
@@ -203,8 +194,6 @@ struct smi_info
interrupts. */
int interrupt_disabled;

- struct ipmi_device_id device_id;
-
/* Slave address, could be reported from DMI. */
unsigned char slave_addr;

@@ -232,6 +221,13 @@ static int register_xaction_notifier(str
return notifier_chain_register(&xaction_notifier_list, nb);
}

+extern struct ipmi_driver ipmidriver;
+extern struct class *ipmi_class;
+extern int ipmi_bmc_register(struct ipmi_si_device *si,
+ struct ipmi_bmc_device **bmcref);
+extern void ipmi_bmc_unregister(struct ipmi_si_device *si,
+ struct ipmi_bmc_device *bmc);
+
static void si_restart_short_timer(struct smi_info *smi_info);

static void deliver_recv_msg(struct smi_info *smi_info,
@@ -1934,6 +1930,75 @@ static int try_init_plug_and_play(int in
return -ENODEV;
}

+static int try_get_dev_guid(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;
+
+ smi_info->dev->bmc->guid_present = 0;
+
+ resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
+ if (!resp)
+ return -ENOMEM;
+
+ /* Do a Get Device GUID command, since it might come back with a
+ * useful globally unique BMC ID. */
+ msg[0] = IPMI_NETFN_APP_REQUEST << 2;
+ msg[1] = IPMI_GET_DEVICE_GUID_CMD;
+ smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
+
+ smi_result = smi_info->handlers->event(smi_info->si_sm, 0);
+ for (;;)
+ {
+ if (smi_result == SI_SM_CALL_WITH_DELAY) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ smi_result = smi_info->handlers->event(
+ smi_info->si_sm, 100);
+ }
+ else if (smi_result == SI_SM_CALL_WITHOUT_DELAY)
+ {
+ smi_result = smi_info->handlers->event(
+ smi_info->si_sm, 0);
+ }
+ else
+ break;
+ }
+ if (smi_result == SI_SM_HOSED) {
+ /* We couldn't get the state machine to run, so whatever's at
+ the port is probably not an IPMI SMI interface. */
+ rv = -ENODEV;
+ goto gout;
+ }
+
+ /* 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) {
+ /* That's odd, it should be longer. */
+ rv = -EINVAL;
+ goto gout;
+ }
+
+ if ((resp[1] != IPMI_GET_DEVICE_GUID_CMD) || (resp[2] != 0)) {
+ /* That's odd, it shouldn't be able to fail. */
+ rv = -EINVAL;
+ goto gout;
+ }
+
+ /* Record info from the get device guid, if none returned, null
+ * terminate first char */
+ if(resp[2]){
+ memcpy(smi_info->dev->bmc->guid, &resp[3], 16);
+ smi_info->dev->bmc->guid_present = 1;
+ }
+ gout:
+ kfree(resp);
+ return rv;
+}

static int try_get_dev_id(struct smi_info *smi_info)
{
@@ -1993,8 +2058,8 @@ static int try_get_dev_id(struct smi_inf
}

/* 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)));
+ memcpy(&smi_info->dev->bmc->id, &resp[3],
+ min_t(unsigned long, resp_len-3, sizeof(struct bmc_device_id)));

out:
kfree(resp);
@@ -2100,7 +2165,7 @@ static int oem_data_avail_to_receive_msg
#define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00}
static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info)
{
- struct ipmi_device_id *id = &smi_info->device_id;
+ struct bmc_device_id *id = &smi_info->dev->bmc->id;
const char mfr[3]=DELL_IANA_MFR_ID;
if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr))) {
if (id->device_id == DELL_POWEREDGE_8G_BMC_DEVICE_ID &&
@@ -2176,7 +2241,7 @@ static struct notifier_block dell_powere
static void
setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info)
{
- struct ipmi_device_id *id = &smi_info->device_id;
+ struct bmc_device_id *id = &smi_info->dev->bmc->id;
const char mfr[3]=DELL_IANA_MFR_ID;
if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr)) &&
smi_info->si_type == SI_BT)
@@ -2208,6 +2273,13 @@ static inline void wait_for_timer_and_th
del_timer_sync(&smi_info->si_timer);
}

+static void ipmi_si_release(struct device *dev)
+{
+ struct ipmi_si_device *si = to_ipmi_si_device(to_platform_device(dev));
+ ipmi_bmc_unregister(si, si->bmc);
+ printk(KERN_DEBUG "ipmi_si release\n");
+}
+
/* Returns 0 if initialized, or negative on an error. */
static int init_one_smi(int intf_num, struct smi_info **smi)
{
@@ -2295,12 +2367,48 @@ static int init_one_smi(int intf_num, st
rv = -ENODEV;
goto out_err;
}
+
+ new_smi->dev = kmalloc(sizeof(struct ipmi_si_device), GFP_KERNEL);
+ if (!new_smi->dev) {
+ printk(KERN_ERR "ipmi_si: Could not allocate ipmi_si_device memory\n");
+ rv = -ENOMEM;
+ goto out_err;
+ }
+ memset(new_smi->dev, 0, sizeof(struct ipmi_si_device));

+ new_smi->dev->class_dev =
+ kmalloc(sizeof(struct class_device), GFP_KERNEL);
+ if (!new_smi->dev->class_dev) {
+ printk(KERN_ERR "ipmi_si: Could not allocate class_device memory\n");
+ rv = -ENOMEM;
+ goto out_err;
+ }
+ memset(new_smi->dev->class_dev, 0, sizeof(struct class_device));
+
+
+ /* Allocate a bmc device struct */
+ new_smi->dev->bmc = kmalloc(sizeof(struct ipmi_bmc_device), GFP_KERNEL);
+ if (!new_smi->dev->bmc) {
+ printk(KERN_ERR "ipmi_si: Could not allocate ipmi_bmc_device memory\n");
+ rv = -ENOMEM;
+ goto out_err;
+ }
+ memset(new_smi->dev->bmc, 0, sizeof(struct ipmi_bmc_device));
+
/* Attempt a get device id command. If it fails, we probably
don't have a SMI here. */
rv = try_get_dev_id(new_smi);
- if (rv)
+ if (rv){
+ printk(KERN_ERR "ipmi_si: Error while trying to get device id.\n");
+ goto out_err;
+ }
+
+ /* Attempt a get device guid command. */
+ rv = try_get_dev_guid(new_smi);
+ if (rv){
+ printk(KERN_ERR " Error while trying to get GUID.\n");
goto out_err;
+ }

setup_oem_data_handler(new_smi);
setup_xaction_handlers(new_smi);
@@ -2342,8 +2450,8 @@ static int init_one_smi(int intf_num, st

rv = ipmi_register_smi(&handlers,
new_smi,
- ipmi_version_major(&new_smi->device_id),
- ipmi_version_minor(&new_smi->device_id),
+ ipmi_version_major(&new_smi->dev->bmc->id),
+ ipmi_version_minor(&new_smi->dev->bmc->id),
new_smi->slave_addr,
&(new_smi->intf));
if (rv) {
@@ -2373,6 +2481,46 @@ static int init_one_smi(int intf_num, st
goto out_err_stop_timer;
}

+ rv = driver_register(&new_smi->handlers->driver->driver);
+ if (rv) {
+ printk(KERN_ERR
+ "ipmi_si: Unable to register driver: %d\n",
+ rv);
+ goto out_err_stop_timer;
+ }
+
+ new_smi->dev->smi = new_smi;
+
+ new_smi->dev->driver = new_smi->handlers->driver;
+ new_smi->dev->dev.name = "ipmi_si";
+ new_smi->dev->dev.id = intf_num;
+ new_smi->dev->dev.dev.driver = &new_smi->handlers->driver->driver;
+ new_smi->dev->dev.dev.release = ipmi_si_release;
+
+ rv = platform_device_register(&new_smi->dev->dev);
+ if (rv) {
+ printk(KERN_ERR
+ "ipmi_si: Unable to register system interface device: %d\n",
+ rv);
+ goto out_err_stop_timer;
+ }
+
+ new_smi->dev->class_dev->class = ipmi_class;
+ new_smi->dev->class_dev->dev = &new_smi->dev->dev.dev;
+ snprintf(new_smi->dev->class_dev->class_id, BUS_ID_SIZE, "smi%d",
+ intf_num);
+ class_device_register(new_smi->dev->class_dev);
+
+ /* register the attatched IPMI BMC */
+ rv = ipmi_bmc_register(new_smi->dev, &new_smi->dev->bmc);
+ if (rv) {
+ printk(KERN_ERR
+ "ipmi_si: Unable to register bmc: %d\n",
+ rv);
+ goto out_err_stop_timer;
+ }
+
+
*smi = new_smi;

printk(" IPMI %s interface initialized\n", si_type[intf_num]);
@@ -2482,6 +2630,10 @@ static void __exit cleanup_one_si(struct
if (! to_clean)
return;

+ class_device_unregister(to_clean->dev->class_dev);
+ platform_device_unregister(&to_clean->dev->dev);
+ driver_unregister(&to_clean->handlers->driver->driver);
+
/* Tell the timer and interrupt handlers that we are shutting
down. */
spin_lock_irqsave(&(to_clean->si_lock), flags);
diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h
index bf3d496..c507c65 100644
--- a/drivers/char/ipmi/ipmi_si_sm.h
+++ b/drivers/char/ipmi/ipmi_si_sm.h
@@ -34,6 +34,8 @@
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/

+#include <linux/ipmi.h>
+
/* This is defined by the state machines themselves, it is an opaque
data type for them to use. */
struct si_sm_data;
@@ -73,9 +75,10 @@ enum si_sm_result
/* Handlers for the SMI state machine. */
struct si_sm_handlers
{
- /* Put the version number of the state machine here so the
- upper layer can print it. */
- char *version;
+ /* The driver model view of the system interface state machine so it can be
+ registered/unregistered, also access to the version number so the upper
+ layer can print it. */
+ struct ipmi_si_driver *driver;

/* Initialize the data and return the amount of I/O space to
reserve for the space. */
diff --git a/drivers/char/ipmi/ipmi_smic_sm.c b/drivers/char/ipmi/ipmi_smic_sm.c
index 39d7e5e..cf7ccaf 100644
--- a/drivers/char/ipmi/ipmi_smic_sm.c
+++ b/drivers/char/ipmi/ipmi_smic_sm.c
@@ -43,6 +43,7 @@

#include <linux/kernel.h> /* For printk. */
#include <linux/string.h>
+#include <linux/ipmi.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ipmi_msgdefs.h> /* for completion codes */
@@ -119,6 +120,17 @@ struct si_sm_data
long smic_timeout;
};

+/**
+ * The driver model view of the IPMI system interface state machine.
+ */
+static struct ipmi_si_driver smic_si_driver = {
+ .owner = THIS_MODULE,
+ .driver = {
+ .name = "ipmi_smic_sm",
+ .bus = &platform_bus_type
+ },
+};
+
static unsigned int init_smic_data (struct si_sm_data *smic,
struct si_sm_io *io)
{
@@ -595,6 +607,7 @@ static int smic_size(void)

struct si_sm_handlers smic_smi_handlers =
{
+ .driver = &smic_si_driver,
.init_data = init_smic_data,
.start_transaction = start_smic_transaction,
.get_result = smic_get_result,
diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h
index d6276e6..485a2c6 100644
--- a/include/linux/ipmi.h
+++ b/include/linux/ipmi.h
@@ -30,10 +30,17 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+/*
+ * IPMI driver model/sysfs support.
+ * Copyright (C) 2005 Yani Ioannou <yani.ioannou@xxxxxxxxx>
+ */

#ifndef __LINUX_IPMI_H
#define __LINUX_IPMI_H

+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/ipmi_smi.h>
#include <linux/ipmi_msgdefs.h>
#include <linux/compiler.h>

@@ -68,6 +75,126 @@
* interface is defined later in the file. */


+/**
+ * struct ipmi_si_driver - represents a ipmi system interface
+ * driver in the kernel.
+ * @owner: Pointer to the module owner of this driver; initialize
+ * it using THIS_MODULE.
+ * @driver: the driver model core driver structure.
+ *
+ * An IPMI baseboard management controller (BMC) may be accessed directly by
+ * the host computer via a system interface of which there are many different
+ * types. This driver provides the interface defined by the general low level
+ * system interface driver.
+ */
+struct ipmi_si_driver
+{
+ struct module *owner;
+ struct device_driver driver;
+ /* driver attributes */
+};
+#define to_ipmi_si_driver(drv) container_of(drv, struct ipmi_si_driver, driver)
+
+/**
+ * struct ipmi_driver - represents the ipmi messaging interface
+ * driver in the kernel.
+ * @owner: Pointer to the module owner of this driver; initialize
+ * it using THIS_MODULE.
+ * @driver: the driver model core driver structure.
+ */
+struct ipmi_driver
+{
+ struct module *owner;
+ struct device_driver driver;
+};
+#define to_ipmi_driver(drv) container_of(drv, struct ipmi_driver, driver)
+
+/**
+ * struct ipmi_si_device - represents ipmi an IPMI system interface in
+ * the kernel.
+ * @smi: the low level interface struct.
+ * @bmc: the IPMI BMC interfaced to.
+ * @driver: the IPMI system interface driver that is bound to this interface.
+ * @dev: driver model's view of this device.
+ * @class_dev: driver model's class view of this device.
+ *
+ * An IPMI baseboard management controller (BMC) may be accessed directly by
+ * the host computer via a system interface of which there are many different
+ * types. This device represents one such system interface.
+ */
+struct ipmi_si_device
+{
+ smi_info_t smi;
+ struct ipmi_bmc_device *bmc;
+ struct ipmi_si_driver *driver;
+ struct platform_device dev;
+ struct class_device *class_dev;
+};
+#define to_ipmi_si_device(device) \
+ container_of(device, struct ipmi_si_device, dev)
+
+/**
+ * struct bmc_device_id - represents an IPMI BMC's response to an
+ * IPMI_GET_DEVICE_ID_CMD command.
+ * @device_id: OEM per product device id (product_id+device_id unique).
+ * @device_revision: the IPMI BMC revision number.
+ * @firmware_revision_1: the BMC's firmware major version number.
+ * @firmware_revision_2: the BMC's firmware minor version number.
+ * @ipmi_version: the ipmi version numbers of the BMC.
+ * @additional_device_support: additional device support.
+ * @manufacturer_id: the manufacturer ID of the BMC.
+ * @product_id: OEM product id.
+ * @aux_firmware_revision: auxilliarly firmware revision.
+ */
+struct bmc_device_id {
+ unsigned char device_id;
+ unsigned char device_revision;
+ unsigned char firmware_rev_1;
+ unsigned char firmware_rev_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));
+
+/* macros to extract major/minor version from IPMI version */
+#define ipmi_version_major(v) ((v)->ipmi_version & 0xf)
+#define ipmi_version_minor(v) ((v)->ipmi_version >> 4)
+
+/**
+ * struct ipmi_bmc_device - represents an IPMI baseboard management controller
+ * (BMC) attatched to one or more system interfaces.
+ * @dev: driver model's view of this device.
+ * @id: the BMC's device id response.
+ * @guid: BMC's globally unique identifier (optional).
+ * @guid_present: set iff a BMC GUID is available.
+ * @interfaces: the number of system interfaces currently interfacing
+ * to this BMC.
+ */
+struct ipmi_bmc_device
+{
+ struct platform_device dev;
+ struct bmc_device_id id;
+ unsigned char guid[16];
+ int guid_present;
+ int interfaces;
+ /* bmc class device */
+ struct class_device *class_dev;
+ /* 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 to_ipmi_bmc_device(device) \
+ container_of(device, struct ipmi_bmc_device, dev)

/*
* This is an overlay for all the address types, so it's easy to
diff --git a/include/linux/ipmi_msgdefs.h b/include/linux/ipmi_msgdefs.h
index 03bc64d..f8572af 100644
--- a/include/linux/ipmi_msgdefs.h
+++ b/include/linux/ipmi_msgdefs.h
@@ -45,6 +45,7 @@

#define IPMI_NETFN_APP_REQUEST 0x06
#define IPMI_NETFN_APP_RESPONSE 0x07
+#define IPMI_GET_DEVICE_GUID_CMD 0x08
#define IPMI_GET_DEVICE_ID_CMD 0x01
#define IPMI_CLEAR_MSG_FLAGS_CMD 0x30
#define IPMI_GET_MSG_FLAGS_CMD 0x31
diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h
index e36ee15..96c2182 100644
--- a/include/linux/ipmi_smi.h
+++ b/include/linux/ipmi_smi.h
@@ -43,6 +43,7 @@

/* Structure for the low-level drivers. */
typedef struct ipmi_smi *ipmi_smi_t;
+typedef struct smi_info *smi_info_t;

/*
* Messages to/from the lower layer. The smi interface will take one
---
0.99.9j