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(unsigned long data)
 296{
 297        static char kreset_data = TW_KR;
 298        struct raw3270_request *rrq;
 299        struct con3270 *cp;
 300        unsigned long flags;
 301        int nr_up, deactivate;
 302
 303        rrq = (struct raw3270_request *)data;
 304        cp = (struct con3270 *) rrq->view;
 305        spin_lock_irqsave(&cp->view.lock, flags);
 306        nr_up = cp->nr_up;
 307        deactivate = 0;
 308        /* Check aid byte. */
 309        switch (cp->input->string[0]) {
 310        case 0x7d:      /* enter: jump to bottom. */
 311                nr_up = 0;
 312                break;
 313        case 0xf3:      /* PF3: deactivate the console view. */
 314                deactivate = 1;
 315                break;
 316        case 0x6d:      /* clear: start from scratch. */
 317                cp->update_flags = CON_UPDATE_ALL;
 318                con3270_set_timer(cp, 1);
 319                break;
 320        case 0xf7:      /* PF7: do a page up in the console log. */
 321                nr_up += cp->view.rows - 2;
 322                if (nr_up + cp->view.rows - 1 > cp->nr_lines) {
 323                        nr_up = cp->nr_lines - cp->view.rows + 1;
 324                        if (nr_up < 0)
 325                                nr_up = 0;
 326                }
 327                break;
 328        case 0xf8:      /* PF8: do a page down in the console log. */
 329                nr_up -= cp->view.rows - 2;
 330                if (nr_up < 0)
 331                        nr_up = 0;
 332                break;
 333        }
 334        if (nr_up != cp->nr_up) {
 335                cp->nr_up = nr_up;
 336                con3270_rebuild_update(cp);
 337                con3270_update_status(cp);
 338                con3270_set_timer(cp, 1);
 339        }
 340        spin_unlock_irqrestore(&cp->view.lock, flags);
 341
 342        /* Start keyboard reset command. */
 343        raw3270_request_reset(cp->kreset);
 344        raw3270_request_set_cmd(cp->kreset, TC_WRITE);
 345        raw3270_request_add_data(cp->kreset, &kreset_data, 1);
 346        raw3270_start(&cp->view, cp->kreset);
 347
 348        if (deactivate)
 349                raw3270_deactivate_view(&cp->view);
 350
 351        raw3270_request_reset(rrq);
 352        xchg(&cp->read, rrq);
 353        raw3270_put_view(&cp->view);
 354}
 355
 356/*
 357 * Read request completion callback.
 358 */
 359static void
 360con3270_read_callback(struct raw3270_request *rq, void *data)
 361{
 362        raw3270_get_view(rq->view);
 363        /* Schedule tasklet to pass input to tty. */
 364        tasklet_schedule(&((struct con3270 *) rq->view)->readlet);
 365}
 366
 367/*
 368 * Issue a read request. Called only from interrupt function.
 369 */
 370static void
 371con3270_issue_read(struct con3270 *cp)
 372{
 373        struct raw3270_request *rrq;
 374        int rc;
 375
 376        rrq = xchg(&cp->read, 0);
 377        if (!rrq)
 378                /* Read already scheduled. */
 379                return;
 380        rrq->callback = con3270_read_callback;
 381        rrq->callback_data = cp;
 382        raw3270_request_set_cmd(rrq, TC_READMOD);
 383        raw3270_request_set_data(rrq, cp->input->string, cp->input->len);
 384        /* Issue the read modified request. */
 385        rc = raw3270_start_irq(&cp->view, rrq);
 386        if (rc)
 387                raw3270_request_reset(rrq);
 388}
 389
 390/*
 391 * Switch to the console view.
 392 */
 393static int
 394con3270_activate(struct raw3270_view *view)
 395{
 396        struct con3270 *cp;
 397
 398        cp = (struct con3270 *) view;
 399        cp->update_flags = CON_UPDATE_ALL;
 400        con3270_set_timer(cp, 1);
 401        return 0;
 402}
 403
 404static void
 405con3270_deactivate(struct raw3270_view *view)
 406{
 407        struct con3270 *cp;
 408
 409        cp = (struct con3270 *) view;
 410        del_timer(&cp->timer);
 411}
 412
 413static void
 414con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb)
 415{
 416        /* Handle ATTN. Schedule tasklet to read aid. */
 417        if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
 418                con3270_issue_read(cp);
 419
 420        if (rq) {
 421                if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
 422                        rq->rc = -EIO;
 423                else
 424                        /* Normal end. Copy residual count. */
 425                        rq->rescnt = irb->scsw.cmd.count;
 426        } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
 427                /* Interrupt without an outstanding request -> update all */
 428                cp->update_flags = CON_UPDATE_ALL;
 429                con3270_set_timer(cp, 1);
 430        }
 431}
 432
 433/* Console view to a 3270 device. */
 434static struct raw3270_fn con3270_fn = {
 435        .activate = con3270_activate,
 436        .deactivate = con3270_deactivate,
 437        .intv = (void *) con3270_irq
 438};
 439
 440static inline void
 441con3270_cline_add(struct con3270 *cp)
 442{
 443        if (!list_empty(&cp->cline->list))
 444                /* Already added. */
 445                return;
 446        list_add_tail(&cp->cline->list, &cp->lines);
 447        cp->nr_lines++;
 448        con3270_rebuild_update(cp);
 449}
 450
 451static inline void
 452con3270_cline_insert(struct con3270 *cp, unsigned char c)
 453{
 454        cp->cline->string[cp->cline->len++] = 
 455                cp->view.ascebc[(c < ' ') ? ' ' : c];
 456        if (list_empty(&cp->cline->update)) {
 457                list_add_tail(&cp->cline->update, &cp->update);
 458                cp->update_flags |= CON_UPDATE_LIST;
 459        }
 460}
 461
 462static inline void
 463con3270_cline_end(struct con3270 *cp)
 464{
 465        struct string *s;
 466        unsigned int size;
 467
 468        /* Copy cline. */
 469        size = (cp->cline->len < cp->view.cols - 5) ?
 470                cp->cline->len + 4 : cp->view.cols;
 471        s = con3270_alloc_string(cp, size);
 472        memcpy(s->string, cp->cline->string, cp->cline->len);
 473        if (cp->cline->len < cp->view.cols - 5) {
 474                s->string[s->len - 4] = TO_RA;
 475                s->string[s->len - 1] = 0;
 476        } else {
 477                while (--size >= cp->cline->len)
 478                        s->string[size] = cp->view.ascebc[' '];
 479        }
 480        /* Replace cline with allocated line s and reset cline. */
 481        list_add(&s->list, &cp->cline->list);
 482        list_del_init(&cp->cline->list);
 483        if (!list_empty(&cp->cline->update)) {
 484                list_add(&s->update, &cp->cline->update);
 485                list_del_init(&cp->cline->update);
 486        }
 487        cp->cline->len = 0;
 488}
 489
 490/*
 491 * Write a string to the 3270 console
 492 */
 493static void
 494con3270_write(struct console *co, const char *str, unsigned int count)
 495{
 496        struct con3270 *cp;
 497        unsigned long flags;
 498        unsigned char c;
 499
 500        cp = condev;
 501        spin_lock_irqsave(&cp->view.lock, flags);
 502        while (count-- > 0) {
 503                c = *str++;
 504                if (cp->cline->len == 0)
 505                        con3270_cline_add(cp);
 506                if (c != '\n')
 507                        con3270_cline_insert(cp, c);
 508                if (c == '\n' || cp->cline->len >= cp->view.cols)
 509                        con3270_cline_end(cp);
 510        }
 511        /* Setup timer to output current console buffer after 1/10 second */
 512        cp->nr_up = 0;
 513        if (cp->view.dev && !timer_pending(&cp->timer))
 514                con3270_set_timer(cp, HZ/10);
 515        spin_unlock_irqrestore(&cp->view.lock,flags);
 516}
 517
 518static struct tty_driver *
 519con3270_device(struct console *c, int *index)
 520{
 521        *index = c->index;
 522        return tty3270_driver;
 523}
 524
 525/*
 526 * Wait for end of write request.
 527 */
 528static void
 529con3270_wait_write(struct con3270 *cp)
 530{
 531        while (!cp->write) {
 532                raw3270_wait_cons_dev(cp->view.dev);
 533                barrier();
 534        }
 535}
 536
 537/*
 538 * The below function is called as a panic/reboot notifier before the
 539 * system enters a disabled, endless loop.
 540 *
 541 * Notice we must use the spin_trylock() alternative, to prevent lockups
 542 * in atomic context (panic routine runs with secondary CPUs, local IRQs
 543 * and preemption disabled).
 544 */
 545static int con3270_notify(struct notifier_block *self,
 546                          unsigned long event, void *data)
 547{
 548        struct con3270 *cp;
 549        unsigned long flags;
 550
 551        cp = condev;
 552        if (!cp->view.dev)
 553                return NOTIFY_DONE;
 554        if (!raw3270_view_lock_unavailable(&cp->view))
 555                raw3270_activate_view(&cp->view);
 556        if (!spin_trylock_irqsave(&cp->view.lock, flags))
 557                return NOTIFY_DONE;
 558        con3270_wait_write(cp);
 559        cp->nr_up = 0;
 560        con3270_rebuild_update(cp);
 561        con3270_update_status(cp);
 562        while (cp->update_flags != 0) {
 563                spin_unlock_irqrestore(&cp->view.lock, flags);
 564                con3270_update(&cp->timer);
 565                spin_lock_irqsave(&cp->view.lock, flags);
 566                con3270_wait_write(cp);
 567        }
 568        spin_unlock_irqrestore(&cp->view.lock, flags);
 569
 570        return NOTIFY_DONE;
 571}
 572
 573static struct notifier_block on_panic_nb = {
 574        .notifier_call = con3270_notify,
 575        .priority = INT_MIN + 1, /* run the callback late */
 576};
 577
 578static struct notifier_block on_reboot_nb = {
 579        .notifier_call = con3270_notify,
 580        .priority = INT_MIN + 1, /* run the callback late */
 581};
 582
 583/*
 584 *  The console structure for the 3270 console
 585 */
 586static struct console con3270 = {
 587        .name    = "tty3270",
 588        .write   = con3270_write,
 589        .device  = con3270_device,
 590        .flags   = CON_PRINTBUFFER,
 591};
 592
 593/*
 594 * 3270 console initialization code called from console_init().
 595 */
 596static int __init
 597con3270_init(void)
 598{
 599        struct raw3270 *rp;
 600        void *cbuf;
 601        int i;
 602
 603        /* Check if 3270 is to be the console */
 604        if (!CONSOLE_IS_3270)
 605                return -ENODEV;
 606
 607        /* Set the console mode for VM */
 608        if (MACHINE_IS_VM) {
 609                cpcmd("TERM CONMODE 3270", NULL, 0, NULL);
 610                cpcmd("TERM AUTOCR OFF", NULL, 0, NULL);
 611        }
 612
 613        rp = raw3270_setup_console();
 614        if (IS_ERR(rp))
 615                return PTR_ERR(rp);
 616
 617        condev = kzalloc(sizeof(struct con3270), GFP_KERNEL | GFP_DMA);
 618        if (!condev)
 619                return -ENOMEM;
 620        condev->view.dev = rp;
 621
 622        condev->read = raw3270_request_alloc(0);
 623        condev->read->callback = con3270_read_callback;
 624        condev->read->callback_data = condev;
 625        condev->write = raw3270_request_alloc(CON3270_OUTPUT_BUFFER_SIZE);
 626        condev->kreset = raw3270_request_alloc(1);
 627
 628        INIT_LIST_HEAD(&condev->lines);
 629        INIT_LIST_HEAD(&condev->update);
 630        timer_setup(&condev->timer, con3270_update, 0);
 631        tasklet_init(&condev->readlet, con3270_read_tasklet,
 632                     (unsigned long) condev->read);
 633
 634        raw3270_add_view(&condev->view, &con3270_fn, 1, RAW3270_VIEW_LOCK_IRQ);
 635
 636        INIT_LIST_HEAD(&condev->freemem);
 637        for (i = 0; i < CON3270_STRING_PAGES; i++) {
 638                cbuf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
 639                add_string_memory(&condev->freemem, cbuf, PAGE_SIZE);
 640        }
 641        condev->cline = alloc_string(&condev->freemem, condev->view.cols);
 642        condev->cline->len = 0;
 643        con3270_create_status(condev);
 644        condev->input = alloc_string(&condev->freemem, 80);
 645        atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
 646        register_reboot_notifier(&on_reboot_nb);
 647        register_console(&con3270);
 648        return 0;
 649}
 650
 651console_initcall(con3270_init);
 652