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