s390/tty3270: add 3270 datastream helpers
[linux-block.git] / drivers / s390 / char / con3270.c
CommitLineData
6f05e69e 1// SPDX-License-Identifier: GPL-2.0
1da177e4 2/*
1da177e4
LT
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>
a53c8fab 8 * -- Copyright IBM Corp. 2003
1da177e4
LT
9 */
10
1da177e4
LT
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>
4d334fd1 19#include <linux/workqueue.h>
c17fe081
SS
20#include <linux/panic_notifier.h>
21#include <linux/reboot.h>
1da177e4 22#include <linux/slab.h>
57c8a661 23#include <linux/memblock.h>
9d4bfd41 24#include <linux/compat.h>
1da177e4
LT
25
26#include <asm/ccwdev.h>
27#include <asm/cio.h>
28#include <asm/ebcdic.h>
c17fe081 29#include <asm/cpcmd.h>
7c0f6ba6 30#include <linux/uaccess.h>
1da177e4 31
1da177e4
LT
32#include "raw3270.h"
33#include "keyboard.h"
34
35#define TTY3270_CHAR_BUF_SIZE 256
36#define TTY3270_OUTPUT_BUFFER_SIZE 1024
37#define TTY3270_STRING_PAGES 5
38
b2057c87
SS
39#define TTY3270_SCREEN_PAGES 8 /* has to be power-of-two */
40
c17fe081 41static struct tty_driver *tty3270_driver;
1da177e4 42static int tty3270_max_index;
c17fe081 43static struct tty3270 *condev;
2b67fc46 44static struct raw3270_fn tty3270_fn;
1da177e4 45
c2e9375e 46struct tty3270_attribute {
94dbb0a7 47 unsigned char alternate_charset:1; /* Graphics charset */
4043ea22
SS
48 unsigned char highlight; /* Blink/reverse/underscore */
49 unsigned char f_color; /* Foreground color */
50 unsigned char b_color; /* Background color */
c2e9375e
SS
51};
52
1da177e4
LT
53struct tty3270_cell {
54 unsigned char character;
c2e9375e 55 struct tty3270_attribute attributes;
1da177e4
LT
56};
57
58struct tty3270_line {
59 struct tty3270_cell *cells;
60 int len;
b2057c87 61 int dirty;
1da177e4
LT
62};
63
cbb36313
SS
64static const unsigned char sfq_read_partition[] = {
65 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81
66};
67
1da177e4
LT
68#define ESCAPE_NPAR 8
69
70/*
71 * The main tty view data structure.
72 * FIXME:
73 * 1) describe line orientation & lines list concept against screen
74 * 2) describe conversion of screen to lines
75 * 3) describe line format.
76 */
77struct tty3270 {
78 struct raw3270_view view;
ba186e7d 79 struct tty_port port;
1da177e4
LT
80 void **freemem_pages; /* Array of pages used for freemem. */
81 struct list_head freemem; /* List of free memory for strings. */
82
83 /* Output stuff. */
84 struct list_head lines; /* List of lines. */
85 struct list_head update; /* List of lines to update. */
86 unsigned char wcc; /* Write control character. */
87 int nr_lines; /* # lines in list. */
88 int nr_up; /* # lines up in history. */
89 unsigned long update_flags; /* Update indication bits. */
90 struct string *status; /* Lower right of display. */
91 struct raw3270_request *write; /* Single write request. */
92 struct timer_list timer; /* Output delay timer. */
93
94 /* Current tty screen. */
95 unsigned int cx, cy; /* Current output position. */
c2e9375e
SS
96 struct tty3270_attribute attributes;
97 struct tty3270_attribute saved_attributes;
b2057c87 98 int allocated_lines;
1da177e4
LT
99 struct tty3270_line *screen;
100
101 /* Input stuff. */
102 struct string *prompt; /* Output string for input area. */
103 struct string *input; /* Input string for read request. */
104 struct raw3270_request *read; /* Single read request. */
105 struct raw3270_request *kreset; /* Single keyboard reset request. */
cbb36313 106 struct raw3270_request *readpartreq;
1da177e4
LT
107 unsigned char inattr; /* Visible/invisible input. */
108 int throttle, attn; /* tty throttle/unthrottle. */
109 struct tasklet_struct readlet; /* Tasklet to issue read request. */
0c756914 110 struct tasklet_struct hanglet; /* Tasklet to hang up the tty. */
1da177e4
LT
111 struct kbd_data *kbd; /* key_maps stuff. */
112
113 /* Escape sequence parsing. */
114 int esc_state, esc_ques, esc_npar;
115 int esc_par[ESCAPE_NPAR];
116 unsigned int saved_cx, saved_cy;
1da177e4
LT
117
118 /* Command recalling. */
119 struct list_head rcl_lines; /* List of recallable lines. */
120 struct list_head *rcl_walk; /* Point in rcl_lines list. */
121 int rcl_nr, rcl_max; /* Number/max number of rcl_lines. */
122
123 /* Character array for put_char/flush_chars. */
124 unsigned int char_count;
125 char char_buf[TTY3270_CHAR_BUF_SIZE];
126};
127
128/* tty3270->update_flags. See tty3270_update for details. */
129#define TTY_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */
130#define TTY_UPDATE_LIST 2 /* Update lines in tty3270->update. */
131#define TTY_UPDATE_INPUT 4 /* Update input line. */
132#define TTY_UPDATE_STATUS 8 /* Update status line. */
205d7ab9 133#define TTY_UPDATE_ALL 16 /* Recreate screen. */
1da177e4 134
9eb99b94 135#define TTY3270_INPUT_AREA_ROWS 2
f77f936a 136
c9602ee7 137static void tty3270_update(struct timer_list *);
1da177e4
LT
138/*
139 * Setup timeout for a device. On timeout trigger an update.
140 */
2b67fc46 141static void tty3270_set_timer(struct tty3270 *tp, int expires)
1da177e4 142{
03439e7d 143 mod_timer(&tp->timer, jiffies + expires);
1da177e4
LT
144}
145
9eb99b94
SS
146static int tty3270_tty_rows(struct tty3270 *tp)
147{
148 return tp->view.rows - TTY3270_INPUT_AREA_ROWS;
149}
150
ae657244
SS
151static char *tty3270_add_ba(struct tty3270 *tp, char *cp, char order, int x, int y)
152{
153 *cp++ = order;
154 raw3270_buffer_address(tp->view.dev, cp, x, y);
155 return cp + 2;
156}
157
158static char *tty3270_add_ra(struct tty3270 *tp, char *cp, int x, int y, char c)
159{
160 cp = tty3270_add_ba(tp, cp, TO_RA, x, y);
161 *cp++ = c;
162 return cp;
163}
164
165static char *tty3270_add_sa(struct tty3270 *tp, char *cp, char attr, char value)
166{
167 *cp++ = TO_SA;
168 *cp++ = attr;
169 *cp++ = value;
170 return cp;
171}
172
173static char *tty3270_add_ge(struct tty3270 *tp, char *cp, char c)
174{
175 *cp++ = TO_GE;
176 *cp++ = c;
177 return cp;
178}
179
1da177e4
LT
180/*
181 * The input line are the two last lines of the screen.
182 */
e6d98bb8 183static void tty3270_update_prompt(struct tty3270 *tp, char *input, int count)
1da177e4
LT
184{
185 struct string *line;
1da177e4
LT
186
187 line = tp->prompt;
188 if (count != 0)
189 line->string[5] = TF_INMDT;
190 else
191 line->string[5] = tp->inattr;
192 if (count > tp->view.cols * 2 - 11)
193 count = tp->view.cols * 2 - 11;
194 memcpy(line->string + 6, input, count);
195 line->string[6 + count] = TO_IC;
196 /* Clear to end of input line. */
197 if (count < tp->view.cols * 2 - 11) {
ae657244 198 tty3270_add_ra(tp, line->string + count + 7, -9, -1, 0);
1da177e4
LT
199 line->len = 11 + count;
200 } else
201 line->len = 7 + count;
202 tp->update_flags |= TTY_UPDATE_INPUT;
203}
204
e6d98bb8 205static void tty3270_create_prompt(struct tty3270 *tp)
1da177e4
LT
206{
207 static const unsigned char blueprint[] =
208 { TO_SBA, 0, 0, 0x6e, TO_SF, TF_INPUT,
209 /* empty input string */
210 TO_IC, TO_RA, 0, 0, 0 };
211 struct string *line;
1da177e4
LT
212
213 line = alloc_string(&tp->freemem,
214 sizeof(blueprint) + tp->view.cols * 2 - 9);
215 tp->prompt = line;
216 tp->inattr = TF_INPUT;
217 /* Copy blueprint to status line */
218 memcpy(line->string, blueprint, sizeof(blueprint));
219 line->len = sizeof(blueprint);
220 /* Set output offsets. */
f77f936a
SS
221
222 raw3270_buffer_address(tp->view.dev, line->string + 1, 0, -2);
223 raw3270_buffer_address(tp->view.dev, line->string + 8, -9, -1);
1da177e4
LT
224
225 /* Allocate input string for reading. */
226 tp->input = alloc_string(&tp->freemem, tp->view.cols * 2 - 9 + 6);
227}
228
229/*
230 * The status line is the last line of the screen. It shows the string
231 * "Running"/"Holding" in the lower right corner of the screen.
232 */
e6d98bb8 233static void tty3270_update_status(struct tty3270 *tp)
1da177e4
LT
234{
235 char *str;
236
237 str = (tp->nr_up != 0) ? "History" : "Running";
238 memcpy(tp->status->string + 8, str, 7);
239 codepage_convert(tp->view.ascebc, tp->status->string + 8, 7);
240 tp->update_flags |= TTY_UPDATE_STATUS;
241}
242
ae657244
SS
243/*
244 * The status line is the last line of the screen. It shows the string
245 * "Running"/"Holding" in the lower right corner of the screen.
246 */
247static void tty3270_create_status(struct tty3270 * tp)
1da177e4 248{
4043ea22
SS
249 static const unsigned char blueprint[] = {
250 TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_FGCOLOR, TAC_GREEN,
251 0, 0, 0, 0, 0, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_FGCOLOR,
252 TAC_RESET
253 };
1da177e4
LT
254 struct string *line;
255 unsigned int offset;
256
257 line = alloc_string(&tp->freemem,sizeof(blueprint));
258 tp->status = line;
259 /* Copy blueprint to status line */
260 memcpy(line->string, blueprint, sizeof(blueprint));
261 /* Set address to start of status string (= last 9 characters). */
262 offset = tp->view.cols * tp->view.rows - 9;
f77f936a 263 raw3270_buffer_address(tp->view.dev, line->string + 1, -9, -1);
1da177e4
LT
264}
265
266/*
267 * Set output offsets to 3270 datastream fragment of a tty string.
268 * (TO_SBA offset at the start and TO_RA offset at the end of the string)
269 */
e6d98bb8 270static void tty3270_update_string(struct tty3270 *tp, struct string *line, int nr)
1da177e4
LT
271{
272 unsigned char *cp;
273
f77f936a 274 raw3270_buffer_address(tp->view.dev, line->string + 1, 0, nr);
1da177e4
LT
275 cp = line->string + line->len - 4;
276 if (*cp == TO_RA)
f77f936a 277 raw3270_buffer_address(tp->view.dev, cp + 1, 0, nr + 1);
1da177e4
LT
278}
279
280/*
281 * Rebuild update list to print all lines.
282 */
e6d98bb8 283static void tty3270_rebuild_update(struct tty3270 *tp)
1da177e4
LT
284{
285 struct string *s, *n;
286 int line, nr_up;
287
288 /*
289 * Throw away update list and create a new one,
290 * containing all lines that will fit on the screen.
291 */
292 list_for_each_entry_safe(s, n, &tp->update, update)
293 list_del_init(&s->update);
9eb99b94 294 line = tty3270_tty_rows(tp) - 1;
1da177e4
LT
295 nr_up = tp->nr_up;
296 list_for_each_entry_reverse(s, &tp->lines, list) {
297 if (nr_up > 0) {
298 nr_up--;
299 continue;
300 }
301 tty3270_update_string(tp, s, line);
302 list_add(&s->update, &tp->update);
303 if (--line < 0)
304 break;
305 }
306 tp->update_flags |= TTY_UPDATE_LIST;
307}
308
309/*
310 * Alloc string for size bytes. If there is not enough room in
311 * freemem, free strings until there is room.
312 */
e6d98bb8 313static struct string *tty3270_alloc_string(struct tty3270 *tp, size_t size)
1da177e4
LT
314{
315 struct string *s, *n;
316
317 s = alloc_string(&tp->freemem, size);
318 if (s)
319 return s;
320 list_for_each_entry_safe(s, n, &tp->lines, list) {
9eb99b94 321 BUG_ON(tp->nr_lines <= tty3270_tty_rows(tp));
1da177e4
LT
322 list_del(&s->list);
323 if (!list_empty(&s->update))
324 list_del(&s->update);
325 tp->nr_lines--;
326 if (free_string(&tp->freemem, s) >= size)
327 break;
328 }
329 s = alloc_string(&tp->freemem, size);
330 BUG_ON(!s);
331 if (tp->nr_up != 0 &&
9eb99b94
SS
332 tp->nr_up + tty3270_tty_rows(tp) >= tp->nr_lines) {
333 tp->nr_up = tp->nr_lines - tp->view.rows + TTY3270_INPUT_AREA_ROWS;
1da177e4
LT
334 tty3270_rebuild_update(tp);
335 tty3270_update_status(tp);
336 }
337 return s;
338}
339
340/*
341 * Add an empty line to the list.
342 */
e6d98bb8 343static void tty3270_blank_line(struct tty3270 *tp)
1da177e4 344{
4043ea22
SS
345 static const unsigned char blueprint[] = {
346 TO_SBA, 0, 0, TO_SA, TAT_EXTHI, TAX_RESET,
347 TO_SA, TAT_FGCOLOR, TAC_RESET, TO_RA, 0, 0, 0,
348 };
1da177e4
LT
349 struct string *s;
350
351 s = tty3270_alloc_string(tp, sizeof(blueprint));
352 memcpy(s->string, blueprint, sizeof(blueprint));
353 s->len = sizeof(blueprint);
354 list_add_tail(&s->list, &tp->lines);
355 tp->nr_lines++;
356 if (tp->nr_up != 0)
357 tp->nr_up++;
358}
359
7e36eff1
MS
360/*
361 * Create a blank screen and remove all lines from the history.
362 */
e6d98bb8 363static void tty3270_blank_screen(struct tty3270 *tp)
7e36eff1
MS
364{
365 struct string *s, *n;
366 int i;
367
9eb99b94 368 for (i = 0; i < tty3270_tty_rows(tp); i++)
7e36eff1
MS
369 tp->screen[i].len = 0;
370 tp->nr_up = 0;
371 list_for_each_entry_safe(s, n, &tp->lines, list) {
372 list_del(&s->list);
373 if (!list_empty(&s->update))
374 list_del(&s->update);
375 tp->nr_lines--;
376 free_string(&tp->freemem, s);
377 }
378}
379
1da177e4
LT
380/*
381 * Write request completion callback.
382 */
e6d98bb8 383static void tty3270_write_callback(struct raw3270_request *rq, void *data)
1da177e4 384{
881e18f9 385 struct tty3270 *tp = container_of(rq->view, struct tty3270, view);
1da177e4 386
1da177e4 387 if (rq->rc != 0) {
25985edc 388 /* Write wasn't successful. Refresh all. */
1da177e4
LT
389 tp->update_flags = TTY_UPDATE_ALL;
390 tty3270_set_timer(tp, 1);
391 }
392 raw3270_request_reset(rq);
393 xchg(&tp->write, rq);
394}
395
2b62ba58
SS
396static int tty3270_required_length(struct tty3270 *tp, int line_nr)
397{
398 unsigned char f_color, b_color, highlight;
399 struct tty3270_line *line;
400 struct tty3270_cell *cell;
401 int i, flen = 3; /* Prefix (TO_SBA). */
402
403 line = tp->screen + line_nr;
404 flen += line->len;
405 highlight = TAX_RESET;
406 f_color = TAC_RESET;
407 b_color = TAC_RESET;
408
409 for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
410 if (cell->attributes.highlight != highlight) {
411 flen += 3; /* TO_SA to switch highlight. */
412 highlight = cell->attributes.highlight;
413 }
414 if (cell->attributes.f_color != f_color) {
415 flen += 3; /* TO_SA to switch color. */
416 f_color = cell->attributes.f_color;
417 }
418 if (cell->attributes.b_color != b_color) {
419 flen += 3; /* TO_SA to switch color. */
420 b_color = cell->attributes.b_color;
421 }
422 if (cell->attributes.alternate_charset)
423 flen += 1; /* TO_GE to switch to graphics extensions */
424 }
425 if (highlight != TAX_RESET)
426 flen += 3; /* TO_SA to reset hightlight. */
427 if (f_color != TAC_RESET)
428 flen += 3; /* TO_SA to reset color. */
429 if (b_color != TAC_RESET)
430 flen += 3; /* TO_SA to reset color. */
431 if (line->len < tp->view.cols)
432 flen += 4; /* Postfix (TO_RA). */
433
434 return flen;
435}
436
437static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_line *line,
438 char *cp, struct tty3270_attribute *attr)
439{
ae657244
SS
440 if (attr->highlight != TAX_RESET)
441 cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET);
442 if (attr->f_color != TAC_RESET)
443 cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAX_RESET);
444 if (attr->b_color != TAC_RESET)
445 cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, TAX_RESET);
446 if (line->len < tp->view.cols)
447 cp = tty3270_add_ra(tp, cp, 0, 0, 0);
2b62ba58
SS
448 return cp;
449}
450
6e49017c
SS
451static char tty3270_graphics_translate(struct tty3270 *tp, char ch)
452{
453 switch (ch) {
454 case 'q': /* - */
455 return 0xa2;
456 case 'x': /* '|' */
457 return 0x85;
458 case 'l': /* |- */
459 return 0xc5;
460 case 't': /* |_ */
461 return 0xc6;
462 case 'u': /* _| */
463 return 0xd6;
464 case 'k': /* -| */
465 return 0xd5;
466 case 'j':
467 return 0xd4;
468 case 'm':
469 return 0xc4;
470 case 'n': /* + */
471 return 0xd3;
472 case 'v':
473 return 0xc7;
474 case 'w':
475 return 0xd7;
476 default:
477 return ch;
478 }
479}
480
481static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *line,
482 struct tty3270_attribute *attr, char *cp)
2b62ba58
SS
483{
484 struct tty3270_cell *cell;
ae657244 485 int c, i;
2b62ba58 486
ae657244 487 cp = tty3270_add_ba(tp, cp, TO_SBA, 0, 0);
2b62ba58
SS
488
489 for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
490 if (cell->attributes.highlight != attr->highlight) {
2b62ba58 491 attr->highlight = cell->attributes.highlight;
ae657244 492 cp = tty3270_add_sa(tp, cp, TAT_EXTHI, attr->highlight);
2b62ba58
SS
493 }
494 if (cell->attributes.f_color != attr->f_color) {
2b62ba58 495 attr->f_color = cell->attributes.f_color;
ae657244 496 cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, attr->f_color);
2b62ba58
SS
497 }
498 if (cell->attributes.b_color != attr->b_color) {
2b62ba58 499 attr->b_color = cell->attributes.b_color;
ae657244 500 cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, attr->b_color);
2b62ba58 501 }
ae657244
SS
502 c = cell->character;
503 if (cell->attributes.alternate_charset)
504 cp = tty3270_add_ge(tp, cp, tty3270_graphics_translate(tp, c));
505 else
506 *cp++ = tp->view.ascebc[c];
2b62ba58
SS
507 }
508 return cp;
509}
510
511static void tty3270_reset_attributes(struct tty3270_attribute *attr)
512{
513 attr->highlight = TAX_RESET;
514 attr->f_color = TAC_RESET;
515 attr->b_color = TAC_RESET;
516}
517
518static struct string *tty3270_resize_line(struct tty3270 *tp, struct string *s, int newlen)
519{
520 struct string *n = tty3270_alloc_string(tp, newlen);
521
522 list_add(&n->list, &s->list);
523 list_del_init(&s->list);
524 if (!list_empty(&s->update))
525 list_del_init(&s->update);
526 free_string(&tp->freemem, s);
527 return n;
528}
529
530/*
531 * Convert a tty3270_line to a 3270 data fragment usable for output.
532 */
533static void tty3270_convert_line(struct tty3270 *tp, int line_nr)
534{
535 struct tty3270_line *line = tp->screen + line_nr;
536 struct tty3270_attribute attr;
537 struct string *s;
538 int flen, i;
539 char *cp;
540
541 /* Determine how long the fragment will be. */
542 flen = tty3270_required_length(tp, line_nr);
543 /* Find the line in the list. */
544 i = tty3270_tty_rows(tp) - line_nr;
545 list_for_each_entry_reverse(s, &tp->lines, list)
546 if (--i <= 0)
547 break;
548 /*
549 * Check if the line needs to get reallocated.
550 */
551 if (s->len != flen)
552 s = tty3270_resize_line(tp, s, flen);
553
554 /* Write 3270 data fragment. */
555 tty3270_reset_attributes(&attr);
6e49017c 556 cp = tty3270_add_attributes(tp, line, &attr, s->string);
2b62ba58
SS
557 cp = tty3270_add_reset_attributes(tp, line, cp, &attr);
558 if (tp->nr_up + line_nr < tty3270_tty_rows(tp)) {
559 /* Line is currently visible on screen. */
560 tty3270_update_string(tp, s, line_nr);
561 /* Add line to update list. */
562 if (list_empty(&s->update)) {
563 list_add_tail(&s->update, &tp->update);
564 tp->update_flags |= TTY_UPDATE_LIST;
565 }
566 }
567}
568
1da177e4
LT
569/*
570 * Update 3270 display.
571 */
e6d98bb8 572static void tty3270_update(struct timer_list *t)
1da177e4 573{
c9602ee7 574 struct tty3270 *tp = from_timer(tp, t, timer);
1da177e4
LT
575 static char invalid_sba[2] = { 0xff, 0xff };
576 struct raw3270_request *wrq;
577 unsigned long updated;
578 struct string *s, *n;
579 char *sba, *str;
580 int rc, len;
581
582 wrq = xchg(&tp->write, 0);
583 if (!wrq) {
584 tty3270_set_timer(tp, 1);
585 return;
586 }
587
c17fe081 588 spin_lock_irq(&tp->view.lock);
1da177e4 589 updated = 0;
205d7ab9
MS
590 if (tp->update_flags & TTY_UPDATE_ALL) {
591 tty3270_rebuild_update(tp);
592 tty3270_update_status(tp);
593 tp->update_flags = TTY_UPDATE_ERASE | TTY_UPDATE_LIST |
594 TTY_UPDATE_INPUT | TTY_UPDATE_STATUS;
595 }
1da177e4
LT
596 if (tp->update_flags & TTY_UPDATE_ERASE) {
597 /* Use erase write alternate to erase display. */
598 raw3270_request_set_cmd(wrq, TC_EWRITEA);
599 updated |= TTY_UPDATE_ERASE;
600 } else
601 raw3270_request_set_cmd(wrq, TC_WRITE);
602
603 raw3270_request_add_data(wrq, &tp->wcc, 1);
604 tp->wcc = TW_NONE;
605
606 /*
607 * Update status line.
608 */
609 if (tp->update_flags & TTY_UPDATE_STATUS)
610 if (raw3270_request_add_data(wrq, tp->status->string,
611 tp->status->len) == 0)
612 updated |= TTY_UPDATE_STATUS;
613
614 /*
615 * Write input line.
616 */
617 if (tp->update_flags & TTY_UPDATE_INPUT)
618 if (raw3270_request_add_data(wrq, tp->prompt->string,
619 tp->prompt->len) == 0)
620 updated |= TTY_UPDATE_INPUT;
621
622 sba = invalid_sba;
623
624 if (tp->update_flags & TTY_UPDATE_LIST) {
625 /* Write strings in the update list to the screen. */
626 list_for_each_entry_safe(s, n, &tp->update, update) {
627 str = s->string;
628 len = s->len;
629 /*
630 * Skip TO_SBA at the start of the string if the
631 * last output position matches the start address
632 * of this line.
633 */
db232eb4
JP
634 if (s->string[1] == sba[0] && s->string[2] == sba[1]) {
635 str += 3;
636 len -= 3;
637 }
1da177e4
LT
638 if (raw3270_request_add_data(wrq, str, len) != 0)
639 break;
640 list_del_init(&s->update);
2e63a3a6
MS
641 if (s->string[s->len - 4] == TO_RA)
642 sba = s->string + s->len - 3;
643 else
644 sba = invalid_sba;
1da177e4
LT
645 }
646 if (list_empty(&tp->update))
647 updated |= TTY_UPDATE_LIST;
648 }
649 wrq->callback = tty3270_write_callback;
650 rc = raw3270_start(&tp->view, wrq);
651 if (rc == 0) {
652 tp->update_flags &= ~updated;
653 if (tp->update_flags)
654 tty3270_set_timer(tp, 1);
655 } else {
656 raw3270_request_reset(wrq);
657 xchg(&tp->write, wrq);
658 }
c17fe081 659 spin_unlock_irq(&tp->view.lock);
1da177e4
LT
660}
661
662/*
663 * Command recalling.
664 */
e6d98bb8 665static void tty3270_rcl_add(struct tty3270 *tp, char *input, int len)
1da177e4
LT
666{
667 struct string *s;
668
d2c993d8 669 tp->rcl_walk = NULL;
1da177e4
LT
670 if (len <= 0)
671 return;
672 if (tp->rcl_nr >= tp->rcl_max) {
673 s = list_entry(tp->rcl_lines.next, struct string, list);
674 list_del(&s->list);
675 free_string(&tp->freemem, s);
676 tp->rcl_nr--;
677 }
678 s = tty3270_alloc_string(tp, len);
679 memcpy(s->string, input, len);
680 list_add_tail(&s->list, &tp->rcl_lines);
681 tp->rcl_nr++;
682}
683
e6d98bb8 684static void tty3270_rcl_backward(struct kbd_data *kbd)
1da177e4 685{
ba186e7d 686 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
1da177e4
LT
687 struct string *s;
688
c17fe081 689 spin_lock_irq(&tp->view.lock);
1da177e4
LT
690 if (tp->inattr == TF_INPUT) {
691 if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines)
692 tp->rcl_walk = tp->rcl_walk->prev;
693 else if (!list_empty(&tp->rcl_lines))
694 tp->rcl_walk = tp->rcl_lines.prev;
695 s = tp->rcl_walk ?
d2c993d8 696 list_entry(tp->rcl_walk, struct string, list) : NULL;
1da177e4
LT
697 if (tp->rcl_walk) {
698 s = list_entry(tp->rcl_walk, struct string, list);
699 tty3270_update_prompt(tp, s->string, s->len);
700 } else
d2c993d8 701 tty3270_update_prompt(tp, NULL, 0);
1da177e4
LT
702 tty3270_set_timer(tp, 1);
703 }
c17fe081 704 spin_unlock_irq(&tp->view.lock);
1da177e4
LT
705}
706
707/*
708 * Deactivate tty view.
709 */
e6d98bb8 710static void tty3270_exit_tty(struct kbd_data *kbd)
1da177e4 711{
ba186e7d 712 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
1da177e4 713
1da177e4
LT
714 raw3270_deactivate_view(&tp->view);
715}
716
717/*
718 * Scroll forward in history.
719 */
e6d98bb8 720static void tty3270_scroll_forward(struct kbd_data *kbd)
1da177e4 721{
ba186e7d 722 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
1da177e4
LT
723 int nr_up;
724
c17fe081 725 spin_lock_irq(&tp->view.lock);
9eb99b94 726 nr_up = tp->nr_up - tp->view.rows + TTY3270_INPUT_AREA_ROWS;
1da177e4
LT
727 if (nr_up < 0)
728 nr_up = 0;
729 if (nr_up != tp->nr_up) {
730 tp->nr_up = nr_up;
731 tty3270_rebuild_update(tp);
732 tty3270_update_status(tp);
733 tty3270_set_timer(tp, 1);
734 }
c17fe081 735 spin_unlock_irq(&tp->view.lock);
1da177e4
LT
736}
737
738/*
739 * Scroll backward in history.
740 */
e6d98bb8 741static void tty3270_scroll_backward(struct kbd_data *kbd)
1da177e4 742{
ba186e7d 743 struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
1da177e4
LT
744 int nr_up;
745
c17fe081 746 spin_lock_irq(&tp->view.lock);
9eb99b94
SS
747 nr_up = tp->nr_up + tty3270_tty_rows(tp);
748 if (nr_up + tty3270_tty_rows(tp) > tp->nr_lines)
749 nr_up = tp->nr_lines - tp->view.rows + TTY3270_INPUT_AREA_ROWS;
1da177e4
LT
750 if (nr_up != tp->nr_up) {
751 tp->nr_up = nr_up;
752 tty3270_rebuild_update(tp);
753 tty3270_update_status(tp);
754 tty3270_set_timer(tp, 1);
755 }
c17fe081 756 spin_unlock_irq(&tp->view.lock);
1da177e4
LT
757}
758
759/*
760 * Pass input line to tty.
761 */
e6d98bb8 762static void tty3270_read_tasklet(unsigned long data)
1da177e4 763{
5cdfbdce 764 struct raw3270_request *rrq = (struct raw3270_request *)data;
1da177e4 765 static char kreset_data = TW_KR;
881e18f9 766 struct tty3270 *tp = container_of(rrq->view, struct tty3270, view);
1da177e4
LT
767 char *input;
768 int len;
769
c17fe081 770 spin_lock_irq(&tp->view.lock);
1da177e4
LT
771 /*
772 * Two AID keys are special: For 0x7d (enter) the input line
773 * has to be emitted to the tty and for 0x6d the screen
774 * needs to be redrawn.
775 */
d2c993d8 776 input = NULL;
1da177e4 777 len = 0;
e22de7d7
SS
778 switch (tp->input->string[0]) {
779 case AID_ENTER:
1da177e4
LT
780 /* Enter: write input to tty. */
781 input = tp->input->string + 6;
782 len = tp->input->len - 6 - rrq->rescnt;
783 if (tp->inattr != TF_INPUTN)
784 tty3270_rcl_add(tp, input, len);
785 if (tp->nr_up > 0) {
786 tp->nr_up = 0;
787 tty3270_rebuild_update(tp);
788 tty3270_update_status(tp);
789 }
790 /* Clear input area. */
d2c993d8 791 tty3270_update_prompt(tp, NULL, 0);
1da177e4 792 tty3270_set_timer(tp, 1);
e22de7d7
SS
793 break;
794 case AID_CLEAR:
1da177e4 795 /* Display has been cleared. Redraw. */
1da177e4
LT
796 tp->update_flags = TTY_UPDATE_ALL;
797 tty3270_set_timer(tp, 1);
cbb36313
SS
798 if (!list_empty(&tp->readpartreq->list))
799 break;
800 raw3270_start_request(&tp->view, tp->readpartreq, TC_WRITESF,
801 (char *)sfq_read_partition, sizeof(sfq_read_partition));
802 break;
803 case AID_READ_PARTITION:
804 raw3270_read_modified_cb(tp->readpartreq, tp->input->string);
805 break;
e22de7d7
SS
806 default:
807 break;
1da177e4 808 }
c17fe081 809 spin_unlock_irq(&tp->view.lock);
1da177e4
LT
810
811 /* Start keyboard reset command. */
f08e3155 812 raw3270_start_request(&tp->view, tp->kreset, TC_WRITE, &kreset_data, 1);
1da177e4 813
ba186e7d
JS
814 while (len-- > 0)
815 kbd_keycode(tp->kbd, *input++);
816 /* Emit keycode for AID byte. */
817 kbd_keycode(tp->kbd, 256 + tp->input->string[0]);
1da177e4
LT
818
819 raw3270_request_reset(rrq);
820 xchg(&tp->read, rrq);
821 raw3270_put_view(&tp->view);
822}
823
824/*
825 * Read request completion callback.
826 */
e6d98bb8 827static void tty3270_read_callback(struct raw3270_request *rq, void *data)
1da177e4 828{
881e18f9 829 struct tty3270 *tp = container_of(rq->view, struct tty3270, view);
1da177e4
LT
830 raw3270_get_view(rq->view);
831 /* Schedule tasklet to pass input to tty. */
881e18f9 832 tasklet_schedule(&tp->readlet);
1da177e4
LT
833}
834
835/*
836 * Issue a read request. Call with device lock.
837 */
e6d98bb8 838static void tty3270_issue_read(struct tty3270 *tp, int lock)
1da177e4
LT
839{
840 struct raw3270_request *rrq;
841 int rc;
842
843 rrq = xchg(&tp->read, 0);
844 if (!rrq)
845 /* Read already scheduled. */
846 return;
847 rrq->callback = tty3270_read_callback;
848 rrq->callback_data = tp;
849 raw3270_request_set_cmd(rrq, TC_READMOD);
850 raw3270_request_set_data(rrq, tp->input->string, tp->input->len);
851 /* Issue the read modified request. */
852 if (lock) {
853 rc = raw3270_start(&tp->view, rrq);
854 } else
855 rc = raw3270_start_irq(&tp->view, rrq);
856 if (rc) {
857 raw3270_request_reset(rrq);
858 xchg(&tp->read, rrq);
859 }
860}
861
0c756914
MS
862/*
863 * Hang up the tty
864 */
e6d98bb8 865static void tty3270_hangup_tasklet(unsigned long data)
0c756914 866{
5cdfbdce 867 struct tty3270 *tp = (struct tty3270 *)data;
0c756914
MS
868 tty_port_tty_hangup(&tp->port, true);
869 raw3270_put_view(&tp->view);
870}
871
1da177e4
LT
872/*
873 * Switch to the tty view.
874 */
e6d98bb8 875static int tty3270_activate(struct raw3270_view *view)
1da177e4 876{
881e18f9 877 struct tty3270 *tp = container_of(view, struct tty3270, view);
1da177e4 878
1da177e4
LT
879 tp->update_flags = TTY_UPDATE_ALL;
880 tty3270_set_timer(tp, 1);
1da177e4
LT
881 return 0;
882}
883
e6d98bb8 884static void tty3270_deactivate(struct raw3270_view *view)
1da177e4 885{
881e18f9 886 struct tty3270 *tp = container_of(view, struct tty3270, view);
205d7ab9 887
205d7ab9 888 del_timer(&tp->timer);
1da177e4
LT
889}
890
e6d98bb8 891static void tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
1da177e4
LT
892{
893 /* Handle ATTN. Schedule tasklet to read aid. */
23d805b6 894 if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
1da177e4
LT
895 if (!tp->throttle)
896 tty3270_issue_read(tp, 0);
897 else
898 tp->attn = 1;
899 }
900
901 if (rq) {
0c756914 902 if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
1da177e4 903 rq->rc = -EIO;
0c756914
MS
904 raw3270_get_view(&tp->view);
905 tasklet_schedule(&tp->hanglet);
906 } else {
1da177e4 907 /* Normal end. Copy residual count. */
23d805b6 908 rq->rescnt = irb->scsw.cmd.count;
0c756914 909 }
05bfd70b
MS
910 } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
911 /* Interrupt without an outstanding request -> update all */
912 tp->update_flags = TTY_UPDATE_ALL;
913 tty3270_set_timer(tp, 1);
1da177e4 914 }
1da177e4
LT
915}
916
917/*
918 * Allocate tty3270 structure.
919 */
e6d98bb8 920static struct tty3270 *tty3270_alloc_view(void)
1da177e4
LT
921{
922 struct tty3270 *tp;
923 int pages;
924
88abaab4 925 tp = kzalloc(sizeof(struct tty3270), GFP_KERNEL);
1da177e4
LT
926 if (!tp)
927 goto out_err;
1da177e4 928 tp->freemem_pages =
6da2ec56
KC
929 kmalloc_array(TTY3270_STRING_PAGES, sizeof(void *),
930 GFP_KERNEL);
1da177e4
LT
931 if (!tp->freemem_pages)
932 goto out_tp;
933 INIT_LIST_HEAD(&tp->freemem);
9d2ae233
JS
934 INIT_LIST_HEAD(&tp->lines);
935 INIT_LIST_HEAD(&tp->update);
936 INIT_LIST_HEAD(&tp->rcl_lines);
937 tp->rcl_max = 20;
9d2ae233 938
1da177e4
LT
939 for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) {
940 tp->freemem_pages[pages] = (void *)
941 __get_free_pages(GFP_KERNEL|GFP_DMA, 0);
942 if (!tp->freemem_pages[pages])
943 goto out_pages;
944 add_string_memory(&tp->freemem,
945 tp->freemem_pages[pages], PAGE_SIZE);
946 }
947 tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE);
ed3cb6f0 948 if (IS_ERR(tp->write))
1da177e4
LT
949 goto out_pages;
950 tp->read = raw3270_request_alloc(0);
ed3cb6f0 951 if (IS_ERR(tp->read))
1da177e4
LT
952 goto out_write;
953 tp->kreset = raw3270_request_alloc(1);
ed3cb6f0 954 if (IS_ERR(tp->kreset))
1da177e4 955 goto out_read;
cbb36313
SS
956 tp->readpartreq = raw3270_request_alloc(sizeof(sfq_read_partition));
957 if (IS_ERR(tp->readpartreq))
958 goto out_reset;
1da177e4
LT
959 tp->kbd = kbd_alloc();
960 if (!tp->kbd)
cbb36313 961 goto out_readpartreq;
57985d7e
MS
962
963 tty_port_init(&tp->port);
c9602ee7 964 timer_setup(&tp->timer, tty3270_update, 0);
5cdfbdce 965 tasklet_init(&tp->readlet, tty3270_read_tasklet,
57985d7e 966 (unsigned long) tp->read);
5cdfbdce 967 tasklet_init(&tp->hanglet, tty3270_hangup_tasklet,
0c756914 968 (unsigned long) tp);
1da177e4
LT
969 return tp;
970
cbb36313
SS
971out_readpartreq:
972 raw3270_request_free(tp->readpartreq);
1da177e4
LT
973out_reset:
974 raw3270_request_free(tp->kreset);
975out_read:
976 raw3270_request_free(tp->read);
977out_write:
978 raw3270_request_free(tp->write);
979out_pages:
980 while (pages--)
981 free_pages((unsigned long) tp->freemem_pages[pages], 0);
982 kfree(tp->freemem_pages);
191c5f10 983 tty_port_destroy(&tp->port);
1da177e4
LT
984out_tp:
985 kfree(tp);
986out_err:
987 return ERR_PTR(-ENOMEM);
988}
989
990/*
991 * Free tty3270 structure.
992 */
e6d98bb8 993static void tty3270_free_view(struct tty3270 *tp)
1da177e4
LT
994{
995 int pages;
996
997 kbd_free(tp->kbd);
998 raw3270_request_free(tp->kreset);
999 raw3270_request_free(tp->read);
1000 raw3270_request_free(tp->write);
1001 for (pages = 0; pages < TTY3270_STRING_PAGES; pages++)
1002 free_pages((unsigned long) tp->freemem_pages[pages], 0);
1003 kfree(tp->freemem_pages);
191c5f10 1004 tty_port_destroy(&tp->port);
1da177e4
LT
1005 kfree(tp);
1006}
1007
1008/*
1009 * Allocate tty3270 screen.
1010 */
b2057c87
SS
1011static struct tty3270_line *tty3270_alloc_screen(struct tty3270 *tp, unsigned int rows,
1012 unsigned int cols, int *allocated_out)
1da177e4 1013{
4d334fd1 1014 struct tty3270_line *screen;
b2057c87 1015 int allocated, lines;
1da177e4 1016
b2057c87
SS
1017 allocated = __roundup_pow_of_two(rows) * TTY3270_SCREEN_PAGES;
1018 screen = kcalloc(allocated, sizeof(struct tty3270_line), GFP_KERNEL);
4d334fd1 1019 if (!screen)
1da177e4 1020 goto out_err;
b2057c87
SS
1021 for (lines = 0; lines < allocated; lines++) {
1022 screen[lines].cells = kcalloc(cols, sizeof(struct tty3270_cell), GFP_KERNEL);
4d334fd1 1023 if (!screen[lines].cells)
1da177e4 1024 goto out_screen;
1da177e4 1025 }
b2057c87 1026 *allocated_out = allocated;
4d334fd1 1027 return screen;
1da177e4
LT
1028out_screen:
1029 while (lines--)
4d334fd1
MS
1030 kfree(screen[lines].cells);
1031 kfree(screen);
1da177e4 1032out_err:
4d334fd1 1033 return ERR_PTR(-ENOMEM);
1da177e4
LT
1034}
1035
1036/*
1037 * Free tty3270 screen.
1038 */
b2057c87 1039static void tty3270_free_screen(struct tty3270_line *screen, int old_lines)
1da177e4
LT
1040{
1041 int lines;
1042
b2057c87 1043 for (lines = 0; lines < old_lines; lines++)
4d334fd1
MS
1044 kfree(screen[lines].cells);
1045 kfree(screen);
1046}
1047
1048/*
1049 * Resize tty3270 screen
1050 */
91621ba7
SS
1051static void tty3270_resize(struct raw3270_view *view,
1052 int new_model, int new_rows, int new_cols,
1053 int old_model, int old_rows, int old_cols)
4d334fd1 1054{
91621ba7 1055 struct tty3270 *tp = container_of(view, struct tty3270, view);
4d334fd1
MS
1056 struct tty3270_line *screen, *oscreen;
1057 struct tty_struct *tty;
4d334fd1 1058 struct winsize ws;
b2057c87 1059 int new_allocated, old_allocated = tp->allocated_lines;
4d334fd1 1060
91621ba7
SS
1061 if (old_model == new_model &&
1062 old_cols == new_cols &&
1063 old_rows == new_rows) {
1064 spin_lock_irq(&tp->view.lock);
1065 tp->update_flags = TTY_UPDATE_ALL;
1066 tty3270_set_timer(tp, 1);
1067 spin_unlock_irq(&tp->view.lock);
1068 return;
1069 }
b2057c87 1070 screen = tty3270_alloc_screen(tp, new_rows, new_cols, &new_allocated);
8f0ba630 1071 if (IS_ERR(screen))
4d334fd1
MS
1072 return;
1073 /* Switch to new output size */
c17fe081 1074 spin_lock_irq(&tp->view.lock);
7e36eff1 1075 tty3270_blank_screen(tp);
4d334fd1 1076 oscreen = tp->screen;
4d334fd1 1077 tp->screen = screen;
b2057c87 1078 tp->allocated_lines = new_allocated;
91621ba7
SS
1079 tp->view.rows = new_rows;
1080 tp->view.cols = new_cols;
1081 tp->view.model = new_model;
1082
4d334fd1
MS
1083 free_string(&tp->freemem, tp->prompt);
1084 free_string(&tp->freemem, tp->status);
1085 tty3270_create_prompt(tp);
1086 tty3270_create_status(tp);
9eb99b94 1087 while (tp->nr_lines < tty3270_tty_rows(tp))
4d334fd1
MS
1088 tty3270_blank_line(tp);
1089 tp->update_flags = TTY_UPDATE_ALL;
c17fe081 1090 spin_unlock_irq(&tp->view.lock);
b2057c87 1091 tty3270_free_screen(oscreen, old_allocated);
4d334fd1
MS
1092 tty3270_set_timer(tp, 1);
1093 /* Informat tty layer about new size */
1094 tty = tty_port_tty_get(&tp->port);
1095 if (!tty)
1096 return;
9eb99b94 1097 ws.ws_row = tty3270_tty_rows(tp);
4d334fd1
MS
1098 ws.ws_col = tp->view.cols;
1099 tty_do_resize(tty, &ws);
5ff04fe5 1100 tty_kref_put(tty);
4d334fd1
MS
1101}
1102
1da177e4
LT
1103/*
1104 * Unlink tty3270 data structure from tty.
1105 */
e6d98bb8 1106static void tty3270_release(struct raw3270_view *view)
1da177e4 1107{
881e18f9 1108 struct tty3270 *tp = container_of(view, struct tty3270, view);
ba186e7d 1109 struct tty_struct *tty = tty_port_tty_get(&tp->port);
1da177e4 1110
1da177e4 1111 if (tty) {
d2c993d8 1112 tty->driver_data = NULL;
ba186e7d 1113 tty_port_tty_set(&tp->port, NULL);
1da177e4
LT
1114 tty_hangup(tty);
1115 raw3270_put_view(&tp->view);
ba186e7d 1116 tty_kref_put(tty);
1da177e4
LT
1117 }
1118}
1119
1120/*
1121 * Free tty3270 data structure
1122 */
e6d98bb8 1123static void tty3270_free(struct raw3270_view *view)
1da177e4 1124{
881e18f9 1125 struct tty3270 *tp = container_of(view, struct tty3270, view);
4d334fd1 1126
03439e7d 1127 del_timer_sync(&tp->timer);
b2057c87 1128 tty3270_free_screen(tp->screen, tp->allocated_lines);
881e18f9 1129 tty3270_free_view(tp);
1da177e4
LT
1130}
1131
1132/*
1133 * Delayed freeing of tty3270 views.
1134 */
e6d98bb8 1135static void tty3270_del_views(void)
1da177e4 1136{
1da177e4
LT
1137 int i;
1138
c95571e6
MS
1139 for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) {
1140 struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i);
881e18f9
JS
1141 if (!IS_ERR(view))
1142 raw3270_del_view(view);
1da177e4
LT
1143 }
1144}
1145
2b67fc46 1146static struct raw3270_fn tty3270_fn = {
1da177e4
LT
1147 .activate = tty3270_activate,
1148 .deactivate = tty3270_deactivate,
1149 .intv = (void *) tty3270_irq,
1150 .release = tty3270_release,
4d334fd1
MS
1151 .free = tty3270_free,
1152 .resize = tty3270_resize
1da177e4
LT
1153};
1154
a21e962e
SS
1155static int
1156tty3270_create_view(int index, struct tty3270 **newtp)
1da177e4
LT
1157{
1158 struct tty3270 *tp;
1159 int i, rc;
1160
a21e962e
SS
1161 if (tty3270_max_index < index + 1)
1162 tty3270_max_index = index + 1;
1da177e4
LT
1163
1164 /* Allocate tty3270 structure on first open. */
1165 tp = tty3270_alloc_view();
1166 if (IS_ERR(tp))
1167 return PTR_ERR(tp);
1168
1fcbba3d 1169 rc = raw3270_add_view(&tp->view, &tty3270_fn,
a21e962e 1170 index + RAW3270_FIRSTMINOR,
c17fe081 1171 RAW3270_VIEW_LOCK_IRQ);
1da177e4
LT
1172 if (rc) {
1173 tty3270_free_view(tp);
1174 return rc;
1175 }
1176
b2057c87
SS
1177 tp->screen = tty3270_alloc_screen(tp, tp->view.rows, tp->view.cols,
1178 &tp->allocated_lines);
4d334fd1
MS
1179 if (IS_ERR(tp->screen)) {
1180 rc = PTR_ERR(tp->screen);
1da177e4 1181 raw3270_put_view(&tp->view);
ed3cb6f0 1182 raw3270_del_view(&tp->view);
4d334fd1 1183 tty3270_free_view(tp);
1da177e4
LT
1184 return rc;
1185 }
1186
1da177e4
LT
1187 tty3270_create_prompt(tp);
1188 tty3270_create_status(tp);
1189 tty3270_update_status(tp);
1190
1191 /* Create blank line for every line in the tty output area. */
9eb99b94 1192 for (i = 0; i < tty3270_tty_rows(tp); i++)
1da177e4
LT
1193 tty3270_blank_line(tp);
1194
ba186e7d 1195 tp->kbd->port = &tp->port;
1da177e4
LT
1196 tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty;
1197 tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward;
1198 tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward;
1199 tp->kbd->fn_handler[KVAL(K_CONS)] = tty3270_rcl_backward;
1200 kbd_ascebc(tp->kbd, tp->view.ascebc);
1201
1202 raw3270_activate_view(&tp->view);
a21e962e
SS
1203 raw3270_put_view(&tp->view);
1204 *newtp = tp;
1205 return 0;
1206}
1207
1208/*
1209 * This routine is called whenever a 3270 tty is opened first time.
1210 */
1211static int
1212tty3270_install(struct tty_driver *driver, struct tty_struct *tty)
1213{
1214 struct raw3270_view *view;
1215 struct tty3270 *tp;
1216 int rc;
20cda6f2 1217
a21e962e
SS
1218 /* Check if the tty3270 is already there. */
1219 view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR);
1220 if (IS_ERR(view)) {
1221 rc = tty3270_create_view(tty->index, &tp);
1222 if (rc)
1223 return rc;
1224 } else {
1225 tp = container_of(view, struct tty3270, view);
1226 tty->driver_data = tp;
1227 tp->inattr = TF_INPUT;
1228 }
1229
9eb99b94 1230 tty->winsize.ws_row = tty3270_tty_rows(tp);
a21e962e 1231 tty->winsize.ws_col = tp->view.cols;
20cda6f2
JS
1232 rc = tty_port_install(&tp->port, driver, tty);
1233 if (rc) {
1234 raw3270_put_view(&tp->view);
1235 return rc;
1236 }
20cda6f2 1237 tty->driver_data = tp;
1da177e4
LT
1238 return 0;
1239}
1240
736c9fd2
MS
1241/*
1242 * This routine is called whenever a 3270 tty is opened.
1243 */
e6d98bb8 1244static int tty3270_open(struct tty_struct *tty, struct file *filp)
736c9fd2
MS
1245{
1246 struct tty3270 *tp = tty->driver_data;
1247 struct tty_port *port = &tp->port;
1248
1249 port->count++;
1250 tty_port_tty_set(port, tty);
1251 return 0;
1252}
1253
1da177e4
LT
1254/*
1255 * This routine is called when the 3270 tty is closed. We wait
1256 * for the remaining request to be completed. Then we clean up.
1257 */
e6d98bb8 1258static void tty3270_close(struct tty_struct *tty, struct file *filp)
1da177e4 1259{
881e18f9 1260 struct tty3270 *tp = tty->driver_data;
1da177e4
LT
1261
1262 if (tty->count > 1)
1263 return;
b512353c 1264 if (tp)
ba186e7d 1265 tty_port_tty_set(&tp->port, NULL);
1da177e4
LT
1266}
1267
20cda6f2
JS
1268static void tty3270_cleanup(struct tty_struct *tty)
1269{
1270 struct tty3270 *tp = tty->driver_data;
1271
b512353c
MS
1272 if (tp) {
1273 tty->driver_data = NULL;
20cda6f2 1274 raw3270_put_view(&tp->view);
b512353c 1275 }
20cda6f2
JS
1276}
1277
1da177e4
LT
1278/*
1279 * We always have room.
1280 */
e6d98bb8 1281static unsigned int tty3270_write_room(struct tty_struct *tty)
1da177e4
LT
1282{
1283 return INT_MAX;
1284}
1285
1286/*
1287 * Insert character into the screen at the current position with the
1288 * current color and highlight. This function does NOT do cursor movement.
1289 */
74c76c84 1290static void tty3270_put_character(struct tty3270 *tp, char ch)
1da177e4
LT
1291{
1292 struct tty3270_line *line;
1293 struct tty3270_cell *cell;
1294
1295 line = tp->screen + tp->cy;
1296 if (line->len <= tp->cx) {
1297 while (line->len < tp->cx) {
1298 cell = line->cells + line->len;
6e49017c 1299 cell->character = ' ';
c2e9375e 1300 cell->attributes = tp->attributes;
1da177e4
LT
1301 line->len++;
1302 }
1303 line->len++;
1304 }
1305 cell = line->cells + tp->cx;
6e49017c 1306 cell->character = ch;
c2e9375e 1307 cell->attributes = tp->attributes;
1da177e4
LT
1308}
1309
1da177e4
LT
1310/*
1311 * Do carriage return.
1312 */
e6d98bb8 1313static void tty3270_cr(struct tty3270 *tp)
1da177e4
LT
1314{
1315 tp->cx = 0;
1316}
1317
1318/*
1319 * Do line feed.
1320 */
e6d98bb8 1321static void tty3270_lf(struct tty3270 *tp)
1da177e4
LT
1322{
1323 struct tty3270_line temp;
1324 int i;
1325
1326 tty3270_convert_line(tp, tp->cy);
9eb99b94 1327 if (tp->cy < tty3270_tty_rows(tp) - 1) {
1da177e4
LT
1328 tp->cy++;
1329 return;
1330 }
1331 /* Last line just filled up. Add new, blank line. */
1332 tty3270_blank_line(tp);
1333 temp = tp->screen[0];
1334 temp.len = 0;
9eb99b94 1335 for (i = 0; i < tty3270_tty_rows(tp) - 1; i++)
1da177e4 1336 tp->screen[i] = tp->screen[i+1];
9eb99b94 1337 tp->screen[tty3270_tty_rows(tp) - 1] = temp;
1da177e4
LT
1338 tty3270_rebuild_update(tp);
1339}
1340
e6d98bb8 1341static void tty3270_ri(struct tty3270 *tp)
1da177e4
LT
1342{
1343 if (tp->cy > 0) {
1344 tty3270_convert_line(tp, tp->cy);
1345 tp->cy--;
1346 }
1347}
1348
c2e9375e
SS
1349static void tty3270_reset_cell(struct tty3270 *tp, struct tty3270_cell *cell)
1350{
6e49017c 1351 cell->character = ' ';
c2e9375e
SS
1352 tty3270_reset_attributes(&cell->attributes);
1353}
1354
1da177e4
LT
1355/*
1356 * Insert characters at current position.
1357 */
e6d98bb8 1358static void tty3270_insert_characters(struct tty3270 *tp, int n)
1da177e4
LT
1359{
1360 struct tty3270_line *line;
1361 int k;
1362
1363 line = tp->screen + tp->cy;
c2e9375e
SS
1364 while (line->len < tp->cx)
1365 tty3270_reset_cell(tp, &line->cells[line->len++]);
1da177e4
LT
1366 if (n > tp->view.cols - tp->cx)
1367 n = tp->view.cols - tp->cx;
1368 k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n);
1369 while (k--)
1370 line->cells[tp->cx + n + k] = line->cells[tp->cx + k];
1371 line->len += n;
1372 if (line->len > tp->view.cols)
1373 line->len = tp->view.cols;
1374 while (n-- > 0) {
6e49017c 1375 line->cells[tp->cx + n].character = ' ';
c2e9375e 1376 line->cells[tp->cx + n].attributes = tp->attributes;
1da177e4
LT
1377 }
1378}
1379
1380/*
1381 * Delete characters at current position.
1382 */
e6d98bb8 1383static void tty3270_delete_characters(struct tty3270 *tp, int n)
1da177e4
LT
1384{
1385 struct tty3270_line *line;
1386 int i;
1387
1388 line = tp->screen + tp->cy;
1389 if (line->len <= tp->cx)
1390 return;
1391 if (line->len - tp->cx <= n) {
1392 line->len = tp->cx;
1393 return;
1394 }
1395 for (i = tp->cx; i + n < line->len; i++)
1396 line->cells[i] = line->cells[i + n];
1397 line->len -= n;
1398}
1399
1400/*
1401 * Erase characters at current position.
1402 */
e6d98bb8 1403static void tty3270_erase_characters(struct tty3270 *tp, int n)
1da177e4
LT
1404{
1405 struct tty3270_line *line;
1406 struct tty3270_cell *cell;
1407
1408 line = tp->screen + tp->cy;
1409 while (line->len > tp->cx && n-- > 0) {
1410 cell = line->cells + tp->cx++;
c2e9375e 1411 tty3270_reset_cell(tp, cell);
1da177e4
LT
1412 }
1413 tp->cx += n;
1414 tp->cx = min_t(int, tp->cx, tp->view.cols - 1);
1415}
1416
1417/*
1418 * Erase line, 3 different cases:
1419 * Esc [ 0 K Erase from current position to end of line inclusive
1420 * Esc [ 1 K Erase from beginning of line to current position inclusive
1421 * Esc [ 2 K Erase entire line (without moving cursor)
1422 */
e6d98bb8 1423static void tty3270_erase_line(struct tty3270 *tp, int mode)
1da177e4
LT
1424{
1425 struct tty3270_line *line;
1426 struct tty3270_cell *cell;
4043ea22 1427 int i, start, end;
1da177e4
LT
1428
1429 line = tp->screen + tp->cy;
4043ea22 1430
815f3eee
SS
1431 switch (mode) {
1432 case 0:
4043ea22
SS
1433 start = tp->cx;
1434 end = tp->view.cols;
815f3eee
SS
1435 break;
1436 case 1:
4043ea22
SS
1437 start = 0;
1438 end = tp->cx;
815f3eee
SS
1439 break;
1440 case 2:
4043ea22
SS
1441 start = 0;
1442 end = tp->view.cols;
815f3eee
SS
1443 break;
1444 default:
1445 return;
1446 }
4043ea22
SS
1447
1448 for (i = start; i < end; i++) {
1449 cell = line->cells + i;
1450 tty3270_reset_cell(tp, cell);
1451 cell->attributes.b_color = tp->attributes.b_color;
1452 }
1453
1454 if (line->len <= end)
1455 line->len = end;
1456
1da177e4
LT
1457 tty3270_convert_line(tp, tp->cy);
1458}
1459
1460/*
1461 * Erase display, 3 different cases:
1462 * Esc [ 0 J Erase from current position to bottom of screen inclusive
1463 * Esc [ 1 J Erase from top of screen to current position inclusive
1464 * Esc [ 2 J Erase entire screen (without moving the cursor)
1465 */
e6d98bb8 1466static void tty3270_erase_display(struct tty3270 *tp, int mode)
1da177e4 1467{
65b77ccb 1468 int i, start, end;
1da177e4 1469
65b77ccb
SS
1470 switch (mode) {
1471 case 0:
1da177e4 1472 tty3270_erase_line(tp, 0);
65b77ccb 1473 start = tp->cy + 1;
9eb99b94 1474 end = tty3270_tty_rows(tp);
65b77ccb
SS
1475 break;
1476 case 1:
1477 start = 0;
1478 end = tp->cy;
1da177e4 1479 tty3270_erase_line(tp, 1);
65b77ccb
SS
1480 break;
1481 case 2:
1482 start = 0;
9eb99b94 1483 end = tty3270_tty_rows(tp);
65b77ccb
SS
1484 break;
1485 default:
1486 return;
1487 }
1488 for (i = start; i < end; i++) {
1489 tp->screen[i].len = 0;
1490 tty3270_convert_line(tp, i);
1da177e4
LT
1491 }
1492 tty3270_rebuild_update(tp);
1493}
1494
1495/*
1496 * Set attributes found in an escape sequence.
1497 * Esc [ <attr> ; <attr> ; ... m
1498 */
e6d98bb8 1499static void tty3270_set_attributes(struct tty3270 *tp)
1da177e4 1500{
4043ea22 1501 static unsigned char colors[] = {
1da177e4
LT
1502 TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE,
1503 TAC_PINK, TAC_TURQ, TAC_WHITE, 0, TAC_DEFAULT
1504 };
1505 int i, attr;
1506
1507 for (i = 0; i <= tp->esc_npar; i++) {
1508 attr = tp->esc_par[i];
1509 switch (attr) {
1510 case 0: /* Reset */
c2e9375e 1511 tty3270_reset_attributes(&tp->attributes);
1da177e4
LT
1512 break;
1513 /* Highlight. */
1514 case 4: /* Start underlining. */
c2e9375e 1515 tp->attributes.highlight = TAX_UNDER;
1da177e4
LT
1516 break;
1517 case 5: /* Start blink. */
c2e9375e 1518 tp->attributes.highlight = TAX_BLINK;
1da177e4
LT
1519 break;
1520 case 7: /* Start reverse. */
c2e9375e 1521 tp->attributes.highlight = TAX_REVER;
1da177e4
LT
1522 break;
1523 case 24: /* End underlining */
c2e9375e
SS
1524 if (tp->attributes.highlight == TAX_UNDER)
1525 tp->attributes.highlight = TAX_RESET;
1da177e4
LT
1526 break;
1527 case 25: /* End blink. */
c2e9375e
SS
1528 if (tp->attributes.highlight == TAX_BLINK)
1529 tp->attributes.highlight = TAX_RESET;
1da177e4
LT
1530 break;
1531 case 27: /* End reverse. */
c2e9375e
SS
1532 if (tp->attributes.highlight == TAX_REVER)
1533 tp->attributes.highlight = TAX_RESET;
1da177e4
LT
1534 break;
1535 /* Foreground color. */
1536 case 30: /* Black */
1537 case 31: /* Red */
1538 case 32: /* Green */
1539 case 33: /* Yellow */
1540 case 34: /* Blue */
1541 case 35: /* Magenta */
1542 case 36: /* Cyan */
1543 case 37: /* White */
1544 case 39: /* Black */
4043ea22
SS
1545 tp->attributes.f_color = colors[attr - 30];
1546 break;
1547 /* Background color. */
1548 case 40: /* Black */
1549 case 41: /* Red */
1550 case 42: /* Green */
1551 case 43: /* Yellow */
1552 case 44: /* Blue */
1553 case 45: /* Magenta */
1554 case 46: /* Cyan */
1555 case 47: /* White */
1556 case 49: /* Black */
1557 tp->attributes.b_color = colors[attr - 40];
1da177e4
LT
1558 break;
1559 }
1560 }
1561}
1562
e6d98bb8 1563static inline int tty3270_getpar(struct tty3270 *tp, int ix)
1da177e4
LT
1564{
1565 return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1;
1566}
1567
e6d98bb8 1568static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy)
1da177e4 1569{
4043ea22
SS
1570 struct tty3270_line *line;
1571 struct tty3270_cell *cell;
364c8558
HC
1572 int max_cx = max(0, cx);
1573 int max_cy = max(0, cy);
1574
1575 tp->cx = min_t(int, tp->view.cols - 1, max_cx);
4043ea22
SS
1576 line = tp->screen + tp->cy;
1577 while (line->len < tp->cx) {
1578 cell = line->cells + line->len;
1579 cell->character = ' ';
1580 cell->attributes = tp->attributes;
1581 line->len++;
1582 }
1583
9eb99b94 1584 cy = min_t(int, tty3270_tty_rows(tp) - 1, max_cy);
1da177e4
LT
1585 if (cy != tp->cy) {
1586 tty3270_convert_line(tp, tp->cy);
1587 tp->cy = cy;
1588 }
1589}
1590
1591/*
1592 * Process escape sequences. Known sequences:
1593 * Esc 7 Save Cursor Position
1594 * Esc 8 Restore Cursor Position
1595 * Esc [ Pn ; Pn ; .. m Set attributes
1596 * Esc [ Pn ; Pn H Cursor Position
1597 * Esc [ Pn ; Pn f Cursor Position
1598 * Esc [ Pn A Cursor Up
1599 * Esc [ Pn B Cursor Down
1600 * Esc [ Pn C Cursor Forward
1601 * Esc [ Pn D Cursor Backward
1602 * Esc [ Pn G Cursor Horizontal Absolute
1603 * Esc [ Pn X Erase Characters
1604 * Esc [ Ps J Erase in Display
1605 * Esc [ Ps K Erase in Line
1606 * // FIXME: add all the new ones.
1607 *
1608 * Pn is a numeric parameter, a string of zero or more decimal digits.
1609 * Ps is a selective parameter.
1610 */
e6d98bb8 1611static void tty3270_escape_sequence(struct tty3270 *tp, char ch)
1da177e4 1612{
e4b57b93 1613 enum { ESnormal, ESesc, ESsquare, ESparen, ESgetpars };
1da177e4
LT
1614
1615 if (tp->esc_state == ESnormal) {
1616 if (ch == 0x1b)
1617 /* Starting new escape sequence. */
1618 tp->esc_state = ESesc;
1619 return;
1620 }
1621 if (tp->esc_state == ESesc) {
1622 tp->esc_state = ESnormal;
1623 switch (ch) {
1624 case '[':
1625 tp->esc_state = ESsquare;
1626 break;
e4b57b93
SS
1627 case '(':
1628 tp->esc_state = ESparen;
1629 break;
1da177e4
LT
1630 case 'E':
1631 tty3270_cr(tp);
1632 tty3270_lf(tp);
1633 break;
1634 case 'M':
1635 tty3270_ri(tp);
1636 break;
1637 case 'D':
1638 tty3270_lf(tp);
1639 break;
1640 case 'Z': /* Respond ID. */
ba186e7d 1641 kbd_puts_queue(&tp->port, "\033[?6c");
1da177e4
LT
1642 break;
1643 case '7': /* Save cursor position. */
1644 tp->saved_cx = tp->cx;
1645 tp->saved_cy = tp->cy;
c2e9375e 1646 tp->saved_attributes = tp->attributes;
1da177e4
LT
1647 break;
1648 case '8': /* Restore cursor position. */
1649 tty3270_convert_line(tp, tp->cy);
1650 tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
c2e9375e 1651 tp->attributes = tp->saved_attributes;
1da177e4
LT
1652 break;
1653 case 'c': /* Reset terminal. */
1654 tp->cx = tp->saved_cx = 0;
1655 tp->cy = tp->saved_cy = 0;
c2e9375e
SS
1656 tty3270_reset_attributes(&tp->attributes);
1657 tty3270_reset_attributes(&tp->saved_attributes);
1da177e4
LT
1658 tty3270_erase_display(tp, 2);
1659 break;
1660 }
1661 return;
1662 }
e4b57b93
SS
1663
1664 switch (tp->esc_state) {
1665 case ESparen:
1666 tp->esc_state = ESnormal;
1667 switch (ch) {
1668 case 'B':
1669 tp->attributes.alternate_charset = 0;
1670 break;
1671 case '0':
1672 tp->attributes.alternate_charset = 1;
1673 break;
1674 }
1675 return;
1676 case ESsquare:
1da177e4
LT
1677 tp->esc_state = ESgetpars;
1678 memset(tp->esc_par, 0, sizeof(tp->esc_par));
1679 tp->esc_npar = 0;
1680 tp->esc_ques = (ch == '?');
1681 if (tp->esc_ques)
1682 return;
e4b57b93
SS
1683 fallthrough;
1684 case ESgetpars:
1da177e4
LT
1685 if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) {
1686 tp->esc_npar++;
1687 return;
1688 }
1689 if (ch >= '0' && ch <= '9') {
1690 tp->esc_par[tp->esc_npar] *= 10;
1691 tp->esc_par[tp->esc_npar] += ch - '0';
1692 return;
1693 }
e4b57b93
SS
1694 break;
1695 default:
1696 break;
1da177e4
LT
1697 }
1698 tp->esc_state = ESnormal;
1699 if (ch == 'n' && !tp->esc_ques) {
1700 if (tp->esc_par[0] == 5) /* Status report. */
ba186e7d 1701 kbd_puts_queue(&tp->port, "\033[0n");
1da177e4
LT
1702 else if (tp->esc_par[0] == 6) { /* Cursor report. */
1703 char buf[40];
1704 sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1);
ba186e7d 1705 kbd_puts_queue(&tp->port, buf);
1da177e4
LT
1706 }
1707 return;
1708 }
1709 if (tp->esc_ques)
1710 return;
1711 switch (ch) {
1712 case 'm':
1713 tty3270_set_attributes(tp);
1714 break;
1715 case 'H': /* Set cursor position. */
1716 case 'f':
1717 tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1,
1718 tty3270_getpar(tp, 0) - 1);
1719 break;
1720 case 'd': /* Set y position. */
1721 tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1);
1722 break;
1723 case 'A': /* Cursor up. */
1724 case 'F':
1725 tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0));
1726 break;
1727 case 'B': /* Cursor down. */
1728 case 'e':
1729 case 'E':
1730 tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0));
1731 break;
1732 case 'C': /* Cursor forward. */
1733 case 'a':
1734 tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy);
1735 break;
1736 case 'D': /* Cursor backward. */
1737 tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy);
1738 break;
1739 case 'G': /* Set x position. */
1740 case '`':
1741 tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy);
1742 break;
1743 case 'X': /* Erase Characters. */
1744 tty3270_erase_characters(tp, tty3270_getpar(tp, 0));
1745 break;
1746 case 'J': /* Erase display. */
1747 tty3270_erase_display(tp, tp->esc_par[0]);
1748 break;
1749 case 'K': /* Erase line. */
1750 tty3270_erase_line(tp, tp->esc_par[0]);
1751 break;
1752 case 'P': /* Delete characters. */
1753 tty3270_delete_characters(tp, tty3270_getpar(tp, 0));
1754 break;
1755 case '@': /* Insert characters. */
1756 tty3270_insert_characters(tp, tty3270_getpar(tp, 0));
1757 break;
1758 case 's': /* Save cursor position. */
1759 tp->saved_cx = tp->cx;
1760 tp->saved_cy = tp->cy;
c2e9375e 1761 tp->saved_attributes = tp->attributes;
1da177e4
LT
1762 break;
1763 case 'u': /* Restore cursor position. */
1764 tty3270_convert_line(tp, tp->cy);
1765 tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
c2e9375e 1766 tp->attributes = tp->saved_attributes;
1da177e4
LT
1767 break;
1768 }
1769}
1770
1771/*
1772 * String write routine for 3270 ttys
1773 */
e6d98bb8
SS
1774static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty,
1775 const unsigned char *buf, int count)
1da177e4
LT
1776{
1777 int i_msg, i;
1778
c17fe081 1779 spin_lock_irq(&tp->view.lock);
6e94dbc7 1780 for (i_msg = 0; !tty->flow.stopped && i_msg < count; i_msg++) {
1da177e4
LT
1781 if (tp->esc_state != 0) {
1782 /* Continue escape sequence. */
1783 tty3270_escape_sequence(tp, buf[i_msg]);
1784 continue;
1785 }
1786
1787 switch (buf[i_msg]) {
970cf9a9
SS
1788 case 0x00:
1789 break;
1da177e4
LT
1790 case 0x07: /* '\a' -- Alarm */
1791 tp->wcc |= TW_PLUSALARM;
1792 break;
1793 case 0x08: /* Backspace. */
1794 if (tp->cx > 0) {
1795 tp->cx--;
1796 tty3270_put_character(tp, ' ');
1797 }
1798 break;
1799 case 0x09: /* '\t' -- Tabulate */
1800 for (i = tp->cx % 8; i < 8; i++) {
1801 if (tp->cx >= tp->view.cols) {
1802 tty3270_cr(tp);
1803 tty3270_lf(tp);
1804 break;
1805 }
1806 tty3270_put_character(tp, ' ');
1807 tp->cx++;
1808 }
1809 break;
1810 case 0x0a: /* '\n' -- New Line */
1811 tty3270_cr(tp);
1812 tty3270_lf(tp);
1813 break;
1814 case 0x0c: /* '\f' -- Form Feed */
1815 tty3270_erase_display(tp, 2);
1816 tp->cx = tp->cy = 0;
1817 break;
1818 case 0x0d: /* '\r' -- Carriage Return */
1819 tp->cx = 0;
1820 break;
94dbb0a7
SS
1821 case 0x0e:
1822 tp->attributes.alternate_charset = 1;
1823 break;
1da177e4 1824 case 0x0f: /* SuSE "exit alternate mode" */
94dbb0a7 1825 tp->attributes.alternate_charset = 0;
1da177e4
LT
1826 break;
1827 case 0x1b: /* Start escape sequence. */
1828 tty3270_escape_sequence(tp, buf[i_msg]);
1829 break;
1830 default: /* Insert normal character. */
1831 if (tp->cx >= tp->view.cols) {
1832 tty3270_cr(tp);
1833 tty3270_lf(tp);
1834 }
1835 tty3270_put_character(tp, buf[i_msg]);
1836 tp->cx++;
1837 break;
1838 }
1839 }
1840 /* Convert current line to 3270 data fragment. */
1841 tty3270_convert_line(tp, tp->cy);
1842
1843 /* Setup timer to update display after 1/10 second */
1844 if (!timer_pending(&tp->timer))
1845 tty3270_set_timer(tp, HZ/10);
1846
c17fe081 1847 spin_unlock_irq(&tp->view.lock);
1da177e4
LT
1848}
1849
1850/*
1851 * String write routine for 3270 ttys
1852 */
e6d98bb8
SS
1853static int tty3270_write(struct tty_struct *tty,
1854 const unsigned char *buf, int count)
1da177e4
LT
1855{
1856 struct tty3270 *tp;
1857
1858 tp = tty->driver_data;
1859 if (!tp)
1860 return 0;
1861 if (tp->char_count > 0) {
20acdfa8 1862 tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);
1da177e4
LT
1863 tp->char_count = 0;
1864 }
20acdfa8 1865 tty3270_do_write(tp, tty, buf, count);
1da177e4
LT
1866 return count;
1867}
1868
1869/*
1870 * Put single characters to the ttys character buffer
1871 */
74c76c84 1872static int tty3270_put_char(struct tty_struct *tty, unsigned char ch)
1da177e4
LT
1873{
1874 struct tty3270 *tp;
1875
1876 tp = tty->driver_data;
74c76c84
HC
1877 if (!tp || tp->char_count >= TTY3270_CHAR_BUF_SIZE)
1878 return 0;
1879 tp->char_buf[tp->char_count++] = ch;
1880 return 1;
1da177e4
LT
1881}
1882
1883/*
1884 * Flush all characters from the ttys characeter buffer put there
1885 * by tty3270_put_char.
1886 */
e6d98bb8 1887static void tty3270_flush_chars(struct tty_struct *tty)
1da177e4
LT
1888{
1889 struct tty3270 *tp;
1890
1891 tp = tty->driver_data;
1892 if (!tp)
1893 return;
1894 if (tp->char_count > 0) {
20acdfa8 1895 tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);
1da177e4
LT
1896 tp->char_count = 0;
1897 }
1898}
1899
1da177e4
LT
1900/*
1901 * Check for visible/invisible input switches
1902 */
e6d98bb8 1903static void tty3270_set_termios(struct tty_struct *tty, const struct ktermios *old)
1da177e4
LT
1904{
1905 struct tty3270 *tp;
1906 int new;
1907
1908 tp = tty->driver_data;
1909 if (!tp)
1910 return;
c17fe081 1911 spin_lock_irq(&tp->view.lock);
1da177e4
LT
1912 if (L_ICANON(tty)) {
1913 new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN;
1914 if (new != tp->inattr) {
1915 tp->inattr = new;
d2c993d8 1916 tty3270_update_prompt(tp, NULL, 0);
1da177e4
LT
1917 tty3270_set_timer(tp, 1);
1918 }
1919 }
c17fe081 1920 spin_unlock_irq(&tp->view.lock);
1da177e4
LT
1921}
1922
1923/*
1924 * Disable reading from a 3270 tty
1925 */
e6d98bb8 1926static void tty3270_throttle(struct tty_struct *tty)
1da177e4
LT
1927{
1928 struct tty3270 *tp;
1929
1930 tp = tty->driver_data;
1931 if (!tp)
1932 return;
1933 tp->throttle = 1;
1934}
1935
1936/*
1937 * Enable reading from a 3270 tty
1938 */
e6d98bb8 1939static void tty3270_unthrottle(struct tty_struct *tty)
1da177e4
LT
1940{
1941 struct tty3270 *tp;
1942
1943 tp = tty->driver_data;
1944 if (!tp)
1945 return;
1946 tp->throttle = 0;
1947 if (tp->attn)
1948 tty3270_issue_read(tp, 1);
1949}
1950
1951/*
1952 * Hang up the tty device.
1953 */
e6d98bb8 1954static void tty3270_hangup(struct tty_struct *tty)
1da177e4 1955{
0c756914
MS
1956 struct tty3270 *tp;
1957
1958 tp = tty->driver_data;
1959 if (!tp)
1960 return;
c17fe081 1961 spin_lock_irq(&tp->view.lock);
0c756914
MS
1962 tp->cx = tp->saved_cx = 0;
1963 tp->cy = tp->saved_cy = 0;
c2e9375e
SS
1964 tty3270_reset_attributes(&tp->attributes);
1965 tty3270_reset_attributes(&tp->saved_attributes);
0c756914 1966 tty3270_blank_screen(tp);
9eb99b94 1967 while (tp->nr_lines < tty3270_tty_rows(tp))
0c756914
MS
1968 tty3270_blank_line(tp);
1969 tp->update_flags = TTY_UPDATE_ALL;
c17fe081 1970 spin_unlock_irq(&tp->view.lock);
0c756914 1971 tty3270_set_timer(tp, 1);
1da177e4
LT
1972}
1973
e6d98bb8 1974static void tty3270_wait_until_sent(struct tty_struct *tty, int timeout)
1da177e4
LT
1975{
1976}
1977
65c56e07
HC
1978static int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd,
1979 unsigned long arg)
1da177e4
LT
1980{
1981 struct tty3270 *tp;
1982
1983 tp = tty->driver_data;
1984 if (!tp)
1985 return -ENODEV;
18900ca6 1986 if (tty_io_error(tty))
1da177e4 1987 return -EIO;
65c56e07 1988 return kbd_ioctl(tp->kbd, cmd, arg);
1da177e4
LT
1989}
1990
9d4bfd41 1991#ifdef CONFIG_COMPAT
65c56e07
HC
1992static long tty3270_compat_ioctl(struct tty_struct *tty,
1993 unsigned int cmd, unsigned long arg)
9d4bfd41
AB
1994{
1995 struct tty3270 *tp;
1996
1997 tp = tty->driver_data;
1998 if (!tp)
1999 return -ENODEV;
18900ca6 2000 if (tty_io_error(tty))
9d4bfd41 2001 return -EIO;
65c56e07 2002 return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg));
9d4bfd41
AB
2003}
2004#endif
2005
b68e31d0 2006static const struct tty_operations tty3270_ops = {
20cda6f2
JS
2007 .install = tty3270_install,
2008 .cleanup = tty3270_cleanup,
736c9fd2 2009 .open = tty3270_open,
1da177e4
LT
2010 .close = tty3270_close,
2011 .write = tty3270_write,
2012 .put_char = tty3270_put_char,
2013 .flush_chars = tty3270_flush_chars,
2014 .write_room = tty3270_write_room,
1da177e4
LT
2015 .throttle = tty3270_throttle,
2016 .unthrottle = tty3270_unthrottle,
2017 .hangup = tty3270_hangup,
2018 .wait_until_sent = tty3270_wait_until_sent,
2019 .ioctl = tty3270_ioctl,
9d4bfd41
AB
2020#ifdef CONFIG_COMPAT
2021 .compat_ioctl = tty3270_compat_ioctl,
2022#endif
1da177e4
LT
2023 .set_termios = tty3270_set_termios
2024};
2025
63df41d6 2026static void tty3270_create_cb(int minor)
c95571e6 2027{
1fcbba3d 2028 tty_register_device(tty3270_driver, minor - RAW3270_FIRSTMINOR, NULL);
c95571e6
MS
2029}
2030
63df41d6 2031static void tty3270_destroy_cb(int minor)
c95571e6 2032{
1fcbba3d 2033 tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR);
c95571e6
MS
2034}
2035
63df41d6 2036static struct raw3270_notifier tty3270_notifier =
c95571e6
MS
2037{
2038 .create = tty3270_create_cb,
2039 .destroy = tty3270_destroy_cb,
2040};
2041
1da177e4
LT
2042/*
2043 * 3270 tty registration code called from tty_init().
2044 * Most kernel services (incl. kmalloc) are available at this poimt.
2045 */
2b67fc46 2046static int __init tty3270_init(void)
1da177e4
LT
2047{
2048 struct tty_driver *driver;
2049 int ret;
2050
c95571e6
MS
2051 driver = tty_alloc_driver(RAW3270_MAXDEVS,
2052 TTY_DRIVER_REAL_RAW |
2053 TTY_DRIVER_DYNAMIC_DEV |
2054 TTY_DRIVER_RESET_TERMIOS);
2055 if (IS_ERR(driver))
2056 return PTR_ERR(driver);
1da177e4
LT
2057
2058 /*
2059 * Initialize the tty_driver structure
2060 * Entries in tty3270_driver that are NOT initialized:
2061 * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
2062 */
c95571e6
MS
2063 driver->driver_name = "tty3270";
2064 driver->name = "3270/tty";
1da177e4 2065 driver->major = IBM_TTY3270_MAJOR;
1fcbba3d
MS
2066 driver->minor_start = RAW3270_FIRSTMINOR;
2067 driver->name_base = RAW3270_FIRSTMINOR;
1da177e4
LT
2068 driver->type = TTY_DRIVER_TYPE_SYSTEM;
2069 driver->subtype = SYSTEM_TYPE_TTY;
2070 driver->init_termios = tty_std_termios;
1da177e4
LT
2071 tty_set_operations(driver, &tty3270_ops);
2072 ret = tty_register_driver(driver);
2073 if (ret) {
9f90a4dd 2074 tty_driver_kref_put(driver);
1da177e4
LT
2075 return ret;
2076 }
2077 tty3270_driver = driver;
c95571e6 2078 raw3270_register_notifier(&tty3270_notifier);
1da177e4
LT
2079 return 0;
2080}
2081
e6d98bb8 2082static void __exit tty3270_exit(void)
1da177e4
LT
2083{
2084 struct tty_driver *driver;
2085
c95571e6 2086 raw3270_unregister_notifier(&tty3270_notifier);
1da177e4 2087 driver = tty3270_driver;
d2c993d8 2088 tty3270_driver = NULL;
1da177e4 2089 tty_unregister_driver(driver);
9f90a4dd 2090 tty_driver_kref_put(driver);
1da177e4
LT
2091 tty3270_del_views();
2092}
2093
c17fe081
SS
2094#if IS_ENABLED(CONFIG_TN3270_CONSOLE)
2095static void
2096con3270_write(struct console *co, const char *str, unsigned int count)
2097{
2098 struct tty3270 *tp = co->data;
2099 unsigned long flags;
2100 char c;
2101
2102 spin_lock_irqsave(&tp->view.lock, flags);
2103 while (count--) {
2104 c = *str++;
2105 if (c == 0x0a) {
2106 tty3270_cr(tp);
2107 tty3270_lf(tp);
2108 } else {
2109 if (tp->cx >= tp->view.cols) {
2110 tty3270_cr(tp);
2111 tty3270_lf(tp);
2112 }
2113 tty3270_put_character(tp, c);
2114 tp->cx++;
2115 }
2116 }
2117 spin_unlock_irqrestore(&tp->view.lock, flags);
2118}
2119
2120static struct tty_driver *
2121con3270_device(struct console *c, int *index)
2122{
2123 *index = c->index;
2124 return tty3270_driver;
2125}
2126
2127static void
2128con3270_wait_write(struct tty3270 *tp)
2129{
2130 while (!tp->write) {
2131 raw3270_wait_cons_dev(tp->view.dev);
2132 barrier();
2133 }
2134}
2135
2136/*
2137 * The below function is called as a panic/reboot notifier before the
2138 * system enters a disabled, endless loop.
2139 *
2140 * Notice we must use the spin_trylock() alternative, to prevent lockups
2141 * in atomic context (panic routine runs with secondary CPUs, local IRQs
2142 * and preemption disabled).
2143 */
2144static int con3270_notify(struct notifier_block *self,
2145 unsigned long event, void *data)
2146{
2147 struct tty3270 *tp;
2148 unsigned long flags;
2149
2150 tp = condev;
2151 if (!tp->view.dev)
2152 return NOTIFY_DONE;
2153 if (!raw3270_view_lock_unavailable(&tp->view))
2154 raw3270_activate_view(&tp->view);
2155 if (!spin_trylock_irqsave(&tp->view.lock, flags))
2156 return NOTIFY_DONE;
2157 con3270_wait_write(tp);
2158 tp->nr_up = 0;
2159 while (tp->update_flags != 0) {
2160 spin_unlock_irqrestore(&tp->view.lock, flags);
2161 tty3270_update(&tp->timer);
2162 spin_lock_irqsave(&tp->view.lock, flags);
2163 con3270_wait_write(tp);
2164 }
2165 spin_unlock_irqrestore(&tp->view.lock, flags);
2166 return NOTIFY_DONE;
2167}
2168
2169static struct notifier_block on_panic_nb = {
2170 .notifier_call = con3270_notify,
2171 .priority = INT_MIN + 1, /* run the callback late */
2172};
2173
2174static struct notifier_block on_reboot_nb = {
2175 .notifier_call = con3270_notify,
2176 .priority = INT_MIN + 1, /* run the callback late */
2177};
2178
2179static struct console con3270 = {
2180 .name = "tty3270",
2181 .write = con3270_write,
2182 .device = con3270_device,
2183 .flags = CON_PRINTBUFFER,
2184};
2185
2186static int __init
2187con3270_init(void)
2188{
2189 struct raw3270_view *view;
2190 struct raw3270 *rp;
2191 struct tty3270 *tp;
2192 int rc;
2193
2194 /* Check if 3270 is to be the console */
2195 if (!CONSOLE_IS_3270)
2196 return -ENODEV;
2197
2198 /* Set the console mode for VM */
2199 if (MACHINE_IS_VM) {
2200 cpcmd("TERM CONMODE 3270", NULL, 0, NULL);
2201 cpcmd("TERM AUTOCR OFF", NULL, 0, NULL);
2202 }
2203
2204 rp = raw3270_setup_console();
2205 if (IS_ERR(rp))
2206 return PTR_ERR(rp);
2207
2208 /* Check if the tty3270 is already there. */
2209 view = raw3270_find_view(&tty3270_fn, RAW3270_FIRSTMINOR);
2210 if (IS_ERR(view)) {
2211 rc = tty3270_create_view(0, &tp);
2212 if (rc)
2213 return rc;
2214 } else {
2215 tp = container_of(view, struct tty3270, view);
2216 tp->inattr = TF_INPUT;
2217 }
2218 con3270.data = tp;
2219 condev = tp;
2220 atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
2221 register_reboot_notifier(&on_reboot_nb);
2222 register_console(&con3270);
2223 return 0;
2224}
2225console_initcall(con3270_init);
2226#endif
2227
1da177e4
LT
2228MODULE_LICENSE("GPL");
2229MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR);
2230
2231module_init(tty3270_init);
2232module_exit(tty3270_exit);