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