linux/drivers/s390/char/con3270.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * IBM/3270 Driver - console view.
   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, 2009
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/console.h>
  13#include <linux/init.h>
  14#include <linux/interrupt.h>
  15#include <linux/list.h>
  16#include <linux/panic_notifier.h>
  17#include <linux/types.h>
  18#include <linux/slab.h>
  19#include <linux/err.h>
  20#include <linux/reboot.h>
  21
  22#include <asm/ccwdev.h>
  23#include <asm/cio.h>
  24#include <asm/cpcmd.h>
  25#include <asm/ebcdic.h>
  26
  27#include "raw3270.h"
  28#include "tty3270.h"
  29#include "ctrlchar.h"
  30
  31#define CON3270_OUTPUT_BUFFER_SIZE 1024
  32#define CON3270_STRING_PAGES 4
  33
  34static struct raw3270_fn con3270_fn;
  35
  36static bool auto_update = true;
  37module_param(auto_update, bool, 0);
  38
  39/*
  40 * Main 3270 console view data structure.
  41 */
  42struct con3270 {
  43        struct raw3270_view view;
  44        struct list_head freemem;       /* list of free memory for strings. */
  45
  46        /* Output stuff. */
  47        struct list_head lines;         /* list of lines. */
  48        struct list_head update;        /* list of lines to update. */
  49        int line_nr;                    /* line number for next update. */
  50        int nr_lines;                   /* # lines in list. */
  51        int nr_up;                      /* # lines up in history. */
  52        unsigned long update_flags;     /* Update indication bits. */
  53        struct string *cline;           /* current output line. */
  54        struct string *status;          /* last line of display. */
  55        struct raw3270_request *write;  /* single write request. */
  56        struct timer_list timer;
  57
  58        /* Input stuff. */
  59        struct string *input;           /* input string for read request. */
  60        struct raw3270_request *read;   /* single read request. */
  61        struct raw3270_request *kreset; /* single keyboard reset request. */
  62        struct tasklet_struct readlet;  /* tasklet to issue read request. */
  63};
  64
  65static struct con3270 *condev;
  66
  67/* con3270->update_flags. See con3270_update for details. */
  68#define CON_UPDATE_ERASE        1       /* Use EWRITEA instead of WRITE. */
  69#define CON_UPDATE_LIST         2       /* Update lines in tty3270->update. */
  70#define CON_UPDATE_STATUS       4       /* Update status line. */
  71#define CON_UPDATE_ALL          8       /* Recreate screen. */
  72
  73static void con3270_update(struct timer_list *);
  74
  75/*
  76 * Setup timeout for a device. On timeout trigger an update.
  77 */
  78static void con3270_set_timer(struct con3270 *cp, int expires)
  79{
  80        if (expires == 0)
  81                del_timer(&cp->timer);
  82        else
  83                mod_timer(&cp->timer, jiffies + expires);
  84}
  85
  86/*
  87 * The status line is the last line of the screen. It shows the string
  88 * "console view" in the lower left corner and "Running"/"More..."/"Holding"
  89 * in the lower right corner of the screen.
  90 */
  91static void
  92con3270_update_status(struct con3270 *cp)
  93{
  94        char *str;
  95
  96        str = (cp->nr_up != 0) ? "History" : "Running";
  97        memcpy(cp->status->string + 24, str, 7);
  98        codepage_convert(cp->view.ascebc, cp->status->string + 24, 7);
  99        cp->update_flags |= CON_UPDATE_STATUS;
 100}
 101
 102static void
 103con3270_create_status(struct con3270 *cp)
 104{
 105        static const unsigned char blueprint[] =
 106                { TO_SBA, 0, 0, TO_SF,TF_LOG,TO_SA,TAT_COLOR, TAC_GREEN,
 107                  'c','o','n','s','o','l','e',' ','v','i','e','w',
 108                  TO_RA,0,0,0,'R','u','n','n','i','n','g',TO_SF,TF_LOG };
 109
 110        cp->status = alloc_string(&cp->freemem, sizeof(blueprint));
 111        /* Copy blueprint to status line */
 112        memcpy(cp->status->string, blueprint, sizeof(blueprint));
 113        /* Set TO_RA addresses. */
 114        raw3270_buffer_address(cp->view.dev, cp->status->string + 1,
 115                               cp->view.cols * (cp->view.rows - 1));
 116        raw3270_buffer_address(cp->view.dev, cp->status->string + 21,
 117                               cp->view.cols * cp->view.rows - 8);
 118        /* Convert strings to ebcdic. */
 119        codepage_convert(cp->view.ascebc, cp->status->string + 8, 12);
 120        codepage_convert(cp->view.ascebc, cp->status->string + 24, 7);
 121}
 122
 123/*
 124 * Set output offsets to 3270 datastream fragment of a console string.
 125 */
 126static void
 127con3270_update_string(struct con3270 *cp, struct string *s, int nr)
 128{
 129        if (s->len < 4) {
 130                /* This indicates a bug, but printing a warning would
 131                 * cause a deadlock. */
 132                return;
 133        }
 134        if (s->string[s->len - 4] != TO_RA)
 135                return;
 136        raw3270_buffer_address(cp->view.dev, s->string + s->len - 3,
 137                               cp->view.cols * (nr + 1));
 138}
 139
 140/*
 141 * Rebuild update list to print all lines.
 142 */
 143static void
 144con3270_rebuild_update(struct con3270 *cp)
 145{
 146        struct string *s, *n;
 147        int nr;
 148
 149        /* 
 150         * Throw away update list and create a new one,
 151         * containing all lines that will fit on the screen.
 152         */
 153        list_for_each_entry_safe(s, n, &cp->update, update)
 154                list_del_init(&s->update);
 155        nr = cp->view.rows - 2 + cp->nr_up;
 156        list_for_each_entry_reverse(s, &cp->lines, list) {
 157                if (nr < cp->view.rows - 1)
 158                        list_add(&s->update, &cp->update);
 159                if (--nr < 0)
 160                        break;
 161        }
 162        cp->line_nr = 0;
 163        cp->update_flags |= CON_UPDATE_LIST;
 164}
 165
 166/*
 167 * Alloc string for size bytes. Free strings from history if necessary.
 168 */
 169static struct string *
 170con3270_alloc_string(struct con3270 *cp, size_t size)
 171{
 172        struct string *s, *n;
 173
 174        s = alloc_string(&cp->freemem, size);
 175        if (s)
 176                return s;
 177        list_for_each_entry_safe(s, n, &cp->lines, list) {
 178                list_del(&s->list);
 179                if (!list_empty(&s->update))
 180                        list_del(&s->update);
 181                cp->nr_lines--;
 182                if (free_string(&cp->freemem, s) >= size)
 183                        break;
 184        }
 185        s = alloc_string(&cp->freemem, size);
 186        BUG_ON(!s);
 187        if (cp->nr_up != 0 && cp->nr_up + cp->view.rows > cp->nr_lines) {
 188                cp->nr_up = cp->nr_lines - cp->view.rows + 1;
 189                con3270_rebuild_update(cp);
 190                con3270_update_status(cp);
 191        }
 192        return s;
 193}
 194
 195/*
 196 * Write completion callback.
 197 */
 198static void
 199con3270_write_callback(struct raw3270_request *rq, void *data)
 200{
 201        raw3270_request_reset(rq);
 202        xchg(&((struct con3270 *) rq->view)->write, rq);
 203}
 204
 205/*
 206 * Update console display.
 207 */
 208static void
 209con3270_update(struct timer_list *t)
 210{
 211        struct con3270 *cp = from_timer(cp, t, timer);
 212        struct raw3270_request *wrq;
 213        char wcc, prolog[6];
 214        unsigned long flags;
 215        unsigned long updated;
 216        struct string *s, *n;
 217        int rc;
 218
 219        if (!auto_update && !raw3270_view_active(&cp->view))
 220                return;
 221        if (cp->view.dev)
 222                raw3270_activate_view(&cp->view);
 223
 224        wrq = xchg(&cp->write, 0);
 225        if (!wrq) {
 226                con3270_set_timer(cp, 1);
 227                return;
 228        }
 229
 230        spin_lock_irqsave(&cp->view.lock, flags);
 231        updated = 0;
 232        if (cp->update_flags & CON_UPDATE_ALL) {
 233                con3270_rebuild_update(cp);
 234                con3270_update_status(cp);
 235                cp->update_flags = CON_UPDATE_ERASE | CON_UPDATE_LIST |
 236                        CON_UPDATE_STATUS;
 237        }
 238        if (cp->update_flags & CON_UPDATE_ERASE) {
 239                /* Use erase write alternate to initialize display. */
 240                raw3270_request_set_cmd(wrq, TC_EWRITEA);
 241                updated |= CON_UPDATE_ERASE;
 242        } else
 243                raw3270_request_set_cmd(wrq, TC_WRITE);
 244
 245        wcc = TW_NONE;
 246        raw3270_request_add_data(wrq, &wcc, 1);
 247
 248        /*
 249         * Update status line.
 250         */
 251        if (cp->update_flags & CON_UPDATE_STATUS)
 252                if (raw3270_request_add_data(wrq, cp->status->string,
 253                                             cp->status->len) == 0)
 254                        updated |= CON_UPDATE_STATUS;
 255
 256        if (cp->update_flags & CON_UPDATE_LIST) {
 257                prolog[0] = TO_SBA;
 258                prolog[3] = TO_SA;
 259                prolog[4] = TAT_COLOR;
 260                prolog[5] = TAC_TURQ;
 261                raw3270_buffer_address(cp->view.dev, prolog + 1,
 262                                       cp->view.cols * cp->line_nr);
 263                raw3270_request_add_data(wrq, prolog, 6);
 264                /* Write strings in the update list to the screen. */
 265                list_for_each_entry_safe(s, n, &cp->update, update) {
 266                        if (s != cp->cline)
 267                                con3270_update_string(cp, s, cp->line_nr);
 268                        if (raw3270_request_add_data(wrq, s->string,
 269                                                     s->len) != 0)
 270                                break;
 271                        list_del_init(&s->update);
 272                        if (s != cp->cline)
 273                                cp->line_nr++;
 274                }
 275                if (list_empty(&cp->update))
 276                        updated |= CON_UPDATE_LIST;
 277        }
 278        wrq->callback = con3270_write_callback;
 279        rc = raw3270_start(&cp->view, wrq);
 280        if (rc == 0) {
 281                cp->update_flags &= ~updated;
 282                if (cp->update_flags)
 283                        con3270_set_timer(cp, 1);
 284        } else {
 285                raw3270_request_reset(wrq);
 286                xchg(&cp->write, wrq);
 287        }
 288        spin_unlock_irqrestore(&cp->view.lock, flags);
 289}
 290
 291/*
 292 * Read tasklet.
 293 */
 294static void
 295con3270_read_tasklet(struct raw3270_request *rrq)
 296{
 297        static char kreset_data = TW_KR;
 298        struct con3270 *cp;
 299        unsigned long flags;
 300        int nr_up, deactivate;
 301
 302        cp = (struct con3270 *) rrq->view;
 303        spin_lock_irqsave(&cp->view.lock, flags);
 304        nr_up = cp->nr_up;
 305        deactivate = 0;
 306        /* Check aid byte. */
 307        switch (cp->input->string[0]) {
 308        case 0x7d:      /* enter: jump to bottom. */
 309                nr_up = 0;
 310                break;
 311        case 0xf3:      /* PF3: deactivate the console view. */
 312                deactivate = 1;
 313                break;
 314        case 0x6d:      /* clear: start from scratch. */
 315                cp->update_flags = CON_UPDATE_ALL;
 316                con3270_set_timer(cp, 1);
 317                break;
 318        case 0xf7:      /* PF7: do a page up in the console log. */
 319                nr_up += cp->view.rows - 2;
 320                if (nr_up + cp->view.rows - 1 > cp->nr_lines) {
 321                        nr_up = cp->nr_lines - cp->view.rows + 1;
 322                        if (nr_up < 0)
 323                                nr_up = 0;
 324                }
 325                break;
 326        case 0xf8:      /* PF8: do a page down in the console log. */
 327                nr_up -= cp->view.rows - 2;
 328                if (nr_up < 0)
 329                        nr_up = 0;
 330                break;
 331        }
 332        if (nr_up != cp->nr_up) {
 333                cp->nr_up = nr_up;
 334                con3270_rebuild_update(cp);
 335                con3270_update_status(cp);
 336                con3270_set_timer(cp, 1);
 337        }
 338        spin_unlock_irqrestore(&cp->view.lock, flags);
 339
 340        /* Start keyboard reset command. */
 341        raw3270_request_reset(cp->kreset);
 342        raw3270_request_set_cmd(cp->kreset, TC_WRITE);
 343        raw3270_request_add_data(cp->kreset, &kreset_data, 1);
 344        raw3270_start(&cp->view, cp->kreset);
 345
 346        if (deactivate)
 347                raw3270_deactivate_view(&cp->view);
 348
 349        raw3270_request_reset(rrq);
 350        xchg(&cp->read, rrq);
 351        raw3270_put_view(&cp->view);
 352}
 353
 354/*
 355 * Read request completion callback.
 356 */
 357static void
 358con3270_read_callback(struct raw3270_request *rq, void *data)
 359{
 360        raw3270_get_view(rq->view);
 361        /* Schedule tasklet to pass input to tty. */
 362        tasklet_schedule(&((struct con3270 *) rq->view)->readlet);
 363}
 364
 365/*
 366 * Issue a read request. Called only from interrupt function.
 367 */
 368static void
 369con3270_issue_read(struct con3270 *cp)
 370{
 371        struct raw3270_request *rrq;
 372        int rc;
 373
 374        rrq = xchg(&cp->read, 0);
 375        if (!rrq)
 376                /* Read already scheduled. */
 377                return;
 378        rrq->callback = con3270_read_callback;
 379        rrq->callback_data = cp;
 380        raw3270_request_set_cmd(rrq, TC_READMOD);
 381        raw3270_request_set_data(rrq, cp->input->string, cp->input->len);
 382        /* Issue the read modified request. */
 383        rc = raw3270_start_irq(&cp->view, rrq);
 384        if (rc)
 385                raw3270_request_reset(rrq);
 386}
 387
 388/*
 389 * Switch to the console view.
 390 */
 391static int
 392con3270_activate(struct raw3270_view *view)
 393{
 394        struct con3270 *cp;
 395
 396        cp = (struct con3270 *) view;
 397        cp->update_flags = CON_UPDATE_ALL;
 398        con3270_set_timer(cp, 1);
 399        return 0;
 400}
 401
 402static void
 403con3270_deactivate(struct raw3270_view *view)
 404{
 405        struct con3270 *cp;
 406
 407        cp = (struct con3270 *) view;
 408        del_timer(&cp->timer);
 409}
 410
 411static void
 412con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb)
 413{
 414        /* Handle ATTN. Schedule tasklet to read aid. */
 415        if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
 416                con3270_issue_read(cp);
 417
 418        if (rq) {
 419                if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
 420                        rq->rc = -EIO;
 421                else
 422                        /* Normal end. Copy residual count. */
 423                        rq->rescnt = irb->scsw.cmd.count;
 424        } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
 425                /* Interrupt without an outstanding request -> update all */
 426                cp->update_flags = CON_UPDATE_ALL;
 427                con3270_set_timer(cp, 1);
 428        }
 429}
 430
 431/* Console view to a 3270 device. */
 432static struct raw3270_fn con3270_fn = {
 433        .activate = con3270_activate,
 434        .deactivate = con3270_deactivate,
 435        .intv = (void *) con3270_irq
 436};
 437
 438static inline void
 439con3270_cline_add(struct con3270 *cp)
 440{
 441        if (!list_empty(&cp->cline->list))
 442                /* Already added. */
 443                return;
 444        list_add_tail(&cp->cline->list, &cp->lines);
 445        cp->nr_lines++;
 446        con3270_rebuild_update(cp);
 447}
 448
 449static inline void
 450con3270_cline_insert(struct con3270 *cp, unsigned char c)
 451{
 452        cp->cline->string[cp->cline->len++] = 
 453                cp->view.ascebc[(c < ' ') ? ' ' : c];
 454        if (list_empty(&cp->cline->update)) {
 455                list_add_tail(&cp->cline->update, &cp->update);
 456                cp->update_flags |= CON_UPDATE_LIST;
 457        }
 458}
 459
 460static inline void
 461con3270_cline_end(struct con3270 *cp)
 462{
 463        struct string *s;
 464        unsigned int size;
 465
 466        /* Copy cline. */
 467        size = (cp->cline->len < cp->view.cols - 5) ?
 468                cp->cline->len + 4 : cp->view.cols;
 469        s = con3270_alloc_string(cp, size);
 470        memcpy(s->string, cp->cline->string, cp->cline->len);
 471        if (cp->cline->len < cp->view.cols - 5) {
 472                s->string[s->len - 4] = TO_RA;
 473                s->string[s->len - 1] = 0;
 474        } else {
 475                while (--size >= cp->cline->len)
 476                        s->string[size] = cp->view.ascebc[' '];
 477        }
 478        /* Replace cline with allocated line s and reset cline. */
 479        list_add(&s->list, &cp->cline->list);
 480        list_del_init(&cp->cline->list);
 481        if (!list_empty(&cp->cline->update)) {
 482                list_add(&s->update, &cp->cline->update);
 483                list_del_init(&cp->cline->update);
 484        }
 485        cp->cline->len = 0;
 486}
 487
 488/*
 489 * Write a string to the 3270 console
 490 */
 491static void
 492con3270_write(struct console *co, const char *str, unsigned int count)
 493{
 494        struct con3270 *cp;
 495        unsigned long flags;
 496        unsigned char c;
 497
 498        cp = condev;
 499        spin_lock_irqsave(&cp->view.lock, flags);
 500        while (count-- > 0) {
 501                c = *str++;
 502                if (cp->cline->len == 0)
 503                        con3270_cline_add(cp);
 504                if (c != '\n')
 505                        con3270_cline_insert(cp, c);
 506                if (c == '\n' || cp->cline->len >= cp->view.cols)
 507                        con3270_cline_end(cp);
 508        }
 509        /* Setup timer to output current console buffer after 1/10 second */
 510        cp->nr_up = 0;
 511        if (cp->view.dev && !timer_pending(&cp->timer))
 512                con3270_set_timer(cp, HZ/10);
 513        spin_unlock_irqrestore(&cp->view.lock,flags);
 514}
 515
 516static struct tty_driver *
 517con3270_device(struct console *c, int *index)
 518{
 519        *index = c->index;
 520        return tty3270_driver;
 521}
 522
 523/*
 524 * Wait for end of write request.
 525 */
 526static void
 527con3270_wait_write(struct con3270 *cp)
 528{
 529        while (!cp->write) {
 530                raw3270_wait_cons_dev(cp->view.dev);
 531                barrier();
 532        }
 533}
 534
 535/*
 536 * panic() calls con3270_flush through a panic_notifier
 537 * before the system enters a disabled, endless loop.
 538 */
 539static void
 540con3270_flush(void)
 541{
 542        struct con3270 *cp;
 543        unsigned long flags;
 544
 545        cp = condev;
 546        if (!cp->view.dev)
 547                return;
 548        raw3270_activate_view(&cp->view);
 549        spin_lock_irqsave(&cp->view.lock, flags);
 550        con3270_wait_write(cp);
 551        cp->nr_up = 0;
 552        con3270_rebuild_update(cp);
 553        con3270_update_status(cp);
 554        while (cp->update_flags != 0) {
 555                spin_unlock_irqrestore(&cp->view.lock, flags);
 556                con3270_update(&cp->timer);
 557                spin_lock_irqsave(&cp->view.lock, flags);
 558                con3270_wait_write(cp);
 559        }
 560        spin_unlock_irqrestore(&cp->view.lock, flags);
 561}
 562
 563static int con3270_notify(struct notifier_block *self,
 564                          unsigned long event, void *data)
 565{
 566        con3270_flush();
 567        return NOTIFY_OK;
 568}
 569
 570static struct notifier_block on_panic_nb = {
 571        .notifier_call = con3270_notify,
 572        .priority = 0,
 573};
 574
 575static struct notifier_block on_reboot_nb = {
 576        .notifier_call = con3270_notify,
 577        .priority = 0,
 578};
 579
 580/*
 581 *  The console structure for the 3270 console
 582 */
 583static struct console con3270 = {
 584        .name    = "tty3270",
 585        .write   = con3270_write,
 586        .device  = con3270_device,
 587        .flags   = CON_PRINTBUFFER,
 588};
 589
 590/*
 591 * 3270 console initialization code called from console_init().
 592 */
 593static int __init
 594con3270_init(void)
 595{
 596        struct raw3270 *rp;
 597        void *cbuf;
 598        int i;
 599
 600        /* Check if 3270 is to be the console */
 601        if (!CONSOLE_IS_3270)
 602                return -ENODEV;
 603
 604        /* Set the console mode for VM */
 605        if (MACHINE_IS_VM) {
 606                cpcmd("TERM CONMODE 3270", NULL, 0, NULL);
 607                cpcmd("TERM AUTOCR OFF", NULL, 0, NULL);
 608        }
 609
 610        rp = raw3270_setup_console();
 611        if (IS_ERR(rp))
 612                return PTR_ERR(rp);
 613
 614        condev = kzalloc(sizeof(struct con3270), GFP_KERNEL | GFP_DMA);
 615        if (!condev)
 616                return -ENOMEM;
 617        condev->view.dev = rp;
 618
 619        condev->read = raw3270_request_alloc(0);
 620        condev->read->callback = con3270_read_callback;
 621        condev->read->callback_data = condev;
 622        condev->write = raw3270_request_alloc(CON3270_OUTPUT_BUFFER_SIZE);
 623        condev->kreset = raw3270_request_alloc(1);
 624
 625        INIT_LIST_HEAD(&condev->lines);
 626        INIT_LIST_HEAD(&condev->update);
 627        timer_setup(&condev->timer, con3270_update, 0);
 628        tasklet_init(&condev->readlet, 
 629                     (void (*)(unsigned long)) con3270_read_tasklet,
 630                     (unsigned long) condev->read);
 631
 632        raw3270_add_view(&condev->view, &con3270_fn, 1, RAW3270_VIEW_LOCK_IRQ);
 633
 634        INIT_LIST_HEAD(&condev->freemem);
 635        for (i = 0; i < CON3270_STRING_PAGES; i++) {
 636                cbuf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
 637                add_string_memory(&condev->freemem, cbuf, PAGE_SIZE);
 638        }
 639        condev->cline = alloc_string(&condev->freemem, condev->view.cols);
 640        condev->cline->len = 0;
 641        con3270_create_status(condev);
 642        condev->input = alloc_string(&condev->freemem, 80);
 643        atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
 644        register_reboot_notifier(&on_reboot_nb);
 645        register_console(&con3270);
 646        return 0;
 647}
 648
 649console_initcall(con3270_init);
 650