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