diff options
-rw-r--r-- | drivers/s390/char/Makefile | 2 | ||||
-rw-r--r-- | drivers/s390/char/sclp_cpi.c | 246 | ||||
-rw-r--r-- | drivers/s390/char/sclp_cpi_sys.c | 400 | ||||
-rw-r--r-- | drivers/s390/char/sclp_cpi_sys.h | 15 |
4 files changed, 432 insertions, 231 deletions
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 130de19916f2..bee3a3af691d 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile | |||
@@ -3,7 +3,7 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ | 5 | obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ |
6 | sclp_info.o sclp_config.o sclp_chp.o | 6 | sclp_info.o sclp_config.o sclp_chp.o sclp_cpi_sys.o |
7 | 7 | ||
8 | obj-$(CONFIG_TN3270) += raw3270.o | 8 | obj-$(CONFIG_TN3270) += raw3270.o |
9 | obj-$(CONFIG_TN3270_CONSOLE) += con3270.o | 9 | obj-$(CONFIG_TN3270_CONSOLE) += con3270.o |
diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c index 82a13d9fdfe4..5716487b8c9d 100644 --- a/drivers/s390/char/sclp_cpi.c +++ b/drivers/s390/char/sclp_cpi.c | |||
@@ -1,255 +1,41 @@ | |||
1 | /* | 1 | /* |
2 | * Author: Martin Peschke <mpeschke@de.ibm.com> | 2 | * drivers/s390/char/sclp_cpi.c |
3 | * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation | 3 | * SCLP control programm identification |
4 | * | 4 | * |
5 | * SCLP Control-Program Identification. | 5 | * Copyright IBM Corp. 2001, 2007 |
6 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> | ||
7 | * Michael Ernst <mernst@de.ibm.com> | ||
6 | */ | 8 | */ |
7 | 9 | ||
8 | #include <linux/version.h> | ||
9 | #include <linux/kmod.h> | 10 | #include <linux/kmod.h> |
10 | #include <linux/module.h> | 11 | #include <linux/module.h> |
11 | #include <linux/moduleparam.h> | 12 | #include <linux/moduleparam.h> |
12 | #include <linux/init.h> | 13 | #include <linux/version.h> |
13 | #include <linux/timer.h> | 14 | #include "sclp_cpi_sys.h" |
14 | #include <linux/string.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <asm/ebcdic.h> | ||
18 | #include <asm/semaphore.h> | ||
19 | |||
20 | #include "sclp.h" | ||
21 | #include "sclp_rw.h" | ||
22 | |||
23 | #define CPI_LENGTH_SYSTEM_TYPE 8 | ||
24 | #define CPI_LENGTH_SYSTEM_NAME 8 | ||
25 | #define CPI_LENGTH_SYSPLEX_NAME 8 | ||
26 | |||
27 | struct cpi_evbuf { | ||
28 | struct evbuf_header header; | ||
29 | u8 id_format; | ||
30 | u8 reserved0; | ||
31 | u8 system_type[CPI_LENGTH_SYSTEM_TYPE]; | ||
32 | u64 reserved1; | ||
33 | u8 system_name[CPI_LENGTH_SYSTEM_NAME]; | ||
34 | u64 reserved2; | ||
35 | u64 system_level; | ||
36 | u64 reserved3; | ||
37 | u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME]; | ||
38 | u8 reserved4[16]; | ||
39 | } __attribute__((packed)); | ||
40 | |||
41 | struct cpi_sccb { | ||
42 | struct sccb_header header; | ||
43 | struct cpi_evbuf cpi_evbuf; | ||
44 | } __attribute__((packed)); | ||
45 | |||
46 | /* Event type structure for write message and write priority message */ | ||
47 | static struct sclp_register sclp_cpi_event = | ||
48 | { | ||
49 | .send_mask = EVTYP_CTLPROGIDENT_MASK | ||
50 | }; | ||
51 | 15 | ||
52 | MODULE_LICENSE("GPL"); | 16 | MODULE_LICENSE("GPL"); |
17 | MODULE_DESCRIPTION("Identify this operating system instance " | ||
18 | "to the System z hardware"); | ||
19 | MODULE_AUTHOR("Martin Peschke <mpeschke@de.ibm.com>, " | ||
20 | "Michael Ernst <mernst@de.ibm.com>"); | ||
53 | 21 | ||
54 | MODULE_AUTHOR( | 22 | static char *system_name = ""; |
55 | "Martin Peschke, IBM Deutschland Entwicklung GmbH " | 23 | static char *sysplex_name = ""; |
56 | "<mpeschke@de.ibm.com>"); | ||
57 | |||
58 | MODULE_DESCRIPTION( | ||
59 | "identify this operating system instance to the S/390 " | ||
60 | "or zSeries hardware"); | ||
61 | 24 | ||
62 | static char *system_name = NULL; | ||
63 | module_param(system_name, charp, 0); | 25 | module_param(system_name, charp, 0); |
64 | MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters"); | 26 | MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters"); |
65 | |||
66 | static char *sysplex_name = NULL; | ||
67 | #ifdef ALLOW_SYSPLEX_NAME | ||
68 | module_param(sysplex_name, charp, 0); | 27 | module_param(sysplex_name, charp, 0); |
69 | MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters"); | 28 | MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters"); |
70 | #endif | ||
71 | |||
72 | /* use default value for this field (as well as for system level) */ | ||
73 | static char *system_type = "LINUX"; | ||
74 | 29 | ||
75 | static int | 30 | static int __init cpi_module_init(void) |
76 | cpi_check_parms(void) | ||
77 | { | 31 | { |
78 | /* reject if no system type specified */ | 32 | return sclp_cpi_set_data(system_name, sysplex_name, "LINUX", |
79 | if (!system_type) { | 33 | LINUX_VERSION_CODE); |
80 | printk("cpi: bug: no system type specified\n"); | ||
81 | return -EINVAL; | ||
82 | } | ||
83 | |||
84 | /* reject if system type larger than 8 characters */ | ||
85 | if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) { | ||
86 | printk("cpi: bug: system type has length of %li characters - " | ||
87 | "only %i characters supported\n", | ||
88 | strlen(system_type), CPI_LENGTH_SYSTEM_TYPE); | ||
89 | return -EINVAL; | ||
90 | } | ||
91 | |||
92 | /* reject if no system name specified */ | ||
93 | if (!system_name) { | ||
94 | printk("cpi: no system name specified\n"); | ||
95 | return -EINVAL; | ||
96 | } | ||
97 | |||
98 | /* reject if system name larger than 8 characters */ | ||
99 | if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) { | ||
100 | printk("cpi: system name has length of %li characters - " | ||
101 | "only %i characters supported\n", | ||
102 | strlen(system_name), CPI_LENGTH_SYSTEM_NAME); | ||
103 | return -EINVAL; | ||
104 | } | ||
105 | |||
106 | /* reject if specified sysplex name larger than 8 characters */ | ||
107 | if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) { | ||
108 | printk("cpi: sysplex name has length of %li characters" | ||
109 | " - only %i characters supported\n", | ||
110 | strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME); | ||
111 | return -EINVAL; | ||
112 | } | ||
113 | return 0; | ||
114 | } | 34 | } |
115 | 35 | ||
116 | static void | ||
117 | cpi_callback(struct sclp_req *req, void *data) | ||
118 | { | ||
119 | struct semaphore *sem; | ||
120 | |||
121 | sem = (struct semaphore *) data; | ||
122 | up(sem); | ||
123 | } | ||
124 | |||
125 | static struct sclp_req * | ||
126 | cpi_prepare_req(void) | ||
127 | { | ||
128 | struct sclp_req *req; | ||
129 | struct cpi_sccb *sccb; | ||
130 | struct cpi_evbuf *evb; | ||
131 | |||
132 | req = kmalloc(sizeof(struct sclp_req), GFP_KERNEL); | ||
133 | if (req == NULL) | ||
134 | return ERR_PTR(-ENOMEM); | ||
135 | sccb = (struct cpi_sccb *) __get_free_page(GFP_KERNEL | GFP_DMA); | ||
136 | if (sccb == NULL) { | ||
137 | kfree(req); | ||
138 | return ERR_PTR(-ENOMEM); | ||
139 | } | ||
140 | memset(sccb, 0, sizeof(struct cpi_sccb)); | ||
141 | |||
142 | /* setup SCCB for Control-Program Identification */ | ||
143 | sccb->header.length = sizeof(struct cpi_sccb); | ||
144 | sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); | ||
145 | sccb->cpi_evbuf.header.type = 0x0B; | ||
146 | evb = &sccb->cpi_evbuf; | ||
147 | |||
148 | /* set system type */ | ||
149 | memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE); | ||
150 | memcpy(evb->system_type, system_type, strlen(system_type)); | ||
151 | sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); | ||
152 | EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); | ||
153 | |||
154 | /* set system name */ | ||
155 | memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME); | ||
156 | memcpy(evb->system_name, system_name, strlen(system_name)); | ||
157 | sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME); | ||
158 | EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME); | ||
159 | |||
160 | /* set system level */ | ||
161 | evb->system_level = LINUX_VERSION_CODE; | ||
162 | |||
163 | /* set sysplex name */ | ||
164 | if (sysplex_name) { | ||
165 | memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME); | ||
166 | memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name)); | ||
167 | sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); | ||
168 | EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); | ||
169 | } | ||
170 | |||
171 | /* prepare request data structure presented to SCLP driver */ | ||
172 | req->command = SCLP_CMDW_WRITE_EVENT_DATA; | ||
173 | req->sccb = sccb; | ||
174 | req->status = SCLP_REQ_FILLED; | ||
175 | req->callback = cpi_callback; | ||
176 | return req; | ||
177 | } | ||
178 | |||
179 | static void | ||
180 | cpi_free_req(struct sclp_req *req) | ||
181 | { | ||
182 | free_page((unsigned long) req->sccb); | ||
183 | kfree(req); | ||
184 | } | ||
185 | |||
186 | static int __init | ||
187 | cpi_module_init(void) | ||
188 | { | ||
189 | struct semaphore sem; | ||
190 | struct sclp_req *req; | ||
191 | int rc; | ||
192 | |||
193 | rc = cpi_check_parms(); | ||
194 | if (rc) | ||
195 | return rc; | ||
196 | |||
197 | rc = sclp_register(&sclp_cpi_event); | ||
198 | if (rc) { | ||
199 | /* could not register sclp event. Die. */ | ||
200 | printk(KERN_WARNING "cpi: could not register to hardware " | ||
201 | "console.\n"); | ||
202 | return -EINVAL; | ||
203 | } | ||
204 | if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) { | ||
205 | printk(KERN_WARNING "cpi: no control program identification " | ||
206 | "support\n"); | ||
207 | sclp_unregister(&sclp_cpi_event); | ||
208 | return -EOPNOTSUPP; | ||
209 | } | ||
210 | |||
211 | req = cpi_prepare_req(); | ||
212 | if (IS_ERR(req)) { | ||
213 | printk(KERN_WARNING "cpi: couldn't allocate request\n"); | ||
214 | sclp_unregister(&sclp_cpi_event); | ||
215 | return PTR_ERR(req); | ||
216 | } | ||
217 | |||
218 | /* Prepare semaphore */ | ||
219 | sema_init(&sem, 0); | ||
220 | req->callback_data = &sem; | ||
221 | /* Add request to sclp queue */ | ||
222 | rc = sclp_add_request(req); | ||
223 | if (rc) { | ||
224 | printk(KERN_WARNING "cpi: could not start request\n"); | ||
225 | cpi_free_req(req); | ||
226 | sclp_unregister(&sclp_cpi_event); | ||
227 | return rc; | ||
228 | } | ||
229 | /* make "insmod" sleep until callback arrives */ | ||
230 | down(&sem); | ||
231 | |||
232 | rc = ((struct cpi_sccb *) req->sccb)->header.response_code; | ||
233 | if (rc != 0x0020) { | ||
234 | printk(KERN_WARNING "cpi: failed with response code 0x%x\n", | ||
235 | rc); | ||
236 | rc = -ECOMM; | ||
237 | } else | ||
238 | rc = 0; | ||
239 | |||
240 | cpi_free_req(req); | ||
241 | sclp_unregister(&sclp_cpi_event); | ||
242 | |||
243 | return rc; | ||
244 | } | ||
245 | |||
246 | |||
247 | static void __exit cpi_module_exit(void) | 36 | static void __exit cpi_module_exit(void) |
248 | { | 37 | { |
249 | } | 38 | } |
250 | 39 | ||
251 | |||
252 | /* declare driver module init/cleanup functions */ | ||
253 | module_init(cpi_module_init); | 40 | module_init(cpi_module_init); |
254 | module_exit(cpi_module_exit); | 41 | module_exit(cpi_module_exit); |
255 | |||
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); | ||
diff --git a/drivers/s390/char/sclp_cpi_sys.h b/drivers/s390/char/sclp_cpi_sys.h new file mode 100644 index 000000000000..deef3e6ff496 --- /dev/null +++ b/drivers/s390/char/sclp_cpi_sys.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /* | ||
2 | * drivers/s390/char/sclp_cpi_sys.h | ||
3 | * SCLP control program identification sysfs interface | ||
4 | * | ||
5 | * Copyright IBM Corp. 2007 | ||
6 | * Author(s): Michael Ernst <mernst@de.ibm.com> | ||
7 | */ | ||
8 | |||
9 | #ifndef __SCLP_CPI_SYS_H__ | ||
10 | #define __SCLP_CPI_SYS_H__ | ||
11 | |||
12 | int sclp_cpi_set_data(const char *system, const char *sysplex, | ||
13 | const char *type, u64 level); | ||
14 | |||
15 | #endif /* __SCLP_CPI_SYS_H__ */ | ||