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