📄 raw3270.c
字号:
/* * drivers/s390/char/raw3270.c * IBM/3270 Driver - core functions. * * Author(s): * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation */#include <linux/config.h>#include <linux/bootmem.h>#include <linux/module.h>#include <linux/err.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/list.h>#include <linux/slab.h>#include <linux/types.h>#include <linux/wait.h>#include <asm/ccwdev.h>#include <asm/cio.h>#include <asm/ebcdic.h>#include "raw3270.h"#include <linux/major.h>#include <linux/kdev_t.h>#include <linux/device.h>struct class *class3270;/* The main 3270 data structure. */struct raw3270 { struct list_head list; struct ccw_device *cdev; int minor; short model, rows, cols; unsigned long flags; struct list_head req_queue; /* Request queue. */ struct list_head view_list; /* List of available views. */ struct raw3270_view *view; /* Active view. */ struct timer_list timer; /* Device timer. */ unsigned char *ascebc; /* ascii -> ebcdic table */ struct class_device *clttydev; /* 3270-class tty device ptr */ struct class_device *cltubdev; /* 3270-class tub device ptr */};/* raw3270->flags */#define RAW3270_FLAGS_14BITADDR 0 /* 14-bit buffer addresses */#define RAW3270_FLAGS_BUSY 1 /* Device busy, leave it alone */#define RAW3270_FLAGS_ATTN 2 /* Device sent an ATTN interrupt */#define RAW3270_FLAGS_READY 4 /* Device is useable by views */#define RAW3270_FLAGS_CONSOLE 8 /* Device is the console. *//* Semaphore to protect global data of raw3270 (devices, views, etc). */static DECLARE_MUTEX(raw3270_sem);/* List of 3270 devices. */static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices);/* * Flag to indicate if the driver has been registered. Some operations * like waiting for the end of i/o need to be done differently as long * as the kernel is still starting up (console support). */static int raw3270_registered;/* Module parameters */static int tubxcorrect = 0;module_param(tubxcorrect, bool, 0);/* * Wait queue for device init/delete, view delete. */DECLARE_WAIT_QUEUE_HEAD(raw3270_wait_queue);/* * Encode array for 12 bit 3270 addresses. */unsigned char raw3270_ebcgraf[64] = { 0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f};voidraw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr){ if (test_bit(RAW3270_FLAGS_14BITADDR, &rp->flags)) { cp[0] = (addr >> 8) & 0x3f; cp[1] = addr & 0xff; } else { cp[0] = raw3270_ebcgraf[(addr >> 6) & 0x3f]; cp[1] = raw3270_ebcgraf[addr & 0x3f]; }}/* * Allocate a new 3270 ccw request */struct raw3270_request *raw3270_request_alloc(size_t size){ struct raw3270_request *rq; /* Allocate request structure */ rq = kmalloc(sizeof(struct raw3270_request), GFP_KERNEL | GFP_DMA); if (!rq) return ERR_PTR(-ENOMEM); memset(rq, 0, sizeof(struct raw3270_request)); /* alloc output buffer. */ if (size > 0) { rq->buffer = kmalloc(size, GFP_KERNEL | GFP_DMA); if (!rq->buffer) { kfree(rq); return ERR_PTR(-ENOMEM); } } rq->size = size; INIT_LIST_HEAD(&rq->list); /* * Setup ccw. */ rq->ccw.cda = __pa(rq->buffer); rq->ccw.flags = CCW_FLAG_SLI; return rq;}#ifdef CONFIG_TN3270_CONSOLE/* * Allocate a new 3270 ccw request from bootmem. Only works very * early in the boot process. Only con3270.c should be using this. */struct raw3270_request *raw3270_request_alloc_bootmem(size_t size){ struct raw3270_request *rq; rq = alloc_bootmem_low(sizeof(struct raw3270)); if (!rq) return ERR_PTR(-ENOMEM); memset(rq, 0, sizeof(struct raw3270_request)); /* alloc output buffer. */ if (size > 0) { rq->buffer = alloc_bootmem_low(size); if (!rq->buffer) { free_bootmem((unsigned long) rq, sizeof(struct raw3270)); return ERR_PTR(-ENOMEM); } } rq->size = size; INIT_LIST_HEAD(&rq->list); /* * Setup ccw. */ rq->ccw.cda = __pa(rq->buffer); rq->ccw.flags = CCW_FLAG_SLI; return rq;}#endif/* * Free 3270 ccw request */voidraw3270_request_free (struct raw3270_request *rq){ kfree(rq->buffer); kfree(rq);}/* * Reset request to initial state. */voidraw3270_request_reset(struct raw3270_request *rq){ BUG_ON(!list_empty(&rq->list)); rq->ccw.cmd_code = 0; rq->ccw.count = 0; rq->ccw.cda = __pa(rq->buffer); rq->ccw.flags = CCW_FLAG_SLI; rq->rescnt = 0; rq->rc = 0;}/* * Set command code to ccw of a request. */voidraw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd){ rq->ccw.cmd_code = cmd;}/* * Add data fragment to output buffer. */intraw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size){ if (size + rq->ccw.count > rq->size) return -E2BIG; memcpy(rq->buffer + rq->ccw.count, data, size); rq->ccw.count += size; return 0;}/* * Set address/length pair to ccw of a request. */voidraw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size){ rq->ccw.cda = __pa(data); rq->ccw.count = size;}/* * Set idal buffer to ccw of a request. */voidraw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib){ rq->ccw.cda = __pa(ib->data); rq->ccw.count = ib->size; rq->ccw.flags |= CCW_FLAG_IDA;}/* * Stop running ccw. */static intraw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq){ int retries; int rc; if (raw3270_request_final(rq)) return 0; /* Check if interrupt has already been processed */ for (retries = 0; retries < 5; retries++) { if (retries < 2) rc = ccw_device_halt(rp->cdev, (long) rq); else rc = ccw_device_clear(rp->cdev, (long) rq); if (rc == 0) break; /* termination successful */ } return rc;}static intraw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq){ unsigned long flags; int rc; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); rc = raw3270_halt_io_nolock(rp, rq); spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); return rc;}/* * Add the request to the request queue, try to start it if the * 3270 device is idle. Return without waiting for end of i/o. */static int__raw3270_start(struct raw3270 *rp, struct raw3270_view *view, struct raw3270_request *rq){ rq->view = view; raw3270_get_view(view); if (list_empty(&rp->req_queue) && !test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) { /* No other requests are on the queue. Start this one. */ rq->rc = ccw_device_start(rp->cdev, &rq->ccw, (unsigned long) rq, 0, 0); if (rq->rc) { raw3270_put_view(view); return rq->rc; } } list_add_tail(&rq->list, &rp->req_queue); return 0;}intraw3270_start(struct raw3270_view *view, struct raw3270_request *rq){ unsigned long flags; struct raw3270 *rp; int rc; spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); rp = view->dev; if (!rp || rp->view != view) rc = -EACCES; else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) rc = -ENODEV; else rc = __raw3270_start(rp, view, rq); spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); return rc;}intraw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq){ struct raw3270 *rp; int rc; rp = view->dev; if (!rp || rp->view != view) rc = -EACCES; else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) rc = -ENODEV; else rc = __raw3270_start(rp, view, rq); return rc;}intraw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq){ struct raw3270 *rp; rp = view->dev; rq->view = view; raw3270_get_view(view); list_add_tail(&rq->list, &rp->req_queue); return 0;}/* * 3270 interrupt routine, called from the ccw_device layer */static voidraw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb){ struct raw3270 *rp; struct raw3270_view *view; struct raw3270_request *rq; int rc; rp = (struct raw3270 *) cdev->dev.driver_data; if (!rp) return; rq = (struct raw3270_request *) intparm; view = rq ? rq->view : rp->view; if (IS_ERR(irb)) rc = RAW3270_IO_RETRY; else if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) { rq->rc = -EIO; rc = RAW3270_IO_DONE; } else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP)) { /* Handle CE-DE-UE and subsequent UDE */ set_bit(RAW3270_FLAGS_BUSY, &rp->flags); rc = RAW3270_IO_BUSY; } else if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) { /* Wait for UDE if busy flag is set. */ if (irb->scsw.dstat & DEV_STAT_DEV_END) { clear_bit(RAW3270_FLAGS_BUSY, &rp->flags); /* Got it, now retry. */ rc = RAW3270_IO_RETRY; } else rc = RAW3270_IO_BUSY; } else if (view) rc = view->fn->intv(view, rq, irb); else rc = RAW3270_IO_DONE; switch (rc) { case RAW3270_IO_DONE: break; case RAW3270_IO_BUSY: /* * Intervention required by the operator. We have to wait * for unsolicited device end. */ return; case RAW3270_IO_RETRY: if (!rq) break; rq->rc = ccw_device_start(rp->cdev, &rq->ccw, (unsigned long) rq, 0, 0); if (rq->rc == 0) return; /* Sucessfully restarted. */ break; case RAW3270_IO_STOP: if (!rq) break; raw3270_halt_io_nolock(rp, rq); rq->rc = -EIO; break; default: BUG(); } if (rq) { BUG_ON(list_empty(&rq->list)); /* The request completed, remove from queue and do callback. */ list_del_init(&rq->list); if (rq->callback) rq->callback(rq, rq->callback_data); /* Do put_device for get_device in raw3270_start. */ raw3270_put_view(view); } /* * Try to start each request on request queue until one is * started successful. */ while (!list_empty(&rp->req_queue)) { rq = list_entry(rp->req_queue.next,struct raw3270_request,list); rq->rc = ccw_device_start(rp->cdev, &rq->ccw, (unsigned long) rq, 0, 0); if (rq->rc == 0) break; /* Start failed. Remove request and do callback. */ list_del_init(&rq->list); if (rq->callback) rq->callback(rq, rq->callback_data); /* Do put_device for get_device in raw3270_start. */ raw3270_put_view(view); }}/* * Size sensing. */struct raw3270_ua { /* Query Reply structure for Usable Area */ struct { /* Usable Area Query Reply Base */ short l; /* Length of this structured field */ char sfid; /* 0x81 if Query Reply */ char qcode; /* 0x81 if Usable Area */ char flags0; char flags1; short w; /* Width of usable area */ short h; /* Heigth of usavle area */ char units; /* 0x00:in; 0x01:mm */ int xr; int yr; char aw; char ah; short buffsz; /* Character buffer size, bytes */ char xmin; char ymin; char xmax; char ymax; } __attribute__ ((packed)) uab; struct { /* Alternate Usable Area Self-Defining Parameter */ char l; /* Length of this Self-Defining Parm */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -