1 // SPDX-License-Identifier: GPL-2.0+
5 * Handling for dynamically adding/removing IPMI devices through
6 * a module parameter (and thus sysfs).
8 #include <linux/moduleparam.h>
9 #include <linux/ipmi.h>
12 #define PFX "ipmi_hotmod: "
14 static int hotmod_handler(const char *val, const struct kernel_param *kp);
16 module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
17 MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See"
18 " Documentation/IPMI.txt in the kernel sources for the"
22 * Parms come in as <op1>[:op2[:op3...]]. ops are:
23 * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
31 enum hotmod_op { HM_ADD, HM_REMOVE };
37 static const struct hotmod_vals hotmod_ops[] = {
39 { "remove", HM_REMOVE },
43 static const struct hotmod_vals hotmod_si[] = {
50 static const struct hotmod_vals hotmod_as[] = {
51 { "mem", IPMI_MEM_ADDR_SPACE },
52 { "i/o", IPMI_IO_ADDR_SPACE },
56 static int parse_str(const struct hotmod_vals *v, int *val, char *name,
62 s = strchr(*curr, ',');
64 pr_warn(PFX "No hotmod %s given.\n", name);
69 for (i = 0; v[i].name; i++) {
70 if (strcmp(*curr, v[i].name) == 0) {
77 pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr);
81 static int check_hotmod_int_op(const char *curr, const char *option,
82 const char *name, int *val)
86 if (strcmp(curr, name) == 0) {
88 pr_warn(PFX "No option given for '%s'\n", curr);
91 *val = simple_strtoul(option, &n, 0);
92 if ((*n != '\0') || (*option == '\0')) {
93 pr_warn(PFX "Bad option given for '%s'\n", curr);
101 static int hotmod_handler(const char *val, const struct kernel_param *kp)
103 char *str = kstrdup(val, GFP_KERNEL);
105 char *next, *curr, *s, *n, *o;
107 enum si_type si_type;
121 /* Kill any trailing spaces, as we can get a "\n" from echo. */
124 while ((ival >= 0) && isspace(str[ival])) {
129 for (curr = str; curr; curr = next) {
134 ipmb = 0; /* Choose the default if not specified */
136 next = strchr(curr, ':');
142 rv = parse_str(hotmod_ops, &ival, "operation", &curr);
147 rv = parse_str(hotmod_si, &ival, "interface type", &curr);
152 rv = parse_str(hotmod_as, &addr_space, "address space", &curr);
156 s = strchr(curr, ',');
161 addr = simple_strtoul(curr, &n, 0);
162 if ((*n != '\0') || (*curr == '\0')) {
163 pr_warn(PFX "Invalid hotmod address '%s'\n", curr);
169 s = strchr(curr, ',');
174 o = strchr(curr, '=');
179 rv = check_hotmod_int_op(curr, o, "rsp", ®spacing);
184 rv = check_hotmod_int_op(curr, o, "rsi", ®size);
189 rv = check_hotmod_int_op(curr, o, "rsh", ®shift);
194 rv = check_hotmod_int_op(curr, o, "irq", &irq);
199 rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb);
206 pr_warn(PFX "Invalid hotmod option '%s'\n", curr);
213 memset(&io, 0, sizeof(io));
214 io.addr_source = SI_HOTMOD;
215 io.si_type = si_type;
217 io.addr_type = addr_space;
220 io.regspacing = regspacing;
222 io.regspacing = DEFAULT_REGSPACING;
223 io.regsize = regsize;
225 io.regsize = DEFAULT_REGSIZE;
226 io.regshift = regshift;
229 io.irq_setup = ipmi_std_irq_setup;
230 io.slave_addr = ipmb;
232 rv = ipmi_si_add_smi(&io);
236 ipmi_si_remove_by_data(addr_space, si_type, addr);