diff options
Diffstat (limited to 'drivers/s390/char')
-rw-r--r-- | drivers/s390/char/Makefile | 2 | ||||
-rw-r--r-- | drivers/s390/char/con3270.c | 3 | ||||
-rw-r--r-- | drivers/s390/char/fs3270.c | 3 | ||||
-rw-r--r-- | drivers/s390/char/raw3270.c | 131 | ||||
-rw-r--r-- | drivers/s390/char/raw3270.h | 8 | ||||
-rw-r--r-- | drivers/s390/char/sclp.h | 38 | ||||
-rw-r--r-- | drivers/s390/char/sclp_cmd.c | 61 | ||||
-rw-r--r-- | drivers/s390/char/sclp_cpi_sys.c | 2 | ||||
-rw-r--r-- | drivers/s390/char/sclp_early.c | 6 | ||||
-rw-r--r-- | drivers/s390/char/sclp_pci.c | 193 | ||||
-rw-r--r-- | drivers/s390/char/tty3270.c | 86 |
11 files changed, 342 insertions, 191 deletions
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index dd2f7c832e5e..41e28b23b26a 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -18,6 +18,8 @@ obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o obj-$(CONFIG_SCLP_ASYNC) += sclp_async.o +obj-$(CONFIG_PCI) += sclp_pci.o + obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o obj-$(CONFIG_VMCP) += vmcp.o diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 4d7a9badfede..6b1577c73fe7 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -400,7 +400,7 @@ con3270_deactivate(struct raw3270_view *view) del_timer(&cp->timer); } -static int +static void con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) { /* Handle ATTN. Schedule tasklet to read aid. */ @@ -418,7 +418,6 @@ con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) cp->update_flags = CON_UPDATE_ALL; con3270_set_timer(cp, 1); } - return RAW3270_IO_DONE; } /* Console view to a 3270 device. */ diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 71e974738014..85eca1cef063 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -217,7 +217,7 @@ fs3270_deactivate(struct raw3270_view *view) fp->init->callback(fp->init, NULL); } -static int +static void fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) { /* Handle ATTN. Set indication and wake waiters for attention. */ @@ -233,7 +233,6 @@ fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) /* Normal end. Copy residual count. */ rq->rescnt = irb->scsw.cmd.count; } - return RAW3270_IO_DONE; } /* diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 220acb4cbee5..a2da898ce90f 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -90,6 +90,8 @@ module_param(tubxcorrect, bool, 0); */ DECLARE_WAIT_QUEUE_HEAD(raw3270_wait_queue); +static void __raw3270_disconnect(struct raw3270 *rp); + /* * Encode array for 12 bit 3270 addresses. */ @@ -229,29 +231,6 @@ raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) } /* - * Stop running ccw. - */ -static int -__raw3270_halt_io(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; -} - -/* * 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. */ @@ -342,7 +321,6 @@ raw3270_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 = dev_get_drvdata(&cdev->dev); if (!rp) @@ -350,57 +328,31 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) rq = (struct raw3270_request *) intparm; view = rq ? rq->view : rp->view; - if (IS_ERR(irb)) - rc = RAW3270_IO_RETRY; - else if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) { - rq->rc = -EIO; - rc = RAW3270_IO_DONE; - } else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END | - DEV_STAT_UNIT_EXCEP)) { + if (!IS_ERR(irb)) { /* 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.cmd.dstat & DEV_STAT_DEV_END) { + if (irb->scsw.cmd.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; + if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | + DEV_STAT_DEV_END | + DEV_STAT_UNIT_EXCEP)) + set_bit(RAW3270_FLAGS_BUSY, &rp->flags); + /* Handle disconnected devices */ + if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) && + (irb->ecw[0] & SNS0_INTERVENTION_REQ)) { + set_bit(RAW3270_FLAGS_BUSY, &rp->flags); + if (rp->state > RAW3270_STATE_RESET) + __raw3270_disconnect(rp); + } + /* Call interrupt handler of the view */ + if (view) + view->fn->intv(view, rq, irb); + } - switch (rc) { - case RAW3270_IO_DONE: - break; - case RAW3270_IO_BUSY: - /* - * Intervention required by the operator. We have to wait - * for unsolicited device end. - */ + if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) + /* Device busy, do not start I/O */ 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; /* Successfully restarted. */ - break; - case RAW3270_IO_STOP: - if (!rq) - break; - __raw3270_halt_io(rp, rq); - rq->rc = -EIO; - break; - default: - BUG(); - } - if (rq) { - BUG_ON(list_empty(&rq->list)); + + if (rq && !list_empty(&rq->list)) { /* The request completed, remove from queue and do callback. */ list_del_init(&rq->list); if (rq->callback) @@ -408,6 +360,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) /* 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. @@ -685,23 +638,34 @@ raw3270_reset(struct raw3270_view *view) return rc; } -static int +static void +__raw3270_disconnect(struct raw3270 *rp) +{ + struct raw3270_request *rq; + struct raw3270_view *view; + + rp->state = RAW3270_STATE_INIT; + rp->view = &rp->init_view; + /* Cancel all queued requests */ + while (!list_empty(&rp->req_queue)) { + rq = list_entry(rp->req_queue.next,struct raw3270_request,list); + view = rq->view; + rq->rc = -EACCES; + list_del_init(&rq->list); + if (rq->callback) + rq->callback(rq, rq->callback_data); + raw3270_put_view(view); + } + /* Start from scratch */ + __raw3270_reset_device(rp); +} + +static void raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, struct irb *irb) { struct raw3270 *rp; - /* - * Unit-Check Processing: - * Expect Command Reject or Intervention Required. - */ - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { - /* Request finished abnormally. */ - if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { - set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); - return RAW3270_IO_BUSY; - } - } if (rq) { if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { if (irb->ecw[0] & SNS0_CMD_REJECT) @@ -715,7 +679,6 @@ raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, rp = view->dev; raw3270_read_modified(rp); } - return RAW3270_IO_DONE; } static struct raw3270_fn raw3270_init_fn = { diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index e1e41c2861fb..56519cbb165c 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -125,19 +125,13 @@ raw3270_request_final(struct raw3270_request *rq) void raw3270_buffer_address(struct raw3270 *, char *, unsigned short); -/* Return value of *intv (see raw3270_fn below) can be one of the following: */ -#define RAW3270_IO_DONE 0 /* request finished */ -#define RAW3270_IO_BUSY 1 /* request still active */ -#define RAW3270_IO_RETRY 2 /* retry current request */ -#define RAW3270_IO_STOP 3 /* kill current request */ - /* * Functions of a 3270 view. */ struct raw3270_fn { int (*activate)(struct raw3270_view *); void (*deactivate)(struct raw3270_view *); - int (*intv)(struct raw3270_view *, + void (*intv)(struct raw3270_view *, struct raw3270_request *, struct irb *); void (*release)(struct raw3270_view *); void (*free)(struct raw3270_view *); diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 026e38990952..7a10c56334bb 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -17,33 +17,35 @@ #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) #define SCLP_CONSOLE_PAGES 6 +#define SCLP_EVTYP_MASK(T) (1U << (32 - (T))) + #define EVTYP_OPCMD 0x01 #define EVTYP_MSG 0x02 +#define EVTYP_CONFMGMDATA 0x04 #define EVTYP_DIAG_TEST 0x07 #define EVTYP_STATECHANGE 0x08 #define EVTYP_PMSGCMD 0x09 -#define EVTYP_CNTLPROGOPCMD 0x20 -#define EVTYP_CNTLPROGIDENT 0x0B -#define EVTYP_SIGQUIESCE 0x1D +#define EVTYP_ASYNC 0x0A +#define EVTYP_CTLPROGIDENT 0x0B +#define EVTYP_ERRNOTIFY 0x18 #define EVTYP_VT220MSG 0x1A -#define EVTYP_CONFMGMDATA 0x04 #define EVTYP_SDIAS 0x1C -#define EVTYP_ASYNC 0x0A +#define EVTYP_SIGQUIESCE 0x1D #define EVTYP_OCF 0x1E -#define EVTYP_OPCMD_MASK 0x80000000 -#define EVTYP_MSG_MASK 0x40000000 -#define EVTYP_DIAG_TEST_MASK 0x02000000 -#define EVTYP_STATECHANGE_MASK 0x01000000 -#define EVTYP_PMSGCMD_MASK 0x00800000 -#define EVTYP_CTLPROGOPCMD_MASK 0x00000001 -#define EVTYP_CTLPROGIDENT_MASK 0x00200000 -#define EVTYP_SIGQUIESCE_MASK 0x00000008 -#define EVTYP_VT220MSG_MASK 0x00000040 -#define EVTYP_CONFMGMDATA_MASK 0x10000000 -#define EVTYP_SDIAS_MASK 0x00000010 -#define EVTYP_ASYNC_MASK 0x00400000 -#define EVTYP_OCF_MASK 0x00000004 +#define EVTYP_OPCMD_MASK SCLP_EVTYP_MASK(EVTYP_OPCMD) +#define EVTYP_MSG_MASK SCLP_EVTYP_MASK(EVTYP_MSG) +#define EVTYP_CONFMGMDATA_MASK SCLP_EVTYP_MASK(EVTYP_CONFMGMDATA) +#define EVTYP_DIAG_TEST_MASK SCLP_EVTYP_MASK(EVTYP_DIAG_TEST) +#define EVTYP_STATECHANGE_MASK SCLP_EVTYP_MASK(EVTYP_STATECHANGE) +#define EVTYP_PMSGCMD_MASK SCLP_EVTYP_MASK(EVTYP_PMSGCMD) +#define EVTYP_ASYNC_MASK SCLP_EVTYP_MASK(EVTYP_ASYNC) +#define EVTYP_CTLPROGIDENT_MASK SCLP_EVTYP_MASK(EVTYP_CTLPROGIDENT) +#define EVTYP_ERRNOTIFY_MASK SCLP_EVTYP_MASK(EVTYP_ERRNOTIFY) +#define EVTYP_VT220MSG_MASK SCLP_EVTYP_MASK(EVTYP_VT220MSG) +#define EVTYP_SDIAS_MASK SCLP_EVTYP_MASK(EVTYP_SDIAS) +#define EVTYP_SIGQUIESCE_MASK SCLP_EVTYP_MASK(EVTYP_SIGQUIESCE) +#define EVTYP_OCF_MASK SCLP_EVTYP_MASK(EVTYP_OCF) #define GNRLMSGFLGS_DOM 0x8000 #define GNRLMSGFLGS_SNDALRM 0x4000 diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index d3947ea3e351..e3fc7539116b 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -576,67 +576,6 @@ __initcall(sclp_detect_standby_memory); #endif /* CONFIG_MEMORY_HOTPLUG */ /* - * PCI I/O adapter configuration related functions. - */ -#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001 -#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001 - -#define SCLP_RECONFIG_PCI_ATPYE 2 - -struct pci_cfg_sccb { - struct sccb_header header; - u8 atype; /* adapter type */ - u8 reserved1; - u16 reserved2; - u32 aid; /* adapter identifier */ -} __packed; - -static int do_pci_configure(sclp_cmdw_t cmd, u32 fid) -{ - struct pci_cfg_sccb *sccb; - int rc; - - if (!SCLP_HAS_PCI_RECONFIG) - return -EOPNOTSUPP; - - sccb = (struct pci_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!sccb) - return -ENOMEM; - - sccb->header.length = PAGE_SIZE; - sccb->atype = SCLP_RECONFIG_PCI_ATPYE; - sccb->aid = fid; - rc = sclp_sync_request(cmd, sccb); - if (rc) - goto out; - switch (sccb->header.response_code) { - case 0x0020: - case 0x0120: - break; - default: - pr_warn("configure PCI I/O adapter failed: cmd=0x%08x response=0x%04x\n", - cmd, sccb->header.response_code); - rc = -EIO; - break; - } -out: - free_page((unsigned long) sccb); - return rc; -} - -int sclp_pci_configure(u32 fid) -{ - return do_pci_configure(SCLP_CMDW_CONFIGURE_PCI, fid); -} -EXPORT_SYMBOL(sclp_pci_configure); - -int sclp_pci_deconfigure(u32 fid) -{ - return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid); -} -EXPORT_SYMBOL(sclp_pci_deconfigure); - -/* * Channel path configuration related functions. */ diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c index f344e5bd2d9f..90d92fbe7b9b 100644 --- a/drivers/s390/char/sclp_cpi_sys.c +++ b/drivers/s390/char/sclp_cpi_sys.c @@ -93,7 +93,7 @@ static struct sclp_req *cpi_prepare_req(void) /* setup SCCB for Control-Program Identification */ sccb->header.length = sizeof(struct cpi_sccb); sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); - sccb->cpi_evbuf.header.type = 0x0b; + sccb->cpi_evbuf.header.type = EVTYP_CTLPROGIDENT; evb = &sccb->cpi_evbuf; /* set system type */ diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index 6804354c42bd..0ac520dd1b21 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c @@ -49,7 +49,9 @@ struct read_info_sccb { u8 _pad_117[119 - 117]; /* 117-118 */ u8 fac119; /* 119 */ u16 hcpua; /* 120-121 */ - u8 _pad_122[4096 - 122]; /* 122-4095 */ + u8 _pad_122[124 - 122]; /* 122-123 */ + u32 hmfai; /* 124-127 */ + u8 _pad_128[4096 - 128]; /* 128-4095 */ } __packed __aligned(PAGE_SIZE); static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata; @@ -155,6 +157,8 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb) sclp.mtid = (sccb->fac42 & 0x80) ? (sccb->fac42 & 31) : 0; sclp.mtid_cp = (sccb->fac42 & 0x80) ? (sccb->fac43 & 31) : 0; sclp.mtid_prev = (sccb->fac42 & 0x80) ? (sccb->fac66 & 31) : 0; + + sclp.hmfai = sccb->hmfai; } /* diff --git a/drivers/s390/char/sclp_pci.c b/drivers/s390/char/sclp_pci.c new file mode 100644 index 000000000000..4dbb3dfd4bc7 --- /dev/null +++ b/drivers/s390/char/sclp_pci.c @@ -0,0 +1,193 @@ +/* + * PCI I/O adapter configuration related functions. + * + * Copyright IBM Corp. 2016 + */ +#define KMSG_COMPONENT "sclp_cmd" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/completion.h> +#include <linux/export.h> +#include <linux/mutex.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/err.h> + +#include <asm/sclp.h> + +#include "sclp.h" + +#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001 +#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001 + +#define SCLP_ATYPE_PCI 2 + +#define SCLP_ERRNOTIFY_AQ_REPAIR 1 +#define SCLP_ERRNOTIFY_AQ_INFO_LOG 2 + +static DEFINE_MUTEX(sclp_pci_mutex); +static struct sclp_register sclp_pci_event = { + .send_mask = EVTYP_ERRNOTIFY_MASK, +}; + +struct err_notify_evbuf { + struct evbuf_header header; + u8 action; + u8 atype; + u32 fh; + u32 fid; + u8 data[0]; +} __packed; + +struct err_notify_sccb { + struct sccb_header header; + struct err_notify_evbuf evbuf; +} __packed; + +struct pci_cfg_sccb { + struct sccb_header header; + u8 atype; /* adapter type */ + u8 reserved1; + u16 reserved2; + u32 aid; /* adapter identifier */ +} __packed; + +static int do_pci_configure(sclp_cmdw_t cmd, u32 fid) +{ + struct pci_cfg_sccb *sccb; + int rc; + + if (!SCLP_HAS_PCI_RECONFIG) + return -EOPNOTSUPP; + + sccb = (struct pci_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + + sccb->header.length = PAGE_SIZE; + sccb->atype = SCLP_ATYPE_PCI; + sccb->aid = fid; + rc = sclp_sync_request(cmd, sccb); + if (rc) + goto out; + switch (sccb->header.response_code) { + case 0x0020: + case 0x0120: + break; + default: + pr_warn("configure PCI I/O adapter failed: cmd=0x%08x response=0x%04x\n", + cmd, sccb->header.response_code); + rc = -EIO; + break; + } +out: + free_page((unsigned long) sccb); + return rc; +} + +int sclp_pci_configure(u32 fid) +{ + return do_pci_configure(SCLP_CMDW_CONFIGURE_PCI, fid); +} +EXPORT_SYMBOL(sclp_pci_configure); + +int sclp_pci_deconfigure(u32 fid) +{ + return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid); +} +EXPORT_SYMBOL(sclp_pci_deconfigure); + +static void sclp_pci_callback(struct sclp_req *req, void *data) +{ + struct completion *completion = data; + + complete(completion); +} + +static int sclp_pci_check_report(struct zpci_report_error_header *report) +{ + if (report->version != 1) + return -EINVAL; + + if (report->action != SCLP_ERRNOTIFY_AQ_REPAIR && + report->action != SCLP_ERRNOTIFY_AQ_INFO_LOG) + return -EINVAL; + + if (report->length > (PAGE_SIZE - sizeof(struct err_notify_sccb))) + return -EINVAL; + + return 0; +} + +int sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid) +{ + DECLARE_COMPLETION_ONSTACK(completion); + struct err_notify_sccb *sccb; + struct sclp_req req; + int ret; + + ret = sclp_pci_check_report(report); + if (ret) + return ret; + + mutex_lock(&sclp_pci_mutex); + ret = sclp_register(&sclp_pci_event); + if (ret) + goto out_unlock; + + if (!(sclp_pci_event.sclp_receive_mask & EVTYP_ERRNOTIFY_MASK)) { + ret = -EOPNOTSUPP; + goto out_unregister; + } + + sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) { + ret = -ENOMEM; + goto out_unregister; + } + + memset(&req, 0, sizeof(req)); + req.callback_data = &completion; + req.callback = sclp_pci_callback; + req.command = SCLP_CMDW_WRITE_EVENT_DATA; + req.status = SCLP_REQ_FILLED; + req.sccb = sccb; + + sccb->evbuf.header.length = sizeof(sccb->evbuf) + report->length; + sccb->evbuf.header.type = EVTYP_ERRNOTIFY; + sccb->header.length = sizeof(sccb->header) + sccb->evbuf.header.length; + + sccb->evbuf.action = report->action; + sccb->evbuf.atype = SCLP_ATYPE_PCI; + sccb->evbuf.fh = fh; + sccb->evbuf.fid = fid; + + memcpy(sccb->evbuf.data, report->data, report->length); + + ret = sclp_add_request(&req); + if (ret) + goto out_free_req; + + wait_for_completion(&completion); + if (req.status != SCLP_REQ_DONE) { + pr_warn("request failed (status=0x%02x)\n", + req.status); + ret = -EIO; + goto out_free_req; + } + + if (sccb->header.response_code != 0x0020) { + pr_warn("request failed with response code 0x%x\n", + sccb->header.response_code); + ret = -EIO; + } + +out_free_req: + free_page((unsigned long) sccb); +out_unregister: + sclp_unregister(&sclp_pci_event); +out_unlock: + mutex_unlock(&sclp_pci_mutex); + return ret; +} diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 080a9872c68f..272cb6cd1b2a 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -92,6 +92,7 @@ struct tty3270 { unsigned char inattr; /* Visible/invisible input. */ int throttle, attn; /* tty throttle/unthrottle. */ struct tasklet_struct readlet; /* Tasklet to issue read request. */ + struct tasklet_struct hanglet; /* Tasklet to hang up the tty. */ struct kbd_data *kbd; /* key_maps stuff. */ /* Escape sequence parsing. */ @@ -319,6 +320,27 @@ tty3270_blank_line(struct tty3270 *tp) } /* + * Create a blank screen and remove all lines from the history. + */ +static void +tty3270_blank_screen(struct tty3270 *tp) +{ + struct string *s, *n; + int i; + + for (i = 0; i < tp->view.rows - 2; i++) + tp->screen[i].len = 0; + tp->nr_up = 0; + list_for_each_entry_safe(s, n, &tp->lines, list) { + list_del(&s->list); + if (!list_empty(&s->update)) + list_del(&s->update); + tp->nr_lines--; + free_string(&tp->freemem, s); + } +} + +/* * Write request completion callback. */ static void @@ -405,7 +427,10 @@ tty3270_update(struct tty3270 *tp) if (raw3270_request_add_data(wrq, str, len) != 0) break; list_del_init(&s->update); - sba = s->string + s->len - 3; + if (s->string[s->len - 4] == TO_RA) + sba = s->string + s->len - 3; + else + sba = invalid_sba; } if (list_empty(&tp->update)) updated |= TTY_UPDATE_LIST; @@ -622,6 +647,16 @@ tty3270_issue_read(struct tty3270 *tp, int lock) } /* + * Hang up the tty + */ +static void +tty3270_hangup_tasklet(struct tty3270 *tp) +{ + tty_port_tty_hangup(&tp->port, true); + raw3270_put_view(&tp->view); +} + +/* * Switch to the tty view. */ static int @@ -642,7 +677,7 @@ tty3270_deactivate(struct raw3270_view *view) del_timer(&tp->timer); } -static int +static void tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) { /* Handle ATTN. Schedule tasklet to read aid. */ @@ -654,17 +689,19 @@ tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) } if (rq) { - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { rq->rc = -EIO; - else + raw3270_get_view(&tp->view); + tasklet_schedule(&tp->hanglet); + } else { /* Normal end. Copy residual count. */ rq->rescnt = irb->scsw.cmd.count; + } } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { /* Interrupt without an outstanding request -> update all */ tp->update_flags = TTY_UPDATE_ALL; tty3270_set_timer(tp, 1); } - return RAW3270_IO_DONE; } /* @@ -716,6 +753,9 @@ tty3270_alloc_view(void) tasklet_init(&tp->readlet, (void (*)(unsigned long)) tty3270_read_tasklet, (unsigned long) tp->read); + tasklet_init(&tp->hanglet, + (void (*)(unsigned long)) tty3270_hangup_tasklet, + (unsigned long) tp); INIT_WORK(&tp->resize_work, tty3270_resize_work); return tp; @@ -814,6 +854,7 @@ static void tty3270_resize_work(struct work_struct *work) return; /* Switch to new output size */ spin_lock_bh(&tp->view.lock); + tty3270_blank_screen(tp); oscreen = tp->screen; orows = tp->view.rows; tp->view.model = tp->n_model; @@ -824,7 +865,6 @@ static void tty3270_resize_work(struct work_struct *work) free_string(&tp->freemem, tp->status); tty3270_create_prompt(tp); tty3270_create_status(tp); - tp->nr_up = 0; while (tp->nr_lines < tp->view.rows - 2) tty3270_blank_line(tp); tp->update_flags = TTY_UPDATE_ALL; @@ -838,6 +878,7 @@ static void tty3270_resize_work(struct work_struct *work) ws.ws_row = tp->view.rows - 2; ws.ws_col = tp->view.cols; tty_do_resize(tty, &ws); + tty_kref_put(tty); } static void @@ -845,6 +886,8 @@ tty3270_resize(struct raw3270_view *view, int model, int rows, int cols) { struct tty3270 *tp = container_of(view, struct tty3270, view); + if (tp->n_model == model && tp->n_rows == rows && tp->n_cols == cols) + return; tp->n_model = model; tp->n_rows = rows; tp->n_cols = cols; @@ -923,10 +966,8 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) tty->winsize.ws_row = tp->view.rows - 2; tty->winsize.ws_col = tp->view.cols; tp->port.low_latency = 0; - /* why to reassign? */ - tty_port_tty_set(&tp->port, tty); tp->inattr = TF_INPUT; - return tty_port_install(&tp->port, driver, tty); + goto port_install; } if (tty3270_max_index < tty->index + 1) tty3270_max_index = tty->index + 1; @@ -952,7 +993,6 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) return rc; } - tty_port_tty_set(&tp->port, tty); tp->port.low_latency = 0; tty->winsize.ws_row = tp->view.rows - 2; tty->winsize.ws_col = tp->view.cols; @@ -974,6 +1014,7 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) raw3270_activate_view(&tp->view); +port_install: rc = tty_port_install(&tp->port, driver, tty); if (rc) { raw3270_put_view(&tp->view); @@ -1010,18 +1051,18 @@ tty3270_close(struct tty_struct *tty, struct file * filp) if (tty->count > 1) return; - if (tp) { - tty->driver_data = NULL; + if (tp) tty_port_tty_set(&tp->port, NULL); - } } static void tty3270_cleanup(struct tty_struct *tty) { struct tty3270 *tp = tty->driver_data; - if (tp) + if (tp) { + tty->driver_data = NULL; raw3270_put_view(&tp->view); + } } /* @@ -1788,7 +1829,22 @@ tty3270_unthrottle(struct tty_struct * tty) static void tty3270_hangup(struct tty_struct *tty) { - // FIXME: implement + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return; + spin_lock_bh(&tp->view.lock); + tp->cx = tp->saved_cx = 0; + tp->cy = tp->saved_cy = 0; + tp->highlight = tp->saved_highlight = TAX_RESET; + tp->f_color = tp->saved_f_color = TAC_RESET; + tty3270_blank_screen(tp); + while (tp->nr_lines < tp->view.rows - 2) + tty3270_blank_line(tp); + tp->update_flags = TTY_UPDATE_ALL; + spin_unlock_bh(&tp->view.lock); + tty3270_set_timer(tp, 1); } static void |