diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/s390/char/raw3270.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/s390/char/raw3270.c')
-rw-r--r-- | drivers/s390/char/raw3270.c | 1335 |
1 files changed, 1335 insertions, 0 deletions
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c new file mode 100644 index 000000000000..8e16a9716686 --- /dev/null +++ b/drivers/s390/char/raw3270.c | |||
@@ -0,0 +1,1335 @@ | |||
1 | /* | ||
2 | * drivers/s390/char/raw3270.c | ||
3 | * IBM/3270 Driver - core functions. | ||
4 | * | ||
5 | * Author(s): | ||
6 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) | ||
7 | * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
8 | * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/bootmem.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/err.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/list.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/wait.h> | ||
21 | |||
22 | #include <asm/ccwdev.h> | ||
23 | #include <asm/cio.h> | ||
24 | #include <asm/ebcdic.h> | ||
25 | |||
26 | #include "raw3270.h" | ||
27 | |||
28 | /* The main 3270 data structure. */ | ||
29 | struct raw3270 { | ||
30 | struct list_head list; | ||
31 | struct ccw_device *cdev; | ||
32 | int minor; | ||
33 | |||
34 | short model, rows, cols; | ||
35 | unsigned long flags; | ||
36 | |||
37 | struct list_head req_queue; /* Request queue. */ | ||
38 | struct list_head view_list; /* List of available views. */ | ||
39 | struct raw3270_view *view; /* Active view. */ | ||
40 | |||
41 | struct timer_list timer; /* Device timer. */ | ||
42 | |||
43 | unsigned char *ascebc; /* ascii -> ebcdic table */ | ||
44 | }; | ||
45 | |||
46 | /* raw3270->flags */ | ||
47 | #define RAW3270_FLAGS_14BITADDR 0 /* 14-bit buffer addresses */ | ||
48 | #define RAW3270_FLAGS_BUSY 1 /* Device busy, leave it alone */ | ||
49 | #define RAW3270_FLAGS_ATTN 2 /* Device sent an ATTN interrupt */ | ||
50 | #define RAW3270_FLAGS_READY 4 /* Device is useable by views */ | ||
51 | #define RAW3270_FLAGS_CONSOLE 8 /* Device is the console. */ | ||
52 | |||
53 | /* Semaphore to protect global data of raw3270 (devices, views, etc). */ | ||
54 | static DECLARE_MUTEX(raw3270_sem); | ||
55 | |||
56 | /* List of 3270 devices. */ | ||
57 | static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices); | ||
58 | |||
59 | /* | ||
60 | * Flag to indicate if the driver has been registered. Some operations | ||
61 | * like waiting for the end of i/o need to be done differently as long | ||
62 | * as the kernel is still starting up (console support). | ||
63 | */ | ||
64 | static int raw3270_registered; | ||
65 | |||
66 | /* Module parameters */ | ||
67 | static int tubxcorrect = 0; | ||
68 | module_param(tubxcorrect, bool, 0); | ||
69 | |||
70 | /* | ||
71 | * Wait queue for device init/delete, view delete. | ||
72 | */ | ||
73 | DECLARE_WAIT_QUEUE_HEAD(raw3270_wait_queue); | ||
74 | |||
75 | /* | ||
76 | * Encode array for 12 bit 3270 addresses. | ||
77 | */ | ||
78 | unsigned char raw3270_ebcgraf[64] = { | ||
79 | 0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, | ||
80 | 0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, | ||
81 | 0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, | ||
82 | 0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, | ||
83 | 0x60, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, | ||
84 | 0xe8, 0xe9, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, | ||
85 | 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, | ||
86 | 0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f | ||
87 | }; | ||
88 | |||
89 | void | ||
90 | raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr) | ||
91 | { | ||
92 | if (test_bit(RAW3270_FLAGS_14BITADDR, &rp->flags)) { | ||
93 | cp[0] = (addr >> 8) & 0x3f; | ||
94 | cp[1] = addr & 0xff; | ||
95 | } else { | ||
96 | cp[0] = raw3270_ebcgraf[(addr >> 6) & 0x3f]; | ||
97 | cp[1] = raw3270_ebcgraf[addr & 0x3f]; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * Allocate a new 3270 ccw request | ||
103 | */ | ||
104 | struct raw3270_request * | ||
105 | raw3270_request_alloc(size_t size) | ||
106 | { | ||
107 | struct raw3270_request *rq; | ||
108 | |||
109 | /* Allocate request structure */ | ||
110 | rq = kmalloc(sizeof(struct raw3270_request), GFP_KERNEL | GFP_DMA); | ||
111 | if (!rq) | ||
112 | return ERR_PTR(-ENOMEM); | ||
113 | memset(rq, 0, sizeof(struct raw3270_request)); | ||
114 | |||
115 | /* alloc output buffer. */ | ||
116 | if (size > 0) { | ||
117 | rq->buffer = kmalloc(size, GFP_KERNEL | GFP_DMA); | ||
118 | if (!rq->buffer) { | ||
119 | kfree(rq); | ||
120 | return ERR_PTR(-ENOMEM); | ||
121 | } | ||
122 | } | ||
123 | rq->size = size; | ||
124 | INIT_LIST_HEAD(&rq->list); | ||
125 | |||
126 | /* | ||
127 | * Setup ccw. | ||
128 | */ | ||
129 | rq->ccw.cda = __pa(rq->buffer); | ||
130 | rq->ccw.flags = CCW_FLAG_SLI; | ||
131 | |||
132 | return rq; | ||
133 | } | ||
134 | |||
135 | #ifdef CONFIG_TN3270_CONSOLE | ||
136 | /* | ||
137 | * Allocate a new 3270 ccw request from bootmem. Only works very | ||
138 | * early in the boot process. Only con3270.c should be using this. | ||
139 | */ | ||
140 | struct raw3270_request * | ||
141 | raw3270_request_alloc_bootmem(size_t size) | ||
142 | { | ||
143 | struct raw3270_request *rq; | ||
144 | |||
145 | rq = alloc_bootmem_low(sizeof(struct raw3270)); | ||
146 | if (!rq) | ||
147 | return ERR_PTR(-ENOMEM); | ||
148 | memset(rq, 0, sizeof(struct raw3270_request)); | ||
149 | |||
150 | /* alloc output buffer. */ | ||
151 | if (size > 0) { | ||
152 | rq->buffer = alloc_bootmem_low(size); | ||
153 | if (!rq->buffer) { | ||
154 | free_bootmem((unsigned long) rq, | ||
155 | sizeof(struct raw3270)); | ||
156 | return ERR_PTR(-ENOMEM); | ||
157 | } | ||
158 | } | ||
159 | rq->size = size; | ||
160 | INIT_LIST_HEAD(&rq->list); | ||
161 | |||
162 | /* | ||
163 | * Setup ccw. | ||
164 | */ | ||
165 | rq->ccw.cda = __pa(rq->buffer); | ||
166 | rq->ccw.flags = CCW_FLAG_SLI; | ||
167 | |||
168 | return rq; | ||
169 | } | ||
170 | #endif | ||
171 | |||
172 | /* | ||
173 | * Free 3270 ccw request | ||
174 | */ | ||
175 | void | ||
176 | raw3270_request_free (struct raw3270_request *rq) | ||
177 | { | ||
178 | if (rq->buffer) | ||
179 | kfree(rq->buffer); | ||
180 | kfree(rq); | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * Reset request to initial state. | ||
185 | */ | ||
186 | void | ||
187 | raw3270_request_reset(struct raw3270_request *rq) | ||
188 | { | ||
189 | BUG_ON(!list_empty(&rq->list)); | ||
190 | rq->ccw.cmd_code = 0; | ||
191 | rq->ccw.count = 0; | ||
192 | rq->ccw.cda = __pa(rq->buffer); | ||
193 | rq->ccw.flags = CCW_FLAG_SLI; | ||
194 | rq->rescnt = 0; | ||
195 | rq->rc = 0; | ||
196 | } | ||
197 | |||
198 | /* | ||
199 | * Set command code to ccw of a request. | ||
200 | */ | ||
201 | void | ||
202 | raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd) | ||
203 | { | ||
204 | rq->ccw.cmd_code = cmd; | ||
205 | } | ||
206 | |||
207 | /* | ||
208 | * Add data fragment to output buffer. | ||
209 | */ | ||
210 | int | ||
211 | raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size) | ||
212 | { | ||
213 | if (size + rq->ccw.count > rq->size) | ||
214 | return -E2BIG; | ||
215 | memcpy(rq->buffer + rq->ccw.count, data, size); | ||
216 | rq->ccw.count += size; | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * Set address/length pair to ccw of a request. | ||
222 | */ | ||
223 | void | ||
224 | raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size) | ||
225 | { | ||
226 | rq->ccw.cda = __pa(data); | ||
227 | rq->ccw.count = size; | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * Set idal buffer to ccw of a request. | ||
232 | */ | ||
233 | void | ||
234 | raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) | ||
235 | { | ||
236 | rq->ccw.cda = __pa(ib->data); | ||
237 | rq->ccw.count = ib->size; | ||
238 | rq->ccw.flags |= CCW_FLAG_IDA; | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * Stop running ccw. | ||
243 | */ | ||
244 | static int | ||
245 | raw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq) | ||
246 | { | ||
247 | int retries; | ||
248 | int rc; | ||
249 | |||
250 | if (raw3270_request_final(rq)) | ||
251 | return 0; | ||
252 | /* Check if interrupt has already been processed */ | ||
253 | for (retries = 0; retries < 5; retries++) { | ||
254 | if (retries < 2) | ||
255 | rc = ccw_device_halt(rp->cdev, (long) rq); | ||
256 | else | ||
257 | rc = ccw_device_clear(rp->cdev, (long) rq); | ||
258 | if (rc == 0) | ||
259 | break; /* termination successful */ | ||
260 | } | ||
261 | return rc; | ||
262 | } | ||
263 | |||
264 | static int | ||
265 | raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq) | ||
266 | { | ||
267 | unsigned long flags; | ||
268 | int rc; | ||
269 | |||
270 | spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); | ||
271 | rc = raw3270_halt_io_nolock(rp, rq); | ||
272 | spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); | ||
273 | return rc; | ||
274 | } | ||
275 | |||
276 | /* | ||
277 | * Add the request to the request queue, try to start it if the | ||
278 | * 3270 device is idle. Return without waiting for end of i/o. | ||
279 | */ | ||
280 | static int | ||
281 | __raw3270_start(struct raw3270 *rp, struct raw3270_view *view, | ||
282 | struct raw3270_request *rq) | ||
283 | { | ||
284 | rq->view = view; | ||
285 | raw3270_get_view(view); | ||
286 | if (list_empty(&rp->req_queue) && | ||
287 | !test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) { | ||
288 | /* No other requests are on the queue. Start this one. */ | ||
289 | rq->rc = ccw_device_start(rp->cdev, &rq->ccw, | ||
290 | (unsigned long) rq, 0, 0); | ||
291 | if (rq->rc) { | ||
292 | raw3270_put_view(view); | ||
293 | return rq->rc; | ||
294 | } | ||
295 | } | ||
296 | list_add_tail(&rq->list, &rp->req_queue); | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | int | ||
301 | raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) | ||
302 | { | ||
303 | unsigned long flags; | ||
304 | struct raw3270 *rp; | ||
305 | int rc; | ||
306 | |||
307 | spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); | ||
308 | rp = view->dev; | ||
309 | if (!rp || rp->view != view) | ||
310 | rc = -EACCES; | ||
311 | else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) | ||
312 | rc = -ENODEV; | ||
313 | else | ||
314 | rc = __raw3270_start(rp, view, rq); | ||
315 | spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); | ||
316 | return rc; | ||
317 | } | ||
318 | |||
319 | int | ||
320 | raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq) | ||
321 | { | ||
322 | struct raw3270 *rp; | ||
323 | |||
324 | rp = view->dev; | ||
325 | rq->view = view; | ||
326 | raw3270_get_view(view); | ||
327 | list_add_tail(&rq->list, &rp->req_queue); | ||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | /* | ||
332 | * 3270 interrupt routine, called from the ccw_device layer | ||
333 | */ | ||
334 | static void | ||
335 | raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) | ||
336 | { | ||
337 | struct raw3270 *rp; | ||
338 | struct raw3270_view *view; | ||
339 | struct raw3270_request *rq; | ||
340 | int rc; | ||
341 | |||
342 | rp = (struct raw3270 *) cdev->dev.driver_data; | ||
343 | if (!rp) | ||
344 | return; | ||
345 | rq = (struct raw3270_request *) intparm; | ||
346 | view = rq ? rq->view : rp->view; | ||
347 | |||
348 | if (IS_ERR(irb)) | ||
349 | rc = RAW3270_IO_RETRY; | ||
350 | else if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) { | ||
351 | rq->rc = -EIO; | ||
352 | rc = RAW3270_IO_DONE; | ||
353 | } else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END | | ||
354 | DEV_STAT_UNIT_EXCEP)) { | ||
355 | /* Handle CE-DE-UE and subsequent UDE */ | ||
356 | set_bit(RAW3270_FLAGS_BUSY, &rp->flags); | ||
357 | rc = RAW3270_IO_BUSY; | ||
358 | } else if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) { | ||
359 | /* Wait for UDE if busy flag is set. */ | ||
360 | if (irb->scsw.dstat & DEV_STAT_DEV_END) { | ||
361 | clear_bit(RAW3270_FLAGS_BUSY, &rp->flags); | ||
362 | /* Got it, now retry. */ | ||
363 | rc = RAW3270_IO_RETRY; | ||
364 | } else | ||
365 | rc = RAW3270_IO_BUSY; | ||
366 | } else if (view) | ||
367 | rc = view->fn->intv(view, rq, irb); | ||
368 | else | ||
369 | rc = RAW3270_IO_DONE; | ||
370 | |||
371 | switch (rc) { | ||
372 | case RAW3270_IO_DONE: | ||
373 | break; | ||
374 | case RAW3270_IO_BUSY: | ||
375 | /* | ||
376 | * Intervention required by the operator. We have to wait | ||
377 | * for unsolicited device end. | ||
378 | */ | ||
379 | return; | ||
380 | case RAW3270_IO_RETRY: | ||
381 | if (!rq) | ||
382 | break; | ||
383 | rq->rc = ccw_device_start(rp->cdev, &rq->ccw, | ||
384 | (unsigned long) rq, 0, 0); | ||
385 | if (rq->rc == 0) | ||
386 | return; /* Sucessfully restarted. */ | ||
387 | break; | ||
388 | case RAW3270_IO_STOP: | ||
389 | if (!rq) | ||
390 | break; | ||
391 | raw3270_halt_io_nolock(rp, rq); | ||
392 | rq->rc = -EIO; | ||
393 | break; | ||
394 | default: | ||
395 | BUG(); | ||
396 | } | ||
397 | if (rq) { | ||
398 | BUG_ON(list_empty(&rq->list)); | ||
399 | /* The request completed, remove from queue and do callback. */ | ||
400 | list_del_init(&rq->list); | ||
401 | if (rq->callback) | ||
402 | rq->callback(rq, rq->callback_data); | ||
403 | /* Do put_device for get_device in raw3270_start. */ | ||
404 | raw3270_put_view(view); | ||
405 | } | ||
406 | /* | ||
407 | * Try to start each request on request queue until one is | ||
408 | * started successful. | ||
409 | */ | ||
410 | while (!list_empty(&rp->req_queue)) { | ||
411 | rq = list_entry(rp->req_queue.next,struct raw3270_request,list); | ||
412 | rq->rc = ccw_device_start(rp->cdev, &rq->ccw, | ||
413 | (unsigned long) rq, 0, 0); | ||
414 | if (rq->rc == 0) | ||
415 | break; | ||
416 | /* Start failed. Remove request and do callback. */ | ||
417 | list_del_init(&rq->list); | ||
418 | if (rq->callback) | ||
419 | rq->callback(rq, rq->callback_data); | ||
420 | /* Do put_device for get_device in raw3270_start. */ | ||
421 | raw3270_put_view(view); | ||
422 | } | ||
423 | } | ||
424 | |||
425 | /* | ||
426 | * Size sensing. | ||
427 | */ | ||
428 | |||
429 | struct raw3270_ua { /* Query Reply structure for Usable Area */ | ||
430 | struct { /* Usable Area Query Reply Base */ | ||
431 | short l; /* Length of this structured field */ | ||
432 | char sfid; /* 0x81 if Query Reply */ | ||
433 | char qcode; /* 0x81 if Usable Area */ | ||
434 | char flags0; | ||
435 | char flags1; | ||
436 | short w; /* Width of usable area */ | ||
437 | short h; /* Heigth of usavle area */ | ||
438 | char units; /* 0x00:in; 0x01:mm */ | ||
439 | int xr; | ||
440 | int yr; | ||
441 | char aw; | ||
442 | char ah; | ||
443 | short buffsz; /* Character buffer size, bytes */ | ||
444 | char xmin; | ||
445 | char ymin; | ||
446 | char xmax; | ||
447 | char ymax; | ||
448 | } __attribute__ ((packed)) uab; | ||
449 | struct { /* Alternate Usable Area Self-Defining Parameter */ | ||
450 | char l; /* Length of this Self-Defining Parm */ | ||
451 | char sdpid; /* 0x02 if Alternate Usable Area */ | ||
452 | char res; | ||
453 | char auaid; /* 0x01 is Id for the A U A */ | ||
454 | short wauai; /* Width of AUAi */ | ||
455 | short hauai; /* Height of AUAi */ | ||
456 | char auaunits; /* 0x00:in, 0x01:mm */ | ||
457 | int auaxr; | ||
458 | int auayr; | ||
459 | char awauai; | ||
460 | char ahauai; | ||
461 | } __attribute__ ((packed)) aua; | ||
462 | } __attribute__ ((packed)); | ||
463 | |||
464 | static unsigned char raw3270_init_data[256]; | ||
465 | static struct raw3270_request raw3270_init_request; | ||
466 | static struct diag210 raw3270_init_diag210; | ||
467 | static DECLARE_MUTEX(raw3270_init_sem); | ||
468 | |||
469 | static int | ||
470 | raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, | ||
471 | struct irb *irb) | ||
472 | { | ||
473 | /* | ||
474 | * Unit-Check Processing: | ||
475 | * Expect Command Reject or Intervention Required. | ||
476 | */ | ||
477 | if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { | ||
478 | /* Request finished abnormally. */ | ||
479 | if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { | ||
480 | set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); | ||
481 | return RAW3270_IO_BUSY; | ||
482 | } | ||
483 | } | ||
484 | if (rq) { | ||
485 | if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { | ||
486 | if (irb->ecw[0] & SNS0_CMD_REJECT) | ||
487 | rq->rc = -EOPNOTSUPP; | ||
488 | else | ||
489 | rq->rc = -EIO; | ||
490 | } else | ||
491 | /* Request finished normally. Copy residual count. */ | ||
492 | rq->rescnt = irb->scsw.count; | ||
493 | } | ||
494 | if (irb->scsw.dstat & DEV_STAT_ATTENTION) { | ||
495 | set_bit(RAW3270_FLAGS_ATTN, &view->dev->flags); | ||
496 | wake_up(&raw3270_wait_queue); | ||
497 | } | ||
498 | return RAW3270_IO_DONE; | ||
499 | } | ||
500 | |||
501 | static struct raw3270_fn raw3270_init_fn = { | ||
502 | .intv = raw3270_init_irq | ||
503 | }; | ||
504 | |||
505 | static struct raw3270_view raw3270_init_view = { | ||
506 | .fn = &raw3270_init_fn | ||
507 | }; | ||
508 | |||
509 | /* | ||
510 | * raw3270_wait/raw3270_wait_interruptible/__raw3270_wakeup | ||
511 | * Wait for end of request. The request must have been started | ||
512 | * with raw3270_start, rc = 0. The device lock may NOT have been | ||
513 | * released between calling raw3270_start and raw3270_wait. | ||
514 | */ | ||
515 | static void | ||
516 | raw3270_wake_init(struct raw3270_request *rq, void *data) | ||
517 | { | ||
518 | wake_up((wait_queue_head_t *) data); | ||
519 | } | ||
520 | |||
521 | /* | ||
522 | * Special wait function that can cope with console initialization. | ||
523 | */ | ||
524 | static int | ||
525 | raw3270_start_init(struct raw3270 *rp, struct raw3270_view *view, | ||
526 | struct raw3270_request *rq) | ||
527 | { | ||
528 | unsigned long flags; | ||
529 | wait_queue_head_t wq; | ||
530 | int rc; | ||
531 | |||
532 | #ifdef CONFIG_TN3270_CONSOLE | ||
533 | if (raw3270_registered == 0) { | ||
534 | spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); | ||
535 | rq->callback = 0; | ||
536 | rc = __raw3270_start(rp, view, rq); | ||
537 | if (rc == 0) | ||
538 | while (!raw3270_request_final(rq)) { | ||
539 | wait_cons_dev(); | ||
540 | barrier(); | ||
541 | } | ||
542 | spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); | ||
543 | return rq->rc; | ||
544 | } | ||
545 | #endif | ||
546 | init_waitqueue_head(&wq); | ||
547 | rq->callback = raw3270_wake_init; | ||
548 | rq->callback_data = &wq; | ||
549 | spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); | ||
550 | rc = __raw3270_start(rp, view, rq); | ||
551 | spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); | ||
552 | if (rc) | ||
553 | return rc; | ||
554 | /* Now wait for the completion. */ | ||
555 | rc = wait_event_interruptible(wq, raw3270_request_final(rq)); | ||
556 | if (rc == -ERESTARTSYS) { /* Interrupted by a signal. */ | ||
557 | raw3270_halt_io(view->dev, rq); | ||
558 | /* No wait for the halt to complete. */ | ||
559 | wait_event(wq, raw3270_request_final(rq)); | ||
560 | return -ERESTARTSYS; | ||
561 | } | ||
562 | return rq->rc; | ||
563 | } | ||
564 | |||
565 | static int | ||
566 | __raw3270_size_device_vm(struct raw3270 *rp) | ||
567 | { | ||
568 | int rc, model; | ||
569 | |||
570 | raw3270_init_diag210.vrdcdvno = | ||
571 | _ccw_device_get_device_number(rp->cdev); | ||
572 | raw3270_init_diag210.vrdclen = sizeof(struct diag210); | ||
573 | rc = diag210(&raw3270_init_diag210); | ||
574 | if (rc) | ||
575 | return rc; | ||
576 | model = raw3270_init_diag210.vrdccrmd; | ||
577 | switch (model) { | ||
578 | case 2: | ||
579 | rp->model = model; | ||
580 | rp->rows = 24; | ||
581 | rp->cols = 80; | ||
582 | break; | ||
583 | case 3: | ||
584 | rp->model = model; | ||
585 | rp->rows = 32; | ||
586 | rp->cols = 80; | ||
587 | break; | ||
588 | case 4: | ||
589 | rp->model = model; | ||
590 | rp->rows = 43; | ||
591 | rp->cols = 80; | ||
592 | break; | ||
593 | case 5: | ||
594 | rp->model = model; | ||
595 | rp->rows = 27; | ||
596 | rp->cols = 132; | ||
597 | break; | ||
598 | default: | ||
599 | printk(KERN_WARNING "vrdccrmd is 0x%.8x\n", model); | ||
600 | rc = -EOPNOTSUPP; | ||
601 | break; | ||
602 | } | ||
603 | return rc; | ||
604 | } | ||
605 | |||
606 | static int | ||
607 | __raw3270_size_device(struct raw3270 *rp) | ||
608 | { | ||
609 | static const unsigned char wbuf[] = | ||
610 | { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; | ||
611 | struct raw3270_ua *uap; | ||
612 | unsigned short count; | ||
613 | int rc; | ||
614 | |||
615 | /* | ||
616 | * To determine the size of the 3270 device we need to do: | ||
617 | * 1) send a 'read partition' data stream to the device | ||
618 | * 2) wait for the attn interrupt that preceeds the query reply | ||
619 | * 3) do a read modified to get the query reply | ||
620 | * To make things worse we have to cope with intervention | ||
621 | * required (3270 device switched to 'stand-by') and command | ||
622 | * rejects (old devices that can't do 'read partition'). | ||
623 | */ | ||
624 | memset(&raw3270_init_request, 0, sizeof(raw3270_init_request)); | ||
625 | memset(raw3270_init_data, 0, sizeof(raw3270_init_data)); | ||
626 | /* Store 'read partition' data stream to raw3270_init_data */ | ||
627 | memcpy(raw3270_init_data, wbuf, sizeof(wbuf)); | ||
628 | INIT_LIST_HEAD(&raw3270_init_request.list); | ||
629 | raw3270_init_request.ccw.cmd_code = TC_WRITESF; | ||
630 | raw3270_init_request.ccw.flags = CCW_FLAG_SLI; | ||
631 | raw3270_init_request.ccw.count = sizeof(wbuf); | ||
632 | raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data); | ||
633 | |||
634 | rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request); | ||
635 | if (rc) { | ||
636 | /* Check error cases: -ERESTARTSYS, -EIO and -EOPNOTSUPP */ | ||
637 | if (rc == -EOPNOTSUPP && MACHINE_IS_VM) | ||
638 | return __raw3270_size_device_vm(rp); | ||
639 | return rc; | ||
640 | } | ||
641 | |||
642 | /* Wait for attention interrupt. */ | ||
643 | #ifdef CONFIG_TN3270_CONSOLE | ||
644 | if (raw3270_registered == 0) { | ||
645 | unsigned long flags; | ||
646 | |||
647 | spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); | ||
648 | while (!test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags)) | ||
649 | wait_cons_dev(); | ||
650 | spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); | ||
651 | } else | ||
652 | #endif | ||
653 | rc = wait_event_interruptible(raw3270_wait_queue, | ||
654 | test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags)); | ||
655 | if (rc) | ||
656 | return rc; | ||
657 | |||
658 | /* | ||
659 | * The device accepted the 'read partition' command. Now | ||
660 | * set up a read ccw and issue it. | ||
661 | */ | ||
662 | raw3270_init_request.ccw.cmd_code = TC_READMOD; | ||
663 | raw3270_init_request.ccw.flags = CCW_FLAG_SLI; | ||
664 | raw3270_init_request.ccw.count = sizeof(raw3270_init_data); | ||
665 | raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data); | ||
666 | rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request); | ||
667 | if (rc) | ||
668 | return rc; | ||
669 | /* Got a Query Reply */ | ||
670 | count = sizeof(raw3270_init_data) - raw3270_init_request.rescnt; | ||
671 | uap = (struct raw3270_ua *) (raw3270_init_data + 1); | ||
672 | /* Paranoia check. */ | ||
673 | if (raw3270_init_data[0] != 0x88 || uap->uab.qcode != 0x81) | ||
674 | return -EOPNOTSUPP; | ||
675 | /* Copy rows/columns of default Usable Area */ | ||
676 | rp->rows = uap->uab.h; | ||
677 | rp->cols = uap->uab.w; | ||
678 | /* Check for 14 bit addressing */ | ||
679 | if ((uap->uab.flags0 & 0x0d) == 0x01) | ||
680 | set_bit(RAW3270_FLAGS_14BITADDR, &rp->flags); | ||
681 | /* Check for Alternate Usable Area */ | ||
682 | if (uap->uab.l == sizeof(struct raw3270_ua) && | ||
683 | uap->aua.sdpid == 0x02) { | ||
684 | rp->rows = uap->aua.hauai; | ||
685 | rp->cols = uap->aua.wauai; | ||
686 | } | ||
687 | return 0; | ||
688 | } | ||
689 | |||
690 | static int | ||
691 | raw3270_size_device(struct raw3270 *rp) | ||
692 | { | ||
693 | int rc; | ||
694 | |||
695 | down(&raw3270_init_sem); | ||
696 | rp->view = &raw3270_init_view; | ||
697 | raw3270_init_view.dev = rp; | ||
698 | rc = __raw3270_size_device(rp); | ||
699 | raw3270_init_view.dev = 0; | ||
700 | rp->view = 0; | ||
701 | up(&raw3270_init_sem); | ||
702 | if (rc == 0) { /* Found something. */ | ||
703 | /* Try to find a model. */ | ||
704 | rp->model = 0; | ||
705 | if (rp->rows == 24 && rp->cols == 80) | ||
706 | rp->model = 2; | ||
707 | if (rp->rows == 32 && rp->cols == 80) | ||
708 | rp->model = 3; | ||
709 | if (rp->rows == 43 && rp->cols == 80) | ||
710 | rp->model = 4; | ||
711 | if (rp->rows == 27 && rp->cols == 132) | ||
712 | rp->model = 5; | ||
713 | } | ||
714 | return rc; | ||
715 | } | ||
716 | |||
717 | static int | ||
718 | raw3270_reset_device(struct raw3270 *rp) | ||
719 | { | ||
720 | int rc; | ||
721 | |||
722 | down(&raw3270_init_sem); | ||
723 | memset(&raw3270_init_request, 0, sizeof(raw3270_init_request)); | ||
724 | memset(raw3270_init_data, 0, sizeof(raw3270_init_data)); | ||
725 | /* Store reset data stream to raw3270_init_data/raw3270_init_request */ | ||
726 | raw3270_init_data[0] = TW_KR; | ||
727 | INIT_LIST_HEAD(&raw3270_init_request.list); | ||
728 | raw3270_init_request.ccw.cmd_code = TC_EWRITEA; | ||
729 | raw3270_init_request.ccw.flags = CCW_FLAG_SLI; | ||
730 | raw3270_init_request.ccw.count = 1; | ||
731 | raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data); | ||
732 | rp->view = &raw3270_init_view; | ||
733 | raw3270_init_view.dev = rp; | ||
734 | rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request); | ||
735 | raw3270_init_view.dev = 0; | ||
736 | rp->view = 0; | ||
737 | up(&raw3270_init_sem); | ||
738 | return rc; | ||
739 | } | ||
740 | |||
741 | /* | ||
742 | * Setup new 3270 device. | ||
743 | */ | ||
744 | static int | ||
745 | raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) | ||
746 | { | ||
747 | struct list_head *l; | ||
748 | struct raw3270 *tmp; | ||
749 | int minor; | ||
750 | |||
751 | memset(rp, 0, sizeof(struct raw3270)); | ||
752 | /* Copy ebcdic -> ascii translation table. */ | ||
753 | memcpy(ascebc, _ascebc, 256); | ||
754 | if (tubxcorrect) { | ||
755 | /* correct brackets and circumflex */ | ||
756 | ascebc['['] = 0xad; | ||
757 | ascebc[']'] = 0xbd; | ||
758 | ascebc['^'] = 0xb0; | ||
759 | } | ||
760 | rp->ascebc = ascebc; | ||
761 | |||
762 | /* Set defaults. */ | ||
763 | rp->rows = 24; | ||
764 | rp->cols = 80; | ||
765 | |||
766 | INIT_LIST_HEAD(&rp->req_queue); | ||
767 | INIT_LIST_HEAD(&rp->view_list); | ||
768 | |||
769 | /* | ||
770 | * Add device to list and find the smallest unused minor | ||
771 | * number for it. | ||
772 | */ | ||
773 | down(&raw3270_sem); | ||
774 | /* Keep the list sorted. */ | ||
775 | minor = 0; | ||
776 | rp->minor = -1; | ||
777 | list_for_each(l, &raw3270_devices) { | ||
778 | tmp = list_entry(l, struct raw3270, list); | ||
779 | if (tmp->minor > minor) { | ||
780 | rp->minor = minor; | ||
781 | __list_add(&rp->list, l->prev, l); | ||
782 | break; | ||
783 | } | ||
784 | minor++; | ||
785 | } | ||
786 | if (rp->minor == -1 && minor < RAW3270_MAXDEVS) { | ||
787 | rp->minor = minor; | ||
788 | list_add_tail(&rp->list, &raw3270_devices); | ||
789 | } | ||
790 | up(&raw3270_sem); | ||
791 | /* No free minor number? Then give up. */ | ||
792 | if (rp->minor == -1) | ||
793 | return -EUSERS; | ||
794 | rp->cdev = cdev; | ||
795 | cdev->dev.driver_data = rp; | ||
796 | cdev->handler = raw3270_irq; | ||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | #ifdef CONFIG_TN3270_CONSOLE | ||
801 | /* | ||
802 | * Setup 3270 device configured as console. | ||
803 | */ | ||
804 | struct raw3270 * | ||
805 | raw3270_setup_console(struct ccw_device *cdev) | ||
806 | { | ||
807 | struct raw3270 *rp; | ||
808 | char *ascebc; | ||
809 | int rc; | ||
810 | |||
811 | rp = (struct raw3270 *) alloc_bootmem(sizeof(struct raw3270)); | ||
812 | ascebc = (char *) alloc_bootmem(256); | ||
813 | rc = raw3270_setup_device(cdev, rp, ascebc); | ||
814 | if (rc) | ||
815 | return ERR_PTR(rc); | ||
816 | set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags); | ||
817 | rc = raw3270_reset_device(rp); | ||
818 | if (rc) | ||
819 | return ERR_PTR(rc); | ||
820 | rc = raw3270_size_device(rp); | ||
821 | if (rc) | ||
822 | return ERR_PTR(rc); | ||
823 | rc = raw3270_reset_device(rp); | ||
824 | if (rc) | ||
825 | return ERR_PTR(rc); | ||
826 | set_bit(RAW3270_FLAGS_READY, &rp->flags); | ||
827 | return rp; | ||
828 | } | ||
829 | |||
830 | void | ||
831 | raw3270_wait_cons_dev(struct raw3270 *rp) | ||
832 | { | ||
833 | unsigned long flags; | ||
834 | |||
835 | spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); | ||
836 | wait_cons_dev(); | ||
837 | spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); | ||
838 | } | ||
839 | |||
840 | #endif | ||
841 | |||
842 | /* | ||
843 | * Create a 3270 device structure. | ||
844 | */ | ||
845 | static struct raw3270 * | ||
846 | raw3270_create_device(struct ccw_device *cdev) | ||
847 | { | ||
848 | struct raw3270 *rp; | ||
849 | char *ascebc; | ||
850 | int rc; | ||
851 | |||
852 | rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL); | ||
853 | if (!rp) | ||
854 | return ERR_PTR(-ENOMEM); | ||
855 | ascebc = kmalloc(256, GFP_KERNEL); | ||
856 | if (!ascebc) { | ||
857 | kfree(rp); | ||
858 | return ERR_PTR(-ENOMEM); | ||
859 | } | ||
860 | rc = raw3270_setup_device(cdev, rp, ascebc); | ||
861 | if (rc) { | ||
862 | kfree(rp->ascebc); | ||
863 | kfree(rp); | ||
864 | rp = ERR_PTR(rc); | ||
865 | } | ||
866 | /* Get reference to ccw_device structure. */ | ||
867 | get_device(&cdev->dev); | ||
868 | return rp; | ||
869 | } | ||
870 | |||
871 | /* | ||
872 | * Activate a view. | ||
873 | */ | ||
874 | int | ||
875 | raw3270_activate_view(struct raw3270_view *view) | ||
876 | { | ||
877 | struct raw3270 *rp; | ||
878 | struct raw3270_view *oldview, *nv; | ||
879 | unsigned long flags; | ||
880 | int rc; | ||
881 | |||
882 | rp = view->dev; | ||
883 | if (!rp) | ||
884 | return -ENODEV; | ||
885 | spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); | ||
886 | if (rp->view == view) | ||
887 | rc = 0; | ||
888 | else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) | ||
889 | rc = -ENODEV; | ||
890 | else { | ||
891 | oldview = 0; | ||
892 | if (rp->view) { | ||
893 | oldview = rp->view; | ||
894 | oldview->fn->deactivate(oldview); | ||
895 | } | ||
896 | rp->view = view; | ||
897 | rc = view->fn->activate(view); | ||
898 | if (rc) { | ||
899 | /* Didn't work. Try to reactivate the old view. */ | ||
900 | rp->view = oldview; | ||
901 | if (!oldview || oldview->fn->activate(oldview) != 0) { | ||
902 | /* Didn't work as well. Try any other view. */ | ||
903 | list_for_each_entry(nv, &rp->view_list, list) | ||
904 | if (nv != view && nv != oldview) { | ||
905 | rp->view = nv; | ||
906 | if (nv->fn->activate(nv) == 0) | ||
907 | break; | ||
908 | rp->view = 0; | ||
909 | } | ||
910 | } | ||
911 | } | ||
912 | } | ||
913 | spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); | ||
914 | return rc; | ||
915 | } | ||
916 | |||
917 | /* | ||
918 | * Deactivate current view. | ||
919 | */ | ||
920 | void | ||
921 | raw3270_deactivate_view(struct raw3270_view *view) | ||
922 | { | ||
923 | unsigned long flags; | ||
924 | struct raw3270 *rp; | ||
925 | |||
926 | rp = view->dev; | ||
927 | if (!rp) | ||
928 | return; | ||
929 | spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); | ||
930 | if (rp->view == view) { | ||
931 | view->fn->deactivate(view); | ||
932 | rp->view = 0; | ||
933 | /* Move deactivated view to end of list. */ | ||
934 | list_del_init(&view->list); | ||
935 | list_add_tail(&view->list, &rp->view_list); | ||
936 | /* Try to activate another view. */ | ||
937 | if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) { | ||
938 | list_for_each_entry(view, &rp->view_list, list) | ||
939 | if (view->fn->activate(view) == 0) { | ||
940 | rp->view = view; | ||
941 | break; | ||
942 | } | ||
943 | } | ||
944 | } | ||
945 | spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); | ||
946 | } | ||
947 | |||
948 | /* | ||
949 | * Add view to device with minor "minor". | ||
950 | */ | ||
951 | int | ||
952 | raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor) | ||
953 | { | ||
954 | unsigned long flags; | ||
955 | struct raw3270 *rp; | ||
956 | int rc; | ||
957 | |||
958 | down(&raw3270_sem); | ||
959 | rc = -ENODEV; | ||
960 | list_for_each_entry(rp, &raw3270_devices, list) { | ||
961 | if (rp->minor != minor) | ||
962 | continue; | ||
963 | spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); | ||
964 | if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) { | ||
965 | atomic_set(&view->ref_count, 2); | ||
966 | view->dev = rp; | ||
967 | view->fn = fn; | ||
968 | view->model = rp->model; | ||
969 | view->rows = rp->rows; | ||
970 | view->cols = rp->cols; | ||
971 | view->ascebc = rp->ascebc; | ||
972 | spin_lock_init(&view->lock); | ||
973 | list_add_tail(&view->list, &rp->view_list); | ||
974 | rc = 0; | ||
975 | } | ||
976 | spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); | ||
977 | break; | ||
978 | } | ||
979 | up(&raw3270_sem); | ||
980 | return rc; | ||
981 | } | ||
982 | |||
983 | /* | ||
984 | * Find specific view of device with minor "minor". | ||
985 | */ | ||
986 | struct raw3270_view * | ||
987 | raw3270_find_view(struct raw3270_fn *fn, int minor) | ||
988 | { | ||
989 | struct raw3270 *rp; | ||
990 | struct raw3270_view *view, *tmp; | ||
991 | unsigned long flags; | ||
992 | |||
993 | down(&raw3270_sem); | ||
994 | view = ERR_PTR(-ENODEV); | ||
995 | list_for_each_entry(rp, &raw3270_devices, list) { | ||
996 | if (rp->minor != minor) | ||
997 | continue; | ||
998 | spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); | ||
999 | if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) { | ||
1000 | view = ERR_PTR(-ENOENT); | ||
1001 | list_for_each_entry(tmp, &rp->view_list, list) { | ||
1002 | if (tmp->fn == fn) { | ||
1003 | raw3270_get_view(tmp); | ||
1004 | view = tmp; | ||
1005 | break; | ||
1006 | } | ||
1007 | } | ||
1008 | } | ||
1009 | spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); | ||
1010 | break; | ||
1011 | } | ||
1012 | up(&raw3270_sem); | ||
1013 | return view; | ||
1014 | } | ||
1015 | |||
1016 | /* | ||
1017 | * Remove view from device and free view structure via call to view->fn->free. | ||
1018 | */ | ||
1019 | void | ||
1020 | raw3270_del_view(struct raw3270_view *view) | ||
1021 | { | ||
1022 | unsigned long flags; | ||
1023 | struct raw3270 *rp; | ||
1024 | struct raw3270_view *nv; | ||
1025 | |||
1026 | rp = view->dev; | ||
1027 | spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); | ||
1028 | if (rp->view == view) { | ||
1029 | view->fn->deactivate(view); | ||
1030 | rp->view = 0; | ||
1031 | } | ||
1032 | list_del_init(&view->list); | ||
1033 | if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags)) { | ||
1034 | /* Try to activate another view. */ | ||
1035 | list_for_each_entry(nv, &rp->view_list, list) { | ||
1036 | if (nv->fn->activate(view) == 0) { | ||
1037 | rp->view = nv; | ||
1038 | break; | ||
1039 | } | ||
1040 | } | ||
1041 | } | ||
1042 | spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); | ||
1043 | /* Wait for reference counter to drop to zero. */ | ||
1044 | atomic_dec(&view->ref_count); | ||
1045 | wait_event(raw3270_wait_queue, atomic_read(&view->ref_count) == 0); | ||
1046 | if (view->fn->free) | ||
1047 | view->fn->free(view); | ||
1048 | } | ||
1049 | |||
1050 | /* | ||
1051 | * Remove a 3270 device structure. | ||
1052 | */ | ||
1053 | static void | ||
1054 | raw3270_delete_device(struct raw3270 *rp) | ||
1055 | { | ||
1056 | struct ccw_device *cdev; | ||
1057 | |||
1058 | /* Remove from device chain. */ | ||
1059 | down(&raw3270_sem); | ||
1060 | list_del_init(&rp->list); | ||
1061 | up(&raw3270_sem); | ||
1062 | |||
1063 | /* Disconnect from ccw_device. */ | ||
1064 | cdev = rp->cdev; | ||
1065 | rp->cdev = 0; | ||
1066 | cdev->dev.driver_data = 0; | ||
1067 | cdev->handler = 0; | ||
1068 | |||
1069 | /* Put ccw_device structure. */ | ||
1070 | put_device(&cdev->dev); | ||
1071 | |||
1072 | /* Now free raw3270 structure. */ | ||
1073 | kfree(rp->ascebc); | ||
1074 | kfree(rp); | ||
1075 | } | ||
1076 | |||
1077 | static int | ||
1078 | raw3270_probe (struct ccw_device *cdev) | ||
1079 | { | ||
1080 | return 0; | ||
1081 | } | ||
1082 | |||
1083 | /* | ||
1084 | * Additional attributes for a 3270 device | ||
1085 | */ | ||
1086 | static ssize_t | ||
1087 | raw3270_model_show(struct device *dev, char *buf) | ||
1088 | { | ||
1089 | return snprintf(buf, PAGE_SIZE, "%i\n", | ||
1090 | ((struct raw3270 *) dev->driver_data)->model); | ||
1091 | } | ||
1092 | static DEVICE_ATTR(model, 0444, raw3270_model_show, 0); | ||
1093 | |||
1094 | static ssize_t | ||
1095 | raw3270_rows_show(struct device *dev, char *buf) | ||
1096 | { | ||
1097 | return snprintf(buf, PAGE_SIZE, "%i\n", | ||
1098 | ((struct raw3270 *) dev->driver_data)->rows); | ||
1099 | } | ||
1100 | static DEVICE_ATTR(rows, 0444, raw3270_rows_show, 0); | ||
1101 | |||
1102 | static ssize_t | ||
1103 | raw3270_columns_show(struct device *dev, char *buf) | ||
1104 | { | ||
1105 | return snprintf(buf, PAGE_SIZE, "%i\n", | ||
1106 | ((struct raw3270 *) dev->driver_data)->cols); | ||
1107 | } | ||
1108 | static DEVICE_ATTR(columns, 0444, raw3270_columns_show, 0); | ||
1109 | |||
1110 | static struct attribute * raw3270_attrs[] = { | ||
1111 | &dev_attr_model.attr, | ||
1112 | &dev_attr_rows.attr, | ||
1113 | &dev_attr_columns.attr, | ||
1114 | NULL, | ||
1115 | }; | ||
1116 | |||
1117 | static struct attribute_group raw3270_attr_group = { | ||
1118 | .attrs = raw3270_attrs, | ||
1119 | }; | ||
1120 | |||
1121 | static void | ||
1122 | raw3270_create_attributes(struct raw3270 *rp) | ||
1123 | { | ||
1124 | //FIXME: check return code | ||
1125 | sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group); | ||
1126 | } | ||
1127 | |||
1128 | /* | ||
1129 | * Notifier for device addition/removal | ||
1130 | */ | ||
1131 | struct raw3270_notifier { | ||
1132 | struct list_head list; | ||
1133 | void (*notifier)(int, int); | ||
1134 | }; | ||
1135 | |||
1136 | static struct list_head raw3270_notifier = LIST_HEAD_INIT(raw3270_notifier); | ||
1137 | |||
1138 | int raw3270_register_notifier(void (*notifier)(int, int)) | ||
1139 | { | ||
1140 | struct raw3270_notifier *np; | ||
1141 | struct raw3270 *rp; | ||
1142 | |||
1143 | np = kmalloc(sizeof(struct raw3270_notifier), GFP_KERNEL); | ||
1144 | if (!np) | ||
1145 | return -ENOMEM; | ||
1146 | np->notifier = notifier; | ||
1147 | down(&raw3270_sem); | ||
1148 | list_add_tail(&np->list, &raw3270_notifier); | ||
1149 | list_for_each_entry(rp, &raw3270_devices, list) { | ||
1150 | get_device(&rp->cdev->dev); | ||
1151 | notifier(rp->minor, 1); | ||
1152 | } | ||
1153 | up(&raw3270_sem); | ||
1154 | return 0; | ||
1155 | } | ||
1156 | |||
1157 | void raw3270_unregister_notifier(void (*notifier)(int, int)) | ||
1158 | { | ||
1159 | struct raw3270_notifier *np; | ||
1160 | |||
1161 | down(&raw3270_sem); | ||
1162 | list_for_each_entry(np, &raw3270_notifier, list) | ||
1163 | if (np->notifier == notifier) { | ||
1164 | list_del(&np->list); | ||
1165 | kfree(np); | ||
1166 | break; | ||
1167 | } | ||
1168 | up(&raw3270_sem); | ||
1169 | } | ||
1170 | |||
1171 | /* | ||
1172 | * Set 3270 device online. | ||
1173 | */ | ||
1174 | static int | ||
1175 | raw3270_set_online (struct ccw_device *cdev) | ||
1176 | { | ||
1177 | struct raw3270 *rp; | ||
1178 | struct raw3270_notifier *np; | ||
1179 | int rc; | ||
1180 | |||
1181 | rp = raw3270_create_device(cdev); | ||
1182 | if (IS_ERR(rp)) | ||
1183 | return PTR_ERR(rp); | ||
1184 | rc = raw3270_reset_device(rp); | ||
1185 | if (rc) | ||
1186 | return rc; | ||
1187 | rc = raw3270_size_device(rp); | ||
1188 | if (rc) | ||
1189 | return rc; | ||
1190 | rc = raw3270_reset_device(rp); | ||
1191 | if (rc) | ||
1192 | return rc; | ||
1193 | raw3270_create_attributes(rp); | ||
1194 | set_bit(RAW3270_FLAGS_READY, &rp->flags); | ||
1195 | down(&raw3270_sem); | ||
1196 | list_for_each_entry(np, &raw3270_notifier, list) | ||
1197 | np->notifier(rp->minor, 1); | ||
1198 | up(&raw3270_sem); | ||
1199 | return 0; | ||
1200 | } | ||
1201 | |||
1202 | /* | ||
1203 | * Remove 3270 device structure. | ||
1204 | */ | ||
1205 | static void | ||
1206 | raw3270_remove (struct ccw_device *cdev) | ||
1207 | { | ||
1208 | unsigned long flags; | ||
1209 | struct raw3270 *rp; | ||
1210 | struct raw3270_view *v; | ||
1211 | struct raw3270_notifier *np; | ||
1212 | |||
1213 | rp = cdev->dev.driver_data; | ||
1214 | clear_bit(RAW3270_FLAGS_READY, &rp->flags); | ||
1215 | |||
1216 | sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group); | ||
1217 | |||
1218 | /* Deactivate current view and remove all views. */ | ||
1219 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); | ||
1220 | if (rp->view) { | ||
1221 | rp->view->fn->deactivate(rp->view); | ||
1222 | rp->view = 0; | ||
1223 | } | ||
1224 | while (!list_empty(&rp->view_list)) { | ||
1225 | v = list_entry(rp->view_list.next, struct raw3270_view, list); | ||
1226 | if (v->fn->release) | ||
1227 | v->fn->release(v); | ||
1228 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | ||
1229 | raw3270_del_view(v); | ||
1230 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); | ||
1231 | } | ||
1232 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | ||
1233 | |||
1234 | down(&raw3270_sem); | ||
1235 | list_for_each_entry(np, &raw3270_notifier, list) | ||
1236 | np->notifier(rp->minor, 0); | ||
1237 | up(&raw3270_sem); | ||
1238 | |||
1239 | /* Reset 3270 device. */ | ||
1240 | raw3270_reset_device(rp); | ||
1241 | /* And finally remove it. */ | ||
1242 | raw3270_delete_device(rp); | ||
1243 | } | ||
1244 | |||
1245 | /* | ||
1246 | * Set 3270 device offline. | ||
1247 | */ | ||
1248 | static int | ||
1249 | raw3270_set_offline (struct ccw_device *cdev) | ||
1250 | { | ||
1251 | struct raw3270 *rp; | ||
1252 | |||
1253 | rp = cdev->dev.driver_data; | ||
1254 | if (test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags)) | ||
1255 | return -EBUSY; | ||
1256 | raw3270_remove(cdev); | ||
1257 | return 0; | ||
1258 | } | ||
1259 | |||
1260 | static struct ccw_device_id raw3270_id[] = { | ||
1261 | { CCW_DEVICE(0x3270, 0) }, | ||
1262 | { CCW_DEVICE(0x3271, 0) }, | ||
1263 | { CCW_DEVICE(0x3272, 0) }, | ||
1264 | { CCW_DEVICE(0x3273, 0) }, | ||
1265 | { CCW_DEVICE(0x3274, 0) }, | ||
1266 | { CCW_DEVICE(0x3275, 0) }, | ||
1267 | { CCW_DEVICE(0x3276, 0) }, | ||
1268 | { CCW_DEVICE(0x3277, 0) }, | ||
1269 | { CCW_DEVICE(0x3278, 0) }, | ||
1270 | { CCW_DEVICE(0x3279, 0) }, | ||
1271 | { CCW_DEVICE(0x3174, 0) }, | ||
1272 | { /* end of list */ }, | ||
1273 | }; | ||
1274 | |||
1275 | static struct ccw_driver raw3270_ccw_driver = { | ||
1276 | .name = "3270", | ||
1277 | .owner = THIS_MODULE, | ||
1278 | .ids = raw3270_id, | ||
1279 | .probe = &raw3270_probe, | ||
1280 | .remove = &raw3270_remove, | ||
1281 | .set_online = &raw3270_set_online, | ||
1282 | .set_offline = &raw3270_set_offline, | ||
1283 | }; | ||
1284 | |||
1285 | static int | ||
1286 | raw3270_init(void) | ||
1287 | { | ||
1288 | struct raw3270 *rp; | ||
1289 | int rc; | ||
1290 | |||
1291 | if (raw3270_registered) | ||
1292 | return 0; | ||
1293 | raw3270_registered = 1; | ||
1294 | rc = ccw_driver_register(&raw3270_ccw_driver); | ||
1295 | if (rc == 0) { | ||
1296 | /* Create attributes for early (= console) device. */ | ||
1297 | down(&raw3270_sem); | ||
1298 | list_for_each_entry(rp, &raw3270_devices, list) { | ||
1299 | get_device(&rp->cdev->dev); | ||
1300 | raw3270_create_attributes(rp); | ||
1301 | } | ||
1302 | up(&raw3270_sem); | ||
1303 | } | ||
1304 | return rc; | ||
1305 | } | ||
1306 | |||
1307 | static void | ||
1308 | raw3270_exit(void) | ||
1309 | { | ||
1310 | ccw_driver_unregister(&raw3270_ccw_driver); | ||
1311 | } | ||
1312 | |||
1313 | MODULE_LICENSE("GPL"); | ||
1314 | |||
1315 | module_init(raw3270_init); | ||
1316 | module_exit(raw3270_exit); | ||
1317 | |||
1318 | EXPORT_SYMBOL(raw3270_request_alloc); | ||
1319 | EXPORT_SYMBOL(raw3270_request_free); | ||
1320 | EXPORT_SYMBOL(raw3270_request_reset); | ||
1321 | EXPORT_SYMBOL(raw3270_request_set_cmd); | ||
1322 | EXPORT_SYMBOL(raw3270_request_add_data); | ||
1323 | EXPORT_SYMBOL(raw3270_request_set_data); | ||
1324 | EXPORT_SYMBOL(raw3270_request_set_idal); | ||
1325 | EXPORT_SYMBOL(raw3270_buffer_address); | ||
1326 | EXPORT_SYMBOL(raw3270_add_view); | ||
1327 | EXPORT_SYMBOL(raw3270_del_view); | ||
1328 | EXPORT_SYMBOL(raw3270_find_view); | ||
1329 | EXPORT_SYMBOL(raw3270_activate_view); | ||
1330 | EXPORT_SYMBOL(raw3270_deactivate_view); | ||
1331 | EXPORT_SYMBOL(raw3270_start); | ||
1332 | EXPORT_SYMBOL(raw3270_start_irq); | ||
1333 | EXPORT_SYMBOL(raw3270_register_notifier); | ||
1334 | EXPORT_SYMBOL(raw3270_unregister_notifier); | ||
1335 | EXPORT_SYMBOL(raw3270_wait_queue); | ||