2194 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * IBM/3270 Driver - tty functions.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. 20039 */10 11#include <linux/module.h>12#include <linux/types.h>13#include <linux/kdev_t.h>14#include <linux/tty.h>15#include <linux/vt_kern.h>16#include <linux/init.h>17#include <linux/console.h>18#include <linux/interrupt.h>19#include <linux/workqueue.h>20#include <linux/panic_notifier.h>21#include <linux/reboot.h>22#include <linux/slab.h>23#include <linux/memblock.h>24#include <linux/compat.h>25 26#include <asm/ccwdev.h>27#include <asm/cio.h>28#include <asm/ebcdic.h>29#include <asm/cpcmd.h>30#include <linux/uaccess.h>31 32#include "raw3270.h"33#include "keyboard.h"34 35#define TTY3270_CHAR_BUF_SIZE 25636#define TTY3270_OUTPUT_BUFFER_SIZE 409637#define TTY3270_SCREEN_PAGES 8 /* has to be power-of-two */38#define TTY3270_RECALL_SIZE 16 /* has to be power-of-two */39#define TTY3270_STATUS_AREA_SIZE 4040 41static struct tty_driver *tty3270_driver;42static int tty3270_max_index;43static struct raw3270_fn tty3270_fn;44 45#define TTY3270_HIGHLIGHT_BLINK 146#define TTY3270_HIGHLIGHT_REVERSE 247#define TTY3270_HIGHLIGHT_UNDERSCORE 448 49struct tty3270_attribute {50 unsigned char alternate_charset:1; /* Graphics charset */51 unsigned char highlight:3; /* Blink/reverse/underscore */52 unsigned char f_color:4; /* Foreground color */53 unsigned char b_color:4; /* Background color */54};55 56struct tty3270_cell {57 u8 character;58 struct tty3270_attribute attributes;59};60 61struct tty3270_line {62 struct tty3270_cell *cells;63 int len;64 int dirty;65};66 67static const unsigned char sfq_read_partition[] = {68 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x8169};70 71#define ESCAPE_NPAR 872 73/*74 * The main tty view data structure.75 * FIXME:76 * 1) describe line orientation & lines list concept against screen77 * 2) describe conversion of screen to lines78 * 3) describe line format.79 */80struct tty3270 {81 struct raw3270_view view;82 struct tty_port port;83 84 /* Output stuff. */85 unsigned char wcc; /* Write control character. */86 int nr_up; /* # lines up in history. */87 unsigned long update_flags; /* Update indication bits. */88 struct raw3270_request *write; /* Single write request. */89 struct timer_list timer; /* Output delay timer. */90 char *converted_line; /* RAW 3270 data stream */91 unsigned int line_view_start; /* Start of visible area */92 unsigned int line_write_start; /* current write position */93 unsigned int oops_line; /* line counter used when print oops */94 95 /* Current tty screen. */96 unsigned int cx, cy; /* Current output position. */97 struct tty3270_attribute attributes;98 struct tty3270_attribute saved_attributes;99 int allocated_lines;100 struct tty3270_line *screen;101 102 /* Input stuff. */103 char *prompt; /* Output string for input area. */104 char *input; /* Input string for read request. */105 struct raw3270_request *read; /* Single read request. */106 struct raw3270_request *kreset; /* Single keyboard reset request. */107 struct raw3270_request *readpartreq;108 unsigned char inattr; /* Visible/invisible input. */109 int throttle, attn; /* tty throttle/unthrottle. */110 struct tasklet_struct readlet; /* Tasklet to issue read request. */111 struct tasklet_struct hanglet; /* Tasklet to hang up the tty. */112 struct kbd_data *kbd; /* key_maps stuff. */113 114 /* Escape sequence parsing. */115 int esc_state, esc_ques, esc_npar;116 int esc_par[ESCAPE_NPAR];117 unsigned int saved_cx, saved_cy;118 119 /* Command recalling. */120 char **rcl_lines; /* Array of recallable lines */121 int rcl_write_index; /* Write index of recallable items */122 int rcl_read_index; /* Read index of recallable items */123 124 /* Character array for put_char/flush_chars. */125 unsigned int char_count;126 u8 char_buf[TTY3270_CHAR_BUF_SIZE];127};128 129/* tty3270->update_flags. See tty3270_update for details. */130#define TTY_UPDATE_INPUT 0x1 /* Update input line. */131#define TTY_UPDATE_STATUS 0x2 /* Update status line. */132#define TTY_UPDATE_LINES 0x4 /* Update visible screen lines */133#define TTY_UPDATE_ALL 0x7 /* Recreate screen. */134 135#define TTY3270_INPUT_AREA_ROWS 2136 137/*138 * Setup timeout for a device. On timeout trigger an update.139 */140static void tty3270_set_timer(struct tty3270 *tp, int expires)141{142 mod_timer(&tp->timer, jiffies + expires);143}144 145static int tty3270_tty_rows(struct tty3270 *tp)146{147 return tp->view.rows - TTY3270_INPUT_AREA_ROWS;148}149 150static char *tty3270_add_ba(struct tty3270 *tp, char *cp, char order, int x, int y)151{152 *cp++ = order;153 raw3270_buffer_address(tp->view.dev, cp, x, y);154 return cp + 2;155}156 157static char *tty3270_add_ra(struct tty3270 *tp, char *cp, int x, int y, char c)158{159 cp = tty3270_add_ba(tp, cp, TO_RA, x, y);160 *cp++ = c;161 return cp;162}163 164static char *tty3270_add_sa(struct tty3270 *tp, char *cp, char attr, char value)165{166 *cp++ = TO_SA;167 *cp++ = attr;168 *cp++ = value;169 return cp;170}171 172static char *tty3270_add_ge(struct tty3270 *tp, char *cp, char c)173{174 *cp++ = TO_GE;175 *cp++ = c;176 return cp;177}178 179static char *tty3270_add_sf(struct tty3270 *tp, char *cp, char type)180{181 *cp++ = TO_SF;182 *cp++ = type;183 return cp;184}185 186static int tty3270_line_increment(struct tty3270 *tp, unsigned int line, unsigned int incr)187{188 return (line + incr) & (tp->allocated_lines - 1);189}190 191static struct tty3270_line *tty3270_get_write_line(struct tty3270 *tp, unsigned int num)192{193 return tp->screen + tty3270_line_increment(tp, tp->line_write_start, num);194}195 196static struct tty3270_line *tty3270_get_view_line(struct tty3270 *tp, unsigned int num)197{198 return tp->screen + tty3270_line_increment(tp, tp->line_view_start, num - tp->nr_up);199}200 201static int tty3270_input_size(int cols)202{203 return cols * 2 - 11;204}205 206static void tty3270_update_prompt(struct tty3270 *tp, char *input)207{208 strcpy(tp->prompt, input);209 tp->update_flags |= TTY_UPDATE_INPUT;210 tty3270_set_timer(tp, 1);211}212 213/*214 * The input line are the two last lines of the screen.215 */216static int tty3270_add_prompt(struct tty3270 *tp)217{218 int count = 0;219 char *cp;220 221 cp = tp->converted_line;222 cp = tty3270_add_ba(tp, cp, TO_SBA, 0, -2);223 *cp++ = tp->view.ascebc['>'];224 225 if (*tp->prompt) {226 cp = tty3270_add_sf(tp, cp, TF_INMDT);227 count = min_t(int, strlen(tp->prompt),228 tp->view.cols * 2 - TTY3270_STATUS_AREA_SIZE - 2);229 memcpy(cp, tp->prompt, count);230 cp += count;231 } else {232 cp = tty3270_add_sf(tp, cp, tp->inattr);233 }234 *cp++ = TO_IC;235 /* Clear to end of input line. */236 if (count < tp->view.cols * 2 - 11)237 cp = tty3270_add_ra(tp, cp, -TTY3270_STATUS_AREA_SIZE, -1, 0);238 return cp - tp->converted_line;239}240 241static char *tty3270_ebcdic_convert(struct tty3270 *tp, char *d, char *s)242{243 while (*s)244 *d++ = tp->view.ascebc[(int)*s++];245 return d;246}247 248/*249 * The status line is the last line of the screen. It shows the string250 * "Running"/"History X" in the lower right corner of the screen.251 */252static int tty3270_add_status(struct tty3270 *tp)253{254 char *cp = tp->converted_line;255 int len;256 257 cp = tty3270_add_ba(tp, cp, TO_SBA, -TTY3270_STATUS_AREA_SIZE, -1);258 cp = tty3270_add_sf(tp, cp, TF_LOG);259 cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_GREEN);260 cp = tty3270_ebcdic_convert(tp, cp, " 7");261 cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER);262 cp = tty3270_ebcdic_convert(tp, cp, "PrevPg");263 cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET);264 cp = tty3270_ebcdic_convert(tp, cp, " 8");265 cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER);266 cp = tty3270_ebcdic_convert(tp, cp, "NextPg");267 cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET);268 cp = tty3270_ebcdic_convert(tp, cp, " 12");269 cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER);270 cp = tty3270_ebcdic_convert(tp, cp, "Recall");271 cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET);272 cp = tty3270_ebcdic_convert(tp, cp, " ");273 if (tp->nr_up) {274 len = sprintf(cp, "History %d", -tp->nr_up);275 codepage_convert(tp->view.ascebc, cp, len);276 cp += len;277 } else {278 cp = tty3270_ebcdic_convert(tp, cp, oops_in_progress ? "Crashed" : "Running");279 }280 cp = tty3270_add_sf(tp, cp, TF_LOG);281 cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_RESET);282 return cp - (char *)tp->converted_line;283}284 285static void tty3270_blank_screen(struct tty3270 *tp)286{287 struct tty3270_line *line;288 int i;289 290 for (i = 0; i < tty3270_tty_rows(tp); i++) {291 line = tty3270_get_write_line(tp, i);292 line->len = 0;293 line->dirty = 1;294 }295 tp->nr_up = 0;296}297 298/*299 * Write request completion callback.300 */301static void tty3270_write_callback(struct raw3270_request *rq, void *data)302{303 struct tty3270 *tp = container_of(rq->view, struct tty3270, view);304 305 if (rq->rc != 0) {306 /* Write wasn't successful. Refresh all. */307 tp->update_flags = TTY_UPDATE_ALL;308 tty3270_set_timer(tp, 1);309 }310 raw3270_request_reset(rq);311 xchg(&tp->write, rq);312}313 314static int tty3270_required_length(struct tty3270 *tp, struct tty3270_line *line)315{316 unsigned char f_color, b_color, highlight;317 struct tty3270_cell *cell;318 int i, flen = 3; /* Prefix (TO_SBA). */319 320 flen += line->len;321 highlight = 0;322 f_color = TAC_RESET;323 b_color = TAC_RESET;324 325 for (i = 0, cell = line->cells; i < line->len; i++, cell++) {326 if (cell->attributes.highlight != highlight) {327 flen += 3; /* TO_SA to switch highlight. */328 highlight = cell->attributes.highlight;329 }330 if (cell->attributes.f_color != f_color) {331 flen += 3; /* TO_SA to switch color. */332 f_color = cell->attributes.f_color;333 }334 if (cell->attributes.b_color != b_color) {335 flen += 3; /* TO_SA to switch color. */336 b_color = cell->attributes.b_color;337 }338 if (cell->attributes.alternate_charset)339 flen += 1; /* TO_GE to switch to graphics extensions */340 }341 if (highlight)342 flen += 3; /* TO_SA to reset hightlight. */343 if (f_color != TAC_RESET)344 flen += 3; /* TO_SA to reset color. */345 if (b_color != TAC_RESET)346 flen += 3; /* TO_SA to reset color. */347 if (line->len < tp->view.cols)348 flen += 4; /* Postfix (TO_RA). */349 350 return flen;351}352 353static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_line *line,354 char *cp, struct tty3270_attribute *attr, int lineno)355{356 if (attr->highlight)357 cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET);358 if (attr->f_color != TAC_RESET)359 cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAX_RESET);360 if (attr->b_color != TAC_RESET)361 cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, TAX_RESET);362 if (line->len < tp->view.cols)363 cp = tty3270_add_ra(tp, cp, 0, lineno + 1, 0);364 return cp;365}366 367static char tty3270_graphics_translate(struct tty3270 *tp, char ch)368{369 switch (ch) {370 case 'q': /* - */371 return 0xa2;372 case 'x': /* '|' */373 return 0x85;374 case 'l': /* |- */375 return 0xc5;376 case 't': /* |_ */377 return 0xc6;378 case 'u': /* _| */379 return 0xd6;380 case 'k': /* -| */381 return 0xd5;382 case 'j':383 return 0xd4;384 case 'm':385 return 0xc4;386 case 'n': /* + */387 return 0xd3;388 case 'v':389 return 0xc7;390 case 'w':391 return 0xd7;392 default:393 return ch;394 }395}396 397static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *line,398 struct tty3270_attribute *attr, char *cp, int lineno)399{400 const unsigned char colors[16] = {401 [0] = TAC_DEFAULT,402 [1] = TAC_RED,403 [2] = TAC_GREEN,404 [3] = TAC_YELLOW,405 [4] = TAC_BLUE,406 [5] = TAC_PINK,407 [6] = TAC_TURQ,408 [7] = TAC_WHITE,409 [9] = TAC_DEFAULT410 };411 412 const unsigned char highlights[8] = {413 [TTY3270_HIGHLIGHT_BLINK] = TAX_BLINK,414 [TTY3270_HIGHLIGHT_REVERSE] = TAX_REVER,415 [TTY3270_HIGHLIGHT_UNDERSCORE] = TAX_UNDER,416 };417 418 struct tty3270_cell *cell;419 int c, i;420 421 cp = tty3270_add_ba(tp, cp, TO_SBA, 0, lineno);422 423 for (i = 0, cell = line->cells; i < line->len; i++, cell++) {424 if (cell->attributes.highlight != attr->highlight) {425 attr->highlight = cell->attributes.highlight;426 cp = tty3270_add_sa(tp, cp, TAT_EXTHI, highlights[attr->highlight]);427 }428 if (cell->attributes.f_color != attr->f_color) {429 attr->f_color = cell->attributes.f_color;430 cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, colors[attr->f_color]);431 }432 if (cell->attributes.b_color != attr->b_color) {433 attr->b_color = cell->attributes.b_color;434 cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, colors[attr->b_color]);435 }436 c = cell->character;437 if (cell->attributes.alternate_charset)438 cp = tty3270_add_ge(tp, cp, tty3270_graphics_translate(tp, c));439 else440 *cp++ = tp->view.ascebc[c];441 }442 return cp;443}444 445static void tty3270_reset_attributes(struct tty3270_attribute *attr)446{447 attr->highlight = TAX_RESET;448 attr->f_color = TAC_RESET;449 attr->b_color = TAC_RESET;450}451 452/*453 * Convert a tty3270_line to a 3270 data fragment usable for output.454 */455static unsigned int tty3270_convert_line(struct tty3270 *tp, struct tty3270_line *line, int lineno)456{457 struct tty3270_attribute attr;458 int flen;459 char *cp;460 461 /* Determine how long the fragment will be. */462 flen = tty3270_required_length(tp, line);463 if (flen > PAGE_SIZE)464 return 0;465 /* Write 3270 data fragment. */466 tty3270_reset_attributes(&attr);467 cp = tty3270_add_attributes(tp, line, &attr, tp->converted_line, lineno);468 cp = tty3270_add_reset_attributes(tp, line, cp, &attr, lineno);469 return cp - (char *)tp->converted_line;470}471 472static void tty3270_update_lines_visible(struct tty3270 *tp, struct raw3270_request *rq)473{474 struct tty3270_line *line;475 int len, i;476 477 for (i = 0; i < tty3270_tty_rows(tp); i++) {478 line = tty3270_get_view_line(tp, i);479 if (!line->dirty)480 continue;481 len = tty3270_convert_line(tp, line, i);482 if (raw3270_request_add_data(rq, tp->converted_line, len))483 break;484 line->dirty = 0;485 }486 if (i == tty3270_tty_rows(tp)) {487 for (i = 0; i < tp->allocated_lines; i++)488 tp->screen[i].dirty = 0;489 tp->update_flags &= ~TTY_UPDATE_LINES;490 }491}492 493static void tty3270_update_lines_all(struct tty3270 *tp, struct raw3270_request *rq)494{495 struct tty3270_line *line;496 char buf[4];497 int len, i;498 499 for (i = 0; i < tp->allocated_lines; i++) {500 line = tty3270_get_write_line(tp, i + tp->cy + 1);501 if (!line->dirty)502 continue;503 len = tty3270_convert_line(tp, line, tp->oops_line);504 if (raw3270_request_add_data(rq, tp->converted_line, len))505 break;506 line->dirty = 0;507 if (++tp->oops_line >= tty3270_tty_rows(tp))508 tp->oops_line = 0;509 }510 511 if (i == tp->allocated_lines) {512 if (tp->oops_line < tty3270_tty_rows(tp)) {513 tty3270_add_ra(tp, buf, 0, tty3270_tty_rows(tp), 0);514 if (raw3270_request_add_data(rq, buf, sizeof(buf)))515 return;516 }517 tp->update_flags &= ~TTY_UPDATE_LINES;518 }519}520 521/*522 * Update 3270 display.523 */524static void tty3270_update(struct timer_list *t)525{526 struct tty3270 *tp = from_timer(tp, t, timer);527 struct raw3270_request *wrq;528 u8 cmd = TC_WRITE;529 int rc, len;530 531 wrq = xchg(&tp->write, 0);532 if (!wrq) {533 tty3270_set_timer(tp, 1);534 return;535 }536 537 spin_lock_irq(&tp->view.lock);538 if (tp->update_flags == TTY_UPDATE_ALL)539 cmd = TC_EWRITEA;540 541 raw3270_request_set_cmd(wrq, cmd);542 raw3270_request_add_data(wrq, &tp->wcc, 1);543 tp->wcc = TW_NONE;544 545 /*546 * Update status line.547 */548 if (tp->update_flags & TTY_UPDATE_STATUS) {549 len = tty3270_add_status(tp);550 if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0)551 tp->update_flags &= ~TTY_UPDATE_STATUS;552 }553 554 /*555 * Write input line.556 */557 if (tp->update_flags & TTY_UPDATE_INPUT) {558 len = tty3270_add_prompt(tp);559 if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0)560 tp->update_flags &= ~TTY_UPDATE_INPUT;561 }562 563 if (tp->update_flags & TTY_UPDATE_LINES) {564 if (oops_in_progress)565 tty3270_update_lines_all(tp, wrq);566 else567 tty3270_update_lines_visible(tp, wrq);568 }569 570 wrq->callback = tty3270_write_callback;571 rc = raw3270_start(&tp->view, wrq);572 if (rc == 0) {573 if (tp->update_flags)574 tty3270_set_timer(tp, 1);575 } else {576 raw3270_request_reset(wrq);577 xchg(&tp->write, wrq);578 }579 spin_unlock_irq(&tp->view.lock);580}581 582/*583 * Command recalling.584 */585static void tty3270_rcl_add(struct tty3270 *tp, char *input, int len)586{587 char *p;588 589 if (len <= 0)590 return;591 p = tp->rcl_lines[tp->rcl_write_index++];592 tp->rcl_write_index &= TTY3270_RECALL_SIZE - 1;593 memcpy(p, input, len);594 p[len] = '\0';595 tp->rcl_read_index = tp->rcl_write_index;596}597 598static void tty3270_rcl_backward(struct kbd_data *kbd)599{600 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);601 int i = 0;602 603 spin_lock_irq(&tp->view.lock);604 if (tp->inattr == TF_INPUT) {605 do {606 tp->rcl_read_index--;607 tp->rcl_read_index &= TTY3270_RECALL_SIZE - 1;608 } while (!*tp->rcl_lines[tp->rcl_read_index] &&609 i++ < TTY3270_RECALL_SIZE - 1);610 tty3270_update_prompt(tp, tp->rcl_lines[tp->rcl_read_index]);611 }612 spin_unlock_irq(&tp->view.lock);613}614 615/*616 * Deactivate tty view.617 */618static void tty3270_exit_tty(struct kbd_data *kbd)619{620 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);621 622 raw3270_deactivate_view(&tp->view);623}624 625static void tty3270_redraw(struct tty3270 *tp)626{627 int i;628 629 for (i = 0; i < tty3270_tty_rows(tp); i++)630 tty3270_get_view_line(tp, i)->dirty = 1;631 tp->update_flags = TTY_UPDATE_ALL;632 tty3270_set_timer(tp, 1);633}634 635/*636 * Scroll forward in history.637 */638static void tty3270_scroll_forward(struct kbd_data *kbd)639{640 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);641 642 spin_lock_irq(&tp->view.lock);643 644 if (tp->nr_up >= tty3270_tty_rows(tp))645 tp->nr_up -= tty3270_tty_rows(tp) / 2;646 else647 tp->nr_up = 0;648 tty3270_redraw(tp);649 spin_unlock_irq(&tp->view.lock);650}651 652/*653 * Scroll backward in history.654 */655static void tty3270_scroll_backward(struct kbd_data *kbd)656{657 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);658 659 spin_lock_irq(&tp->view.lock);660 tp->nr_up += tty3270_tty_rows(tp) / 2;661 if (tp->nr_up > tp->allocated_lines - tty3270_tty_rows(tp))662 tp->nr_up = tp->allocated_lines - tty3270_tty_rows(tp);663 tty3270_redraw(tp);664 spin_unlock_irq(&tp->view.lock);665}666 667/*668 * Pass input line to tty.669 */670static void tty3270_read_tasklet(unsigned long data)671{672 struct raw3270_request *rrq = (struct raw3270_request *)data;673 static char kreset_data = TW_KR;674 struct tty3270 *tp = container_of(rrq->view, struct tty3270, view);675 char *input;676 int len;677 678 spin_lock_irq(&tp->view.lock);679 /*680 * Two AID keys are special: For 0x7d (enter) the input line681 * has to be emitted to the tty and for 0x6d the screen682 * needs to be redrawn.683 */684 input = NULL;685 len = 0;686 switch (tp->input[0]) {687 case AID_ENTER:688 /* Enter: write input to tty. */689 input = tp->input + 6;690 len = tty3270_input_size(tp->view.cols) - 6 - rrq->rescnt;691 if (tp->inattr != TF_INPUTN)692 tty3270_rcl_add(tp, input, len);693 if (tp->nr_up > 0)694 tp->nr_up = 0;695 /* Clear input area. */696 tty3270_update_prompt(tp, "");697 tty3270_set_timer(tp, 1);698 break;699 case AID_CLEAR:700 /* Display has been cleared. Redraw. */701 tp->update_flags = TTY_UPDATE_ALL;702 tty3270_set_timer(tp, 1);703 if (!list_empty(&tp->readpartreq->list))704 break;705 raw3270_start_request(&tp->view, tp->readpartreq, TC_WRITESF,706 (char *)sfq_read_partition, sizeof(sfq_read_partition));707 break;708 case AID_READ_PARTITION:709 raw3270_read_modified_cb(tp->readpartreq, tp->input);710 break;711 default:712 break;713 }714 spin_unlock_irq(&tp->view.lock);715 716 /* Start keyboard reset command. */717 raw3270_start_request(&tp->view, tp->kreset, TC_WRITE, &kreset_data, 1);718 719 while (len-- > 0)720 kbd_keycode(tp->kbd, *input++);721 /* Emit keycode for AID byte. */722 kbd_keycode(tp->kbd, 256 + tp->input[0]);723 724 raw3270_request_reset(rrq);725 xchg(&tp->read, rrq);726 raw3270_put_view(&tp->view);727}728 729/*730 * Read request completion callback.731 */732static void tty3270_read_callback(struct raw3270_request *rq, void *data)733{734 struct tty3270 *tp = container_of(rq->view, struct tty3270, view);735 736 raw3270_get_view(rq->view);737 /* Schedule tasklet to pass input to tty. */738 tasklet_schedule(&tp->readlet);739}740 741/*742 * Issue a read request. Call with device lock.743 */744static void tty3270_issue_read(struct tty3270 *tp, int lock)745{746 struct raw3270_request *rrq;747 int rc;748 749 rrq = xchg(&tp->read, 0);750 if (!rrq)751 /* Read already scheduled. */752 return;753 rrq->callback = tty3270_read_callback;754 rrq->callback_data = tp;755 raw3270_request_set_cmd(rrq, TC_READMOD);756 raw3270_request_set_data(rrq, tp->input, tty3270_input_size(tp->view.cols));757 /* Issue the read modified request. */758 if (lock)759 rc = raw3270_start(&tp->view, rrq);760 else761 rc = raw3270_start_irq(&tp->view, rrq);762 if (rc) {763 raw3270_request_reset(rrq);764 xchg(&tp->read, rrq);765 }766}767 768/*769 * Hang up the tty770 */771static void tty3270_hangup_tasklet(unsigned long data)772{773 struct tty3270 *tp = (struct tty3270 *)data;774 775 tty_port_tty_hangup(&tp->port, true);776 raw3270_put_view(&tp->view);777}778 779/*780 * Switch to the tty view.781 */782static int tty3270_activate(struct raw3270_view *view)783{784 struct tty3270 *tp = container_of(view, struct tty3270, view);785 786 tp->update_flags = TTY_UPDATE_ALL;787 tty3270_set_timer(tp, 1);788 return 0;789}790 791static void tty3270_deactivate(struct raw3270_view *view)792{793 struct tty3270 *tp = container_of(view, struct tty3270, view);794 795 del_timer(&tp->timer);796}797 798static void tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)799{800 /* Handle ATTN. Schedule tasklet to read aid. */801 if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {802 if (!tp->throttle)803 tty3270_issue_read(tp, 0);804 else805 tp->attn = 1;806 }807 808 if (rq) {809 if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {810 rq->rc = -EIO;811 raw3270_get_view(&tp->view);812 tasklet_schedule(&tp->hanglet);813 } else {814 /* Normal end. Copy residual count. */815 rq->rescnt = irb->scsw.cmd.count;816 }817 } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {818 /* Interrupt without an outstanding request -> update all */819 tp->update_flags = TTY_UPDATE_ALL;820 tty3270_set_timer(tp, 1);821 }822}823 824/*825 * Allocate tty3270 structure.826 */827static struct tty3270 *tty3270_alloc_view(void)828{829 struct tty3270 *tp;830 831 tp = kzalloc(sizeof(*tp), GFP_KERNEL);832 if (!tp)833 goto out_err;834 835 tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE);836 if (IS_ERR(tp->write))837 goto out_tp;838 tp->read = raw3270_request_alloc(0);839 if (IS_ERR(tp->read))840 goto out_write;841 tp->kreset = raw3270_request_alloc(1);842 if (IS_ERR(tp->kreset))843 goto out_read;844 tp->readpartreq = raw3270_request_alloc(sizeof(sfq_read_partition));845 if (IS_ERR(tp->readpartreq))846 goto out_reset;847 tp->kbd = kbd_alloc();848 if (!tp->kbd)849 goto out_readpartreq;850 851 tty_port_init(&tp->port);852 timer_setup(&tp->timer, tty3270_update, 0);853 tasklet_init(&tp->readlet, tty3270_read_tasklet,854 (unsigned long)tp->read);855 tasklet_init(&tp->hanglet, tty3270_hangup_tasklet,856 (unsigned long)tp);857 return tp;858 859out_readpartreq:860 raw3270_request_free(tp->readpartreq);861out_reset:862 raw3270_request_free(tp->kreset);863out_read:864 raw3270_request_free(tp->read);865out_write:866 raw3270_request_free(tp->write);867out_tp:868 kfree(tp);869out_err:870 return ERR_PTR(-ENOMEM);871}872 873/*874 * Free tty3270 structure.875 */876static void tty3270_free_view(struct tty3270 *tp)877{878 kbd_free(tp->kbd);879 raw3270_request_free(tp->kreset);880 raw3270_request_free(tp->read);881 raw3270_request_free(tp->write);882 free_page((unsigned long)tp->converted_line);883 tty_port_destroy(&tp->port);884 kfree(tp);885}886 887/*888 * Allocate tty3270 screen.889 */890static struct tty3270_line *tty3270_alloc_screen(struct tty3270 *tp, unsigned int rows,891 unsigned int cols, int *allocated_out)892{893 struct tty3270_line *screen;894 int allocated, lines;895 896 allocated = __roundup_pow_of_two(rows) * TTY3270_SCREEN_PAGES;897 screen = kcalloc(allocated, sizeof(struct tty3270_line), GFP_KERNEL);898 if (!screen)899 goto out_err;900 for (lines = 0; lines < allocated; lines++) {901 screen[lines].cells = kcalloc(cols, sizeof(struct tty3270_cell), GFP_KERNEL);902 if (!screen[lines].cells)903 goto out_screen;904 }905 *allocated_out = allocated;906 return screen;907out_screen:908 while (lines--)909 kfree(screen[lines].cells);910 kfree(screen);911out_err:912 return ERR_PTR(-ENOMEM);913}914 915static char **tty3270_alloc_recall(int cols)916{917 char **lines;918 int i;919 920 lines = kmalloc_array(TTY3270_RECALL_SIZE, sizeof(char *), GFP_KERNEL);921 if (!lines)922 return NULL;923 for (i = 0; i < TTY3270_RECALL_SIZE; i++) {924 lines[i] = kcalloc(1, tty3270_input_size(cols) + 1, GFP_KERNEL);925 if (!lines[i])926 break;927 }928 929 if (i == TTY3270_RECALL_SIZE)930 return lines;931 932 while (i--)933 kfree(lines[i]);934 kfree(lines);935 return NULL;936}937 938static void tty3270_free_recall(char **lines)939{940 int i;941 942 for (i = 0; i < TTY3270_RECALL_SIZE; i++)943 kfree(lines[i]);944 kfree(lines);945}946 947/*948 * Free tty3270 screen.949 */950static void tty3270_free_screen(struct tty3270_line *screen, int old_lines)951{952 int lines;953 954 for (lines = 0; lines < old_lines; lines++)955 kfree(screen[lines].cells);956 kfree(screen);957}958 959/*960 * Resize tty3270 screen961 */962static void tty3270_resize(struct raw3270_view *view,963 int new_model, int new_rows, int new_cols,964 int old_model, int old_rows, int old_cols)965{966 struct tty3270 *tp = container_of(view, struct tty3270, view);967 struct tty3270_line *screen, *oscreen;968 char **old_rcl_lines, **new_rcl_lines;969 char *old_prompt, *new_prompt;970 char *old_input, *new_input;971 struct tty_struct *tty;972 struct winsize ws;973 int new_allocated, old_allocated = tp->allocated_lines;974 975 if (old_model == new_model &&976 old_cols == new_cols &&977 old_rows == new_rows) {978 spin_lock_irq(&tp->view.lock);979 tty3270_redraw(tp);980 spin_unlock_irq(&tp->view.lock);981 return;982 }983 984 new_input = kzalloc(tty3270_input_size(new_cols), GFP_KERNEL | GFP_DMA);985 if (!new_input)986 return;987 new_prompt = kzalloc(tty3270_input_size(new_cols), GFP_KERNEL);988 if (!new_prompt)989 goto out_input;990 screen = tty3270_alloc_screen(tp, new_rows, new_cols, &new_allocated);991 if (IS_ERR(screen))992 goto out_prompt;993 new_rcl_lines = tty3270_alloc_recall(new_cols);994 if (!new_rcl_lines)995 goto out_screen;996 997 /* Switch to new output size */998 spin_lock_irq(&tp->view.lock);999 tty3270_blank_screen(tp);1000 oscreen = tp->screen;1001 tp->screen = screen;1002 tp->allocated_lines = new_allocated;1003 tp->view.rows = new_rows;1004 tp->view.cols = new_cols;1005 tp->view.model = new_model;1006 tp->update_flags = TTY_UPDATE_ALL;1007 old_input = tp->input;1008 old_prompt = tp->prompt;1009 old_rcl_lines = tp->rcl_lines;1010 tp->input = new_input;1011 tp->prompt = new_prompt;1012 tp->rcl_lines = new_rcl_lines;1013 tp->rcl_read_index = 0;1014 tp->rcl_write_index = 0;1015 spin_unlock_irq(&tp->view.lock);1016 tty3270_free_screen(oscreen, old_allocated);1017 kfree(old_input);1018 kfree(old_prompt);1019 tty3270_free_recall(old_rcl_lines);1020 tty3270_set_timer(tp, 1);1021 /* Informat tty layer about new size */1022 tty = tty_port_tty_get(&tp->port);1023 if (!tty)1024 return;1025 ws.ws_row = tty3270_tty_rows(tp);1026 ws.ws_col = tp->view.cols;1027 tty_do_resize(tty, &ws);1028 tty_kref_put(tty);1029 return;1030out_screen:1031 tty3270_free_screen(screen, new_rows);1032out_prompt:1033 kfree(new_prompt);1034out_input:1035 kfree(new_input);1036}1037 1038/*1039 * Unlink tty3270 data structure from tty.1040 */1041static void tty3270_release(struct raw3270_view *view)1042{1043 struct tty3270 *tp = container_of(view, struct tty3270, view);1044 struct tty_struct *tty = tty_port_tty_get(&tp->port);1045 1046 if (tty) {1047 tty->driver_data = NULL;1048 tty_port_tty_set(&tp->port, NULL);1049 tty_hangup(tty);1050 raw3270_put_view(&tp->view);1051 tty_kref_put(tty);1052 }1053}1054 1055/*1056 * Free tty3270 data structure1057 */1058static void tty3270_free(struct raw3270_view *view)1059{1060 struct tty3270 *tp = container_of(view, struct tty3270, view);1061 1062 del_timer_sync(&tp->timer);1063 tty3270_free_screen(tp->screen, tp->allocated_lines);1064 free_page((unsigned long)tp->converted_line);1065 kfree(tp->input);1066 kfree(tp->prompt);1067 tty3270_free_view(tp);1068}1069 1070/*1071 * Delayed freeing of tty3270 views.1072 */1073static void tty3270_del_views(void)1074{1075 int i;1076 1077 for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) {1078 struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i);1079 1080 if (!IS_ERR(view))1081 raw3270_del_view(view);1082 }1083}1084 1085static struct raw3270_fn tty3270_fn = {1086 .activate = tty3270_activate,1087 .deactivate = tty3270_deactivate,1088 .intv = (void *)tty3270_irq,1089 .release = tty3270_release,1090 .free = tty3270_free,1091 .resize = tty3270_resize1092};1093 1094static int1095tty3270_create_view(int index, struct tty3270 **newtp)1096{1097 struct tty3270 *tp;1098 int rc;1099 1100 if (tty3270_max_index < index + 1)1101 tty3270_max_index = index + 1;1102 1103 /* Allocate tty3270 structure on first open. */1104 tp = tty3270_alloc_view();1105 if (IS_ERR(tp))1106 return PTR_ERR(tp);1107 1108 rc = raw3270_add_view(&tp->view, &tty3270_fn,1109 index + RAW3270_FIRSTMINOR,1110 RAW3270_VIEW_LOCK_IRQ);1111 if (rc)1112 goto out_free_view;1113 1114 tp->screen = tty3270_alloc_screen(tp, tp->view.rows, tp->view.cols,1115 &tp->allocated_lines);1116 if (IS_ERR(tp->screen)) {1117 rc = PTR_ERR(tp->screen);1118 goto out_put_view;1119 }1120 1121 tp->converted_line = (void *)__get_free_page(GFP_KERNEL);1122 if (!tp->converted_line) {1123 rc = -ENOMEM;1124 goto out_free_screen;1125 }1126 1127 tp->input = kzalloc(tty3270_input_size(tp->view.cols), GFP_KERNEL | GFP_DMA);1128 if (!tp->input) {1129 rc = -ENOMEM;1130 goto out_free_converted_line;1131 }1132 1133 tp->prompt = kzalloc(tty3270_input_size(tp->view.cols), GFP_KERNEL);1134 if (!tp->prompt) {1135 rc = -ENOMEM;1136 goto out_free_input;1137 }1138 1139 tp->rcl_lines = tty3270_alloc_recall(tp->view.cols);1140 if (!tp->rcl_lines) {1141 rc = -ENOMEM;1142 goto out_free_prompt;1143 }1144 1145 /* Create blank line for every line in the tty output area. */1146 tty3270_blank_screen(tp);1147 1148 tp->kbd->port = &tp->port;1149 tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty;1150 tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward;1151 tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward;1152 tp->kbd->fn_handler[KVAL(K_CONS)] = tty3270_rcl_backward;1153 kbd_ascebc(tp->kbd, tp->view.ascebc);1154 1155 raw3270_activate_view(&tp->view);1156 raw3270_put_view(&tp->view);1157 *newtp = tp;1158 return 0;1159 1160out_free_prompt:1161 kfree(tp->prompt);1162out_free_input:1163 kfree(tp->input);1164out_free_converted_line:1165 free_page((unsigned long)tp->converted_line);1166out_free_screen:1167 tty3270_free_screen(tp->screen, tp->view.rows);1168out_put_view:1169 raw3270_put_view(&tp->view);1170 raw3270_del_view(&tp->view);1171out_free_view:1172 tty3270_free_view(tp);1173 return rc;1174}1175 1176/*1177 * This routine is called whenever a 3270 tty is opened first time.1178 */1179static int1180tty3270_install(struct tty_driver *driver, struct tty_struct *tty)1181{1182 struct raw3270_view *view;1183 struct tty3270 *tp;1184 int rc;1185 1186 /* Check if the tty3270 is already there. */1187 view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR);1188 if (IS_ERR(view)) {1189 rc = tty3270_create_view(tty->index, &tp);1190 if (rc)1191 return rc;1192 } else {1193 tp = container_of(view, struct tty3270, view);1194 tty->driver_data = tp;1195 tp->inattr = TF_INPUT;1196 }1197 1198 tty->winsize.ws_row = tty3270_tty_rows(tp);1199 tty->winsize.ws_col = tp->view.cols;1200 rc = tty_port_install(&tp->port, driver, tty);1201 if (rc) {1202 raw3270_put_view(&tp->view);1203 return rc;1204 }1205 tty->driver_data = tp;1206 return 0;1207}1208 1209/*1210 * This routine is called whenever a 3270 tty is opened.1211 */1212static int tty3270_open(struct tty_struct *tty, struct file *filp)1213{1214 struct tty3270 *tp = tty->driver_data;1215 struct tty_port *port = &tp->port;1216 1217 port->count++;1218 tty_port_tty_set(port, tty);1219 return 0;1220}1221 1222/*1223 * This routine is called when the 3270 tty is closed. We wait1224 * for the remaining request to be completed. Then we clean up.1225 */1226static void tty3270_close(struct tty_struct *tty, struct file *filp)1227{1228 struct tty3270 *tp = tty->driver_data;1229 1230 if (tty->count > 1)1231 return;1232 if (tp)1233 tty_port_tty_set(&tp->port, NULL);1234}1235 1236static void tty3270_cleanup(struct tty_struct *tty)1237{1238 struct tty3270 *tp = tty->driver_data;1239 1240 if (tp) {1241 tty->driver_data = NULL;1242 raw3270_put_view(&tp->view);1243 }1244}1245 1246/*1247 * We always have room.1248 */1249static unsigned int tty3270_write_room(struct tty_struct *tty)1250{1251 return INT_MAX;1252}1253 1254/*1255 * Insert character into the screen at the current position with the1256 * current color and highlight. This function does NOT do cursor movement.1257 */1258static void tty3270_put_character(struct tty3270 *tp, u8 ch)1259{1260 struct tty3270_line *line;1261 struct tty3270_cell *cell;1262 1263 line = tty3270_get_write_line(tp, tp->cy);1264 if (line->len <= tp->cx) {1265 while (line->len < tp->cx) {1266 cell = line->cells + line->len;1267 cell->character = ' ';1268 cell->attributes = tp->attributes;1269 line->len++;1270 }1271 line->len++;1272 }1273 cell = line->cells + tp->cx;1274 cell->character = ch;1275 cell->attributes = tp->attributes;1276 line->dirty = 1;1277}1278 1279/*1280 * Do carriage return.1281 */1282static void tty3270_cr(struct tty3270 *tp)1283{1284 tp->cx = 0;1285}1286 1287/*1288 * Do line feed.1289 */1290static void tty3270_lf(struct tty3270 *tp)1291{1292 struct tty3270_line *line;1293 int i;1294 1295 if (tp->cy < tty3270_tty_rows(tp) - 1) {1296 tp->cy++;1297 } else {1298 tp->line_view_start = tty3270_line_increment(tp, tp->line_view_start, 1);1299 tp->line_write_start = tty3270_line_increment(tp, tp->line_write_start, 1);1300 for (i = 0; i < tty3270_tty_rows(tp); i++)1301 tty3270_get_view_line(tp, i)->dirty = 1;1302 }1303 1304 line = tty3270_get_write_line(tp, tp->cy);1305 line->len = 0;1306 line->dirty = 1;1307}1308 1309static void tty3270_ri(struct tty3270 *tp)1310{1311 if (tp->cy > 0)1312 tp->cy--;1313}1314 1315static void tty3270_reset_cell(struct tty3270 *tp, struct tty3270_cell *cell)1316{1317 cell->character = ' ';1318 tty3270_reset_attributes(&cell->attributes);1319}1320 1321/*1322 * Insert characters at current position.1323 */1324static void tty3270_insert_characters(struct tty3270 *tp, int n)1325{1326 struct tty3270_line *line;1327 int k;1328 1329 line = tty3270_get_write_line(tp, tp->cy);1330 while (line->len < tp->cx)1331 tty3270_reset_cell(tp, &line->cells[line->len++]);1332 if (n > tp->view.cols - tp->cx)1333 n = tp->view.cols - tp->cx;1334 k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n);1335 while (k--)1336 line->cells[tp->cx + n + k] = line->cells[tp->cx + k];1337 line->len += n;1338 if (line->len > tp->view.cols)1339 line->len = tp->view.cols;1340 while (n-- > 0) {1341 line->cells[tp->cx + n].character = ' ';1342 line->cells[tp->cx + n].attributes = tp->attributes;1343 }1344}1345 1346/*1347 * Delete characters at current position.1348 */1349static void tty3270_delete_characters(struct tty3270 *tp, int n)1350{1351 struct tty3270_line *line;1352 int i;1353 1354 line = tty3270_get_write_line(tp, tp->cy);1355 if (line->len <= tp->cx)1356 return;1357 if (line->len - tp->cx <= n) {1358 line->len = tp->cx;1359 return;1360 }1361 for (i = tp->cx; i + n < line->len; i++)1362 line->cells[i] = line->cells[i + n];1363 line->len -= n;1364}1365 1366/*1367 * Erase characters at current position.1368 */1369static void tty3270_erase_characters(struct tty3270 *tp, int n)1370{1371 struct tty3270_line *line;1372 struct tty3270_cell *cell;1373 1374 line = tty3270_get_write_line(tp, tp->cy);1375 while (line->len > tp->cx && n-- > 0) {1376 cell = line->cells + tp->cx++;1377 tty3270_reset_cell(tp, cell);1378 }1379 tp->cx += n;1380 tp->cx = min_t(int, tp->cx, tp->view.cols - 1);1381}1382 1383/*1384 * Erase line, 3 different cases:1385 * Esc [ 0 K Erase from current position to end of line inclusive1386 * Esc [ 1 K Erase from beginning of line to current position inclusive1387 * Esc [ 2 K Erase entire line (without moving cursor)1388 */1389static void tty3270_erase_line(struct tty3270 *tp, int mode)1390{1391 struct tty3270_line *line;1392 struct tty3270_cell *cell;1393 int i, start, end;1394 1395 line = tty3270_get_write_line(tp, tp->cy);1396 1397 switch (mode) {1398 case 0:1399 start = tp->cx;1400 end = tp->view.cols;1401 break;1402 case 1:1403 start = 0;1404 end = tp->cx;1405 break;1406 case 2:1407 start = 0;1408 end = tp->view.cols;1409 break;1410 default:1411 return;1412 }1413 1414 for (i = start; i < end; i++) {1415 cell = line->cells + i;1416 tty3270_reset_cell(tp, cell);1417 cell->attributes.b_color = tp->attributes.b_color;1418 }1419 1420 if (line->len <= end)1421 line->len = end;1422}1423 1424/*1425 * Erase display, 3 different cases:1426 * Esc [ 0 J Erase from current position to bottom of screen inclusive1427 * Esc [ 1 J Erase from top of screen to current position inclusive1428 * Esc [ 2 J Erase entire screen (without moving the cursor)1429 */1430static void tty3270_erase_display(struct tty3270 *tp, int mode)1431{1432 struct tty3270_line *line;1433 int i, start, end;1434 1435 switch (mode) {1436 case 0:1437 tty3270_erase_line(tp, 0);1438 start = tp->cy + 1;1439 end = tty3270_tty_rows(tp);1440 break;1441 case 1:1442 start = 0;1443 end = tp->cy;1444 tty3270_erase_line(tp, 1);1445 break;1446 case 2:1447 start = 0;1448 end = tty3270_tty_rows(tp);1449 break;1450 default:1451 return;1452 }1453 for (i = start; i < end; i++) {1454 line = tty3270_get_write_line(tp, i);1455 line->len = 0;1456 line->dirty = 1;1457 }1458}1459 1460/*1461 * Set attributes found in an escape sequence.1462 * Esc [ <attr> ; <attr> ; ... m1463 */1464static void tty3270_set_attributes(struct tty3270 *tp)1465{1466 int i, attr;1467 1468 for (i = 0; i <= tp->esc_npar; i++) {1469 attr = tp->esc_par[i];1470 switch (attr) {1471 case 0: /* Reset */1472 tty3270_reset_attributes(&tp->attributes);1473 break;1474 /* Highlight. */1475 case 4: /* Start underlining. */1476 tp->attributes.highlight = TTY3270_HIGHLIGHT_UNDERSCORE;1477 break;1478 case 5: /* Start blink. */1479 tp->attributes.highlight = TTY3270_HIGHLIGHT_BLINK;1480 break;1481 case 7: /* Start reverse. */1482 tp->attributes.highlight = TTY3270_HIGHLIGHT_REVERSE;1483 break;1484 case 24: /* End underlining */1485 tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_UNDERSCORE;1486 break;1487 case 25: /* End blink. */1488 tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_BLINK;1489 break;1490 case 27: /* End reverse. */1491 tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_REVERSE;1492 break;1493 /* Foreground color. */1494 case 30: /* Black */1495 case 31: /* Red */1496 case 32: /* Green */1497 case 33: /* Yellow */1498 case 34: /* Blue */1499 case 35: /* Magenta */1500 case 36: /* Cyan */1501 case 37: /* White */1502 case 39: /* Black */1503 tp->attributes.f_color = attr - 30;1504 break;1505 /* Background color. */1506 case 40: /* Black */1507 case 41: /* Red */1508 case 42: /* Green */1509 case 43: /* Yellow */1510 case 44: /* Blue */1511 case 45: /* Magenta */1512 case 46: /* Cyan */1513 case 47: /* White */1514 case 49: /* Black */1515 tp->attributes.b_color = attr - 40;1516 break;1517 }1518 }1519}1520 1521static inline int tty3270_getpar(struct tty3270 *tp, int ix)1522{1523 return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1;1524}1525 1526static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy)1527{1528 struct tty3270_line *line;1529 struct tty3270_cell *cell;1530 int max_cx = max(0, cx);1531 int max_cy = max(0, cy);1532 1533 tp->cx = min_t(int, tp->view.cols - 1, max_cx);1534 line = tty3270_get_write_line(tp, tp->cy);1535 while (line->len < tp->cx) {1536 cell = line->cells + line->len;1537 cell->character = ' ';1538 cell->attributes = tp->attributes;1539 line->len++;1540 }1541 tp->cy = min_t(int, tty3270_tty_rows(tp) - 1, max_cy);1542}1543 1544/*1545 * Process escape sequences. Known sequences:1546 * Esc 7 Save Cursor Position1547 * Esc 8 Restore Cursor Position1548 * Esc [ Pn ; Pn ; .. m Set attributes1549 * Esc [ Pn ; Pn H Cursor Position1550 * Esc [ Pn ; Pn f Cursor Position1551 * Esc [ Pn A Cursor Up1552 * Esc [ Pn B Cursor Down1553 * Esc [ Pn C Cursor Forward1554 * Esc [ Pn D Cursor Backward1555 * Esc [ Pn G Cursor Horizontal Absolute1556 * Esc [ Pn X Erase Characters1557 * Esc [ Ps J Erase in Display1558 * Esc [ Ps K Erase in Line1559 * // FIXME: add all the new ones.1560 *1561 * Pn is a numeric parameter, a string of zero or more decimal digits.1562 * Ps is a selective parameter.1563 */1564static void tty3270_escape_sequence(struct tty3270 *tp, u8 ch)1565{1566 enum { ES_NORMAL, ES_ESC, ES_SQUARE, ES_PAREN, ES_GETPARS };1567 1568 if (tp->esc_state == ES_NORMAL) {1569 if (ch == 0x1b)1570 /* Starting new escape sequence. */1571 tp->esc_state = ES_ESC;1572 return;1573 }1574 if (tp->esc_state == ES_ESC) {1575 tp->esc_state = ES_NORMAL;1576 switch (ch) {1577 case '[':1578 tp->esc_state = ES_SQUARE;1579 break;1580 case '(':1581 tp->esc_state = ES_PAREN;1582 break;1583 case 'E':1584 tty3270_cr(tp);1585 tty3270_lf(tp);1586 break;1587 case 'M':1588 tty3270_ri(tp);1589 break;1590 case 'D':1591 tty3270_lf(tp);1592 break;1593 case 'Z': /* Respond ID. */1594 kbd_puts_queue(&tp->port, "\033[?6c");1595 break;1596 case '7': /* Save cursor position. */1597 tp->saved_cx = tp->cx;1598 tp->saved_cy = tp->cy;1599 tp->saved_attributes = tp->attributes;1600 break;1601 case '8': /* Restore cursor position. */1602 tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);1603 tp->attributes = tp->saved_attributes;1604 break;1605 case 'c': /* Reset terminal. */1606 tp->cx = 0;1607 tp->cy = 0;1608 tp->saved_cx = 0;1609 tp->saved_cy = 0;1610 tty3270_reset_attributes(&tp->attributes);1611 tty3270_reset_attributes(&tp->saved_attributes);1612 tty3270_erase_display(tp, 2);1613 break;1614 }1615 return;1616 }1617 1618 switch (tp->esc_state) {1619 case ES_PAREN:1620 tp->esc_state = ES_NORMAL;1621 switch (ch) {1622 case 'B':1623 tp->attributes.alternate_charset = 0;1624 break;1625 case '0':1626 tp->attributes.alternate_charset = 1;1627 break;1628 }1629 return;1630 case ES_SQUARE:1631 tp->esc_state = ES_GETPARS;1632 memset(tp->esc_par, 0, sizeof(tp->esc_par));1633 tp->esc_npar = 0;1634 tp->esc_ques = (ch == '?');1635 if (tp->esc_ques)1636 return;1637 fallthrough;1638 case ES_GETPARS:1639 if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) {1640 tp->esc_npar++;1641 return;1642 }1643 if (ch >= '0' && ch <= '9') {1644 tp->esc_par[tp->esc_npar] *= 10;1645 tp->esc_par[tp->esc_npar] += ch - '0';1646 return;1647 }1648 break;1649 default:1650 break;1651 }1652 tp->esc_state = ES_NORMAL;1653 if (ch == 'n' && !tp->esc_ques) {1654 if (tp->esc_par[0] == 5) /* Status report. */1655 kbd_puts_queue(&tp->port, "\033[0n");1656 else if (tp->esc_par[0] == 6) { /* Cursor report. */1657 char buf[40];1658 1659 sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1);1660 kbd_puts_queue(&tp->port, buf);1661 }1662 return;1663 }1664 if (tp->esc_ques)1665 return;1666 switch (ch) {1667 case 'm':1668 tty3270_set_attributes(tp);1669 break;1670 case 'H': /* Set cursor position. */1671 case 'f':1672 tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1,1673 tty3270_getpar(tp, 0) - 1);1674 break;1675 case 'd': /* Set y position. */1676 tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1);1677 break;1678 case 'A': /* Cursor up. */1679 case 'F':1680 tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0));1681 break;1682 case 'B': /* Cursor down. */1683 case 'e':1684 case 'E':1685 tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0));1686 break;1687 case 'C': /* Cursor forward. */1688 case 'a':1689 tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy);1690 break;1691 case 'D': /* Cursor backward. */1692 tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy);1693 break;1694 case 'G': /* Set x position. */1695 case '`':1696 tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy);1697 break;1698 case 'X': /* Erase Characters. */1699 tty3270_erase_characters(tp, tty3270_getpar(tp, 0));1700 break;1701 case 'J': /* Erase display. */1702 tty3270_erase_display(tp, tp->esc_par[0]);1703 break;1704 case 'K': /* Erase line. */1705 tty3270_erase_line(tp, tp->esc_par[0]);1706 break;1707 case 'P': /* Delete characters. */1708 tty3270_delete_characters(tp, tty3270_getpar(tp, 0));1709 break;1710 case '@': /* Insert characters. */1711 tty3270_insert_characters(tp, tty3270_getpar(tp, 0));1712 break;1713 case 's': /* Save cursor position. */1714 tp->saved_cx = tp->cx;1715 tp->saved_cy = tp->cy;1716 tp->saved_attributes = tp->attributes;1717 break;1718 case 'u': /* Restore cursor position. */1719 tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);1720 tp->attributes = tp->saved_attributes;1721 break;1722 }1723}1724 1725/*1726 * String write routine for 3270 ttys1727 */1728static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty,1729 const u8 *buf, size_t count)1730{1731 int i_msg, i;1732 1733 spin_lock_irq(&tp->view.lock);1734 for (i_msg = 0; !tty->flow.stopped && i_msg < count; i_msg++) {1735 if (tp->esc_state != 0) {1736 /* Continue escape sequence. */1737 tty3270_escape_sequence(tp, buf[i_msg]);1738 continue;1739 }1740 1741 switch (buf[i_msg]) {1742 case 0x00:1743 break;1744 case 0x07: /* '\a' -- Alarm */1745 tp->wcc |= TW_PLUSALARM;1746 break;1747 case 0x08: /* Backspace. */1748 if (tp->cx > 0) {1749 tp->cx--;1750 tty3270_put_character(tp, ' ');1751 }1752 break;1753 case 0x09: /* '\t' -- Tabulate */1754 for (i = tp->cx % 8; i < 8; i++) {1755 if (tp->cx >= tp->view.cols) {1756 tty3270_cr(tp);1757 tty3270_lf(tp);1758 break;1759 }1760 tty3270_put_character(tp, ' ');1761 tp->cx++;1762 }1763 break;1764 case 0x0a: /* '\n' -- New Line */1765 tty3270_cr(tp);1766 tty3270_lf(tp);1767 break;1768 case 0x0c: /* '\f' -- Form Feed */1769 tty3270_erase_display(tp, 2);1770 tp->cx = 0;1771 tp->cy = 0;1772 break;1773 case 0x0d: /* '\r' -- Carriage Return */1774 tp->cx = 0;1775 break;1776 case 0x0e:1777 tp->attributes.alternate_charset = 1;1778 break;1779 case 0x0f: /* SuSE "exit alternate mode" */1780 tp->attributes.alternate_charset = 0;1781 break;1782 case 0x1b: /* Start escape sequence. */1783 tty3270_escape_sequence(tp, buf[i_msg]);1784 break;1785 default: /* Insert normal character. */1786 if (tp->cx >= tp->view.cols) {1787 tty3270_cr(tp);1788 tty3270_lf(tp);1789 }1790 tty3270_put_character(tp, buf[i_msg]);1791 tp->cx++;1792 break;1793 }1794 }1795 /* Setup timer to update display after 1/10 second */1796 tp->update_flags |= TTY_UPDATE_LINES;1797 if (!timer_pending(&tp->timer))1798 tty3270_set_timer(tp, msecs_to_jiffies(100));1799 1800 spin_unlock_irq(&tp->view.lock);1801}1802 1803/*1804 * String write routine for 3270 ttys1805 */1806static ssize_t tty3270_write(struct tty_struct *tty, const u8 *buf,1807 size_t count)1808{1809 struct tty3270 *tp;1810 1811 tp = tty->driver_data;1812 if (!tp)1813 return 0;1814 if (tp->char_count > 0) {1815 tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);1816 tp->char_count = 0;1817 }1818 tty3270_do_write(tp, tty, buf, count);1819 return count;1820}1821 1822/*1823 * Put single characters to the ttys character buffer1824 */1825static int tty3270_put_char(struct tty_struct *tty, u8 ch)1826{1827 struct tty3270 *tp;1828 1829 tp = tty->driver_data;1830 if (!tp || tp->char_count >= TTY3270_CHAR_BUF_SIZE)1831 return 0;1832 tp->char_buf[tp->char_count++] = ch;1833 return 1;1834}1835 1836/*1837 * Flush all characters from the ttys characeter buffer put there1838 * by tty3270_put_char.1839 */1840static void tty3270_flush_chars(struct tty_struct *tty)1841{1842 struct tty3270 *tp;1843 1844 tp = tty->driver_data;1845 if (!tp)1846 return;1847 if (tp->char_count > 0) {1848 tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);1849 tp->char_count = 0;1850 }1851}1852 1853/*1854 * Check for visible/invisible input switches1855 */1856static void tty3270_set_termios(struct tty_struct *tty, const struct ktermios *old)1857{1858 struct tty3270 *tp;1859 int new;1860 1861 tp = tty->driver_data;1862 if (!tp)1863 return;1864 spin_lock_irq(&tp->view.lock);1865 if (L_ICANON(tty)) {1866 new = L_ECHO(tty) ? TF_INPUT : TF_INPUTN;1867 if (new != tp->inattr) {1868 tp->inattr = new;1869 tty3270_update_prompt(tp, "");1870 tty3270_set_timer(tp, 1);1871 }1872 }1873 spin_unlock_irq(&tp->view.lock);1874}1875 1876/*1877 * Disable reading from a 3270 tty1878 */1879static void tty3270_throttle(struct tty_struct *tty)1880{1881 struct tty3270 *tp;1882 1883 tp = tty->driver_data;1884 if (!tp)1885 return;1886 tp->throttle = 1;1887}1888 1889/*1890 * Enable reading from a 3270 tty1891 */1892static void tty3270_unthrottle(struct tty_struct *tty)1893{1894 struct tty3270 *tp;1895 1896 tp = tty->driver_data;1897 if (!tp)1898 return;1899 tp->throttle = 0;1900 if (tp->attn)1901 tty3270_issue_read(tp, 1);1902}1903 1904/*1905 * Hang up the tty device.1906 */1907static void tty3270_hangup(struct tty_struct *tty)1908{1909 struct tty3270 *tp;1910 1911 tp = tty->driver_data;1912 if (!tp)1913 return;1914 spin_lock_irq(&tp->view.lock);1915 tp->cx = 0;1916 tp->cy = 0;1917 tp->saved_cx = 0;1918 tp->saved_cy = 0;1919 tty3270_reset_attributes(&tp->attributes);1920 tty3270_reset_attributes(&tp->saved_attributes);1921 tty3270_blank_screen(tp);1922 tp->update_flags = TTY_UPDATE_ALL;1923 spin_unlock_irq(&tp->view.lock);1924 tty3270_set_timer(tp, 1);1925}1926 1927static void tty3270_wait_until_sent(struct tty_struct *tty, int timeout)1928{1929}1930 1931static int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd,1932 unsigned long arg)1933{1934 struct tty3270 *tp;1935 1936 tp = tty->driver_data;1937 if (!tp)1938 return -ENODEV;1939 if (tty_io_error(tty))1940 return -EIO;1941 return kbd_ioctl(tp->kbd, cmd, arg);1942}1943 1944#ifdef CONFIG_COMPAT1945static long tty3270_compat_ioctl(struct tty_struct *tty,1946 unsigned int cmd, unsigned long arg)1947{1948 struct tty3270 *tp;1949 1950 tp = tty->driver_data;1951 if (!tp)1952 return -ENODEV;1953 if (tty_io_error(tty))1954 return -EIO;1955 return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg));1956}1957#endif1958 1959static const struct tty_operations tty3270_ops = {1960 .install = tty3270_install,1961 .cleanup = tty3270_cleanup,1962 .open = tty3270_open,1963 .close = tty3270_close,1964 .write = tty3270_write,1965 .put_char = tty3270_put_char,1966 .flush_chars = tty3270_flush_chars,1967 .write_room = tty3270_write_room,1968 .throttle = tty3270_throttle,1969 .unthrottle = tty3270_unthrottle,1970 .hangup = tty3270_hangup,1971 .wait_until_sent = tty3270_wait_until_sent,1972 .ioctl = tty3270_ioctl,1973#ifdef CONFIG_COMPAT1974 .compat_ioctl = tty3270_compat_ioctl,1975#endif1976 .set_termios = tty3270_set_termios1977};1978 1979static void tty3270_create_cb(int minor)1980{1981 tty_register_device(tty3270_driver, minor - RAW3270_FIRSTMINOR, NULL);1982}1983 1984static void tty3270_destroy_cb(int minor)1985{1986 tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR);1987}1988 1989static struct raw3270_notifier tty3270_notifier = {1990 .create = tty3270_create_cb,1991 .destroy = tty3270_destroy_cb,1992};1993 1994/*1995 * 3270 tty registration code called from tty_init().1996 * Most kernel services (incl. kmalloc) are available at this poimt.1997 */1998static int __init tty3270_init(void)1999{2000 struct tty_driver *driver;2001 int ret;2002 2003 driver = tty_alloc_driver(RAW3270_MAXDEVS,2004 TTY_DRIVER_REAL_RAW |2005 TTY_DRIVER_DYNAMIC_DEV |2006 TTY_DRIVER_RESET_TERMIOS);2007 if (IS_ERR(driver))2008 return PTR_ERR(driver);2009 2010 /*2011 * Initialize the tty_driver structure2012 * Entries in tty3270_driver that are NOT initialized:2013 * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc2014 */2015 driver->driver_name = "tty3270";2016 driver->name = "3270/tty";2017 driver->major = IBM_TTY3270_MAJOR;2018 driver->minor_start = RAW3270_FIRSTMINOR;2019 driver->name_base = RAW3270_FIRSTMINOR;2020 driver->type = TTY_DRIVER_TYPE_SYSTEM;2021 driver->subtype = SYSTEM_TYPE_TTY;2022 driver->init_termios = tty_std_termios;2023 tty_set_operations(driver, &tty3270_ops);2024 ret = tty_register_driver(driver);2025 if (ret) {2026 tty_driver_kref_put(driver);2027 return ret;2028 }2029 tty3270_driver = driver;2030 raw3270_register_notifier(&tty3270_notifier);2031 return 0;2032}2033 2034static void __exit tty3270_exit(void)2035{2036 struct tty_driver *driver;2037 2038 raw3270_unregister_notifier(&tty3270_notifier);2039 driver = tty3270_driver;2040 tty3270_driver = NULL;2041 tty_unregister_driver(driver);2042 tty_driver_kref_put(driver);2043 tty3270_del_views();2044}2045 2046#if IS_ENABLED(CONFIG_TN3270_CONSOLE)2047 2048static struct tty3270 *condev;2049 2050static void2051con3270_write(struct console *co, const char *str, unsigned int count)2052{2053 struct tty3270 *tp = co->data;2054 unsigned long flags;2055 u8 c;2056 2057 spin_lock_irqsave(&tp->view.lock, flags);2058 while (count--) {2059 c = *str++;2060 if (c == 0x0a) {2061 tty3270_cr(tp);2062 tty3270_lf(tp);2063 } else {2064 if (tp->cx >= tp->view.cols) {2065 tty3270_cr(tp);2066 tty3270_lf(tp);2067 }2068 tty3270_put_character(tp, c);2069 tp->cx++;2070 }2071 }2072 spin_unlock_irqrestore(&tp->view.lock, flags);2073}2074 2075static struct tty_driver *2076con3270_device(struct console *c, int *index)2077{2078 *index = c->index;2079 return tty3270_driver;2080}2081 2082static void2083con3270_wait_write(struct tty3270 *tp)2084{2085 while (!tp->write) {2086 raw3270_wait_cons_dev(tp->view.dev);2087 barrier();2088 }2089}2090 2091/*2092 * The below function is called as a panic/reboot notifier before the2093 * system enters a disabled, endless loop.2094 *2095 * Notice we must use the spin_trylock() alternative, to prevent lockups2096 * in atomic context (panic routine runs with secondary CPUs, local IRQs2097 * and preemption disabled).2098 */2099static int con3270_notify(struct notifier_block *self,2100 unsigned long event, void *data)2101{2102 struct tty3270 *tp;2103 unsigned long flags;2104 int rc;2105 2106 tp = condev;2107 if (!tp->view.dev)2108 return NOTIFY_DONE;2109 if (!raw3270_view_lock_unavailable(&tp->view)) {2110 rc = raw3270_activate_view(&tp->view);2111 if (rc)2112 return NOTIFY_DONE;2113 }2114 if (!spin_trylock_irqsave(&tp->view.lock, flags))2115 return NOTIFY_DONE;2116 con3270_wait_write(tp);2117 tp->nr_up = 0;2118 tp->update_flags = TTY_UPDATE_ALL;2119 while (tp->update_flags != 0) {2120 spin_unlock_irqrestore(&tp->view.lock, flags);2121 tty3270_update(&tp->timer);2122 spin_lock_irqsave(&tp->view.lock, flags);2123 con3270_wait_write(tp);2124 }2125 spin_unlock_irqrestore(&tp->view.lock, flags);2126 return NOTIFY_DONE;2127}2128 2129static struct notifier_block on_panic_nb = {2130 .notifier_call = con3270_notify,2131 .priority = INT_MIN + 1, /* run the callback late */2132};2133 2134static struct notifier_block on_reboot_nb = {2135 .notifier_call = con3270_notify,2136 .priority = INT_MIN + 1, /* run the callback late */2137};2138 2139static struct console con3270 = {2140 .name = "tty3270",2141 .write = con3270_write,2142 .device = con3270_device,2143 .flags = CON_PRINTBUFFER,2144};2145 2146static int __init2147con3270_init(void)2148{2149 struct raw3270_view *view;2150 struct raw3270 *rp;2151 struct tty3270 *tp;2152 int rc;2153 2154 /* Check if 3270 is to be the console */2155 if (!CONSOLE_IS_3270)2156 return -ENODEV;2157 2158 /* Set the console mode for VM */2159 if (MACHINE_IS_VM) {2160 cpcmd("TERM CONMODE 3270", NULL, 0, NULL);2161 cpcmd("TERM AUTOCR OFF", NULL, 0, NULL);2162 }2163 2164 rp = raw3270_setup_console();2165 if (IS_ERR(rp))2166 return PTR_ERR(rp);2167 2168 /* Check if the tty3270 is already there. */2169 view = raw3270_find_view(&tty3270_fn, RAW3270_FIRSTMINOR);2170 if (IS_ERR(view)) {2171 rc = tty3270_create_view(0, &tp);2172 if (rc)2173 return rc;2174 } else {2175 tp = container_of(view, struct tty3270, view);2176 tp->inattr = TF_INPUT;2177 }2178 con3270.data = tp;2179 condev = tp;2180 atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);2181 register_reboot_notifier(&on_reboot_nb);2182 register_console(&con3270);2183 return 0;2184}2185console_initcall(con3270_init);2186#endif2187 2188MODULE_DESCRIPTION("IBM/3270 Driver - tty functions");2189MODULE_LICENSE("GPL");2190MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR);2191 2192module_init(tty3270_init);2193module_exit(tty3270_exit);2194