Linux 4.18.10
[linux/fpc-iii.git] / drivers / char / ipmi / ipmi_si_hotmod.c
bloba98ca42a50b110e7a62fd55ed8628c9e31939585
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * ipmi_si_hotmod.c
5 * Handling for dynamically adding/removing IPMI devices through
6 * a module parameter (and thus sysfs).
7 */
8 #include <linux/moduleparam.h>
9 #include <linux/ipmi.h>
10 #include "ipmi_si.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"
19 " gory details.");
22 * Parms come in as <op1>[:op2[:op3...]]. ops are:
23 * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
24 * Options are:
25 * rsp=<regspacing>
26 * rsi=<regsize>
27 * rsh=<regshift>
28 * irq=<irq>
29 * ipmb=<ipmb addr>
31 enum hotmod_op { HM_ADD, HM_REMOVE };
32 struct hotmod_vals {
33 const char *name;
34 const int val;
37 static const struct hotmod_vals hotmod_ops[] = {
38 { "add", HM_ADD },
39 { "remove", HM_REMOVE },
40 { NULL }
43 static const struct hotmod_vals hotmod_si[] = {
44 { "kcs", SI_KCS },
45 { "smic", SI_SMIC },
46 { "bt", SI_BT },
47 { NULL }
50 static const struct hotmod_vals hotmod_as[] = {
51 { "mem", IPMI_MEM_ADDR_SPACE },
52 { "i/o", IPMI_IO_ADDR_SPACE },
53 { NULL }
56 static int parse_str(const struct hotmod_vals *v, int *val, char *name,
57 char **curr)
59 char *s;
60 int i;
62 s = strchr(*curr, ',');
63 if (!s) {
64 pr_warn(PFX "No hotmod %s given.\n", name);
65 return -EINVAL;
67 *s = '\0';
68 s++;
69 for (i = 0; v[i].name; i++) {
70 if (strcmp(*curr, v[i].name) == 0) {
71 *val = v[i].val;
72 *curr = s;
73 return 0;
77 pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr);
78 return -EINVAL;
81 static int check_hotmod_int_op(const char *curr, const char *option,
82 const char *name, int *val)
84 char *n;
86 if (strcmp(curr, name) == 0) {
87 if (!option) {
88 pr_warn(PFX "No option given for '%s'\n", curr);
89 return -EINVAL;
91 *val = simple_strtoul(option, &n, 0);
92 if ((*n != '\0') || (*option == '\0')) {
93 pr_warn(PFX "Bad option given for '%s'\n", curr);
94 return -EINVAL;
96 return 1;
98 return 0;
101 static int hotmod_handler(const char *val, const struct kernel_param *kp)
103 char *str = kstrdup(val, GFP_KERNEL);
104 int rv;
105 char *next, *curr, *s, *n, *o;
106 enum hotmod_op op;
107 enum si_type si_type;
108 int addr_space;
109 unsigned long addr;
110 int regspacing;
111 int regsize;
112 int regshift;
113 int irq;
114 int ipmb;
115 int ival;
116 int len;
118 if (!str)
119 return -ENOMEM;
121 /* Kill any trailing spaces, as we can get a "\n" from echo. */
122 len = strlen(str);
123 ival = len - 1;
124 while ((ival >= 0) && isspace(str[ival])) {
125 str[ival] = '\0';
126 ival--;
129 for (curr = str; curr; curr = next) {
130 regspacing = 1;
131 regsize = 1;
132 regshift = 0;
133 irq = 0;
134 ipmb = 0; /* Choose the default if not specified */
136 next = strchr(curr, ':');
137 if (next) {
138 *next = '\0';
139 next++;
142 rv = parse_str(hotmod_ops, &ival, "operation", &curr);
143 if (rv)
144 break;
145 op = ival;
147 rv = parse_str(hotmod_si, &ival, "interface type", &curr);
148 if (rv)
149 break;
150 si_type = ival;
152 rv = parse_str(hotmod_as, &addr_space, "address space", &curr);
153 if (rv)
154 break;
156 s = strchr(curr, ',');
157 if (s) {
158 *s = '\0';
159 s++;
161 addr = simple_strtoul(curr, &n, 0);
162 if ((*n != '\0') || (*curr == '\0')) {
163 pr_warn(PFX "Invalid hotmod address '%s'\n", curr);
164 break;
167 while (s) {
168 curr = s;
169 s = strchr(curr, ',');
170 if (s) {
171 *s = '\0';
172 s++;
174 o = strchr(curr, '=');
175 if (o) {
176 *o = '\0';
177 o++;
179 rv = check_hotmod_int_op(curr, o, "rsp", &regspacing);
180 if (rv < 0)
181 goto out;
182 else if (rv)
183 continue;
184 rv = check_hotmod_int_op(curr, o, "rsi", &regsize);
185 if (rv < 0)
186 goto out;
187 else if (rv)
188 continue;
189 rv = check_hotmod_int_op(curr, o, "rsh", &regshift);
190 if (rv < 0)
191 goto out;
192 else if (rv)
193 continue;
194 rv = check_hotmod_int_op(curr, o, "irq", &irq);
195 if (rv < 0)
196 goto out;
197 else if (rv)
198 continue;
199 rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb);
200 if (rv < 0)
201 goto out;
202 else if (rv)
203 continue;
205 rv = -EINVAL;
206 pr_warn(PFX "Invalid hotmod option '%s'\n", curr);
207 goto out;
210 if (op == HM_ADD) {
211 struct si_sm_io io;
213 memset(&io, 0, sizeof(io));
214 io.addr_source = SI_HOTMOD;
215 io.si_type = si_type;
216 io.addr_data = addr;
217 io.addr_type = addr_space;
219 io.addr = NULL;
220 io.regspacing = regspacing;
221 if (!io.regspacing)
222 io.regspacing = DEFAULT_REGSPACING;
223 io.regsize = regsize;
224 if (!io.regsize)
225 io.regsize = DEFAULT_REGSIZE;
226 io.regshift = regshift;
227 io.irq = irq;
228 if (io.irq)
229 io.irq_setup = ipmi_std_irq_setup;
230 io.slave_addr = ipmb;
232 rv = ipmi_si_add_smi(&io);
233 if (rv)
234 goto out;
235 } else {
236 ipmi_si_remove_by_data(addr_space, si_type, addr);
239 rv = len;
240 out:
241 kfree(str);
242 return rv;