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