18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IBM/3270 Driver - fullscreen driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author(s): 68c2ecf20Sopenharmony_ci * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) 78c2ecf20Sopenharmony_ci * Rewritten for 2.5/2.6 by Martin Schwidefsky <schwidefsky@de.ibm.com> 88c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2003, 2009 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/memblock.h> 128c2ecf20Sopenharmony_ci#include <linux/console.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/compat.h> 168c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/list.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/types.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <asm/ccwdev.h> 238c2ecf20Sopenharmony_ci#include <asm/cio.h> 248c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 258c2ecf20Sopenharmony_ci#include <asm/idals.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "raw3270.h" 288c2ecf20Sopenharmony_ci#include "ctrlchar.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic struct raw3270_fn fs3270_fn; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct fs3270 { 338c2ecf20Sopenharmony_ci struct raw3270_view view; 348c2ecf20Sopenharmony_ci struct pid *fs_pid; /* Pid of controlling program. */ 358c2ecf20Sopenharmony_ci int read_command; /* ccw command to use for reads. */ 368c2ecf20Sopenharmony_ci int write_command; /* ccw command to use for writes. */ 378c2ecf20Sopenharmony_ci int attention; /* Got attention. */ 388c2ecf20Sopenharmony_ci int active; /* Fullscreen view is active. */ 398c2ecf20Sopenharmony_ci struct raw3270_request *init; /* single init request. */ 408c2ecf20Sopenharmony_ci wait_queue_head_t wait; /* Init & attention wait queue. */ 418c2ecf20Sopenharmony_ci struct idal_buffer *rdbuf; /* full-screen-deactivate buffer */ 428c2ecf20Sopenharmony_ci size_t rdbuf_size; /* size of data returned by RDBUF */ 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(fs3270_mutex); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic void 488c2ecf20Sopenharmony_cifs3270_wake_up(struct raw3270_request *rq, void *data) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci wake_up((wait_queue_head_t *) data); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic inline int 548c2ecf20Sopenharmony_cifs3270_working(struct fs3270 *fp) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci /* 578c2ecf20Sopenharmony_ci * The fullscreen view is in working order if the view 588c2ecf20Sopenharmony_ci * has been activated AND the initial request is finished. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci return fp->active && raw3270_request_final(fp->init); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int 648c2ecf20Sopenharmony_cifs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct fs3270 *fp; 678c2ecf20Sopenharmony_ci int rc; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci fp = (struct fs3270 *) view; 708c2ecf20Sopenharmony_ci rq->callback = fs3270_wake_up; 718c2ecf20Sopenharmony_ci rq->callback_data = &fp->wait; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci do { 748c2ecf20Sopenharmony_ci if (!fs3270_working(fp)) { 758c2ecf20Sopenharmony_ci /* Fullscreen view isn't ready yet. */ 768c2ecf20Sopenharmony_ci rc = wait_event_interruptible(fp->wait, 778c2ecf20Sopenharmony_ci fs3270_working(fp)); 788c2ecf20Sopenharmony_ci if (rc != 0) 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci rc = raw3270_start(view, rq); 828c2ecf20Sopenharmony_ci if (rc == 0) { 838c2ecf20Sopenharmony_ci /* Started successfully. Now wait for completion. */ 848c2ecf20Sopenharmony_ci wait_event(fp->wait, raw3270_request_final(rq)); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci } while (rc == -EACCES); 878c2ecf20Sopenharmony_ci return rc; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * Switch to the fullscreen view. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_cistatic void 948c2ecf20Sopenharmony_cifs3270_reset_callback(struct raw3270_request *rq, void *data) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct fs3270 *fp; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci fp = (struct fs3270 *) rq->view; 998c2ecf20Sopenharmony_ci raw3270_request_reset(rq); 1008c2ecf20Sopenharmony_ci wake_up(&fp->wait); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void 1048c2ecf20Sopenharmony_cifs3270_restore_callback(struct raw3270_request *rq, void *data) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct fs3270 *fp; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci fp = (struct fs3270 *) rq->view; 1098c2ecf20Sopenharmony_ci if (rq->rc != 0 || rq->rescnt != 0) { 1108c2ecf20Sopenharmony_ci if (fp->fs_pid) 1118c2ecf20Sopenharmony_ci kill_pid(fp->fs_pid, SIGHUP, 1); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci fp->rdbuf_size = 0; 1148c2ecf20Sopenharmony_ci raw3270_request_reset(rq); 1158c2ecf20Sopenharmony_ci wake_up(&fp->wait); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int 1198c2ecf20Sopenharmony_cifs3270_activate(struct raw3270_view *view) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct fs3270 *fp; 1228c2ecf20Sopenharmony_ci char *cp; 1238c2ecf20Sopenharmony_ci int rc; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci fp = (struct fs3270 *) view; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* If an old init command is still running just return. */ 1288c2ecf20Sopenharmony_ci if (!raw3270_request_final(fp->init)) 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (fp->rdbuf_size == 0) { 1328c2ecf20Sopenharmony_ci /* No saved buffer. Just clear the screen. */ 1338c2ecf20Sopenharmony_ci raw3270_request_set_cmd(fp->init, TC_EWRITEA); 1348c2ecf20Sopenharmony_ci fp->init->callback = fs3270_reset_callback; 1358c2ecf20Sopenharmony_ci } else { 1368c2ecf20Sopenharmony_ci /* Restore fullscreen buffer saved by fs3270_deactivate. */ 1378c2ecf20Sopenharmony_ci raw3270_request_set_cmd(fp->init, TC_EWRITEA); 1388c2ecf20Sopenharmony_ci raw3270_request_set_idal(fp->init, fp->rdbuf); 1398c2ecf20Sopenharmony_ci fp->init->ccw.count = fp->rdbuf_size; 1408c2ecf20Sopenharmony_ci cp = fp->rdbuf->data[0]; 1418c2ecf20Sopenharmony_ci cp[0] = TW_KR; 1428c2ecf20Sopenharmony_ci cp[1] = TO_SBA; 1438c2ecf20Sopenharmony_ci cp[2] = cp[6]; 1448c2ecf20Sopenharmony_ci cp[3] = cp[7]; 1458c2ecf20Sopenharmony_ci cp[4] = TO_IC; 1468c2ecf20Sopenharmony_ci cp[5] = TO_SBA; 1478c2ecf20Sopenharmony_ci cp[6] = 0x40; 1488c2ecf20Sopenharmony_ci cp[7] = 0x40; 1498c2ecf20Sopenharmony_ci fp->init->rescnt = 0; 1508c2ecf20Sopenharmony_ci fp->init->callback = fs3270_restore_callback; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci rc = fp->init->rc = raw3270_start_locked(view, fp->init); 1538c2ecf20Sopenharmony_ci if (rc) 1548c2ecf20Sopenharmony_ci fp->init->callback(fp->init, NULL); 1558c2ecf20Sopenharmony_ci else 1568c2ecf20Sopenharmony_ci fp->active = 1; 1578c2ecf20Sopenharmony_ci return rc; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* 1618c2ecf20Sopenharmony_ci * Shutdown fullscreen view. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_cistatic void 1648c2ecf20Sopenharmony_cifs3270_save_callback(struct raw3270_request *rq, void *data) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct fs3270 *fp; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci fp = (struct fs3270 *) rq->view; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Correct idal buffer element 0 address. */ 1718c2ecf20Sopenharmony_ci fp->rdbuf->data[0] -= 5; 1728c2ecf20Sopenharmony_ci fp->rdbuf->size += 5; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* 1758c2ecf20Sopenharmony_ci * If the rdbuf command failed or the idal buffer is 1768c2ecf20Sopenharmony_ci * to small for the amount of data returned by the 1778c2ecf20Sopenharmony_ci * rdbuf command, then we have no choice but to send 1788c2ecf20Sopenharmony_ci * a SIGHUP to the application. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci if (rq->rc != 0 || rq->rescnt == 0) { 1818c2ecf20Sopenharmony_ci if (fp->fs_pid) 1828c2ecf20Sopenharmony_ci kill_pid(fp->fs_pid, SIGHUP, 1); 1838c2ecf20Sopenharmony_ci fp->rdbuf_size = 0; 1848c2ecf20Sopenharmony_ci } else 1858c2ecf20Sopenharmony_ci fp->rdbuf_size = fp->rdbuf->size - rq->rescnt; 1868c2ecf20Sopenharmony_ci raw3270_request_reset(rq); 1878c2ecf20Sopenharmony_ci wake_up(&fp->wait); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void 1918c2ecf20Sopenharmony_cifs3270_deactivate(struct raw3270_view *view) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct fs3270 *fp; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci fp = (struct fs3270 *) view; 1968c2ecf20Sopenharmony_ci fp->active = 0; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* If an old init command is still running just return. */ 1998c2ecf20Sopenharmony_ci if (!raw3270_request_final(fp->init)) 2008c2ecf20Sopenharmony_ci return; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Prepare read-buffer request. */ 2038c2ecf20Sopenharmony_ci raw3270_request_set_cmd(fp->init, TC_RDBUF); 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * Hackish: skip first 5 bytes of the idal buffer to make 2068c2ecf20Sopenharmony_ci * room for the TW_KR/TO_SBA/<address>/<address>/TO_IC sequence 2078c2ecf20Sopenharmony_ci * in the activation command. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci fp->rdbuf->data[0] += 5; 2108c2ecf20Sopenharmony_ci fp->rdbuf->size -= 5; 2118c2ecf20Sopenharmony_ci raw3270_request_set_idal(fp->init, fp->rdbuf); 2128c2ecf20Sopenharmony_ci fp->init->rescnt = 0; 2138c2ecf20Sopenharmony_ci fp->init->callback = fs3270_save_callback; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* Start I/O to read in the 3270 buffer. */ 2168c2ecf20Sopenharmony_ci fp->init->rc = raw3270_start_locked(view, fp->init); 2178c2ecf20Sopenharmony_ci if (fp->init->rc) 2188c2ecf20Sopenharmony_ci fp->init->callback(fp->init, NULL); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void 2228c2ecf20Sopenharmony_cifs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci /* Handle ATTN. Set indication and wake waiters for attention. */ 2258c2ecf20Sopenharmony_ci if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { 2268c2ecf20Sopenharmony_ci fp->attention = 1; 2278c2ecf20Sopenharmony_ci wake_up(&fp->wait); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (rq) { 2318c2ecf20Sopenharmony_ci if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) 2328c2ecf20Sopenharmony_ci rq->rc = -EIO; 2338c2ecf20Sopenharmony_ci else 2348c2ecf20Sopenharmony_ci /* Normal end. Copy residual count. */ 2358c2ecf20Sopenharmony_ci rq->rescnt = irb->scsw.cmd.count; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* 2408c2ecf20Sopenharmony_ci * Process reads from fullscreen 3270. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistatic ssize_t 2438c2ecf20Sopenharmony_cifs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct fs3270 *fp; 2468c2ecf20Sopenharmony_ci struct raw3270_request *rq; 2478c2ecf20Sopenharmony_ci struct idal_buffer *ib; 2488c2ecf20Sopenharmony_ci ssize_t rc; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (count == 0 || count > 65535) 2518c2ecf20Sopenharmony_ci return -EINVAL; 2528c2ecf20Sopenharmony_ci fp = filp->private_data; 2538c2ecf20Sopenharmony_ci if (!fp) 2548c2ecf20Sopenharmony_ci return -ENODEV; 2558c2ecf20Sopenharmony_ci ib = idal_buffer_alloc(count, 0); 2568c2ecf20Sopenharmony_ci if (IS_ERR(ib)) 2578c2ecf20Sopenharmony_ci return -ENOMEM; 2588c2ecf20Sopenharmony_ci rq = raw3270_request_alloc(0); 2598c2ecf20Sopenharmony_ci if (!IS_ERR(rq)) { 2608c2ecf20Sopenharmony_ci if (fp->read_command == 0 && fp->write_command != 0) 2618c2ecf20Sopenharmony_ci fp->read_command = 6; 2628c2ecf20Sopenharmony_ci raw3270_request_set_cmd(rq, fp->read_command ? : 2); 2638c2ecf20Sopenharmony_ci raw3270_request_set_idal(rq, ib); 2648c2ecf20Sopenharmony_ci rc = wait_event_interruptible(fp->wait, fp->attention); 2658c2ecf20Sopenharmony_ci fp->attention = 0; 2668c2ecf20Sopenharmony_ci if (rc == 0) { 2678c2ecf20Sopenharmony_ci rc = fs3270_do_io(&fp->view, rq); 2688c2ecf20Sopenharmony_ci if (rc == 0) { 2698c2ecf20Sopenharmony_ci count -= rq->rescnt; 2708c2ecf20Sopenharmony_ci if (idal_buffer_to_user(ib, data, count) != 0) 2718c2ecf20Sopenharmony_ci rc = -EFAULT; 2728c2ecf20Sopenharmony_ci else 2738c2ecf20Sopenharmony_ci rc = count; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci raw3270_request_free(rq); 2788c2ecf20Sopenharmony_ci } else 2798c2ecf20Sopenharmony_ci rc = PTR_ERR(rq); 2808c2ecf20Sopenharmony_ci idal_buffer_free(ib); 2818c2ecf20Sopenharmony_ci return rc; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* 2858c2ecf20Sopenharmony_ci * Process writes to fullscreen 3270. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_cistatic ssize_t 2888c2ecf20Sopenharmony_cifs3270_write(struct file *filp, const char __user *data, size_t count, loff_t *off) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct fs3270 *fp; 2918c2ecf20Sopenharmony_ci struct raw3270_request *rq; 2928c2ecf20Sopenharmony_ci struct idal_buffer *ib; 2938c2ecf20Sopenharmony_ci int write_command; 2948c2ecf20Sopenharmony_ci ssize_t rc; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci fp = filp->private_data; 2978c2ecf20Sopenharmony_ci if (!fp) 2988c2ecf20Sopenharmony_ci return -ENODEV; 2998c2ecf20Sopenharmony_ci ib = idal_buffer_alloc(count, 0); 3008c2ecf20Sopenharmony_ci if (IS_ERR(ib)) 3018c2ecf20Sopenharmony_ci return -ENOMEM; 3028c2ecf20Sopenharmony_ci rq = raw3270_request_alloc(0); 3038c2ecf20Sopenharmony_ci if (!IS_ERR(rq)) { 3048c2ecf20Sopenharmony_ci if (idal_buffer_from_user(ib, data, count) == 0) { 3058c2ecf20Sopenharmony_ci write_command = fp->write_command ? : 1; 3068c2ecf20Sopenharmony_ci if (write_command == 5) 3078c2ecf20Sopenharmony_ci write_command = 13; 3088c2ecf20Sopenharmony_ci raw3270_request_set_cmd(rq, write_command); 3098c2ecf20Sopenharmony_ci raw3270_request_set_idal(rq, ib); 3108c2ecf20Sopenharmony_ci rc = fs3270_do_io(&fp->view, rq); 3118c2ecf20Sopenharmony_ci if (rc == 0) 3128c2ecf20Sopenharmony_ci rc = count - rq->rescnt; 3138c2ecf20Sopenharmony_ci } else 3148c2ecf20Sopenharmony_ci rc = -EFAULT; 3158c2ecf20Sopenharmony_ci raw3270_request_free(rq); 3168c2ecf20Sopenharmony_ci } else 3178c2ecf20Sopenharmony_ci rc = PTR_ERR(rq); 3188c2ecf20Sopenharmony_ci idal_buffer_free(ib); 3198c2ecf20Sopenharmony_ci return rc; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/* 3238c2ecf20Sopenharmony_ci * process ioctl commands for the tube driver 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_cistatic long 3268c2ecf20Sopenharmony_cifs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci char __user *argp; 3298c2ecf20Sopenharmony_ci struct fs3270 *fp; 3308c2ecf20Sopenharmony_ci struct raw3270_iocb iocb; 3318c2ecf20Sopenharmony_ci int rc; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci fp = filp->private_data; 3348c2ecf20Sopenharmony_ci if (!fp) 3358c2ecf20Sopenharmony_ci return -ENODEV; 3368c2ecf20Sopenharmony_ci if (is_compat_task()) 3378c2ecf20Sopenharmony_ci argp = compat_ptr(arg); 3388c2ecf20Sopenharmony_ci else 3398c2ecf20Sopenharmony_ci argp = (char __user *)arg; 3408c2ecf20Sopenharmony_ci rc = 0; 3418c2ecf20Sopenharmony_ci mutex_lock(&fs3270_mutex); 3428c2ecf20Sopenharmony_ci switch (cmd) { 3438c2ecf20Sopenharmony_ci case TUBICMD: 3448c2ecf20Sopenharmony_ci fp->read_command = arg; 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci case TUBOCMD: 3478c2ecf20Sopenharmony_ci fp->write_command = arg; 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci case TUBGETI: 3508c2ecf20Sopenharmony_ci rc = put_user(fp->read_command, argp); 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci case TUBGETO: 3538c2ecf20Sopenharmony_ci rc = put_user(fp->write_command, argp); 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci case TUBGETMOD: 3568c2ecf20Sopenharmony_ci iocb.model = fp->view.model; 3578c2ecf20Sopenharmony_ci iocb.line_cnt = fp->view.rows; 3588c2ecf20Sopenharmony_ci iocb.col_cnt = fp->view.cols; 3598c2ecf20Sopenharmony_ci iocb.pf_cnt = 24; 3608c2ecf20Sopenharmony_ci iocb.re_cnt = 20; 3618c2ecf20Sopenharmony_ci iocb.map = 0; 3628c2ecf20Sopenharmony_ci if (copy_to_user(argp, &iocb, sizeof(struct raw3270_iocb))) 3638c2ecf20Sopenharmony_ci rc = -EFAULT; 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci mutex_unlock(&fs3270_mutex); 3678c2ecf20Sopenharmony_ci return rc; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/* 3718c2ecf20Sopenharmony_ci * Allocate fs3270 structure. 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_cistatic struct fs3270 * 3748c2ecf20Sopenharmony_cifs3270_alloc_view(void) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct fs3270 *fp; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci fp = kzalloc(sizeof(struct fs3270),GFP_KERNEL); 3798c2ecf20Sopenharmony_ci if (!fp) 3808c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3818c2ecf20Sopenharmony_ci fp->init = raw3270_request_alloc(0); 3828c2ecf20Sopenharmony_ci if (IS_ERR(fp->init)) { 3838c2ecf20Sopenharmony_ci kfree(fp); 3848c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci return fp; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* 3908c2ecf20Sopenharmony_ci * Free fs3270 structure. 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_cistatic void 3938c2ecf20Sopenharmony_cifs3270_free_view(struct raw3270_view *view) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct fs3270 *fp; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci fp = (struct fs3270 *) view; 3988c2ecf20Sopenharmony_ci if (fp->rdbuf) 3998c2ecf20Sopenharmony_ci idal_buffer_free(fp->rdbuf); 4008c2ecf20Sopenharmony_ci raw3270_request_free(((struct fs3270 *) view)->init); 4018c2ecf20Sopenharmony_ci kfree(view); 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* 4058c2ecf20Sopenharmony_ci * Unlink fs3270 data structure from filp. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_cistatic void 4088c2ecf20Sopenharmony_cifs3270_release(struct raw3270_view *view) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct fs3270 *fp; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci fp = (struct fs3270 *) view; 4138c2ecf20Sopenharmony_ci if (fp->fs_pid) 4148c2ecf20Sopenharmony_ci kill_pid(fp->fs_pid, SIGHUP, 1); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci/* View to a 3270 device. Can be console, tty or fullscreen. */ 4188c2ecf20Sopenharmony_cistatic struct raw3270_fn fs3270_fn = { 4198c2ecf20Sopenharmony_ci .activate = fs3270_activate, 4208c2ecf20Sopenharmony_ci .deactivate = fs3270_deactivate, 4218c2ecf20Sopenharmony_ci .intv = (void *) fs3270_irq, 4228c2ecf20Sopenharmony_ci .release = fs3270_release, 4238c2ecf20Sopenharmony_ci .free = fs3270_free_view 4248c2ecf20Sopenharmony_ci}; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci/* 4278c2ecf20Sopenharmony_ci * This routine is called whenever a 3270 fullscreen device is opened. 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_cistatic int 4308c2ecf20Sopenharmony_cifs3270_open(struct inode *inode, struct file *filp) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct fs3270 *fp; 4338c2ecf20Sopenharmony_ci struct idal_buffer *ib; 4348c2ecf20Sopenharmony_ci int minor, rc = 0; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (imajor(file_inode(filp)) != IBM_FS3270_MAJOR) 4378c2ecf20Sopenharmony_ci return -ENODEV; 4388c2ecf20Sopenharmony_ci minor = iminor(file_inode(filp)); 4398c2ecf20Sopenharmony_ci /* Check for minor 0 multiplexer. */ 4408c2ecf20Sopenharmony_ci if (minor == 0) { 4418c2ecf20Sopenharmony_ci struct tty_struct *tty = get_current_tty(); 4428c2ecf20Sopenharmony_ci if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) { 4438c2ecf20Sopenharmony_ci tty_kref_put(tty); 4448c2ecf20Sopenharmony_ci return -ENODEV; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci minor = tty->index; 4478c2ecf20Sopenharmony_ci tty_kref_put(tty); 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci mutex_lock(&fs3270_mutex); 4508c2ecf20Sopenharmony_ci /* Check if some other program is already using fullscreen mode. */ 4518c2ecf20Sopenharmony_ci fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); 4528c2ecf20Sopenharmony_ci if (!IS_ERR(fp)) { 4538c2ecf20Sopenharmony_ci raw3270_put_view(&fp->view); 4548c2ecf20Sopenharmony_ci rc = -EBUSY; 4558c2ecf20Sopenharmony_ci goto out; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci /* Allocate fullscreen view structure. */ 4588c2ecf20Sopenharmony_ci fp = fs3270_alloc_view(); 4598c2ecf20Sopenharmony_ci if (IS_ERR(fp)) { 4608c2ecf20Sopenharmony_ci rc = PTR_ERR(fp); 4618c2ecf20Sopenharmony_ci goto out; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci init_waitqueue_head(&fp->wait); 4658c2ecf20Sopenharmony_ci fp->fs_pid = get_pid(task_pid(current)); 4668c2ecf20Sopenharmony_ci rc = raw3270_add_view(&fp->view, &fs3270_fn, minor, 4678c2ecf20Sopenharmony_ci RAW3270_VIEW_LOCK_BH); 4688c2ecf20Sopenharmony_ci if (rc) { 4698c2ecf20Sopenharmony_ci fs3270_free_view(&fp->view); 4708c2ecf20Sopenharmony_ci goto out; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* Allocate idal-buffer. */ 4748c2ecf20Sopenharmony_ci ib = idal_buffer_alloc(2*fp->view.rows*fp->view.cols + 5, 0); 4758c2ecf20Sopenharmony_ci if (IS_ERR(ib)) { 4768c2ecf20Sopenharmony_ci raw3270_put_view(&fp->view); 4778c2ecf20Sopenharmony_ci raw3270_del_view(&fp->view); 4788c2ecf20Sopenharmony_ci rc = PTR_ERR(ib); 4798c2ecf20Sopenharmony_ci goto out; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci fp->rdbuf = ib; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci rc = raw3270_activate_view(&fp->view); 4848c2ecf20Sopenharmony_ci if (rc) { 4858c2ecf20Sopenharmony_ci raw3270_put_view(&fp->view); 4868c2ecf20Sopenharmony_ci raw3270_del_view(&fp->view); 4878c2ecf20Sopenharmony_ci goto out; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci stream_open(inode, filp); 4908c2ecf20Sopenharmony_ci filp->private_data = fp; 4918c2ecf20Sopenharmony_ciout: 4928c2ecf20Sopenharmony_ci mutex_unlock(&fs3270_mutex); 4938c2ecf20Sopenharmony_ci return rc; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/* 4978c2ecf20Sopenharmony_ci * This routine is called when the 3270 tty is closed. We wait 4988c2ecf20Sopenharmony_ci * for the remaining request to be completed. Then we clean up. 4998c2ecf20Sopenharmony_ci */ 5008c2ecf20Sopenharmony_cistatic int 5018c2ecf20Sopenharmony_cifs3270_close(struct inode *inode, struct file *filp) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct fs3270 *fp; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci fp = filp->private_data; 5068c2ecf20Sopenharmony_ci filp->private_data = NULL; 5078c2ecf20Sopenharmony_ci if (fp) { 5088c2ecf20Sopenharmony_ci put_pid(fp->fs_pid); 5098c2ecf20Sopenharmony_ci fp->fs_pid = NULL; 5108c2ecf20Sopenharmony_ci raw3270_reset(&fp->view); 5118c2ecf20Sopenharmony_ci raw3270_put_view(&fp->view); 5128c2ecf20Sopenharmony_ci raw3270_del_view(&fp->view); 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic const struct file_operations fs3270_fops = { 5188c2ecf20Sopenharmony_ci .owner = THIS_MODULE, /* owner */ 5198c2ecf20Sopenharmony_ci .read = fs3270_read, /* read */ 5208c2ecf20Sopenharmony_ci .write = fs3270_write, /* write */ 5218c2ecf20Sopenharmony_ci .unlocked_ioctl = fs3270_ioctl, /* ioctl */ 5228c2ecf20Sopenharmony_ci .compat_ioctl = fs3270_ioctl, /* ioctl */ 5238c2ecf20Sopenharmony_ci .open = fs3270_open, /* open */ 5248c2ecf20Sopenharmony_ci .release = fs3270_close, /* release */ 5258c2ecf20Sopenharmony_ci .llseek = no_llseek, 5268c2ecf20Sopenharmony_ci}; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic void fs3270_create_cb(int minor) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci __register_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub", &fs3270_fops); 5318c2ecf20Sopenharmony_ci device_create(class3270, NULL, MKDEV(IBM_FS3270_MAJOR, minor), 5328c2ecf20Sopenharmony_ci NULL, "3270/tub%d", minor); 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic void fs3270_destroy_cb(int minor) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, minor)); 5388c2ecf20Sopenharmony_ci __unregister_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub"); 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic struct raw3270_notifier fs3270_notifier = 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci .create = fs3270_create_cb, 5448c2ecf20Sopenharmony_ci .destroy = fs3270_destroy_cb, 5458c2ecf20Sopenharmony_ci}; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/* 5488c2ecf20Sopenharmony_ci * 3270 fullscreen driver initialization. 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_cistatic int __init 5518c2ecf20Sopenharmony_cifs3270_init(void) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci int rc; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci rc = __register_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270", &fs3270_fops); 5568c2ecf20Sopenharmony_ci if (rc) 5578c2ecf20Sopenharmony_ci return rc; 5588c2ecf20Sopenharmony_ci device_create(class3270, NULL, MKDEV(IBM_FS3270_MAJOR, 0), 5598c2ecf20Sopenharmony_ci NULL, "3270/tub"); 5608c2ecf20Sopenharmony_ci raw3270_register_notifier(&fs3270_notifier); 5618c2ecf20Sopenharmony_ci return 0; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic void __exit 5658c2ecf20Sopenharmony_cifs3270_exit(void) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci raw3270_unregister_notifier(&fs3270_notifier); 5688c2ecf20Sopenharmony_ci device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, 0)); 5698c2ecf20Sopenharmony_ci __unregister_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270"); 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5738c2ecf20Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(IBM_FS3270_MAJOR); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cimodule_init(fs3270_init); 5768c2ecf20Sopenharmony_cimodule_exit(fs3270_exit); 577