📄 ipmi_si_intf.c
字号:
info->io.regsize = DEFAULT_REGSPACING; info->io.regshift = regshifts[intf_num]; irqs[intf_num] = ipmi_data.irq; *new_info = info; printk("ipmi_si: Found SMBIOS-specified state machine at %s" " address 0x%lx\n", io_type, (unsigned long)ipmi_data.base_addr); return 0;}#endif /* CONFIG_X86 */#ifdef CONFIG_PCI#define PCI_ERMC_CLASSCODE 0x0C0700#define PCI_HP_VENDOR_ID 0x103C#define PCI_MMC_DEVICE_ID 0x121A#define PCI_MMC_ADDR_CW 0x10/* Avoid more than one attempt to probe pci smic. */static int pci_smic_checked = 0;static int find_pci_smic(int intf_num, struct smi_info **new_info){ struct smi_info *info; int error; struct pci_dev *pci_dev = NULL; u16 base_addr; int fe_rmc = 0; if (pci_smic_checked) return -ENODEV; pci_smic_checked = 1; if ((pci_dev = pci_get_device(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID, NULL))) ; else if ((pci_dev = pci_get_class(PCI_ERMC_CLASSCODE, NULL)) && pci_dev->subsystem_vendor == PCI_HP_VENDOR_ID) fe_rmc = 1; else return -ENODEV; error = pci_read_config_word(pci_dev, PCI_MMC_ADDR_CW, &base_addr); if (error) { pci_dev_put(pci_dev); printk(KERN_ERR "ipmi_si: pci_read_config_word() failed (%d).\n", error); return -ENODEV; } /* Bit 0: 1 specifies programmed I/O, 0 specifies memory mapped I/O */ if (!(base_addr & 0x0001)) { pci_dev_put(pci_dev); printk(KERN_ERR "ipmi_si: memory mapped I/O not supported for PCI" " smic.\n"); return -ENODEV; } base_addr &= 0xFFFE; if (!fe_rmc) /* Data register starts at base address + 1 in eRMC */ ++base_addr; if (!is_new_interface(-1, IPMI_IO_ADDR_SPACE, base_addr)) { pci_dev_put(pci_dev); return -ENODEV; } info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) { pci_dev_put(pci_dev); printk(KERN_ERR "ipmi_si: Could not allocate SI data (5)\n"); return -ENOMEM; } memset(info, 0, sizeof(*info)); info->io_setup = port_setup; ports[intf_num] = base_addr; info->io.info = &(ports[intf_num]); info->io.regspacing = regspacings[intf_num]; if (!info->io.regspacing) info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = DEFAULT_REGSPACING; info->io.regshift = regshifts[intf_num]; *new_info = info; irqs[intf_num] = pci_dev->irq; si_type[intf_num] = "smic"; 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) { 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 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. */ smi_info->ipmi_si_dev_rev = resp[4] & 0xf; smi_info->ipmi_si_fw_rev_major = resp[5] & 0x7f; smi_info->ipmi_si_fw_rev_minor = resp[6]; smi_info->ipmi_version_major = resp[7] & 0xf; smi_info->ipmi_version_minor = resp[7] >> 4; 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));}/* 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_INTERPRETER 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; /* 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; new_smi->timer_stopped = 0; new_smi->stop_operation = 0; /* 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)); rv = ipmi_register_smi(&handlers, new_smi, new_smi->ipmi_version_major, new_smi->ipmi_version_minor, &(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: new_smi->stop_operation = 1; /* Wait for the timer to stop. This avoids problems with race conditions removing the timer here. */ while (!new_smi->timer_stopped) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); } out_err: if (new_smi->intf) ipmi_unregister_smi(new_smi->intf); new_smi->irq_cleanup(new_smi); /* Wait until we know that we are out of any interrupt handlers might have been running before we freed the interrupt. */ synchronize_kernel(); if (new_smi->si_sm) { if (new_smi->handlers) new_smi->handlers->cleanup(new_smi->si_sm); kfree(new_smi->si_sm); } new_smi->io_cleanup(new_smi); return rv;}static __init int init_ipmi_si(void){ int rv = 0; int pos = 0; int i; char *str; if (initialized) return 0; initialized = 1; /* Parse out the si_type string into its components. */ str = si_type_str; if (*str != '\0') { for (i=0; (i<SI_MAX_PARMS) && (*str != '\0'); i++) { si_type[i] = str; str = strchr(str, ','); if (str) { *str = '\0'; str++; } else { break; } } } printk(KERN_INFO "IPMI System Interface driver version " IPMI_SI_VERSION); if (kcs_smi_handlers.version) printk(", KCS version %s", kcs_smi_handlers.version); if (smic_smi_handlers.version) printk(", SMIC version %s", smic_smi_handlers.version); if (bt_smi_handlers.version) printk(", BT version %s", bt_smi_handlers.version); printk("\n"); rv = init_one_smi(0, &(smi_infos[pos])); if (rv && !ports[0] && si_trydefaults) { /* If we are trying defaults and the initial port is not set, then set it. */ si_type[0] = "kcs"; ports[0] = DEFAULT_KCS_IO_PORT; rv = init_one_smi(0, &(smi_infos[pos])); if (rv) { /* No KCS - try SMIC */ si_type[0] = "smic"; ports[0] = DEFAULT_SMIC_IO_PORT; rv = init_one_smi(0, &(smi_infos[pos])); } if (rv) { /* No SMIC - try BT */ si_type[0] = "bt"; ports[0] = DEFAULT_BT_IO_PORT; rv = init_one_smi(0, &(smi_infos[pos])); } } if (rv == 0) pos++; for (i=1; i < SI_MAX_PARMS; i++) { rv = init_one_smi(i, &(smi_infos[pos])); if (rv == 0) pos++; } if (smi_infos[0] == NULL) { printk("ipmi_si: Unable to find any System Interface(s)\n"); return -ENODEV; } return 0;}module_init(init_ipmi_si);static void __exit cleanup_one_si(struct smi_info *to_clean){ int rv; unsigned long flags; if (! to_clean) return; /* Tell the timer and interrupt handlers that we are shutting down. */ spin_lock_irqsave(&(to_clean->si_lock), flags); spin_lock(&(to_clean->msg_lock)); to_clean->stop_operation = 1; to_clean->irq_cleanup(to_clean); spin_unlock(&(to_clean->msg_lock)); spin_unlock_irqrestore(&(to_clean->si_lock), flags); /* Wait until we know that we are out of any interrupt handlers might have been running before we freed the interrupt. */ synchronize_kernel(); /* Wait for the timer to stop. This avoids problems with race conditions removing the timer here. */ while (!to_clean->timer_stopped) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); } /* Interrupts and timeouts are stopped, now make sure the interface is in a clean state. */ while ((to_clean->curr_msg) || (to_clean->si_state != SI_NORMAL)) { poll(to_clean); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); } rv = ipmi_unregister_smi(to_clean->intf); if (rv) { printk(KERN_ERR "ipmi_si: Unable to unregister device: errno=%d\n", rv); } to_clean->handlers->cleanup(to_clean->si_sm); kfree(to_clean->si_sm); to_clean->io_cleanup(to_clean);}static __exit void cleanup_ipmi_si(void){ int i; if (!initialized) return; for (i=0; i<SI_MAX_DRIVERS; i++) { cleanup_one_si(smi_infos[i]); }}module_exit(cleanup_ipmi_si);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -