ipmi_si_intf.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 2,443 行 · 第 1/5 页
C
2,443 行
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 out; } /* Otherwise, we got some data. */ resp_len = smi_info->handlers->get_result(smi_info->si_sm, resp, IPMI_MAX_MSG_LENGTH); if (resp_len < 14) { /* That's odd, it should be longer. */ rv = -EINVAL; goto out; } if ((resp[1] != IPMI_GET_DEVICE_ID_CMD) || (resp[2] != 0)) { /* That's odd, it shouldn't be able to fail. */ rv = -EINVAL; goto out; } /* Record info from the get device id, in case we need it. */ ipmi_demangle_device_id(resp+3, resp_len-3, &smi_info->device_id); out: kfree(resp); return rv;}static int type_file_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){ char *out = (char *) page; struct smi_info *smi = data; switch (smi->si_type) { case SI_KCS: return sprintf(out, "kcs\n"); case SI_SMIC: return sprintf(out, "smic\n"); case SI_BT: return sprintf(out, "bt\n"); default: return 0; }}static int stat_file_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){ char *out = (char *) page; struct smi_info *smi = data; out += sprintf(out, "interrupts_enabled: %d\n", smi->irq && !smi->interrupt_disabled); out += sprintf(out, "short_timeouts: %ld\n", smi->short_timeouts); out += sprintf(out, "long_timeouts: %ld\n", smi->long_timeouts); out += sprintf(out, "timeout_restarts: %ld\n", smi->timeout_restarts); out += sprintf(out, "idles: %ld\n", smi->idles); out += sprintf(out, "interrupts: %ld\n", smi->interrupts); out += sprintf(out, "attentions: %ld\n", smi->attentions); out += sprintf(out, "flag_fetches: %ld\n", smi->flag_fetches); out += sprintf(out, "hosed_count: %ld\n", smi->hosed_count); out += sprintf(out, "complete_transactions: %ld\n", smi->complete_transactions); out += sprintf(out, "events: %ld\n", smi->events); out += sprintf(out, "watchdog_pretimeouts: %ld\n", smi->watchdog_pretimeouts); out += sprintf(out, "incoming_messages: %ld\n", smi->incoming_messages); return (out - ((char *) page));}/* * oem_data_avail_to_receive_msg_avail * @info - smi_info structure with msg_flags set * * Converts flags from OEM_DATA_AVAIL to RECEIVE_MSG_AVAIL * Returns 1 indicating need to re-run handle_flags(). */static int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info){ smi_info->msg_flags = ((smi_info->msg_flags & ~OEM_DATA_AVAIL) | RECEIVE_MSG_AVAIL); return 1;}/* * setup_dell_poweredge_oem_data_handler * @info - smi_info.device_id must be populated * * Systems that match, but have firmware version < 1.40 may assert * OEM0_DATA_AVAIL on their own, without being told via Set Flags that * it's safe to do so. Such systems will de-assert OEM1_DATA_AVAIL * upon receipt of IPMI_GET_MSG_CMD, so we should treat these flags * as RECEIVE_MSG_AVAIL instead. * * As Dell has no plans to release IPMI 1.5 firmware that *ever* * assert the OEM[012] bits, and if it did, the driver would have to * change to handle that properly, we don't actually check for the * firmware version. * Device ID = 0x20 BMC on PowerEdge 8G servers * Device Revision = 0x80 * Firmware Revision1 = 0x01 BMC version 1.40 * Firmware Revision2 = 0x40 BCD encoded * IPMI Version = 0x51 IPMI 1.5 * Manufacturer ID = A2 02 00 Dell IANA * * Additionally, PowerEdge systems with IPMI < 1.5 may also assert * OEM0_DATA_AVAIL and needs to be treated as RECEIVE_MSG_AVAIL. * */#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 0x0002a2static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info){ struct ipmi_device_id *id = &smi_info->device_id; 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) { smi_info->oem_data_avail_handler = oem_data_avail_to_receive_msg_avail; } else if (ipmi_version_major(id) < 1 || (ipmi_version_major(id) == 1 && ipmi_version_minor(id) < 5)) { smi_info->oem_data_avail_handler = oem_data_avail_to_receive_msg_avail; } }}#define CANNOT_RETURN_REQUESTED_LENGTH 0xCAstatic void return_hosed_msg_badsize(struct smi_info *smi_info){ struct ipmi_smi_msg *msg = smi_info->curr_msg; /* Make it a reponse */ msg->rsp[0] = msg->data[0] | 4; msg->rsp[1] = msg->data[1]; msg->rsp[2] = CANNOT_RETURN_REQUESTED_LENGTH; msg->rsp_size = 3; smi_info->curr_msg = NULL; deliver_recv_msg(smi_info, msg);}/* * dell_poweredge_bt_xaction_handler * @info - smi_info.device_id must be populated * * Dell PowerEdge servers with the BT interface (x6xx and 1750) will * not respond to a Get SDR command if the length of the data * requested is exactly 0x3A, which leads to command timeouts and no * data returned. This intercepts such commands, and causes userspace * callers to try again with a different-sized buffer, which succeeds. */#define STORAGE_NETFN 0x0A#define STORAGE_CMD_GET_SDR 0x23static int dell_poweredge_bt_xaction_handler(struct notifier_block *self, unsigned long unused, void *in){ struct smi_info *smi_info = in; unsigned char *data = smi_info->curr_msg->data; unsigned int size = smi_info->curr_msg->data_size; if (size >= 8 && (data[0]>>2) == STORAGE_NETFN && data[1] == STORAGE_CMD_GET_SDR && data[7] == 0x3A) { return_hosed_msg_badsize(smi_info); return NOTIFY_STOP; } return NOTIFY_DONE;}static struct notifier_block dell_poweredge_bt_xaction_notifier = { .notifier_call = dell_poweredge_bt_xaction_handler,};/* * setup_dell_poweredge_bt_xaction_handler * @info - smi_info.device_id must be filled in already * * Fills in smi_info.device_id.start_transaction_pre_hook * when we know what function to use there. */static voidsetup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info){ struct ipmi_device_id *id = &smi_info->device_id; if (id->manufacturer_id == DELL_IANA_MFR_ID && smi_info->si_type == SI_BT) register_xaction_notifier(&dell_poweredge_bt_xaction_notifier);}/* * setup_oem_data_handler * @info - smi_info.device_id must be filled in already * * Fills in smi_info.device_id.oem_data_available_handler * when we know what function to use there. */static void setup_oem_data_handler(struct smi_info *smi_info){ setup_dell_poweredge_oem_data_handler(smi_info);}static void setup_xaction_handlers(struct smi_info *smi_info){ setup_dell_poweredge_bt_xaction_handler(smi_info);}static inline void wait_for_timer_and_thread(struct smi_info *smi_info){ if (smi_info->intf) { /* The timer and thread are only running if the interface has been started up and registered. */ if (smi_info->thread != NULL) kthread_stop(smi_info->thread); del_timer_sync(&smi_info->si_timer); }}static __devinitdata struct ipmi_default_vals{ int type; int port;} ipmi_defaults[] ={ { .type = SI_KCS, .port = 0xca2 }, { .type = SI_SMIC, .port = 0xca9 }, { .type = SI_BT, .port = 0xe4 }, { .port = 0 }};static __devinit void default_find_bmc(void){ struct smi_info *info; int i; for (i = 0; ; i++) { if (!ipmi_defaults[i].port) break; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return; info->addr_source = NULL; info->si_type = ipmi_defaults[i].type; info->io_setup = port_setup; info->io.addr_data = ipmi_defaults[i].port; info->io.addr_type = IPMI_IO_ADDR_SPACE; info->io.addr = NULL; info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = DEFAULT_REGSPACING; info->io.regshift = 0; if (try_smi_init(info) == 0) { /* Found one... */ printk(KERN_INFO "ipmi_si: Found default %s state" " machine at %s address 0x%lx\n", si_to_str[info->si_type], addr_space_to_str[info->io.addr_type], info->io.addr_data); return; } }}static int is_new_interface(struct smi_info *info){ struct smi_info *e; list_for_each_entry(e, &smi_infos, link) { if (e->io.addr_type != info->io.addr_type) continue; if (e->io.addr_data == info->io.addr_data) return 0; } return 1;}static int try_smi_init(struct smi_info *new_smi){ int rv; if (new_smi->addr_source) { printk(KERN_INFO "ipmi_si: Trying %s-specified %s state" " machine at %s address 0x%lx, slave address 0x%x," " irq %d\n", new_smi->addr_source, si_to_str[new_smi->si_type], addr_space_to_str[new_smi->io.addr_type], new_smi->io.addr_data, new_smi->slave_addr, new_smi->irq); } mutex_lock(&smi_infos_lock); if (!is_new_interface(new_smi)) { printk(KERN_WARNING "ipmi_si: duplicate interface\n"); rv = -EBUSY; goto out_err; } /* So we know not to free it unless we have allocated one. */ new_smi->intf = NULL; new_smi->si_sm = NULL; new_smi->handlers = NULL; switch (new_smi->si_type) { case SI_KCS: new_smi->handlers = &kcs_smi_handlers; break; case SI_SMIC: new_smi->handlers = &smic_smi_handlers; break; case SI_BT: new_smi->handlers = &bt_smi_handlers; break; default: /* No support for anything else yet. */ rv = -EIO; goto out_err; } /* Allocate the state machine's data and initialize it. */ new_smi->si_sm = kmalloc(new_smi->handlers->size(), GFP_KERNEL); if (!new_smi->si_sm) { printk(" Could not allocate state machine memory\n"); rv = -ENOMEM; goto out_err; } new_smi->io_size = new_smi->handlers->init_data(new_smi->si_sm, &new_smi->io); /* Now that we know the I/O size, we can set up the I/O. */ rv = new_smi->io_setup(new_smi); if (rv) { printk(" Could not set up I/O space\n"); goto out_err; } spin_lock_init(&(new_smi->si_lock)); spin_lock_init(&(new_smi->msg_lock)); spin_lock_init(&(new_smi->count_lock)); /* Do low-level detection first. */ if (new_smi->handlers->detect(new_smi->si_sm)) { if (new_smi->addr_source) printk(KERN_INFO "ipmi_si: Interface detection" " failed\n"); rv = -ENODEV; goto out_err; } /* Attempt a get device id command. If it fails, we probably don't have a BMC here. */ rv = try_get_dev_id(new_smi); if (rv) { if (new_smi->addr_source) printk(KERN_INFO "ipmi_si: There appears to be no BMC" " at this location\n"); goto out_err; } setup_oem_data_handler(new_smi); setup_xaction_handlers(new_smi); /* Try to claim any interrupts. */ if (new_smi->irq_setup) new_smi->irq_setup(new_smi); INIT_LIST_HEAD(&(new_smi->xmit_msgs)); INIT_LIST_HEAD(&(new_smi->hp_xmit_msgs)); new_smi->curr_msg = NULL; atomic_set(&new_smi->req_events, 0); new_smi->run_to_completion = 0; new_smi->interrupt_disabled = 0; atomic_set(&new_smi->stop_operation, 0); new_smi->intf_num = smi_num; smi_num++; /* Start clearing the flags before we enable interrupts or the timer to avoid racing with the timer. */ start_clear_flags(new_smi); /* IRQ is defined to be set when non-zero. */ if (new_smi->irq) new_smi->si_state = SI_CLEARING_FLAGS_THEN_SET_IRQ; 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; } 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; } new_smi->dev_registered = 1; } rv = ipmi_register_smi(&handlers, new_smi, &new_smi->device_id, new_smi->dev, new_smi->slave_addr); if (rv) { printk(KERN_ERR "ipmi_si: Unable to register device: error %d\n", rv); goto out_err_stop_timer; } rv = ipmi_smi_add_proc_entry(new_smi->intf, "type", type_file_read_proc, NULL, new_smi, THIS_MODULE); if (rv) { printk(KERN_ERR "ipmi_si: Unable to create proc entry: %d\n", rv); goto out_err_stop_timer; } rv = ipmi_smi_add_proc_entry(new_smi->intf, "si_stats", stat_file_read_proc, NULL, new_smi, THIS_MODULE); if (rv) { printk(KERN_ERR "ipmi_si: Unable to create proc entry: %d\n", rv); goto out_err_stop_timer; } list_add_tail(&new_smi->link, &smi_infos); mutex_unlock(&smi_infos_lock); printk(" IPMI %s interface init
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?