Commit 50c812b2 authored by Corey Minyard's avatar Corey Minyard Committed by Linus Torvalds

[PATCH] ipmi: add full sysfs support

Add full driver model support for the IPMI driver.  It links in the proper
bus and device support.

It adds an "ipmi" driver interface that has each BMC discovered by the
driver (as a device).  These BMCs appear in the devices/platform directory.
 If there are multiple interfaces to the same BMC, the driver should
discover this and will only have one BMC entry.  The BMC entry will have
pointers to each interface device that connects to it.

The device information (statistics and config information) has not yet been
ported over to the driver model from proc, that will come later.

This work was based on work by Yani Ioannou.  I basically rewrote it using
that code as a guide, but he still deserves credit :).

[bunk@stusta.de: make ipmi_find_bmc_guid() static]
Signed-off-by: default avatarCorey Minyard <minyard@acm.org>
Signed-off-by: default avatarYani Ioannou <yani.ioannou@gmail.com>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent b0defcdb
...@@ -90,7 +90,7 @@ static unsigned int ipmi_poll(struct file *file, poll_table *wait) ...@@ -90,7 +90,7 @@ static unsigned int ipmi_poll(struct file *file, poll_table *wait)
spin_lock_irqsave(&priv->recv_msg_lock, flags); spin_lock_irqsave(&priv->recv_msg_lock, flags);
if (! list_empty(&(priv->recv_msgs))) if (!list_empty(&(priv->recv_msgs)))
mask |= (POLLIN | POLLRDNORM); mask |= (POLLIN | POLLRDNORM);
spin_unlock_irqrestore(&priv->recv_msg_lock, flags); spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
...@@ -789,21 +789,53 @@ MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By" ...@@ -789,21 +789,53 @@ MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By"
" interface. Other values will set the major device number" " interface. Other values will set the major device number"
" to that value."); " to that value.");
/* Keep track of the devices that are registered. */
struct ipmi_reg_list {
dev_t dev;
struct list_head link;
};
static LIST_HEAD(reg_list);
static DEFINE_MUTEX(reg_list_mutex);
static struct class *ipmi_class; static struct class *ipmi_class;
static void ipmi_new_smi(int if_num) static void ipmi_new_smi(int if_num, struct device *device)
{ {
dev_t dev = MKDEV(ipmi_major, if_num); dev_t dev = MKDEV(ipmi_major, if_num);
struct ipmi_reg_list *entry;
devfs_mk_cdev(dev, S_IFCHR | S_IRUSR | S_IWUSR, devfs_mk_cdev(dev, S_IFCHR | S_IRUSR | S_IWUSR,
"ipmidev/%d", if_num); "ipmidev/%d", if_num);
class_device_create(ipmi_class, NULL, dev, NULL, "ipmi%d", if_num); entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
printk(KERN_ERR "ipmi_devintf: Unable to create the"
" ipmi class device link\n");
return;
}
entry->dev = dev;
mutex_lock(&reg_list_mutex);
class_device_create(ipmi_class, NULL, dev, device, "ipmi%d", if_num);
list_add(&entry->link, &reg_list);
mutex_unlock(&reg_list_mutex);
} }
static void ipmi_smi_gone(int if_num) static void ipmi_smi_gone(int if_num)
{ {
class_device_destroy(ipmi_class, MKDEV(ipmi_major, if_num)); dev_t dev = MKDEV(ipmi_major, if_num);
struct ipmi_reg_list *entry;
mutex_lock(&reg_list_mutex);
list_for_each_entry(entry, &reg_list, link) {
if (entry->dev == dev) {
list_del(&entry->link);
kfree(entry);
break;
}
}
class_device_destroy(ipmi_class, dev);
mutex_unlock(&reg_list_mutex);
devfs_remove("ipmidev/%d", if_num); devfs_remove("ipmidev/%d", if_num);
} }
...@@ -856,6 +888,14 @@ module_init(init_ipmi_devintf); ...@@ -856,6 +888,14 @@ module_init(init_ipmi_devintf);
static __exit void cleanup_ipmi(void) static __exit void cleanup_ipmi(void)
{ {
struct ipmi_reg_list *entry, *entry2;
mutex_lock(&reg_list_mutex);
list_for_each_entry_safe(entry, entry2, &reg_list, link) {
list_del(&entry->link);
class_device_destroy(ipmi_class, entry->dev);
kfree(entry);
}
mutex_unlock(&reg_list_mutex);
class_destroy(ipmi_class); class_destroy(ipmi_class);
ipmi_smi_watcher_unregister(&smi_watcher); ipmi_smi_watcher_unregister(&smi_watcher);
devfs_remove(DEVICE_NAME); devfs_remove(DEVICE_NAME);
......
This diff is collapsed.
...@@ -464,7 +464,7 @@ static void ipmi_poweroff_function (void) ...@@ -464,7 +464,7 @@ static void ipmi_poweroff_function (void)
/* Wait for an IPMI interface to be installed, the first one installed /* Wait for an IPMI interface to be installed, the first one installed
will be grabbed by this code and used to perform the powerdown. */ will be grabbed by this code and used to perform the powerdown. */
static void ipmi_po_new_smi(int if_num) static void ipmi_po_new_smi(int if_num, struct device *device)
{ {
struct ipmi_system_interface_addr smi_addr; struct ipmi_system_interface_addr smi_addr;
struct kernel_ipmi_msg send_msg; struct kernel_ipmi_msg send_msg;
......
...@@ -112,20 +112,13 @@ enum si_type { ...@@ -112,20 +112,13 @@ enum si_type {
}; };
static char *si_to_str[] = { "KCS", "SMIC", "BT" }; static char *si_to_str[] = { "KCS", "SMIC", "BT" };
struct ipmi_device_id { #define DEVICE_NAME "ipmi_si"
unsigned char device_id;
unsigned char device_revision; static struct device_driver ipmi_driver =
unsigned char firmware_revision_1; {
unsigned char firmware_revision_2; .name = DEVICE_NAME,
unsigned char ipmi_version; .bus = &platform_bus_type
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 struct smi_info
{ {
...@@ -208,8 +201,17 @@ struct smi_info ...@@ -208,8 +201,17 @@ struct smi_info
interrupts. */ interrupts. */
int interrupt_disabled; int interrupt_disabled;
/* From the get device id response... */
struct ipmi_device_id device_id; 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. */ /* Slave address, could be reported from DMI. */
unsigned char slave_addr; unsigned char slave_addr;
...@@ -987,8 +989,6 @@ static LIST_HEAD(smi_infos); ...@@ -987,8 +989,6 @@ static LIST_HEAD(smi_infos);
static DECLARE_MUTEX(smi_infos_lock); static DECLARE_MUTEX(smi_infos_lock);
static int smi_num; /* Used to sequence the SMIs */ static int smi_num; /* Used to sequence the SMIs */
#define DEVICE_NAME "ipmi_si"
#define DEFAULT_REGSPACING 1 #define DEFAULT_REGSPACING 1
static int si_trydefaults = 1; static int si_trydefaults = 1;
...@@ -1164,7 +1164,6 @@ static void port_cleanup(struct smi_info *info) ...@@ -1164,7 +1164,6 @@ static void port_cleanup(struct smi_info *info)
release_region (addr, mapsize); release_region (addr, mapsize);
} }
kfree(info);
} }
static int port_setup(struct smi_info *info) static int port_setup(struct smi_info *info)
...@@ -1273,7 +1272,6 @@ static void mem_cleanup(struct smi_info *info) ...@@ -1273,7 +1272,6 @@ static void mem_cleanup(struct smi_info *info)
release_mem_region(addr, mapsize); release_mem_region(addr, mapsize);
} }
kfree(info);
} }
static int mem_setup(struct smi_info *info) static int mem_setup(struct smi_info *info)
...@@ -1858,6 +1856,8 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, ...@@ -1858,6 +1856,8 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
if (info->irq) if (info->irq)
info->irq_setup = std_irq_setup; info->irq_setup = std_irq_setup;
info->dev = &pdev->dev;
return try_smi_init(info); return try_smi_init(info);
} }
...@@ -1898,11 +1898,11 @@ static struct pci_driver ipmi_pci_driver = { ...@@ -1898,11 +1898,11 @@ static struct pci_driver ipmi_pci_driver = {
static int try_get_dev_id(struct smi_info *smi_info) static int try_get_dev_id(struct smi_info *smi_info)
{ {
unsigned char msg[2]; unsigned char msg[2];
unsigned char *resp; unsigned char *resp;
unsigned long resp_len; unsigned long resp_len;
enum si_sm_result smi_result; enum si_sm_result smi_result;
int rv = 0; int rv = 0;
resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
if (!resp) if (!resp)
...@@ -1941,7 +1941,7 @@ static int try_get_dev_id(struct smi_info *smi_info) ...@@ -1941,7 +1941,7 @@ static int try_get_dev_id(struct smi_info *smi_info)
/* Otherwise, we got some data. */ /* Otherwise, we got some data. */
resp_len = smi_info->handlers->get_result(smi_info->si_sm, resp_len = smi_info->handlers->get_result(smi_info->si_sm,
resp, IPMI_MAX_MSG_LENGTH); resp, IPMI_MAX_MSG_LENGTH);
if (resp_len < 6) { if (resp_len < 14) {
/* That's odd, it should be longer. */ /* That's odd, it should be longer. */
rv = -EINVAL; rv = -EINVAL;
goto out; goto out;
...@@ -1954,8 +1954,7 @@ static int try_get_dev_id(struct smi_info *smi_info) ...@@ -1954,8 +1954,7 @@ static int try_get_dev_id(struct smi_info *smi_info)
} }
/* Record info from the get device id, in case we need it. */ /* Record info from the get device id, in case we need it. */
memcpy(&smi_info->device_id, &resp[3], ipmi_demangle_device_id(resp+3, resp_len-3, &smi_info->device_id);
min_t(unsigned long, resp_len-3, sizeof(smi_info->device_id)));
out: out:
kfree(resp); kfree(resp);
...@@ -2058,15 +2057,14 @@ static int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info) ...@@ -2058,15 +2057,14 @@ static int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info)
#define DELL_POWEREDGE_8G_BMC_DEVICE_ID 0x20 #define DELL_POWEREDGE_8G_BMC_DEVICE_ID 0x20
#define DELL_POWEREDGE_8G_BMC_DEVICE_REV 0x80 #define DELL_POWEREDGE_8G_BMC_DEVICE_REV 0x80
#define DELL_POWEREDGE_8G_BMC_IPMI_VERSION 0x51 #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) static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info)
{ {
struct ipmi_device_id *id = &smi_info->device_id; struct ipmi_device_id *id = &smi_info->device_id;
const char mfr[3]=DELL_IANA_MFR_ID; if (id->manufacturer_id == DELL_IANA_MFR_ID) {
if (!memcmp(mfr, id->manufacturer_id, sizeof(mfr))) {
if (id->device_id == DELL_POWEREDGE_8G_BMC_DEVICE_ID && if (id->device_id == DELL_POWEREDGE_8G_BMC_DEVICE_ID &&
id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV && 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 = smi_info->oem_data_avail_handler =
oem_data_avail_to_receive_msg_avail; oem_data_avail_to_receive_msg_avail;
} }
...@@ -2138,8 +2136,7 @@ static void ...@@ -2138,8 +2136,7 @@ static void
setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info) setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info)
{ {
struct ipmi_device_id *id = &smi_info->device_id; struct ipmi_device_id *id = &smi_info->device_id;
const char mfr[3]=DELL_IANA_MFR_ID; if (id->manufacturer_id == DELL_IANA_MFR_ID &&
if (!memcmp(mfr, id->manufacturer_id, sizeof(mfr)) &&
smi_info->si_type == SI_BT) smi_info->si_type == SI_BT)
register_xaction_notifier(&dell_poweredge_bt_xaction_notifier); register_xaction_notifier(&dell_poweredge_bt_xaction_notifier);
} }
...@@ -2358,10 +2355,36 @@ static int try_smi_init(struct smi_info *new_smi) ...@@ -2358,10 +2355,36 @@ static int try_smi_init(struct smi_info *new_smi)
new_smi->thread = kthread_run(ipmi_thread, new_smi, new_smi->thread = kthread_run(ipmi_thread, new_smi,
"kipmi%d", new_smi->intf_num); "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, rv = ipmi_register_smi(&handlers,
new_smi, new_smi,
ipmi_version_major(&new_smi->device_id), &new_smi->device_id,
ipmi_version_minor(&new_smi->device_id), new_smi->dev,
new_smi->slave_addr, new_smi->slave_addr,
&(new_smi->intf)); &(new_smi->intf));
if (rv) { if (rv) {
...@@ -2425,6 +2448,11 @@ static int try_smi_init(struct smi_info *new_smi) ...@@ -2425,6 +2448,11 @@ static int try_smi_init(struct smi_info *new_smi)
if (new_smi->io_cleanup) if (new_smi->io_cleanup)
new_smi->io_cleanup(new_smi); new_smi->io_cleanup(new_smi);
if (new_smi->dev_registered)
platform_device_unregister(new_smi->pdev);
kfree(new_smi);
up(&smi_infos_lock); up(&smi_infos_lock);
return rv; return rv;
...@@ -2434,11 +2462,22 @@ static __devinit int init_ipmi_si(void) ...@@ -2434,11 +2462,22 @@ static __devinit int init_ipmi_si(void)
{ {
int i; int i;
char *str; char *str;
int rv;
if (initialized) if (initialized)
return 0; return 0;
initialized = 1; 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. */ /* Parse out the si_type string into its components. */
str = si_type_str; str = si_type_str;
if (*str != '\0') { if (*str != '\0') {
...@@ -2549,6 +2588,11 @@ static void __devexit cleanup_one_si(struct smi_info *to_clean) ...@@ -2549,6 +2588,11 @@ static void __devexit cleanup_one_si(struct smi_info *to_clean)
to_clean->addr_source_cleanup(to_clean); to_clean->addr_source_cleanup(to_clean);
if (to_clean->io_cleanup) if (to_clean->io_cleanup)
to_clean->io_cleanup(to_clean); 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) static __exit void cleanup_ipmi_si(void)
...@@ -2566,6 +2610,8 @@ static __exit void cleanup_ipmi_si(void) ...@@ -2566,6 +2610,8 @@ static __exit void cleanup_ipmi_si(void)
list_for_each_entry_safe(e, tmp_e, &smi_infos, link) list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
cleanup_one_si(e); cleanup_one_si(e);
up(&smi_infos_lock); up(&smi_infos_lock);
driver_unregister(&ipmi_driver);
} }
module_exit(cleanup_ipmi_si); module_exit(cleanup_ipmi_si);
......
...@@ -996,7 +996,7 @@ static struct notifier_block wdog_panic_notifier = { ...@@ -996,7 +996,7 @@ static struct notifier_block wdog_panic_notifier = {
}; };
static void ipmi_new_smi(int if_num) static void ipmi_new_smi(int if_num, struct device *device)
{ {
ipmi_register_watchdog(if_num); ipmi_register_watchdog(if_num);
} }
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/ipmi_msgdefs.h> #include <linux/ipmi_msgdefs.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/device.h>
/* /*
* This file describes an interface to an IPMI driver. You have to * This file describes an interface to an IPMI driver. You have to
...@@ -397,7 +398,7 @@ struct ipmi_smi_watcher ...@@ -397,7 +398,7 @@ struct ipmi_smi_watcher
the watcher list. So you can add and remove users from the the watcher list. So you can add and remove users from the
IPMI interface, send messages, etc., but you cannot add IPMI interface, send messages, etc., but you cannot add
or remove SMI watchers or SMI interfaces. */ or remove SMI watchers or SMI interfaces. */
void (*new_smi)(int if_num); void (*new_smi)(int if_num, struct device *dev);
void (*smi_gone)(int if_num); void (*smi_gone)(int if_num);
}; };
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#define IPMI_NETFN_APP_RESPONSE 0x07 #define IPMI_NETFN_APP_RESPONSE 0x07
#define IPMI_GET_DEVICE_ID_CMD 0x01 #define IPMI_GET_DEVICE_ID_CMD 0x01
#define IPMI_CLEAR_MSG_FLAGS_CMD 0x30 #define IPMI_CLEAR_MSG_FLAGS_CMD 0x30
#define IPMI_GET_DEVICE_GUID_CMD 0x08
#define IPMI_GET_MSG_FLAGS_CMD 0x31 #define IPMI_GET_MSG_FLAGS_CMD 0x31
#define IPMI_SEND_MSG_CMD 0x34 #define IPMI_SEND_MSG_CMD 0x34
#define IPMI_GET_MSG_CMD 0x33 #define IPMI_GET_MSG_CMD 0x33
......
...@@ -37,6 +37,9 @@ ...@@ -37,6 +37,9 @@
#include <linux/ipmi_msgdefs.h> #include <linux/ipmi_msgdefs.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/ipmi_smi.h>
/* This files describes the interface for IPMI system management interface /* This files describes the interface for IPMI system management interface
drivers to bind into the IPMI message handler. */ drivers to bind into the IPMI message handler. */
...@@ -113,12 +116,52 @@ struct ipmi_smi_handlers ...@@ -113,12 +116,52 @@ struct ipmi_smi_handlers
void (*dec_usecount)(void *send_info); void (*dec_usecount)(void *send_info);
}; };
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 int manufacturer_id;
unsigned int product_id;
unsigned char aux_firmware_revision[4];
unsigned int aux_firmware_revision_set : 1;
};
#define ipmi_version_major(v) ((v)->ipmi_version & 0xf)
#define ipmi_version_minor(v) ((v)->ipmi_version >> 4)
/* Take a pointer to a raw data buffer and a length and extract device
id information from it. The first byte of data must point to the
byte from the get device id response after the completion code.
The caller is responsible for making sure the length is at least
11 and the command completed without error. */
static inline void ipmi_demangle_device_id(unsigned char *data,
unsigned int data_len,
struct ipmi_device_id *id)
{
id->device_id = data[0];
id->device_revision = data[1];
id->firmware_revision_1 = data[2];
id->firmware_revision_2 = data[3];
id->ipmi_version = data[4];
id->additional_device_support = data[5];
id->manufacturer_id = data[6] | (data[7] << 8) | (data[8] << 16);
id->product_id = data[9] | (data[10] << 8);
if (data_len >= 15) {
memcpy(id->aux_firmware_revision, data+11, 4);
id->aux_firmware_revision_set = 1;
} else
id->aux_firmware_revision_set = 0;
}
/* Add a low-level interface to the IPMI driver. Note that if the /* Add a low-level interface to the IPMI driver. Note that if the
interface doesn't know its slave address, it should pass in zero. */ interface doesn't know its slave address, it should pass in zero. */
int ipmi_register_smi(struct ipmi_smi_handlers *handlers, int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
void *send_info, void *send_info,
unsigned char version_major, struct ipmi_device_id *device_id,
unsigned char version_minor, struct device *dev,
unsigned char slave_addr, unsigned char slave_addr,
ipmi_smi_t *intf); ipmi_smi_t *intf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment