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