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