diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/ipmi/Makefile | 3 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si.h | 2 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_hotmod.c | 242 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 261 |
4 files changed, 264 insertions, 244 deletions
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index eefb0b301e83..61c7d5d32f4a 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -2,7 +2,8 @@ # Makefile for the ipmi drivers. # -ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o +ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o \ + ipmi_si_hotmod.o obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h index 9573b35d73af..4ceb5ac3ad98 100644 --- a/drivers/char/ipmi/ipmi_si.h +++ b/drivers/char/ipmi/ipmi_si.h @@ -20,3 +20,5 @@ void ipmi_irq_start_cleanup(struct si_sm_io *io); int ipmi_std_irq_setup(struct si_sm_io *io); void ipmi_irq_finish_setup(struct si_sm_io *io); int ipmi_si_remove_by_dev(struct device *dev); +void ipmi_si_remove_by_data(int addr_space, enum si_type si_type, + unsigned long addr); diff --git a/drivers/char/ipmi/ipmi_si_hotmod.c b/drivers/char/ipmi/ipmi_si_hotmod.c new file mode 100644 index 000000000000..da5716159974 --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_hotmod.c @@ -0,0 +1,242 @@ +/* + * ipmi_si_hotmod.c + * + * Handling for dynamically adding/removing IPMI devices through + * a module parameter (and thus sysfs). + */ +#include <linux/moduleparam.h> +#include <linux/ipmi.h> +#include "ipmi_si.h" + +#define PFX "ipmi_hotmod: " + +static int hotmod_handler(const char *val, struct kernel_param *kp); + +module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); +MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" + " Documentation/IPMI.txt in the kernel sources for the" + " gory details."); + +/* + * Parms come in as <op1>[:op2[:op3...]]. ops are: + * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]] + * Options are: + * rsp=<regspacing> + * rsi=<regsize> + * rsh=<regshift> + * irq=<irq> + * ipmb=<ipmb addr> + */ +enum hotmod_op { HM_ADD, HM_REMOVE }; +struct hotmod_vals { + const char *name; + const int val; +}; + +static const struct hotmod_vals hotmod_ops[] = { + { "add", HM_ADD }, + { "remove", HM_REMOVE }, + { NULL } +}; + +static const struct hotmod_vals hotmod_si[] = { + { "kcs", SI_KCS }, + { "smic", SI_SMIC }, + { "bt", SI_BT }, + { NULL } +}; + +static const struct hotmod_vals hotmod_as[] = { + { "mem", IPMI_MEM_ADDR_SPACE }, + { "i/o", IPMI_IO_ADDR_SPACE }, + { NULL } +}; + +static int parse_str(const struct hotmod_vals *v, int *val, char *name, + char **curr) +{ + char *s; + int i; + + s = strchr(*curr, ','); + if (!s) { + pr_warn(PFX "No hotmod %s given.\n", name); + return -EINVAL; + } + *s = '\0'; + s++; + for (i = 0; v[i].name; i++) { + if (strcmp(*curr, v[i].name) == 0) { + *val = v[i].val; + *curr = s; + return 0; + } + } + + pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr); + return -EINVAL; +} + +static int check_hotmod_int_op(const char *curr, const char *option, + const char *name, int *val) +{ + char *n; + + if (strcmp(curr, name) == 0) { + if (!option) { + pr_warn(PFX "No option given for '%s'\n", curr); + return -EINVAL; + } + *val = simple_strtoul(option, &n, 0); + if ((*n != '\0') || (*option == '\0')) { + pr_warn(PFX "Bad option given for '%s'\n", curr); + return -EINVAL; + } + return 1; + } + return 0; +} + +static int hotmod_handler(const char *val, struct kernel_param *kp) +{ + char *str = kstrdup(val, GFP_KERNEL); + int rv; + char *next, *curr, *s, *n, *o; + enum hotmod_op op; + enum si_type si_type; + int addr_space; + unsigned long addr; + int regspacing; + int regsize; + int regshift; + int irq; + int ipmb; + int ival; + int len; + + if (!str) + return -ENOMEM; + + /* Kill any trailing spaces, as we can get a "\n" from echo. */ + len = strlen(str); + ival = len - 1; + while ((ival >= 0) && isspace(str[ival])) { + str[ival] = '\0'; + ival--; + } + + for (curr = str; curr; curr = next) { + regspacing = 1; + regsize = 1; + regshift = 0; + irq = 0; + ipmb = 0; /* Choose the default if not specified */ + + next = strchr(curr, ':'); + if (next) { + *next = '\0'; + next++; + } + + rv = parse_str(hotmod_ops, &ival, "operation", &curr); + if (rv) + break; + op = ival; + + rv = parse_str(hotmod_si, &ival, "interface type", &curr); + if (rv) + break; + si_type = ival; + + rv = parse_str(hotmod_as, &addr_space, "address space", &curr); + if (rv) + break; + + s = strchr(curr, ','); + if (s) { + *s = '\0'; + s++; + } + addr = simple_strtoul(curr, &n, 0); + if ((*n != '\0') || (*curr == '\0')) { + pr_warn(PFX "Invalid hotmod address '%s'\n", curr); + break; + } + + while (s) { + curr = s; + s = strchr(curr, ','); + if (s) { + *s = '\0'; + s++; + } + o = strchr(curr, '='); + if (o) { + *o = '\0'; + o++; + } + rv = check_hotmod_int_op(curr, o, "rsp", ®spacing); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "rsi", ®size); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "rsh", ®shift); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "irq", &irq); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb); + if (rv < 0) + goto out; + else if (rv) + continue; + + rv = -EINVAL; + pr_warn(PFX "Invalid hotmod option '%s'\n", curr); + goto out; + } + + if (op == HM_ADD) { + struct si_sm_io io; + + memset(&io, 0, sizeof(io)); + io.addr_source = SI_HOTMOD; + io.si_type = si_type; + io.addr_data = addr; + io.addr_type = addr_space; + + io.addr = NULL; + io.regspacing = regspacing; + if (!io.regspacing) + io.regspacing = DEFAULT_REGSPACING; + io.regsize = regsize; + if (!io.regsize) + io.regsize = DEFAULT_REGSIZE; + io.regshift = regshift; + io.irq = irq; + if (io.irq) + io.irq_setup = ipmi_std_irq_setup; + io.slave_addr = ipmb; + + rv = ipmi_si_add_smi(&io); + if (rv) + goto out; + } else { + ipmi_si_remove_by_data(addr_space, si_type, addr); + } + } + rv = len; +out: + kfree(str); + return rv; +} diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 6c2e14af8321..02e263b2152a 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1310,13 +1310,6 @@ static unsigned int num_slave_addrs; static const char * const addr_space_to_str[] = { "i/o", "mem" }; -static int hotmod_handler(const char *val, struct kernel_param *kp); - -module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); -MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" - " Documentation/IPMI.txt in the kernel sources for the" - " gory details."); - #ifdef CONFIG_ACPI module_param_named(tryacpi, si_tryacpi, bool, 0); MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" @@ -1689,86 +1682,6 @@ static int mem_setup(struct si_sm_io *io) return 0; } -/* - * Parms come in as <op1>[:op2[:op3...]]. ops are: - * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]] - * Options are: - * rsp=<regspacing> - * rsi=<regsize> - * rsh=<regshift> - * irq=<irq> - * ipmb=<ipmb addr> - */ -enum hotmod_op { HM_ADD, HM_REMOVE }; -struct hotmod_vals { - const char *name; - const int val; -}; - -static const struct hotmod_vals hotmod_ops[] = { - { "add", HM_ADD }, - { "remove", HM_REMOVE }, - { NULL } -}; - -static const struct hotmod_vals hotmod_si[] = { - { "kcs", SI_KCS }, - { "smic", SI_SMIC }, - { "bt", SI_BT }, - { NULL } -}; - -static const struct hotmod_vals hotmod_as[] = { - { "mem", IPMI_MEM_ADDR_SPACE }, - { "i/o", IPMI_IO_ADDR_SPACE }, - { NULL } -}; - -static int parse_str(const struct hotmod_vals *v, int *val, char *name, - char **curr) -{ - char *s; - int i; - - s = strchr(*curr, ','); - if (!s) { - pr_warn(PFX "No hotmod %s given.\n", name); - return -EINVAL; - } - *s = '\0'; - s++; - for (i = 0; v[i].name; i++) { - if (strcmp(*curr, v[i].name) == 0) { - *val = v[i].val; - *curr = s; - return 0; - } - } - - pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr); - return -EINVAL; -} - -static int check_hotmod_int_op(const char *curr, const char *option, - const char *name, int *val) -{ - char *n; - - if (strcmp(curr, name) == 0) { - if (!option) { - pr_warn(PFX "No option given for '%s'\n", curr); - return -EINVAL; - } - *val = simple_strtoul(option, &n, 0); - if ((*n != '\0') || (*option == '\0')) { - pr_warn(PFX "Bad option given for '%s'\n", curr); - return -EINVAL; - } - return 1; - } - return 0; -} - static struct smi_info *smi_info_alloc(void) { struct smi_info *info = kzalloc(sizeof(*info), GFP_KERNEL); @@ -1778,162 +1691,6 @@ static struct smi_info *smi_info_alloc(void) return info; } -static int hotmod_handler(const char *val, struct kernel_param *kp) -{ - char *str = kstrdup(val, GFP_KERNEL); - int rv; - char *next, *curr, *s, *n, *o; - enum hotmod_op op; - enum si_type si_type; - int addr_space; - unsigned long addr; - int regspacing; - int regsize; - int regshift; - int irq; - int ipmb; - int ival; - int len; - - if (!str) - return -ENOMEM; - - /* Kill any trailing spaces, as we can get a "\n" from echo. */ - len = strlen(str); - ival = len - 1; - while ((ival >= 0) && isspace(str[ival])) { - str[ival] = '\0'; - ival--; - } - - for (curr = str; curr; curr = next) { - regspacing = 1; - regsize = 1; - regshift = 0; - irq = 0; - ipmb = 0; /* Choose the default if not specified */ - - next = strchr(curr, ':'); - if (next) { - *next = '\0'; - next++; - } - - rv = parse_str(hotmod_ops, &ival, "operation", &curr); - if (rv) - break; - op = ival; - - rv = parse_str(hotmod_si, &ival, "interface type", &curr); - if (rv) - break; - si_type = ival; - - rv = parse_str(hotmod_as, &addr_space, "address space", &curr); - if (rv) - break; - - s = strchr(curr, ','); - if (s) { - *s = '\0'; - s++; - } - addr = simple_strtoul(curr, &n, 0); - if ((*n != '\0') || (*curr == '\0')) { - pr_warn(PFX "Invalid hotmod address '%s'\n", curr); - break; - } - - while (s) { - curr = s; - s = strchr(curr, ','); - if (s) { - *s = '\0'; - s++; - } - o = strchr(curr, '='); - if (o) { - *o = '\0'; - o++; - } - rv = check_hotmod_int_op(curr, o, "rsp", ®spacing); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "rsi", ®size); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "rsh", ®shift); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "irq", &irq); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb); - if (rv < 0) - goto out; - else if (rv) - continue; - - rv = -EINVAL; - pr_warn(PFX "Invalid hotmod option '%s'\n", curr); - goto out; - } - - if (op == HM_ADD) { - struct si_sm_io io; - - memset(&io, 0, sizeof(io)); - io.addr_source = SI_HOTMOD; - io.si_type = si_type; - io.addr_data = addr; - io.addr_type = addr_space; - - io.addr = NULL; - io.regspacing = regspacing; - if (!io.regspacing) - io.regspacing = DEFAULT_REGSPACING; - io.regsize = regsize; - if (!io.regsize) - io.regsize = DEFAULT_REGSIZE; - io.regshift = regshift; - io.irq = irq; - if (io.irq) - io.irq_setup = ipmi_std_irq_setup; - io.slave_addr = ipmb; - - rv = ipmi_si_add_smi(&io); - if (rv) - goto out; - } else { - /* remove */ - struct smi_info *e, *tmp_e; - - mutex_lock(&smi_infos_lock); - list_for_each_entry_safe(e, tmp_e, &smi_infos, link) { - if (e->io.addr_type != addr_space) - continue; - if (e->io.si_type != si_type) - continue; - if (e->io.addr_data == addr) - cleanup_one_si(e); - } - mutex_unlock(&smi_infos_lock); - } - } - rv = len; -out: - kfree(str); - return rv; -} - static int hardcode_find_bmc(void) { int ret = -ENODEV; @@ -3779,6 +3536,24 @@ int ipmi_si_remove_by_dev(struct device *dev) return rv; } +void ipmi_si_remove_by_data(int addr_space, enum si_type si_type, + unsigned long addr) +{ + /* remove */ + struct smi_info *e, *tmp_e; + + mutex_lock(&smi_infos_lock); + list_for_each_entry_safe(e, tmp_e, &smi_infos, link) { + if (e->io.addr_type != addr_space) + continue; + if (e->io.si_type != si_type) + continue; + if (e->io.addr_data == addr) + cleanup_one_si(e); + } + mutex_unlock(&smi_infos_lock); +} + static void cleanup_ipmi_si(void) { struct smi_info *e, *tmp_e; |