ipmi: msghandler: Add and use pr_fmt and dev_fmt, remove PFX
[linux-2.6-block.git] / drivers / char / ipmi / ipmi_si_hotmod.c
CommitLineData
243ac210 1// SPDX-License-Identifier: GPL-2.0+
44814ec9
CM
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#include <linux/moduleparam.h>
9#include <linux/ipmi.h>
10#include "ipmi_si.h"
11
12#define PFX "ipmi_hotmod: "
13
6297fabd 14static int hotmod_handler(const char *val, const struct kernel_param *kp);
44814ec9
CM
15
16module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
17MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See"
18 " Documentation/IPMI.txt in the kernel sources for the"
19 " gory details.");
20
21/*
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>
30 */
31enum hotmod_op { HM_ADD, HM_REMOVE };
32struct hotmod_vals {
33 const char *name;
34 const int val;
35};
36
37static const struct hotmod_vals hotmod_ops[] = {
38 { "add", HM_ADD },
39 { "remove", HM_REMOVE },
40 { NULL }
41};
42
43static const struct hotmod_vals hotmod_si[] = {
44 { "kcs", SI_KCS },
45 { "smic", SI_SMIC },
46 { "bt", SI_BT },
47 { NULL }
48};
49
50static const struct hotmod_vals hotmod_as[] = {
51 { "mem", IPMI_MEM_ADDR_SPACE },
52 { "i/o", IPMI_IO_ADDR_SPACE },
53 { NULL }
54};
55
56static int parse_str(const struct hotmod_vals *v, int *val, char *name,
57 char **curr)
58{
59 char *s;
60 int i;
61
62 s = strchr(*curr, ',');
63 if (!s) {
64 pr_warn(PFX "No hotmod %s given.\n", name);
65 return -EINVAL;
66 }
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;
74 }
75 }
76
77 pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr);
78 return -EINVAL;
79}
80
81static int check_hotmod_int_op(const char *curr, const char *option,
82 const char *name, int *val)
83{
84 char *n;
85
86 if (strcmp(curr, name) == 0) {
87 if (!option) {
88 pr_warn(PFX "No option given for '%s'\n", curr);
89 return -EINVAL;
90 }
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;
95 }
96 return 1;
97 }
98 return 0;
99}
100
6297fabd 101static int hotmod_handler(const char *val, const struct kernel_param *kp)
44814ec9
CM
102{
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;
117
118 if (!str)
119 return -ENOMEM;
120
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--;
127 }
128
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 */
135
136 next = strchr(curr, ':');
137 if (next) {
138 *next = '\0';
139 next++;
140 }
141
142 rv = parse_str(hotmod_ops, &ival, "operation", &curr);
143 if (rv)
144 break;
145 op = ival;
146
147 rv = parse_str(hotmod_si, &ival, "interface type", &curr);
148 if (rv)
149 break;
150 si_type = ival;
151
152 rv = parse_str(hotmod_as, &addr_space, "address space", &curr);
153 if (rv)
154 break;
155
156 s = strchr(curr, ',');
157 if (s) {
158 *s = '\0';
159 s++;
160 }
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;
165 }
166
167 while (s) {
168 curr = s;
169 s = strchr(curr, ',');
170 if (s) {
171 *s = '\0';
172 s++;
173 }
174 o = strchr(curr, '=');
175 if (o) {
176 *o = '\0';
177 o++;
178 }
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;
204
205 rv = -EINVAL;
206 pr_warn(PFX "Invalid hotmod option '%s'\n", curr);
207 goto out;
208 }
209
210 if (op == HM_ADD) {
211 struct si_sm_io io;
212
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;
218
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;
231
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);
237 }
238 }
239 rv = len;
240out:
241 kfree(str);
242 return rv;
243}