Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
[cascardo/linux.git] / drivers / s390 / char / tty3270.c
index e96fc7f..0a9f219 100644 (file)
@@ -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. */
@@ -318,6 +319,27 @@ tty3270_blank_line(struct tty3270 *tp)
                tp->nr_up++;
 }
 
+/*
+ * 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.
  */
@@ -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;
@@ -621,6 +646,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.
  */
@@ -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