blob: 1f4aff78eaca66db0c4502dd0a5ff6026c8a414c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#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>
Arnd Bergmann9d4bfd42009-12-07 12:52:13 +010022#include <linux/compat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
24#include <asm/ccwdev.h>
25#include <asm/cio.h>
26#include <asm/ebcdic.h>
27#include <asm/uaccess.h>
28
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include "raw3270.h"
Heiko Carstens364c8552007-10-12 16:11:35 +020030#include "tty3270.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#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
Heiko Carstens2b67fc42007-02-05 21:16:47 +010040static struct raw3270_fn tty3270_fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
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. */
Martin Schwidefsky205d7ab2009-06-12 10:26:31 +0200116#define TTY_UPDATE_ALL 16 /* Recreate screen. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
118static void tty3270_update(struct tty3270 *);
119
120/*
121 * Setup timeout for a device. On timeout trigger an update.
122 */
Heiko Carstens2b67fc42007-02-05 21:16:47 +0100123static void tty3270_set_timer(struct tty3270 *tp, int expires)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124{
Martin Schwidefsky205d7ab2009-06-12 10:26:31 +0200125 if (expires == 0)
126 del_timer(&tp->timer);
127 else
128 mod_timer(&tp->timer, jiffies + expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129}
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{
Jiri Slaby881e18f2012-04-02 13:54:16 +0200327 struct tty3270 *tp = container_of(rq->view, struct tty3270, view);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 if (rq->rc != 0) {
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300330 /* Write wasn't successful. Refresh all. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 tp->update_flags = TTY_UPDATE_ALL;
332 tty3270_set_timer(tp, 1);
333 }
334 raw3270_request_reset(rq);
335 xchg(&tp->write, rq);
336}
337
338/*
339 * Update 3270 display.
340 */
341static void
342tty3270_update(struct tty3270 *tp)
343{
344 static char invalid_sba[2] = { 0xff, 0xff };
345 struct raw3270_request *wrq;
346 unsigned long updated;
347 struct string *s, *n;
348 char *sba, *str;
349 int rc, len;
350
351 wrq = xchg(&tp->write, 0);
352 if (!wrq) {
353 tty3270_set_timer(tp, 1);
354 return;
355 }
356
357 spin_lock(&tp->view.lock);
358 updated = 0;
Martin Schwidefsky205d7ab2009-06-12 10:26:31 +0200359 if (tp->update_flags & TTY_UPDATE_ALL) {
360 tty3270_rebuild_update(tp);
361 tty3270_update_status(tp);
362 tp->update_flags = TTY_UPDATE_ERASE | TTY_UPDATE_LIST |
363 TTY_UPDATE_INPUT | TTY_UPDATE_STATUS;
364 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 if (tp->update_flags & TTY_UPDATE_ERASE) {
366 /* Use erase write alternate to erase display. */
367 raw3270_request_set_cmd(wrq, TC_EWRITEA);
368 updated |= TTY_UPDATE_ERASE;
369 } else
370 raw3270_request_set_cmd(wrq, TC_WRITE);
371
372 raw3270_request_add_data(wrq, &tp->wcc, 1);
373 tp->wcc = TW_NONE;
374
375 /*
376 * Update status line.
377 */
378 if (tp->update_flags & TTY_UPDATE_STATUS)
379 if (raw3270_request_add_data(wrq, tp->status->string,
380 tp->status->len) == 0)
381 updated |= TTY_UPDATE_STATUS;
382
383 /*
384 * Write input line.
385 */
386 if (tp->update_flags & TTY_UPDATE_INPUT)
387 if (raw3270_request_add_data(wrq, tp->prompt->string,
388 tp->prompt->len) == 0)
389 updated |= TTY_UPDATE_INPUT;
390
391 sba = invalid_sba;
392
393 if (tp->update_flags & TTY_UPDATE_LIST) {
394 /* Write strings in the update list to the screen. */
395 list_for_each_entry_safe(s, n, &tp->update, update) {
396 str = s->string;
397 len = s->len;
398 /*
399 * Skip TO_SBA at the start of the string if the
400 * last output position matches the start address
401 * of this line.
402 */
403 if (s->string[1] == sba[0] && s->string[2] == sba[1])
404 str += 3, len -= 3;
405 if (raw3270_request_add_data(wrq, str, len) != 0)
406 break;
407 list_del_init(&s->update);
408 sba = s->string + s->len - 3;
409 }
410 if (list_empty(&tp->update))
411 updated |= TTY_UPDATE_LIST;
412 }
413 wrq->callback = tty3270_write_callback;
414 rc = raw3270_start(&tp->view, wrq);
415 if (rc == 0) {
416 tp->update_flags &= ~updated;
417 if (tp->update_flags)
418 tty3270_set_timer(tp, 1);
419 } else {
420 raw3270_request_reset(wrq);
421 xchg(&tp->write, wrq);
422 }
423 spin_unlock(&tp->view.lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424}
425
426/*
427 * Command recalling.
428 */
429static void
430tty3270_rcl_add(struct tty3270 *tp, char *input, int len)
431{
432 struct string *s;
433
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200434 tp->rcl_walk = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 if (len <= 0)
436 return;
437 if (tp->rcl_nr >= tp->rcl_max) {
438 s = list_entry(tp->rcl_lines.next, struct string, list);
439 list_del(&s->list);
440 free_string(&tp->freemem, s);
441 tp->rcl_nr--;
442 }
443 s = tty3270_alloc_string(tp, len);
444 memcpy(s->string, input, len);
445 list_add_tail(&s->list, &tp->rcl_lines);
446 tp->rcl_nr++;
447}
448
449static void
450tty3270_rcl_backward(struct kbd_data *kbd)
451{
452 struct tty3270 *tp;
453 struct string *s;
454
455 tp = kbd->tty->driver_data;
456 spin_lock_bh(&tp->view.lock);
457 if (tp->inattr == TF_INPUT) {
458 if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines)
459 tp->rcl_walk = tp->rcl_walk->prev;
460 else if (!list_empty(&tp->rcl_lines))
461 tp->rcl_walk = tp->rcl_lines.prev;
462 s = tp->rcl_walk ?
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200463 list_entry(tp->rcl_walk, struct string, list) : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 if (tp->rcl_walk) {
465 s = list_entry(tp->rcl_walk, struct string, list);
466 tty3270_update_prompt(tp, s->string, s->len);
467 } else
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200468 tty3270_update_prompt(tp, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 tty3270_set_timer(tp, 1);
470 }
471 spin_unlock_bh(&tp->view.lock);
472}
473
474/*
475 * Deactivate tty view.
476 */
477static void
478tty3270_exit_tty(struct kbd_data *kbd)
479{
480 struct tty3270 *tp;
481
482 tp = kbd->tty->driver_data;
483 raw3270_deactivate_view(&tp->view);
484}
485
486/*
487 * Scroll forward in history.
488 */
489static void
490tty3270_scroll_forward(struct kbd_data *kbd)
491{
492 struct tty3270 *tp;
493 int nr_up;
494
495 tp = kbd->tty->driver_data;
496 spin_lock_bh(&tp->view.lock);
497 nr_up = tp->nr_up - tp->view.rows + 2;
498 if (nr_up < 0)
499 nr_up = 0;
500 if (nr_up != tp->nr_up) {
501 tp->nr_up = nr_up;
502 tty3270_rebuild_update(tp);
503 tty3270_update_status(tp);
504 tty3270_set_timer(tp, 1);
505 }
506 spin_unlock_bh(&tp->view.lock);
507}
508
509/*
510 * Scroll backward in history.
511 */
512static void
513tty3270_scroll_backward(struct kbd_data *kbd)
514{
515 struct tty3270 *tp;
516 int nr_up;
517
518 tp = kbd->tty->driver_data;
519 spin_lock_bh(&tp->view.lock);
520 nr_up = tp->nr_up + tp->view.rows - 2;
521 if (nr_up + tp->view.rows - 2 > tp->nr_lines)
522 nr_up = tp->nr_lines - tp->view.rows + 2;
523 if (nr_up != tp->nr_up) {
524 tp->nr_up = nr_up;
525 tty3270_rebuild_update(tp);
526 tty3270_update_status(tp);
527 tty3270_set_timer(tp, 1);
528 }
529 spin_unlock_bh(&tp->view.lock);
530}
531
532/*
533 * Pass input line to tty.
534 */
535static void
536tty3270_read_tasklet(struct raw3270_request *rrq)
537{
538 static char kreset_data = TW_KR;
Jiri Slaby881e18f2012-04-02 13:54:16 +0200539 struct tty3270 *tp = container_of(rrq->view, struct tty3270, view);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 char *input;
541 int len;
542
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 spin_lock_bh(&tp->view.lock);
544 /*
545 * Two AID keys are special: For 0x7d (enter) the input line
546 * has to be emitted to the tty and for 0x6d the screen
547 * needs to be redrawn.
548 */
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200549 input = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 len = 0;
551 if (tp->input->string[0] == 0x7d) {
552 /* Enter: write input to tty. */
553 input = tp->input->string + 6;
554 len = tp->input->len - 6 - rrq->rescnt;
555 if (tp->inattr != TF_INPUTN)
556 tty3270_rcl_add(tp, input, len);
557 if (tp->nr_up > 0) {
558 tp->nr_up = 0;
559 tty3270_rebuild_update(tp);
560 tty3270_update_status(tp);
561 }
562 /* Clear input area. */
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200563 tty3270_update_prompt(tp, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 tty3270_set_timer(tp, 1);
565 } else if (tp->input->string[0] == 0x6d) {
566 /* Display has been cleared. Redraw. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 tp->update_flags = TTY_UPDATE_ALL;
568 tty3270_set_timer(tp, 1);
569 }
570 spin_unlock_bh(&tp->view.lock);
571
572 /* Start keyboard reset command. */
573 raw3270_request_reset(tp->kreset);
574 raw3270_request_set_cmd(tp->kreset, TC_WRITE);
575 raw3270_request_add_data(tp->kreset, &kreset_data, 1);
576 raw3270_start(&tp->view, tp->kreset);
577
578 /* Emit input string. */
579 if (tp->tty) {
580 while (len-- > 0)
581 kbd_keycode(tp->kbd, *input++);
582 /* Emit keycode for AID byte. */
583 kbd_keycode(tp->kbd, 256 + tp->input->string[0]);
584 }
585
586 raw3270_request_reset(rrq);
587 xchg(&tp->read, rrq);
588 raw3270_put_view(&tp->view);
589}
590
591/*
592 * Read request completion callback.
593 */
594static void
595tty3270_read_callback(struct raw3270_request *rq, void *data)
596{
Jiri Slaby881e18f2012-04-02 13:54:16 +0200597 struct tty3270 *tp = container_of(rq->view, struct tty3270, view);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 raw3270_get_view(rq->view);
599 /* Schedule tasklet to pass input to tty. */
Jiri Slaby881e18f2012-04-02 13:54:16 +0200600 tasklet_schedule(&tp->readlet);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601}
602
603/*
604 * Issue a read request. Call with device lock.
605 */
606static void
607tty3270_issue_read(struct tty3270 *tp, int lock)
608{
609 struct raw3270_request *rrq;
610 int rc;
611
612 rrq = xchg(&tp->read, 0);
613 if (!rrq)
614 /* Read already scheduled. */
615 return;
616 rrq->callback = tty3270_read_callback;
617 rrq->callback_data = tp;
618 raw3270_request_set_cmd(rrq, TC_READMOD);
619 raw3270_request_set_data(rrq, tp->input->string, tp->input->len);
620 /* Issue the read modified request. */
621 if (lock) {
622 rc = raw3270_start(&tp->view, rrq);
623 } else
624 rc = raw3270_start_irq(&tp->view, rrq);
625 if (rc) {
626 raw3270_request_reset(rrq);
627 xchg(&tp->read, rrq);
628 }
629}
630
631/*
632 * Switch to the tty view.
633 */
634static int
635tty3270_activate(struct raw3270_view *view)
636{
Jiri Slaby881e18f2012-04-02 13:54:16 +0200637 struct tty3270 *tp = container_of(view, struct tty3270, view);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 tp->update_flags = TTY_UPDATE_ALL;
640 tty3270_set_timer(tp, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 return 0;
642}
643
644static void
645tty3270_deactivate(struct raw3270_view *view)
646{
Jiri Slaby881e18f2012-04-02 13:54:16 +0200647 struct tty3270 *tp = container_of(view, struct tty3270, view);
Martin Schwidefsky205d7ab2009-06-12 10:26:31 +0200648
Martin Schwidefsky205d7ab2009-06-12 10:26:31 +0200649 del_timer(&tp->timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650}
651
652static int
653tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
654{
655 /* Handle ATTN. Schedule tasklet to read aid. */
Peter Oberparleiter23d805b2008-07-14 09:58:50 +0200656 if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 if (!tp->throttle)
658 tty3270_issue_read(tp, 0);
659 else
660 tp->attn = 1;
661 }
662
663 if (rq) {
Peter Oberparleiter23d805b2008-07-14 09:58:50 +0200664 if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 rq->rc = -EIO;
666 else
667 /* Normal end. Copy residual count. */
Peter Oberparleiter23d805b2008-07-14 09:58:50 +0200668 rq->rescnt = irb->scsw.cmd.count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 }
670 return RAW3270_IO_DONE;
671}
672
673/*
674 * Allocate tty3270 structure.
675 */
676static struct tty3270 *
677tty3270_alloc_view(void)
678{
679 struct tty3270 *tp;
680 int pages;
681
Eric Sesterhenn88abaab2006-03-24 03:15:31 -0800682 tp = kzalloc(sizeof(struct tty3270), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 if (!tp)
684 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 tp->freemem_pages =
686 kmalloc(sizeof(void *) * TTY3270_STRING_PAGES, GFP_KERNEL);
687 if (!tp->freemem_pages)
688 goto out_tp;
689 INIT_LIST_HEAD(&tp->freemem);
Jiri Slaby9d2ae232012-04-02 13:54:15 +0200690 INIT_LIST_HEAD(&tp->lines);
691 INIT_LIST_HEAD(&tp->update);
692 INIT_LIST_HEAD(&tp->rcl_lines);
693 tp->rcl_max = 20;
694 setup_timer(&tp->timer, (void (*)(unsigned long)) tty3270_update,
695 (unsigned long) tp);
696 tasklet_init(&tp->readlet,
697 (void (*)(unsigned long)) tty3270_read_tasklet,
698 (unsigned long) tp->read);
699
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) {
701 tp->freemem_pages[pages] = (void *)
702 __get_free_pages(GFP_KERNEL|GFP_DMA, 0);
703 if (!tp->freemem_pages[pages])
704 goto out_pages;
705 add_string_memory(&tp->freemem,
706 tp->freemem_pages[pages], PAGE_SIZE);
707 }
708 tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE);
Richard Hitted3cb6f2005-10-30 15:00:10 -0800709 if (IS_ERR(tp->write))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 goto out_pages;
711 tp->read = raw3270_request_alloc(0);
Richard Hitted3cb6f2005-10-30 15:00:10 -0800712 if (IS_ERR(tp->read))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 goto out_write;
714 tp->kreset = raw3270_request_alloc(1);
Richard Hitted3cb6f2005-10-30 15:00:10 -0800715 if (IS_ERR(tp->kreset))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 goto out_read;
717 tp->kbd = kbd_alloc();
718 if (!tp->kbd)
719 goto out_reset;
720 return tp;
721
722out_reset:
723 raw3270_request_free(tp->kreset);
724out_read:
725 raw3270_request_free(tp->read);
726out_write:
727 raw3270_request_free(tp->write);
728out_pages:
729 while (pages--)
730 free_pages((unsigned long) tp->freemem_pages[pages], 0);
731 kfree(tp->freemem_pages);
732out_tp:
733 kfree(tp);
734out_err:
735 return ERR_PTR(-ENOMEM);
736}
737
738/*
739 * Free tty3270 structure.
740 */
741static void
742tty3270_free_view(struct tty3270 *tp)
743{
744 int pages;
745
Martin Schwidefsky205d7ab2009-06-12 10:26:31 +0200746 del_timer_sync(&tp->timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 kbd_free(tp->kbd);
748 raw3270_request_free(tp->kreset);
749 raw3270_request_free(tp->read);
750 raw3270_request_free(tp->write);
751 for (pages = 0; pages < TTY3270_STRING_PAGES; pages++)
752 free_pages((unsigned long) tp->freemem_pages[pages], 0);
753 kfree(tp->freemem_pages);
754 kfree(tp);
755}
756
757/*
758 * Allocate tty3270 screen.
759 */
760static int
761tty3270_alloc_screen(struct tty3270 *tp)
762{
763 unsigned long size;
764 int lines;
765
766 size = sizeof(struct tty3270_line) * (tp->view.rows - 2);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -0800767 tp->screen = kzalloc(size, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 if (!tp->screen)
769 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 for (lines = 0; lines < tp->view.rows - 2; lines++) {
771 size = sizeof(struct tty3270_cell) * tp->view.cols;
Eric Sesterhenn88abaab2006-03-24 03:15:31 -0800772 tp->screen[lines].cells = kzalloc(size, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 if (!tp->screen[lines].cells)
774 goto out_screen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 }
776 return 0;
777out_screen:
778 while (lines--)
779 kfree(tp->screen[lines].cells);
780 kfree(tp->screen);
781out_err:
782 return -ENOMEM;
783}
784
785/*
786 * Free tty3270 screen.
787 */
788static void
789tty3270_free_screen(struct tty3270 *tp)
790{
791 int lines;
792
793 for (lines = 0; lines < tp->view.rows - 2; lines++)
794 kfree(tp->screen[lines].cells);
795 kfree(tp->screen);
796}
797
798/*
799 * Unlink tty3270 data structure from tty.
800 */
801static void
802tty3270_release(struct raw3270_view *view)
803{
Jiri Slaby881e18f2012-04-02 13:54:16 +0200804 struct tty3270 *tp = container_of(view, struct tty3270, view);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 struct tty_struct *tty;
806
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 tty = tp->tty;
808 if (tty) {
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200809 tty->driver_data = NULL;
810 tp->tty = tp->kbd->tty = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 tty_hangup(tty);
812 raw3270_put_view(&tp->view);
813 }
814}
815
816/*
817 * Free tty3270 data structure
818 */
819static void
820tty3270_free(struct raw3270_view *view)
821{
Jiri Slaby881e18f2012-04-02 13:54:16 +0200822 struct tty3270 *tp = container_of(view, struct tty3270, view);
823 tty3270_free_screen(tp);
824 tty3270_free_view(tp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825}
826
827/*
828 * Delayed freeing of tty3270 views.
829 */
830static void
831tty3270_del_views(void)
832{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 int i;
834
835 for (i = 0; i < tty3270_max_index; i++) {
Jiri Slaby881e18f2012-04-02 13:54:16 +0200836 struct raw3270_view *view =
Richard Hitted3cb6f2005-10-30 15:00:10 -0800837 raw3270_find_view(&tty3270_fn, i + RAW3270_FIRSTMINOR);
Jiri Slaby881e18f2012-04-02 13:54:16 +0200838 if (!IS_ERR(view))
839 raw3270_del_view(view);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 }
841}
842
Heiko Carstens2b67fc42007-02-05 21:16:47 +0100843static struct raw3270_fn tty3270_fn = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 .activate = tty3270_activate,
845 .deactivate = tty3270_deactivate,
846 .intv = (void *) tty3270_irq,
847 .release = tty3270_release,
848 .free = tty3270_free
849};
850
851/*
852 * This routine is called whenever a 3270 tty is opened.
853 */
854static int
855tty3270_open(struct tty_struct *tty, struct file * filp)
856{
Jiri Slaby881e18f2012-04-02 13:54:16 +0200857 struct raw3270_view *view;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 struct tty3270 *tp;
859 int i, rc;
860
861 if (tty->count > 1)
862 return 0;
863 /* Check if the tty3270 is already there. */
Jiri Slaby881e18f2012-04-02 13:54:16 +0200864 view = raw3270_find_view(&tty3270_fn,
Richard Hitted3cb6f2005-10-30 15:00:10 -0800865 tty->index + RAW3270_FIRSTMINOR);
Jiri Slaby881e18f2012-04-02 13:54:16 +0200866 if (!IS_ERR(view)) {
867 tp = container_of(view, struct tty3270, view);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 tty->driver_data = tp;
869 tty->winsize.ws_row = tp->view.rows - 2;
870 tty->winsize.ws_col = tp->view.cols;
871 tty->low_latency = 0;
872 tp->tty = tty;
873 tp->kbd->tty = tty;
874 tp->inattr = TF_INPUT;
875 return 0;
876 }
877 if (tty3270_max_index < tty->index + 1)
878 tty3270_max_index = tty->index + 1;
879
880 /* Quick exit if there is no device for tty->index. */
Jiri Slaby881e18f2012-04-02 13:54:16 +0200881 if (PTR_ERR(view) == -ENODEV)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 return -ENODEV;
883
884 /* Allocate tty3270 structure on first open. */
885 tp = tty3270_alloc_view();
886 if (IS_ERR(tp))
887 return PTR_ERR(tp);
888
Richard Hitted3cb6f2005-10-30 15:00:10 -0800889 rc = raw3270_add_view(&tp->view, &tty3270_fn,
890 tty->index + RAW3270_FIRSTMINOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 if (rc) {
892 tty3270_free_view(tp);
893 return rc;
894 }
895
896 rc = tty3270_alloc_screen(tp);
897 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 raw3270_put_view(&tp->view);
Richard Hitted3cb6f2005-10-30 15:00:10 -0800899 raw3270_del_view(&tp->view);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 return rc;
901 }
902
903 tp->tty = tty;
904 tty->low_latency = 0;
905 tty->driver_data = tp;
906 tty->winsize.ws_row = tp->view.rows - 2;
907 tty->winsize.ws_col = tp->view.cols;
908
909 tty3270_create_prompt(tp);
910 tty3270_create_status(tp);
911 tty3270_update_status(tp);
912
913 /* Create blank line for every line in the tty output area. */
914 for (i = 0; i < tp->view.rows - 2; i++)
915 tty3270_blank_line(tp);
916
917 tp->kbd->tty = tty;
918 tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty;
919 tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward;
920 tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward;
921 tp->kbd->fn_handler[KVAL(K_CONS)] = tty3270_rcl_backward;
922 kbd_ascebc(tp->kbd, tp->view.ascebc);
923
924 raw3270_activate_view(&tp->view);
925 return 0;
926}
927
928/*
929 * This routine is called when the 3270 tty is closed. We wait
930 * for the remaining request to be completed. Then we clean up.
931 */
932static void
933tty3270_close(struct tty_struct *tty, struct file * filp)
934{
Jiri Slaby881e18f2012-04-02 13:54:16 +0200935 struct tty3270 *tp = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
937 if (tty->count > 1)
938 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 if (tp) {
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200940 tty->driver_data = NULL;
941 tp->tty = tp->kbd->tty = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 raw3270_put_view(&tp->view);
943 }
944}
945
946/*
947 * We always have room.
948 */
949static int
950tty3270_write_room(struct tty_struct *tty)
951{
952 return INT_MAX;
953}
954
955/*
956 * Insert character into the screen at the current position with the
957 * current color and highlight. This function does NOT do cursor movement.
958 */
Heiko Carstens74c76c82008-05-07 09:22:58 +0200959static void tty3270_put_character(struct tty3270 *tp, char ch)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960{
961 struct tty3270_line *line;
962 struct tty3270_cell *cell;
963
964 line = tp->screen + tp->cy;
965 if (line->len <= tp->cx) {
966 while (line->len < tp->cx) {
967 cell = line->cells + line->len;
968 cell->character = tp->view.ascebc[' '];
969 cell->highlight = tp->highlight;
970 cell->f_color = tp->f_color;
971 line->len++;
972 }
973 line->len++;
974 }
975 cell = line->cells + tp->cx;
976 cell->character = tp->view.ascebc[(unsigned int) ch];
977 cell->highlight = tp->highlight;
978 cell->f_color = tp->f_color;
979}
980
981/*
982 * Convert a tty3270_line to a 3270 data fragment usable for output.
983 */
984static void
985tty3270_convert_line(struct tty3270 *tp, int line_nr)
986{
987 struct tty3270_line *line;
988 struct tty3270_cell *cell;
989 struct string *s, *n;
990 unsigned char highlight;
991 unsigned char f_color;
992 char *cp;
993 int flen, i;
994
995 /* Determine how long the fragment will be. */
996 flen = 3; /* Prefix (TO_SBA). */
997 line = tp->screen + line_nr;
998 flen += line->len;
999 highlight = TAX_RESET;
1000 f_color = TAC_RESET;
1001 for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
1002 if (cell->highlight != highlight) {
1003 flen += 3; /* TO_SA to switch highlight. */
1004 highlight = cell->highlight;
1005 }
1006 if (cell->f_color != f_color) {
1007 flen += 3; /* TO_SA to switch color. */
1008 f_color = cell->f_color;
1009 }
1010 }
1011 if (highlight != TAX_RESET)
1012 flen += 3; /* TO_SA to reset hightlight. */
1013 if (f_color != TAC_RESET)
1014 flen += 3; /* TO_SA to reset color. */
1015 if (line->len < tp->view.cols)
1016 flen += 4; /* Postfix (TO_RA). */
1017
1018 /* Find the line in the list. */
1019 i = tp->view.rows - 2 - line_nr;
1020 list_for_each_entry_reverse(s, &tp->lines, list)
1021 if (--i <= 0)
1022 break;
1023 /*
1024 * Check if the line needs to get reallocated.
1025 */
1026 if (s->len != flen) {
1027 /* Reallocate string. */
1028 n = tty3270_alloc_string(tp, flen);
1029 list_add(&n->list, &s->list);
1030 list_del_init(&s->list);
1031 if (!list_empty(&s->update))
1032 list_del_init(&s->update);
1033 free_string(&tp->freemem, s);
1034 s = n;
1035 }
1036
1037 /* Write 3270 data fragment. */
1038 cp = s->string;
1039 *cp++ = TO_SBA;
1040 *cp++ = 0;
1041 *cp++ = 0;
1042
1043 highlight = TAX_RESET;
1044 f_color = TAC_RESET;
1045 for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
1046 if (cell->highlight != highlight) {
1047 *cp++ = TO_SA;
1048 *cp++ = TAT_EXTHI;
1049 *cp++ = cell->highlight;
1050 highlight = cell->highlight;
1051 }
1052 if (cell->f_color != f_color) {
1053 *cp++ = TO_SA;
1054 *cp++ = TAT_COLOR;
1055 *cp++ = cell->f_color;
1056 f_color = cell->f_color;
1057 }
1058 *cp++ = cell->character;
1059 }
1060 if (highlight != TAX_RESET) {
1061 *cp++ = TO_SA;
1062 *cp++ = TAT_EXTHI;
1063 *cp++ = TAX_RESET;
1064 }
1065 if (f_color != TAC_RESET) {
1066 *cp++ = TO_SA;
1067 *cp++ = TAT_COLOR;
1068 *cp++ = TAC_RESET;
1069 }
1070 if (line->len < tp->view.cols) {
1071 *cp++ = TO_RA;
1072 *cp++ = 0;
1073 *cp++ = 0;
1074 *cp++ = 0;
1075 }
1076
1077 if (tp->nr_up + line_nr < tp->view.rows - 2) {
1078 /* Line is currently visible on screen. */
1079 tty3270_update_string(tp, s, line_nr);
1080 /* Add line to update list. */
1081 if (list_empty(&s->update)) {
1082 list_add_tail(&s->update, &tp->update);
1083 tp->update_flags |= TTY_UPDATE_LIST;
1084 }
1085 }
1086}
1087
1088/*
1089 * Do carriage return.
1090 */
1091static void
1092tty3270_cr(struct tty3270 *tp)
1093{
1094 tp->cx = 0;
1095}
1096
1097/*
1098 * Do line feed.
1099 */
1100static void
1101tty3270_lf(struct tty3270 *tp)
1102{
1103 struct tty3270_line temp;
1104 int i;
1105
1106 tty3270_convert_line(tp, tp->cy);
1107 if (tp->cy < tp->view.rows - 3) {
1108 tp->cy++;
1109 return;
1110 }
1111 /* Last line just filled up. Add new, blank line. */
1112 tty3270_blank_line(tp);
1113 temp = tp->screen[0];
1114 temp.len = 0;
1115 for (i = 0; i < tp->view.rows - 3; i++)
1116 tp->screen[i] = tp->screen[i+1];
1117 tp->screen[tp->view.rows - 3] = temp;
1118 tty3270_rebuild_update(tp);
1119}
1120
1121static void
1122tty3270_ri(struct tty3270 *tp)
1123{
1124 if (tp->cy > 0) {
1125 tty3270_convert_line(tp, tp->cy);
1126 tp->cy--;
1127 }
1128}
1129
1130/*
1131 * Insert characters at current position.
1132 */
1133static void
1134tty3270_insert_characters(struct tty3270 *tp, int n)
1135{
1136 struct tty3270_line *line;
1137 int k;
1138
1139 line = tp->screen + tp->cy;
1140 while (line->len < tp->cx) {
1141 line->cells[line->len].character = tp->view.ascebc[' '];
1142 line->cells[line->len].highlight = TAX_RESET;
1143 line->cells[line->len].f_color = TAC_RESET;
1144 line->len++;
1145 }
1146 if (n > tp->view.cols - tp->cx)
1147 n = tp->view.cols - tp->cx;
1148 k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n);
1149 while (k--)
1150 line->cells[tp->cx + n + k] = line->cells[tp->cx + k];
1151 line->len += n;
1152 if (line->len > tp->view.cols)
1153 line->len = tp->view.cols;
1154 while (n-- > 0) {
1155 line->cells[tp->cx + n].character = tp->view.ascebc[' '];
1156 line->cells[tp->cx + n].highlight = tp->highlight;
1157 line->cells[tp->cx + n].f_color = tp->f_color;
1158 }
1159}
1160
1161/*
1162 * Delete characters at current position.
1163 */
1164static void
1165tty3270_delete_characters(struct tty3270 *tp, int n)
1166{
1167 struct tty3270_line *line;
1168 int i;
1169
1170 line = tp->screen + tp->cy;
1171 if (line->len <= tp->cx)
1172 return;
1173 if (line->len - tp->cx <= n) {
1174 line->len = tp->cx;
1175 return;
1176 }
1177 for (i = tp->cx; i + n < line->len; i++)
1178 line->cells[i] = line->cells[i + n];
1179 line->len -= n;
1180}
1181
1182/*
1183 * Erase characters at current position.
1184 */
1185static void
1186tty3270_erase_characters(struct tty3270 *tp, int n)
1187{
1188 struct tty3270_line *line;
1189 struct tty3270_cell *cell;
1190
1191 line = tp->screen + tp->cy;
1192 while (line->len > tp->cx && n-- > 0) {
1193 cell = line->cells + tp->cx++;
1194 cell->character = ' ';
1195 cell->highlight = TAX_RESET;
1196 cell->f_color = TAC_RESET;
1197 }
1198 tp->cx += n;
1199 tp->cx = min_t(int, tp->cx, tp->view.cols - 1);
1200}
1201
1202/*
1203 * Erase line, 3 different cases:
1204 * Esc [ 0 K Erase from current position to end of line inclusive
1205 * Esc [ 1 K Erase from beginning of line to current position inclusive
1206 * Esc [ 2 K Erase entire line (without moving cursor)
1207 */
1208static void
1209tty3270_erase_line(struct tty3270 *tp, int mode)
1210{
1211 struct tty3270_line *line;
1212 struct tty3270_cell *cell;
1213 int i;
1214
1215 line = tp->screen + tp->cy;
1216 if (mode == 0)
1217 line->len = tp->cx;
1218 else if (mode == 1) {
1219 for (i = 0; i < tp->cx; i++) {
1220 cell = line->cells + i;
1221 cell->character = ' ';
1222 cell->highlight = TAX_RESET;
1223 cell->f_color = TAC_RESET;
1224 }
1225 if (line->len <= tp->cx)
1226 line->len = tp->cx + 1;
1227 } else if (mode == 2)
1228 line->len = 0;
1229 tty3270_convert_line(tp, tp->cy);
1230}
1231
1232/*
1233 * Erase display, 3 different cases:
1234 * Esc [ 0 J Erase from current position to bottom of screen inclusive
1235 * Esc [ 1 J Erase from top of screen to current position inclusive
1236 * Esc [ 2 J Erase entire screen (without moving the cursor)
1237 */
1238static void
1239tty3270_erase_display(struct tty3270 *tp, int mode)
1240{
1241 int i;
1242
1243 if (mode == 0) {
1244 tty3270_erase_line(tp, 0);
1245 for (i = tp->cy + 1; i < tp->view.rows - 2; i++) {
1246 tp->screen[i].len = 0;
1247 tty3270_convert_line(tp, i);
1248 }
1249 } else if (mode == 1) {
1250 for (i = 0; i < tp->cy; i++) {
1251 tp->screen[i].len = 0;
1252 tty3270_convert_line(tp, i);
1253 }
1254 tty3270_erase_line(tp, 1);
1255 } else if (mode == 2) {
1256 for (i = 0; i < tp->view.rows - 2; i++) {
1257 tp->screen[i].len = 0;
1258 tty3270_convert_line(tp, i);
1259 }
1260 }
1261 tty3270_rebuild_update(tp);
1262}
1263
1264/*
1265 * Set attributes found in an escape sequence.
1266 * Esc [ <attr> ; <attr> ; ... m
1267 */
1268static void
1269tty3270_set_attributes(struct tty3270 *tp)
1270{
1271 static unsigned char f_colors[] = {
1272 TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE,
1273 TAC_PINK, TAC_TURQ, TAC_WHITE, 0, TAC_DEFAULT
1274 };
1275 int i, attr;
1276
1277 for (i = 0; i <= tp->esc_npar; i++) {
1278 attr = tp->esc_par[i];
1279 switch (attr) {
1280 case 0: /* Reset */
1281 tp->highlight = TAX_RESET;
1282 tp->f_color = TAC_RESET;
1283 break;
1284 /* Highlight. */
1285 case 4: /* Start underlining. */
1286 tp->highlight = TAX_UNDER;
1287 break;
1288 case 5: /* Start blink. */
1289 tp->highlight = TAX_BLINK;
1290 break;
1291 case 7: /* Start reverse. */
1292 tp->highlight = TAX_REVER;
1293 break;
1294 case 24: /* End underlining */
1295 if (tp->highlight == TAX_UNDER)
1296 tp->highlight = TAX_RESET;
1297 break;
1298 case 25: /* End blink. */
1299 if (tp->highlight == TAX_BLINK)
1300 tp->highlight = TAX_RESET;
1301 break;
1302 case 27: /* End reverse. */
1303 if (tp->highlight == TAX_REVER)
1304 tp->highlight = TAX_RESET;
1305 break;
1306 /* Foreground color. */
1307 case 30: /* Black */
1308 case 31: /* Red */
1309 case 32: /* Green */
1310 case 33: /* Yellow */
1311 case 34: /* Blue */
1312 case 35: /* Magenta */
1313 case 36: /* Cyan */
1314 case 37: /* White */
1315 case 39: /* Black */
1316 tp->f_color = f_colors[attr - 30];
1317 break;
1318 }
1319 }
1320}
1321
1322static inline int
1323tty3270_getpar(struct tty3270 *tp, int ix)
1324{
1325 return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1;
1326}
1327
1328static void
1329tty3270_goto_xy(struct tty3270 *tp, int cx, int cy)
1330{
Heiko Carstens364c8552007-10-12 16:11:35 +02001331 int max_cx = max(0, cx);
1332 int max_cy = max(0, cy);
1333
1334 tp->cx = min_t(int, tp->view.cols - 1, max_cx);
1335 cy = min_t(int, tp->view.rows - 3, max_cy);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 if (cy != tp->cy) {
1337 tty3270_convert_line(tp, tp->cy);
1338 tp->cy = cy;
1339 }
1340}
1341
1342/*
1343 * Process escape sequences. Known sequences:
1344 * Esc 7 Save Cursor Position
1345 * Esc 8 Restore Cursor Position
1346 * Esc [ Pn ; Pn ; .. m Set attributes
1347 * Esc [ Pn ; Pn H Cursor Position
1348 * Esc [ Pn ; Pn f Cursor Position
1349 * Esc [ Pn A Cursor Up
1350 * Esc [ Pn B Cursor Down
1351 * Esc [ Pn C Cursor Forward
1352 * Esc [ Pn D Cursor Backward
1353 * Esc [ Pn G Cursor Horizontal Absolute
1354 * Esc [ Pn X Erase Characters
1355 * Esc [ Ps J Erase in Display
1356 * Esc [ Ps K Erase in Line
1357 * // FIXME: add all the new ones.
1358 *
1359 * Pn is a numeric parameter, a string of zero or more decimal digits.
1360 * Ps is a selective parameter.
1361 */
1362static void
1363tty3270_escape_sequence(struct tty3270 *tp, char ch)
1364{
1365 enum { ESnormal, ESesc, ESsquare, ESgetpars };
1366
1367 if (tp->esc_state == ESnormal) {
1368 if (ch == 0x1b)
1369 /* Starting new escape sequence. */
1370 tp->esc_state = ESesc;
1371 return;
1372 }
1373 if (tp->esc_state == ESesc) {
1374 tp->esc_state = ESnormal;
1375 switch (ch) {
1376 case '[':
1377 tp->esc_state = ESsquare;
1378 break;
1379 case 'E':
1380 tty3270_cr(tp);
1381 tty3270_lf(tp);
1382 break;
1383 case 'M':
1384 tty3270_ri(tp);
1385 break;
1386 case 'D':
1387 tty3270_lf(tp);
1388 break;
1389 case 'Z': /* Respond ID. */
1390 kbd_puts_queue(tp->tty, "\033[?6c");
1391 break;
1392 case '7': /* Save cursor position. */
1393 tp->saved_cx = tp->cx;
1394 tp->saved_cy = tp->cy;
1395 tp->saved_highlight = tp->highlight;
1396 tp->saved_f_color = tp->f_color;
1397 break;
1398 case '8': /* Restore cursor position. */
1399 tty3270_convert_line(tp, tp->cy);
1400 tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
1401 tp->highlight = tp->saved_highlight;
1402 tp->f_color = tp->saved_f_color;
1403 break;
1404 case 'c': /* Reset terminal. */
1405 tp->cx = tp->saved_cx = 0;
1406 tp->cy = tp->saved_cy = 0;
1407 tp->highlight = tp->saved_highlight = TAX_RESET;
1408 tp->f_color = tp->saved_f_color = TAC_RESET;
1409 tty3270_erase_display(tp, 2);
1410 break;
1411 }
1412 return;
1413 }
1414 if (tp->esc_state == ESsquare) {
1415 tp->esc_state = ESgetpars;
1416 memset(tp->esc_par, 0, sizeof(tp->esc_par));
1417 tp->esc_npar = 0;
1418 tp->esc_ques = (ch == '?');
1419 if (tp->esc_ques)
1420 return;
1421 }
1422 if (tp->esc_state == ESgetpars) {
1423 if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) {
1424 tp->esc_npar++;
1425 return;
1426 }
1427 if (ch >= '0' && ch <= '9') {
1428 tp->esc_par[tp->esc_npar] *= 10;
1429 tp->esc_par[tp->esc_npar] += ch - '0';
1430 return;
1431 }
1432 }
1433 tp->esc_state = ESnormal;
1434 if (ch == 'n' && !tp->esc_ques) {
1435 if (tp->esc_par[0] == 5) /* Status report. */
1436 kbd_puts_queue(tp->tty, "\033[0n");
1437 else if (tp->esc_par[0] == 6) { /* Cursor report. */
1438 char buf[40];
1439 sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1);
1440 kbd_puts_queue(tp->tty, buf);
1441 }
1442 return;
1443 }
1444 if (tp->esc_ques)
1445 return;
1446 switch (ch) {
1447 case 'm':
1448 tty3270_set_attributes(tp);
1449 break;
1450 case 'H': /* Set cursor position. */
1451 case 'f':
1452 tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1,
1453 tty3270_getpar(tp, 0) - 1);
1454 break;
1455 case 'd': /* Set y position. */
1456 tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1);
1457 break;
1458 case 'A': /* Cursor up. */
1459 case 'F':
1460 tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0));
1461 break;
1462 case 'B': /* Cursor down. */
1463 case 'e':
1464 case 'E':
1465 tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0));
1466 break;
1467 case 'C': /* Cursor forward. */
1468 case 'a':
1469 tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy);
1470 break;
1471 case 'D': /* Cursor backward. */
1472 tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy);
1473 break;
1474 case 'G': /* Set x position. */
1475 case '`':
1476 tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy);
1477 break;
1478 case 'X': /* Erase Characters. */
1479 tty3270_erase_characters(tp, tty3270_getpar(tp, 0));
1480 break;
1481 case 'J': /* Erase display. */
1482 tty3270_erase_display(tp, tp->esc_par[0]);
1483 break;
1484 case 'K': /* Erase line. */
1485 tty3270_erase_line(tp, tp->esc_par[0]);
1486 break;
1487 case 'P': /* Delete characters. */
1488 tty3270_delete_characters(tp, tty3270_getpar(tp, 0));
1489 break;
1490 case '@': /* Insert characters. */
1491 tty3270_insert_characters(tp, tty3270_getpar(tp, 0));
1492 break;
1493 case 's': /* Save cursor position. */
1494 tp->saved_cx = tp->cx;
1495 tp->saved_cy = tp->cy;
1496 tp->saved_highlight = tp->highlight;
1497 tp->saved_f_color = tp->f_color;
1498 break;
1499 case 'u': /* Restore cursor position. */
1500 tty3270_convert_line(tp, tp->cy);
1501 tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
1502 tp->highlight = tp->saved_highlight;
1503 tp->f_color = tp->saved_f_color;
1504 break;
1505 }
1506}
1507
1508/*
1509 * String write routine for 3270 ttys
1510 */
1511static void
Jiri Slaby20acdfa2012-04-02 13:54:17 +02001512tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty,
1513 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514{
1515 int i_msg, i;
1516
1517 spin_lock_bh(&tp->view.lock);
Jiri Slaby20acdfa2012-04-02 13:54:17 +02001518 for (i_msg = 0; !tty->stopped && i_msg < count; i_msg++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 if (tp->esc_state != 0) {
1520 /* Continue escape sequence. */
1521 tty3270_escape_sequence(tp, buf[i_msg]);
1522 continue;
1523 }
1524
1525 switch (buf[i_msg]) {
1526 case 0x07: /* '\a' -- Alarm */
1527 tp->wcc |= TW_PLUSALARM;
1528 break;
1529 case 0x08: /* Backspace. */
1530 if (tp->cx > 0) {
1531 tp->cx--;
1532 tty3270_put_character(tp, ' ');
1533 }
1534 break;
1535 case 0x09: /* '\t' -- Tabulate */
1536 for (i = tp->cx % 8; i < 8; i++) {
1537 if (tp->cx >= tp->view.cols) {
1538 tty3270_cr(tp);
1539 tty3270_lf(tp);
1540 break;
1541 }
1542 tty3270_put_character(tp, ' ');
1543 tp->cx++;
1544 }
1545 break;
1546 case 0x0a: /* '\n' -- New Line */
1547 tty3270_cr(tp);
1548 tty3270_lf(tp);
1549 break;
1550 case 0x0c: /* '\f' -- Form Feed */
1551 tty3270_erase_display(tp, 2);
1552 tp->cx = tp->cy = 0;
1553 break;
1554 case 0x0d: /* '\r' -- Carriage Return */
1555 tp->cx = 0;
1556 break;
1557 case 0x0f: /* SuSE "exit alternate mode" */
1558 break;
1559 case 0x1b: /* Start escape sequence. */
1560 tty3270_escape_sequence(tp, buf[i_msg]);
1561 break;
1562 default: /* Insert normal character. */
1563 if (tp->cx >= tp->view.cols) {
1564 tty3270_cr(tp);
1565 tty3270_lf(tp);
1566 }
1567 tty3270_put_character(tp, buf[i_msg]);
1568 tp->cx++;
1569 break;
1570 }
1571 }
1572 /* Convert current line to 3270 data fragment. */
1573 tty3270_convert_line(tp, tp->cy);
1574
1575 /* Setup timer to update display after 1/10 second */
1576 if (!timer_pending(&tp->timer))
1577 tty3270_set_timer(tp, HZ/10);
1578
1579 spin_unlock_bh(&tp->view.lock);
1580}
1581
1582/*
1583 * String write routine for 3270 ttys
1584 */
1585static int
1586tty3270_write(struct tty_struct * tty,
1587 const unsigned char *buf, int count)
1588{
1589 struct tty3270 *tp;
1590
1591 tp = tty->driver_data;
1592 if (!tp)
1593 return 0;
1594 if (tp->char_count > 0) {
Jiri Slaby20acdfa2012-04-02 13:54:17 +02001595 tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596 tp->char_count = 0;
1597 }
Jiri Slaby20acdfa2012-04-02 13:54:17 +02001598 tty3270_do_write(tp, tty, buf, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 return count;
1600}
1601
1602/*
1603 * Put single characters to the ttys character buffer
1604 */
Heiko Carstens74c76c82008-05-07 09:22:58 +02001605static int tty3270_put_char(struct tty_struct *tty, unsigned char ch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606{
1607 struct tty3270 *tp;
1608
1609 tp = tty->driver_data;
Heiko Carstens74c76c82008-05-07 09:22:58 +02001610 if (!tp || tp->char_count >= TTY3270_CHAR_BUF_SIZE)
1611 return 0;
1612 tp->char_buf[tp->char_count++] = ch;
1613 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614}
1615
1616/*
1617 * Flush all characters from the ttys characeter buffer put there
1618 * by tty3270_put_char.
1619 */
1620static void
1621tty3270_flush_chars(struct tty_struct *tty)
1622{
1623 struct tty3270 *tp;
1624
1625 tp = tty->driver_data;
1626 if (!tp)
1627 return;
1628 if (tp->char_count > 0) {
Jiri Slaby20acdfa2012-04-02 13:54:17 +02001629 tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630 tp->char_count = 0;
1631 }
1632}
1633
1634/*
1635 * Returns the number of characters in the output buffer. This is
1636 * used in tty_wait_until_sent to wait until all characters have
1637 * appeared on the screen.
1638 */
1639static int
1640tty3270_chars_in_buffer(struct tty_struct *tty)
1641{
1642 return 0;
1643}
1644
1645static void
1646tty3270_flush_buffer(struct tty_struct *tty)
1647{
1648}
1649
1650/*
1651 * Check for visible/invisible input switches
1652 */
1653static void
Alan Cox606d0992006-12-08 02:38:45 -08001654tty3270_set_termios(struct tty_struct *tty, struct ktermios *old)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655{
1656 struct tty3270 *tp;
1657 int new;
1658
1659 tp = tty->driver_data;
1660 if (!tp)
1661 return;
1662 spin_lock_bh(&tp->view.lock);
1663 if (L_ICANON(tty)) {
1664 new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN;
1665 if (new != tp->inattr) {
1666 tp->inattr = new;
Heiko Carstensd2c993d2006-07-12 16:41:55 +02001667 tty3270_update_prompt(tp, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 tty3270_set_timer(tp, 1);
1669 }
1670 }
1671 spin_unlock_bh(&tp->view.lock);
1672}
1673
1674/*
1675 * Disable reading from a 3270 tty
1676 */
1677static void
1678tty3270_throttle(struct tty_struct * tty)
1679{
1680 struct tty3270 *tp;
1681
1682 tp = tty->driver_data;
1683 if (!tp)
1684 return;
1685 tp->throttle = 1;
1686}
1687
1688/*
1689 * Enable reading from a 3270 tty
1690 */
1691static void
1692tty3270_unthrottle(struct tty_struct * tty)
1693{
1694 struct tty3270 *tp;
1695
1696 tp = tty->driver_data;
1697 if (!tp)
1698 return;
1699 tp->throttle = 0;
1700 if (tp->attn)
1701 tty3270_issue_read(tp, 1);
1702}
1703
1704/*
1705 * Hang up the tty device.
1706 */
1707static void
1708tty3270_hangup(struct tty_struct *tty)
1709{
1710 // FIXME: implement
1711}
1712
1713static void
1714tty3270_wait_until_sent(struct tty_struct *tty, int timeout)
1715{
1716}
1717
Heiko Carstens65c56e02011-02-25 14:28:30 +01001718static int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd,
1719 unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720{
1721 struct tty3270 *tp;
1722
1723 tp = tty->driver_data;
1724 if (!tp)
1725 return -ENODEV;
1726 if (tty->flags & (1 << TTY_IO_ERROR))
1727 return -EIO;
Heiko Carstens65c56e02011-02-25 14:28:30 +01001728 return kbd_ioctl(tp->kbd, cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729}
1730
Arnd Bergmann9d4bfd42009-12-07 12:52:13 +01001731#ifdef CONFIG_COMPAT
Heiko Carstens65c56e02011-02-25 14:28:30 +01001732static long tty3270_compat_ioctl(struct tty_struct *tty,
1733 unsigned int cmd, unsigned long arg)
Arnd Bergmann9d4bfd42009-12-07 12:52:13 +01001734{
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;
Heiko Carstens65c56e02011-02-25 14:28:30 +01001742 return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg));
Arnd Bergmann9d4bfd42009-12-07 12:52:13 +01001743}
1744#endif
1745
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001746static const struct tty_operations tty3270_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 .open = tty3270_open,
1748 .close = tty3270_close,
1749 .write = tty3270_write,
1750 .put_char = tty3270_put_char,
1751 .flush_chars = tty3270_flush_chars,
1752 .write_room = tty3270_write_room,
1753 .chars_in_buffer = tty3270_chars_in_buffer,
1754 .flush_buffer = tty3270_flush_buffer,
1755 .throttle = tty3270_throttle,
1756 .unthrottle = tty3270_unthrottle,
1757 .hangup = tty3270_hangup,
1758 .wait_until_sent = tty3270_wait_until_sent,
1759 .ioctl = tty3270_ioctl,
Arnd Bergmann9d4bfd42009-12-07 12:52:13 +01001760#ifdef CONFIG_COMPAT
1761 .compat_ioctl = tty3270_compat_ioctl,
1762#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 .set_termios = tty3270_set_termios
1764};
1765
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766/*
1767 * 3270 tty registration code called from tty_init().
1768 * Most kernel services (incl. kmalloc) are available at this poimt.
1769 */
Heiko Carstens2b67fc42007-02-05 21:16:47 +01001770static int __init tty3270_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771{
1772 struct tty_driver *driver;
1773 int ret;
1774
Richard Hitted3cb6f2005-10-30 15:00:10 -08001775 driver = alloc_tty_driver(RAW3270_MAXDEVS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776 if (!driver)
1777 return -ENOMEM;
1778
1779 /*
1780 * Initialize the tty_driver structure
1781 * Entries in tty3270_driver that are NOT initialized:
1782 * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
1783 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 driver->driver_name = "ttyTUB";
1785 driver->name = "ttyTUB";
1786 driver->major = IBM_TTY3270_MAJOR;
Richard Hitted3cb6f2005-10-30 15:00:10 -08001787 driver->minor_start = RAW3270_FIRSTMINOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 driver->type = TTY_DRIVER_TYPE_SYSTEM;
1789 driver->subtype = SYSTEM_TYPE_TTY;
1790 driver->init_termios = tty_std_termios;
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001791 driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792 tty_set_operations(driver, &tty3270_ops);
1793 ret = tty_register_driver(driver);
1794 if (ret) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795 put_tty_driver(driver);
1796 return ret;
1797 }
1798 tty3270_driver = driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 return 0;
1800}
1801
1802static void __exit
1803tty3270_exit(void)
1804{
1805 struct tty_driver *driver;
1806
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807 driver = tty3270_driver;
Heiko Carstensd2c993d2006-07-12 16:41:55 +02001808 tty3270_driver = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 tty_unregister_driver(driver);
1810 tty3270_del_views();
1811}
1812
1813MODULE_LICENSE("GPL");
1814MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR);
1815
1816module_init(tty3270_init);
1817module_exit(tty3270_exit);