Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
13static int hotmod_handler(const char *val, const struct kernel_param *kp);
14
15module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
16MODULE_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 */
30enum hotmod_op { HM_ADD, HM_REMOVE };
31struct hotmod_vals {
32 const char *name;
33 const int val;
34};
35
36static const struct hotmod_vals hotmod_ops[] = {
37 { "add", HM_ADD },
38 { "remove", HM_REMOVE },
39 { NULL }
40};
41
42static const struct hotmod_vals hotmod_si[] = {
43 { "kcs", SI_KCS },
44 { "smic", SI_SMIC },
45 { "bt", SI_BT },
46 { NULL }
47};
48
49static const struct hotmod_vals hotmod_as[] = {
50 { "mem", IPMI_MEM_ADDR_SPACE },
51 { "i/o", IPMI_IO_ADDR_SPACE },
52 { NULL }
53};
54
55static 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
80static 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
100static 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;
239out:
240 kfree(str);
241 return rv;
242}