📄 ipmi_si_intf.c
字号:
printk("ipmi_si: Found PCI SMIC at I/O address 0x%lx\n", (long unsigned int) base_addr); pci_dev_put(pci_dev); return 0;}#endif /* CONFIG_PCI */static int try_init_plug_and_play(int intf_num, struct smi_info **new_info){#ifdef CONFIG_PCI if (find_pci_smic(intf_num, new_info) == 0) return 0;#endif /* Include other methods here. */ return -ENODEV;}static int try_get_dev_id(struct smi_info *smi_info){ unsigned char msg[2]; unsigned char *resp; unsigned long resp_len; enum si_sm_result smi_result; int rv = 0; resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); if (! resp) return -ENOMEM; /* Do a Get Device ID command, since it comes back with some useful info. */ msg[0] = IPMI_NETFN_APP_REQUEST << 2; msg[1] = IPMI_GET_DEVICE_ID_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 || smi_result == SI_SM_CALL_WITH_TICK_DELAY) { schedule_timeout_uninterruptible(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 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 < 6) { /* 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. */ memcpy(&smi_info->device_id, &resp[3], min_t(unsigned long, resp_len-3, sizeof(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 {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; 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 && 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; const char mfr[3]=DELL_IANA_MFR_ID; if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr)) && 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->thread != NULL && smi_info->thread != ERR_PTR(-ENOMEM)) kthread_stop(smi_info->thread); del_timer_sync(&smi_info->si_timer);}/* Returns 0 if initialized, or negative on an error. */static int init_one_smi(int intf_num, struct smi_info **smi){ int rv; struct smi_info *new_smi; rv = try_init_mem(intf_num, &new_smi); if (rv) rv = try_init_port(intf_num, &new_smi);#ifdef CONFIG_ACPI if (rv && si_trydefaults) rv = try_init_acpi(intf_num, &new_smi);#endif#ifdef CONFIG_X86 if (rv && si_trydefaults) rv = try_init_smbios(intf_num, &new_smi);#endif if (rv && si_trydefaults) rv = try_init_plug_and_play(intf_num, &new_smi); if (rv) return rv; /* 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; if (! new_smi->irq_setup) { new_smi->irq = irqs[intf_num]; new_smi->irq_setup = std_irq_setup; new_smi->irq_cleanup = std_irq_cleanup; } /* Default to KCS if no type is specified. */ if (si_type[intf_num] == NULL) { if (si_trydefaults) si_type[intf_num] = "kcs"; else { rv = -EINVAL; goto out_err; } } /* Set up the state machine to use. */ if (strcmp(si_type[intf_num], "kcs") == 0) { new_smi->handlers = &kcs_smi_handlers; new_smi->si_type = SI_KCS; } else if (strcmp(si_type[intf_num], "smic") == 0) { new_smi->handlers = &smic_smi_handlers; new_smi->si_type = SI_SMIC; } else if (strcmp(si_type[intf_num], "bt") == 0) { new_smi->handlers = &bt_smi_handlers; new_smi->si_type = SI_BT; } else { /* 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)) { rv = -ENODEV; goto out_err; } /* 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) goto out_err; setup_oem_data_handler(new_smi); setup_xaction_handlers(new_smi); /* Try to claim any interrupts. */ 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 = intf_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; /* The ipmi_register_smi() code does some operations to determine the channel information, so we must be ready to handle operations before it is called. This means we have to stop the timer if we get an error after this point. */ init_timer(&(new_smi->si_timer)); new_smi->si_timer.data = (long) new_smi; new_smi->si_timer.function = smi_timeout; new_smi->last_timeout_jiffies = jiffies; new_smi->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; add_timer(&(new_smi->si_timer)); if (new_smi->si_type != SI_BT) new_smi->thread = kthread_run(ipmi_thread, new_smi, "kipmi%d", new_smi->intf_num); rv = ipmi_register_smi(&handlers, new_smi, ipmi_version_major(&new_smi->device_id), ipmi_version_minor(&new_smi->device_id), new_smi->slave_addr, &(new_smi->intf)); 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; } *smi = new_smi; printk(" IPMI %s interface initialized\n", si_type[intf_num]); return 0; out_err_stop_timer: atomic_inc(&new_smi->stop_operation); wait_for_timer_and_thread(new_smi); out_err: if (new_smi->intf) ipmi_unregister_smi(new_smi->intf); new_smi->irq_cleanup(new_smi); /* Wait unt
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -