diff options
author | Michael Ernst <mernst@de.ibm.com> | 2008-01-26 08:10:55 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-01-26 08:11:08 -0500 |
commit | c05ffc4f2b208da8ba7d3a9b5ab886c76f8939b5 (patch) | |
tree | 4cd35a23eaf7eb3b5a15f30e82a676b590622448 /drivers/s390/char/sclp_cpi_sys.c | |
parent | dfa77f611ff295598e218aa0eb6efa73a5cf26d0 (diff) |
[S390] sclp: sysfs interface for SCLP cpi
Signed-off-by: Michael Ernst <mernst@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/char/sclp_cpi_sys.c')
-rw-r--r-- | drivers/s390/char/sclp_cpi_sys.c | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c new file mode 100644 index 000000000000..41617032afdc --- /dev/null +++ b/drivers/s390/char/sclp_cpi_sys.c | |||
@@ -0,0 +1,400 @@ | |||
1 | /* | ||
2 | * drivers/s390/char/sclp_cpi_sys.c | ||
3 | * SCLP control program identification sysfs interface | ||
4 | * | ||
5 | * Copyright IBM Corp. 2001, 2007 | ||
6 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> | ||
7 | * Michael Ernst <mernst@de.ibm.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/stat.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/ctype.h> | ||
16 | #include <linux/kmod.h> | ||
17 | #include <linux/timer.h> | ||
18 | #include <linux/err.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/completion.h> | ||
21 | #include <asm/ebcdic.h> | ||
22 | #include <asm/sclp.h> | ||
23 | #include "sclp.h" | ||
24 | #include "sclp_rw.h" | ||
25 | #include "sclp_cpi_sys.h" | ||
26 | |||
27 | #define CPI_LENGTH_NAME 8 | ||
28 | #define CPI_LENGTH_LEVEL 16 | ||
29 | |||
30 | struct cpi_evbuf { | ||
31 | struct evbuf_header header; | ||
32 | u8 id_format; | ||
33 | u8 reserved0; | ||
34 | u8 system_type[CPI_LENGTH_NAME]; | ||
35 | u64 reserved1; | ||
36 | u8 system_name[CPI_LENGTH_NAME]; | ||
37 | u64 reserved2; | ||
38 | u64 system_level; | ||
39 | u64 reserved3; | ||
40 | u8 sysplex_name[CPI_LENGTH_NAME]; | ||
41 | u8 reserved4[16]; | ||
42 | } __attribute__((packed)); | ||
43 | |||
44 | struct cpi_sccb { | ||
45 | struct sccb_header header; | ||
46 | struct cpi_evbuf cpi_evbuf; | ||
47 | } __attribute__((packed)); | ||
48 | |||
49 | static struct sclp_register sclp_cpi_event = { | ||
50 | .send_mask = EVTYP_CTLPROGIDENT_MASK, | ||
51 | }; | ||
52 | |||
53 | static char system_name[CPI_LENGTH_NAME + 1]; | ||
54 | static char sysplex_name[CPI_LENGTH_NAME + 1]; | ||
55 | static char system_type[CPI_LENGTH_NAME + 1]; | ||
56 | static u64 system_level; | ||
57 | |||
58 | static void set_data(char *field, char *data) | ||
59 | { | ||
60 | memset(field, ' ', CPI_LENGTH_NAME); | ||
61 | memcpy(field, data, strlen(data)); | ||
62 | sclp_ascebc_str(field, CPI_LENGTH_NAME); | ||
63 | } | ||
64 | |||
65 | static void cpi_callback(struct sclp_req *req, void *data) | ||
66 | { | ||
67 | struct completion *completion = data; | ||
68 | |||
69 | complete(completion); | ||
70 | } | ||
71 | |||
72 | static struct sclp_req *cpi_prepare_req(void) | ||
73 | { | ||
74 | struct sclp_req *req; | ||
75 | struct cpi_sccb *sccb; | ||
76 | struct cpi_evbuf *evb; | ||
77 | |||
78 | req = kzalloc(sizeof(struct sclp_req), GFP_KERNEL); | ||
79 | if (!req) | ||
80 | return ERR_PTR(-ENOMEM); | ||
81 | sccb = (struct cpi_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
82 | if (!sccb) { | ||
83 | kfree(req); | ||
84 | return ERR_PTR(-ENOMEM); | ||
85 | } | ||
86 | |||
87 | /* setup SCCB for Control-Program Identification */ | ||
88 | sccb->header.length = sizeof(struct cpi_sccb); | ||
89 | sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); | ||
90 | sccb->cpi_evbuf.header.type = 0x0b; | ||
91 | evb = &sccb->cpi_evbuf; | ||
92 | |||
93 | /* set system type */ | ||
94 | set_data(evb->system_type, system_type); | ||
95 | |||
96 | /* set system name */ | ||
97 | set_data(evb->system_name, system_name); | ||
98 | |||
99 | /* set sytem level */ | ||
100 | evb->system_level = system_level; | ||
101 | |||
102 | /* set sysplex name */ | ||
103 | set_data(evb->sysplex_name, sysplex_name); | ||
104 | |||
105 | /* prepare request data structure presented to SCLP driver */ | ||
106 | req->command = SCLP_CMDW_WRITE_EVENT_DATA; | ||
107 | req->sccb = sccb; | ||
108 | req->status = SCLP_REQ_FILLED; | ||
109 | req->callback = cpi_callback; | ||
110 | return req; | ||
111 | } | ||
112 | |||
113 | static void cpi_free_req(struct sclp_req *req) | ||
114 | { | ||
115 | free_page((unsigned long) req->sccb); | ||
116 | kfree(req); | ||
117 | } | ||
118 | |||
119 | static int cpi_req(void) | ||
120 | { | ||
121 | struct completion completion; | ||
122 | struct sclp_req *req; | ||
123 | int rc; | ||
124 | int response; | ||
125 | |||
126 | rc = sclp_register(&sclp_cpi_event); | ||
127 | if (rc) { | ||
128 | printk(KERN_WARNING "cpi: could not register " | ||
129 | "to hardware console.\n"); | ||
130 | goto out; | ||
131 | } | ||
132 | if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) { | ||
133 | printk(KERN_WARNING "cpi: no control program " | ||
134 | "identification support\n"); | ||
135 | rc = -EOPNOTSUPP; | ||
136 | goto out_unregister; | ||
137 | } | ||
138 | |||
139 | req = cpi_prepare_req(); | ||
140 | if (IS_ERR(req)) { | ||
141 | printk(KERN_WARNING "cpi: could not allocate request\n"); | ||
142 | rc = PTR_ERR(req); | ||
143 | goto out_unregister; | ||
144 | } | ||
145 | |||
146 | init_completion(&completion); | ||
147 | req->callback_data = &completion; | ||
148 | |||
149 | /* Add request to sclp queue */ | ||
150 | rc = sclp_add_request(req); | ||
151 | if (rc) { | ||
152 | printk(KERN_WARNING "cpi: could not start request\n"); | ||
153 | goto out_free_req; | ||
154 | } | ||
155 | |||
156 | wait_for_completion(&completion); | ||
157 | |||
158 | if (req->status != SCLP_REQ_DONE) { | ||
159 | printk(KERN_WARNING "cpi: request failed (status=0x%02x)\n", | ||
160 | req->status); | ||
161 | rc = -EIO; | ||
162 | goto out_free_req; | ||
163 | } | ||
164 | |||
165 | response = ((struct cpi_sccb *) req->sccb)->header.response_code; | ||
166 | if (response != 0x0020) { | ||
167 | printk(KERN_WARNING "cpi: failed with " | ||
168 | "response code 0x%x\n", response); | ||
169 | rc = -EIO; | ||
170 | } | ||
171 | |||
172 | out_free_req: | ||
173 | cpi_free_req(req); | ||
174 | |||
175 | out_unregister: | ||
176 | sclp_unregister(&sclp_cpi_event); | ||
177 | |||
178 | out: | ||
179 | return rc; | ||
180 | } | ||
181 | |||
182 | static int check_string(const char *attr, const char *str) | ||
183 | { | ||
184 | size_t len; | ||
185 | size_t i; | ||
186 | |||
187 | len = strlen(str); | ||
188 | |||
189 | if ((len > 0) && (str[len - 1] == '\n')) | ||
190 | len--; | ||
191 | |||
192 | if (len > CPI_LENGTH_NAME) | ||
193 | return -EINVAL; | ||
194 | |||
195 | for (i = 0; i < len ; i++) { | ||
196 | if (isalpha(str[i]) || isdigit(str[i]) || | ||
197 | strchr("$@# ", str[i])) | ||
198 | continue; | ||
199 | return -EINVAL; | ||
200 | } | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static void set_string(char *attr, const char *value) | ||
206 | { | ||
207 | size_t len; | ||
208 | size_t i; | ||
209 | |||
210 | len = strlen(value); | ||
211 | |||
212 | if ((len > 0) && (value[len - 1] == '\n')) | ||
213 | len--; | ||
214 | |||
215 | for (i = 0; i < CPI_LENGTH_NAME; i++) { | ||
216 | if (i < len) | ||
217 | attr[i] = toupper(value[i]); | ||
218 | else | ||
219 | attr[i] = ' '; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | static ssize_t system_name_show(struct kobject *kobj, | ||
224 | struct kobj_attribute *attr, char *page) | ||
225 | { | ||
226 | return snprintf(page, PAGE_SIZE, "%s\n", system_name); | ||
227 | } | ||
228 | |||
229 | static ssize_t system_name_store(struct kobject *kobj, | ||
230 | struct kobj_attribute *attr, | ||
231 | const char *buf, | ||
232 | size_t len) | ||
233 | { | ||
234 | int rc; | ||
235 | |||
236 | rc = check_string("system_name", buf); | ||
237 | if (rc) | ||
238 | return rc; | ||
239 | |||
240 | set_string(system_name, buf); | ||
241 | |||
242 | return len; | ||
243 | } | ||
244 | |||
245 | static struct kobj_attribute system_name_attr = | ||
246 | __ATTR(system_name, 0644, system_name_show, system_name_store); | ||
247 | |||
248 | static ssize_t sysplex_name_show(struct kobject *kobj, | ||
249 | struct kobj_attribute *attr, char *page) | ||
250 | { | ||
251 | return snprintf(page, PAGE_SIZE, "%s\n", sysplex_name); | ||
252 | } | ||
253 | |||
254 | static ssize_t sysplex_name_store(struct kobject *kobj, | ||
255 | struct kobj_attribute *attr, | ||
256 | const char *buf, | ||
257 | size_t len) | ||
258 | { | ||
259 | int rc; | ||
260 | |||
261 | rc = check_string("sysplex_name", buf); | ||
262 | if (rc) | ||
263 | return rc; | ||
264 | |||
265 | set_string(sysplex_name, buf); | ||
266 | |||
267 | return len; | ||
268 | } | ||
269 | |||
270 | static struct kobj_attribute sysplex_name_attr = | ||
271 | __ATTR(sysplex_name, 0644, sysplex_name_show, sysplex_name_store); | ||
272 | |||
273 | static ssize_t system_type_show(struct kobject *kobj, | ||
274 | struct kobj_attribute *attr, char *page) | ||
275 | { | ||
276 | return snprintf(page, PAGE_SIZE, "%s\n", system_type); | ||
277 | } | ||
278 | |||
279 | static ssize_t system_type_store(struct kobject *kobj, | ||
280 | struct kobj_attribute *attr, | ||
281 | const char *buf, | ||
282 | size_t len) | ||
283 | { | ||
284 | int rc; | ||
285 | |||
286 | rc = check_string("system_type", buf); | ||
287 | if (rc) | ||
288 | return rc; | ||
289 | |||
290 | set_string(system_type, buf); | ||
291 | |||
292 | return len; | ||
293 | } | ||
294 | |||
295 | static struct kobj_attribute system_type_attr = | ||
296 | __ATTR(system_type, 0644, system_type_show, system_type_store); | ||
297 | |||
298 | static ssize_t system_level_show(struct kobject *kobj, | ||
299 | struct kobj_attribute *attr, char *page) | ||
300 | { | ||
301 | unsigned long long level = system_level; | ||
302 | |||
303 | return snprintf(page, PAGE_SIZE, "%#018llx\n", level); | ||
304 | } | ||
305 | |||
306 | static ssize_t system_level_store(struct kobject *kobj, | ||
307 | struct kobj_attribute *attr, | ||
308 | const char *buf, | ||
309 | size_t len) | ||
310 | { | ||
311 | unsigned long long level; | ||
312 | char *endp; | ||
313 | |||
314 | level = simple_strtoull(buf, &endp, 16); | ||
315 | |||
316 | if (endp == buf) | ||
317 | return -EINVAL; | ||
318 | if (*endp == '\n') | ||
319 | endp++; | ||
320 | if (*endp) | ||
321 | return -EINVAL; | ||
322 | |||
323 | system_level = level; | ||
324 | |||
325 | return len; | ||
326 | } | ||
327 | |||
328 | static struct kobj_attribute system_level_attr = | ||
329 | __ATTR(system_level, 0644, system_level_show, system_level_store); | ||
330 | |||
331 | static ssize_t set_store(struct kobject *kobj, | ||
332 | struct kobj_attribute *attr, | ||
333 | const char *buf, size_t len) | ||
334 | { | ||
335 | int rc; | ||
336 | |||
337 | rc = cpi_req(); | ||
338 | if (rc) | ||
339 | return rc; | ||
340 | |||
341 | return len; | ||
342 | } | ||
343 | |||
344 | static struct kobj_attribute set_attr = __ATTR(set, 0200, NULL, set_store); | ||
345 | |||
346 | static struct attribute *cpi_attrs[] = { | ||
347 | &system_name_attr.attr, | ||
348 | &sysplex_name_attr.attr, | ||
349 | &system_type_attr.attr, | ||
350 | &system_level_attr.attr, | ||
351 | &set_attr.attr, | ||
352 | NULL, | ||
353 | }; | ||
354 | |||
355 | static struct attribute_group cpi_attr_group = { | ||
356 | .attrs = cpi_attrs, | ||
357 | }; | ||
358 | |||
359 | static struct kset *cpi_kset; | ||
360 | |||
361 | int sclp_cpi_set_data(const char *system, const char *sysplex, const char *type, | ||
362 | const u64 level) | ||
363 | { | ||
364 | int rc; | ||
365 | |||
366 | rc = check_string("system_name", system); | ||
367 | if (rc) | ||
368 | return rc; | ||
369 | rc = check_string("sysplex_name", sysplex); | ||
370 | if (rc) | ||
371 | return rc; | ||
372 | rc = check_string("system_type", type); | ||
373 | if (rc) | ||
374 | return rc; | ||
375 | |||
376 | set_string(system_name, system); | ||
377 | set_string(sysplex_name, sysplex); | ||
378 | set_string(system_type, type); | ||
379 | system_level = level; | ||
380 | |||
381 | return cpi_req(); | ||
382 | } | ||
383 | EXPORT_SYMBOL(sclp_cpi_set_data); | ||
384 | |||
385 | static int __init cpi_init(void) | ||
386 | { | ||
387 | int rc; | ||
388 | |||
389 | cpi_kset = kset_create_and_add("cpi", NULL, firmware_kobj); | ||
390 | if (!cpi_kset) | ||
391 | return -ENOMEM; | ||
392 | |||
393 | rc = sysfs_create_group(&cpi_kset->kobj, &cpi_attr_group); | ||
394 | if (rc) | ||
395 | kset_unregister(cpi_kset); | ||
396 | |||
397 | return rc; | ||
398 | } | ||
399 | |||
400 | __initcall(cpi_init); | ||