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.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.dstat & DEV_STAT_UNIT_CHECK)
 675                        rq->rc = -EIO;
 676                else
 677                        /* Normal end. Copy residual count. */
 678                        rq->rescnt = irb->scsw.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
 969tty3270_put_character(struct tty3270 *tp, char ch)
 970{
 971        struct tty3270_line *line;
 972        struct tty3270_cell *cell;
 973
 974        line = tp->screen + tp->cy;
 975        if (line->len <= tp->cx) {
 976                while (line->len < tp->cx) {
 977                        cell = line->cells + line->len;
 978                        cell->character = tp->view.ascebc[' '];
 979                        cell->highlight = tp->highlight;
 980                        cell->f_color = tp->f_color;
 981                        line->len++;
 982                }
 983                line->len++;
 984        }
 985        cell = line->cells + tp->cx;
 986        cell->character = tp->view.ascebc[(unsigned int) ch];
 987        cell->highlight = tp->highlight;
 988        cell->f_color = tp->f_color;
 989}
 990
 991/*
 992 * Convert a tty3270_line to a 3270 data fragment usable for output.
 993 */
 994static void
 995tty3270_convert_line(struct tty3270 *tp, int line_nr)
 996{
 997        struct tty3270_line *line;
 998        struct tty3270_cell *cell;
 999        struct string *s, *n;
1000        unsigned char highlight;
1001        unsigned char f_color;
1002        char *cp;
1003        int flen, i;
1004
1005        /* Determine how long the fragment will be. */
1006        flen = 3;               /* Prefix (TO_SBA). */
1007        line = tp->screen + line_nr;
1008        flen += line->len;
1009        highlight = TAX_RESET;
1010        f_color = TAC_RESET;
1011        for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
1012                if (cell->highlight != highlight) {
1013                        flen += 3;      /* TO_SA to switch highlight. */
1014                        highlight = cell->highlight;
1015                }
1016                if (cell->f_color != f_color) {
1017                        flen += 3;      /* TO_SA to switch color. */
1018                        f_color = cell->f_color;
1019                }
1020        }
1021        if (highlight != TAX_RESET)
1022                flen += 3;      /* TO_SA to reset hightlight. */
1023        if (f_color != TAC_RESET)
1024                flen += 3;      /* TO_SA to reset color. */
1025        if (line->len < tp->view.cols)
1026                flen += 4;      /* Postfix (TO_RA). */
1027
1028        /* Find the line in the list. */
1029        i = tp->view.rows - 2 - line_nr;
1030        list_for_each_entry_reverse(s, &tp->lines, list)
1031                if (--i <= 0)
1032                        break;
1033        /*
1034         * Check if the line needs to get reallocated.
1035         */
1036        if (s->len != flen) {
1037                /* Reallocate string. */
1038                n = tty3270_alloc_string(tp, flen);
1039                list_add(&n->list, &s->list);
1040                list_del_init(&s->list);
1041                if (!list_empty(&s->update))
1042                        list_del_init(&s->update);
1043                free_string(&tp->freemem, s);
1044                s = n;
1045        }
1046
1047        /* Write 3270 data fragment. */
1048        cp = s->string;
1049        *cp++ = TO_SBA;
1050        *cp++ = 0;
1051        *cp++ = 0;
1052
1053        highlight = TAX_RESET;
1054        f_color = TAC_RESET;
1055        for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
1056                if (cell->highlight != highlight) {
1057                        *cp++ = TO_SA;
1058                        *cp++ = TAT_EXTHI;
1059                        *cp++ = cell->highlight;
1060                        highlight = cell->highlight;
1061                }
1062                if (cell->f_color != f_color) {
1063                        *cp++ = TO_SA;
1064                        *cp++ = TAT_COLOR;
1065                        *cp++ = cell->f_color;
1066                        f_color = cell->f_color;
1067                }
1068                *cp++ = cell->character;
1069        }
1070        if (highlight != TAX_RESET) {
1071                *cp++ = TO_SA;
1072                *cp++ = TAT_EXTHI;
1073                *cp++ = TAX_RESET;
1074        }
1075        if (f_color != TAC_RESET) {
1076                *cp++ = TO_SA;
1077                *cp++ = TAT_COLOR;
1078                *cp++ = TAC_RESET;
1079        }
1080        if (line->len < tp->view.cols) {
1081                *cp++ = TO_RA;
1082                *cp++ = 0;
1083                *cp++ = 0;
1084                *cp++ = 0;
1085        }
1086
1087        if (tp->nr_up + line_nr < tp->view.rows - 2) {
1088                /* Line is currently visible on screen. */
1089                tty3270_update_string(tp, s, line_nr);
1090                /* Add line to update list. */
1091                if (list_empty(&s->update)) {
1092                        list_add_tail(&s->update, &tp->update);
1093                        tp->update_flags |= TTY_UPDATE_LIST;
1094                }
1095        }
1096}
1097
1098/*
1099 * Do carriage return.
1100 */
1101static void
1102tty3270_cr(struct tty3270 *tp)
1103{
1104        tp->cx = 0;
1105}
1106
1107/*
1108 * Do line feed.
1109 */
1110static void
1111tty3270_lf(struct tty3270 *tp)
1112{
1113        struct tty3270_line temp;
1114        int i;
1115
1116        tty3270_convert_line(tp, tp->cy);
1117        if (tp->cy < tp->view.rows - 3) {
1118                tp->cy++;
1119                return;
1120        }
1121        /* Last line just filled up. Add new, blank line. */
1122        tty3270_blank_line(tp);
1123        temp = tp->screen[0];
1124        temp.len = 0;
1125        for (i = 0; i < tp->view.rows - 3; i++)
1126                tp->screen[i] = tp->screen[i+1];
1127        tp->screen[tp->view.rows - 3] = temp;
1128        tty3270_rebuild_update(tp);
1129}
1130
1131static void
1132tty3270_ri(struct tty3270 *tp)
1133{
1134        if (tp->cy > 0) {
1135            tty3270_convert_line(tp, tp->cy);
1136            tp->cy--;
1137        }
1138}
1139
1140/*
1141 * Insert characters at current position.
1142 */
1143static void
1144tty3270_insert_characters(struct tty3270 *tp, int n)
1145{
1146        struct tty3270_line *line;
1147        int k;
1148
1149        line = tp->screen + tp->cy;
1150        while (line->len < tp->cx) {
1151                line->cells[line->len].character = tp->view.ascebc[' '];
1152                line->cells[line->len].highlight = TAX_RESET;
1153                line->cells[line->len].f_color = TAC_RESET;
1154                line->len++;
1155        }
1156        if (n > tp->view.cols - tp->cx)
1157                n = tp->view.cols - tp->cx;
1158        k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n);
1159        while (k--)
1160                line->cells[tp->cx + n + k] = line->cells[tp->cx + k];
1161        line->len += n;
1162        if (line->len > tp->view.cols)
1163                line->len = tp->view.cols;
1164        while (n-- > 0) {
1165                line->cells[tp->cx + n].character = tp->view.ascebc[' '];
1166                line->cells[tp->cx + n].highlight = tp->highlight;
1167                line->cells[tp->cx + n].f_color = tp->f_color;
1168        }
1169}
1170
1171/*
1172 * Delete characters at current position.
1173 */
1174static void
1175tty3270_delete_characters(struct tty3270 *tp, int n)
1176{
1177        struct tty3270_line *line;
1178        int i;
1179
1180        line = tp->screen + tp->cy;
1181        if (line->len <= tp->cx)
1182                return;
1183        if (line->len - tp->cx <= n) {
1184                line->len = tp->cx;
1185                return;
1186        }
1187        for (i = tp->cx; i + n < line->len; i++)
1188                line->cells[i] = line->cells[i + n];
1189        line->len -= n;
1190}
1191
1192/*
1193 * Erase characters at current position.
1194 */
1195static void
1196tty3270_erase_characters(struct tty3270 *tp, int n)
1197{
1198        struct tty3270_line *line;
1199        struct tty3270_cell *cell;
1200
1201        line = tp->screen + tp->cy;
1202        while (line->len > tp->cx && n-- > 0) {
1203                cell = line->cells + tp->cx++;
1204                cell->character = ' ';
1205                cell->highlight = TAX_RESET;
1206                cell->f_color = TAC_RESET;
1207        }
1208        tp->cx += n;
1209        tp->cx = min_t(int, tp->cx, tp->view.cols - 1);
1210}
1211
1212/*
1213 * Erase line, 3 different cases:
1214 *  Esc [ 0 K   Erase from current position to end of line inclusive
1215 *  Esc [ 1 K   Erase from beginning of line to current position inclusive
1216 *  Esc [ 2 K   Erase entire line (without moving cursor)
1217 */
1218static void
1219tty3270_erase_line(struct tty3270 *tp, int mode)
1220{
1221        struct tty3270_line *line;
1222        struct tty3270_cell *cell;
1223        int i;
1224
1225        line = tp->screen + tp->cy;
1226        if (mode == 0)
1227                line->len = tp->cx;
1228        else if (mode == 1) {
1229                for (i = 0; i < tp->cx; i++) {
1230                        cell = line->cells + i;
1231                        cell->character = ' ';
1232                        cell->highlight = TAX_RESET;
1233                        cell->f_color = TAC_RESET;
1234                }
1235                if (line->len <= tp->cx)
1236                        line->len = tp->cx + 1;
1237        } else if (mode == 2)
1238                line->len = 0;
1239        tty3270_convert_line(tp, tp->cy);
1240}
1241
1242/*
1243 * Erase display, 3 different cases:
1244 *  Esc [ 0 J   Erase from current position to bottom of screen inclusive
1245 *  Esc [ 1 J   Erase from top of screen to current position inclusive
1246 *  Esc [ 2 J   Erase entire screen (without moving the cursor)
1247 */
1248static void
1249tty3270_erase_display(struct tty3270 *tp, int mode)
1250{
1251        int i;
1252
1253        if (mode == 0) {
1254                tty3270_erase_line(tp, 0);
1255                for (i = tp->cy + 1; i < tp->view.rows - 2; i++) {
1256                        tp->screen[i].len = 0;
1257                        tty3270_convert_line(tp, i);
1258                }
1259        } else if (mode == 1) {
1260                for (i = 0; i < tp->cy; i++) {
1261                        tp->screen[i].len = 0;
1262                        tty3270_convert_line(tp, i);
1263                }
1264                tty3270_erase_line(tp, 1);
1265        } else if (mode == 2) {
1266                for (i = 0; i < tp->view.rows - 2; i++) {
1267                        tp->screen[i].len = 0;
1268                        tty3270_convert_line(tp, i);
1269                }
1270        }
1271        tty3270_rebuild_update(tp);
1272}
1273
1274/*
1275 * Set attributes found in an escape sequence.
1276 *  Esc [ <attr> ; <attr> ; ... m
1277 */
1278static void
1279tty3270_set_attributes(struct tty3270 *tp)
1280{
1281        static unsigned char f_colors[] = {
1282                TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE,
1283                TAC_PINK, TAC_TURQ, TAC_WHITE, 0, TAC_DEFAULT
1284        };
1285        int i, attr;
1286
1287        for (i = 0; i <= tp->esc_npar; i++) {
1288                attr = tp->esc_par[i];
1289                switch (attr) {
1290                case 0:         /* Reset */
1291                        tp->highlight = TAX_RESET;
1292                        tp->f_color = TAC_RESET;
1293                        break;
1294                /* Highlight. */
1295                case 4:         /* Start underlining. */
1296                        tp->highlight = TAX_UNDER;
1297                        break;
1298                case 5:         /* Start blink. */
1299                        tp->highlight = TAX_BLINK;
1300                        break;
1301                case 7:         /* Start reverse. */
1302                        tp->highlight = TAX_REVER;
1303                        break;
1304                case 24:        /* End underlining */
1305                        if (tp->highlight == TAX_UNDER)
1306                                tp->highlight = TAX_RESET;
1307                        break;
1308                case 25:        /* End blink. */
1309                        if (tp->highlight == TAX_BLINK)
1310                                tp->highlight = TAX_RESET;
1311                        break;
1312                case 27:        /* End reverse. */
1313                        if (tp->highlight == TAX_REVER)
1314                                tp->highlight = TAX_RESET;
1315                        break;
1316                /* Foreground color. */
1317                case 30:        /* Black */
1318                case 31:        /* Red */
1319                case 32:        /* Green */
1320                case 33:        /* Yellow */
1321                case 34:        /* Blue */
1322                case 35:        /* Magenta */
1323                case 36:        /* Cyan */
1324                case 37:        /* White */
1325                case 39:        /* Black */
1326                        tp->f_color = f_colors[attr - 30];
1327                        break;
1328                }
1329        }
1330}
1331
1332static inline int
1333tty3270_getpar(struct tty3270 *tp, int ix)
1334{
1335        return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1;
1336}
1337
1338static void
1339tty3270_goto_xy(struct tty3270 *tp, int cx, int cy)
1340{
1341        int max_cx = max(0, cx);
1342        int max_cy = max(0, cy);
1343
1344        tp->cx = min_t(int, tp->view.cols - 1, max_cx);
1345        cy = min_t(int, tp->view.rows - 3, max_cy);
1346        if (cy != tp->cy) {
1347                tty3270_convert_line(tp, tp->cy);
1348                tp->cy = cy;
1349        }
1350}
1351
1352/*
1353 * Process escape sequences. Known sequences:
1354 *  Esc 7                       Save Cursor Position
1355 *  Esc 8                       Restore Cursor Position
1356 *  Esc [ Pn ; Pn ; .. m        Set attributes
1357 *  Esc [ Pn ; Pn H             Cursor Position
1358 *  Esc [ Pn ; Pn f             Cursor Position
1359 *  Esc [ Pn A                  Cursor Up
1360 *  Esc [ Pn B                  Cursor Down
1361 *  Esc [ Pn C                  Cursor Forward
1362 *  Esc [ Pn D                  Cursor Backward
1363 *  Esc [ Pn G                  Cursor Horizontal Absolute
1364 *  Esc [ Pn X                  Erase Characters
1365 *  Esc [ Ps J                  Erase in Display
1366 *  Esc [ Ps K                  Erase in Line
1367 * // FIXME: add all the new ones.
1368 *
1369 *  Pn is a numeric parameter, a string of zero or more decimal digits.
1370 *  Ps is a selective parameter.
1371 */
1372static void
1373tty3270_escape_sequence(struct tty3270 *tp, char ch)
1374{
1375        enum { ESnormal, ESesc, ESsquare, ESgetpars };
1376
1377        if (tp->esc_state == ESnormal) {
1378                if (ch == 0x1b)
1379                        /* Starting new escape sequence. */
1380                        tp->esc_state = ESesc;
1381                return;
1382        }
1383        if (tp->esc_state == ESesc) {
1384                tp->esc_state = ESnormal;
1385                switch (ch) {
1386                case '[':
1387                        tp->esc_state = ESsquare;
1388                        break;
1389                case 'E':
1390                        tty3270_cr(tp);
1391                        tty3270_lf(tp);
1392                        break;
1393                case 'M':
1394                        tty3270_ri(tp);
1395                        break;
1396                case 'D':
1397                        tty3270_lf(tp);
1398                        break;
1399                case 'Z':               /* Respond ID. */
1400                        kbd_puts_queue(tp->tty, "\033[?6c");
1401                        break;
1402                case '7':               /* Save cursor position. */
1403                        tp->saved_cx = tp->cx;
1404                        tp->saved_cy = tp->cy;
1405                        tp->saved_highlight = tp->highlight;
1406                        tp->saved_f_color = tp->f_color;
1407                        break;
1408                case '8':               /* Restore cursor position. */
1409                        tty3270_convert_line(tp, tp->cy);
1410                        tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
1411                        tp->highlight = tp->saved_highlight;
1412                        tp->f_color = tp->saved_f_color;
1413                        break;
1414                case 'c':               /* Reset terminal. */
1415                        tp->cx = tp->saved_cx = 0;
1416                        tp->cy = tp->saved_cy = 0;
1417                        tp->highlight = tp->saved_highlight = TAX_RESET;
1418                        tp->f_color = tp->saved_f_color = TAC_RESET;
1419                        tty3270_erase_display(tp, 2);
1420                        break;
1421                }
1422                return;
1423        }
1424        if (tp->esc_state == ESsquare) {
1425                tp->esc_state = ESgetpars;
1426                memset(tp->esc_par, 0, sizeof(tp->esc_par));
1427                tp->esc_npar = 0;
1428                tp->esc_ques = (ch == '?');
1429                if (tp->esc_ques)
1430                        return;
1431        }
1432        if (tp->esc_state == ESgetpars) {
1433                if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) {
1434                        tp->esc_npar++;
1435                        return;
1436                }
1437                if (ch >= '0' && ch <= '9') {
1438                        tp->esc_par[tp->esc_npar] *= 10;
1439                        tp->esc_par[tp->esc_npar] += ch - '0';
1440                        return;
1441                }
1442        }
1443        tp->esc_state = ESnormal;
1444        if (ch == 'n' && !tp->esc_ques) {
1445                if (tp->esc_par[0] == 5)                /* Status report. */
1446                        kbd_puts_queue(tp->tty, "\033[0n");
1447                else if (tp->esc_par[0] == 6) { /* Cursor report. */
1448                        char buf[40];
1449                        sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1);
1450                        kbd_puts_queue(tp->tty, buf);
1451                }
1452                return;
1453        }
1454        if (tp->esc_ques)
1455                return;
1456        switch (ch) {
1457        case 'm':
1458                tty3270_set_attributes(tp);
1459                break;
1460        case 'H':       /* Set cursor position. */
1461        case 'f':
1462                tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1,
1463                                tty3270_getpar(tp, 0) - 1);
1464                break;
1465        case 'd':       /* Set y position. */
1466                tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1);
1467                break;
1468        case 'A':       /* Cursor up. */
1469        case 'F':
1470                tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0));
1471                break;
1472        case 'B':       /* Cursor down. */
1473        case 'e':
1474        case 'E':
1475                tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0));
1476                break;
1477        case 'C':       /* Cursor forward. */
1478        case 'a':
1479                tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy);
1480                break;
1481        case 'D':       /* Cursor backward. */
1482                tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy);
1483                break;
1484        case 'G':       /* Set x position. */
1485        case '`':
1486                tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy);
1487                break;
1488        case 'X':       /* Erase Characters. */
1489                tty3270_erase_characters(tp, tty3270_getpar(tp, 0));
1490                break;
1491        case 'J':       /* Erase display. */
1492                tty3270_erase_display(tp, tp->esc_par[0]);
1493                break;
1494        case 'K':       /* Erase line. */
1495                tty3270_erase_line(tp, tp->esc_par[0]);
1496                break;
1497        case 'P':       /* Delete characters. */
1498                tty3270_delete_characters(tp, tty3270_getpar(tp, 0));
1499                break;
1500        case '@':       /* Insert characters. */
1501                tty3270_insert_characters(tp, tty3270_getpar(tp, 0));
1502                break;
1503        case 's':       /* Save cursor position. */
1504                tp->saved_cx = tp->cx;
1505                tp->saved_cy = tp->cy;
1506                tp->saved_highlight = tp->highlight;
1507                tp->saved_f_color = tp->f_color;
1508                break;
1509        case 'u':       /* Restore cursor position. */
1510                tty3270_convert_line(tp, tp->cy);
1511                tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
1512                tp->highlight = tp->saved_highlight;
1513                tp->f_color = tp->saved_f_color;
1514                break;
1515        }
1516}
1517
1518/*
1519 * String write routine for 3270 ttys
1520 */
1521static void
1522tty3270_do_write(struct tty3270 *tp, const unsigned char *buf, int count)
1523{
1524        int i_msg, i;
1525
1526        spin_lock_bh(&tp->view.lock);
1527        for (i_msg = 0; !tp->tty->stopped && i_msg < count; i_msg++) {
1528                if (tp->esc_state != 0) {
1529                        /* Continue escape sequence. */
1530                        tty3270_escape_sequence(tp, buf[i_msg]);
1531                        continue;
1532                }
1533
1534                switch (buf[i_msg]) {
1535                case 0x07:              /* '\a' -- Alarm */
1536                        tp->wcc |= TW_PLUSALARM;
1537                        break;
1538                case 0x08:              /* Backspace. */
1539                        if (tp->cx > 0) {
1540                                tp->cx--;
1541                                tty3270_put_character(tp, ' ');
1542                        }
1543                        break;
1544                case 0x09:              /* '\t' -- Tabulate */
1545                        for (i = tp->cx % 8; i < 8; i++) {
1546                                if (tp->cx >= tp->view.cols) {
1547                                        tty3270_cr(tp);
1548                                        tty3270_lf(tp);
1549                                        break;
1550                                }
1551                                tty3270_put_character(tp, ' ');
1552                                tp->cx++;
1553                        }
1554                        break;
1555                case 0x0a:              /* '\n' -- New Line */
1556                        tty3270_cr(tp);
1557                        tty3270_lf(tp);
1558                        break;
1559                case 0x0c:              /* '\f' -- Form Feed */
1560                        tty3270_erase_display(tp, 2);
1561                        tp->cx = tp->cy = 0;
1562                        break;
1563                case 0x0d:              /* '\r' -- Carriage Return */
1564                        tp->cx = 0;
1565                        break;
1566                case 0x0f:              /* SuSE "exit alternate mode" */
1567                        break;
1568                case 0x1b:              /* Start escape sequence. */
1569                        tty3270_escape_sequence(tp, buf[i_msg]);
1570                        break;
1571                default:                /* Insert normal character. */
1572                        if (tp->cx >= tp->view.cols) {
1573                                tty3270_cr(tp);
1574                                tty3270_lf(tp);
1575                        }
1576                        tty3270_put_character(tp, buf[i_msg]);
1577                        tp->cx++;
1578                        break;
1579                }
1580        }
1581        /* Convert current line to 3270 data fragment. */
1582        tty3270_convert_line(tp, tp->cy);
1583
1584        /* Setup timer to update display after 1/10 second */
1585        if (!timer_pending(&tp->timer))
1586                tty3270_set_timer(tp, HZ/10);
1587
1588        spin_unlock_bh(&tp->view.lock);
1589}
1590
1591/*
1592 * String write routine for 3270 ttys
1593 */
1594static int
1595tty3270_write(struct tty_struct * tty,
1596              const unsigned char *buf, int count)
1597{
1598        struct tty3270 *tp;
1599
1600        tp = tty->driver_data;
1601        if (!tp)
1602                return 0;
1603        if (tp->char_count > 0) {
1604                tty3270_do_write(tp, tp->char_buf, tp->char_count);
1605                tp->char_count = 0;
1606        }
1607        tty3270_do_write(tp, buf, count);
1608        return count;
1609}
1610
1611/*
1612 * Put single characters to the ttys character buffer
1613 */
1614static void
1615tty3270_put_char(struct tty_struct *tty, unsigned char ch)
1616{
1617        struct tty3270 *tp;
1618
1619        tp = tty->driver_data;
1620        if (!tp)
1621                return;
1622        if (tp->char_count < TTY3270_CHAR_BUF_SIZE)
1623                tp->char_buf[tp->char_count++] = ch;
1624}
1625
1626/*
1627 * Flush all characters from the ttys characeter buffer put there
1628 * by tty3270_put_char.
1629 */
1630static void
1631tty3270_flush_chars(struct tty_struct *tty)
1632{
1633        struct tty3270 *tp;
1634
1635        tp = tty->driver_data;
1636        if (!tp)
1637                return;
1638        if (tp->char_count > 0) {
1639                tty3270_do_write(tp, tp->char_buf, tp->char_count);
1640                tp->char_count = 0;
1641        }
1642}
1643
1644/*
1645 * Returns the number of characters in the output buffer. This is
1646 * used in tty_wait_until_sent to wait until all characters have
1647 * appeared on the screen.
1648 */
1649static int
1650tty3270_chars_in_buffer(struct tty_struct *tty)
1651{
1652        return 0;
1653}
1654
1655static void
1656tty3270_flush_buffer(struct tty_struct *tty)
1657{
1658}
1659
1660/*
1661 * Check for visible/invisible input switches
1662 */
1663static void
1664tty3270_set_termios(struct tty_struct *tty, struct ktermios *old)
1665{
1666        struct tty3270 *tp;
1667        int new;
1668
1669        tp = tty->driver_data;
1670        if (!tp)
1671                return;
1672        spin_lock_bh(&tp->view.lock);
1673        if (L_ICANON(tty)) {
1674                new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN;
1675                if (new != tp->inattr) {
1676                        tp->inattr = new;
1677                        tty3270_update_prompt(tp, NULL, 0);
1678                        tty3270_set_timer(tp, 1);
1679                }
1680        }
1681        spin_unlock_bh(&tp->view.lock);
1682}
1683
1684/*
1685 * Disable reading from a 3270 tty
1686 */
1687static void
1688tty3270_throttle(struct tty_struct * tty)
1689{
1690        struct tty3270 *tp;
1691
1692        tp = tty->driver_data;
1693        if (!tp)
1694                return;
1695        tp->throttle = 1;
1696}
1697
1698/*
1699 * Enable reading from a 3270 tty
1700 */
1701static void
1702tty3270_unthrottle(struct tty_struct * tty)
1703{
1704        struct tty3270 *tp;
1705
1706        tp = tty->driver_data;
1707        if (!tp)
1708                return;
1709        tp->throttle = 0;
1710        if (tp->attn)
1711                tty3270_issue_read(tp, 1);
1712}
1713
1714/*
1715 * Hang up the tty device.
1716 */
1717static void
1718tty3270_hangup(struct tty_struct *tty)
1719{
1720        // FIXME: implement
1721}
1722
1723static void
1724tty3270_wait_until_sent(struct tty_struct *tty, int timeout)
1725{
1726}
1727
1728static int
1729tty3270_ioctl(struct tty_struct *tty, struct file *file,
1730              unsigned int cmd, unsigned long arg)
1731{
1732        struct tty3270 *tp;
1733
1734        tp = tty->driver_data;
1735        if (!tp)
1736                return -ENODEV;
1737        if (tty->flags & (1 << TTY_IO_ERROR))
1738                return -EIO;
1739        return kbd_ioctl(tp->kbd, file, cmd, arg);
1740}
1741
1742static const struct tty_operations tty3270_ops = {
1743        .open = tty3270_open,
1744        .close = tty3270_close,
1745        .write = tty3270_write,
1746        .put_char = tty3270_put_char,
1747        .flush_chars = tty3270_flush_chars,
1748        .write_room = tty3270_write_room,
1749        .chars_in_buffer = tty3270_chars_in_buffer,
1750        .flush_buffer = tty3270_flush_buffer,
1751        .throttle = tty3270_throttle,
1752        .unthrottle = tty3270_unthrottle,
1753        .hangup = tty3270_hangup,
1754        .wait_until_sent = tty3270_wait_until_sent,
1755        .ioctl = tty3270_ioctl,
1756        .set_termios = tty3270_set_termios
1757};
1758
1759static void tty3270_notifier(int index, int active)
1760{
1761        if (active)
1762                tty_register_device(tty3270_driver, index, NULL);
1763        else
1764                tty_unregister_device(tty3270_driver, index);
1765}
1766
1767/*
1768 * 3270 tty registration code called from tty_init().
1769 * Most kernel services (incl. kmalloc) are available at this poimt.
1770 */
1771static int __init tty3270_init(void)
1772{
1773        struct tty_driver *driver;
1774        int ret;
1775
1776        driver = alloc_tty_driver(RAW3270_MAXDEVS);
1777        if (!driver)
1778                return -ENOMEM;
1779
1780        /*
1781         * Initialize the tty_driver structure
1782         * Entries in tty3270_driver that are NOT initialized:
1783         * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
1784         */
1785        driver->owner = THIS_MODULE;
1786        driver->driver_name = "ttyTUB";
1787        driver->name = "ttyTUB";
1788        driver->major = IBM_TTY3270_MAJOR;
1789        driver->minor_start = RAW3270_FIRSTMINOR;
1790        driver->type = TTY_DRIVER_TYPE_SYSTEM;
1791        driver->subtype = SYSTEM_TYPE_TTY;
1792        driver->init_termios = tty_std_termios;
1793        driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_DYNAMIC_DEV;
1794        tty_set_operations(driver, &tty3270_ops);
1795        ret = tty_register_driver(driver);
1796        if (ret) {
1797                printk(KERN_ERR "tty3270 registration failed with %d\n", ret);
1798                put_tty_driver(driver);
1799                return ret;
1800        }
1801        tty3270_driver = driver;
1802        ret = raw3270_register_notifier(tty3270_notifier);
1803        if (ret) {
1804                printk(KERN_ERR "tty3270 notifier registration failed "
1805                       "with %d\n", ret);
1806                put_tty_driver(driver);
1807                return ret;
1808
1809        }
1810        return 0;
1811}
1812
1813static void __exit
1814tty3270_exit(void)
1815{
1816        struct tty_driver *driver;
1817
1818        raw3270_unregister_notifier(tty3270_notifier);
1819        driver = tty3270_driver;
1820        tty3270_driver = NULL;
1821        tty_unregister_driver(driver);
1822        tty3270_del_views();
1823}
1824
1825MODULE_LICENSE("GPL");
1826MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR);
1827
1828module_init(tty3270_init);
1829module_exit(tty3270_exit);
1830