Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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