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