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