root/drivers/s390/char/con3270.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. con3270_set_timer
  2. con3270_update_status
  3. con3270_create_status
  4. con3270_update_string
  5. con3270_rebuild_update
  6. con3270_alloc_string
  7. con3270_write_callback
  8. con3270_update
  9. con3270_read_tasklet
  10. con3270_read_callback
  11. con3270_issue_read
  12. con3270_activate
  13. con3270_deactivate
  14. con3270_irq
  15. con3270_cline_add
  16. con3270_cline_insert
  17. con3270_cline_end
  18. con3270_write
  19. con3270_device
  20. con3270_wait_write
  21. con3270_flush
  22. con3270_notify
  23. con3270_init

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

/* [<][>][^][v][top][bottom][index][help] */