Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * IBM/3270 Driver - console view.
0004  *
0005  * Author(s):
0006  *   Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
0007  *   Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
0008  *     Copyright IBM Corp. 2003, 2009
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/console.h>
0013 #include <linux/init.h>
0014 #include <linux/interrupt.h>
0015 #include <linux/list.h>
0016 #include <linux/panic_notifier.h>
0017 #include <linux/types.h>
0018 #include <linux/slab.h>
0019 #include <linux/err.h>
0020 #include <linux/reboot.h>
0021 
0022 #include <asm/ccwdev.h>
0023 #include <asm/cio.h>
0024 #include <asm/cpcmd.h>
0025 #include <asm/ebcdic.h>
0026 
0027 #include "raw3270.h"
0028 #include "tty3270.h"
0029 #include "ctrlchar.h"
0030 
0031 #define CON3270_OUTPUT_BUFFER_SIZE 1024
0032 #define CON3270_STRING_PAGES 4
0033 
0034 static struct raw3270_fn con3270_fn;
0035 
0036 static bool auto_update = true;
0037 module_param(auto_update, bool, 0);
0038 
0039 /*
0040  * Main 3270 console view data structure.
0041  */
0042 struct con3270 {
0043     struct raw3270_view view;
0044     struct list_head freemem;   /* list of free memory for strings. */
0045 
0046     /* Output stuff. */
0047     struct list_head lines;     /* list of lines. */
0048     struct list_head update;    /* list of lines to update. */
0049     int line_nr;            /* line number for next update. */
0050     int nr_lines;           /* # lines in list. */
0051     int nr_up;          /* # lines up in history. */
0052     unsigned long update_flags; /* Update indication bits. */
0053     struct string *cline;       /* current output line. */
0054     struct string *status;      /* last line of display. */
0055     struct raw3270_request *write;  /* single write request. */
0056     struct timer_list timer;
0057 
0058     /* Input stuff. */
0059     struct string *input;       /* input string for read request. */
0060     struct raw3270_request *read;   /* single read request. */
0061     struct raw3270_request *kreset; /* single keyboard reset request. */
0062     struct tasklet_struct readlet;  /* tasklet to issue read request. */
0063 };
0064 
0065 static struct con3270 *condev;
0066 
0067 /* con3270->update_flags. See con3270_update for details. */
0068 #define CON_UPDATE_ERASE    1   /* Use EWRITEA instead of WRITE. */
0069 #define CON_UPDATE_LIST     2   /* Update lines in tty3270->update. */
0070 #define CON_UPDATE_STATUS   4   /* Update status line. */
0071 #define CON_UPDATE_ALL      8   /* Recreate screen. */
0072 
0073 static void con3270_update(struct timer_list *);
0074 
0075 /*
0076  * Setup timeout for a device. On timeout trigger an update.
0077  */
0078 static void con3270_set_timer(struct con3270 *cp, int expires)
0079 {
0080     if (expires == 0)
0081         del_timer(&cp->timer);
0082     else
0083         mod_timer(&cp->timer, jiffies + expires);
0084 }
0085 
0086 /*
0087  * The status line is the last line of the screen. It shows the string
0088  * "console view" in the lower left corner and "Running"/"More..."/"Holding"
0089  * in the lower right corner of the screen.
0090  */
0091 static void
0092 con3270_update_status(struct con3270 *cp)
0093 {
0094     char *str;
0095 
0096     str = (cp->nr_up != 0) ? "History" : "Running";
0097     memcpy(cp->status->string + 24, str, 7);
0098     codepage_convert(cp->view.ascebc, cp->status->string + 24, 7);
0099     cp->update_flags |= CON_UPDATE_STATUS;
0100 }
0101 
0102 static void
0103 con3270_create_status(struct con3270 *cp)
0104 {
0105     static const unsigned char blueprint[] =
0106         { TO_SBA, 0, 0, TO_SF,TF_LOG,TO_SA,TAT_COLOR, TAC_GREEN,
0107           'c','o','n','s','o','l','e',' ','v','i','e','w',
0108           TO_RA,0,0,0,'R','u','n','n','i','n','g',TO_SF,TF_LOG };
0109 
0110     cp->status = alloc_string(&cp->freemem, sizeof(blueprint));
0111     /* Copy blueprint to status line */
0112     memcpy(cp->status->string, blueprint, sizeof(blueprint));
0113     /* Set TO_RA addresses. */
0114     raw3270_buffer_address(cp->view.dev, cp->status->string + 1,
0115                    cp->view.cols * (cp->view.rows - 1));
0116     raw3270_buffer_address(cp->view.dev, cp->status->string + 21,
0117                    cp->view.cols * cp->view.rows - 8);
0118     /* Convert strings to ebcdic. */
0119     codepage_convert(cp->view.ascebc, cp->status->string + 8, 12);
0120     codepage_convert(cp->view.ascebc, cp->status->string + 24, 7);
0121 }
0122 
0123 /*
0124  * Set output offsets to 3270 datastream fragment of a console string.
0125  */
0126 static void
0127 con3270_update_string(struct con3270 *cp, struct string *s, int nr)
0128 {
0129     if (s->len < 4) {
0130         /* This indicates a bug, but printing a warning would
0131          * cause a deadlock. */
0132         return;
0133     }
0134     if (s->string[s->len - 4] != TO_RA)
0135         return;
0136     raw3270_buffer_address(cp->view.dev, s->string + s->len - 3,
0137                    cp->view.cols * (nr + 1));
0138 }
0139 
0140 /*
0141  * Rebuild update list to print all lines.
0142  */
0143 static void
0144 con3270_rebuild_update(struct con3270 *cp)
0145 {
0146     struct string *s, *n;
0147     int nr;
0148 
0149     /* 
0150      * Throw away update list and create a new one,
0151      * containing all lines that will fit on the screen.
0152      */
0153     list_for_each_entry_safe(s, n, &cp->update, update)
0154         list_del_init(&s->update);
0155     nr = cp->view.rows - 2 + cp->nr_up;
0156     list_for_each_entry_reverse(s, &cp->lines, list) {
0157         if (nr < cp->view.rows - 1)
0158             list_add(&s->update, &cp->update);
0159         if (--nr < 0)
0160             break;
0161     }
0162     cp->line_nr = 0;
0163     cp->update_flags |= CON_UPDATE_LIST;
0164 }
0165 
0166 /*
0167  * Alloc string for size bytes. Free strings from history if necessary.
0168  */
0169 static struct string *
0170 con3270_alloc_string(struct con3270 *cp, size_t size)
0171 {
0172     struct string *s, *n;
0173 
0174     s = alloc_string(&cp->freemem, size);
0175     if (s)
0176         return s;
0177     list_for_each_entry_safe(s, n, &cp->lines, list) {
0178         list_del(&s->list);
0179         if (!list_empty(&s->update))
0180             list_del(&s->update);
0181         cp->nr_lines--;
0182         if (free_string(&cp->freemem, s) >= size)
0183             break;
0184     }
0185     s = alloc_string(&cp->freemem, size);
0186     BUG_ON(!s);
0187     if (cp->nr_up != 0 && cp->nr_up + cp->view.rows > cp->nr_lines) {
0188         cp->nr_up = cp->nr_lines - cp->view.rows + 1;
0189         con3270_rebuild_update(cp);
0190         con3270_update_status(cp);
0191     }
0192     return s;
0193 }
0194 
0195 /*
0196  * Write completion callback.
0197  */
0198 static void
0199 con3270_write_callback(struct raw3270_request *rq, void *data)
0200 {
0201     raw3270_request_reset(rq);
0202     xchg(&((struct con3270 *) rq->view)->write, rq);
0203 }
0204 
0205 /*
0206  * Update console display.
0207  */
0208 static void
0209 con3270_update(struct timer_list *t)
0210 {
0211     struct con3270 *cp = from_timer(cp, t, timer);
0212     struct raw3270_request *wrq;
0213     char wcc, prolog[6];
0214     unsigned long flags;
0215     unsigned long updated;
0216     struct string *s, *n;
0217     int rc;
0218 
0219     if (!auto_update && !raw3270_view_active(&cp->view))
0220         return;
0221     if (cp->view.dev)
0222         raw3270_activate_view(&cp->view);
0223 
0224     wrq = xchg(&cp->write, 0);
0225     if (!wrq) {
0226         con3270_set_timer(cp, 1);
0227         return;
0228     }
0229 
0230     spin_lock_irqsave(&cp->view.lock, flags);
0231     updated = 0;
0232     if (cp->update_flags & CON_UPDATE_ALL) {
0233         con3270_rebuild_update(cp);
0234         con3270_update_status(cp);
0235         cp->update_flags = CON_UPDATE_ERASE | CON_UPDATE_LIST |
0236             CON_UPDATE_STATUS;
0237     }
0238     if (cp->update_flags & CON_UPDATE_ERASE) {
0239         /* Use erase write alternate to initialize display. */
0240         raw3270_request_set_cmd(wrq, TC_EWRITEA);
0241         updated |= CON_UPDATE_ERASE;
0242     } else
0243         raw3270_request_set_cmd(wrq, TC_WRITE);
0244 
0245     wcc = TW_NONE;
0246     raw3270_request_add_data(wrq, &wcc, 1);
0247 
0248     /*
0249      * Update status line.
0250      */
0251     if (cp->update_flags & CON_UPDATE_STATUS)
0252         if (raw3270_request_add_data(wrq, cp->status->string,
0253                          cp->status->len) == 0)
0254             updated |= CON_UPDATE_STATUS;
0255 
0256     if (cp->update_flags & CON_UPDATE_LIST) {
0257         prolog[0] = TO_SBA;
0258         prolog[3] = TO_SA;
0259         prolog[4] = TAT_COLOR;
0260         prolog[5] = TAC_TURQ;
0261         raw3270_buffer_address(cp->view.dev, prolog + 1,
0262                        cp->view.cols * cp->line_nr);
0263         raw3270_request_add_data(wrq, prolog, 6);
0264         /* Write strings in the update list to the screen. */
0265         list_for_each_entry_safe(s, n, &cp->update, update) {
0266             if (s != cp->cline)
0267                 con3270_update_string(cp, s, cp->line_nr);
0268             if (raw3270_request_add_data(wrq, s->string,
0269                              s->len) != 0)
0270                 break;
0271             list_del_init(&s->update);
0272             if (s != cp->cline)
0273                 cp->line_nr++;
0274         }
0275         if (list_empty(&cp->update))
0276             updated |= CON_UPDATE_LIST;
0277     }
0278     wrq->callback = con3270_write_callback;
0279     rc = raw3270_start(&cp->view, wrq);
0280     if (rc == 0) {
0281         cp->update_flags &= ~updated;
0282         if (cp->update_flags)
0283             con3270_set_timer(cp, 1);
0284     } else {
0285         raw3270_request_reset(wrq);
0286         xchg(&cp->write, wrq);
0287     }
0288     spin_unlock_irqrestore(&cp->view.lock, flags);
0289 }
0290 
0291 /*
0292  * Read tasklet.
0293  */
0294 static void
0295 con3270_read_tasklet(unsigned long data)
0296 {
0297     static char kreset_data = TW_KR;
0298     struct raw3270_request *rrq;
0299     struct con3270 *cp;
0300     unsigned long flags;
0301     int nr_up, deactivate;
0302 
0303     rrq = (struct raw3270_request *)data;
0304     cp = (struct con3270 *) rrq->view;
0305     spin_lock_irqsave(&cp->view.lock, flags);
0306     nr_up = cp->nr_up;
0307     deactivate = 0;
0308     /* Check aid byte. */
0309     switch (cp->input->string[0]) {
0310     case 0x7d:  /* enter: jump to bottom. */
0311         nr_up = 0;
0312         break;
0313     case 0xf3:  /* PF3: deactivate the console view. */
0314         deactivate = 1;
0315         break;
0316     case 0x6d:  /* clear: start from scratch. */
0317         cp->update_flags = CON_UPDATE_ALL;
0318         con3270_set_timer(cp, 1);
0319         break;
0320     case 0xf7:  /* PF7: do a page up in the console log. */
0321         nr_up += cp->view.rows - 2;
0322         if (nr_up + cp->view.rows - 1 > cp->nr_lines) {
0323             nr_up = cp->nr_lines - cp->view.rows + 1;
0324             if (nr_up < 0)
0325                 nr_up = 0;
0326         }
0327         break;
0328     case 0xf8:  /* PF8: do a page down in the console log. */
0329         nr_up -= cp->view.rows - 2;
0330         if (nr_up < 0)
0331             nr_up = 0;
0332         break;
0333     }
0334     if (nr_up != cp->nr_up) {
0335         cp->nr_up = nr_up;
0336         con3270_rebuild_update(cp);
0337         con3270_update_status(cp);
0338         con3270_set_timer(cp, 1);
0339     }
0340     spin_unlock_irqrestore(&cp->view.lock, flags);
0341 
0342     /* Start keyboard reset command. */
0343     raw3270_request_reset(cp->kreset);
0344     raw3270_request_set_cmd(cp->kreset, TC_WRITE);
0345     raw3270_request_add_data(cp->kreset, &kreset_data, 1);
0346     raw3270_start(&cp->view, cp->kreset);
0347 
0348     if (deactivate)
0349         raw3270_deactivate_view(&cp->view);
0350 
0351     raw3270_request_reset(rrq);
0352     xchg(&cp->read, rrq);
0353     raw3270_put_view(&cp->view);
0354 }
0355 
0356 /*
0357  * Read request completion callback.
0358  */
0359 static void
0360 con3270_read_callback(struct raw3270_request *rq, void *data)
0361 {
0362     raw3270_get_view(rq->view);
0363     /* Schedule tasklet to pass input to tty. */
0364     tasklet_schedule(&((struct con3270 *) rq->view)->readlet);
0365 }
0366 
0367 /*
0368  * Issue a read request. Called only from interrupt function.
0369  */
0370 static void
0371 con3270_issue_read(struct con3270 *cp)
0372 {
0373     struct raw3270_request *rrq;
0374     int rc;
0375 
0376     rrq = xchg(&cp->read, 0);
0377     if (!rrq)
0378         /* Read already scheduled. */
0379         return;
0380     rrq->callback = con3270_read_callback;
0381     rrq->callback_data = cp;
0382     raw3270_request_set_cmd(rrq, TC_READMOD);
0383     raw3270_request_set_data(rrq, cp->input->string, cp->input->len);
0384     /* Issue the read modified request. */
0385     rc = raw3270_start_irq(&cp->view, rrq);
0386     if (rc)
0387         raw3270_request_reset(rrq);
0388 }
0389 
0390 /*
0391  * Switch to the console view.
0392  */
0393 static int
0394 con3270_activate(struct raw3270_view *view)
0395 {
0396     struct con3270 *cp;
0397 
0398     cp = (struct con3270 *) view;
0399     cp->update_flags = CON_UPDATE_ALL;
0400     con3270_set_timer(cp, 1);
0401     return 0;
0402 }
0403 
0404 static void
0405 con3270_deactivate(struct raw3270_view *view)
0406 {
0407     struct con3270 *cp;
0408 
0409     cp = (struct con3270 *) view;
0410     del_timer(&cp->timer);
0411 }
0412 
0413 static void
0414 con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb)
0415 {
0416     /* Handle ATTN. Schedule tasklet to read aid. */
0417     if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
0418         con3270_issue_read(cp);
0419 
0420     if (rq) {
0421         if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
0422             rq->rc = -EIO;
0423         else
0424             /* Normal end. Copy residual count. */
0425             rq->rescnt = irb->scsw.cmd.count;
0426     } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
0427         /* Interrupt without an outstanding request -> update all */
0428         cp->update_flags = CON_UPDATE_ALL;
0429         con3270_set_timer(cp, 1);
0430     }
0431 }
0432 
0433 /* Console view to a 3270 device. */
0434 static struct raw3270_fn con3270_fn = {
0435     .activate = con3270_activate,
0436     .deactivate = con3270_deactivate,
0437     .intv = (void *) con3270_irq
0438 };
0439 
0440 static inline void
0441 con3270_cline_add(struct con3270 *cp)
0442 {
0443     if (!list_empty(&cp->cline->list))
0444         /* Already added. */
0445         return;
0446     list_add_tail(&cp->cline->list, &cp->lines);
0447     cp->nr_lines++;
0448     con3270_rebuild_update(cp);
0449 }
0450 
0451 static inline void
0452 con3270_cline_insert(struct con3270 *cp, unsigned char c)
0453 {
0454     cp->cline->string[cp->cline->len++] = 
0455         cp->view.ascebc[(c < ' ') ? ' ' : c];
0456     if (list_empty(&cp->cline->update)) {
0457         list_add_tail(&cp->cline->update, &cp->update);
0458         cp->update_flags |= CON_UPDATE_LIST;
0459     }
0460 }
0461 
0462 static inline void
0463 con3270_cline_end(struct con3270 *cp)
0464 {
0465     struct string *s;
0466     unsigned int size;
0467 
0468     /* Copy cline. */
0469     size = (cp->cline->len < cp->view.cols - 5) ?
0470         cp->cline->len + 4 : cp->view.cols;
0471     s = con3270_alloc_string(cp, size);
0472     memcpy(s->string, cp->cline->string, cp->cline->len);
0473     if (cp->cline->len < cp->view.cols - 5) {
0474         s->string[s->len - 4] = TO_RA;
0475         s->string[s->len - 1] = 0;
0476     } else {
0477         while (--size >= cp->cline->len)
0478             s->string[size] = cp->view.ascebc[' '];
0479     }
0480     /* Replace cline with allocated line s and reset cline. */
0481     list_add(&s->list, &cp->cline->list);
0482     list_del_init(&cp->cline->list);
0483     if (!list_empty(&cp->cline->update)) {
0484         list_add(&s->update, &cp->cline->update);
0485         list_del_init(&cp->cline->update);
0486     }
0487     cp->cline->len = 0;
0488 }
0489 
0490 /*
0491  * Write a string to the 3270 console
0492  */
0493 static void
0494 con3270_write(struct console *co, const char *str, unsigned int count)
0495 {
0496     struct con3270 *cp;
0497     unsigned long flags;
0498     unsigned char c;
0499 
0500     cp = condev;
0501     spin_lock_irqsave(&cp->view.lock, flags);
0502     while (count-- > 0) {
0503         c = *str++;
0504         if (cp->cline->len == 0)
0505             con3270_cline_add(cp);
0506         if (c != '\n')
0507             con3270_cline_insert(cp, c);
0508         if (c == '\n' || cp->cline->len >= cp->view.cols)
0509             con3270_cline_end(cp);
0510     }
0511     /* Setup timer to output current console buffer after 1/10 second */
0512     cp->nr_up = 0;
0513     if (cp->view.dev && !timer_pending(&cp->timer))
0514         con3270_set_timer(cp, HZ/10);
0515     spin_unlock_irqrestore(&cp->view.lock,flags);
0516 }
0517 
0518 static struct tty_driver *
0519 con3270_device(struct console *c, int *index)
0520 {
0521     *index = c->index;
0522     return tty3270_driver;
0523 }
0524 
0525 /*
0526  * Wait for end of write request.
0527  */
0528 static void
0529 con3270_wait_write(struct con3270 *cp)
0530 {
0531     while (!cp->write) {
0532         raw3270_wait_cons_dev(cp->view.dev);
0533         barrier();
0534     }
0535 }
0536 
0537 /*
0538  * The below function is called as a panic/reboot notifier before the
0539  * system enters a disabled, endless loop.
0540  *
0541  * Notice we must use the spin_trylock() alternative, to prevent lockups
0542  * in atomic context (panic routine runs with secondary CPUs, local IRQs
0543  * and preemption disabled).
0544  */
0545 static int con3270_notify(struct notifier_block *self,
0546               unsigned long event, void *data)
0547 {
0548     struct con3270 *cp;
0549     unsigned long flags;
0550 
0551     cp = condev;
0552     if (!cp->view.dev)
0553         return NOTIFY_DONE;
0554     if (!raw3270_view_lock_unavailable(&cp->view))
0555         raw3270_activate_view(&cp->view);
0556     if (!spin_trylock_irqsave(&cp->view.lock, flags))
0557         return NOTIFY_DONE;
0558     con3270_wait_write(cp);
0559     cp->nr_up = 0;
0560     con3270_rebuild_update(cp);
0561     con3270_update_status(cp);
0562     while (cp->update_flags != 0) {
0563         spin_unlock_irqrestore(&cp->view.lock, flags);
0564         con3270_update(&cp->timer);
0565         spin_lock_irqsave(&cp->view.lock, flags);
0566         con3270_wait_write(cp);
0567     }
0568     spin_unlock_irqrestore(&cp->view.lock, flags);
0569 
0570     return NOTIFY_DONE;
0571 }
0572 
0573 static struct notifier_block on_panic_nb = {
0574     .notifier_call = con3270_notify,
0575     .priority = INT_MIN + 1, /* run the callback late */
0576 };
0577 
0578 static struct notifier_block on_reboot_nb = {
0579     .notifier_call = con3270_notify,
0580     .priority = INT_MIN + 1, /* run the callback late */
0581 };
0582 
0583 /*
0584  *  The console structure for the 3270 console
0585  */
0586 static struct console con3270 = {
0587     .name    = "tty3270",
0588     .write   = con3270_write,
0589     .device  = con3270_device,
0590     .flags   = CON_PRINTBUFFER,
0591 };
0592 
0593 /*
0594  * 3270 console initialization code called from console_init().
0595  */
0596 static int __init
0597 con3270_init(void)
0598 {
0599     struct raw3270 *rp;
0600     void *cbuf;
0601     int i;
0602 
0603     /* Check if 3270 is to be the console */
0604     if (!CONSOLE_IS_3270)
0605         return -ENODEV;
0606 
0607     /* Set the console mode for VM */
0608     if (MACHINE_IS_VM) {
0609         cpcmd("TERM CONMODE 3270", NULL, 0, NULL);
0610         cpcmd("TERM AUTOCR OFF", NULL, 0, NULL);
0611     }
0612 
0613     rp = raw3270_setup_console();
0614     if (IS_ERR(rp))
0615         return PTR_ERR(rp);
0616 
0617     condev = kzalloc(sizeof(struct con3270), GFP_KERNEL | GFP_DMA);
0618     if (!condev)
0619         return -ENOMEM;
0620     condev->view.dev = rp;
0621 
0622     condev->read = raw3270_request_alloc(0);
0623     condev->read->callback = con3270_read_callback;
0624     condev->read->callback_data = condev;
0625     condev->write = raw3270_request_alloc(CON3270_OUTPUT_BUFFER_SIZE);
0626     condev->kreset = raw3270_request_alloc(1);
0627 
0628     INIT_LIST_HEAD(&condev->lines);
0629     INIT_LIST_HEAD(&condev->update);
0630     timer_setup(&condev->timer, con3270_update, 0);
0631     tasklet_init(&condev->readlet, con3270_read_tasklet,
0632              (unsigned long) condev->read);
0633 
0634     raw3270_add_view(&condev->view, &con3270_fn, 1, RAW3270_VIEW_LOCK_IRQ);
0635 
0636     INIT_LIST_HEAD(&condev->freemem);
0637     for (i = 0; i < CON3270_STRING_PAGES; i++) {
0638         cbuf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
0639         add_string_memory(&condev->freemem, cbuf, PAGE_SIZE);
0640     }
0641     condev->cline = alloc_string(&condev->freemem, condev->view.cols);
0642     condev->cline->len = 0;
0643     con3270_create_status(condev);
0644     condev->input = alloc_string(&condev->freemem, 80);
0645     atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
0646     register_reboot_notifier(&on_reboot_nb);
0647     register_console(&con3270);
0648     return 0;
0649 }
0650 
0651 console_initcall(con3270_init);