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