1 /* 2 * ipmi_si_hotmod.c 3 * 4 * Handling for dynamically adding/removing IPMI devices through 5 * a module parameter (and thus sysfs). 6 */ 7 #include <linux/moduleparam.h> 8 #include <linux/ipmi.h> 9 #include "ipmi_si.h" 10 11 #define PFX "ipmi_hotmod: " 12 13 static int hotmod_handler(const char *val, const struct kernel_param *kp); 14 15 module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); 16 MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" 17 " Documentation/IPMI.txt in the kernel sources for the" 18 " gory details."); 19 20 /* 21 * Parms come in as <op1>[:op2[:op3...]]. ops are: 22 * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]] 23 * Options are: 24 * rsp=<regspacing> 25 * rsi=<regsize> 26 * rsh=<regshift> 27 * irq=<irq> 28 * ipmb=<ipmb addr> 29 */ 30 enum hotmod_op { HM_ADD, HM_REMOVE }; 31 struct hotmod_vals { 32 const char *name; 33 const int val; 34 }; 35 36 static const struct hotmod_vals hotmod_ops[] = { 37 { "add", HM_ADD }, 38 { "remove", HM_REMOVE }, 39 { NULL } 40 }; 41 42 static const struct hotmod_vals hotmod_si[] = { 43 { "kcs", SI_KCS }, 44 { "smic", SI_SMIC }, 45 { "bt", SI_BT }, 46 { NULL } 47 }; 48 49 static const struct hotmod_vals hotmod_as[] = { 50 { "mem", IPMI_MEM_ADDR_SPACE }, 51 { "i/o", IPMI_IO_ADDR_SPACE }, 52 { NULL } 53 }; 54 55 static int parse_str(const struct hotmod_vals *v, int *val, char *name, 56 char **curr) 57 { 58 char *s; 59 int i; 60 61 s = strchr(*curr, ','); 62 if (!s) { 63 pr_warn(PFX "No hotmod %s given.\n", name); 64 return -EINVAL; 65 } 66 *s = '\0'; 67 s++; 68 for (i = 0; v[i].name; i++) { 69 if (strcmp(*curr, v[i].name) == 0) { 70 *val = v[i].val; 71 *curr = s; 72 return 0; 73 } 74 } 75 76 pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr); 77 return -EINVAL; 78 } 79 80 static int check_hotmod_int_op(const char *curr, const char *option, 81 const char *name, int *val) 82 { 83 char *n; 84 85 if (strcmp(curr, name) == 0) { 86 if (!option) { 87 pr_warn(PFX "No option given for '%s'\n", curr); 88 return -EINVAL; 89 } 90 *val = simple_strtoul(option, &n, 0); 91 if ((*n != '\0') || (*option == '\0')) { 92 pr_warn(PFX "Bad option given for '%s'\n", curr); 93 return -EINVAL; 94 } 95 return 1; 96 } 97 return 0; 98 } 99 100 static int hotmod_handler(const char *val, const struct kernel_param *kp) 101 { 102 char *str = kstrdup(val, GFP_KERNEL); 103 int rv; 104 char *next, *curr, *s, *n, *o; 105 enum hotmod_op op; 106 enum si_type si_type; 107 int addr_space; 108 unsigned long addr; 109 int regspacing; 110 int regsize; 111 int regshift; 112 int irq; 113 int ipmb; 114 int ival; 115 int len; 116 117 if (!str) 118 return -ENOMEM; 119 120 /* Kill any trailing spaces, as we can get a "\n" from echo. */ 121 len = strlen(str); 122 ival = len - 1; 123 while ((ival >= 0) && isspace(str[ival])) { 124 str[ival] = '\0'; 125 ival--; 126 } 127 128 for (curr = str; curr; curr = next) { 129 regspacing = 1; 130 regsize = 1; 131 regshift = 0; 132 irq = 0; 133 ipmb = 0; /* Choose the default if not specified */ 134 135 next = strchr(curr, ':'); 136 if (next) { 137 *next = '\0'; 138 next++; 139 } 140 141 rv = parse_str(hotmod_ops, &ival, "operation", &curr); 142 if (rv) 143 break; 144 op = ival; 145 146 rv = parse_str(hotmod_si, &ival, "interface type", &curr); 147 if (rv) 148 break; 149 si_type = ival; 150 151 rv = parse_str(hotmod_as, &addr_space, "address space", &curr); 152 if (rv) 153 break; 154 155 s = strchr(curr, ','); 156 if (s) { 157 *s = '\0'; 158 s++; 159 } 160 addr = simple_strtoul(curr, &n, 0); 161 if ((*n != '\0') || (*curr == '\0')) { 162 pr_warn(PFX "Invalid hotmod address '%s'\n", curr); 163 break; 164 } 165 166 while (s) { 167 curr = s; 168 s = strchr(curr, ','); 169 if (s) { 170 *s = '\0'; 171 s++; 172 } 173 o = strchr(curr, '='); 174 if (o) { 175 *o = '\0'; 176 o++; 177 } 178 rv = check_hotmod_int_op(curr, o, "rsp", ®spacing); 179 if (rv < 0) 180 goto out; 181 else if (rv) 182 continue; 183 rv = check_hotmod_int_op(curr, o, "rsi", ®size); 184 if (rv < 0) 185 goto out; 186 else if (rv) 187 continue; 188 rv = check_hotmod_int_op(curr, o, "rsh", ®shift); 189 if (rv < 0) 190 goto out; 191 else if (rv) 192 continue; 193 rv = check_hotmod_int_op(curr, o, "irq", &irq); 194 if (rv < 0) 195 goto out; 196 else if (rv) 197 continue; 198 rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb); 199 if (rv < 0) 200 goto out; 201 else if (rv) 202 continue; 203 204 rv = -EINVAL; 205 pr_warn(PFX "Invalid hotmod option '%s'\n", curr); 206 goto out; 207 } 208 209 if (op == HM_ADD) { 210 struct si_sm_io io; 211 212 memset(&io, 0, sizeof(io)); 213 io.addr_source = SI_HOTMOD; 214 io.si_type = si_type; 215 io.addr_data = addr; 216 io.addr_type = addr_space; 217 218 io.addr = NULL; 219 io.regspacing = regspacing; 220 if (!io.regspacing) 221 io.regspacing = DEFAULT_REGSPACING; 222 io.regsize = regsize; 223 if (!io.regsize) 224 io.regsize = DEFAULT_REGSIZE; 225 io.regshift = regshift; 226 io.irq = irq; 227 if (io.irq) 228 io.irq_setup = ipmi_std_irq_setup; 229 io.slave_addr = ipmb; 230 231 rv = ipmi_si_add_smi(&io); 232 if (rv) 233 goto out; 234 } else { 235 ipmi_si_remove_by_data(addr_space, si_type, addr); 236 } 237 } 238 rv = len; 239 out: 240 kfree(str); 241 return rv; 242 } 243