root/drivers/char/ipmi/ipmi_si_hotmod.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. parse_str
  2. check_hotmod_int_op
  3. parse_hotmod_str
  4. hotmod_handler
  5. ipmi_si_hotmod_exit

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

/* [<][>][^][v][top][bottom][index][help] */