18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IBM/3270 Driver - console view. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author(s): 68c2ecf20Sopenharmony_ci * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) 78c2ecf20Sopenharmony_ci * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> 88c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2003, 2009 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/console.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/list.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/reboot.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <asm/ccwdev.h> 228c2ecf20Sopenharmony_ci#include <asm/cio.h> 238c2ecf20Sopenharmony_ci#include <asm/cpcmd.h> 248c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "raw3270.h" 278c2ecf20Sopenharmony_ci#include "tty3270.h" 288c2ecf20Sopenharmony_ci#include "ctrlchar.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define CON3270_OUTPUT_BUFFER_SIZE 1024 318c2ecf20Sopenharmony_ci#define CON3270_STRING_PAGES 4 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct raw3270_fn con3270_fn; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic bool auto_update = true; 368c2ecf20Sopenharmony_cimodule_param(auto_update, bool, 0); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * Main 3270 console view data structure. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_cistruct con3270 { 428c2ecf20Sopenharmony_ci struct raw3270_view view; 438c2ecf20Sopenharmony_ci struct list_head freemem; /* list of free memory for strings. */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* Output stuff. */ 468c2ecf20Sopenharmony_ci struct list_head lines; /* list of lines. */ 478c2ecf20Sopenharmony_ci struct list_head update; /* list of lines to update. */ 488c2ecf20Sopenharmony_ci int line_nr; /* line number for next update. */ 498c2ecf20Sopenharmony_ci int nr_lines; /* # lines in list. */ 508c2ecf20Sopenharmony_ci int nr_up; /* # lines up in history. */ 518c2ecf20Sopenharmony_ci unsigned long update_flags; /* Update indication bits. */ 528c2ecf20Sopenharmony_ci struct string *cline; /* current output line. */ 538c2ecf20Sopenharmony_ci struct string *status; /* last line of display. */ 548c2ecf20Sopenharmony_ci struct raw3270_request *write; /* single write request. */ 558c2ecf20Sopenharmony_ci struct timer_list timer; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* Input stuff. */ 588c2ecf20Sopenharmony_ci struct string *input; /* input string for read request. */ 598c2ecf20Sopenharmony_ci struct raw3270_request *read; /* single read request. */ 608c2ecf20Sopenharmony_ci struct raw3270_request *kreset; /* single keyboard reset request. */ 618c2ecf20Sopenharmony_ci struct tasklet_struct readlet; /* tasklet to issue read request. */ 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic struct con3270 *condev; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* con3270->update_flags. See con3270_update for details. */ 678c2ecf20Sopenharmony_ci#define CON_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */ 688c2ecf20Sopenharmony_ci#define CON_UPDATE_LIST 2 /* Update lines in tty3270->update. */ 698c2ecf20Sopenharmony_ci#define CON_UPDATE_STATUS 4 /* Update status line. */ 708c2ecf20Sopenharmony_ci#define CON_UPDATE_ALL 8 /* Recreate screen. */ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void con3270_update(struct timer_list *); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 758c2ecf20Sopenharmony_ci * Setup timeout for a device. On timeout trigger an update. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_cistatic void con3270_set_timer(struct con3270 *cp, int expires) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci if (expires == 0) 808c2ecf20Sopenharmony_ci del_timer(&cp->timer); 818c2ecf20Sopenharmony_ci else 828c2ecf20Sopenharmony_ci mod_timer(&cp->timer, jiffies + expires); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* 868c2ecf20Sopenharmony_ci * The status line is the last line of the screen. It shows the string 878c2ecf20Sopenharmony_ci * "console view" in the lower left corner and "Running"/"More..."/"Holding" 888c2ecf20Sopenharmony_ci * in the lower right corner of the screen. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_cistatic void 918c2ecf20Sopenharmony_cicon3270_update_status(struct con3270 *cp) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci char *str; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci str = (cp->nr_up != 0) ? "History" : "Running"; 968c2ecf20Sopenharmony_ci memcpy(cp->status->string + 24, str, 7); 978c2ecf20Sopenharmony_ci codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); 988c2ecf20Sopenharmony_ci cp->update_flags |= CON_UPDATE_STATUS; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic void 1028c2ecf20Sopenharmony_cicon3270_create_status(struct con3270 *cp) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci static const unsigned char blueprint[] = 1058c2ecf20Sopenharmony_ci { TO_SBA, 0, 0, TO_SF,TF_LOG,TO_SA,TAT_COLOR, TAC_GREEN, 1068c2ecf20Sopenharmony_ci 'c','o','n','s','o','l','e',' ','v','i','e','w', 1078c2ecf20Sopenharmony_ci TO_RA,0,0,0,'R','u','n','n','i','n','g',TO_SF,TF_LOG }; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci cp->status = alloc_string(&cp->freemem, sizeof(blueprint)); 1108c2ecf20Sopenharmony_ci /* Copy blueprint to status line */ 1118c2ecf20Sopenharmony_ci memcpy(cp->status->string, blueprint, sizeof(blueprint)); 1128c2ecf20Sopenharmony_ci /* Set TO_RA addresses. */ 1138c2ecf20Sopenharmony_ci raw3270_buffer_address(cp->view.dev, cp->status->string + 1, 1148c2ecf20Sopenharmony_ci cp->view.cols * (cp->view.rows - 1)); 1158c2ecf20Sopenharmony_ci raw3270_buffer_address(cp->view.dev, cp->status->string + 21, 1168c2ecf20Sopenharmony_ci cp->view.cols * cp->view.rows - 8); 1178c2ecf20Sopenharmony_ci /* Convert strings to ebcdic. */ 1188c2ecf20Sopenharmony_ci codepage_convert(cp->view.ascebc, cp->status->string + 8, 12); 1198c2ecf20Sopenharmony_ci codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* 1238c2ecf20Sopenharmony_ci * Set output offsets to 3270 datastream fragment of a console string. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistatic void 1268c2ecf20Sopenharmony_cicon3270_update_string(struct con3270 *cp, struct string *s, int nr) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci if (s->len < 4) { 1298c2ecf20Sopenharmony_ci /* This indicates a bug, but printing a warning would 1308c2ecf20Sopenharmony_ci * cause a deadlock. */ 1318c2ecf20Sopenharmony_ci return; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci if (s->string[s->len - 4] != TO_RA) 1348c2ecf20Sopenharmony_ci return; 1358c2ecf20Sopenharmony_ci raw3270_buffer_address(cp->view.dev, s->string + s->len - 3, 1368c2ecf20Sopenharmony_ci cp->view.cols * (nr + 1)); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* 1408c2ecf20Sopenharmony_ci * Rebuild update list to print all lines. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_cistatic void 1438c2ecf20Sopenharmony_cicon3270_rebuild_update(struct con3270 *cp) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct string *s, *n; 1468c2ecf20Sopenharmony_ci int nr; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* 1498c2ecf20Sopenharmony_ci * Throw away update list and create a new one, 1508c2ecf20Sopenharmony_ci * containing all lines that will fit on the screen. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci list_for_each_entry_safe(s, n, &cp->update, update) 1538c2ecf20Sopenharmony_ci list_del_init(&s->update); 1548c2ecf20Sopenharmony_ci nr = cp->view.rows - 2 + cp->nr_up; 1558c2ecf20Sopenharmony_ci list_for_each_entry_reverse(s, &cp->lines, list) { 1568c2ecf20Sopenharmony_ci if (nr < cp->view.rows - 1) 1578c2ecf20Sopenharmony_ci list_add(&s->update, &cp->update); 1588c2ecf20Sopenharmony_ci if (--nr < 0) 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci cp->line_nr = 0; 1628c2ecf20Sopenharmony_ci cp->update_flags |= CON_UPDATE_LIST; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* 1668c2ecf20Sopenharmony_ci * Alloc string for size bytes. Free strings from history if necessary. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_cistatic struct string * 1698c2ecf20Sopenharmony_cicon3270_alloc_string(struct con3270 *cp, size_t size) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct string *s, *n; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci s = alloc_string(&cp->freemem, size); 1748c2ecf20Sopenharmony_ci if (s) 1758c2ecf20Sopenharmony_ci return s; 1768c2ecf20Sopenharmony_ci list_for_each_entry_safe(s, n, &cp->lines, list) { 1778c2ecf20Sopenharmony_ci list_del(&s->list); 1788c2ecf20Sopenharmony_ci if (!list_empty(&s->update)) 1798c2ecf20Sopenharmony_ci list_del(&s->update); 1808c2ecf20Sopenharmony_ci cp->nr_lines--; 1818c2ecf20Sopenharmony_ci if (free_string(&cp->freemem, s) >= size) 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci s = alloc_string(&cp->freemem, size); 1858c2ecf20Sopenharmony_ci BUG_ON(!s); 1868c2ecf20Sopenharmony_ci if (cp->nr_up != 0 && cp->nr_up + cp->view.rows > cp->nr_lines) { 1878c2ecf20Sopenharmony_ci cp->nr_up = cp->nr_lines - cp->view.rows + 1; 1888c2ecf20Sopenharmony_ci con3270_rebuild_update(cp); 1898c2ecf20Sopenharmony_ci con3270_update_status(cp); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci return s; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* 1958c2ecf20Sopenharmony_ci * Write completion callback. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_cistatic void 1988c2ecf20Sopenharmony_cicon3270_write_callback(struct raw3270_request *rq, void *data) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci raw3270_request_reset(rq); 2018c2ecf20Sopenharmony_ci xchg(&((struct con3270 *) rq->view)->write, rq); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* 2058c2ecf20Sopenharmony_ci * Update console display. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_cistatic void 2088c2ecf20Sopenharmony_cicon3270_update(struct timer_list *t) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct con3270 *cp = from_timer(cp, t, timer); 2118c2ecf20Sopenharmony_ci struct raw3270_request *wrq; 2128c2ecf20Sopenharmony_ci char wcc, prolog[6]; 2138c2ecf20Sopenharmony_ci unsigned long flags; 2148c2ecf20Sopenharmony_ci unsigned long updated; 2158c2ecf20Sopenharmony_ci struct string *s, *n; 2168c2ecf20Sopenharmony_ci int rc; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (!auto_update && !raw3270_view_active(&cp->view)) 2198c2ecf20Sopenharmony_ci return; 2208c2ecf20Sopenharmony_ci if (cp->view.dev) 2218c2ecf20Sopenharmony_ci raw3270_activate_view(&cp->view); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci wrq = xchg(&cp->write, 0); 2248c2ecf20Sopenharmony_ci if (!wrq) { 2258c2ecf20Sopenharmony_ci con3270_set_timer(cp, 1); 2268c2ecf20Sopenharmony_ci return; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->view.lock, flags); 2308c2ecf20Sopenharmony_ci updated = 0; 2318c2ecf20Sopenharmony_ci if (cp->update_flags & CON_UPDATE_ALL) { 2328c2ecf20Sopenharmony_ci con3270_rebuild_update(cp); 2338c2ecf20Sopenharmony_ci con3270_update_status(cp); 2348c2ecf20Sopenharmony_ci cp->update_flags = CON_UPDATE_ERASE | CON_UPDATE_LIST | 2358c2ecf20Sopenharmony_ci CON_UPDATE_STATUS; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci if (cp->update_flags & CON_UPDATE_ERASE) { 2388c2ecf20Sopenharmony_ci /* Use erase write alternate to initialize display. */ 2398c2ecf20Sopenharmony_ci raw3270_request_set_cmd(wrq, TC_EWRITEA); 2408c2ecf20Sopenharmony_ci updated |= CON_UPDATE_ERASE; 2418c2ecf20Sopenharmony_ci } else 2428c2ecf20Sopenharmony_ci raw3270_request_set_cmd(wrq, TC_WRITE); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci wcc = TW_NONE; 2458c2ecf20Sopenharmony_ci raw3270_request_add_data(wrq, &wcc, 1); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* 2488c2ecf20Sopenharmony_ci * Update status line. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci if (cp->update_flags & CON_UPDATE_STATUS) 2518c2ecf20Sopenharmony_ci if (raw3270_request_add_data(wrq, cp->status->string, 2528c2ecf20Sopenharmony_ci cp->status->len) == 0) 2538c2ecf20Sopenharmony_ci updated |= CON_UPDATE_STATUS; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (cp->update_flags & CON_UPDATE_LIST) { 2568c2ecf20Sopenharmony_ci prolog[0] = TO_SBA; 2578c2ecf20Sopenharmony_ci prolog[3] = TO_SA; 2588c2ecf20Sopenharmony_ci prolog[4] = TAT_COLOR; 2598c2ecf20Sopenharmony_ci prolog[5] = TAC_TURQ; 2608c2ecf20Sopenharmony_ci raw3270_buffer_address(cp->view.dev, prolog + 1, 2618c2ecf20Sopenharmony_ci cp->view.cols * cp->line_nr); 2628c2ecf20Sopenharmony_ci raw3270_request_add_data(wrq, prolog, 6); 2638c2ecf20Sopenharmony_ci /* Write strings in the update list to the screen. */ 2648c2ecf20Sopenharmony_ci list_for_each_entry_safe(s, n, &cp->update, update) { 2658c2ecf20Sopenharmony_ci if (s != cp->cline) 2668c2ecf20Sopenharmony_ci con3270_update_string(cp, s, cp->line_nr); 2678c2ecf20Sopenharmony_ci if (raw3270_request_add_data(wrq, s->string, 2688c2ecf20Sopenharmony_ci s->len) != 0) 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci list_del_init(&s->update); 2718c2ecf20Sopenharmony_ci if (s != cp->cline) 2728c2ecf20Sopenharmony_ci cp->line_nr++; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci if (list_empty(&cp->update)) 2758c2ecf20Sopenharmony_ci updated |= CON_UPDATE_LIST; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci wrq->callback = con3270_write_callback; 2788c2ecf20Sopenharmony_ci rc = raw3270_start(&cp->view, wrq); 2798c2ecf20Sopenharmony_ci if (rc == 0) { 2808c2ecf20Sopenharmony_ci cp->update_flags &= ~updated; 2818c2ecf20Sopenharmony_ci if (cp->update_flags) 2828c2ecf20Sopenharmony_ci con3270_set_timer(cp, 1); 2838c2ecf20Sopenharmony_ci } else { 2848c2ecf20Sopenharmony_ci raw3270_request_reset(wrq); 2858c2ecf20Sopenharmony_ci xchg(&cp->write, wrq); 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->view.lock, flags); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* 2918c2ecf20Sopenharmony_ci * Read tasklet. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_cistatic void 2948c2ecf20Sopenharmony_cicon3270_read_tasklet(struct raw3270_request *rrq) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci static char kreset_data = TW_KR; 2978c2ecf20Sopenharmony_ci struct con3270 *cp; 2988c2ecf20Sopenharmony_ci unsigned long flags; 2998c2ecf20Sopenharmony_ci int nr_up, deactivate; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci cp = (struct con3270 *) rrq->view; 3028c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->view.lock, flags); 3038c2ecf20Sopenharmony_ci nr_up = cp->nr_up; 3048c2ecf20Sopenharmony_ci deactivate = 0; 3058c2ecf20Sopenharmony_ci /* Check aid byte. */ 3068c2ecf20Sopenharmony_ci switch (cp->input->string[0]) { 3078c2ecf20Sopenharmony_ci case 0x7d: /* enter: jump to bottom. */ 3088c2ecf20Sopenharmony_ci nr_up = 0; 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci case 0xf3: /* PF3: deactivate the console view. */ 3118c2ecf20Sopenharmony_ci deactivate = 1; 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci case 0x6d: /* clear: start from scratch. */ 3148c2ecf20Sopenharmony_ci cp->update_flags = CON_UPDATE_ALL; 3158c2ecf20Sopenharmony_ci con3270_set_timer(cp, 1); 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci case 0xf7: /* PF7: do a page up in the console log. */ 3188c2ecf20Sopenharmony_ci nr_up += cp->view.rows - 2; 3198c2ecf20Sopenharmony_ci if (nr_up + cp->view.rows - 1 > cp->nr_lines) { 3208c2ecf20Sopenharmony_ci nr_up = cp->nr_lines - cp->view.rows + 1; 3218c2ecf20Sopenharmony_ci if (nr_up < 0) 3228c2ecf20Sopenharmony_ci nr_up = 0; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci case 0xf8: /* PF8: do a page down in the console log. */ 3268c2ecf20Sopenharmony_ci nr_up -= cp->view.rows - 2; 3278c2ecf20Sopenharmony_ci if (nr_up < 0) 3288c2ecf20Sopenharmony_ci nr_up = 0; 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci if (nr_up != cp->nr_up) { 3328c2ecf20Sopenharmony_ci cp->nr_up = nr_up; 3338c2ecf20Sopenharmony_ci con3270_rebuild_update(cp); 3348c2ecf20Sopenharmony_ci con3270_update_status(cp); 3358c2ecf20Sopenharmony_ci con3270_set_timer(cp, 1); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->view.lock, flags); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Start keyboard reset command. */ 3408c2ecf20Sopenharmony_ci raw3270_request_reset(cp->kreset); 3418c2ecf20Sopenharmony_ci raw3270_request_set_cmd(cp->kreset, TC_WRITE); 3428c2ecf20Sopenharmony_ci raw3270_request_add_data(cp->kreset, &kreset_data, 1); 3438c2ecf20Sopenharmony_ci raw3270_start(&cp->view, cp->kreset); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (deactivate) 3468c2ecf20Sopenharmony_ci raw3270_deactivate_view(&cp->view); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci raw3270_request_reset(rrq); 3498c2ecf20Sopenharmony_ci xchg(&cp->read, rrq); 3508c2ecf20Sopenharmony_ci raw3270_put_view(&cp->view); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/* 3548c2ecf20Sopenharmony_ci * Read request completion callback. 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_cistatic void 3578c2ecf20Sopenharmony_cicon3270_read_callback(struct raw3270_request *rq, void *data) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci raw3270_get_view(rq->view); 3608c2ecf20Sopenharmony_ci /* Schedule tasklet to pass input to tty. */ 3618c2ecf20Sopenharmony_ci tasklet_schedule(&((struct con3270 *) rq->view)->readlet); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* 3658c2ecf20Sopenharmony_ci * Issue a read request. Called only from interrupt function. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_cistatic void 3688c2ecf20Sopenharmony_cicon3270_issue_read(struct con3270 *cp) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct raw3270_request *rrq; 3718c2ecf20Sopenharmony_ci int rc; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci rrq = xchg(&cp->read, 0); 3748c2ecf20Sopenharmony_ci if (!rrq) 3758c2ecf20Sopenharmony_ci /* Read already scheduled. */ 3768c2ecf20Sopenharmony_ci return; 3778c2ecf20Sopenharmony_ci rrq->callback = con3270_read_callback; 3788c2ecf20Sopenharmony_ci rrq->callback_data = cp; 3798c2ecf20Sopenharmony_ci raw3270_request_set_cmd(rrq, TC_READMOD); 3808c2ecf20Sopenharmony_ci raw3270_request_set_data(rrq, cp->input->string, cp->input->len); 3818c2ecf20Sopenharmony_ci /* Issue the read modified request. */ 3828c2ecf20Sopenharmony_ci rc = raw3270_start_irq(&cp->view, rrq); 3838c2ecf20Sopenharmony_ci if (rc) 3848c2ecf20Sopenharmony_ci raw3270_request_reset(rrq); 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/* 3888c2ecf20Sopenharmony_ci * Switch to the console view. 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_cistatic int 3918c2ecf20Sopenharmony_cicon3270_activate(struct raw3270_view *view) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct con3270 *cp; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci cp = (struct con3270 *) view; 3968c2ecf20Sopenharmony_ci cp->update_flags = CON_UPDATE_ALL; 3978c2ecf20Sopenharmony_ci con3270_set_timer(cp, 1); 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic void 4028c2ecf20Sopenharmony_cicon3270_deactivate(struct raw3270_view *view) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct con3270 *cp; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci cp = (struct con3270 *) view; 4078c2ecf20Sopenharmony_ci del_timer(&cp->timer); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic void 4118c2ecf20Sopenharmony_cicon3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci /* Handle ATTN. Schedule tasklet to read aid. */ 4148c2ecf20Sopenharmony_ci if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) 4158c2ecf20Sopenharmony_ci con3270_issue_read(cp); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (rq) { 4188c2ecf20Sopenharmony_ci if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) 4198c2ecf20Sopenharmony_ci rq->rc = -EIO; 4208c2ecf20Sopenharmony_ci else 4218c2ecf20Sopenharmony_ci /* Normal end. Copy residual count. */ 4228c2ecf20Sopenharmony_ci rq->rescnt = irb->scsw.cmd.count; 4238c2ecf20Sopenharmony_ci } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { 4248c2ecf20Sopenharmony_ci /* Interrupt without an outstanding request -> update all */ 4258c2ecf20Sopenharmony_ci cp->update_flags = CON_UPDATE_ALL; 4268c2ecf20Sopenharmony_ci con3270_set_timer(cp, 1); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci/* Console view to a 3270 device. */ 4318c2ecf20Sopenharmony_cistatic struct raw3270_fn con3270_fn = { 4328c2ecf20Sopenharmony_ci .activate = con3270_activate, 4338c2ecf20Sopenharmony_ci .deactivate = con3270_deactivate, 4348c2ecf20Sopenharmony_ci .intv = (void *) con3270_irq 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic inline void 4388c2ecf20Sopenharmony_cicon3270_cline_add(struct con3270 *cp) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci if (!list_empty(&cp->cline->list)) 4418c2ecf20Sopenharmony_ci /* Already added. */ 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci list_add_tail(&cp->cline->list, &cp->lines); 4448c2ecf20Sopenharmony_ci cp->nr_lines++; 4458c2ecf20Sopenharmony_ci con3270_rebuild_update(cp); 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic inline void 4498c2ecf20Sopenharmony_cicon3270_cline_insert(struct con3270 *cp, unsigned char c) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci cp->cline->string[cp->cline->len++] = 4528c2ecf20Sopenharmony_ci cp->view.ascebc[(c < ' ') ? ' ' : c]; 4538c2ecf20Sopenharmony_ci if (list_empty(&cp->cline->update)) { 4548c2ecf20Sopenharmony_ci list_add_tail(&cp->cline->update, &cp->update); 4558c2ecf20Sopenharmony_ci cp->update_flags |= CON_UPDATE_LIST; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic inline void 4608c2ecf20Sopenharmony_cicon3270_cline_end(struct con3270 *cp) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct string *s; 4638c2ecf20Sopenharmony_ci unsigned int size; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* Copy cline. */ 4668c2ecf20Sopenharmony_ci size = (cp->cline->len < cp->view.cols - 5) ? 4678c2ecf20Sopenharmony_ci cp->cline->len + 4 : cp->view.cols; 4688c2ecf20Sopenharmony_ci s = con3270_alloc_string(cp, size); 4698c2ecf20Sopenharmony_ci memcpy(s->string, cp->cline->string, cp->cline->len); 4708c2ecf20Sopenharmony_ci if (cp->cline->len < cp->view.cols - 5) { 4718c2ecf20Sopenharmony_ci s->string[s->len - 4] = TO_RA; 4728c2ecf20Sopenharmony_ci s->string[s->len - 1] = 0; 4738c2ecf20Sopenharmony_ci } else { 4748c2ecf20Sopenharmony_ci while (--size >= cp->cline->len) 4758c2ecf20Sopenharmony_ci s->string[size] = cp->view.ascebc[' ']; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci /* Replace cline with allocated line s and reset cline. */ 4788c2ecf20Sopenharmony_ci list_add(&s->list, &cp->cline->list); 4798c2ecf20Sopenharmony_ci list_del_init(&cp->cline->list); 4808c2ecf20Sopenharmony_ci if (!list_empty(&cp->cline->update)) { 4818c2ecf20Sopenharmony_ci list_add(&s->update, &cp->cline->update); 4828c2ecf20Sopenharmony_ci list_del_init(&cp->cline->update); 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci cp->cline->len = 0; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci/* 4888c2ecf20Sopenharmony_ci * Write a string to the 3270 console 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_cistatic void 4918c2ecf20Sopenharmony_cicon3270_write(struct console *co, const char *str, unsigned int count) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct con3270 *cp; 4948c2ecf20Sopenharmony_ci unsigned long flags; 4958c2ecf20Sopenharmony_ci unsigned char c; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci cp = condev; 4988c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->view.lock, flags); 4998c2ecf20Sopenharmony_ci while (count-- > 0) { 5008c2ecf20Sopenharmony_ci c = *str++; 5018c2ecf20Sopenharmony_ci if (cp->cline->len == 0) 5028c2ecf20Sopenharmony_ci con3270_cline_add(cp); 5038c2ecf20Sopenharmony_ci if (c != '\n') 5048c2ecf20Sopenharmony_ci con3270_cline_insert(cp, c); 5058c2ecf20Sopenharmony_ci if (c == '\n' || cp->cline->len >= cp->view.cols) 5068c2ecf20Sopenharmony_ci con3270_cline_end(cp); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci /* Setup timer to output current console buffer after 1/10 second */ 5098c2ecf20Sopenharmony_ci cp->nr_up = 0; 5108c2ecf20Sopenharmony_ci if (cp->view.dev && !timer_pending(&cp->timer)) 5118c2ecf20Sopenharmony_ci con3270_set_timer(cp, HZ/10); 5128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->view.lock,flags); 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic struct tty_driver * 5168c2ecf20Sopenharmony_cicon3270_device(struct console *c, int *index) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci *index = c->index; 5198c2ecf20Sopenharmony_ci return tty3270_driver; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/* 5238c2ecf20Sopenharmony_ci * Wait for end of write request. 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_cistatic void 5268c2ecf20Sopenharmony_cicon3270_wait_write(struct con3270 *cp) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci while (!cp->write) { 5298c2ecf20Sopenharmony_ci raw3270_wait_cons_dev(cp->view.dev); 5308c2ecf20Sopenharmony_ci barrier(); 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci/* 5358c2ecf20Sopenharmony_ci * panic() calls con3270_flush through a panic_notifier 5368c2ecf20Sopenharmony_ci * before the system enters a disabled, endless loop. 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_cistatic void 5398c2ecf20Sopenharmony_cicon3270_flush(void) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct con3270 *cp; 5428c2ecf20Sopenharmony_ci unsigned long flags; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci cp = condev; 5458c2ecf20Sopenharmony_ci if (!cp->view.dev) 5468c2ecf20Sopenharmony_ci return; 5478c2ecf20Sopenharmony_ci raw3270_pm_unfreeze(&cp->view); 5488c2ecf20Sopenharmony_ci raw3270_activate_view(&cp->view); 5498c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->view.lock, flags); 5508c2ecf20Sopenharmony_ci con3270_wait_write(cp); 5518c2ecf20Sopenharmony_ci cp->nr_up = 0; 5528c2ecf20Sopenharmony_ci con3270_rebuild_update(cp); 5538c2ecf20Sopenharmony_ci con3270_update_status(cp); 5548c2ecf20Sopenharmony_ci while (cp->update_flags != 0) { 5558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->view.lock, flags); 5568c2ecf20Sopenharmony_ci con3270_update(&cp->timer); 5578c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->view.lock, flags); 5588c2ecf20Sopenharmony_ci con3270_wait_write(cp); 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->view.lock, flags); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int con3270_notify(struct notifier_block *self, 5648c2ecf20Sopenharmony_ci unsigned long event, void *data) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci con3270_flush(); 5678c2ecf20Sopenharmony_ci return NOTIFY_OK; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic struct notifier_block on_panic_nb = { 5718c2ecf20Sopenharmony_ci .notifier_call = con3270_notify, 5728c2ecf20Sopenharmony_ci .priority = 0, 5738c2ecf20Sopenharmony_ci}; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic struct notifier_block on_reboot_nb = { 5768c2ecf20Sopenharmony_ci .notifier_call = con3270_notify, 5778c2ecf20Sopenharmony_ci .priority = 0, 5788c2ecf20Sopenharmony_ci}; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci/* 5818c2ecf20Sopenharmony_ci * The console structure for the 3270 console 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_cistatic struct console con3270 = { 5848c2ecf20Sopenharmony_ci .name = "tty3270", 5858c2ecf20Sopenharmony_ci .write = con3270_write, 5868c2ecf20Sopenharmony_ci .device = con3270_device, 5878c2ecf20Sopenharmony_ci .flags = CON_PRINTBUFFER, 5888c2ecf20Sopenharmony_ci}; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/* 5918c2ecf20Sopenharmony_ci * 3270 console initialization code called from console_init(). 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_cistatic int __init 5948c2ecf20Sopenharmony_cicon3270_init(void) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct raw3270 *rp; 5978c2ecf20Sopenharmony_ci void *cbuf; 5988c2ecf20Sopenharmony_ci int i; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* Check if 3270 is to be the console */ 6018c2ecf20Sopenharmony_ci if (!CONSOLE_IS_3270) 6028c2ecf20Sopenharmony_ci return -ENODEV; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* Set the console mode for VM */ 6058c2ecf20Sopenharmony_ci if (MACHINE_IS_VM) { 6068c2ecf20Sopenharmony_ci cpcmd("TERM CONMODE 3270", NULL, 0, NULL); 6078c2ecf20Sopenharmony_ci cpcmd("TERM AUTOCR OFF", NULL, 0, NULL); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci rp = raw3270_setup_console(); 6118c2ecf20Sopenharmony_ci if (IS_ERR(rp)) 6128c2ecf20Sopenharmony_ci return PTR_ERR(rp); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci condev = kzalloc(sizeof(struct con3270), GFP_KERNEL | GFP_DMA); 6158c2ecf20Sopenharmony_ci if (!condev) 6168c2ecf20Sopenharmony_ci return -ENOMEM; 6178c2ecf20Sopenharmony_ci condev->view.dev = rp; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci condev->read = raw3270_request_alloc(0); 6208c2ecf20Sopenharmony_ci condev->read->callback = con3270_read_callback; 6218c2ecf20Sopenharmony_ci condev->read->callback_data = condev; 6228c2ecf20Sopenharmony_ci condev->write = raw3270_request_alloc(CON3270_OUTPUT_BUFFER_SIZE); 6238c2ecf20Sopenharmony_ci condev->kreset = raw3270_request_alloc(1); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&condev->lines); 6268c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&condev->update); 6278c2ecf20Sopenharmony_ci timer_setup(&condev->timer, con3270_update, 0); 6288c2ecf20Sopenharmony_ci tasklet_init(&condev->readlet, 6298c2ecf20Sopenharmony_ci (void (*)(unsigned long)) con3270_read_tasklet, 6308c2ecf20Sopenharmony_ci (unsigned long) condev->read); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci raw3270_add_view(&condev->view, &con3270_fn, 1, RAW3270_VIEW_LOCK_IRQ); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&condev->freemem); 6358c2ecf20Sopenharmony_ci for (i = 0; i < CON3270_STRING_PAGES; i++) { 6368c2ecf20Sopenharmony_ci cbuf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 6378c2ecf20Sopenharmony_ci add_string_memory(&condev->freemem, cbuf, PAGE_SIZE); 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci condev->cline = alloc_string(&condev->freemem, condev->view.cols); 6408c2ecf20Sopenharmony_ci condev->cline->len = 0; 6418c2ecf20Sopenharmony_ci con3270_create_status(condev); 6428c2ecf20Sopenharmony_ci condev->input = alloc_string(&condev->freemem, 80); 6438c2ecf20Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); 6448c2ecf20Sopenharmony_ci register_reboot_notifier(&on_reboot_nb); 6458c2ecf20Sopenharmony_ci register_console(&con3270); 6468c2ecf20Sopenharmony_ci return 0; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ciconsole_initcall(con3270_init); 650