linux/drivers/s390/char/tty3270.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *    IBM/3270 Driver - tty functions.
   4 *
   5 *  Author(s):
   6 *    Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
   7 *    Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
   8 *      -- Copyright IBM Corp. 2003
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/types.h>
  13#include <linux/kdev_t.h>
  14#include <linux/tty.h>
  15#include <linux/vt_kern.h>
  16#include <linux/init.h>
  17#include <linux/console.h>
  18#include <linux/interrupt.h>
  19#include <linux/workqueue.h>
  20
  21#include <linux/slab.h>
  22#include <linux/memblock.h>
  23#include <linux/compat.h>
  24
  25#include <asm/ccwdev.h>
  26#include <asm/cio.h>
  27#include <asm/ebcdic.h>
  28#include <linux/uaccess.h>
  29
  30#include "raw3270.h"
  31#include "tty3270.h"
  32#include "keyboard.h"
  33
  34#define TTY3270_CHAR_BUF_SIZE 256
  35#define TTY3270_OUTPUT_BUFFER_SIZE 1024
  36#define TTY3270_STRING_PAGES 5
  37
  38struct tty_driver *tty3270_driver;
  39static int tty3270_max_index;
  40
  41static struct raw3270_fn tty3270_fn;
  42
  43struct tty3270_cell {
  44        unsigned char character;
  45        unsigned char highlight;
  46        unsigned char f_color;
  47};
  48
  49struct tty3270_line {
  50        struct tty3270_cell *cells;
  51        int len;
  52};
  53
  54#define ESCAPE_NPAR 8
  55
  56/*
  57 * The main tty view data structure.
  58 * FIXME:
  59 * 1) describe line orientation & lines list concept against screen
  60 * 2) describe conversion of screen to lines
  61 * 3) describe line format.
  62 */
  63struct tty3270 {
  64        struct raw3270_view view;
  65        struct tty_port port;
  66        void **freemem_pages;           /* Array of pages used for freemem. */
  67        struct list_head freemem;       /* List of free memory for strings. */
  68
  69        /* Output stuff. */
  70        struct list_head lines;         /* List of lines. */
  71        struct list_head update;        /* List of lines to update. */
  72        unsigned char wcc;              /* Write control character. */
  73        int nr_lines;                   /* # lines in list. */
  74        int nr_up;                      /* # lines up in history. */
  75        unsigned long update_flags;     /* Update indication bits. */
  76        struct string *status;          /* Lower right of display. */
  77        struct raw3270_request *write;  /* Single write request. */
  78        struct timer_list timer;        /* Output delay timer. */
  79
  80        /* Current tty screen. */
  81        unsigned int cx, cy;            /* Current output position. */
  82        unsigned int highlight;         /* Blink/reverse/underscore */
  83        unsigned int f_color;           /* Foreground color */
  84        struct tty3270_line *screen;
  85        unsigned int n_model, n_cols, n_rows;   /* New model & size */
  86        struct work_struct resize_work;
  87
  88        /* Input stuff. */
  89        struct string *prompt;          /* Output string for input area. */
  90        struct string *input;           /* Input string for read request. */
  91        struct raw3270_request *read;   /* Single read request. */
  92        struct raw3270_request *kreset; /* Single keyboard reset request. */
  93        unsigned char inattr;           /* Visible/invisible input. */
  94        int throttle, attn;             /* tty throttle/unthrottle. */
  95        struct tasklet_struct readlet;  /* Tasklet to issue read request. */
  96        struct tasklet_struct hanglet;  /* Tasklet to hang up the tty. */
  97        struct kbd_data *kbd;           /* key_maps stuff. */
  98
  99        /* Escape sequence parsing. */
 100        int esc_state, esc_ques, esc_npar;
 101        int esc_par[ESCAPE_NPAR];
 102        unsigned int saved_cx, saved_cy;
 103        unsigned int saved_highlight, saved_f_color;
 104
 105        /* Command recalling. */
 106        struct list_head rcl_lines;     /* List of recallable lines. */
 107        struct list_head *rcl_walk;     /* Point in rcl_lines list. */
 108        int rcl_nr, rcl_max;            /* Number/max number of rcl_lines. */
 109
 110        /* Character array for put_char/flush_chars. */
 111        unsigned int char_count;
 112        char char_buf[TTY3270_CHAR_BUF_SIZE];
 113};
 114
 115/* tty3270->update_flags. See tty3270_update for details. */
 116#define TTY_UPDATE_ERASE        1       /* Use EWRITEA instead of WRITE. */
 117#define TTY_UPDATE_LIST         2       /* Update lines in tty3270->update. */
 118#define TTY_UPDATE_INPUT        4       /* Update input line. */
 119#define TTY_UPDATE_STATUS       8       /* Update status line. */
 120#define TTY_UPDATE_ALL          16      /* Recreate screen. */
 121
 122static void tty3270_update(struct timer_list *);
 123static void tty3270_resize_work(struct work_struct *work);
 124
 125/*
 126 * Setup timeout for a device. On timeout trigger an update.
 127 */
 128static void tty3270_set_timer(struct tty3270 *tp, int expires)
 129{
 130        mod_timer(&tp->timer, jiffies + expires);
 131}
 132
 133/*
 134 * The input line are the two last lines of the screen.
 135 */
 136static void
 137tty3270_update_prompt(struct tty3270 *tp, char *input, int count)
 138{
 139        struct string *line;
 140        unsigned int off;
 141
 142        line = tp->prompt;
 143        if (count != 0)
 144                line->string[5] = TF_INMDT;
 145        else
 146                line->string[5] = tp->inattr;
 147        if (count > tp->view.cols * 2 - 11)
 148                count = tp->view.cols * 2 - 11;
 149        memcpy(line->string + 6, input, count);
 150        line->string[6 + count] = TO_IC;
 151        /* Clear to end of input line. */
 152        if (count < tp->view.cols * 2 - 11) {
 153                line->string[7 + count] = TO_RA;
 154                line->string[10 + count] = 0;
 155                off = tp->view.cols * tp->view.rows - 9;
 156                raw3270_buffer_address(tp->view.dev, line->string+count+8, off);
 157                line->len = 11 + count;
 158        } else
 159                line->len = 7 + count;
 160        tp->update_flags |= TTY_UPDATE_INPUT;
 161}
 162
 163static void
 164tty3270_create_prompt(struct tty3270 *tp)
 165{
 166        static const unsigned char blueprint[] =
 167                { TO_SBA, 0, 0, 0x6e, TO_SF, TF_INPUT,
 168                  /* empty input string */
 169                  TO_IC, TO_RA, 0, 0, 0 };
 170        struct string *line;
 171        unsigned int offset;
 172
 173        line = alloc_string(&tp->freemem,
 174                            sizeof(blueprint) + tp->view.cols * 2 - 9);
 175        tp->prompt = line;
 176        tp->inattr = TF_INPUT;
 177        /* Copy blueprint to status line */
 178        memcpy(line->string, blueprint, sizeof(blueprint));
 179        line->len = sizeof(blueprint);
 180        /* Set output offsets. */
 181        offset = tp->view.cols * (tp->view.rows - 2);
 182        raw3270_buffer_address(tp->view.dev, line->string + 1, offset);
 183        offset = tp->view.cols * tp->view.rows - 9;
 184        raw3270_buffer_address(tp->view.dev, line->string + 8, offset);
 185
 186        /* Allocate input string for reading. */
 187        tp->input = alloc_string(&tp->freemem, tp->view.cols * 2 - 9 + 6);
 188}
 189
 190/*
 191 * The status line is the last line of the screen. It shows the string
 192 * "Running"/"Holding" in the lower right corner of the screen.
 193 */
 194static void
 195tty3270_update_status(struct tty3270 * tp)
 196{
 197        char *str;
 198
 199        str = (tp->nr_up != 0) ? "History" : "Running";
 200        memcpy(tp->status->string + 8, str, 7);
 201        codepage_convert(tp->view.ascebc, tp->status->string + 8, 7);
 202        tp->update_flags |= TTY_UPDATE_STATUS;
 203}
 204
 205static void
 206tty3270_create_status(struct tty3270 * tp)
 207{
 208        static const unsigned char blueprint[] =
 209                { TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, TAC_GREEN,
 210                  0, 0, 0, 0, 0, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR,
 211                  TAC_RESET };
 212        struct string *line;
 213        unsigned int offset;
 214
 215        line = alloc_string(&tp->freemem,sizeof(blueprint));
 216        tp->status = line;
 217        /* Copy blueprint to status line */
 218        memcpy(line->string, blueprint, sizeof(blueprint));
 219        /* Set address to start of status string (= last 9 characters). */
 220        offset = tp->view.cols * tp->view.rows - 9;
 221        raw3270_buffer_address(tp->view.dev, line->string + 1, offset);
 222}
 223
 224/*
 225 * Set output offsets to 3270 datastream fragment of a tty string.
 226 * (TO_SBA offset at the start and TO_RA offset at the end of the string)
 227 */
 228static void
 229tty3270_update_string(struct tty3270 *tp, struct string *line, int nr)
 230{
 231        unsigned char *cp;
 232
 233        raw3270_buffer_address(tp->view.dev, line->string + 1,
 234                               tp->view.cols * nr);
 235        cp = line->string + line->len - 4;
 236        if (*cp == TO_RA)
 237                raw3270_buffer_address(tp->view.dev, cp + 1,
 238                                       tp->view.cols * (nr + 1));
 239}
 240
 241/*
 242 * Rebuild update list to print all lines.
 243 */
 244static void
 245tty3270_rebuild_update(struct tty3270 *tp)
 246{
 247        struct string *s, *n;
 248        int line, nr_up;
 249
 250        /* 
 251         * Throw away update list and create a new one,
 252         * containing all lines that will fit on the screen.
 253         */
 254        list_for_each_entry_safe(s, n, &tp->update, update)
 255                list_del_init(&s->update);
 256        line = tp->view.rows - 3;
 257        nr_up = tp->nr_up;
 258        list_for_each_entry_reverse(s, &tp->lines, list) {
 259                if (nr_up > 0) {
 260                        nr_up--;
 261                        continue;
 262                }
 263                tty3270_update_string(tp, s, line);
 264                list_add(&s->update, &tp->update);
 265                if (--line < 0)
 266                        break;
 267        }
 268        tp->update_flags |= TTY_UPDATE_LIST;
 269}
 270
 271/*
 272 * Alloc string for size bytes. If there is not enough room in
 273 * freemem, free strings until there is room.
 274 */
 275static struct string *
 276tty3270_alloc_string(struct tty3270 *tp, size_t size)
 277{
 278        struct string *s, *n;
 279
 280        s = alloc_string(&tp->freemem, size);
 281        if (s)
 282                return s;
 283        list_for_each_entry_safe(s, n, &tp->lines, list) {
 284                BUG_ON(tp->nr_lines <= tp->view.rows - 2);
 285                list_del(&s->list);
 286                if (!list_empty(&s->update))
 287                        list_del(&s->update);
 288                tp->nr_lines--;
 289                if (free_string(&tp->freemem, s) >= size)
 290                        break;
 291        }
 292        s = alloc_string(&tp->freemem, size);
 293        BUG_ON(!s);
 294        if (tp->nr_up != 0 &&
 295            tp->nr_up + tp->view.rows - 2 >= tp->nr_lines) {
 296                tp->nr_up = tp->nr_lines - tp->view.rows + 2;
 297                tty3270_rebuild_update(tp);
 298                tty3270_update_status(tp);
 299        }
 300        return s;
 301}
 302
 303/*
 304 * Add an empty line to the list.
 305 */
 306static void
 307tty3270_blank_line(struct tty3270 *tp)
 308{
 309        static const unsigned char blueprint[] =
 310                { TO_SBA, 0, 0, TO_SA, TAT_EXTHI, TAX_RESET,
 311                  TO_SA, TAT_COLOR, TAC_RESET, TO_RA, 0, 0, 0 };
 312        struct string *s;
 313
 314        s = tty3270_alloc_string(tp, sizeof(blueprint));
 315        memcpy(s->string, blueprint, sizeof(blueprint));
 316        s->len = sizeof(blueprint);
 317        list_add_tail(&s->list, &tp->lines);
 318        tp->nr_lines++;
 319        if (tp->nr_up != 0)
 320                tp->nr_up++;
 321}
 322
 323/*
 324 * Create a blank screen and remove all lines from the history.
 325 */
 326static void
 327tty3270_blank_screen(struct tty3270 *tp)
 328{
 329        struct string *s, *n;
 330        int i;
 331
 332        for (i = 0; i < tp->view.rows - 2; i++)
 333                tp->screen[i].len = 0;
 334        tp->nr_up = 0;
 335        list_for_each_entry_safe(s, n, &tp->lines, list) {
 336                list_del(&s->list);
 337                if (!list_empty(&s->update))
 338                        list_del(&s->update);
 339                tp->nr_lines--;
 340                free_string(&tp->freemem, s);
 341        }
 342}
 343
 344/*
 345 * Write request completion callback.
 346 */
 347static void
 348tty3270_write_callback(struct raw3270_request *rq, void *data)
 349{
 350        struct tty3270 *tp = container_of(rq->view, struct tty3270, view);
 351
 352        if (rq->rc != 0) {
 353                /* Write wasn't successful. Refresh all. */
 354                tp->update_flags = TTY_UPDATE_ALL;
 355                tty3270_set_timer(tp, 1);
 356        }
 357        raw3270_request_reset(rq);
 358        xchg(&tp->write, rq);
 359}
 360
 361/*
 362 * Update 3270 display.
 363 */
 364static void
 365tty3270_update(struct timer_list *t)
 366{
 367        struct tty3270 *tp = from_timer(tp, t, timer);
 368        static char invalid_sba[2] = { 0xff, 0xff };
 369        struct raw3270_request *wrq;
 370        unsigned long updated;
 371        struct string *s, *n;
 372        char *sba, *str;
 373        int rc, len;
 374
 375        wrq = xchg(&tp->write, 0);
 376        if (!wrq) {
 377                tty3270_set_timer(tp, 1);
 378                return;
 379        }
 380
 381        spin_lock(&tp->view.lock);
 382        updated = 0;
 383        if (tp->update_flags & TTY_UPDATE_ALL) {
 384                tty3270_rebuild_update(tp);
 385                tty3270_update_status(tp);
 386                tp->update_flags = TTY_UPDATE_ERASE | TTY_UPDATE_LIST |
 387                        TTY_UPDATE_INPUT | TTY_UPDATE_STATUS;
 388        }
 389        if (tp->update_flags & TTY_UPDATE_ERASE) {
 390                /* Use erase write alternate to erase display. */
 391                raw3270_request_set_cmd(wrq, TC_EWRITEA);
 392                updated |= TTY_UPDATE_ERASE;
 393        } else
 394                raw3270_request_set_cmd(wrq, TC_WRITE);
 395
 396        raw3270_request_add_data(wrq, &tp->wcc, 1);
 397        tp->wcc = TW_NONE;
 398
 399        /*
 400         * Update status line.
 401         */
 402        if (tp->update_flags & TTY_UPDATE_STATUS)
 403                if (raw3270_request_add_data(wrq, tp->status->string,
 404                                             tp->status->len) == 0)
 405                        updated |= TTY_UPDATE_STATUS;
 406
 407        /*
 408         * Write input line.
 409         */
 410        if (tp->update_flags & TTY_UPDATE_INPUT)
 411                if (raw3270_request_add_data(wrq, tp->prompt->string,
 412                                             tp->prompt->len) == 0)
 413                        updated |= TTY_UPDATE_INPUT;
 414
 415        sba = invalid_sba;
 416        
 417        if (tp->update_flags & TTY_UPDATE_LIST) {
 418                /* Write strings in the update list to the screen. */
 419                list_for_each_entry_safe(s, n, &tp->update, update) {
 420                        str = s->string;
 421                        len = s->len;
 422                        /*
 423                         * Skip TO_SBA at the start of the string if the
 424                         * last output position matches the start address
 425                         * of this line.
 426                         */
 427                        if (s->string[1] == sba[0] && s->string[2] == sba[1]) {
 428                                str += 3;
 429                                len -= 3;
 430                        }
 431                        if (raw3270_request_add_data(wrq, str, len) != 0)
 432                                break;
 433                        list_del_init(&s->update);
 434                        if (s->string[s->len - 4] == TO_RA)
 435                                sba = s->string + s->len - 3;
 436                        else
 437                                sba = invalid_sba;
 438                }
 439                if (list_empty(&tp->update))
 440                        updated |= TTY_UPDATE_LIST;
 441        }
 442        wrq->callback = tty3270_write_callback;
 443        rc = raw3270_start(&tp->view, wrq);
 444        if (rc == 0) {
 445                tp->update_flags &= ~updated;
 446                if (tp->update_flags)
 447                        tty3270_set_timer(tp, 1);
 448        } else {
 449                raw3270_request_reset(wrq);
 450                xchg(&tp->write, wrq);
 451        }
 452        spin_unlock(&tp->view.lock);
 453}
 454
 455/*
 456 * Command recalling.
 457 */
 458static void
 459tty3270_rcl_add(struct tty3270 *tp, char *input, int len)
 460{
 461        struct string *s;
 462
 463        tp->rcl_walk = NULL;
 464        if (len <= 0)
 465                return;
 466        if (tp->rcl_nr >= tp->rcl_max) {
 467                s = list_entry(tp->rcl_lines.next, struct string, list);
 468                list_del(&s->list);
 469                free_string(&tp->freemem, s);
 470                tp->rcl_nr--;
 471        }
 472        s = tty3270_alloc_string(tp, len);
 473        memcpy(s->string, input, len);
 474        list_add_tail(&s->list, &tp->rcl_lines);
 475        tp->rcl_nr++;
 476}
 477
 478static void
 479tty3270_rcl_backward(struct kbd_data *kbd)
 480{
 481        struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
 482        struct string *s;
 483
 484        spin_lock_bh(&tp->view.lock);
 485        if (tp->inattr == TF_INPUT) {
 486                if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines)
 487                        tp->rcl_walk = tp->rcl_walk->prev;
 488                else if (!list_empty(&tp->rcl_lines))
 489                        tp->rcl_walk = tp->rcl_lines.prev;
 490                s = tp->rcl_walk ? 
 491                        list_entry(tp->rcl_walk, struct string, list) : NULL;
 492                if (tp->rcl_walk) {
 493                        s = list_entry(tp->rcl_walk, struct string, list);
 494                        tty3270_update_prompt(tp, s->string, s->len);
 495                } else
 496                        tty3270_update_prompt(tp, NULL, 0);
 497                tty3270_set_timer(tp, 1);
 498        }
 499        spin_unlock_bh(&tp->view.lock);
 500}
 501
 502/*
 503 * Deactivate tty view.
 504 */
 505static void
 506tty3270_exit_tty(struct kbd_data *kbd)
 507{
 508        struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
 509
 510        raw3270_deactivate_view(&tp->view);
 511}
 512
 513/*
 514 * Scroll forward in history.
 515 */
 516static void
 517tty3270_scroll_forward(struct kbd_data *kbd)
 518{
 519        struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
 520        int nr_up;
 521
 522        spin_lock_bh(&tp->view.lock);
 523        nr_up = tp->nr_up - tp->view.rows + 2;
 524        if (nr_up < 0)
 525                nr_up = 0;
 526        if (nr_up != tp->nr_up) {
 527                tp->nr_up = nr_up;
 528                tty3270_rebuild_update(tp);
 529                tty3270_update_status(tp);
 530                tty3270_set_timer(tp, 1);
 531        }
 532        spin_unlock_bh(&tp->view.lock);
 533}
 534
 535/*
 536 * Scroll backward in history.
 537 */
 538static void
 539tty3270_scroll_backward(struct kbd_data *kbd)
 540{
 541        struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
 542        int nr_up;
 543
 544        spin_lock_bh(&tp->view.lock);
 545        nr_up = tp->nr_up + tp->view.rows - 2;
 546        if (nr_up + tp->view.rows - 2 > tp->nr_lines)
 547                nr_up = tp->nr_lines - tp->view.rows + 2;
 548        if (nr_up != tp->nr_up) {
 549                tp->nr_up = nr_up;
 550                tty3270_rebuild_update(tp);
 551                tty3270_update_status(tp);
 552                tty3270_set_timer(tp, 1);
 553        }
 554        spin_unlock_bh(&tp->view.lock);
 555}
 556
 557/*
 558 * Pass input line to tty.
 559 */
 560static void
 561tty3270_read_tasklet(unsigned long data)
 562{
 563        struct raw3270_request *rrq = (struct raw3270_request *)data;
 564        static char kreset_data = TW_KR;
 565        struct tty3270 *tp = container_of(rrq->view, struct tty3270, view);
 566        char *input;
 567        int len;
 568
 569        spin_lock_bh(&tp->view.lock);
 570        /*
 571         * Two AID keys are special: For 0x7d (enter) the input line
 572         * has to be emitted to the tty and for 0x6d the screen
 573         * needs to be redrawn.
 574         */
 575        input = NULL;
 576        len = 0;
 577        if (tp->input->string[0] == 0x7d) {
 578                /* Enter: write input to tty. */
 579                input = tp->input->string + 6;
 580                len = tp->input->len - 6 - rrq->rescnt;
 581                if (tp->inattr != TF_INPUTN)
 582                        tty3270_rcl_add(tp, input, len);
 583                if (tp->nr_up > 0) {
 584                        tp->nr_up = 0;
 585                        tty3270_rebuild_update(tp);
 586                        tty3270_update_status(tp);
 587                }
 588                /* Clear input area. */
 589                tty3270_update_prompt(tp, NULL, 0);
 590                tty3270_set_timer(tp, 1);
 591        } else if (tp->input->string[0] == 0x6d) {
 592                /* Display has been cleared. Redraw. */
 593                tp->update_flags = TTY_UPDATE_ALL;
 594                tty3270_set_timer(tp, 1);
 595        }
 596        spin_unlock_bh(&tp->view.lock);
 597
 598        /* Start keyboard reset command. */
 599        raw3270_request_reset(tp->kreset);
 600        raw3270_request_set_cmd(tp->kreset, TC_WRITE);
 601        raw3270_request_add_data(tp->kreset, &kreset_data, 1);
 602        raw3270_start(&tp->view, tp->kreset);
 603
 604        while (len-- > 0)
 605                kbd_keycode(tp->kbd, *input++);
 606        /* Emit keycode for AID byte. */
 607        kbd_keycode(tp->kbd, 256 + tp->input->string[0]);
 608
 609        raw3270_request_reset(rrq);
 610        xchg(&tp->read, rrq);
 611        raw3270_put_view(&tp->view);
 612}
 613
 614/*
 615 * Read request completion callback.
 616 */
 617static void
 618tty3270_read_callback(struct raw3270_request *rq, void *data)
 619{
 620        struct tty3270 *tp = container_of(rq->view, struct tty3270, view);
 621        raw3270_get_view(rq->view);
 622        /* Schedule tasklet to pass input to tty. */
 623        tasklet_schedule(&tp->readlet);
 624}
 625
 626/*
 627 * Issue a read request. Call with device lock.
 628 */
 629static void
 630tty3270_issue_read(struct tty3270 *tp, int lock)
 631{
 632        struct raw3270_request *rrq;
 633        int rc;
 634
 635        rrq = xchg(&tp->read, 0);
 636        if (!rrq)
 637                /* Read already scheduled. */
 638                return;
 639        rrq->callback = tty3270_read_callback;
 640        rrq->callback_data = tp;
 641        raw3270_request_set_cmd(rrq, TC_READMOD);
 642        raw3270_request_set_data(rrq, tp->input->string, tp->input->len);
 643        /* Issue the read modified request. */
 644        if (lock) {
 645                rc = raw3270_start(&tp->view, rrq);
 646        } else
 647                rc = raw3270_start_irq(&tp->view, rrq);
 648        if (rc) {
 649                raw3270_request_reset(rrq);
 650                xchg(&tp->read, rrq);
 651        }
 652}
 653
 654/*
 655 * Hang up the tty
 656 */
 657static void
 658tty3270_hangup_tasklet(unsigned long data)
 659{
 660        struct tty3270 *tp = (struct tty3270 *)data;
 661        tty_port_tty_hangup(&tp->port, true);
 662        raw3270_put_view(&tp->view);
 663}
 664
 665/*
 666 * Switch to the tty view.
 667 */
 668static int
 669tty3270_activate(struct raw3270_view *view)
 670{
 671        struct tty3270 *tp = container_of(view, struct tty3270, view);
 672
 673        tp->update_flags = TTY_UPDATE_ALL;
 674        tty3270_set_timer(tp, 1);
 675        return 0;
 676}
 677
 678static void
 679tty3270_deactivate(struct raw3270_view *view)
 680{
 681        struct tty3270 *tp = container_of(view, struct tty3270, view);
 682
 683        del_timer(&tp->timer);
 684}
 685
 686static void
 687tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
 688{
 689        /* Handle ATTN. Schedule tasklet to read aid. */
 690        if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
 691                if (!tp->throttle)
 692                        tty3270_issue_read(tp, 0);
 693                else
 694                        tp->attn = 1;
 695        }
 696
 697        if (rq) {
 698                if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
 699                        rq->rc = -EIO;
 700                        raw3270_get_view(&tp->view);
 701                        tasklet_schedule(&tp->hanglet);
 702                } else {
 703                        /* Normal end. Copy residual count. */
 704                        rq->rescnt = irb->scsw.cmd.count;
 705                }
 706        } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
 707                /* Interrupt without an outstanding request -> update all */
 708                tp->update_flags = TTY_UPDATE_ALL;
 709                tty3270_set_timer(tp, 1);
 710        }
 711}
 712
 713/*
 714 * Allocate tty3270 structure.
 715 */
 716static struct tty3270 *
 717tty3270_alloc_view(void)
 718{
 719        struct tty3270 *tp;
 720        int pages;
 721
 722        tp = kzalloc(sizeof(struct tty3270), GFP_KERNEL);
 723        if (!tp)
 724                goto out_err;
 725        tp->freemem_pages =
 726                kmalloc_array(TTY3270_STRING_PAGES, sizeof(void *),
 727                              GFP_KERNEL);
 728        if (!tp->freemem_pages)
 729                goto out_tp;
 730        INIT_LIST_HEAD(&tp->freemem);
 731        INIT_LIST_HEAD(&tp->lines);
 732        INIT_LIST_HEAD(&tp->update);
 733        INIT_LIST_HEAD(&tp->rcl_lines);
 734        tp->rcl_max = 20;
 735
 736        for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) {
 737                tp->freemem_pages[pages] = (void *)
 738                        __get_free_pages(GFP_KERNEL|GFP_DMA, 0);
 739                if (!tp->freemem_pages[pages])
 740                        goto out_pages;
 741                add_string_memory(&tp->freemem,
 742                                  tp->freemem_pages[pages], PAGE_SIZE);
 743        }
 744        tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE);
 745        if (IS_ERR(tp->write))
 746                goto out_pages;
 747        tp->read = raw3270_request_alloc(0);
 748        if (IS_ERR(tp->read))
 749                goto out_write;
 750        tp->kreset = raw3270_request_alloc(1);
 751        if (IS_ERR(tp->kreset))
 752                goto out_read;
 753        tp->kbd = kbd_alloc();
 754        if (!tp->kbd)
 755                goto out_reset;
 756
 757        tty_port_init(&tp->port);
 758        timer_setup(&tp->timer, tty3270_update, 0);
 759        tasklet_init(&tp->readlet, tty3270_read_tasklet,
 760                     (unsigned long) tp->read);
 761        tasklet_init(&tp->hanglet, tty3270_hangup_tasklet,
 762                     (unsigned long) tp);
 763        INIT_WORK(&tp->resize_work, tty3270_resize_work);
 764
 765        return tp;
 766
 767out_reset:
 768        raw3270_request_free(tp->kreset);
 769out_read:
 770        raw3270_request_free(tp->read);
 771out_write:
 772        raw3270_request_free(tp->write);
 773out_pages:
 774        while (pages--)
 775                free_pages((unsigned long) tp->freemem_pages[pages], 0);
 776        kfree(tp->freemem_pages);
 777        tty_port_destroy(&tp->port);
 778out_tp:
 779        kfree(tp);
 780out_err:
 781        return ERR_PTR(-ENOMEM);
 782}
 783
 784/*
 785 * Free tty3270 structure.
 786 */
 787static void
 788tty3270_free_view(struct tty3270 *tp)
 789{
 790        int pages;
 791
 792        kbd_free(tp->kbd);
 793        raw3270_request_free(tp->kreset);
 794        raw3270_request_free(tp->read);
 795        raw3270_request_free(tp->write);
 796        for (pages = 0; pages < TTY3270_STRING_PAGES; pages++)
 797                free_pages((unsigned long) tp->freemem_pages[pages], 0);
 798        kfree(tp->freemem_pages);
 799        tty_port_destroy(&tp->port);
 800        kfree(tp);
 801}
 802
 803/*
 804 * Allocate tty3270 screen.
 805 */
 806static struct tty3270_line *
 807tty3270_alloc_screen(unsigned int rows, unsigned int cols)
 808{
 809        struct tty3270_line *screen;
 810        unsigned long size;
 811        int lines;
 812
 813        size = sizeof(struct tty3270_line) * (rows - 2);
 814        screen = kzalloc(size, GFP_KERNEL);
 815        if (!screen)
 816                goto out_err;
 817        for (lines = 0; lines < rows - 2; lines++) {
 818                size = sizeof(struct tty3270_cell) * cols;
 819                screen[lines].cells = kzalloc(size, GFP_KERNEL);
 820                if (!screen[lines].cells)
 821                        goto out_screen;
 822        }
 823        return screen;
 824out_screen:
 825        while (lines--)
 826                kfree(screen[lines].cells);
 827        kfree(screen);
 828out_err:
 829        return ERR_PTR(-ENOMEM);
 830}
 831
 832/*
 833 * Free tty3270 screen.
 834 */
 835static void
 836tty3270_free_screen(struct tty3270_line *screen, unsigned int rows)
 837{
 838        int lines;
 839
 840        for (lines = 0; lines < rows - 2; lines++)
 841                kfree(screen[lines].cells);
 842        kfree(screen);
 843}
 844
 845/*
 846 * Resize tty3270 screen
 847 */
 848static void tty3270_resize_work(struct work_struct *work)
 849{
 850        struct tty3270 *tp = container_of(work, struct tty3270, resize_work);
 851        struct tty3270_line *screen, *oscreen;
 852        struct tty_struct *tty;
 853        unsigned int orows;
 854        struct winsize ws;
 855
 856        screen = tty3270_alloc_screen(tp->n_rows, tp->n_cols);
 857        if (IS_ERR(screen))
 858                return;
 859        /* Switch to new output size */
 860        spin_lock_bh(&tp->view.lock);
 861        tty3270_blank_screen(tp);
 862        oscreen = tp->screen;
 863        orows = tp->view.rows;
 864        tp->view.model = tp->n_model;
 865        tp->view.rows = tp->n_rows;
 866        tp->view.cols = tp->n_cols;
 867        tp->screen = screen;
 868        free_string(&tp->freemem, tp->prompt);
 869        free_string(&tp->freemem, tp->status);
 870        tty3270_create_prompt(tp);
 871        tty3270_create_status(tp);
 872        while (tp->nr_lines < tp->view.rows - 2)
 873                tty3270_blank_line(tp);
 874        tp->update_flags = TTY_UPDATE_ALL;
 875        spin_unlock_bh(&tp->view.lock);
 876        tty3270_free_screen(oscreen, orows);
 877        tty3270_set_timer(tp, 1);
 878        /* Informat tty layer about new size */
 879        tty = tty_port_tty_get(&tp->port);
 880        if (!tty)
 881                return;
 882        ws.ws_row = tp->view.rows - 2;
 883        ws.ws_col = tp->view.cols;
 884        tty_do_resize(tty, &ws);
 885        tty_kref_put(tty);
 886}
 887
 888static void
 889tty3270_resize(struct raw3270_view *view, int model, int rows, int cols)
 890{
 891        struct tty3270 *tp = container_of(view, struct tty3270, view);
 892
 893        if (tp->n_model == model && tp->n_rows == rows && tp->n_cols == cols)
 894                return;
 895        tp->n_model = model;
 896        tp->n_rows = rows;
 897        tp->n_cols = cols;
 898        schedule_work(&tp->resize_work);
 899}
 900
 901/*
 902 * Unlink tty3270 data structure from tty.
 903 */
 904static void
 905tty3270_release(struct raw3270_view *view)
 906{
 907        struct tty3270 *tp = container_of(view, struct tty3270, view);
 908        struct tty_struct *tty = tty_port_tty_get(&tp->port);
 909
 910        if (tty) {
 911                tty->driver_data = NULL;
 912                tty_port_tty_set(&tp->port, NULL);
 913                tty_hangup(tty);
 914                raw3270_put_view(&tp->view);
 915                tty_kref_put(tty);
 916        }
 917}
 918
 919/*
 920 * Free tty3270 data structure
 921 */
 922static void
 923tty3270_free(struct raw3270_view *view)
 924{
 925        struct tty3270 *tp = container_of(view, struct tty3270, view);
 926
 927        del_timer_sync(&tp->timer);
 928        tty3270_free_screen(tp->screen, tp->view.rows);
 929        tty3270_free_view(tp);
 930}
 931
 932/*
 933 * Delayed freeing of tty3270 views.
 934 */
 935static void
 936tty3270_del_views(void)
 937{
 938        int i;
 939
 940        for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) {
 941                struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i);
 942                if (!IS_ERR(view))
 943                        raw3270_del_view(view);
 944        }
 945}
 946
 947static struct raw3270_fn tty3270_fn = {
 948        .activate = tty3270_activate,
 949        .deactivate = tty3270_deactivate,
 950        .intv = (void *) tty3270_irq,
 951        .release = tty3270_release,
 952        .free = tty3270_free,
 953        .resize = tty3270_resize
 954};
 955
 956/*
 957 * This routine is called whenever a 3270 tty is opened first time.
 958 */
 959static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty)
 960{
 961        struct raw3270_view *view;
 962        struct tty3270 *tp;
 963        int i, rc;
 964
 965        /* Check if the tty3270 is already there. */
 966        view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR);
 967        if (!IS_ERR(view)) {
 968                tp = container_of(view, struct tty3270, view);
 969                tty->driver_data = tp;
 970                tty->winsize.ws_row = tp->view.rows - 2;
 971                tty->winsize.ws_col = tp->view.cols;
 972                tp->inattr = TF_INPUT;
 973                goto port_install;
 974        }
 975        if (tty3270_max_index < tty->index + 1)
 976                tty3270_max_index = tty->index + 1;
 977
 978        /* Allocate tty3270 structure on first open. */
 979        tp = tty3270_alloc_view();
 980        if (IS_ERR(tp))
 981                return PTR_ERR(tp);
 982
 983        rc = raw3270_add_view(&tp->view, &tty3270_fn,
 984                              tty->index + RAW3270_FIRSTMINOR,
 985                              RAW3270_VIEW_LOCK_BH);
 986        if (rc) {
 987                tty3270_free_view(tp);
 988                return rc;
 989        }
 990
 991        tp->screen = tty3270_alloc_screen(tp->view.rows, tp->view.cols);
 992        if (IS_ERR(tp->screen)) {
 993                rc = PTR_ERR(tp->screen);
 994                raw3270_put_view(&tp->view);
 995                raw3270_del_view(&tp->view);
 996                tty3270_free_view(tp);
 997                return rc;
 998        }
 999
1000        tty->winsize.ws_row = tp->view.rows - 2;
1001        tty->winsize.ws_col = tp->view.cols;
1002
1003        tty3270_create_prompt(tp);
1004        tty3270_create_status(tp);
1005        tty3270_update_status(tp);
1006
1007        /* Create blank line for every line in the tty output area. */
1008        for (i = 0; i < tp->view.rows - 2; i++)
1009                tty3270_blank_line(tp);
1010
1011        tp->kbd->port = &tp->port;
1012        tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty;
1013        tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward;
1014        tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward;
1015        tp->kbd->fn_handler[KVAL(K_CONS)] = tty3270_rcl_backward;
1016        kbd_ascebc(tp->kbd, tp->view.ascebc);
1017
1018        raw3270_activate_view(&tp->view);
1019
1020port_install:
1021        rc = tty_port_install(&tp->port, driver, tty);
1022        if (rc) {
1023                raw3270_put_view(&tp->view);
1024                return rc;
1025        }
1026
1027        tty->driver_data = tp;
1028
1029        return 0;
1030}
1031
1032/*
1033 * This routine is called whenever a 3270 tty is opened.
1034 */
1035static int
1036tty3270_open(struct tty_struct *tty, struct file *filp)
1037{
1038        struct tty3270 *tp = tty->driver_data;
1039        struct tty_port *port = &tp->port;
1040
1041        port->count++;
1042        tty_port_tty_set(port, tty);
1043        return 0;
1044}
1045
1046/*
1047 * This routine is called when the 3270 tty is closed. We wait
1048 * for the remaining request to be completed. Then we clean up.
1049 */
1050static void
1051tty3270_close(struct tty_struct *tty, struct file * filp)
1052{
1053        struct tty3270 *tp = tty->driver_data;
1054
1055        if (tty->count > 1)
1056                return;
1057        if (tp)
1058                tty_port_tty_set(&tp->port, NULL);
1059}
1060
1061static void tty3270_cleanup(struct tty_struct *tty)
1062{
1063        struct tty3270 *tp = tty->driver_data;
1064
1065        if (tp) {
1066                tty->driver_data = NULL;
1067                raw3270_put_view(&tp->view);
1068        }
1069}
1070
1071/*
1072 * We always have room.
1073 */
1074static unsigned int
1075tty3270_write_room(struct tty_struct *tty)
1076{
1077        return INT_MAX;
1078}
1079
1080/*
1081 * Insert character into the screen at the current position with the
1082 * current color and highlight. This function does NOT do cursor movement.
1083 */
1084static void tty3270_put_character(struct tty3270 *tp, char ch)
1085{
1086        struct tty3270_line *line;
1087        struct tty3270_cell *cell;
1088
1089        line = tp->screen + tp->cy;
1090        if (line->len <= tp->cx) {
1091                while (line->len < tp->cx) {
1092                        cell = line->cells + line->len;
1093                        cell->character = tp->view.ascebc[' '];
1094                        cell->highlight = tp->highlight;
1095                        cell->f_color = tp->f_color;
1096                        line->len++;
1097                }
1098                line->len++;
1099        }
1100        cell = line->cells + tp->cx;
1101        cell->character = tp->view.ascebc[(unsigned int) ch];
1102        cell->highlight = tp->highlight;
1103        cell->f_color = tp->f_color;
1104}
1105
1106/*
1107 * Convert a tty3270_line to a 3270 data fragment usable for output.
1108 */
1109static void
1110tty3270_convert_line(struct tty3270 *tp, int line_nr)
1111{
1112        struct tty3270_line *line;
1113        struct tty3270_cell *cell;
1114        struct string *s, *n;
1115        unsigned char highlight;
1116        unsigned char f_color;
1117        char *cp;
1118        int flen, i;
1119
1120        /* Determine how long the fragment will be. */
1121        flen = 3;               /* Prefix (TO_SBA). */
1122        line = tp->screen + line_nr;
1123        flen += line->len;
1124        highlight = TAX_RESET;
1125        f_color = TAC_RESET;
1126        for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
1127                if (cell->highlight != highlight) {
1128                        flen += 3;      /* TO_SA to switch highlight. */
1129                        highlight = cell->highlight;
1130                }
1131                if (cell->f_color != f_color) {
1132                        flen += 3;      /* TO_SA to switch color. */
1133                        f_color = cell->f_color;
1134                }
1135        }
1136        if (highlight != TAX_RESET)
1137                flen += 3;      /* TO_SA to reset hightlight. */
1138        if (f_color != TAC_RESET)
1139                flen += 3;      /* TO_SA to reset color. */
1140        if (line->len < tp->view.cols)
1141                flen += 4;      /* Postfix (TO_RA). */
1142
1143        /* Find the line in the list. */
1144        i = tp->view.rows - 2 - line_nr;
1145        list_for_each_entry_reverse(s, &tp->lines, list)
1146                if (--i <= 0)
1147                        break;
1148        /*
1149         * Check if the line needs to get reallocated.
1150         */
1151        if (s->len != flen) {
1152                /* Reallocate string. */
1153                n = tty3270_alloc_string(tp, flen);
1154                list_add(&n->list, &s->list);
1155                list_del_init(&s->list);
1156                if (!list_empty(&s->update))
1157                        list_del_init(&s->update);
1158                free_string(&tp->freemem, s);
1159                s = n;
1160        }
1161
1162        /* Write 3270 data fragment. */
1163        cp = s->string;
1164        *cp++ = TO_SBA;
1165        *cp++ = 0;
1166        *cp++ = 0;
1167
1168        highlight = TAX_RESET;
1169        f_color = TAC_RESET;
1170        for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
1171                if (cell->highlight != highlight) {
1172                        *cp++ = TO_SA;
1173                        *cp++ = TAT_EXTHI;
1174                        *cp++ = cell->highlight;
1175                        highlight = cell->highlight;
1176                }
1177                if (cell->f_color != f_color) {
1178                        *cp++ = TO_SA;
1179                        *cp++ = TAT_COLOR;
1180                        *cp++ = cell->f_color;
1181                        f_color = cell->f_color;
1182                }
1183                *cp++ = cell->character;
1184        }
1185        if (highlight != TAX_RESET) {
1186                *cp++ = TO_SA;
1187                *cp++ = TAT_EXTHI;
1188                *cp++ = TAX_RESET;
1189        }
1190        if (f_color != TAC_RESET) {
1191                *cp++ = TO_SA;
1192                *cp++ = TAT_COLOR;
1193                *cp++ = TAC_RESET;
1194        }
1195        if (line->len < tp->view.cols) {
1196                *cp++ = TO_RA;
1197                *cp++ = 0;
1198                *cp++ = 0;
1199                *cp++ = 0;
1200        }
1201
1202        if (tp->nr_up + line_nr < tp->view.rows - 2) {
1203                /* Line is currently visible on screen. */
1204                tty3270_update_string(tp, s, line_nr);
1205                /* Add line to update list. */
1206                if (list_empty(&s->update)) {
1207                        list_add_tail(&s->update, &tp->update);
1208                        tp->update_flags |= TTY_UPDATE_LIST;
1209                }
1210        }
1211}
1212
1213/*
1214 * Do carriage return.
1215 */
1216static void
1217tty3270_cr(struct tty3270 *tp)
1218{
1219        tp->cx = 0;
1220}
1221
1222/*
1223 * Do line feed.
1224 */
1225static void
1226tty3270_lf(struct tty3270 *tp)
1227{
1228        struct tty3270_line temp;
1229        int i;
1230
1231        tty3270_convert_line(tp, tp->cy);
1232        if (tp->cy < tp->view.rows - 3) {
1233                tp->cy++;
1234                return;
1235        }
1236        /* Last line just filled up. Add new, blank line. */
1237        tty3270_blank_line(tp);
1238        temp = tp->screen[0];
1239        temp.len = 0;
1240        for (i = 0; i < tp->view.rows - 3; i++)
1241                tp->screen[i] = tp->screen[i+1];
1242        tp->screen[tp->view.rows - 3] = temp;
1243        tty3270_rebuild_update(tp);
1244}
1245
1246static void
1247tty3270_ri(struct tty3270 *tp)
1248{
1249        if (tp->cy > 0) {
1250            tty3270_convert_line(tp, tp->cy);
1251            tp->cy--;
1252        }
1253}
1254
1255/*
1256 * Insert characters at current position.
1257 */
1258static void
1259tty3270_insert_characters(struct tty3270 *tp, int n)
1260{
1261        struct tty3270_line *line;
1262        int k;
1263
1264        line = tp->screen + tp->cy;
1265        while (line->len < tp->cx) {
1266                line->cells[line->len].character = tp->view.ascebc[' '];
1267                line->cells[line->len].highlight = TAX_RESET;
1268                line->cells[line->len].f_color = TAC_RESET;
1269                line->len++;
1270        }
1271        if (n > tp->view.cols - tp->cx)
1272                n = tp->view.cols - tp->cx;
1273        k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n);
1274        while (k--)
1275                line->cells[tp->cx + n + k] = line->cells[tp->cx + k];
1276        line->len += n;
1277        if (line->len > tp->view.cols)
1278                line->len = tp->view.cols;
1279        while (n-- > 0) {
1280                line->cells[tp->cx + n].character = tp->view.ascebc[' '];
1281                line->cells[tp->cx + n].highlight = tp->highlight;
1282                line->cells[tp->cx + n].f_color = tp->f_color;
1283        }
1284}
1285
1286/*
1287 * Delete characters at current position.
1288 */
1289static void
1290tty3270_delete_characters(struct tty3270 *tp, int n)
1291{
1292        struct tty3270_line *line;
1293        int i;
1294
1295        line = tp->screen + tp->cy;
1296        if (line->len <= tp->cx)
1297                return;
1298        if (line->len - tp->cx <= n) {
1299                line->len = tp->cx;
1300                return;
1301        }
1302        for (i = tp->cx; i + n < line->len; i++)
1303                line->cells[i] = line->cells[i + n];
1304        line->len -= n;
1305}
1306
1307/*
1308 * Erase characters at current position.
1309 */
1310static void
1311tty3270_erase_characters(struct tty3270 *tp, int n)
1312{
1313        struct tty3270_line *line;
1314        struct tty3270_cell *cell;
1315
1316        line = tp->screen + tp->cy;
1317        while (line->len > tp->cx && n-- > 0) {
1318                cell = line->cells + tp->cx++;
1319                cell->character = ' ';
1320                cell->highlight = TAX_RESET;
1321                cell->f_color = TAC_RESET;
1322        }
1323        tp->cx += n;
1324        tp->cx = min_t(int, tp->cx, tp->view.cols - 1);
1325}
1326
1327/*
1328 * Erase line, 3 different cases:
1329 *  Esc [ 0 K   Erase from current position to end of line inclusive
1330 *  Esc [ 1 K   Erase from beginning of line to current position inclusive
1331 *  Esc [ 2 K   Erase entire line (without moving cursor)
1332 */
1333static void
1334tty3270_erase_line(struct tty3270 *tp, int mode)
1335{
1336        struct tty3270_line *line;
1337        struct tty3270_cell *cell;
1338        int i;
1339
1340        line = tp->screen + tp->cy;
1341        if (mode == 0)
1342                line->len = tp->cx;
1343        else if (mode == 1) {
1344                for (i = 0; i < tp->cx; i++) {
1345                        cell = line->cells + i;
1346                        cell->character = ' ';
1347                        cell->highlight = TAX_RESET;
1348                        cell->f_color = TAC_RESET;
1349                }
1350                if (line->len <= tp->cx)
1351                        line->len = tp->cx + 1;
1352        } else if (mode == 2)
1353                line->len = 0;
1354        tty3270_convert_line(tp, tp->cy);
1355}
1356
1357/*
1358 * Erase display, 3 different cases:
1359 *  Esc [ 0 J   Erase from current position to bottom of screen inclusive
1360 *  Esc [ 1 J   Erase from top of screen to current position inclusive
1361 *  Esc [ 2 J   Erase entire screen (without moving the cursor)
1362 */
1363static void
1364tty3270_erase_display(struct tty3270 *tp, int mode)
1365{
1366        int i;
1367
1368        if (mode == 0) {
1369                tty3270_erase_line(tp, 0);
1370                for (i = tp->cy + 1; i < tp->view.rows - 2; i++) {
1371                        tp->screen[i].len = 0;
1372                        tty3270_convert_line(tp, i);
1373                }
1374        } else if (mode == 1) {
1375                for (i = 0; i < tp->cy; i++) {
1376                        tp->screen[i].len = 0;
1377                        tty3270_convert_line(tp, i);
1378                }
1379                tty3270_erase_line(tp, 1);
1380        } else if (mode == 2) {
1381                for (i = 0; i < tp->view.rows - 2; i++) {
1382                        tp->screen[i].len = 0;
1383                        tty3270_convert_line(tp, i);
1384                }
1385        }
1386        tty3270_rebuild_update(tp);
1387}
1388
1389/*
1390 * Set attributes found in an escape sequence.
1391 *  Esc [ <attr> ; <attr> ; ... m
1392 */
1393static void
1394tty3270_set_attributes(struct tty3270 *tp)
1395{
1396        static unsigned char f_colors[] = {
1397                TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE,
1398                TAC_PINK, TAC_TURQ, TAC_WHITE, 0, TAC_DEFAULT
1399        };
1400        int i, attr;
1401
1402        for (i = 0; i <= tp->esc_npar; i++) {
1403                attr = tp->esc_par[i];
1404                switch (attr) {
1405                case 0:         /* Reset */
1406                        tp->highlight = TAX_RESET;
1407                        tp->f_color = TAC_RESET;
1408                        break;
1409                /* Highlight. */
1410                case 4:         /* Start underlining. */
1411                        tp->highlight = TAX_UNDER;
1412                        break;
1413                case 5:         /* Start blink. */
1414                        tp->highlight = TAX_BLINK;
1415                        break;
1416                case 7:         /* Start reverse. */
1417                        tp->highlight = TAX_REVER;
1418                        break;
1419                case 24:        /* End underlining */
1420                        if (tp->highlight == TAX_UNDER)
1421                                tp->highlight = TAX_RESET;
1422                        break;
1423                case 25:        /* End blink. */
1424                        if (tp->highlight == TAX_BLINK)
1425                                tp->highlight = TAX_RESET;
1426                        break;
1427                case 27:        /* End reverse. */
1428                        if (tp->highlight == TAX_REVER)
1429                                tp->highlight = TAX_RESET;
1430                        break;
1431                /* Foreground color. */
1432                case 30:        /* Black */
1433                case 31:        /* Red */
1434                case 32:        /* Green */
1435                case 33:        /* Yellow */
1436                case 34:        /* Blue */
1437                case 35:        /* Magenta */
1438                case 36:        /* Cyan */
1439                case 37:        /* White */
1440                case 39:        /* Black */
1441                        tp->f_color = f_colors[attr - 30];
1442                        break;
1443                }
1444        }
1445}
1446
1447static inline int
1448tty3270_getpar(struct tty3270 *tp, int ix)
1449{
1450        return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1;
1451}
1452
1453static void
1454tty3270_goto_xy(struct tty3270 *tp, int cx, int cy)
1455{
1456        int max_cx = max(0, cx);
1457        int max_cy = max(0, cy);
1458
1459        tp->cx = min_t(int, tp->view.cols - 1, max_cx);
1460        cy = min_t(int, tp->view.rows - 3, max_cy);
1461        if (cy != tp->cy) {
1462                tty3270_convert_line(tp, tp->cy);
1463                tp->cy = cy;
1464        }
1465}
1466
1467/*
1468 * Process escape sequences. Known sequences:
1469 *  Esc 7                       Save Cursor Position
1470 *  Esc 8                       Restore Cursor Position
1471 *  Esc [ Pn ; Pn ; .. m        Set attributes
1472 *  Esc [ Pn ; Pn H             Cursor Position
1473 *  Esc [ Pn ; Pn f             Cursor Position
1474 *  Esc [ Pn A                  Cursor Up
1475 *  Esc [ Pn B                  Cursor Down
1476 *  Esc [ Pn C                  Cursor Forward
1477 *  Esc [ Pn D                  Cursor Backward
1478 *  Esc [ Pn G                  Cursor Horizontal Absolute
1479 *  Esc [ Pn X                  Erase Characters
1480 *  Esc [ Ps J                  Erase in Display
1481 *  Esc [ Ps K                  Erase in Line
1482 * // FIXME: add all the new ones.
1483 *
1484 *  Pn is a numeric parameter, a string of zero or more decimal digits.
1485 *  Ps is a selective parameter.
1486 */
1487static void
1488tty3270_escape_sequence(struct tty3270 *tp, char ch)
1489{
1490        enum { ESnormal, ESesc, ESsquare, ESgetpars };
1491
1492        if (tp->esc_state == ESnormal) {
1493                if (ch == 0x1b)
1494                        /* Starting new escape sequence. */
1495                        tp->esc_state = ESesc;
1496                return;
1497        }
1498        if (tp->esc_state == ESesc) {
1499                tp->esc_state = ESnormal;
1500                switch (ch) {
1501                case '[':
1502                        tp->esc_state = ESsquare;
1503                        break;
1504                case 'E':
1505                        tty3270_cr(tp);
1506                        tty3270_lf(tp);
1507                        break;
1508                case 'M':
1509                        tty3270_ri(tp);
1510                        break;
1511                case 'D':
1512                        tty3270_lf(tp);
1513                        break;
1514                case 'Z':               /* Respond ID. */
1515                        kbd_puts_queue(&tp->port, "\033[?6c");
1516                        break;
1517                case '7':               /* Save cursor position. */
1518                        tp->saved_cx = tp->cx;
1519                        tp->saved_cy = tp->cy;
1520                        tp->saved_highlight = tp->highlight;
1521                        tp->saved_f_color = tp->f_color;
1522                        break;
1523                case '8':               /* Restore cursor position. */
1524                        tty3270_convert_line(tp, tp->cy);
1525                        tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
1526                        tp->highlight = tp->saved_highlight;
1527                        tp->f_color = tp->saved_f_color;
1528                        break;
1529                case 'c':               /* Reset terminal. */
1530                        tp->cx = tp->saved_cx = 0;
1531                        tp->cy = tp->saved_cy = 0;
1532                        tp->highlight = tp->saved_highlight = TAX_RESET;
1533                        tp->f_color = tp->saved_f_color = TAC_RESET;
1534                        tty3270_erase_display(tp, 2);
1535                        break;
1536                }
1537                return;
1538        }
1539        if (tp->esc_state == ESsquare) {
1540                tp->esc_state = ESgetpars;
1541                memset(tp->esc_par, 0, sizeof(tp->esc_par));
1542                tp->esc_npar = 0;
1543                tp->esc_ques = (ch == '?');
1544                if (tp->esc_ques)
1545                        return;
1546        }
1547        if (tp->esc_state == ESgetpars) {
1548                if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) {
1549                        tp->esc_npar++;
1550                        return;
1551                }
1552                if (ch >= '0' && ch <= '9') {
1553                        tp->esc_par[tp->esc_npar] *= 10;
1554                        tp->esc_par[tp->esc_npar] += ch - '0';
1555                        return;
1556                }
1557        }
1558        tp->esc_state = ESnormal;
1559        if (ch == 'n' && !tp->esc_ques) {
1560                if (tp->esc_par[0] == 5)                /* Status report. */
1561                        kbd_puts_queue(&tp->port, "\033[0n");
1562                else if (tp->esc_par[0] == 6) { /* Cursor report. */
1563                        char buf[40];
1564                        sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1);
1565                        kbd_puts_queue(&tp->port, buf);
1566                }
1567                return;
1568        }
1569        if (tp->esc_ques)
1570                return;
1571        switch (ch) {
1572        case 'm':
1573                tty3270_set_attributes(tp);
1574                break;
1575        case 'H':       /* Set cursor position. */
1576        case 'f':
1577                tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1,
1578                                tty3270_getpar(tp, 0) - 1);
1579                break;
1580        case 'd':       /* Set y position. */
1581                tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1);
1582                break;
1583        case 'A':       /* Cursor up. */
1584        case 'F':
1585                tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0));
1586                break;
1587        case 'B':       /* Cursor down. */
1588        case 'e':
1589        case 'E':
1590                tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0));
1591                break;
1592        case 'C':       /* Cursor forward. */
1593        case 'a':
1594                tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy);
1595                break;
1596        case 'D':       /* Cursor backward. */
1597                tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy);
1598                break;
1599        case 'G':       /* Set x position. */
1600        case '`':
1601                tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy);
1602                break;
1603        case 'X':       /* Erase Characters. */
1604                tty3270_erase_characters(tp, tty3270_getpar(tp, 0));
1605                break;
1606        case 'J':       /* Erase display. */
1607                tty3270_erase_display(tp, tp->esc_par[0]);
1608                break;
1609        case 'K':       /* Erase line. */
1610                tty3270_erase_line(tp, tp->esc_par[0]);
1611                break;
1612        case 'P':       /* Delete characters. */
1613                tty3270_delete_characters(tp, tty3270_getpar(tp, 0));
1614                break;
1615        case '@':       /* Insert characters. */
1616                tty3270_insert_characters(tp, tty3270_getpar(tp, 0));
1617                break;
1618        case 's':       /* Save cursor position. */
1619                tp->saved_cx = tp->cx;
1620                tp->saved_cy = tp->cy;
1621                tp->saved_highlight = tp->highlight;
1622                tp->saved_f_color = tp->f_color;
1623                break;
1624        case 'u':       /* Restore cursor position. */
1625                tty3270_convert_line(tp, tp->cy);
1626                tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
1627                tp->highlight = tp->saved_highlight;
1628                tp->f_color = tp->saved_f_color;
1629                break;
1630        }
1631}
1632
1633/*
1634 * String write routine for 3270 ttys
1635 */
1636static void
1637tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty,
1638                const unsigned char *buf, int count)
1639{
1640        int i_msg, i;
1641
1642        spin_lock_bh(&tp->view.lock);
1643        for (i_msg = 0; !tty->flow.stopped && i_msg < count; i_msg++) {
1644                if (tp->esc_state != 0) {
1645                        /* Continue escape sequence. */
1646                        tty3270_escape_sequence(tp, buf[i_msg]);
1647                        continue;
1648                }
1649
1650                switch (buf[i_msg]) {
1651                case 0x07:              /* '\a' -- Alarm */
1652                        tp->wcc |= TW_PLUSALARM;
1653                        break;
1654                case 0x08:              /* Backspace. */
1655                        if (tp->cx > 0) {
1656                                tp->cx--;
1657                                tty3270_put_character(tp, ' ');
1658                        }
1659                        break;
1660                case 0x09:              /* '\t' -- Tabulate */
1661                        for (i = tp->cx % 8; i < 8; i++) {
1662                                if (tp->cx >= tp->view.cols) {
1663                                        tty3270_cr(tp);
1664                                        tty3270_lf(tp);
1665                                        break;
1666                                }
1667                                tty3270_put_character(tp, ' ');
1668                                tp->cx++;
1669                        }
1670                        break;
1671                case 0x0a:              /* '\n' -- New Line */
1672                        tty3270_cr(tp);
1673                        tty3270_lf(tp);
1674                        break;
1675                case 0x0c:              /* '\f' -- Form Feed */
1676                        tty3270_erase_display(tp, 2);
1677                        tp->cx = tp->cy = 0;
1678                        break;
1679                case 0x0d:              /* '\r' -- Carriage Return */
1680                        tp->cx = 0;
1681                        break;
1682                case 0x0f:              /* SuSE "exit alternate mode" */
1683                        break;
1684                case 0x1b:              /* Start escape sequence. */
1685                        tty3270_escape_sequence(tp, buf[i_msg]);
1686                        break;
1687                default:                /* Insert normal character. */
1688                        if (tp->cx >= tp->view.cols) {
1689                                tty3270_cr(tp);
1690                                tty3270_lf(tp);
1691                        }
1692                        tty3270_put_character(tp, buf[i_msg]);
1693                        tp->cx++;
1694                        break;
1695                }
1696        }
1697        /* Convert current line to 3270 data fragment. */
1698        tty3270_convert_line(tp, tp->cy);
1699
1700        /* Setup timer to update display after 1/10 second */
1701        if (!timer_pending(&tp->timer))
1702                tty3270_set_timer(tp, HZ/10);
1703
1704        spin_unlock_bh(&tp->view.lock);
1705}
1706
1707/*
1708 * String write routine for 3270 ttys
1709 */
1710static int
1711tty3270_write(struct tty_struct * tty,
1712              const unsigned char *buf, int count)
1713{
1714        struct tty3270 *tp;
1715
1716        tp = tty->driver_data;
1717        if (!tp)
1718                return 0;
1719        if (tp->char_count > 0) {
1720                tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);
1721                tp->char_count = 0;
1722        }
1723        tty3270_do_write(tp, tty, buf, count);
1724        return count;
1725}
1726
1727/*
1728 * Put single characters to the ttys character buffer
1729 */
1730static int tty3270_put_char(struct tty_struct *tty, unsigned char ch)
1731{
1732        struct tty3270 *tp;
1733
1734        tp = tty->driver_data;
1735        if (!tp || tp->char_count >= TTY3270_CHAR_BUF_SIZE)
1736                return 0;
1737        tp->char_buf[tp->char_count++] = ch;
1738        return 1;
1739}
1740
1741/*
1742 * Flush all characters from the ttys characeter buffer put there
1743 * by tty3270_put_char.
1744 */
1745static void
1746tty3270_flush_chars(struct tty_struct *tty)
1747{
1748        struct tty3270 *tp;
1749
1750        tp = tty->driver_data;
1751        if (!tp)
1752                return;
1753        if (tp->char_count > 0) {
1754                tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);
1755                tp->char_count = 0;
1756        }
1757}
1758
1759/*
1760 * Check for visible/invisible input switches
1761 */
1762static void
1763tty3270_set_termios(struct tty_struct *tty, struct ktermios *old)
1764{
1765        struct tty3270 *tp;
1766        int new;
1767
1768        tp = tty->driver_data;
1769        if (!tp)
1770                return;
1771        spin_lock_bh(&tp->view.lock);
1772        if (L_ICANON(tty)) {
1773                new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN;
1774                if (new != tp->inattr) {
1775                        tp->inattr = new;
1776                        tty3270_update_prompt(tp, NULL, 0);
1777                        tty3270_set_timer(tp, 1);
1778                }
1779        }
1780        spin_unlock_bh(&tp->view.lock);
1781}
1782
1783/*
1784 * Disable reading from a 3270 tty
1785 */
1786static void
1787tty3270_throttle(struct tty_struct * tty)
1788{
1789        struct tty3270 *tp;
1790
1791        tp = tty->driver_data;
1792        if (!tp)
1793                return;
1794        tp->throttle = 1;
1795}
1796
1797/*
1798 * Enable reading from a 3270 tty
1799 */
1800static void
1801tty3270_unthrottle(struct tty_struct * tty)
1802{
1803        struct tty3270 *tp;
1804
1805        tp = tty->driver_data;
1806        if (!tp)
1807                return;
1808        tp->throttle = 0;
1809        if (tp->attn)
1810                tty3270_issue_read(tp, 1);
1811}
1812
1813/*
1814 * Hang up the tty device.
1815 */
1816static void
1817tty3270_hangup(struct tty_struct *tty)
1818{
1819        struct tty3270 *tp;
1820
1821        tp = tty->driver_data;
1822        if (!tp)
1823                return;
1824        spin_lock_bh(&tp->view.lock);
1825        tp->cx = tp->saved_cx = 0;
1826        tp->cy = tp->saved_cy = 0;
1827        tp->highlight = tp->saved_highlight = TAX_RESET;
1828        tp->f_color = tp->saved_f_color = TAC_RESET;
1829        tty3270_blank_screen(tp);
1830        while (tp->nr_lines < tp->view.rows - 2)
1831                tty3270_blank_line(tp);
1832        tp->update_flags = TTY_UPDATE_ALL;
1833        spin_unlock_bh(&tp->view.lock);
1834        tty3270_set_timer(tp, 1);
1835}
1836
1837static void
1838tty3270_wait_until_sent(struct tty_struct *tty, int timeout)
1839{
1840}
1841
1842static int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd,
1843                         unsigned long arg)
1844{
1845        struct tty3270 *tp;
1846
1847        tp = tty->driver_data;
1848        if (!tp)
1849                return -ENODEV;
1850        if (tty_io_error(tty))
1851                return -EIO;
1852        return kbd_ioctl(tp->kbd, cmd, arg);
1853}
1854
1855#ifdef CONFIG_COMPAT
1856static long tty3270_compat_ioctl(struct tty_struct *tty,
1857                                 unsigned int cmd, unsigned long arg)
1858{
1859        struct tty3270 *tp;
1860
1861        tp = tty->driver_data;
1862        if (!tp)
1863                return -ENODEV;
1864        if (tty_io_error(tty))
1865                return -EIO;
1866        return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg));
1867}
1868#endif
1869
1870static const struct tty_operations tty3270_ops = {
1871        .install = tty3270_install,
1872        .cleanup = tty3270_cleanup,
1873        .open = tty3270_open,
1874        .close = tty3270_close,
1875        .write = tty3270_write,
1876        .put_char = tty3270_put_char,
1877        .flush_chars = tty3270_flush_chars,
1878        .write_room = tty3270_write_room,
1879        .throttle = tty3270_throttle,
1880        .unthrottle = tty3270_unthrottle,
1881        .hangup = tty3270_hangup,
1882        .wait_until_sent = tty3270_wait_until_sent,
1883        .ioctl = tty3270_ioctl,
1884#ifdef CONFIG_COMPAT
1885        .compat_ioctl = tty3270_compat_ioctl,
1886#endif
1887        .set_termios = tty3270_set_termios
1888};
1889
1890static void tty3270_create_cb(int minor)
1891{
1892        tty_register_device(tty3270_driver, minor - RAW3270_FIRSTMINOR, NULL);
1893}
1894
1895static void tty3270_destroy_cb(int minor)
1896{
1897        tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR);
1898}
1899
1900static struct raw3270_notifier tty3270_notifier =
1901{
1902        .create = tty3270_create_cb,
1903        .destroy = tty3270_destroy_cb,
1904};
1905
1906/*
1907 * 3270 tty registration code called from tty_init().
1908 * Most kernel services (incl. kmalloc) are available at this poimt.
1909 */
1910static int __init tty3270_init(void)
1911{
1912        struct tty_driver *driver;
1913        int ret;
1914
1915        driver = tty_alloc_driver(RAW3270_MAXDEVS,
1916                                  TTY_DRIVER_REAL_RAW |
1917                                  TTY_DRIVER_DYNAMIC_DEV |
1918                                  TTY_DRIVER_RESET_TERMIOS);
1919        if (IS_ERR(driver))
1920                return PTR_ERR(driver);
1921
1922        /*
1923         * Initialize the tty_driver structure
1924         * Entries in tty3270_driver that are NOT initialized:
1925         * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
1926         */
1927        driver->driver_name = "tty3270";
1928        driver->name = "3270/tty";
1929        driver->major = IBM_TTY3270_MAJOR;
1930        driver->minor_start = RAW3270_FIRSTMINOR;
1931        driver->name_base = RAW3270_FIRSTMINOR;
1932        driver->type = TTY_DRIVER_TYPE_SYSTEM;
1933        driver->subtype = SYSTEM_TYPE_TTY;
1934        driver->init_termios = tty_std_termios;
1935        tty_set_operations(driver, &tty3270_ops);
1936        ret = tty_register_driver(driver);
1937        if (ret) {
1938                tty_driver_kref_put(driver);
1939                return ret;
1940        }
1941        tty3270_driver = driver;
1942        raw3270_register_notifier(&tty3270_notifier);
1943        return 0;
1944}
1945
1946static void __exit
1947tty3270_exit(void)
1948{
1949        struct tty_driver *driver;
1950
1951        raw3270_unregister_notifier(&tty3270_notifier);
1952        driver = tty3270_driver;
1953        tty3270_driver = NULL;
1954        tty_unregister_driver(driver);
1955        tty_driver_kref_put(driver);
1956        tty3270_del_views();
1957}
1958
1959MODULE_LICENSE("GPL");
1960MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR);
1961
1962module_init(tty3270_init);
1963module_exit(tty3270_exit);
1964