Commit 4d334fd1 authored by Martin Schwidefsky's avatar Martin Schwidefsky

s390/3270: asynchronous size sensing

Convert the synchronous size sense code to an interrupt driven
approach. This allows to set the device online even if the
terminal is not connected. With the new code views can be
registered without a connected terminal, the tty can be opened
as soon as the device is online. After the terminal has been
connected and the size has been determined the tty is resized
to match the device characteristics..
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent c95571e6
......@@ -37,6 +37,7 @@ struct raw3270 {
int minor;
short model, rows, cols;
unsigned int state;
unsigned long flags;
struct list_head req_queue; /* Request queue. */
......@@ -47,17 +48,25 @@ struct raw3270 {
unsigned char *ascebc; /* ascii -> ebcdic table */
struct raw3270_request init_request;
struct raw3270_view init_view;
struct raw3270_request init_reset;
struct raw3270_request init_readpart;
struct raw3270_request init_readmod;
unsigned char init_data[256];
};
/* raw3270->state */
#define RAW3270_STATE_INIT 0 /* Initial state */
#define RAW3270_STATE_RESET 1 /* Reset command is pending */
#define RAW3270_STATE_W4ATTN 2 /* Wait for attention interrupt */
#define RAW3270_STATE_READMOD 3 /* Read partition is pending */
#define RAW3270_STATE_READY 4 /* Device is usable by views */
/* 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. */
#define RAW3270_FLAGS_FROZEN 16 /* set if 3270 is frozen for suspend */
#define RAW3270_FLAGS_CONSOLE 2 /* Device is the console. */
#define RAW3270_FLAGS_FROZEN 3 /* set if 3270 is frozen for suspend */
/* Semaphore to protect global data of raw3270 (devices, views, etc). */
static DEFINE_MUTEX(raw3270_mutex);
......@@ -95,6 +104,17 @@ static unsigned char raw3270_ebcgraf[64] = {
0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
};
static inline int raw3270_state_ready(struct raw3270 *rp)
{
return rp->state == RAW3270_STATE_READY;
}
static inline int raw3270_state_final(struct raw3270 *rp)
{
return rp->state == RAW3270_STATE_INIT ||
rp->state == RAW3270_STATE_READY;
}
void
raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr)
{
......@@ -212,7 +232,7 @@ raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib)
* Stop running ccw.
*/
static int
raw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq)
__raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq)
{
int retries;
int rc;
......@@ -231,18 +251,6 @@ raw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq)
return rc;
}
static int
raw3270_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.
......@@ -279,8 +287,8 @@ raw3270_start(struct raw3270_view *view, struct raw3270_request *rq)
if (!rp || rp->view != view ||
test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))
rc = -EACCES;
else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
rc = -ENODEV;
else if (!raw3270_state_ready(rp))
rc = -EBUSY;
else
rc = __raw3270_start(rp, view, rq);
spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags);
......@@ -297,8 +305,8 @@ raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq)
if (!rp || rp->view != view ||
test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))
rc = -EACCES;
else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
rc = -ENODEV;
else if (!raw3270_state_ready(rp))
rc = -EBUSY;
else
rc = __raw3270_start(rp, view, rq);
return rc;
......@@ -376,7 +384,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
case RAW3270_IO_STOP:
if (!rq)
break;
raw3270_halt_io_nolock(rp, rq);
__raw3270_halt_io(rp, rq);
rq->rc = -EIO;
break;
default:
......@@ -411,9 +419,14 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
}
/*
* Size sensing.
* To determine the size of the 3270 device we need to do:
* 1) send a 'read partition' data stream to the device
* 2) wait for the attn interrupt that precedes the query reply
* 3) do a read modified to get the query reply
* To make things worse we have to cope with intervention
* required (3270 device switched to 'stand-by') and command
* rejects (old devices that can't do 'read partition').
*/
struct raw3270_ua { /* Query Reply structure for Usable Area */
struct { /* Usable Area Query Reply Base */
short l; /* Length of this structured field */
......@@ -449,117 +462,21 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */
} __attribute__ ((packed)) aua;
} __attribute__ ((packed));
static struct diag210 raw3270_init_diag210;
static DEFINE_MUTEX(raw3270_init_mutex);
static int
raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq,
struct irb *irb)
{
/*
* 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)
rq->rc = -EOPNOTSUPP;
else
rq->rc = -EIO;
} else
/* Request finished normally. Copy residual count. */
rq->rescnt = irb->scsw.cmd.count;
}
if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
set_bit(RAW3270_FLAGS_ATTN, &view->dev->flags);
wake_up(&raw3270_wait_queue);
}
return RAW3270_IO_DONE;
}
static struct raw3270_fn raw3270_init_fn = {
.intv = raw3270_init_irq
};
static struct raw3270_view raw3270_init_view = {
.fn = &raw3270_init_fn
};
/*
* raw3270_wait/raw3270_wait_interruptible/__raw3270_wakeup
* Wait for end of request. The request must have been started
* with raw3270_start, rc = 0. The device lock may NOT have been
* released between calling raw3270_start and raw3270_wait.
*/
static void
raw3270_wake_init(struct raw3270_request *rq, void *data)
{
wake_up((wait_queue_head_t *) data);
}
/*
* Special wait function that can cope with console initialization.
*/
static int
raw3270_start_init(struct raw3270 *rp, struct raw3270_view *view,
struct raw3270_request *rq)
{
unsigned long flags;
int rc;
#ifdef CONFIG_TN3270_CONSOLE
if (raw3270_registered == 0) {
spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags);
rq->callback = NULL;
rc = __raw3270_start(rp, view, rq);
if (rc == 0)
while (!raw3270_request_final(rq)) {
wait_cons_dev();
barrier();
}
spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags);
return rq->rc;
}
#endif
rq->callback = raw3270_wake_init;
rq->callback_data = &raw3270_wait_queue;
spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags);
rc = __raw3270_start(rp, view, rq);
spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags);
if (rc)
return rc;
/* Now wait for the completion. */
rc = wait_event_interruptible(raw3270_wait_queue,
raw3270_request_final(rq));
if (rc == -ERESTARTSYS) { /* Interrupted by a signal. */
raw3270_halt_io(view->dev, rq);
/* No wait for the halt to complete. */
wait_event(raw3270_wait_queue, raw3270_request_final(rq));
return -ERESTARTSYS;
}
return rq->rc;
}
static int
__raw3270_size_device_vm(struct raw3270 *rp)
raw3270_size_device_vm(struct raw3270 *rp)
{
int rc, model;
struct ccw_dev_id dev_id;
struct diag210 diag_data;
ccw_device_get_id(rp->cdev, &dev_id);
raw3270_init_diag210.vrdcdvno = dev_id.devno;
raw3270_init_diag210.vrdclen = sizeof(struct diag210);
rc = diag210(&raw3270_init_diag210);
if (rc)
return rc;
model = raw3270_init_diag210.vrdccrmd;
diag_data.vrdcdvno = dev_id.devno;
diag_data.vrdclen = sizeof(struct diag210);
rc = diag210(&diag_data);
model = diag_data.vrdccrmd;
/* Use default model 2 if the size could not be detected */
if (rc || model < 2 || model > 5)
model = 2;
switch (model) {
case 2:
rp->model = model;
......@@ -581,77 +498,25 @@ __raw3270_size_device_vm(struct raw3270 *rp)
rp->rows = 27;
rp->cols = 132;
break;
default:
rc = -EOPNOTSUPP;
break;
}
return rc;
}
static int
__raw3270_size_device(struct raw3270 *rp)
static void
raw3270_size_device(struct raw3270 *rp)
{
static const unsigned char wbuf[] =
{ 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 };
struct raw3270_ua *uap;
int rc;
/*
* To determine the size of the 3270 device we need to do:
* 1) send a 'read partition' data stream to the device
* 2) wait for the attn interrupt that precedes the query reply
* 3) do a read modified to get the query reply
* To make things worse we have to cope with intervention
* required (3270 device switched to 'stand-by') and command
* rejects (old devices that can't do 'read partition').
*/
memset(&rp->init_request, 0, sizeof(rp->init_request));
memset(&rp->init_data, 0, 256);
/* Store 'read partition' data stream to init_data */
memcpy(&rp->init_data, wbuf, sizeof(wbuf));
INIT_LIST_HEAD(&rp->init_request.list);
rp->init_request.ccw.cmd_code = TC_WRITESF;
rp->init_request.ccw.flags = CCW_FLAG_SLI;
rp->init_request.ccw.count = sizeof(wbuf);
rp->init_request.ccw.cda = (__u32) __pa(&rp->init_data);
rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request);
if (rc)
/* Check error cases: -ERESTARTSYS, -EIO and -EOPNOTSUPP */
return rc;
/* Wait for attention interrupt. */
#ifdef CONFIG_TN3270_CONSOLE
if (raw3270_registered == 0) {
unsigned long flags;
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
while (!test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags))
wait_cons_dev();
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
} else
#endif
rc = wait_event_interruptible(raw3270_wait_queue,
test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags));
if (rc)
return rc;
/*
* The device accepted the 'read partition' command. Now
* set up a read ccw and issue it.
*/
rp->init_request.ccw.cmd_code = TC_READMOD;
rp->init_request.ccw.flags = CCW_FLAG_SLI;
rp->init_request.ccw.count = sizeof(rp->init_data);
rp->init_request.ccw.cda = (__u32) __pa(rp->init_data);
rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request);
if (rc)
return rc;
/* Got a Query Reply */
uap = (struct raw3270_ua *) (rp->init_data + 1);
/* Paranoia check. */
if (rp->init_data[0] != 0x88 || uap->uab.qcode != 0x81)
return -EOPNOTSUPP;
if (rp->init_readmod.rc || rp->init_data[0] != 0x88 ||
uap->uab.qcode != 0x81) {
/* Couldn't detect size. Use default model 2. */
rp->model = 2;
rp->rows = 24;
rp->cols = 80;
return;
}
/* Copy rows/columns of default Usable Area */
rp->rows = uap->uab.h;
rp->cols = uap->uab.w;
......@@ -664,66 +529,131 @@ __raw3270_size_device(struct raw3270 *rp)
rp->rows = uap->aua.hauai;
rp->cols = uap->aua.wauai;
}
return 0;
/* Try to find a model. */
rp->model = 0;
if (rp->rows == 24 && rp->cols == 80)
rp->model = 2;
if (rp->rows == 32 && rp->cols == 80)
rp->model = 3;
if (rp->rows == 43 && rp->cols == 80)
rp->model = 4;
if (rp->rows == 27 && rp->cols == 132)
rp->model = 5;
}
static int
raw3270_size_device(struct raw3270 *rp)
static void
raw3270_size_device_done(struct raw3270 *rp)
{
int rc;
struct raw3270_view *view;
mutex_lock(&raw3270_init_mutex);
rp->view = &raw3270_init_view;
raw3270_init_view.dev = rp;
if (MACHINE_IS_VM)
rc = __raw3270_size_device_vm(rp);
else
rc = __raw3270_size_device(rp);
raw3270_init_view.dev = NULL;
rp->view = NULL;
mutex_unlock(&raw3270_init_mutex);
if (rc == 0) { /* Found something. */
/* Try to find a model. */
rp->model = 0;
if (rp->rows == 24 && rp->cols == 80)
rp->model = 2;
if (rp->rows == 32 && rp->cols == 80)
rp->model = 3;
if (rp->rows == 43 && rp->cols == 80)
rp->model = 4;
if (rp->rows == 27 && rp->cols == 132)
rp->model = 5;
} else {
/* Couldn't detect size. Use default model 2. */
rp->model = 2;
rp->rows = 24;
rp->cols = 80;
return 0;
rp->state = RAW3270_STATE_READY;
/* Notify views about new size */
list_for_each_entry(view, &rp->view_list, list)
if (view->fn->resize)
view->fn->resize(view, rp->model, rp->rows, rp->cols);
/* Setup processing done, now activate a view */
list_for_each_entry(view, &rp->view_list, list) {
rp->view = view;
if (view->fn->activate(view) == 0)
break;
rp->view = NULL;
}
return rc;
}
static void
raw3270_read_modified_cb(struct raw3270_request *rq, void *data)
{
struct raw3270 *rp = rq->view->dev;
raw3270_size_device(rp);
raw3270_size_device_done(rp);
}
static void
raw3270_read_modified(struct raw3270 *rp)
{
if (rp->state != RAW3270_STATE_W4ATTN)
return;
/* Use 'read modified' to get the result of a read partition. */
memset(&rp->init_readmod, 0, sizeof(rp->init_readmod));
memset(&rp->init_data, 0, sizeof(rp->init_data));
rp->init_readmod.ccw.cmd_code = TC_READMOD;
rp->init_readmod.ccw.flags = CCW_FLAG_SLI;
rp->init_readmod.ccw.count = sizeof(rp->init_data);
rp->init_readmod.ccw.cda = (__u32) __pa(rp->init_data);
rp->init_readmod.callback = raw3270_read_modified_cb;
rp->state = RAW3270_STATE_READMOD;
raw3270_start_irq(&rp->init_view, &rp->init_readmod);
}
static void
raw3270_writesf_readpart(struct raw3270 *rp)
{
static const unsigned char wbuf[] =
{ 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 };
/* Store 'read partition' data stream to init_data */
memset(&rp->init_readpart, 0, sizeof(rp->init_readpart));
memset(&rp->init_data, 0, sizeof(rp->init_data));
memcpy(&rp->init_data, wbuf, sizeof(wbuf));
rp->init_readpart.ccw.cmd_code = TC_WRITESF;
rp->init_readpart.ccw.flags = CCW_FLAG_SLI;
rp->init_readpart.ccw.count = sizeof(wbuf);
rp->init_readpart.ccw.cda = (__u32) __pa(&rp->init_data);
rp->state = RAW3270_STATE_W4ATTN;
raw3270_start_irq(&rp->init_view, &rp->init_readpart);
}
/*
* Device reset
*/
static void
raw3270_reset_device_cb(struct raw3270_request *rq, void *data)
{
struct raw3270 *rp = rq->view->dev;
if (rp->state != RAW3270_STATE_RESET)
return;
if (rq && rq->rc) {
/* Reset command failed. */
rp->state = RAW3270_STATE_INIT;
} else if (0 && MACHINE_IS_VM) {
raw3270_size_device_vm(rp);
raw3270_size_device_done(rp);
} else
raw3270_writesf_readpart(rp);
}
static int
raw3270_reset_device(struct raw3270 *rp)
__raw3270_reset_device(struct raw3270 *rp)
{
int rc;
mutex_lock(&raw3270_init_mutex);
memset(&rp->init_request, 0, sizeof(rp->init_request));
/* Store reset data stream to init_data/init_reset */
memset(&rp->init_reset, 0, sizeof(rp->init_reset));
memset(&rp->init_data, 0, sizeof(rp->init_data));
/* Store reset data stream to init_data/init_request */
rp->init_data[0] = TW_KR;
INIT_LIST_HEAD(&rp->init_request.list);
rp->init_request.ccw.cmd_code = TC_EWRITEA;
rp->init_request.ccw.flags = CCW_FLAG_SLI;
rp->init_request.ccw.count = 1;
rp->init_request.ccw.cda = (__u32) __pa(rp->init_data);
rp->view = &raw3270_init_view;
raw3270_init_view.dev = rp;
rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request);
raw3270_init_view.dev = NULL;
rp->view = NULL;
mutex_unlock(&raw3270_init_mutex);
rp->init_reset.ccw.cmd_code = TC_EWRITEA;
rp->init_reset.ccw.flags = CCW_FLAG_SLI;
rp->init_reset.ccw.count = 1;
rp->init_reset.ccw.cda = (__u32) __pa(rp->init_data);
rp->init_reset.callback = raw3270_reset_device_cb;
rc = __raw3270_start(rp, &rp->init_view, &rp->init_reset);
if (rc == 0 && rp->state == RAW3270_STATE_INIT)
rp->state = RAW3270_STATE_RESET;
return rc;
}
static int
raw3270_reset_device(struct raw3270 *rp)
{
unsigned long flags;
int rc;
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
rc = __raw3270_reset_device(rp);
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
return rc;
}
......@@ -737,13 +667,50 @@ raw3270_reset(struct raw3270_view *view)
if (!rp || rp->view != view ||
test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))
rc = -EACCES;
else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
rc = -ENODEV;
else if (!raw3270_state_ready(rp))
rc = -EBUSY;
else
rc = raw3270_reset_device(view->dev);
return rc;
}
static int
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)
rq->rc = -EOPNOTSUPP;
else
rq->rc = -EIO;
}
}
if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
/* Queue read modified after attention interrupt */
rp = view->dev;
raw3270_read_modified(rp);
}
return RAW3270_IO_DONE;
}
static struct raw3270_fn raw3270_init_fn = {
.intv = raw3270_init_irq
};
/*
* Setup new 3270 device.
*/
......@@ -772,6 +739,10 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc)
INIT_LIST_HEAD(&rp->req_queue);
INIT_LIST_HEAD(&rp->view_list);
rp->init_view.dev = rp;
rp->init_view.fn = &raw3270_init_fn;
rp->view = &rp->init_view;
/*
* Add device to list and find the smallest unused minor
* number for it. Note: there is no device with minor 0,
......@@ -810,6 +781,7 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc)
*/
struct raw3270 __init *raw3270_setup_console(struct ccw_device *cdev)
{
unsigned long flags;
struct raw3270 *rp;
char *ascebc;
int rc;
......@@ -820,16 +792,15 @@ struct raw3270 __init *raw3270_setup_console(struct ccw_device *cdev)
if (rc)
return ERR_PTR(rc);
set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags);
rc = raw3270_reset_device(rp);
if (rc)
return ERR_PTR(rc);
rc = raw3270_size_device(rp);
if (rc)
return ERR_PTR(rc);
rc = raw3270_reset_device(rp);
if (rc)
return ERR_PTR(rc);
set_bit(RAW3270_FLAGS_READY, &rp->flags);
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
do {
__raw3270_reset_device(rp);
while (!raw3270_state_final(rp)) {
wait_cons_dev();
barrier();
}
} while (rp->state != RAW3270_STATE_READY);
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
return rp;
}
......@@ -891,13 +862,13 @@ raw3270_activate_view(struct raw3270_view *view)
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
if (rp->view == view)
rc = 0;
else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
rc = -ENODEV;
else if (!raw3270_state_ready(rp))
rc = -EBUSY;
else if (test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))
rc = -EACCES;
else {
oldview = NULL;
if (rp->view) {
if (rp->view && rp->view->fn->deactivate) {
oldview = rp->view;
oldview->fn->deactivate(oldview);
}
......@@ -942,7 +913,7 @@ raw3270_deactivate_view(struct raw3270_view *view)
list_del_init(&view->list);
list_add_tail(&view->list, &rp->view_list);
/* Try to activate another view. */
if (test_bit(RAW3270_FLAGS_READY, &rp->flags) &&
if (raw3270_state_ready(rp) &&
!test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) {
list_for_each_entry(view, &rp->view_list, list) {
rp->view = view;
......@@ -973,18 +944,16 @@ raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor)
if (rp->minor != minor)
continue;
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
atomic_set(&view->ref_count, 2);
view->dev = rp;
view->fn = fn;
view->model = rp->model;
view->rows = rp->rows;
view->cols = rp->cols;
view->ascebc = rp->ascebc;
spin_lock_init(&view->lock);
list_add(&view->list, &rp->view_list);
rc = 0;
}
atomic_set(&view->ref_count, 2);
view->dev = rp;
view->fn = fn;
view->model = rp->model;
view->rows = rp->rows;
view->cols = rp->cols;
view->ascebc = rp->ascebc;
spin_lock_init(&view->lock);
list_add(&view->list, &rp->view_list);
rc = 0;
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
break;
}
......@@ -1008,14 +977,11 @@ raw3270_find_view(struct raw3270_fn *fn, int minor)
if (rp->minor != minor)
continue;
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
view = ERR_PTR(-ENOENT);
list_for_each_entry(tmp, &rp->view_list, list) {
if (tmp->fn == fn) {
raw3270_get_view(tmp);
view = tmp;
break;
}
list_for_each_entry(tmp, &rp->view_list, list) {
if (tmp->fn == fn) {
raw3270_get_view(tmp);
view = tmp;
break;
}
}
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
......@@ -1042,7 +1008,7 @@ raw3270_del_view(struct raw3270_view *view)
rp->view = NULL;
}
list_del_init(&view->list);
if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags) &&
if (!rp->view && raw3270_state_ready(rp) &&
!test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) {
/* Try to activate another view. */
list_for_each_entry(nv, &rp->view_list, list) {
......@@ -1177,19 +1143,10 @@ raw3270_set_online (struct ccw_device *cdev)
rp = raw3270_create_device(cdev);
if (IS_ERR(rp))
return PTR_ERR(rp);
rc = raw3270_reset_device(rp);
if (rc)
goto failure;
rc = raw3270_size_device(rp);
if (rc)
goto failure;
rc = raw3270_reset_device(rp);
if (rc)
goto failure;
rc = raw3270_create_attributes(rp);
if (rc)
goto failure;
set_bit(RAW3270_FLAGS_READY, &rp->flags);
raw3270_reset_device(rp);
mutex_lock(&raw3270_mutex);
list_for_each_entry(np, &raw3270_notifier, list)
np->create(rp->minor);
......@@ -1221,14 +1178,14 @@ raw3270_remove (struct ccw_device *cdev)
*/
if (rp == NULL)
return;
clear_bit(RAW3270_FLAGS_READY, &rp->flags);
sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group);
/* Deactivate current view and remove all views. */
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
if (rp->view) {
rp->view->fn->deactivate(rp->view);
if (rp->view->fn->deactivate)
rp->view->fn->deactivate(rp->view);
rp->view = NULL;
}
while (!list_empty(&rp->view_list)) {
......@@ -1277,7 +1234,7 @@ static int raw3270_pm_stop(struct ccw_device *cdev)
if (!rp)
return 0;
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
if (rp->view)
if (rp->view && rp->view->fn->deactivate)
rp->view->fn->deactivate(rp->view);
if (!test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags)) {
/*
......@@ -1304,7 +1261,7 @@ static int raw3270_pm_start(struct ccw_device *cdev)
return 0;
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
clear_bit(RAW3270_FLAGS_FROZEN, &rp->flags);
if (rp->view)
if (rp->view && rp->view->fn->activate)
rp->view->fn->activate(rp->view);
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
return 0;
......
......@@ -141,6 +141,7 @@ struct raw3270_fn {
struct raw3270_request *, struct irb *);
void (*release)(struct raw3270_view *);
void (*free)(struct raw3270_view *);
void (*resize)(struct raw3270_view *, int, int, int);
};
/*
......
......@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/console.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/bootmem.h>
......@@ -80,6 +81,8 @@ struct tty3270 {
unsigned int highlight; /* Blink/reverse/underscore */
unsigned int f_color; /* Foreground color */
struct tty3270_line *screen;
unsigned int n_model, n_cols, n_rows; /* New model & size */
struct work_struct resize_work;
/* Input stuff. */
struct string *prompt; /* Output string for input area. */
......@@ -115,6 +118,7 @@ struct tty3270 {
#define TTY_UPDATE_ALL 16 /* Recreate screen. */
static void tty3270_update(struct tty3270 *);
static void tty3270_resize_work(struct work_struct *work);
/*
* Setup timeout for a device. On timeout trigger an update.
......@@ -711,6 +715,7 @@ tty3270_alloc_view(void)
tasklet_init(&tp->readlet,
(void (*)(unsigned long)) tty3270_read_tasklet,
(unsigned long) tp->read);
INIT_WORK(&tp->resize_work, tty3270_resize_work);
return tp;
......@@ -754,42 +759,96 @@ tty3270_free_view(struct tty3270 *tp)
/*
* Allocate tty3270 screen.
*/
static int
tty3270_alloc_screen(struct tty3270 *tp)
static struct tty3270_line *
tty3270_alloc_screen(unsigned int rows, unsigned int cols)
{
struct tty3270_line *screen;
unsigned long size;
int lines;
size = sizeof(struct tty3270_line) * (tp->view.rows - 2);
tp->screen = kzalloc(size, GFP_KERNEL);
if (!tp->screen)
size = sizeof(struct tty3270_line) * (rows - 2);
screen = kzalloc(size, GFP_KERNEL);
if (!screen)
goto out_err;
for (lines = 0; lines < tp->view.rows - 2; lines++) {
size = sizeof(struct tty3270_cell) * tp->view.cols;
tp->screen[lines].cells = kzalloc(size, GFP_KERNEL);
if (!tp->screen[lines].cells)
for (lines = 0; lines < rows - 2; lines++) {
size = sizeof(struct tty3270_cell) * cols;
screen[lines].cells = kzalloc(size, GFP_KERNEL);
if (!screen[lines].cells)
goto out_screen;
}
return 0;
return screen;
out_screen:
while (lines--)
kfree(tp->screen[lines].cells);
kfree(tp->screen);
kfree(screen[lines].cells);
kfree(screen);
out_err:
return -ENOMEM;
return ERR_PTR(-ENOMEM);
}
/*
* Free tty3270 screen.
*/
static void
tty3270_free_screen(struct tty3270 *tp)
tty3270_free_screen(struct tty3270_line *screen, unsigned int rows)
{
int lines;
for (lines = 0; lines < tp->view.rows - 2; lines++)
kfree(tp->screen[lines].cells);
kfree(tp->screen);
for (lines = 0; lines < rows - 2; lines++)
kfree(screen[lines].cells);
kfree(screen);
}
/*
* Resize tty3270 screen
*/
static void tty3270_resize_work(struct work_struct *work)
{
struct tty3270 *tp = container_of(work, struct tty3270, resize_work);
struct tty3270_line *screen, *oscreen;
struct tty_struct *tty;
unsigned int orows;
struct winsize ws;
screen = tty3270_alloc_screen(tp->n_rows, tp->n_cols);
if (!screen)
return;
/* Switch to new output size */
spin_lock_bh(&tp->view.lock);
oscreen = tp->screen;
orows = tp->view.rows;
tp->view.model = tp->n_model;
tp->view.rows = tp->n_rows;
tp->view.cols = tp->n_cols;
tp->screen = screen;
free_string(&tp->freemem, tp->prompt);
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;
spin_unlock_bh(&tp->view.lock);
tty3270_free_screen(oscreen, orows);
tty3270_set_timer(tp, 1);
/* Informat tty layer about new size */
tty = tty_port_tty_get(&tp->port);
if (!tty)
return;
ws.ws_row = tp->view.rows - 2;
ws.ws_col = tp->view.cols;
tty_do_resize(tty, &ws);
}
static void
tty3270_resize(struct raw3270_view *view, int model, int rows, int cols)
{
struct tty3270 *tp = container_of(view, struct tty3270, view);
tp->n_model = model;
tp->n_rows = rows;
tp->n_cols = cols;
schedule_work(&tp->resize_work);
}
/*
......@@ -817,7 +876,8 @@ static void
tty3270_free(struct raw3270_view *view)
{
struct tty3270 *tp = container_of(view, struct tty3270, view);
tty3270_free_screen(tp);
tty3270_free_screen(tp->screen, tp->view.rows);
tty3270_free_view(tp);
}
......@@ -841,7 +901,8 @@ static struct raw3270_fn tty3270_fn = {
.deactivate = tty3270_deactivate,
.intv = (void *) tty3270_irq,
.release = tty3270_release,
.free = tty3270_free
.free = tty3270_free,
.resize = tty3270_resize
};
/*
......@@ -869,10 +930,6 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty)
if (tty3270_max_index < tty->index)
tty3270_max_index = tty->index;
/* Quick exit if there is no device for tty->index. */
if (PTR_ERR(view) == -ENODEV)
return -ENODEV;
/* Allocate tty3270 structure on first open. */
tp = tty3270_alloc_view();
if (IS_ERR(tp))
......@@ -884,10 +941,12 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty)
return rc;
}
rc = tty3270_alloc_screen(tp);
if (rc) {
tp->screen = tty3270_alloc_screen(tp->view.cols, tp->view.rows);
if (IS_ERR(tp->screen)) {
rc = PTR_ERR(tp->screen);
raw3270_put_view(&tp->view);
raw3270_del_view(&tp->view);
tty3270_free_view(tp);
return rc;
}
......
......@@ -2203,6 +2203,7 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
mutex_unlock(&tty->termios_mutex);
return 0;
}
EXPORT_SYMBOL(tty_do_resize);
/**
* tiocswinsz - implement window size set ioctl
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment