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