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