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