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