|
@@ -162,6 +162,28 @@ struct ipmi_proc_entry
|
|
};
|
|
};
|
|
#endif
|
|
#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_IPMB_NUM_SEQ 64
|
|
#define IPMI_MAX_CHANNELS 16
|
|
#define IPMI_MAX_CHANNELS 16
|
|
struct ipmi_smi
|
|
struct ipmi_smi
|
|
@@ -178,9 +200,8 @@ struct ipmi_smi
|
|
/* Used for wake ups at startup. */
|
|
/* Used for wake ups at startup. */
|
|
wait_queue_head_t waitq;
|
|
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. */
|
|
/* This is the lower-layer's sender routine. */
|
|
struct ipmi_smi_handlers *handlers;
|
|
struct ipmi_smi_handlers *handlers;
|
|
@@ -194,6 +215,9 @@ struct ipmi_smi
|
|
struct ipmi_proc_entry *proc_entries;
|
|
struct ipmi_proc_entry *proc_entries;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
+ /* Driver-model device for the system interface. */
|
|
|
|
+ struct device *si_dev;
|
|
|
|
+
|
|
/* A table of sequence numbers for this interface. We use the
|
|
/* A table of sequence numbers for this interface. We use the
|
|
sequence numbers for IPMB messages that go out of the
|
|
sequence numbers for IPMB messages that go out of the
|
|
interface to match them up with their responses. A routine
|
|
interface to match them up with their responses. A routine
|
|
@@ -312,6 +336,7 @@ struct ipmi_smi
|
|
/* Events that were received with the proper format. */
|
|
/* Events that were received with the proper format. */
|
|
unsigned int events;
|
|
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
|
|
/* 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
|
|
* free entry, either, primarily used at creation and deletion time so
|
|
@@ -320,6 +345,15 @@ struct ipmi_smi
|
|
#define IPMI_INVALID_INTERFACE(i) (((i) == NULL) \
|
|
#define IPMI_INVALID_INTERFACE(i) (((i) == NULL) \
|
|
|| (i == IPMI_INVALID_INTERFACE_ENTRY))
|
|
|| (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
|
|
#define MAX_IPMI_INTERFACES 4
|
|
static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES];
|
|
static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES];
|
|
|
|
|
|
@@ -393,7 +427,7 @@ int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
|
|
if (IPMI_INVALID_INTERFACE(intf))
|
|
if (IPMI_INVALID_INTERFACE(intf))
|
|
continue;
|
|
continue;
|
|
spin_unlock_irqrestore(&interfaces_lock, flags);
|
|
spin_unlock_irqrestore(&interfaces_lock, flags);
|
|
- watcher->new_smi(i);
|
|
|
|
|
|
+ watcher->new_smi(i, intf->si_dev);
|
|
spin_lock_irqsave(&interfaces_lock, flags);
|
|
spin_lock_irqsave(&interfaces_lock, flags);
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&interfaces_lock, flags);
|
|
spin_unlock_irqrestore(&interfaces_lock, flags);
|
|
@@ -409,14 +443,14 @@ int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher)
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
static void
|
|
-call_smi_watchers(int i)
|
|
|
|
|
|
+call_smi_watchers(int i, struct device *dev)
|
|
{
|
|
{
|
|
struct ipmi_smi_watcher *w;
|
|
struct ipmi_smi_watcher *w;
|
|
|
|
|
|
down_read(&smi_watchers_sem);
|
|
down_read(&smi_watchers_sem);
|
|
list_for_each_entry(w, &smi_watchers, link) {
|
|
list_for_each_entry(w, &smi_watchers, link) {
|
|
if (try_module_get(w->owner)) {
|
|
if (try_module_get(w->owner)) {
|
|
- w->new_smi(i);
|
|
|
|
|
|
+ w->new_smi(i, dev);
|
|
module_put(w->owner);
|
|
module_put(w->owner);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -844,8 +878,8 @@ void ipmi_get_version(ipmi_user_t user,
|
|
unsigned char *major,
|
|
unsigned char *major,
|
|
unsigned char *minor)
|
|
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,
|
|
int ipmi_set_my_address(ipmi_user_t user,
|
|
@@ -1553,7 +1587,8 @@ static int version_file_read_proc(char *page, char **start, off_t off,
|
|
ipmi_smi_t intf = data;
|
|
ipmi_smi_t intf = data;
|
|
|
|
|
|
return sprintf(out, "%d.%d\n",
|
|
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,
|
|
static int stat_file_read_proc(char *page, char **start, off_t off,
|
|
@@ -1712,6 +1747,470 @@ static void remove_proc_entries(ipmi_smi_t smi)
|
|
#endif /* CONFIG_PROC_FS */
|
|
#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
|
|
static int
|
|
send_channel_info_cmd(ipmi_smi_t intf, int chan)
|
|
send_channel_info_cmd(ipmi_smi_t intf, int chan)
|
|
{
|
|
{
|
|
@@ -1804,8 +2303,8 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
|
|
|
|
|
|
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,
|
|
|
|
- unsigned char version_minor,
|
|
|
|
|
|
+ struct ipmi_device_id *device_id,
|
|
|
|
+ struct device *si_dev,
|
|
unsigned char slave_addr,
|
|
unsigned char slave_addr,
|
|
ipmi_smi_t *new_intf)
|
|
ipmi_smi_t *new_intf)
|
|
{
|
|
{
|
|
@@ -1813,7 +2312,11 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
|
|
int rv;
|
|
int rv;
|
|
ipmi_smi_t intf;
|
|
ipmi_smi_t intf;
|
|
unsigned long flags;
|
|
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
|
|
/* Make sure the driver is actually initialized, this handles
|
|
problems with initialization order. */
|
|
problems with initialization order. */
|
|
@@ -1831,10 +2334,15 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
|
|
if (!intf)
|
|
if (!intf)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
memset(intf, 0, sizeof(*intf));
|
|
memset(intf, 0, sizeof(*intf));
|
|
|
|
+ intf->bmc = kzalloc(sizeof(*intf->bmc), GFP_KERNEL);
|
|
|
|
+ if (!intf->bmc) {
|
|
|
|
+ kfree(intf);
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ }
|
|
intf->intf_num = -1;
|
|
intf->intf_num = -1;
|
|
kref_init(&intf->refcount);
|
|
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++) {
|
|
for (j = 0; j < IPMI_MAX_CHANNELS; j++) {
|
|
intf->channels[j].address = IPMI_BMC_SLAVE_ADDR;
|
|
intf->channels[j].address = IPMI_BMC_SLAVE_ADDR;
|
|
intf->channels[j].lun = 2;
|
|
intf->channels[j].lun = 2;
|
|
@@ -1884,6 +2392,8 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
|
|
caller before sending any messages with it. */
|
|
caller before sending any messages with it. */
|
|
*new_intf = intf;
|
|
*new_intf = intf;
|
|
|
|
|
|
|
|
+ get_guid(intf);
|
|
|
|
+
|
|
if ((version_major > 1)
|
|
if ((version_major > 1)
|
|
|| ((version_major == 1) && (version_minor >= 5)))
|
|
|| ((version_major == 1) && (version_minor >= 5)))
|
|
{
|
|
{
|
|
@@ -1898,6 +2408,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
|
|
/* Wait for the channel info to be read. */
|
|
/* Wait for the channel info to be read. */
|
|
wait_event(intf->waitq,
|
|
wait_event(intf->waitq,
|
|
intf->curr_channel >= IPMI_MAX_CHANNELS);
|
|
intf->curr_channel >= IPMI_MAX_CHANNELS);
|
|
|
|
+ intf->null_user_handler = NULL;
|
|
} else {
|
|
} else {
|
|
/* Assume a single IPMB channel at zero. */
|
|
/* Assume a single IPMB channel at zero. */
|
|
intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
|
|
intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
|
|
@@ -1907,6 +2418,8 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
|
|
if (rv == 0)
|
|
if (rv == 0)
|
|
rv = add_proc_entries(intf, i);
|
|
rv = add_proc_entries(intf, i);
|
|
|
|
|
|
|
|
+ rv = ipmi_bmc_register(intf);
|
|
|
|
+
|
|
out:
|
|
out:
|
|
if (rv) {
|
|
if (rv) {
|
|
if (intf->proc_dir)
|
|
if (intf->proc_dir)
|
|
@@ -1921,7 +2434,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
|
|
spin_lock_irqsave(&interfaces_lock, flags);
|
|
spin_lock_irqsave(&interfaces_lock, flags);
|
|
ipmi_interfaces[i] = intf;
|
|
ipmi_interfaces[i] = intf;
|
|
spin_unlock_irqrestore(&interfaces_lock, flags);
|
|
spin_unlock_irqrestore(&interfaces_lock, flags);
|
|
- call_smi_watchers(i);
|
|
|
|
|
|
+ call_smi_watchers(i, intf->si_dev);
|
|
}
|
|
}
|
|
|
|
|
|
return rv;
|
|
return rv;
|
|
@@ -1933,6 +2446,8 @@ int ipmi_unregister_smi(ipmi_smi_t intf)
|
|
struct ipmi_smi_watcher *w;
|
|
struct ipmi_smi_watcher *w;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
+ ipmi_bmc_unregister(intf);
|
|
|
|
+
|
|
spin_lock_irqsave(&interfaces_lock, flags);
|
|
spin_lock_irqsave(&interfaces_lock, flags);
|
|
for (i = 0; i < MAX_IPMI_INTERFACES; i++) {
|
|
for (i = 0; i < MAX_IPMI_INTERFACES; i++) {
|
|
if (ipmi_interfaces[i] == intf) {
|
|
if (ipmi_interfaces[i] == intf) {
|
|
@@ -3196,10 +3711,17 @@ static struct notifier_block panic_block = {
|
|
static int ipmi_init_msghandler(void)
|
|
static int ipmi_init_msghandler(void)
|
|
{
|
|
{
|
|
int i;
|
|
int i;
|
|
|
|
+ int rv;
|
|
|
|
|
|
if (initialized)
|
|
if (initialized)
|
|
return 0;
|
|
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 "
|
|
printk(KERN_INFO "ipmi message handler version "
|
|
IPMI_DRIVER_VERSION "\n");
|
|
IPMI_DRIVER_VERSION "\n");
|
|
|
|
|
|
@@ -3256,6 +3778,8 @@ static __exit void cleanup_ipmi(void)
|
|
remove_proc_entry(proc_ipmi_root->name, &proc_root);
|
|
remove_proc_entry(proc_ipmi_root->name, &proc_root);
|
|
#endif /* CONFIG_PROC_FS */
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
|
|
|
|
+ driver_unregister(&ipmidriver);
|
|
|
|
+
|
|
initialized = 0;
|
|
initialized = 0;
|
|
|
|
|
|
/* Check for buffer leaks. */
|
|
/* Check for buffer leaks. */
|