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