1/* 2 * drivers/s390/char/con3270.c 3 * IBM/3270 Driver - console view. 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/bootmem.h> 12#include <linux/console.h> 13#include <linux/init.h> 14#include <linux/interrupt.h> 15#include <linux/list.h> 16#include <linux/types.h> 17#include <linux/err.h> 18 19#include <asm/ccwdev.h> 20#include <asm/cio.h> 21#include <asm/cpcmd.h> 22#include <asm/ebcdic.h> 23 24#include "raw3270.h" 25#include "ctrlchar.h" 26 27#define CON3270_OUTPUT_BUFFER_SIZE 1024 28#define CON3270_STRING_PAGES 4 29 30static struct raw3270_fn con3270_fn; 31 32/* 33 * Main 3270 console view data structure. 34 */ 35struct con3270 { 36 struct raw3270_view view; 37 spinlock_t lock; 38 struct list_head freemem; /* list of free memory for strings. */ 39 40 /* Output stuff. */ 41 struct list_head lines; /* list of lines. */ 42 struct list_head update; /* list of lines to update. */ 43 int line_nr; /* line number for next update. */ 44 int nr_lines; /* # lines in list. */ 45 int nr_up; /* # lines up in history. */ 46 unsigned long update_flags; /* Update indication bits. */ 47 struct string *cline; /* current output line. */ 48 struct string *status; /* last line of display. */ 49 struct raw3270_request *write; /* single write request. */ 50 struct timer_list timer; 51 52 /* Input stuff. */ 53 struct string *input; /* input string for read request. */ 54 struct raw3270_request *read; /* single read request. */ 55 struct raw3270_request *kreset; /* single keyboard reset request. */ 56 struct tasklet_struct readlet; /* tasklet to issue read request. */ 57}; 58 59static struct con3270 *condev; 60 61/* con3270->update_flags. See con3270_update for details. */ 62#define CON_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */ 63#define CON_UPDATE_LIST 2 /* Update lines in tty3270->update. */ 64#define CON_UPDATE_STATUS 4 /* Update status line. */ 65#define CON_UPDATE_ALL 7 66 67static void con3270_update(struct con3270 *); 68 69/* 70 * Setup timeout for a device. On timeout trigger an update. 71 */ 72static void con3270_set_timer(struct con3270 *cp, int expires) 73{ 74 if (expires == 0) { 75 if (timer_pending(&cp->timer)) 76 del_timer(&cp->timer); 77 return; 78 } 79 if (timer_pending(&cp->timer) && 80 mod_timer(&cp->timer, jiffies + expires)) 81 return; 82 cp->timer.function = (void (*)(unsigned long)) con3270_update; 83 cp->timer.data = (unsigned long) cp; 84 cp->timer.expires = jiffies + expires; 85 add_timer(&cp->timer); 86} 87 88/* 89 * The status line is the last line of the screen. It shows the string 90 * "console view" in the lower left corner and "Running"/"More..."/"Holding" 91 * in the lower right corner of the screen. 92 */ 93static void 94con3270_update_status(struct con3270 *cp) 95{ 96 char *str; 97 98 str = (cp->nr_up != 0) ? "History" : "Running"; 99 memcpy(cp->status->string + 24, str, 7); 100 codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); 101 cp->update_flags |= CON_UPDATE_STATUS; 102} 103 104static void 105con3270_create_status(struct con3270 *cp) 106{ 107 static const unsigned char blueprint[] = 108 { TO_SBA, 0, 0, TO_SF,TF_LOG,TO_SA,TAT_COLOR, TAC_GREEN, 109 'c','o','n','s','o','l','e',' ','v','i','e','w', 110 TO_RA,0,0,0,'R','u','n','n','i','n','g',TO_SF,TF_LOG }; 111 112 cp->status = alloc_string(&cp->freemem, sizeof(blueprint)); 113 /* Copy blueprint to status line */ 114 memcpy(cp->status->string, blueprint, sizeof(blueprint)); 115 /* Set TO_RA addresses. */ 116 raw3270_buffer_address(cp->view.dev, cp->status->string + 1, 117 cp->view.cols * (cp->view.rows - 1)); 118 raw3270_buffer_address(cp->view.dev, cp->status->string + 21, 119 cp->view.cols * cp->view.rows - 8); 120 /* Convert strings to ebcdic. */ 121 codepage_convert(cp->view.ascebc, cp->status->string + 8, 12); 122 codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); 123} 124 125/* 126 * Set output offsets to 3270 datastream fragment of a console string. 127 */ 128static void 129con3270_update_string(struct con3270 *cp, struct string *s, int nr) 130{ 131 if (s->len >= cp->view.cols - 5) 132 return; 133 raw3270_buffer_address(cp->view.dev, s->string + s->len - 3, 134 cp->view.cols * (nr + 1)); 135} 136 137/* 138 * Rebuild update list to print all lines. 139 */ 140static void 141con3270_rebuild_update(struct con3270 *cp) 142{ 143 struct string *s, *n; 144 int nr; 145 146 /* 147 * Throw away update list and create a new one, 148 * containing all lines that will fit on the screen. 149 */ 150 list_for_each_entry_safe(s, n, &cp->update, update) 151 list_del_init(&s->update); 152 nr = cp->view.rows - 2 + cp->nr_up; 153 list_for_each_entry_reverse(s, &cp->lines, list) { 154 if (nr < cp->view.rows - 1) 155 list_add(&s->update, &cp->update); 156 if (--nr < 0) 157 break; 158 } 159 cp->line_nr = 0; 160 cp->update_flags |= CON_UPDATE_LIST; 161} 162 163/* 164 * Alloc string for size bytes. Free strings from history if necessary. 165 */ 166static struct string * 167con3270_alloc_string(struct con3270 *cp, size_t size) 168{ 169 struct string *s, *n; 170 171 s = alloc_string(&cp->freemem, size); 172 if (s) 173 return s; 174 list_for_each_entry_safe(s, n, &cp->lines, list) { 175 list_del(&s->list); 176 if (!list_empty(&s->update)) 177 list_del(&s->update); 178 cp->nr_lines--; 179 if (free_string(&cp->freemem, s) >= size) 180 break; 181 } 182 s = alloc_string(&cp->freemem, size); 183 BUG_ON(!s); 184 if (cp->nr_up != 0 && cp->nr_up + cp->view.rows > cp->nr_lines) { 185 cp->nr_up = cp->nr_lines - cp->view.rows + 1; 186 con3270_rebuild_update(cp); 187 con3270_update_status(cp); 188 } 189 return s; 190} 191 192/* 193 * Write completion callback. 194 */ 195static void 196con3270_write_callback(struct raw3270_request *rq, void *data) 197{ 198 raw3270_request_reset(rq); 199 xchg(&((struct con3270 *) rq->view)->write, rq); 200} 201 202/* 203 * Update console display. 204 */ 205static void 206con3270_update(struct con3270 *cp) 207{ 208 struct raw3270_request *wrq; 209 char wcc, prolog[6]; 210 unsigned long flags; 211 unsigned long updated; 212 struct string *s, *n; 213 int rc; 214 215 if (cp->view.dev) 216 raw3270_activate_view(&cp->view); 217 218 wrq = xchg(&cp->write, 0); 219 if (!wrq) { 220 con3270_set_timer(cp, 1); 221 return; 222 } 223 224 spin_lock_irqsave(&cp->view.lock, flags); 225 updated = 0; 226 if (cp->update_flags & CON_UPDATE_ERASE) { 227 /* Use erase write alternate to initialize display. */ 228 raw3270_request_set_cmd(wrq, TC_EWRITEA); 229 updated |= CON_UPDATE_ERASE; 230 } else 231 raw3270_request_set_cmd(wrq, TC_WRITE); 232 233 wcc = TW_NONE; 234 raw3270_request_add_data(wrq, &wcc, 1); 235 236 /* 237 * Update status line. 238 */ 239 if (cp->update_flags & CON_UPDATE_STATUS) 240 if (raw3270_request_add_data(wrq, cp->status->string, 241 cp->status->len) == 0) 242 updated |= CON_UPDATE_STATUS; 243 244 if (cp->update_flags & CON_UPDATE_LIST) { 245 prolog[0] = TO_SBA; 246 prolog[3] = TO_SA; 247 prolog[4] = TAT_COLOR; 248 prolog[5] = TAC_TURQ; 249 raw3270_buffer_address(cp->view.dev, prolog + 1, 250 cp->view.cols * cp->line_nr); 251 raw3270_request_add_data(wrq, prolog, 6); 252 /* Write strings in the update list to the screen. */ 253 list_for_each_entry_safe(s, n, &cp->update, update) { 254 if (s != cp->cline) 255 con3270_update_string(cp, s, cp->line_nr); 256 if (raw3270_request_add_data(wrq, s->string, 257 s->len) != 0) 258 break; 259 list_del_init(&s->update); 260 if (s != cp->cline) 261 cp->line_nr++; 262 } 263 if (list_empty(&cp->update)) 264 updated |= CON_UPDATE_LIST; 265 } 266 wrq->callback = con3270_write_callback; 267 rc = raw3270_start(&cp->view, wrq); 268 if (rc == 0) { 269 cp->update_flags &= ~updated; 270 if (cp->update_flags) 271 con3270_set_timer(cp, 1); 272 } else { 273 raw3270_request_reset(wrq); 274 xchg(&cp->write, wrq); 275 } 276 spin_unlock_irqrestore(&cp->view.lock, flags); 277} 278 279/* 280 * Read tasklet. 281 */ 282static void 283con3270_read_tasklet(struct raw3270_request *rrq) 284{ 285 static char kreset_data = TW_KR; 286 struct con3270 *cp; 287 unsigned long flags; 288 int nr_up, deactivate; 289 290 cp = (struct con3270 *) rrq->view; 291 spin_lock_irqsave(&cp->view.lock, flags); 292 nr_up = cp->nr_up; 293 deactivate = 0; 294 /* Check aid byte. */ 295 switch (cp->input->string[0]) { 296 case 0x7d: /* enter: jump to bottom. */ 297 nr_up = 0; 298 break; 299 case 0xf3: /* PF3: deactivate the console view. */ 300 deactivate = 1; 301 break; 302 case 0x6d: /* clear: start from scratch. */ 303 con3270_rebuild_update(cp); 304 cp->update_flags = CON_UPDATE_ALL; 305 con3270_set_timer(cp, 1); 306 break; 307 case 0xf7: /* PF7: do a page up in the console log. */ 308 nr_up += cp->view.rows - 2; 309 if (nr_up + cp->view.rows - 1 > cp->nr_lines) { 310 nr_up = cp->nr_lines - cp->view.rows + 1; 311 if (nr_up < 0) 312 nr_up = 0; 313 } 314 break; 315 case 0xf8: /* PF8: do a page down in the console log. */ 316 nr_up -= cp->view.rows - 2; 317 if (nr_up < 0) 318 nr_up = 0; 319 break; 320 } 321 if (nr_up != cp->nr_up) { 322 cp->nr_up = nr_up; 323 con3270_rebuild_update(cp); 324 con3270_update_status(cp); 325 con3270_set_timer(cp, 1); 326 } 327 spin_unlock_irqrestore(&cp->view.lock, flags); 328 329 /* Start keyboard reset command. */ 330 raw3270_request_reset(cp->kreset); 331 raw3270_request_set_cmd(cp->kreset, TC_WRITE); 332 raw3270_request_add_data(cp->kreset, &kreset_data, 1); 333 raw3270_start(&cp->view, cp->kreset); 334 335 if (deactivate) 336 raw3270_deactivate_view(&cp->view); 337 338 raw3270_request_reset(rrq); 339 xchg(&cp->read, rrq); 340 raw3270_put_view(&cp->view); 341} 342 343/* 344 * Read request completion callback. 345 */ 346static void 347con3270_read_callback(struct raw3270_request *rq, void *data) 348{ 349 raw3270_get_view(rq->view); 350 /* Schedule tasklet to pass input to tty. */ 351 tasklet_schedule(&((struct con3270 *) rq->view)->readlet); 352} 353 354/* 355 * Issue a read request. Called only from interrupt function. 356 */ 357static void 358con3270_issue_read(struct con3270 *cp) 359{ 360 struct raw3270_request *rrq; 361 int rc; 362 363 rrq = xchg(&cp->read, 0); 364 if (!rrq) 365 /* Read already scheduled. */ 366 return; 367 rrq->callback = con3270_read_callback; 368 rrq->callback_data = cp; 369 raw3270_request_set_cmd(rrq, TC_READMOD); 370 raw3270_request_set_data(rrq, cp->input->string, cp->input->len); 371 /* Issue the read modified request. */ 372 rc = raw3270_start_irq(&cp->view, rrq); 373 if (rc) 374 raw3270_request_reset(rrq); 375} 376 377/* 378 * Switch to the console view. 379 */ 380static int 381con3270_activate(struct raw3270_view *view) 382{ 383 unsigned long flags; 384 struct con3270 *cp; 385 386 cp = (struct con3270 *) view; 387 spin_lock_irqsave(&cp->view.lock, flags); 388 cp->nr_up = 0; 389 con3270_rebuild_update(cp); 390 con3270_update_status(cp); 391 cp->update_flags = CON_UPDATE_ALL; 392 con3270_set_timer(cp, 1); 393 spin_unlock_irqrestore(&cp->view.lock, flags); 394 return 0; 395} 396 397static void 398con3270_deactivate(struct raw3270_view *view) 399{ 400 unsigned long flags; 401 struct con3270 *cp; 402 403 cp = (struct con3270 *) view; 404 spin_lock_irqsave(&cp->view.lock, flags); 405 del_timer(&cp->timer); 406 spin_unlock_irqrestore(&cp->view.lock, flags); 407} 408 409static int 410con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) 411{ 412 /* Handle ATTN. Schedule tasklet to read aid. */ 413 if (irb->scsw.dstat & DEV_STAT_ATTENTION) 414 con3270_issue_read(cp); 415 416 if (rq) { 417 if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) 418 rq->rc = -EIO; 419 else 420 /* Normal end. Copy residual count. */ 421 rq->rescnt = irb->scsw.count; 422 } 423 return RAW3270_IO_DONE; 424} 425 426/* Console view to a 3270 device. */ 427static struct raw3270_fn con3270_fn = { 428 .activate = con3270_activate, 429 .deactivate = con3270_deactivate, 430 .intv = (void *) con3270_irq 431}; 432 433static inline void 434con3270_cline_add(struct con3270 *cp) 435{ 436 if (!list_empty(&cp->cline->list)) 437 /* Already added. */ 438 return; 439 list_add_tail(&cp->cline->list, &cp->lines); 440 cp->nr_lines++; 441 con3270_rebuild_update(cp); 442} 443 444static inline void 445con3270_cline_insert(struct con3270 *cp, unsigned char c) 446{ 447 cp->cline->string[cp->cline->len++] = 448 cp->view.ascebc[(c < ' ') ? ' ' : c]; 449 if (list_empty(&cp->cline->update)) { 450 list_add_tail(&cp->cline->update, &cp->update); 451 cp->update_flags |= CON_UPDATE_LIST; 452 } 453} 454 455static inline void 456con3270_cline_end(struct con3270 *cp) 457{ 458 struct string *s; 459 unsigned int size; 460 461 /* Copy cline. */ 462 size = (cp->cline->len < cp->view.cols - 5) ? 463 cp->cline->len + 4 : cp->view.cols; 464 s = con3270_alloc_string(cp, size); 465 memcpy(s->string, cp->cline->string, cp->cline->len); 466 if (s->len < cp->view.cols - 5) { 467 s->string[s->len - 4] = TO_RA; 468 s->string[s->len - 1] = 0; 469 } else { 470 while (--size > cp->cline->len) 471 s->string[size] = cp->view.ascebc[' ']; 472 } 473 /* Replace cline with allocated line s and reset cline. */ 474 list_add(&s->list, &cp->cline->list); 475 list_del_init(&cp->cline->list); 476 if (!list_empty(&cp->cline->update)) { 477 list_add(&s->update, &cp->cline->update); 478 list_del_init(&cp->cline->update); 479 } 480 cp->cline->len = 0; 481} 482 483/* 484 * Write a string to the 3270 console 485 */ 486static void 487con3270_write(struct console *co, const char *str, unsigned int count) 488{ 489 struct con3270 *cp; 490 unsigned long flags; 491 unsigned char c; 492 493 cp = condev; 494 spin_lock_irqsave(&cp->view.lock, flags); 495 while (count-- > 0) { 496 c = *str++; 497 if (cp->cline->len == 0) 498 con3270_cline_add(cp); 499 if (c != '\n') 500 con3270_cline_insert(cp, c); 501 if (c == '\n' || cp->cline->len >= cp->view.cols) 502 con3270_cline_end(cp); 503 } 504 /* Setup timer to output current console buffer after 1/10 second */ 505 if (cp->view.dev && !timer_pending(&cp->timer)) 506 con3270_set_timer(cp, HZ/10); 507 spin_unlock_irqrestore(&cp->view.lock,flags); 508} 509 510extern struct tty_driver *tty3270_driver; 511 512static struct tty_driver * 513con3270_device(struct console *c, int *index) 514{ 515 *index = c->index; 516 return tty3270_driver; 517} 518 519/* 520 * Wait for end of write request. 521 */ 522static void 523con3270_wait_write(struct con3270 *cp) 524{ 525 while (!cp->write) { 526 raw3270_wait_cons_dev(cp->view.dev); 527 barrier(); 528 } 529} 530 531/* 532 * panic() calls console_unblank before the system enters a 533 * disabled, endless loop. 534 */ 535static void 536con3270_unblank(void) 537{ 538 struct con3270 *cp; 539 unsigned long flags; 540 541 cp = condev; 542 if (!cp->view.dev) 543 return; 544 spin_lock_irqsave(&cp->view.lock, flags); 545 con3270_wait_write(cp); 546 cp->nr_up = 0; 547 con3270_rebuild_update(cp); 548 con3270_update_status(cp); 549 while (cp->update_flags != 0) { 550 spin_unlock_irqrestore(&cp->view.lock, flags); 551 con3270_update(cp); 552 spin_lock_irqsave(&cp->view.lock, flags); 553 con3270_wait_write(cp); 554 } 555 spin_unlock_irqrestore(&cp->view.lock, flags); 556} 557 558/* 559 * The console structure for the 3270 console 560 */ 561static struct console con3270 = { 562 .name = "tty3270", 563 .write = con3270_write, 564 .device = con3270_device, 565 .unblank = con3270_unblank, 566 .flags = CON_PRINTBUFFER, 567}; 568 569/* 570 * 3270 console initialization code called from console_init(). 571 * NOTE: This is called before kmalloc is available. 572 */ 573static int __init 574con3270_init(void) 575{ 576 struct ccw_device *cdev; 577 struct raw3270 *rp; 578 void *cbuf; 579 int i; 580 581 /* Check if 3270 is to be the console */ 582 if (!CONSOLE_IS_3270) 583 return -ENODEV; 584 585 /* Set the console mode for VM */ 586 if (MACHINE_IS_VM) { 587 cpcmd("TERM CONMODE 3270", NULL, 0, NULL); 588 cpcmd("TERM AUTOCR OFF", NULL, 0, NULL); 589 } 590 591 cdev = ccw_device_probe_console(); 592 if (IS_ERR(cdev)) 593 return -ENODEV; 594 rp = raw3270_setup_console(cdev); 595 if (IS_ERR(rp)) 596 return PTR_ERR(rp); 597 598 condev = (struct con3270 *) alloc_bootmem_low(sizeof(struct con3270)); 599 memset(condev, 0, sizeof(struct con3270)); 600 condev->view.dev = rp; 601 602 condev->read = raw3270_request_alloc_bootmem(0); 603 condev->read->callback = con3270_read_callback; 604 condev->read->callback_data = condev; 605 condev->write = 606 raw3270_request_alloc_bootmem(CON3270_OUTPUT_BUFFER_SIZE); 607 condev->kreset = raw3270_request_alloc_bootmem(1); 608 609 INIT_LIST_HEAD(&condev->lines); 610 INIT_LIST_HEAD(&condev->update); 611 init_timer(&condev->timer); 612 tasklet_init(&condev->readlet, 613 (void (*)(unsigned long)) con3270_read_tasklet, 614 (unsigned long) condev->read); 615 616 raw3270_add_view(&condev->view, &con3270_fn, 1); 617 618 INIT_LIST_HEAD(&condev->freemem); 619 for (i = 0; i < CON3270_STRING_PAGES; i++) { 620 cbuf = (void *) alloc_bootmem_low_pages(PAGE_SIZE); 621 add_string_memory(&condev->freemem, cbuf, PAGE_SIZE); 622 } 623 condev->cline = alloc_string(&condev->freemem, condev->view.cols); 624 condev->cline->len = 0; 625 con3270_create_status(condev); 626 condev->input = alloc_string(&condev->freemem, 80); 627 register_console(&con3270); 628 return 0; 629} 630 631console_initcall(con3270_init); 632