tty3270.c (47070B)
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; 429 len -= 3; 430 } 431 if (raw3270_request_add_data(wrq, str, len) != 0) 432 break; 433 list_del_init(&s->update); 434 if (s->string[s->len - 4] == TO_RA) 435 sba = s->string + s->len - 3; 436 else 437 sba = invalid_sba; 438 } 439 if (list_empty(&tp->update)) 440 updated |= TTY_UPDATE_LIST; 441 } 442 wrq->callback = tty3270_write_callback; 443 rc = raw3270_start(&tp->view, wrq); 444 if (rc == 0) { 445 tp->update_flags &= ~updated; 446 if (tp->update_flags) 447 tty3270_set_timer(tp, 1); 448 } else { 449 raw3270_request_reset(wrq); 450 xchg(&tp->write, wrq); 451 } 452 spin_unlock(&tp->view.lock); 453} 454 455/* 456 * Command recalling. 457 */ 458static void 459tty3270_rcl_add(struct tty3270 *tp, char *input, int len) 460{ 461 struct string *s; 462 463 tp->rcl_walk = NULL; 464 if (len <= 0) 465 return; 466 if (tp->rcl_nr >= tp->rcl_max) { 467 s = list_entry(tp->rcl_lines.next, struct string, list); 468 list_del(&s->list); 469 free_string(&tp->freemem, s); 470 tp->rcl_nr--; 471 } 472 s = tty3270_alloc_string(tp, len); 473 memcpy(s->string, input, len); 474 list_add_tail(&s->list, &tp->rcl_lines); 475 tp->rcl_nr++; 476} 477 478static void 479tty3270_rcl_backward(struct kbd_data *kbd) 480{ 481 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); 482 struct string *s; 483 484 spin_lock_bh(&tp->view.lock); 485 if (tp->inattr == TF_INPUT) { 486 if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines) 487 tp->rcl_walk = tp->rcl_walk->prev; 488 else if (!list_empty(&tp->rcl_lines)) 489 tp->rcl_walk = tp->rcl_lines.prev; 490 s = tp->rcl_walk ? 491 list_entry(tp->rcl_walk, struct string, list) : NULL; 492 if (tp->rcl_walk) { 493 s = list_entry(tp->rcl_walk, struct string, list); 494 tty3270_update_prompt(tp, s->string, s->len); 495 } else 496 tty3270_update_prompt(tp, NULL, 0); 497 tty3270_set_timer(tp, 1); 498 } 499 spin_unlock_bh(&tp->view.lock); 500} 501 502/* 503 * Deactivate tty view. 504 */ 505static void 506tty3270_exit_tty(struct kbd_data *kbd) 507{ 508 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); 509 510 raw3270_deactivate_view(&tp->view); 511} 512 513/* 514 * Scroll forward in history. 515 */ 516static void 517tty3270_scroll_forward(struct kbd_data *kbd) 518{ 519 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); 520 int nr_up; 521 522 spin_lock_bh(&tp->view.lock); 523 nr_up = tp->nr_up - tp->view.rows + 2; 524 if (nr_up < 0) 525 nr_up = 0; 526 if (nr_up != tp->nr_up) { 527 tp->nr_up = nr_up; 528 tty3270_rebuild_update(tp); 529 tty3270_update_status(tp); 530 tty3270_set_timer(tp, 1); 531 } 532 spin_unlock_bh(&tp->view.lock); 533} 534 535/* 536 * Scroll backward in history. 537 */ 538static void 539tty3270_scroll_backward(struct kbd_data *kbd) 540{ 541 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); 542 int nr_up; 543 544 spin_lock_bh(&tp->view.lock); 545 nr_up = tp->nr_up + tp->view.rows - 2; 546 if (nr_up + tp->view.rows - 2 > tp->nr_lines) 547 nr_up = tp->nr_lines - tp->view.rows + 2; 548 if (nr_up != tp->nr_up) { 549 tp->nr_up = nr_up; 550 tty3270_rebuild_update(tp); 551 tty3270_update_status(tp); 552 tty3270_set_timer(tp, 1); 553 } 554 spin_unlock_bh(&tp->view.lock); 555} 556 557/* 558 * Pass input line to tty. 559 */ 560static void 561tty3270_read_tasklet(unsigned long data) 562{ 563 struct raw3270_request *rrq = (struct raw3270_request *)data; 564 static char kreset_data = TW_KR; 565 struct tty3270 *tp = container_of(rrq->view, struct tty3270, view); 566 char *input; 567 int len; 568 569 spin_lock_bh(&tp->view.lock); 570 /* 571 * Two AID keys are special: For 0x7d (enter) the input line 572 * has to be emitted to the tty and for 0x6d the screen 573 * needs to be redrawn. 574 */ 575 input = NULL; 576 len = 0; 577 if (tp->input->string[0] == 0x7d) { 578 /* Enter: write input to tty. */ 579 input = tp->input->string + 6; 580 len = tp->input->len - 6 - rrq->rescnt; 581 if (tp->inattr != TF_INPUTN) 582 tty3270_rcl_add(tp, input, len); 583 if (tp->nr_up > 0) { 584 tp->nr_up = 0; 585 tty3270_rebuild_update(tp); 586 tty3270_update_status(tp); 587 } 588 /* Clear input area. */ 589 tty3270_update_prompt(tp, NULL, 0); 590 tty3270_set_timer(tp, 1); 591 } else if (tp->input->string[0] == 0x6d) { 592 /* Display has been cleared. Redraw. */ 593 tp->update_flags = TTY_UPDATE_ALL; 594 tty3270_set_timer(tp, 1); 595 } 596 spin_unlock_bh(&tp->view.lock); 597 598 /* Start keyboard reset command. */ 599 raw3270_request_reset(tp->kreset); 600 raw3270_request_set_cmd(tp->kreset, TC_WRITE); 601 raw3270_request_add_data(tp->kreset, &kreset_data, 1); 602 raw3270_start(&tp->view, tp->kreset); 603 604 while (len-- > 0) 605 kbd_keycode(tp->kbd, *input++); 606 /* Emit keycode for AID byte. */ 607 kbd_keycode(tp->kbd, 256 + tp->input->string[0]); 608 609 raw3270_request_reset(rrq); 610 xchg(&tp->read, rrq); 611 raw3270_put_view(&tp->view); 612} 613 614/* 615 * Read request completion callback. 616 */ 617static void 618tty3270_read_callback(struct raw3270_request *rq, void *data) 619{ 620 struct tty3270 *tp = container_of(rq->view, struct tty3270, view); 621 raw3270_get_view(rq->view); 622 /* Schedule tasklet to pass input to tty. */ 623 tasklet_schedule(&tp->readlet); 624} 625 626/* 627 * Issue a read request. Call with device lock. 628 */ 629static void 630tty3270_issue_read(struct tty3270 *tp, int lock) 631{ 632 struct raw3270_request *rrq; 633 int rc; 634 635 rrq = xchg(&tp->read, 0); 636 if (!rrq) 637 /* Read already scheduled. */ 638 return; 639 rrq->callback = tty3270_read_callback; 640 rrq->callback_data = tp; 641 raw3270_request_set_cmd(rrq, TC_READMOD); 642 raw3270_request_set_data(rrq, tp->input->string, tp->input->len); 643 /* Issue the read modified request. */ 644 if (lock) { 645 rc = raw3270_start(&tp->view, rrq); 646 } else 647 rc = raw3270_start_irq(&tp->view, rrq); 648 if (rc) { 649 raw3270_request_reset(rrq); 650 xchg(&tp->read, rrq); 651 } 652} 653 654/* 655 * Hang up the tty 656 */ 657static void 658tty3270_hangup_tasklet(unsigned long data) 659{ 660 struct tty3270 *tp = (struct tty3270 *)data; 661 tty_port_tty_hangup(&tp->port, true); 662 raw3270_put_view(&tp->view); 663} 664 665/* 666 * Switch to the tty view. 667 */ 668static int 669tty3270_activate(struct raw3270_view *view) 670{ 671 struct tty3270 *tp = container_of(view, struct tty3270, view); 672 673 tp->update_flags = TTY_UPDATE_ALL; 674 tty3270_set_timer(tp, 1); 675 return 0; 676} 677 678static void 679tty3270_deactivate(struct raw3270_view *view) 680{ 681 struct tty3270 *tp = container_of(view, struct tty3270, view); 682 683 del_timer(&tp->timer); 684} 685 686static void 687tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) 688{ 689 /* Handle ATTN. Schedule tasklet to read aid. */ 690 if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { 691 if (!tp->throttle) 692 tty3270_issue_read(tp, 0); 693 else 694 tp->attn = 1; 695 } 696 697 if (rq) { 698 if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { 699 rq->rc = -EIO; 700 raw3270_get_view(&tp->view); 701 tasklet_schedule(&tp->hanglet); 702 } else { 703 /* Normal end. Copy residual count. */ 704 rq->rescnt = irb->scsw.cmd.count; 705 } 706 } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { 707 /* Interrupt without an outstanding request -> update all */ 708 tp->update_flags = TTY_UPDATE_ALL; 709 tty3270_set_timer(tp, 1); 710 } 711} 712 713/* 714 * Allocate tty3270 structure. 715 */ 716static struct tty3270 * 717tty3270_alloc_view(void) 718{ 719 struct tty3270 *tp; 720 int pages; 721 722 tp = kzalloc(sizeof(struct tty3270), GFP_KERNEL); 723 if (!tp) 724 goto out_err; 725 tp->freemem_pages = 726 kmalloc_array(TTY3270_STRING_PAGES, sizeof(void *), 727 GFP_KERNEL); 728 if (!tp->freemem_pages) 729 goto out_tp; 730 INIT_LIST_HEAD(&tp->freemem); 731 INIT_LIST_HEAD(&tp->lines); 732 INIT_LIST_HEAD(&tp->update); 733 INIT_LIST_HEAD(&tp->rcl_lines); 734 tp->rcl_max = 20; 735 736 for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) { 737 tp->freemem_pages[pages] = (void *) 738 __get_free_pages(GFP_KERNEL|GFP_DMA, 0); 739 if (!tp->freemem_pages[pages]) 740 goto out_pages; 741 add_string_memory(&tp->freemem, 742 tp->freemem_pages[pages], PAGE_SIZE); 743 } 744 tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE); 745 if (IS_ERR(tp->write)) 746 goto out_pages; 747 tp->read = raw3270_request_alloc(0); 748 if (IS_ERR(tp->read)) 749 goto out_write; 750 tp->kreset = raw3270_request_alloc(1); 751 if (IS_ERR(tp->kreset)) 752 goto out_read; 753 tp->kbd = kbd_alloc(); 754 if (!tp->kbd) 755 goto out_reset; 756 757 tty_port_init(&tp->port); 758 timer_setup(&tp->timer, tty3270_update, 0); 759 tasklet_init(&tp->readlet, tty3270_read_tasklet, 760 (unsigned long) tp->read); 761 tasklet_init(&tp->hanglet, tty3270_hangup_tasklet, 762 (unsigned long) tp); 763 INIT_WORK(&tp->resize_work, tty3270_resize_work); 764 765 return tp; 766 767out_reset: 768 raw3270_request_free(tp->kreset); 769out_read: 770 raw3270_request_free(tp->read); 771out_write: 772 raw3270_request_free(tp->write); 773out_pages: 774 while (pages--) 775 free_pages((unsigned long) tp->freemem_pages[pages], 0); 776 kfree(tp->freemem_pages); 777 tty_port_destroy(&tp->port); 778out_tp: 779 kfree(tp); 780out_err: 781 return ERR_PTR(-ENOMEM); 782} 783 784/* 785 * Free tty3270 structure. 786 */ 787static void 788tty3270_free_view(struct tty3270 *tp) 789{ 790 int pages; 791 792 kbd_free(tp->kbd); 793 raw3270_request_free(tp->kreset); 794 raw3270_request_free(tp->read); 795 raw3270_request_free(tp->write); 796 for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) 797 free_pages((unsigned long) tp->freemem_pages[pages], 0); 798 kfree(tp->freemem_pages); 799 tty_port_destroy(&tp->port); 800 kfree(tp); 801} 802 803/* 804 * Allocate tty3270 screen. 805 */ 806static struct tty3270_line * 807tty3270_alloc_screen(unsigned int rows, unsigned int cols) 808{ 809 struct tty3270_line *screen; 810 unsigned long size; 811 int lines; 812 813 size = sizeof(struct tty3270_line) * (rows - 2); 814 screen = kzalloc(size, GFP_KERNEL); 815 if (!screen) 816 goto out_err; 817 for (lines = 0; lines < rows - 2; lines++) { 818 size = sizeof(struct tty3270_cell) * cols; 819 screen[lines].cells = kzalloc(size, GFP_KERNEL); 820 if (!screen[lines].cells) 821 goto out_screen; 822 } 823 return screen; 824out_screen: 825 while (lines--) 826 kfree(screen[lines].cells); 827 kfree(screen); 828out_err: 829 return ERR_PTR(-ENOMEM); 830} 831 832/* 833 * Free tty3270 screen. 834 */ 835static void 836tty3270_free_screen(struct tty3270_line *screen, unsigned int rows) 837{ 838 int lines; 839 840 for (lines = 0; lines < rows - 2; lines++) 841 kfree(screen[lines].cells); 842 kfree(screen); 843} 844 845/* 846 * Resize tty3270 screen 847 */ 848static void tty3270_resize_work(struct work_struct *work) 849{ 850 struct tty3270 *tp = container_of(work, struct tty3270, resize_work); 851 struct tty3270_line *screen, *oscreen; 852 struct tty_struct *tty; 853 unsigned int orows; 854 struct winsize ws; 855 856 screen = tty3270_alloc_screen(tp->n_rows, tp->n_cols); 857 if (IS_ERR(screen)) 858 return; 859 /* Switch to new output size */ 860 spin_lock_bh(&tp->view.lock); 861 tty3270_blank_screen(tp); 862 oscreen = tp->screen; 863 orows = tp->view.rows; 864 tp->view.model = tp->n_model; 865 tp->view.rows = tp->n_rows; 866 tp->view.cols = tp->n_cols; 867 tp->screen = screen; 868 free_string(&tp->freemem, tp->prompt); 869 free_string(&tp->freemem, tp->status); 870 tty3270_create_prompt(tp); 871 tty3270_create_status(tp); 872 while (tp->nr_lines < tp->view.rows - 2) 873 tty3270_blank_line(tp); 874 tp->update_flags = TTY_UPDATE_ALL; 875 spin_unlock_bh(&tp->view.lock); 876 tty3270_free_screen(oscreen, orows); 877 tty3270_set_timer(tp, 1); 878 /* Informat tty layer about new size */ 879 tty = tty_port_tty_get(&tp->port); 880 if (!tty) 881 return; 882 ws.ws_row = tp->view.rows - 2; 883 ws.ws_col = tp->view.cols; 884 tty_do_resize(tty, &ws); 885 tty_kref_put(tty); 886} 887 888static void 889tty3270_resize(struct raw3270_view *view, int model, int rows, int cols) 890{ 891 struct tty3270 *tp = container_of(view, struct tty3270, view); 892 893 if (tp->n_model == model && tp->n_rows == rows && tp->n_cols == cols) 894 return; 895 tp->n_model = model; 896 tp->n_rows = rows; 897 tp->n_cols = cols; 898 schedule_work(&tp->resize_work); 899} 900 901/* 902 * Unlink tty3270 data structure from tty. 903 */ 904static void 905tty3270_release(struct raw3270_view *view) 906{ 907 struct tty3270 *tp = container_of(view, struct tty3270, view); 908 struct tty_struct *tty = tty_port_tty_get(&tp->port); 909 910 if (tty) { 911 tty->driver_data = NULL; 912 tty_port_tty_set(&tp->port, NULL); 913 tty_hangup(tty); 914 raw3270_put_view(&tp->view); 915 tty_kref_put(tty); 916 } 917} 918 919/* 920 * Free tty3270 data structure 921 */ 922static void 923tty3270_free(struct raw3270_view *view) 924{ 925 struct tty3270 *tp = container_of(view, struct tty3270, view); 926 927 del_timer_sync(&tp->timer); 928 tty3270_free_screen(tp->screen, tp->view.rows); 929 tty3270_free_view(tp); 930} 931 932/* 933 * Delayed freeing of tty3270 views. 934 */ 935static void 936tty3270_del_views(void) 937{ 938 int i; 939 940 for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) { 941 struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i); 942 if (!IS_ERR(view)) 943 raw3270_del_view(view); 944 } 945} 946 947static struct raw3270_fn tty3270_fn = { 948 .activate = tty3270_activate, 949 .deactivate = tty3270_deactivate, 950 .intv = (void *) tty3270_irq, 951 .release = tty3270_release, 952 .free = tty3270_free, 953 .resize = tty3270_resize 954}; 955 956/* 957 * This routine is called whenever a 3270 tty is opened first time. 958 */ 959static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) 960{ 961 struct raw3270_view *view; 962 struct tty3270 *tp; 963 int i, rc; 964 965 /* Check if the tty3270 is already there. */ 966 view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR); 967 if (!IS_ERR(view)) { 968 tp = container_of(view, struct tty3270, view); 969 tty->driver_data = tp; 970 tty->winsize.ws_row = tp->view.rows - 2; 971 tty->winsize.ws_col = tp->view.cols; 972 tp->inattr = TF_INPUT; 973 goto port_install; 974 } 975 if (tty3270_max_index < tty->index + 1) 976 tty3270_max_index = tty->index + 1; 977 978 /* Allocate tty3270 structure on first open. */ 979 tp = tty3270_alloc_view(); 980 if (IS_ERR(tp)) 981 return PTR_ERR(tp); 982 983 rc = raw3270_add_view(&tp->view, &tty3270_fn, 984 tty->index + RAW3270_FIRSTMINOR, 985 RAW3270_VIEW_LOCK_BH); 986 if (rc) { 987 tty3270_free_view(tp); 988 return rc; 989 } 990 991 tp->screen = tty3270_alloc_screen(tp->view.rows, tp->view.cols); 992 if (IS_ERR(tp->screen)) { 993 rc = PTR_ERR(tp->screen); 994 raw3270_put_view(&tp->view); 995 raw3270_del_view(&tp->view); 996 tty3270_free_view(tp); 997 return rc; 998 } 999 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 unsigned 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->flow.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 * Check for visible/invisible input switches 1761 */ 1762static void 1763tty3270_set_termios(struct tty_struct *tty, struct ktermios *old) 1764{ 1765 struct tty3270 *tp; 1766 int new; 1767 1768 tp = tty->driver_data; 1769 if (!tp) 1770 return; 1771 spin_lock_bh(&tp->view.lock); 1772 if (L_ICANON(tty)) { 1773 new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN; 1774 if (new != tp->inattr) { 1775 tp->inattr = new; 1776 tty3270_update_prompt(tp, NULL, 0); 1777 tty3270_set_timer(tp, 1); 1778 } 1779 } 1780 spin_unlock_bh(&tp->view.lock); 1781} 1782 1783/* 1784 * Disable reading from a 3270 tty 1785 */ 1786static void 1787tty3270_throttle(struct tty_struct * tty) 1788{ 1789 struct tty3270 *tp; 1790 1791 tp = tty->driver_data; 1792 if (!tp) 1793 return; 1794 tp->throttle = 1; 1795} 1796 1797/* 1798 * Enable reading from a 3270 tty 1799 */ 1800static void 1801tty3270_unthrottle(struct tty_struct * tty) 1802{ 1803 struct tty3270 *tp; 1804 1805 tp = tty->driver_data; 1806 if (!tp) 1807 return; 1808 tp->throttle = 0; 1809 if (tp->attn) 1810 tty3270_issue_read(tp, 1); 1811} 1812 1813/* 1814 * Hang up the tty device. 1815 */ 1816static void 1817tty3270_hangup(struct tty_struct *tty) 1818{ 1819 struct tty3270 *tp; 1820 1821 tp = tty->driver_data; 1822 if (!tp) 1823 return; 1824 spin_lock_bh(&tp->view.lock); 1825 tp->cx = tp->saved_cx = 0; 1826 tp->cy = tp->saved_cy = 0; 1827 tp->highlight = tp->saved_highlight = TAX_RESET; 1828 tp->f_color = tp->saved_f_color = TAC_RESET; 1829 tty3270_blank_screen(tp); 1830 while (tp->nr_lines < tp->view.rows - 2) 1831 tty3270_blank_line(tp); 1832 tp->update_flags = TTY_UPDATE_ALL; 1833 spin_unlock_bh(&tp->view.lock); 1834 tty3270_set_timer(tp, 1); 1835} 1836 1837static void 1838tty3270_wait_until_sent(struct tty_struct *tty, int timeout) 1839{ 1840} 1841 1842static int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd, 1843 unsigned long arg) 1844{ 1845 struct tty3270 *tp; 1846 1847 tp = tty->driver_data; 1848 if (!tp) 1849 return -ENODEV; 1850 if (tty_io_error(tty)) 1851 return -EIO; 1852 return kbd_ioctl(tp->kbd, cmd, arg); 1853} 1854 1855#ifdef CONFIG_COMPAT 1856static long tty3270_compat_ioctl(struct tty_struct *tty, 1857 unsigned int cmd, unsigned long arg) 1858{ 1859 struct tty3270 *tp; 1860 1861 tp = tty->driver_data; 1862 if (!tp) 1863 return -ENODEV; 1864 if (tty_io_error(tty)) 1865 return -EIO; 1866 return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg)); 1867} 1868#endif 1869 1870static const struct tty_operations tty3270_ops = { 1871 .install = tty3270_install, 1872 .cleanup = tty3270_cleanup, 1873 .open = tty3270_open, 1874 .close = tty3270_close, 1875 .write = tty3270_write, 1876 .put_char = tty3270_put_char, 1877 .flush_chars = tty3270_flush_chars, 1878 .write_room = tty3270_write_room, 1879 .throttle = tty3270_throttle, 1880 .unthrottle = tty3270_unthrottle, 1881 .hangup = tty3270_hangup, 1882 .wait_until_sent = tty3270_wait_until_sent, 1883 .ioctl = tty3270_ioctl, 1884#ifdef CONFIG_COMPAT 1885 .compat_ioctl = tty3270_compat_ioctl, 1886#endif 1887 .set_termios = tty3270_set_termios 1888}; 1889 1890static void tty3270_create_cb(int minor) 1891{ 1892 tty_register_device(tty3270_driver, minor - RAW3270_FIRSTMINOR, NULL); 1893} 1894 1895static void tty3270_destroy_cb(int minor) 1896{ 1897 tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR); 1898} 1899 1900static struct raw3270_notifier tty3270_notifier = 1901{ 1902 .create = tty3270_create_cb, 1903 .destroy = tty3270_destroy_cb, 1904}; 1905 1906/* 1907 * 3270 tty registration code called from tty_init(). 1908 * Most kernel services (incl. kmalloc) are available at this poimt. 1909 */ 1910static int __init tty3270_init(void) 1911{ 1912 struct tty_driver *driver; 1913 int ret; 1914 1915 driver = tty_alloc_driver(RAW3270_MAXDEVS, 1916 TTY_DRIVER_REAL_RAW | 1917 TTY_DRIVER_DYNAMIC_DEV | 1918 TTY_DRIVER_RESET_TERMIOS); 1919 if (IS_ERR(driver)) 1920 return PTR_ERR(driver); 1921 1922 /* 1923 * Initialize the tty_driver structure 1924 * Entries in tty3270_driver that are NOT initialized: 1925 * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc 1926 */ 1927 driver->driver_name = "tty3270"; 1928 driver->name = "3270/tty"; 1929 driver->major = IBM_TTY3270_MAJOR; 1930 driver->minor_start = RAW3270_FIRSTMINOR; 1931 driver->name_base = RAW3270_FIRSTMINOR; 1932 driver->type = TTY_DRIVER_TYPE_SYSTEM; 1933 driver->subtype = SYSTEM_TYPE_TTY; 1934 driver->init_termios = tty_std_termios; 1935 tty_set_operations(driver, &tty3270_ops); 1936 ret = tty_register_driver(driver); 1937 if (ret) { 1938 tty_driver_kref_put(driver); 1939 return ret; 1940 } 1941 tty3270_driver = driver; 1942 raw3270_register_notifier(&tty3270_notifier); 1943 return 0; 1944} 1945 1946static void __exit 1947tty3270_exit(void) 1948{ 1949 struct tty_driver *driver; 1950 1951 raw3270_unregister_notifier(&tty3270_notifier); 1952 driver = tty3270_driver; 1953 tty3270_driver = NULL; 1954 tty_unregister_driver(driver); 1955 tty_driver_kref_put(driver); 1956 tty3270_del_views(); 1957} 1958 1959MODULE_LICENSE("GPL"); 1960MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR); 1961 1962module_init(tty3270_init); 1963module_exit(tty3270_exit);