1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * IBM/3270 Driver - fullscreen driver. |
4 | * |
5 | * Author(s): |
6 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) |
7 | * Rewritten for 2.5/2.6 by Martin Schwidefsky <schwidefsky@de.ibm.com> |
8 | * Copyright IBM Corp. 2003, 2009 |
9 | */ |
10 | |
11 | #include <linux/memblock.h> |
12 | #include <linux/console.h> |
13 | #include <linux/init.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/compat.h> |
16 | #include <linux/sched/signal.h> |
17 | #include <linux/module.h> |
18 | #include <linux/list.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/types.h> |
21 | |
22 | #include <uapi/asm/fs3270.h> |
23 | #include <asm/ccwdev.h> |
24 | #include <asm/cio.h> |
25 | #include <asm/ebcdic.h> |
26 | #include <asm/idals.h> |
27 | |
28 | #include "raw3270.h" |
29 | #include "ctrlchar.h" |
30 | |
31 | static struct raw3270_fn fs3270_fn; |
32 | |
33 | struct fs3270 { |
34 | struct raw3270_view view; |
35 | struct pid *fs_pid; /* Pid of controlling program. */ |
36 | int read_command; /* ccw command to use for reads. */ |
37 | int write_command; /* ccw command to use for writes. */ |
38 | int attention; /* Got attention. */ |
39 | int active; /* Fullscreen view is active. */ |
40 | struct raw3270_request *init; /* single init request. */ |
41 | wait_queue_head_t wait; /* Init & attention wait queue. */ |
42 | struct idal_buffer *rdbuf; /* full-screen-deactivate buffer */ |
43 | size_t rdbuf_size; /* size of data returned by RDBUF */ |
44 | }; |
45 | |
46 | static DEFINE_MUTEX(fs3270_mutex); |
47 | |
48 | static void fs3270_wake_up(struct raw3270_request *rq, void *data) |
49 | { |
50 | wake_up((wait_queue_head_t *)data); |
51 | } |
52 | |
53 | static inline int fs3270_working(struct fs3270 *fp) |
54 | { |
55 | /* |
56 | * The fullscreen view is in working order if the view |
57 | * has been activated AND the initial request is finished. |
58 | */ |
59 | return fp->active && raw3270_request_final(rq: fp->init); |
60 | } |
61 | |
62 | static int fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) |
63 | { |
64 | struct fs3270 *fp; |
65 | int rc; |
66 | |
67 | fp = (struct fs3270 *)view; |
68 | rq->callback = fs3270_wake_up; |
69 | rq->callback_data = &fp->wait; |
70 | |
71 | do { |
72 | if (!fs3270_working(fp)) { |
73 | /* Fullscreen view isn't ready yet. */ |
74 | rc = wait_event_interruptible(fp->wait, |
75 | fs3270_working(fp)); |
76 | if (rc != 0) |
77 | break; |
78 | } |
79 | rc = raw3270_start(view, rq); |
80 | if (rc == 0) { |
81 | /* Started successfully. Now wait for completion. */ |
82 | wait_event(fp->wait, raw3270_request_final(rq)); |
83 | } |
84 | } while (rc == -EACCES); |
85 | return rc; |
86 | } |
87 | |
88 | /* |
89 | * Switch to the fullscreen view. |
90 | */ |
91 | static void fs3270_reset_callback(struct raw3270_request *rq, void *data) |
92 | { |
93 | struct fs3270 *fp; |
94 | |
95 | fp = (struct fs3270 *)rq->view; |
96 | raw3270_request_reset(rq); |
97 | wake_up(&fp->wait); |
98 | } |
99 | |
100 | static void fs3270_restore_callback(struct raw3270_request *rq, void *data) |
101 | { |
102 | struct fs3270 *fp; |
103 | |
104 | fp = (struct fs3270 *)rq->view; |
105 | if (rq->rc != 0 || rq->rescnt != 0) { |
106 | if (fp->fs_pid) |
107 | kill_pid(pid: fp->fs_pid, SIGHUP, priv: 1); |
108 | } |
109 | fp->rdbuf_size = 0; |
110 | raw3270_request_reset(rq); |
111 | wake_up(&fp->wait); |
112 | } |
113 | |
114 | static int fs3270_activate(struct raw3270_view *view) |
115 | { |
116 | struct fs3270 *fp; |
117 | char *cp; |
118 | int rc; |
119 | |
120 | fp = (struct fs3270 *)view; |
121 | |
122 | /* If an old init command is still running just return. */ |
123 | if (!raw3270_request_final(rq: fp->init)) |
124 | return 0; |
125 | |
126 | raw3270_request_set_cmd(rq: fp->init, cmd: TC_EWRITEA); |
127 | raw3270_request_set_idal(rq: fp->init, ib: fp->rdbuf); |
128 | fp->init->rescnt = 0; |
129 | cp = fp->rdbuf->data[0]; |
130 | if (fp->rdbuf_size == 0) { |
131 | /* No saved buffer. Just clear the screen. */ |
132 | fp->init->ccw.count = 1; |
133 | fp->init->callback = fs3270_reset_callback; |
134 | cp[0] = 0; |
135 | } else { |
136 | /* Restore fullscreen buffer saved by fs3270_deactivate. */ |
137 | fp->init->ccw.count = fp->rdbuf_size; |
138 | fp->init->callback = fs3270_restore_callback; |
139 | cp[0] = TW_KR; |
140 | cp[1] = TO_SBA; |
141 | cp[2] = cp[6]; |
142 | cp[3] = cp[7]; |
143 | cp[4] = TO_IC; |
144 | cp[5] = TO_SBA; |
145 | cp[6] = 0x40; |
146 | cp[7] = 0x40; |
147 | } |
148 | rc = raw3270_start_locked(view, rq: fp->init); |
149 | fp->init->rc = rc; |
150 | if (rc) |
151 | fp->init->callback(fp->init, NULL); |
152 | else |
153 | fp->active = 1; |
154 | return rc; |
155 | } |
156 | |
157 | /* |
158 | * Shutdown fullscreen view. |
159 | */ |
160 | static void fs3270_save_callback(struct raw3270_request *rq, void *data) |
161 | { |
162 | struct fs3270 *fp; |
163 | |
164 | fp = (struct fs3270 *)rq->view; |
165 | |
166 | /* Correct idal buffer element 0 address. */ |
167 | fp->rdbuf->data[0] -= 5; |
168 | fp->rdbuf->size += 5; |
169 | |
170 | /* |
171 | * If the rdbuf command failed or the idal buffer is |
172 | * to small for the amount of data returned by the |
173 | * rdbuf command, then we have no choice but to send |
174 | * a SIGHUP to the application. |
175 | */ |
176 | if (rq->rc != 0 || rq->rescnt == 0) { |
177 | if (fp->fs_pid) |
178 | kill_pid(pid: fp->fs_pid, SIGHUP, priv: 1); |
179 | fp->rdbuf_size = 0; |
180 | } else { |
181 | fp->rdbuf_size = fp->rdbuf->size - rq->rescnt; |
182 | } |
183 | raw3270_request_reset(rq); |
184 | wake_up(&fp->wait); |
185 | } |
186 | |
187 | static void fs3270_deactivate(struct raw3270_view *view) |
188 | { |
189 | struct fs3270 *fp; |
190 | |
191 | fp = (struct fs3270 *)view; |
192 | fp->active = 0; |
193 | |
194 | /* If an old init command is still running just return. */ |
195 | if (!raw3270_request_final(rq: fp->init)) |
196 | return; |
197 | |
198 | /* Prepare read-buffer request. */ |
199 | raw3270_request_set_cmd(rq: fp->init, cmd: TC_RDBUF); |
200 | /* |
201 | * Hackish: skip first 5 bytes of the idal buffer to make |
202 | * room for the TW_KR/TO_SBA/<address>/<address>/TO_IC sequence |
203 | * in the activation command. |
204 | */ |
205 | fp->rdbuf->data[0] += 5; |
206 | fp->rdbuf->size -= 5; |
207 | raw3270_request_set_idal(rq: fp->init, ib: fp->rdbuf); |
208 | fp->init->rescnt = 0; |
209 | fp->init->callback = fs3270_save_callback; |
210 | |
211 | /* Start I/O to read in the 3270 buffer. */ |
212 | fp->init->rc = raw3270_start_locked(view, rq: fp->init); |
213 | if (fp->init->rc) |
214 | fp->init->callback(fp->init, NULL); |
215 | } |
216 | |
217 | static void fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, |
218 | struct irb *irb) |
219 | { |
220 | /* Handle ATTN. Set indication and wake waiters for attention. */ |
221 | if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { |
222 | fp->attention = 1; |
223 | wake_up(&fp->wait); |
224 | } |
225 | |
226 | if (rq) { |
227 | if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) |
228 | rq->rc = -EIO; |
229 | else |
230 | /* Normal end. Copy residual count. */ |
231 | rq->rescnt = irb->scsw.cmd.count; |
232 | } |
233 | } |
234 | |
235 | /* |
236 | * Process reads from fullscreen 3270. |
237 | */ |
238 | static ssize_t fs3270_read(struct file *filp, char __user *data, |
239 | size_t count, loff_t *off) |
240 | { |
241 | struct fs3270 *fp; |
242 | struct raw3270_request *rq; |
243 | struct idal_buffer *ib; |
244 | ssize_t rc; |
245 | |
246 | if (count == 0 || count > 65535) |
247 | return -EINVAL; |
248 | fp = filp->private_data; |
249 | if (!fp) |
250 | return -ENODEV; |
251 | ib = idal_buffer_alloc(count, 0); |
252 | if (IS_ERR(ptr: ib)) |
253 | return -ENOMEM; |
254 | rq = raw3270_request_alloc(size: 0); |
255 | if (!IS_ERR(ptr: rq)) { |
256 | if (fp->read_command == 0 && fp->write_command != 0) |
257 | fp->read_command = 6; |
258 | raw3270_request_set_cmd(rq, cmd: fp->read_command ? : 2); |
259 | raw3270_request_set_idal(rq, ib); |
260 | rc = wait_event_interruptible(fp->wait, fp->attention); |
261 | fp->attention = 0; |
262 | if (rc == 0) { |
263 | rc = fs3270_do_io(view: &fp->view, rq); |
264 | if (rc == 0) { |
265 | count -= rq->rescnt; |
266 | if (idal_buffer_to_user(ib, data, count) != 0) |
267 | rc = -EFAULT; |
268 | else |
269 | rc = count; |
270 | } |
271 | } |
272 | raw3270_request_free(rq); |
273 | } else { |
274 | rc = PTR_ERR(ptr: rq); |
275 | } |
276 | idal_buffer_free(ib); |
277 | return rc; |
278 | } |
279 | |
280 | /* |
281 | * Process writes to fullscreen 3270. |
282 | */ |
283 | static ssize_t fs3270_write(struct file *filp, const char __user *data, |
284 | size_t count, loff_t *off) |
285 | { |
286 | struct fs3270 *fp; |
287 | struct raw3270_request *rq; |
288 | struct idal_buffer *ib; |
289 | int write_command; |
290 | ssize_t rc; |
291 | |
292 | fp = filp->private_data; |
293 | if (!fp) |
294 | return -ENODEV; |
295 | ib = idal_buffer_alloc(count, 0); |
296 | if (IS_ERR(ptr: ib)) |
297 | return -ENOMEM; |
298 | rq = raw3270_request_alloc(size: 0); |
299 | if (!IS_ERR(ptr: rq)) { |
300 | if (idal_buffer_from_user(ib, data, count) == 0) { |
301 | write_command = fp->write_command ? : 1; |
302 | if (write_command == 5) |
303 | write_command = 13; |
304 | raw3270_request_set_cmd(rq, cmd: write_command); |
305 | raw3270_request_set_idal(rq, ib); |
306 | rc = fs3270_do_io(view: &fp->view, rq); |
307 | if (rc == 0) |
308 | rc = count - rq->rescnt; |
309 | } else { |
310 | rc = -EFAULT; |
311 | } |
312 | raw3270_request_free(rq); |
313 | } else { |
314 | rc = PTR_ERR(ptr: rq); |
315 | } |
316 | idal_buffer_free(ib); |
317 | return rc; |
318 | } |
319 | |
320 | /* |
321 | * process ioctl commands for the tube driver |
322 | */ |
323 | static long fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
324 | { |
325 | char __user *argp; |
326 | struct fs3270 *fp; |
327 | struct raw3270_iocb iocb; |
328 | int rc; |
329 | |
330 | fp = filp->private_data; |
331 | if (!fp) |
332 | return -ENODEV; |
333 | if (is_compat_task()) |
334 | argp = compat_ptr(uptr: arg); |
335 | else |
336 | argp = (char __user *)arg; |
337 | rc = 0; |
338 | mutex_lock(&fs3270_mutex); |
339 | switch (cmd) { |
340 | case TUBICMD: |
341 | fp->read_command = arg; |
342 | break; |
343 | case TUBOCMD: |
344 | fp->write_command = arg; |
345 | break; |
346 | case TUBGETI: |
347 | rc = put_user(fp->read_command, argp); |
348 | break; |
349 | case TUBGETO: |
350 | rc = put_user(fp->write_command, argp); |
351 | break; |
352 | case TUBGETMOD: |
353 | iocb.model = fp->view.model; |
354 | iocb.line_cnt = fp->view.rows; |
355 | iocb.col_cnt = fp->view.cols; |
356 | iocb.pf_cnt = 24; |
357 | iocb.re_cnt = 20; |
358 | iocb.map = 0; |
359 | if (copy_to_user(argp, &iocb, sizeof(struct raw3270_iocb))) |
360 | rc = -EFAULT; |
361 | break; |
362 | } |
363 | mutex_unlock(lock: &fs3270_mutex); |
364 | return rc; |
365 | } |
366 | |
367 | /* |
368 | * Allocate fs3270 structure. |
369 | */ |
370 | static struct fs3270 *fs3270_alloc_view(void) |
371 | { |
372 | struct fs3270 *fp; |
373 | |
374 | fp = kzalloc(size: sizeof(*fp), GFP_KERNEL); |
375 | if (!fp) |
376 | return ERR_PTR(error: -ENOMEM); |
377 | fp->init = raw3270_request_alloc(size: 0); |
378 | if (IS_ERR(ptr: fp->init)) { |
379 | kfree(objp: fp); |
380 | return ERR_PTR(error: -ENOMEM); |
381 | } |
382 | return fp; |
383 | } |
384 | |
385 | /* |
386 | * Free fs3270 structure. |
387 | */ |
388 | static void fs3270_free_view(struct raw3270_view *view) |
389 | { |
390 | struct fs3270 *fp; |
391 | |
392 | fp = (struct fs3270 *)view; |
393 | if (fp->rdbuf) |
394 | idal_buffer_free(fp->rdbuf); |
395 | raw3270_request_free(rq: ((struct fs3270 *)view)->init); |
396 | kfree(objp: view); |
397 | } |
398 | |
399 | /* |
400 | * Unlink fs3270 data structure from filp. |
401 | */ |
402 | static void fs3270_release(struct raw3270_view *view) |
403 | { |
404 | struct fs3270 *fp; |
405 | |
406 | fp = (struct fs3270 *)view; |
407 | if (fp->fs_pid) |
408 | kill_pid(pid: fp->fs_pid, SIGHUP, priv: 1); |
409 | } |
410 | |
411 | /* View to a 3270 device. Can be console, tty or fullscreen. */ |
412 | static struct raw3270_fn fs3270_fn = { |
413 | .activate = fs3270_activate, |
414 | .deactivate = fs3270_deactivate, |
415 | .intv = (void *)fs3270_irq, |
416 | .release = fs3270_release, |
417 | .free = fs3270_free_view |
418 | }; |
419 | |
420 | /* |
421 | * This routine is called whenever a 3270 fullscreen device is opened. |
422 | */ |
423 | static int fs3270_open(struct inode *inode, struct file *filp) |
424 | { |
425 | struct fs3270 *fp; |
426 | struct idal_buffer *ib; |
427 | int minor, rc = 0; |
428 | |
429 | if (imajor(inode: file_inode(f: filp)) != IBM_FS3270_MAJOR) |
430 | return -ENODEV; |
431 | minor = iminor(inode: file_inode(f: filp)); |
432 | /* Check for minor 0 multiplexer. */ |
433 | if (minor == 0) { |
434 | struct tty_struct *tty = get_current_tty(); |
435 | |
436 | if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) { |
437 | tty_kref_put(tty); |
438 | return -ENODEV; |
439 | } |
440 | minor = tty->index; |
441 | tty_kref_put(tty); |
442 | } |
443 | mutex_lock(&fs3270_mutex); |
444 | /* Check if some other program is already using fullscreen mode. */ |
445 | fp = (struct fs3270 *)raw3270_find_view(fn: &fs3270_fn, minor); |
446 | if (!IS_ERR(ptr: fp)) { |
447 | raw3270_put_view(view: &fp->view); |
448 | rc = -EBUSY; |
449 | goto out; |
450 | } |
451 | /* Allocate fullscreen view structure. */ |
452 | fp = fs3270_alloc_view(); |
453 | if (IS_ERR(ptr: fp)) { |
454 | rc = PTR_ERR(ptr: fp); |
455 | goto out; |
456 | } |
457 | |
458 | init_waitqueue_head(&fp->wait); |
459 | fp->fs_pid = get_pid(pid: task_pid(current)); |
460 | rc = raw3270_add_view(view: &fp->view, fn: &fs3270_fn, minor, |
461 | RAW3270_VIEW_LOCK_BH); |
462 | if (rc) { |
463 | fs3270_free_view(view: &fp->view); |
464 | goto out; |
465 | } |
466 | |
467 | /* Allocate idal-buffer. */ |
468 | ib = idal_buffer_alloc(2 * fp->view.rows * fp->view.cols + 5, 0); |
469 | if (IS_ERR(ptr: ib)) { |
470 | raw3270_put_view(view: &fp->view); |
471 | raw3270_del_view(view: &fp->view); |
472 | rc = PTR_ERR(ptr: ib); |
473 | goto out; |
474 | } |
475 | fp->rdbuf = ib; |
476 | |
477 | rc = raw3270_activate_view(view: &fp->view); |
478 | if (rc) { |
479 | raw3270_put_view(view: &fp->view); |
480 | raw3270_del_view(view: &fp->view); |
481 | goto out; |
482 | } |
483 | stream_open(inode, filp); |
484 | filp->private_data = fp; |
485 | out: |
486 | mutex_unlock(lock: &fs3270_mutex); |
487 | return rc; |
488 | } |
489 | |
490 | /* |
491 | * This routine is called when the 3270 tty is closed. We wait |
492 | * for the remaining request to be completed. Then we clean up. |
493 | */ |
494 | static int fs3270_close(struct inode *inode, struct file *filp) |
495 | { |
496 | struct fs3270 *fp; |
497 | |
498 | fp = filp->private_data; |
499 | filp->private_data = NULL; |
500 | if (fp) { |
501 | put_pid(pid: fp->fs_pid); |
502 | fp->fs_pid = NULL; |
503 | raw3270_reset(view: &fp->view); |
504 | raw3270_put_view(view: &fp->view); |
505 | raw3270_del_view(view: &fp->view); |
506 | } |
507 | return 0; |
508 | } |
509 | |
510 | static const struct file_operations fs3270_fops = { |
511 | .owner = THIS_MODULE, /* owner */ |
512 | .read = fs3270_read, /* read */ |
513 | .write = fs3270_write, /* write */ |
514 | .unlocked_ioctl = fs3270_ioctl, /* ioctl */ |
515 | .compat_ioctl = fs3270_ioctl, /* ioctl */ |
516 | .open = fs3270_open, /* open */ |
517 | .release = fs3270_close, /* release */ |
518 | .llseek = no_llseek, |
519 | }; |
520 | |
521 | static void fs3270_create_cb(int minor) |
522 | { |
523 | __register_chrdev(IBM_FS3270_MAJOR, baseminor: minor, count: 1, name: "tub" , fops: &fs3270_fops); |
524 | device_create(cls: class3270, NULL, MKDEV(IBM_FS3270_MAJOR, minor), |
525 | NULL, fmt: "3270/tub%d" , minor); |
526 | } |
527 | |
528 | static void fs3270_destroy_cb(int minor) |
529 | { |
530 | device_destroy(cls: class3270, MKDEV(IBM_FS3270_MAJOR, minor)); |
531 | __unregister_chrdev(IBM_FS3270_MAJOR, baseminor: minor, count: 1, name: "tub" ); |
532 | } |
533 | |
534 | static struct raw3270_notifier fs3270_notifier = { |
535 | .create = fs3270_create_cb, |
536 | .destroy = fs3270_destroy_cb, |
537 | }; |
538 | |
539 | /* |
540 | * 3270 fullscreen driver initialization. |
541 | */ |
542 | static int __init fs3270_init(void) |
543 | { |
544 | int rc; |
545 | |
546 | rc = __register_chrdev(IBM_FS3270_MAJOR, baseminor: 0, count: 1, name: "fs3270" , fops: &fs3270_fops); |
547 | if (rc) |
548 | return rc; |
549 | device_create(cls: class3270, NULL, MKDEV(IBM_FS3270_MAJOR, 0), |
550 | NULL, fmt: "3270/tub" ); |
551 | raw3270_register_notifier(notifier: &fs3270_notifier); |
552 | return 0; |
553 | } |
554 | |
555 | static void __exit fs3270_exit(void) |
556 | { |
557 | raw3270_unregister_notifier(notifier: &fs3270_notifier); |
558 | device_destroy(cls: class3270, MKDEV(IBM_FS3270_MAJOR, 0)); |
559 | __unregister_chrdev(IBM_FS3270_MAJOR, baseminor: 0, count: 1, name: "fs3270" ); |
560 | } |
561 | |
562 | MODULE_LICENSE("GPL" ); |
563 | MODULE_ALIAS_CHARDEV_MAJOR(IBM_FS3270_MAJOR); |
564 | |
565 | module_init(fs3270_init); |
566 | module_exit(fs3270_exit); |
567 | |