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