📄 ipmi_kcs_intf.c
字号:
MODULE_PARM(kcs_addrs, "1-4l");MODULE_PARM(kcs_irqs, "1-4i");MODULE_PARM(kcs_ports, "1-4i");/* Returns 0 if initialized, or negative on an error. */static int init_one_kcs(int kcs_port, int irq, unsigned long kcs_physaddr, struct kcs_info **kcs){ int rv; struct kcs_info *new_kcs; /* Did anything get passed in at all? Both == zero disables the driver. */ if (!(kcs_port || kcs_physaddr)) return -ENODEV; /* Only initialize a port OR a physical address on this call. Also, IRQs can go with either ports or addresses. */ if (kcs_port && kcs_physaddr) return -EINVAL; new_kcs = kmalloc(sizeof(*new_kcs), GFP_KERNEL); if (!new_kcs) { printk(KERN_ERR "ipmi_kcs: out of memory\n"); return -ENOMEM; } /* So we know not to free it unless we have allocated one. */ new_kcs->kcs_sm = NULL; new_kcs->addr = NULL; new_kcs->physaddr = kcs_physaddr; new_kcs->port = kcs_port; if (kcs_port) { if (request_region(kcs_port, 2, DEVICE_NAME) == NULL) { kfree(new_kcs); printk(KERN_ERR "ipmi_kcs: can't reserve port @ 0x%4.4x\n", kcs_port); return -EIO; } } else { if (request_mem_region(kcs_physaddr, 2, DEVICE_NAME) == NULL) { kfree(new_kcs); printk(KERN_ERR "ipmi_kcs: can't reserve memory @ 0x%lx\n", kcs_physaddr); return -EIO; } if ((new_kcs->addr = ioremap(kcs_physaddr, 2)) == NULL) { kfree(new_kcs); printk(KERN_ERR "ipmi_kcs: can't remap memory at 0x%lx\n", kcs_physaddr); return -EIO; } } new_kcs->kcs_sm = kmalloc(kcs_size(), GFP_KERNEL); if (!new_kcs->kcs_sm) { printk(KERN_ERR "ipmi_kcs: out of memory\n"); rv = -ENOMEM; goto out_err; } init_kcs_data(new_kcs->kcs_sm, kcs_port, new_kcs->addr); spin_lock_init(&(new_kcs->kcs_lock)); spin_lock_init(&(new_kcs->msg_lock)); rv = ipmi_kcs_detect_hardware(kcs_port, new_kcs->addr, new_kcs->kcs_sm); if (rv) { if (kcs_port) printk(KERN_ERR "ipmi_kcs: No KCS @ port 0x%4.4x\n", kcs_port); else printk(KERN_ERR "ipmi_kcs: No KCS @ addr 0x%lx\n", kcs_physaddr); goto out_err; } if (irq != 0) { rv = request_irq(irq, kcs_irq_handler, SA_INTERRUPT, DEVICE_NAME, new_kcs); if (rv) { printk(KERN_WARNING "ipmi_kcs: %s unable to claim interrupt %d," " running polled\n", DEVICE_NAME, irq); irq = 0; } } new_kcs->irq = irq; INIT_LIST_HEAD(&(new_kcs->xmit_msgs)); INIT_LIST_HEAD(&(new_kcs->hp_xmit_msgs)); new_kcs->curr_msg = NULL; atomic_set(&new_kcs->req_events, 0); new_kcs->run_to_completion = 0; start_clear_flags(new_kcs); if (irq) { new_kcs->kcs_state = KCS_CLEARING_FLAGS_THEN_SET_IRQ; printk(KERN_INFO "ipmi_kcs: Acquiring BMC @ port=0x%x irq=%d\n", kcs_port, irq); } else { if (kcs_port) printk(KERN_INFO "ipmi_kcs: Acquiring BMC @ port=0x%x\n", kcs_port); else printk(KERN_INFO "ipmi_kcs: Acquiring BMC @ addr=0x%lx\n", kcs_physaddr); } rv = ipmi_register_smi(&handlers, new_kcs, ipmi_version_major, ipmi_version_minor, &(new_kcs->intf)); if (rv) { free_irq(irq, new_kcs); printk(KERN_ERR "ipmi_kcs: Unable to register device: error %d\n", rv); goto out_err; } new_kcs->interrupt_disabled = 0; new_kcs->timer_stopped = 0; new_kcs->stop_operation = 0; init_timer(&(new_kcs->kcs_timer)); new_kcs->kcs_timer.data = (long) new_kcs; new_kcs->kcs_timer.function = kcs_timeout; new_kcs->last_timeout_jiffies = jiffies; new_kcs->kcs_timer.expires = jiffies + KCS_TIMEOUT_JIFFIES; add_timer(&(new_kcs->kcs_timer)); *kcs = new_kcs; return 0; out_err: if (kcs_port) release_region (kcs_port, 2); if (new_kcs->addr) iounmap(new_kcs->addr); if (kcs_physaddr) release_mem_region(kcs_physaddr, 2); if (new_kcs->kcs_sm) kfree(new_kcs->kcs_sm); kfree(new_kcs); return rv;}#ifdef CONFIG_ACPI/* Retrieve the base physical address from ACPI tables. Originally from Hewlett-Packard simple bmc.c, a GPL KCS driver. */#include <linux/acpi.h>/* A real hack, but everything's not there yet in 2.4. */#define COMPILER_DEPENDENT_UINT64 unsigned long#include <../drivers/acpi/include/acpi.h>#include <../drivers/acpi/include/actypes.h>struct SPMITable { s8 Signature[4]; u32 Length; u8 Revision; u8 Checksum; s8 OEMID[6]; s8 OEMTableID[8]; s8 OEMRevision[4]; s8 CreatorID[4]; s8 CreatorRevision[4]; s16 InterfaceType; s16 SpecificationRevision; u8 InterruptType; u8 GPE; s16 Reserved; u64 GlobalSystemInterrupt; u8 BaseAddress[12]; u8 UID[4];} __attribute__ ((packed));static unsigned long acpi_find_bmc(void){ acpi_status status; acpi_table_header *spmi; static unsigned long io_base = 0; if (io_base != 0) return io_base; status = acpi_get_firmware_table("SPMI", 1, ACPI_LOGICAL_ADDRESSING, &spmi); if (status != AE_OK) { printk(KERN_ERR "ipmi_kcs: SPMI table not found.\n"); return 0; } memcpy(&io_base, ((struct SPMITable *)spmi)->BaseAddress, sizeof(io_base)); return io_base;}#endifstatic __init int init_ipmi_kcs(void){ int rv = 0; int pos = 0; int i = 0;#ifdef CONFIG_ACPI unsigned long physaddr = 0;#endif if (initialized) return 0; initialized = 1; /* First do the "command-line" parameters */ for (i=0; i < KCS_MAX_PARMS; i++) { rv = init_one_kcs(kcs_ports[i], kcs_irqs[i], 0, &(kcs_infos[pos])); if (rv == 0) pos++; rv = init_one_kcs(0, kcs_irqs[i], kcs_addrs[i], &(kcs_infos[pos])); if (rv == 0) pos++; } /* Only try the defaults if enabled and resources are available (because they weren't already specified above). */ if (kcs_trydefaults) {#ifdef CONFIG_ACPI if ((physaddr = acpi_find_bmc())) { if (!check_mem_region(physaddr, 2)) { rv = init_one_kcs(0, 0, physaddr, &(kcs_infos[pos])); if (rv == 0) pos++; } }#endif if (!check_region(DEFAULT_IO_PORT, 2)) { rv = init_one_kcs(DEFAULT_IO_PORT, 0, 0, &(kcs_infos[pos])); if (rv == 0) pos++; } } if (kcs_infos[0] == NULL) { printk("ipmi_kcs: Unable to find any KCS interfaces\n"); return -ENODEV; } return 0;}module_init(init_ipmi_kcs);#ifdef MODULEvoid __exit cleanup_one_kcs(struct kcs_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->kcs_lock), flags); spin_lock(&(to_clean->msg_lock)); to_clean->stop_operation = 1; if (to_clean->irq != 0) free_irq(to_clean->irq, to_clean); if (to_clean->port) { printk(KERN_INFO "ipmi_kcs: Releasing BMC @ port=0x%x\n", to_clean->port); release_region (to_clean->port, 2); } if (to_clean->addr) { printk(KERN_INFO "ipmi_kcs: Releasing BMC @ addr=0x%lx\n", to_clean->physaddr); iounmap(to_clean->addr); release_mem_region(to_clean->physaddr, 2); } spin_unlock(&(to_clean->msg_lock)); spin_unlock_irqrestore(&(to_clean->kcs_lock), flags); /* Wait for the timer to stop. This avoids problems with race conditions removing the timer here. Hopefully this will be long enough to avoid problems with interrupts still running. */ schedule_timeout(2); while (!to_clean->timer_stopped) { schedule_timeout(1); } rv = ipmi_unregister_smi(to_clean->intf); if (rv) { printk(KERN_ERR "ipmi_kcs: Unable to unregister device: errno=%d\n", rv); } initialized = 0; kfree(to_clean->kcs_sm); kfree(to_clean);}static __exit void cleanup_ipmi_kcs(void){ int i; if (!initialized) return; for (i=0; i<KCS_MAX_DRIVERS; i++) { cleanup_one_kcs(kcs_infos[i]); }}module_exit(cleanup_ipmi_kcs);#else/* Unfortunately, cmdline::get_options() only returns integers, not longs. Since we need ulongs (64-bit physical addresses) parse the comma-separated list manually. Arguments can be one of these forms: m0xaabbccddeeff A physical memory address without an IRQ m0xaabbccddeeff:cc A physical memory address with an IRQ p0xaabb An IO port without an IRQ p0xaabb:cc An IO port with an IRQ nodefaults Suppress trying the default IO port or ACPI address For example, to pass one IO port with an IRQ, one address, and suppress the use of the default IO port and ACPI address, use this option string: ipmi_kcs=p0xCA2:5,m0xFF5B0022,nodefaults Remember, ipmi_kcs_setup() is passed the string after the equal sign. */static int __init ipmi_kcs_setup(char *str){ unsigned long val; char *cur, *colon; int pos; pos = 0; cur = strsep(&str, ","); while ((cur) && (*cur) && (pos < KCS_MAX_PARMS)) { switch (*cur) { case 'n': if (strcmp(cur, "nodefaults") == 0) kcs_trydefaults = 0; else printk(KERN_INFO "ipmi_kcs: bad parameter value %s\n", cur); break; case 'm': case 'p': val = simple_strtoul(cur + 1, &colon, 0); if (*cur == 'p') kcs_ports[pos] = val; else kcs_addrs[pos] = val; if (*colon == ':') { val = simple_strtoul(colon + 1, &colon, 0); kcs_irqs[pos] = val; } pos++; break; default: printk(KERN_INFO "ipmi_kcs: bad parameter value %s\n", cur); } cur = strsep(&str, ","); } return 1;}__setup("ipmi_kcs=", ipmi_kcs_setup);#endifMODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -