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