Commit | Line | Data |
---|---|---|
6f05e69e | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 | 2 | /* |
1da177e4 LT |
3 | * IBM/3270 Driver - tty functions. |
4 | * | |
5 | * Author(s): | |
6 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) | |
7 | * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> | |
a53c8fab | 8 | * -- Copyright IBM Corp. 2003 |
1da177e4 LT |
9 | */ |
10 | ||
1da177e4 LT |
11 | #include <linux/module.h> |
12 | #include <linux/types.h> | |
13 | #include <linux/kdev_t.h> | |
14 | #include <linux/tty.h> | |
15 | #include <linux/vt_kern.h> | |
16 | #include <linux/init.h> | |
17 | #include <linux/console.h> | |
18 | #include <linux/interrupt.h> | |
4d334fd1 | 19 | #include <linux/workqueue.h> |
c17fe081 SS |
20 | #include <linux/panic_notifier.h> |
21 | #include <linux/reboot.h> | |
1da177e4 | 22 | #include <linux/slab.h> |
57c8a661 | 23 | #include <linux/memblock.h> |
9d4bfd41 | 24 | #include <linux/compat.h> |
1da177e4 LT |
25 | |
26 | #include <asm/ccwdev.h> | |
27 | #include <asm/cio.h> | |
28 | #include <asm/ebcdic.h> | |
c17fe081 | 29 | #include <asm/cpcmd.h> |
7c0f6ba6 | 30 | #include <linux/uaccess.h> |
1da177e4 | 31 | |
1da177e4 LT |
32 | #include "raw3270.h" |
33 | #include "keyboard.h" | |
34 | ||
35 | #define TTY3270_CHAR_BUF_SIZE 256 | |
9c138af9 | 36 | #define TTY3270_OUTPUT_BUFFER_SIZE 4096 |
b2057c87 | 37 | #define TTY3270_SCREEN_PAGES 8 /* has to be power-of-two */ |
76485078 | 38 | #define TTY3270_RECALL_SIZE 16 /* has to be power-of-two */ |
525c919d | 39 | #define TTY3270_STATUS_AREA_SIZE 40 |
b2057c87 | 40 | |
c17fe081 | 41 | static struct tty_driver *tty3270_driver; |
1da177e4 | 42 | static int tty3270_max_index; |
2b67fc46 | 43 | static struct raw3270_fn tty3270_fn; |
1da177e4 | 44 | |
0573fff2 SS |
45 | #define TTY3270_HIGHLIGHT_BLINK 1 |
46 | #define TTY3270_HIGHLIGHT_REVERSE 2 | |
47 | #define TTY3270_HIGHLIGHT_UNDERSCORE 4 | |
48 | ||
c2e9375e | 49 | struct tty3270_attribute { |
94dbb0a7 | 50 | unsigned char alternate_charset:1; /* Graphics charset */ |
0573fff2 | 51 | unsigned char highlight:3; /* Blink/reverse/underscore */ |
18fc2e93 SS |
52 | unsigned char f_color:4; /* Foreground color */ |
53 | unsigned char b_color:4; /* Background color */ | |
c2e9375e SS |
54 | }; |
55 | ||
1da177e4 | 56 | struct tty3270_cell { |
e17934c1 | 57 | u8 character; |
c2e9375e | 58 | struct tty3270_attribute attributes; |
1da177e4 LT |
59 | }; |
60 | ||
61 | struct tty3270_line { | |
62 | struct tty3270_cell *cells; | |
63 | int len; | |
b2057c87 | 64 | int dirty; |
1da177e4 LT |
65 | }; |
66 | ||
cbb36313 SS |
67 | static const unsigned char sfq_read_partition[] = { |
68 | 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 | |
69 | }; | |
70 | ||
1da177e4 LT |
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; | |
ba186e7d | 82 | struct tty_port port; |
1da177e4 LT |
83 | |
84 | /* Output stuff. */ | |
1da177e4 | 85 | unsigned char wcc; /* Write control character. */ |
1da177e4 LT |
86 | int nr_up; /* # lines up in history. */ |
87 | unsigned long update_flags; /* Update indication bits. */ | |
1da177e4 LT |
88 | struct raw3270_request *write; /* Single write request. */ |
89 | struct timer_list timer; /* Output delay timer. */ | |
ec1b0a33 | 90 | char *converted_line; /* RAW 3270 data stream */ |
9c138af9 SS |
91 | unsigned int line_view_start; /* Start of visible area */ |
92 | unsigned int line_write_start; /* current write position */ | |
ba5c2e2a | 93 | unsigned int oops_line; /* line counter used when print oops */ |
1da177e4 LT |
94 | |
95 | /* Current tty screen. */ | |
96 | unsigned int cx, cy; /* Current output position. */ | |
c2e9375e SS |
97 | struct tty3270_attribute attributes; |
98 | struct tty3270_attribute saved_attributes; | |
b2057c87 | 99 | int allocated_lines; |
1da177e4 LT |
100 | struct tty3270_line *screen; |
101 | ||
102 | /* Input stuff. */ | |
164eb669 SS |
103 | char *prompt; /* Output string for input area. */ |
104 | char *input; /* Input string for read request. */ | |
1da177e4 LT |
105 | struct raw3270_request *read; /* Single read request. */ |
106 | struct raw3270_request *kreset; /* Single keyboard reset request. */ | |
cbb36313 | 107 | struct raw3270_request *readpartreq; |
1da177e4 LT |
108 | unsigned char inattr; /* Visible/invisible input. */ |
109 | int throttle, attn; /* tty throttle/unthrottle. */ | |
110 | struct tasklet_struct readlet; /* Tasklet to issue read request. */ | |
0c756914 | 111 | struct tasklet_struct hanglet; /* Tasklet to hang up the tty. */ |
1da177e4 LT |
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; | |
1da177e4 LT |
118 | |
119 | /* Command recalling. */ | |
76485078 SS |
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 */ | |
1da177e4 LT |
123 | |
124 | /* Character array for put_char/flush_chars. */ | |
125 | unsigned int char_count; | |
e17934c1 | 126 | u8 char_buf[TTY3270_CHAR_BUF_SIZE]; |
1da177e4 LT |
127 | }; |
128 | ||
129 | /* tty3270->update_flags. See tty3270_update for details. */ | |
da4e272e SS |
130 | #define TTY_UPDATE_INPUT 0x1 /* Update input line. */ |
131 | #define TTY_UPDATE_STATUS 0x2 /* Update status line. */ | |
ba5c2e2a SS |
132 | #define TTY_UPDATE_LINES 0x4 /* Update visible screen lines */ |
133 | #define TTY_UPDATE_ALL 0x7 /* Recreate screen. */ | |
1da177e4 | 134 | |
9eb99b94 | 135 | #define TTY3270_INPUT_AREA_ROWS 2 |
f77f936a | 136 | |
1da177e4 LT |
137 | /* |
138 | * Setup timeout for a device. On timeout trigger an update. | |
139 | */ | |
2b67fc46 | 140 | static void tty3270_set_timer(struct tty3270 *tp, int expires) |
1da177e4 | 141 | { |
03439e7d | 142 | mod_timer(&tp->timer, jiffies + expires); |
1da177e4 LT |
143 | } |
144 | ||
9eb99b94 SS |
145 | static int tty3270_tty_rows(struct tty3270 *tp) |
146 | { | |
147 | return tp->view.rows - TTY3270_INPUT_AREA_ROWS; | |
148 | } | |
149 | ||
ae657244 SS |
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, 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 | ||
ec1b0a33 SS |
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 | ||
9c138af9 SS |
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, tp->line_write_start, 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, tp->line_view_start, num - tp->nr_up); | |
199 | } | |
200 | ||
164eb669 | 201 | static int tty3270_input_size(int cols) |
1da177e4 | 202 | { |
164eb669 SS |
203 | return cols * 2 - 11; |
204 | } | |
1da177e4 | 205 | |
76485078 | 206 | static void tty3270_update_prompt(struct tty3270 *tp, char *input) |
164eb669 | 207 | { |
76485078 | 208 | strcpy(tp->prompt, input); |
1da177e4 | 209 | tp->update_flags |= TTY_UPDATE_INPUT; |
164eb669 | 210 | tty3270_set_timer(tp, 1); |
1da177e4 LT |
211 | } |
212 | ||
164eb669 SS |
213 | /* |
214 | * The input line are the two last lines of the screen. | |
215 | */ | |
216 | static int tty3270_add_prompt(struct tty3270 *tp) | |
1da177e4 | 217 | { |
164eb669 SS |
218 | int count = 0; |
219 | char *cp; | |
220 | ||
221 | cp = tp->converted_line; | |
222 | cp = tty3270_add_ba(tp, cp, TO_SBA, 0, -2); | |
223 | *cp++ = tp->view.ascebc['>']; | |
224 | ||
225 | if (*tp->prompt) { | |
226 | cp = tty3270_add_sf(tp, cp, TF_INMDT); | |
525c919d SS |
227 | count = min_t(int, strlen(tp->prompt), |
228 | tp->view.cols * 2 - TTY3270_STATUS_AREA_SIZE - 2); | |
164eb669 SS |
229 | memcpy(cp, tp->prompt, count); |
230 | cp += count; | |
231 | } else { | |
232 | cp = tty3270_add_sf(tp, cp, tp->inattr); | |
233 | } | |
234 | *cp++ = TO_IC; | |
235 | /* Clear to end of input line. */ | |
236 | if (count < tp->view.cols * 2 - 11) | |
525c919d | 237 | cp = tty3270_add_ra(tp, cp, -TTY3270_STATUS_AREA_SIZE, -1, 0); |
164eb669 | 238 | return cp - tp->converted_line; |
1da177e4 LT |
239 | } |
240 | ||
525c919d SS |
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 | ||
ae657244 SS |
248 | /* |
249 | * The status line is the last line of the screen. It shows the string | |
525c919d | 250 | * "Running"/"History X" in the lower right corner of the screen. |
ae657244 | 251 | */ |
ec1b0a33 | 252 | static int tty3270_add_status(struct tty3270 *tp) |
1da177e4 | 253 | { |
ec1b0a33 SS |
254 | char *cp = tp->converted_line; |
255 | int len; | |
1da177e4 | 256 | |
525c919d | 257 | cp = tty3270_add_ba(tp, cp, TO_SBA, -TTY3270_STATUS_AREA_SIZE, -1); |
ec1b0a33 SS |
258 | cp = tty3270_add_sf(tp, cp, TF_LOG); |
259 | cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_GREEN); | |
525c919d SS |
260 | cp = tty3270_ebcdic_convert(tp, cp, " 7"); |
261 | cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER); | |
262 | cp = tty3270_ebcdic_convert(tp, cp, "PrevPg"); | |
263 | cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); | |
264 | cp = tty3270_ebcdic_convert(tp, cp, " 8"); | |
265 | cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER); | |
266 | cp = tty3270_ebcdic_convert(tp, cp, "NextPg"); | |
267 | cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); | |
268 | cp = tty3270_ebcdic_convert(tp, cp, " 12"); | |
269 | cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER); | |
270 | cp = tty3270_ebcdic_convert(tp, cp, "Recall"); | |
271 | cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); | |
272 | cp = tty3270_ebcdic_convert(tp, cp, " "); | |
273 | if (tp->nr_up) { | |
274 | len = sprintf(cp, "History %d", -tp->nr_up); | |
275 | codepage_convert(tp->view.ascebc, cp, len); | |
276 | cp += len; | |
277 | } else { | |
ba5c2e2a | 278 | cp = tty3270_ebcdic_convert(tp, cp, oops_in_progress ? "Crashed" : "Running"); |
525c919d | 279 | } |
ec1b0a33 SS |
280 | cp = tty3270_add_sf(tp, cp, TF_LOG); |
281 | cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_RESET); | |
282 | return cp - (char *)tp->converted_line; | |
1da177e4 LT |
283 | } |
284 | ||
e6d98bb8 | 285 | static void tty3270_blank_screen(struct tty3270 *tp) |
7e36eff1 | 286 | { |
9c138af9 | 287 | struct tty3270_line *line; |
7e36eff1 MS |
288 | int i; |
289 | ||
9c138af9 SS |
290 | for (i = 0; i < tty3270_tty_rows(tp); i++) { |
291 | line = tty3270_get_write_line(tp, i); | |
292 | line->len = 0; | |
293 | line->dirty = 1; | |
7e36eff1 | 294 | } |
9c138af9 | 295 | tp->nr_up = 0; |
7e36eff1 MS |
296 | } |
297 | ||
1da177e4 LT |
298 | /* |
299 | * Write request completion callback. | |
300 | */ | |
e6d98bb8 | 301 | static void tty3270_write_callback(struct raw3270_request *rq, void *data) |
1da177e4 | 302 | { |
881e18f9 | 303 | struct tty3270 *tp = container_of(rq->view, struct tty3270, view); |
1da177e4 | 304 | |
1da177e4 | 305 | if (rq->rc != 0) { |
25985edc | 306 | /* Write wasn't successful. Refresh all. */ |
1da177e4 LT |
307 | tp->update_flags = TTY_UPDATE_ALL; |
308 | tty3270_set_timer(tp, 1); | |
309 | } | |
310 | raw3270_request_reset(rq); | |
311 | xchg(&tp->write, rq); | |
312 | } | |
313 | ||
9c138af9 | 314 | static int tty3270_required_length(struct tty3270 *tp, struct tty3270_line *line) |
2b62ba58 SS |
315 | { |
316 | unsigned char f_color, b_color, highlight; | |
2b62ba58 SS |
317 | struct tty3270_cell *cell; |
318 | int i, flen = 3; /* Prefix (TO_SBA). */ | |
319 | ||
2b62ba58 | 320 | flen += line->len; |
0573fff2 | 321 | highlight = 0; |
2b62ba58 SS |
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 | } | |
0573fff2 | 341 | if (highlight) |
2b62ba58 SS |
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, | |
422a78ea | 354 | char *cp, struct tty3270_attribute *attr, int lineno) |
2b62ba58 | 355 | { |
0573fff2 | 356 | if (attr->highlight) |
ae657244 SS |
357 | cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); |
358 | if (attr->f_color != TAC_RESET) | |
359 | cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAX_RESET); | |
360 | if (attr->b_color != TAC_RESET) | |
361 | cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, TAX_RESET); | |
362 | if (line->len < tp->view.cols) | |
422a78ea | 363 | cp = tty3270_add_ra(tp, cp, 0, lineno + 1, 0); |
2b62ba58 SS |
364 | return cp; |
365 | } | |
366 | ||
6e49017c SS |
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, | |
422a78ea | 398 | struct tty3270_attribute *attr, char *cp, int lineno) |
2b62ba58 | 399 | { |
18fc2e93 SS |
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 | ||
0573fff2 SS |
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 | ||
2b62ba58 | 418 | struct tty3270_cell *cell; |
ae657244 | 419 | int c, i; |
2b62ba58 | 420 | |
422a78ea | 421 | cp = tty3270_add_ba(tp, cp, TO_SBA, 0, lineno); |
2b62ba58 SS |
422 | |
423 | for (i = 0, cell = line->cells; i < line->len; i++, cell++) { | |
424 | if (cell->attributes.highlight != attr->highlight) { | |
2b62ba58 | 425 | attr->highlight = cell->attributes.highlight; |
0573fff2 | 426 | cp = tty3270_add_sa(tp, cp, TAT_EXTHI, highlights[attr->highlight]); |
2b62ba58 SS |
427 | } |
428 | if (cell->attributes.f_color != attr->f_color) { | |
2b62ba58 | 429 | attr->f_color = cell->attributes.f_color; |
18fc2e93 | 430 | cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, colors[attr->f_color]); |
2b62ba58 SS |
431 | } |
432 | if (cell->attributes.b_color != attr->b_color) { | |
2b62ba58 | 433 | attr->b_color = cell->attributes.b_color; |
18fc2e93 | 434 | cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, colors[attr->b_color]); |
2b62ba58 | 435 | } |
ae657244 SS |
436 | c = cell->character; |
437 | if (cell->attributes.alternate_charset) | |
438 | cp = tty3270_add_ge(tp, cp, tty3270_graphics_translate(tp, c)); | |
439 | else | |
440 | *cp++ = tp->view.ascebc[c]; | |
2b62ba58 SS |
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 | ||
2b62ba58 SS |
452 | /* |
453 | * Convert a tty3270_line to a 3270 data fragment usable for output. | |
454 | */ | |
422a78ea | 455 | static unsigned int tty3270_convert_line(struct tty3270 *tp, struct tty3270_line *line, int lineno) |
2b62ba58 | 456 | { |
2b62ba58 | 457 | struct tty3270_attribute attr; |
9c138af9 | 458 | int flen; |
2b62ba58 SS |
459 | char *cp; |
460 | ||
461 | /* Determine how long the fragment will be. */ | |
9c138af9 SS |
462 | flen = tty3270_required_length(tp, line); |
463 | if (flen > PAGE_SIZE) | |
464 | return 0; | |
2b62ba58 SS |
465 | /* Write 3270 data fragment. */ |
466 | tty3270_reset_attributes(&attr); | |
422a78ea SS |
467 | cp = tty3270_add_attributes(tp, line, &attr, tp->converted_line, lineno); |
468 | cp = tty3270_add_reset_attributes(tp, line, cp, &attr, lineno); | |
9c138af9 | 469 | return cp - (char *)tp->converted_line; |
2b62ba58 SS |
470 | } |
471 | ||
ba5c2e2a SS |
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, i); | |
479 | if (!line->dirty) | |
480 | continue; | |
481 | len = tty3270_convert_line(tp, line, i); | |
482 | if (raw3270_request_add_data(rq, tp->converted_line, 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, i + tp->cy + 1); | |
501 | if (!line->dirty) | |
502 | continue; | |
503 | len = tty3270_convert_line(tp, line, tp->oops_line); | |
504 | if (raw3270_request_add_data(rq, tp->converted_line, 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, buf, 0, tty3270_tty_rows(tp), 0); | |
514 | if (raw3270_request_add_data(rq, buf, sizeof(buf))) | |
515 | return; | |
516 | } | |
517 | tp->update_flags &= ~TTY_UPDATE_LINES; | |
518 | } | |
519 | } | |
520 | ||
1da177e4 LT |
521 | /* |
522 | * Update 3270 display. | |
523 | */ | |
e6d98bb8 | 524 | static void tty3270_update(struct timer_list *t) |
1da177e4 | 525 | { |
c9602ee7 | 526 | struct tty3270 *tp = from_timer(tp, t, timer); |
1da177e4 | 527 | struct raw3270_request *wrq; |
da4e272e | 528 | u8 cmd = TC_WRITE; |
ba5c2e2a | 529 | int rc, len; |
1da177e4 LT |
530 | |
531 | wrq = xchg(&tp->write, 0); | |
532 | if (!wrq) { | |
533 | tty3270_set_timer(tp, 1); | |
534 | return; | |
535 | } | |
536 | ||
c17fe081 | 537 | spin_lock_irq(&tp->view.lock); |
da4e272e SS |
538 | if (tp->update_flags == TTY_UPDATE_ALL) |
539 | cmd = TC_EWRITEA; | |
540 | ||
541 | raw3270_request_set_cmd(wrq, cmd); | |
1da177e4 LT |
542 | raw3270_request_add_data(wrq, &tp->wcc, 1); |
543 | tp->wcc = TW_NONE; | |
544 | ||
545 | /* | |
546 | * Update status line. | |
547 | */ | |
ec1b0a33 SS |
548 | if (tp->update_flags & TTY_UPDATE_STATUS) { |
549 | len = tty3270_add_status(tp); | |
550 | if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0) | |
da4e272e | 551 | tp->update_flags &= ~TTY_UPDATE_STATUS; |
ec1b0a33 | 552 | } |
1da177e4 LT |
553 | |
554 | /* | |
555 | * Write input line. | |
556 | */ | |
164eb669 SS |
557 | if (tp->update_flags & TTY_UPDATE_INPUT) { |
558 | len = tty3270_add_prompt(tp); | |
559 | if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0) | |
da4e272e | 560 | tp->update_flags &= ~TTY_UPDATE_INPUT; |
164eb669 | 561 | } |
1da177e4 | 562 | |
ba5c2e2a SS |
563 | if (tp->update_flags & TTY_UPDATE_LINES) { |
564 | if (oops_in_progress) | |
565 | tty3270_update_lines_all(tp, wrq); | |
566 | else | |
567 | tty3270_update_lines_visible(tp, wrq); | |
1da177e4 | 568 | } |
9c138af9 | 569 | |
1da177e4 LT |
570 | wrq->callback = tty3270_write_callback; |
571 | rc = raw3270_start(&tp->view, wrq); | |
572 | if (rc == 0) { | |
1da177e4 LT |
573 | if (tp->update_flags) |
574 | tty3270_set_timer(tp, 1); | |
575 | } else { | |
576 | raw3270_request_reset(wrq); | |
577 | xchg(&tp->write, wrq); | |
578 | } | |
c17fe081 | 579 | spin_unlock_irq(&tp->view.lock); |
1da177e4 LT |
580 | } |
581 | ||
582 | /* | |
583 | * Command recalling. | |
584 | */ | |
e6d98bb8 | 585 | static void tty3270_rcl_add(struct tty3270 *tp, char *input, int len) |
1da177e4 | 586 | { |
76485078 | 587 | char *p; |
7ef21387 | 588 | |
1da177e4 LT |
589 | if (len <= 0) |
590 | return; | |
76485078 SS |
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; | |
1da177e4 LT |
596 | } |
597 | ||
e6d98bb8 | 598 | static void tty3270_rcl_backward(struct kbd_data *kbd) |
1da177e4 | 599 | { |
ba186e7d | 600 | struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); |
76485078 | 601 | int i = 0; |
1da177e4 | 602 | |
c17fe081 | 603 | spin_lock_irq(&tp->view.lock); |
1da177e4 | 604 | if (tp->inattr == TF_INPUT) { |
76485078 SS |
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, tp->rcl_lines[tp->rcl_read_index]); | |
1da177e4 | 611 | } |
c17fe081 | 612 | spin_unlock_irq(&tp->view.lock); |
1da177e4 LT |
613 | } |
614 | ||
615 | /* | |
616 | * Deactivate tty view. | |
617 | */ | |
e6d98bb8 | 618 | static void tty3270_exit_tty(struct kbd_data *kbd) |
1da177e4 | 619 | { |
ba186e7d | 620 | struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); |
1da177e4 | 621 | |
1da177e4 LT |
622 | raw3270_deactivate_view(&tp->view); |
623 | } | |
624 | ||
9c138af9 SS |
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, i)->dirty = 1; | |
631 | tp->update_flags = TTY_UPDATE_ALL; | |
632 | tty3270_set_timer(tp, 1); | |
633 | } | |
7ef21387 | 634 | |
1da177e4 LT |
635 | /* |
636 | * Scroll forward in history. | |
637 | */ | |
e6d98bb8 | 638 | static void tty3270_scroll_forward(struct kbd_data *kbd) |
1da177e4 | 639 | { |
ba186e7d | 640 | struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); |
1da177e4 | 641 | |
c17fe081 | 642 | spin_lock_irq(&tp->view.lock); |
9c138af9 SS |
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); | |
c17fe081 | 649 | spin_unlock_irq(&tp->view.lock); |
1da177e4 LT |
650 | } |
651 | ||
652 | /* | |
653 | * Scroll backward in history. | |
654 | */ | |
e6d98bb8 | 655 | static void tty3270_scroll_backward(struct kbd_data *kbd) |
1da177e4 | 656 | { |
ba186e7d | 657 | struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); |
1da177e4 | 658 | |
c17fe081 | 659 | spin_lock_irq(&tp->view.lock); |
9c138af9 SS |
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); | |
c17fe081 | 664 | spin_unlock_irq(&tp->view.lock); |
1da177e4 LT |
665 | } |
666 | ||
667 | /* | |
668 | * Pass input line to tty. | |
669 | */ | |
e6d98bb8 | 670 | static void tty3270_read_tasklet(unsigned long data) |
1da177e4 | 671 | { |
5cdfbdce | 672 | struct raw3270_request *rrq = (struct raw3270_request *)data; |
1da177e4 | 673 | static char kreset_data = TW_KR; |
881e18f9 | 674 | struct tty3270 *tp = container_of(rrq->view, struct tty3270, view); |
1da177e4 LT |
675 | char *input; |
676 | int len; | |
677 | ||
c17fe081 | 678 | spin_lock_irq(&tp->view.lock); |
1da177e4 LT |
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 | */ | |
d2c993d8 | 684 | input = NULL; |
1da177e4 | 685 | len = 0; |
164eb669 | 686 | switch (tp->input[0]) { |
e22de7d7 | 687 | case AID_ENTER: |
1da177e4 | 688 | /* Enter: write input to tty. */ |
164eb669 SS |
689 | input = tp->input + 6; |
690 | len = tty3270_input_size(tp->view.cols) - 6 - rrq->rescnt; | |
1da177e4 LT |
691 | if (tp->inattr != TF_INPUTN) |
692 | tty3270_rcl_add(tp, input, len); | |
9c138af9 | 693 | if (tp->nr_up > 0) |
1da177e4 | 694 | tp->nr_up = 0; |
1da177e4 | 695 | /* Clear input area. */ |
76485078 | 696 | tty3270_update_prompt(tp, ""); |
1da177e4 | 697 | tty3270_set_timer(tp, 1); |
e22de7d7 SS |
698 | break; |
699 | case AID_CLEAR: | |
1da177e4 | 700 | /* Display has been cleared. Redraw. */ |
1da177e4 LT |
701 | tp->update_flags = TTY_UPDATE_ALL; |
702 | tty3270_set_timer(tp, 1); | |
cbb36313 SS |
703 | if (!list_empty(&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: | |
164eb669 | 709 | raw3270_read_modified_cb(tp->readpartreq, tp->input); |
cbb36313 | 710 | break; |
e22de7d7 SS |
711 | default: |
712 | break; | |
1da177e4 | 713 | } |
c17fe081 | 714 | spin_unlock_irq(&tp->view.lock); |
1da177e4 LT |
715 | |
716 | /* Start keyboard reset command. */ | |
f08e3155 | 717 | raw3270_start_request(&tp->view, tp->kreset, TC_WRITE, &kreset_data, 1); |
1da177e4 | 718 | |
ba186e7d JS |
719 | while (len-- > 0) |
720 | kbd_keycode(tp->kbd, *input++); | |
721 | /* Emit keycode for AID byte. */ | |
164eb669 | 722 | kbd_keycode(tp->kbd, 256 + tp->input[0]); |
1da177e4 LT |
723 | |
724 | raw3270_request_reset(rrq); | |
725 | xchg(&tp->read, rrq); | |
726 | raw3270_put_view(&tp->view); | |
727 | } | |
728 | ||
729 | /* | |
730 | * Read request completion callback. | |
731 | */ | |
e6d98bb8 | 732 | static void tty3270_read_callback(struct raw3270_request *rq, void *data) |
1da177e4 | 733 | { |
881e18f9 | 734 | struct tty3270 *tp = container_of(rq->view, struct tty3270, view); |
7ef21387 | 735 | |
1da177e4 LT |
736 | raw3270_get_view(rq->view); |
737 | /* Schedule tasklet to pass input to tty. */ | |
881e18f9 | 738 | tasklet_schedule(&tp->readlet); |
1da177e4 LT |
739 | } |
740 | ||
741 | /* | |
742 | * Issue a read request. Call with device lock. | |
743 | */ | |
e6d98bb8 | 744 | static void tty3270_issue_read(struct tty3270 *tp, int lock) |
1da177e4 LT |
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); | |
164eb669 | 756 | raw3270_request_set_data(rrq, tp->input, tty3270_input_size(tp->view.cols)); |
1da177e4 | 757 | /* Issue the read modified request. */ |
7ef21387 | 758 | if (lock) |
1da177e4 | 759 | rc = raw3270_start(&tp->view, rrq); |
7ef21387 | 760 | else |
1da177e4 LT |
761 | rc = raw3270_start_irq(&tp->view, rrq); |
762 | if (rc) { | |
763 | raw3270_request_reset(rrq); | |
764 | xchg(&tp->read, rrq); | |
765 | } | |
766 | } | |
767 | ||
0c756914 MS |
768 | /* |
769 | * Hang up the tty | |
770 | */ | |
e6d98bb8 | 771 | static void tty3270_hangup_tasklet(unsigned long data) |
0c756914 | 772 | { |
5cdfbdce | 773 | struct tty3270 *tp = (struct tty3270 *)data; |
7ef21387 | 774 | |
0c756914 MS |
775 | tty_port_tty_hangup(&tp->port, true); |
776 | raw3270_put_view(&tp->view); | |
777 | } | |
778 | ||
1da177e4 LT |
779 | /* |
780 | * Switch to the tty view. | |
781 | */ | |
e6d98bb8 | 782 | static int tty3270_activate(struct raw3270_view *view) |
1da177e4 | 783 | { |
881e18f9 | 784 | struct tty3270 *tp = container_of(view, struct tty3270, view); |
1da177e4 | 785 | |
1da177e4 LT |
786 | tp->update_flags = TTY_UPDATE_ALL; |
787 | tty3270_set_timer(tp, 1); | |
1da177e4 LT |
788 | return 0; |
789 | } | |
790 | ||
e6d98bb8 | 791 | static void tty3270_deactivate(struct raw3270_view *view) |
1da177e4 | 792 | { |
881e18f9 | 793 | struct tty3270 *tp = container_of(view, struct tty3270, view); |
205d7ab9 | 794 | |
205d7ab9 | 795 | del_timer(&tp->timer); |
1da177e4 LT |
796 | } |
797 | ||
e6d98bb8 | 798 | static void tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) |
1da177e4 LT |
799 | { |
800 | /* Handle ATTN. Schedule tasklet to read aid. */ | |
23d805b6 | 801 | if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { |
1da177e4 LT |
802 | if (!tp->throttle) |
803 | tty3270_issue_read(tp, 0); | |
804 | else | |
805 | tp->attn = 1; | |
806 | } | |
807 | ||
808 | if (rq) { | |
0c756914 | 809 | if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { |
1da177e4 | 810 | rq->rc = -EIO; |
0c756914 MS |
811 | raw3270_get_view(&tp->view); |
812 | tasklet_schedule(&tp->hanglet); | |
813 | } else { | |
1da177e4 | 814 | /* Normal end. Copy residual count. */ |
23d805b6 | 815 | rq->rescnt = irb->scsw.cmd.count; |
0c756914 | 816 | } |
05bfd70b MS |
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, 1); | |
1da177e4 | 821 | } |
1da177e4 LT |
822 | } |
823 | ||
824 | /* | |
825 | * Allocate tty3270 structure. | |
826 | */ | |
e6d98bb8 | 827 | static struct tty3270 *tty3270_alloc_view(void) |
1da177e4 LT |
828 | { |
829 | struct tty3270 *tp; | |
1da177e4 | 830 | |
7ef21387 | 831 | tp = kzalloc(sizeof(*tp), GFP_KERNEL); |
1da177e4 LT |
832 | if (!tp) |
833 | goto out_err; | |
76485078 | 834 | |
1da177e4 | 835 | tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE); |
ed3cb6f0 | 836 | if (IS_ERR(tp->write)) |
76485078 | 837 | goto out_tp; |
1da177e4 | 838 | tp->read = raw3270_request_alloc(0); |
ed3cb6f0 | 839 | if (IS_ERR(tp->read)) |
1da177e4 LT |
840 | goto out_write; |
841 | tp->kreset = raw3270_request_alloc(1); | |
ed3cb6f0 | 842 | if (IS_ERR(tp->kreset)) |
1da177e4 | 843 | goto out_read; |
cbb36313 SS |
844 | tp->readpartreq = raw3270_request_alloc(sizeof(sfq_read_partition)); |
845 | if (IS_ERR(tp->readpartreq)) | |
846 | goto out_reset; | |
1da177e4 LT |
847 | tp->kbd = kbd_alloc(); |
848 | if (!tp->kbd) | |
cbb36313 | 849 | goto out_readpartreq; |
57985d7e MS |
850 | |
851 | tty_port_init(&tp->port); | |
c9602ee7 | 852 | timer_setup(&tp->timer, tty3270_update, 0); |
5cdfbdce | 853 | tasklet_init(&tp->readlet, tty3270_read_tasklet, |
7ef21387 | 854 | (unsigned long)tp->read); |
5cdfbdce | 855 | tasklet_init(&tp->hanglet, tty3270_hangup_tasklet, |
7ef21387 | 856 | (unsigned long)tp); |
1da177e4 LT |
857 | return tp; |
858 | ||
cbb36313 SS |
859 | out_readpartreq: |
860 | raw3270_request_free(tp->readpartreq); | |
1da177e4 LT |
861 | out_reset: |
862 | raw3270_request_free(tp->kreset); | |
863 | out_read: | |
864 | raw3270_request_free(tp->read); | |
865 | out_write: | |
866 | raw3270_request_free(tp->write); | |
1da177e4 LT |
867 | out_tp: |
868 | kfree(tp); | |
869 | out_err: | |
870 | return ERR_PTR(-ENOMEM); | |
871 | } | |
872 | ||
873 | /* | |
874 | * Free tty3270 structure. | |
875 | */ | |
e6d98bb8 | 876 | static void tty3270_free_view(struct tty3270 *tp) |
1da177e4 | 877 | { |
1da177e4 LT |
878 | kbd_free(tp->kbd); |
879 | raw3270_request_free(tp->kreset); | |
880 | raw3270_request_free(tp->read); | |
881 | raw3270_request_free(tp->write); | |
9c138af9 | 882 | free_page((unsigned long)tp->converted_line); |
191c5f10 | 883 | tty_port_destroy(&tp->port); |
1da177e4 LT |
884 | kfree(tp); |
885 | } | |
886 | ||
887 | /* | |
888 | * Allocate tty3270 screen. | |
889 | */ | |
b2057c87 SS |
890 | static struct tty3270_line *tty3270_alloc_screen(struct tty3270 *tp, unsigned int rows, |
891 | unsigned int cols, int *allocated_out) | |
1da177e4 | 892 | { |
4d334fd1 | 893 | struct tty3270_line *screen; |
b2057c87 | 894 | int allocated, lines; |
1da177e4 | 895 | |
b2057c87 SS |
896 | allocated = __roundup_pow_of_two(rows) * TTY3270_SCREEN_PAGES; |
897 | screen = kcalloc(allocated, sizeof(struct tty3270_line), GFP_KERNEL); | |
4d334fd1 | 898 | if (!screen) |
1da177e4 | 899 | goto out_err; |
b2057c87 SS |
900 | for (lines = 0; lines < allocated; lines++) { |
901 | screen[lines].cells = kcalloc(cols, sizeof(struct tty3270_cell), GFP_KERNEL); | |
4d334fd1 | 902 | if (!screen[lines].cells) |
1da177e4 | 903 | goto out_screen; |
1da177e4 | 904 | } |
b2057c87 | 905 | *allocated_out = allocated; |
4d334fd1 | 906 | return screen; |
1da177e4 LT |
907 | out_screen: |
908 | while (lines--) | |
4d334fd1 MS |
909 | kfree(screen[lines].cells); |
910 | kfree(screen); | |
1da177e4 | 911 | out_err: |
4d334fd1 | 912 | return ERR_PTR(-ENOMEM); |
1da177e4 LT |
913 | } |
914 | ||
76485078 SS |
915 | static char **tty3270_alloc_recall(int cols) |
916 | { | |
917 | char **lines; | |
918 | int i; | |
919 | ||
920 | lines = kmalloc_array(TTY3270_RECALL_SIZE, sizeof(char *), GFP_KERNEL); | |
921 | if (!lines) | |
922 | return NULL; | |
923 | for (i = 0; i < TTY3270_RECALL_SIZE; i++) { | |
924 | lines[i] = kcalloc(1, 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(lines[i]); | |
934 | kfree(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(lines[i]); | |
944 | kfree(lines); | |
945 | } | |
946 | ||
1da177e4 LT |
947 | /* |
948 | * Free tty3270 screen. | |
949 | */ | |
b2057c87 | 950 | static void tty3270_free_screen(struct tty3270_line *screen, int old_lines) |
1da177e4 LT |
951 | { |
952 | int lines; | |
953 | ||
b2057c87 | 954 | for (lines = 0; lines < old_lines; lines++) |
4d334fd1 MS |
955 | kfree(screen[lines].cells); |
956 | kfree(screen); | |
957 | } | |
958 | ||
959 | /* | |
960 | * Resize tty3270 screen | |
961 | */ | |
91621ba7 SS |
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) | |
4d334fd1 | 965 | { |
91621ba7 | 966 | struct tty3270 *tp = container_of(view, struct tty3270, view); |
4d334fd1 | 967 | struct tty3270_line *screen, *oscreen; |
76485078 | 968 | char **old_rcl_lines, **new_rcl_lines; |
164eb669 SS |
969 | char *old_prompt, *new_prompt; |
970 | char *old_input, *new_input; | |
4d334fd1 | 971 | struct tty_struct *tty; |
4d334fd1 | 972 | struct winsize ws; |
b2057c87 | 973 | int new_allocated, old_allocated = tp->allocated_lines; |
4d334fd1 | 974 | |
91621ba7 SS |
975 | if (old_model == new_model && |
976 | old_cols == new_cols && | |
977 | old_rows == new_rows) { | |
978 | spin_lock_irq(&tp->view.lock); | |
9c138af9 | 979 | tty3270_redraw(tp); |
91621ba7 SS |
980 | spin_unlock_irq(&tp->view.lock); |
981 | return; | |
982 | } | |
164eb669 SS |
983 | |
984 | new_input = kzalloc(tty3270_input_size(new_cols), GFP_KERNEL | GFP_DMA); | |
985 | if (!new_input) | |
986 | return; | |
987 | new_prompt = kzalloc(tty3270_input_size(new_cols), GFP_KERNEL); | |
988 | if (!new_prompt) | |
989 | goto out_input; | |
b2057c87 | 990 | screen = tty3270_alloc_screen(tp, new_rows, new_cols, &new_allocated); |
8f0ba630 | 991 | if (IS_ERR(screen)) |
164eb669 | 992 | goto out_prompt; |
76485078 SS |
993 | new_rcl_lines = tty3270_alloc_recall(new_cols); |
994 | if (!new_rcl_lines) | |
995 | goto out_screen; | |
164eb669 | 996 | |
4d334fd1 | 997 | /* Switch to new output size */ |
c17fe081 | 998 | spin_lock_irq(&tp->view.lock); |
7e36eff1 | 999 | tty3270_blank_screen(tp); |
4d334fd1 | 1000 | oscreen = tp->screen; |
4d334fd1 | 1001 | tp->screen = screen; |
b2057c87 | 1002 | tp->allocated_lines = new_allocated; |
91621ba7 SS |
1003 | tp->view.rows = new_rows; |
1004 | tp->view.cols = new_cols; | |
1005 | tp->view.model = new_model; | |
4d334fd1 | 1006 | tp->update_flags = TTY_UPDATE_ALL; |
164eb669 SS |
1007 | old_input = tp->input; |
1008 | old_prompt = tp->prompt; | |
76485078 | 1009 | old_rcl_lines = tp->rcl_lines; |
164eb669 SS |
1010 | tp->input = new_input; |
1011 | tp->prompt = new_prompt; | |
76485078 SS |
1012 | tp->rcl_lines = new_rcl_lines; |
1013 | tp->rcl_read_index = 0; | |
1014 | tp->rcl_write_index = 0; | |
c17fe081 | 1015 | spin_unlock_irq(&tp->view.lock); |
b2057c87 | 1016 | tty3270_free_screen(oscreen, old_allocated); |
164eb669 SS |
1017 | kfree(old_input); |
1018 | kfree(old_prompt); | |
76485078 | 1019 | tty3270_free_recall(old_rcl_lines); |
4d334fd1 MS |
1020 | tty3270_set_timer(tp, 1); |
1021 | /* Informat tty layer about new size */ | |
1022 | tty = tty_port_tty_get(&tp->port); | |
1023 | if (!tty) | |
1024 | return; | |
9eb99b94 | 1025 | ws.ws_row = tty3270_tty_rows(tp); |
4d334fd1 MS |
1026 | ws.ws_col = tp->view.cols; |
1027 | tty_do_resize(tty, &ws); | |
5ff04fe5 | 1028 | tty_kref_put(tty); |
164eb669 | 1029 | return; |
76485078 SS |
1030 | out_screen: |
1031 | tty3270_free_screen(screen, new_rows); | |
164eb669 SS |
1032 | out_prompt: |
1033 | kfree(new_prompt); | |
1034 | out_input: | |
1035 | kfree(new_input); | |
4d334fd1 MS |
1036 | } |
1037 | ||
1da177e4 LT |
1038 | /* |
1039 | * Unlink tty3270 data structure from tty. | |
1040 | */ | |
e6d98bb8 | 1041 | static void tty3270_release(struct raw3270_view *view) |
1da177e4 | 1042 | { |
881e18f9 | 1043 | struct tty3270 *tp = container_of(view, struct tty3270, view); |
ba186e7d | 1044 | struct tty_struct *tty = tty_port_tty_get(&tp->port); |
1da177e4 | 1045 | |
1da177e4 | 1046 | if (tty) { |
d2c993d8 | 1047 | tty->driver_data = NULL; |
ba186e7d | 1048 | tty_port_tty_set(&tp->port, NULL); |
1da177e4 LT |
1049 | tty_hangup(tty); |
1050 | raw3270_put_view(&tp->view); | |
ba186e7d | 1051 | tty_kref_put(tty); |
1da177e4 LT |
1052 | } |
1053 | } | |
1054 | ||
1055 | /* | |
1056 | * Free tty3270 data structure | |
1057 | */ | |
e6d98bb8 | 1058 | static void tty3270_free(struct raw3270_view *view) |
1da177e4 | 1059 | { |
881e18f9 | 1060 | struct tty3270 *tp = container_of(view, struct tty3270, view); |
4d334fd1 | 1061 | |
03439e7d | 1062 | del_timer_sync(&tp->timer); |
b2057c87 | 1063 | tty3270_free_screen(tp->screen, tp->allocated_lines); |
ec1b0a33 | 1064 | free_page((unsigned long)tp->converted_line); |
164eb669 SS |
1065 | kfree(tp->input); |
1066 | kfree(tp->prompt); | |
881e18f9 | 1067 | tty3270_free_view(tp); |
1da177e4 LT |
1068 | } |
1069 | ||
1070 | /* | |
1071 | * Delayed freeing of tty3270 views. | |
1072 | */ | |
e6d98bb8 | 1073 | static void tty3270_del_views(void) |
1da177e4 | 1074 | { |
1da177e4 LT |
1075 | int i; |
1076 | ||
c95571e6 MS |
1077 | for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) { |
1078 | struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i); | |
7ef21387 | 1079 | |
881e18f9 JS |
1080 | if (!IS_ERR(view)) |
1081 | raw3270_del_view(view); | |
1da177e4 LT |
1082 | } |
1083 | } | |
1084 | ||
2b67fc46 | 1085 | static struct raw3270_fn tty3270_fn = { |
1da177e4 LT |
1086 | .activate = tty3270_activate, |
1087 | .deactivate = tty3270_deactivate, | |
7ef21387 | 1088 | .intv = (void *)tty3270_irq, |
1da177e4 | 1089 | .release = tty3270_release, |
4d334fd1 MS |
1090 | .free = tty3270_free, |
1091 | .resize = tty3270_resize | |
1da177e4 LT |
1092 | }; |
1093 | ||
a21e962e SS |
1094 | static int |
1095 | tty3270_create_view(int index, struct tty3270 **newtp) | |
1da177e4 LT |
1096 | { |
1097 | struct tty3270 *tp; | |
9c138af9 | 1098 | int rc; |
1da177e4 | 1099 | |
a21e962e SS |
1100 | if (tty3270_max_index < index + 1) |
1101 | tty3270_max_index = index + 1; | |
1da177e4 LT |
1102 | |
1103 | /* Allocate tty3270 structure on first open. */ | |
1104 | tp = tty3270_alloc_view(); | |
1105 | if (IS_ERR(tp)) | |
1106 | return PTR_ERR(tp); | |
1107 | ||
1fcbba3d | 1108 | rc = raw3270_add_view(&tp->view, &tty3270_fn, |
a21e962e | 1109 | index + RAW3270_FIRSTMINOR, |
c17fe081 | 1110 | RAW3270_VIEW_LOCK_IRQ); |
ec1b0a33 SS |
1111 | if (rc) |
1112 | goto out_free_view; | |
1da177e4 | 1113 | |
b2057c87 SS |
1114 | tp->screen = tty3270_alloc_screen(tp, tp->view.rows, tp->view.cols, |
1115 | &tp->allocated_lines); | |
4d334fd1 MS |
1116 | if (IS_ERR(tp->screen)) { |
1117 | rc = PTR_ERR(tp->screen); | |
ec1b0a33 SS |
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; | |
1da177e4 LT |
1125 | } |
1126 | ||
164eb669 SS |
1127 | tp->input = kzalloc(tty3270_input_size(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(tty3270_input_size(tp->view.cols), GFP_KERNEL); | |
1134 | if (!tp->prompt) { | |
1135 | rc = -ENOMEM; | |
1136 | goto out_free_input; | |
1137 | } | |
1da177e4 | 1138 | |
76485078 SS |
1139 | tp->rcl_lines = tty3270_alloc_recall(tp->view.cols); |
1140 | if (!tp->rcl_lines) { | |
1141 | rc = -ENOMEM; | |
1142 | goto out_free_prompt; | |
1143 | } | |
1144 | ||
1da177e4 | 1145 | /* Create blank line for every line in the tty output area. */ |
9c138af9 | 1146 | tty3270_blank_screen(tp); |
1da177e4 | 1147 | |
ba186e7d | 1148 | tp->kbd->port = &tp->port; |
1da177e4 LT |
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(&tp->view); | |
a21e962e SS |
1156 | raw3270_put_view(&tp->view); |
1157 | *newtp = tp; | |
1158 | return 0; | |
ec1b0a33 | 1159 | |
76485078 SS |
1160 | out_free_prompt: |
1161 | kfree(tp->prompt); | |
164eb669 SS |
1162 | out_free_input: |
1163 | kfree(tp->input); | |
1164 | out_free_converted_line: | |
1165 | free_page((unsigned long)tp->converted_line); | |
ec1b0a33 SS |
1166 | out_free_screen: |
1167 | tty3270_free_screen(tp->screen, tp->view.rows); | |
1168 | out_put_view: | |
1169 | raw3270_put_view(&tp->view); | |
1170 | raw3270_del_view(&tp->view); | |
1171 | out_free_view: | |
1172 | tty3270_free_view(tp); | |
1173 | return rc; | |
a21e962e SS |
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; | |
20cda6f2 | 1185 | |
a21e962e SS |
1186 | /* Check if the tty3270 is already there. */ |
1187 | view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR); | |
1188 | if (IS_ERR(view)) { | |
1189 | rc = tty3270_create_view(tty->index, &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 | ||
9eb99b94 | 1198 | tty->winsize.ws_row = tty3270_tty_rows(tp); |
a21e962e | 1199 | tty->winsize.ws_col = tp->view.cols; |
20cda6f2 JS |
1200 | rc = tty_port_install(&tp->port, driver, tty); |
1201 | if (rc) { | |
1202 | raw3270_put_view(&tp->view); | |
1203 | return rc; | |
1204 | } | |
20cda6f2 | 1205 | tty->driver_data = tp; |
1da177e4 LT |
1206 | return 0; |
1207 | } | |
1208 | ||
736c9fd2 MS |
1209 | /* |
1210 | * This routine is called whenever a 3270 tty is opened. | |
1211 | */ | |
e6d98bb8 | 1212 | static int tty3270_open(struct tty_struct *tty, struct file *filp) |
736c9fd2 MS |
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 | ||
1da177e4 LT |
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 | */ | |
e6d98bb8 | 1226 | static void tty3270_close(struct tty_struct *tty, struct file *filp) |
1da177e4 | 1227 | { |
881e18f9 | 1228 | struct tty3270 *tp = tty->driver_data; |
1da177e4 LT |
1229 | |
1230 | if (tty->count > 1) | |
1231 | return; | |
b512353c | 1232 | if (tp) |
ba186e7d | 1233 | tty_port_tty_set(&tp->port, NULL); |
1da177e4 LT |
1234 | } |
1235 | ||
20cda6f2 JS |
1236 | static void tty3270_cleanup(struct tty_struct *tty) |
1237 | { | |
1238 | struct tty3270 *tp = tty->driver_data; | |
1239 | ||
b512353c MS |
1240 | if (tp) { |
1241 | tty->driver_data = NULL; | |
20cda6f2 | 1242 | raw3270_put_view(&tp->view); |
b512353c | 1243 | } |
20cda6f2 JS |
1244 | } |
1245 | ||
1da177e4 LT |
1246 | /* |
1247 | * We always have room. | |
1248 | */ | |
e6d98bb8 | 1249 | static unsigned int tty3270_write_room(struct tty_struct *tty) |
1da177e4 LT |
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 | */ | |
e17934c1 | 1258 | static void tty3270_put_character(struct tty3270 *tp, u8 ch) |
1da177e4 LT |
1259 | { |
1260 | struct tty3270_line *line; | |
1261 | struct tty3270_cell *cell; | |
1262 | ||
9c138af9 | 1263 | line = tty3270_get_write_line(tp, tp->cy); |
1da177e4 LT |
1264 | if (line->len <= tp->cx) { |
1265 | while (line->len < tp->cx) { | |
1266 | cell = line->cells + line->len; | |
6e49017c | 1267 | cell->character = ' '; |
c2e9375e | 1268 | cell->attributes = tp->attributes; |
1da177e4 LT |
1269 | line->len++; |
1270 | } | |
1271 | line->len++; | |
1272 | } | |
1273 | cell = line->cells + tp->cx; | |
6e49017c | 1274 | cell->character = ch; |
c2e9375e | 1275 | cell->attributes = tp->attributes; |
9c138af9 | 1276 | line->dirty = 1; |
1da177e4 LT |
1277 | } |
1278 | ||
1da177e4 LT |
1279 | /* |
1280 | * Do carriage return. | |
1281 | */ | |
e6d98bb8 | 1282 | static void tty3270_cr(struct tty3270 *tp) |
1da177e4 LT |
1283 | { |
1284 | tp->cx = 0; | |
1285 | } | |
1286 | ||
1287 | /* | |
1288 | * Do line feed. | |
1289 | */ | |
e6d98bb8 | 1290 | static void tty3270_lf(struct tty3270 *tp) |
1da177e4 | 1291 | { |
9c138af9 | 1292 | struct tty3270_line *line; |
1da177e4 LT |
1293 | int i; |
1294 | ||
9eb99b94 | 1295 | if (tp->cy < tty3270_tty_rows(tp) - 1) { |
1da177e4 | 1296 | tp->cy++; |
9c138af9 SS |
1297 | } else { |
1298 | tp->line_view_start = tty3270_line_increment(tp, tp->line_view_start, 1); | |
1299 | tp->line_write_start = tty3270_line_increment(tp, tp->line_write_start, 1); | |
1300 | for (i = 0; i < tty3270_tty_rows(tp); i++) | |
1301 | tty3270_get_view_line(tp, i)->dirty = 1; | |
1da177e4 | 1302 | } |
9c138af9 SS |
1303 | |
1304 | line = tty3270_get_write_line(tp, tp->cy); | |
1305 | line->len = 0; | |
1306 | line->dirty = 1; | |
1da177e4 LT |
1307 | } |
1308 | ||
e6d98bb8 | 1309 | static void tty3270_ri(struct tty3270 *tp) |
1da177e4 | 1310 | { |
9c138af9 SS |
1311 | if (tp->cy > 0) |
1312 | tp->cy--; | |
1da177e4 LT |
1313 | } |
1314 | ||
c2e9375e SS |
1315 | static void tty3270_reset_cell(struct tty3270 *tp, struct tty3270_cell *cell) |
1316 | { | |
6e49017c | 1317 | cell->character = ' '; |
c2e9375e SS |
1318 | tty3270_reset_attributes(&cell->attributes); |
1319 | } | |
1320 | ||
1da177e4 LT |
1321 | /* |
1322 | * Insert characters at current position. | |
1323 | */ | |
e6d98bb8 | 1324 | static void tty3270_insert_characters(struct tty3270 *tp, int n) |
1da177e4 LT |
1325 | { |
1326 | struct tty3270_line *line; | |
1327 | int k; | |
1328 | ||
9c138af9 | 1329 | line = tty3270_get_write_line(tp, tp->cy); |
c2e9375e SS |
1330 | while (line->len < tp->cx) |
1331 | tty3270_reset_cell(tp, &line->cells[line->len++]); | |
1da177e4 LT |
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) { | |
6e49017c | 1341 | line->cells[tp->cx + n].character = ' '; |
c2e9375e | 1342 | line->cells[tp->cx + n].attributes = tp->attributes; |
1da177e4 LT |
1343 | } |
1344 | } | |
1345 | ||
1346 | /* | |
1347 | * Delete characters at current position. | |
1348 | */ | |
e6d98bb8 | 1349 | static void tty3270_delete_characters(struct tty3270 *tp, int n) |
1da177e4 LT |
1350 | { |
1351 | struct tty3270_line *line; | |
1352 | int i; | |
1353 | ||
9c138af9 | 1354 | line = tty3270_get_write_line(tp, tp->cy); |
1da177e4 LT |
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 | */ | |
e6d98bb8 | 1369 | static void tty3270_erase_characters(struct tty3270 *tp, int n) |
1da177e4 LT |
1370 | { |
1371 | struct tty3270_line *line; | |
1372 | struct tty3270_cell *cell; | |
1373 | ||
9c138af9 | 1374 | line = tty3270_get_write_line(tp, tp->cy); |
1da177e4 LT |
1375 | while (line->len > tp->cx && n-- > 0) { |
1376 | cell = line->cells + tp->cx++; | |
c2e9375e | 1377 | tty3270_reset_cell(tp, cell); |
1da177e4 LT |
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 | */ | |
e6d98bb8 | 1389 | static void tty3270_erase_line(struct tty3270 *tp, int mode) |
1da177e4 LT |
1390 | { |
1391 | struct tty3270_line *line; | |
1392 | struct tty3270_cell *cell; | |
4043ea22 | 1393 | int i, start, end; |
1da177e4 | 1394 | |
9c138af9 | 1395 | line = tty3270_get_write_line(tp, tp->cy); |
4043ea22 | 1396 | |
815f3eee SS |
1397 | switch (mode) { |
1398 | case 0: | |
4043ea22 SS |
1399 | start = tp->cx; |
1400 | end = tp->view.cols; | |
815f3eee SS |
1401 | break; |
1402 | case 1: | |
4043ea22 SS |
1403 | start = 0; |
1404 | end = tp->cx; | |
815f3eee SS |
1405 | break; |
1406 | case 2: | |
4043ea22 SS |
1407 | start = 0; |
1408 | end = tp->view.cols; | |
815f3eee SS |
1409 | break; |
1410 | default: | |
1411 | return; | |
1412 | } | |
4043ea22 SS |
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; | |
1da177e4 LT |
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 | */ | |
e6d98bb8 | 1430 | static void tty3270_erase_display(struct tty3270 *tp, int mode) |
1da177e4 | 1431 | { |
9c138af9 | 1432 | struct tty3270_line *line; |
65b77ccb | 1433 | int i, start, end; |
1da177e4 | 1434 | |
65b77ccb SS |
1435 | switch (mode) { |
1436 | case 0: | |
1da177e4 | 1437 | tty3270_erase_line(tp, 0); |
65b77ccb | 1438 | start = tp->cy + 1; |
9eb99b94 | 1439 | end = tty3270_tty_rows(tp); |
65b77ccb SS |
1440 | break; |
1441 | case 1: | |
1442 | start = 0; | |
1443 | end = tp->cy; | |
1da177e4 | 1444 | tty3270_erase_line(tp, 1); |
65b77ccb SS |
1445 | break; |
1446 | case 2: | |
1447 | start = 0; | |
9eb99b94 | 1448 | end = tty3270_tty_rows(tp); |
65b77ccb SS |
1449 | break; |
1450 | default: | |
1451 | return; | |
1452 | } | |
1453 | for (i = start; i < end; i++) { | |
9c138af9 SS |
1454 | line = tty3270_get_write_line(tp, i); |
1455 | line->len = 0; | |
1456 | line->dirty = 1; | |
1da177e4 | 1457 | } |
1da177e4 LT |
1458 | } |
1459 | ||
1460 | /* | |
1461 | * Set attributes found in an escape sequence. | |
1462 | * Esc [ <attr> ; <attr> ; ... m | |
1463 | */ | |
e6d98bb8 | 1464 | static void tty3270_set_attributes(struct tty3270 *tp) |
1da177e4 | 1465 | { |
1da177e4 LT |
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 */ | |
c2e9375e | 1472 | tty3270_reset_attributes(&tp->attributes); |
1da177e4 LT |
1473 | break; |
1474 | /* Highlight. */ | |
1475 | case 4: /* Start underlining. */ | |
0573fff2 | 1476 | tp->attributes.highlight = TTY3270_HIGHLIGHT_UNDERSCORE; |
1da177e4 LT |
1477 | break; |
1478 | case 5: /* Start blink. */ | |
0573fff2 | 1479 | tp->attributes.highlight = TTY3270_HIGHLIGHT_BLINK; |
1da177e4 LT |
1480 | break; |
1481 | case 7: /* Start reverse. */ | |
0573fff2 | 1482 | tp->attributes.highlight = TTY3270_HIGHLIGHT_REVERSE; |
1da177e4 LT |
1483 | break; |
1484 | case 24: /* End underlining */ | |
0573fff2 | 1485 | tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_UNDERSCORE; |
1da177e4 LT |
1486 | break; |
1487 | case 25: /* End blink. */ | |
0573fff2 | 1488 | tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_BLINK; |
1da177e4 LT |
1489 | break; |
1490 | case 27: /* End reverse. */ | |
0573fff2 | 1491 | tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_REVERSE; |
1da177e4 LT |
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 */ | |
18fc2e93 | 1503 | tp->attributes.f_color = attr - 30; |
4043ea22 SS |
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 */ | |
18fc2e93 | 1515 | tp->attributes.b_color = attr - 40; |
1da177e4 LT |
1516 | break; |
1517 | } | |
1518 | } | |
1519 | } | |
1520 | ||
e6d98bb8 | 1521 | static inline int tty3270_getpar(struct tty3270 *tp, int ix) |
1da177e4 LT |
1522 | { |
1523 | return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1; | |
1524 | } | |
1525 | ||
e6d98bb8 | 1526 | static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) |
1da177e4 | 1527 | { |
4043ea22 SS |
1528 | struct tty3270_line *line; |
1529 | struct tty3270_cell *cell; | |
364c8558 HC |
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); | |
9c138af9 | 1534 | line = tty3270_get_write_line(tp, tp->cy); |
4043ea22 SS |
1535 | while (line->len < tp->cx) { |
1536 | cell = line->cells + line->len; | |
1537 | cell->character = ' '; | |
1538 | cell->attributes = tp->attributes; | |
1539 | line->len++; | |
1540 | } | |
9c138af9 | 1541 | tp->cy = min_t(int, tty3270_tty_rows(tp) - 1, max_cy); |
1da177e4 LT |
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 | */ | |
e17934c1 | 1564 | static void tty3270_escape_sequence(struct tty3270 *tp, u8 ch) |
1da177e4 | 1565 | { |
303bac9d | 1566 | enum { ES_NORMAL, ES_ESC, ES_SQUARE, ES_PAREN, ES_GETPARS }; |
1da177e4 | 1567 | |
303bac9d | 1568 | if (tp->esc_state == ES_NORMAL) { |
1da177e4 LT |
1569 | if (ch == 0x1b) |
1570 | /* Starting new escape sequence. */ | |
303bac9d | 1571 | tp->esc_state = ES_ESC; |
1da177e4 LT |
1572 | return; |
1573 | } | |
303bac9d SS |
1574 | if (tp->esc_state == ES_ESC) { |
1575 | tp->esc_state = ES_NORMAL; | |
1da177e4 LT |
1576 | switch (ch) { |
1577 | case '[': | |
303bac9d | 1578 | tp->esc_state = ES_SQUARE; |
1da177e4 | 1579 | break; |
e4b57b93 | 1580 | case '(': |
303bac9d | 1581 | tp->esc_state = ES_PAREN; |
e4b57b93 | 1582 | break; |
1da177e4 LT |
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. */ | |
ba186e7d | 1594 | kbd_puts_queue(&tp->port, "\033[?6c"); |
1da177e4 LT |
1595 | break; |
1596 | case '7': /* Save cursor position. */ | |
1597 | tp->saved_cx = tp->cx; | |
1598 | tp->saved_cy = tp->cy; | |
c2e9375e | 1599 | tp->saved_attributes = tp->attributes; |
1da177e4 LT |
1600 | break; |
1601 | case '8': /* Restore cursor position. */ | |
1da177e4 | 1602 | tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); |
c2e9375e | 1603 | tp->attributes = tp->saved_attributes; |
1da177e4 LT |
1604 | break; |
1605 | case 'c': /* Reset terminal. */ | |
f8674930 SS |
1606 | tp->cx = 0; |
1607 | tp->cy = 0; | |
1608 | tp->saved_cx = 0; | |
1609 | tp->saved_cy = 0; | |
c2e9375e SS |
1610 | tty3270_reset_attributes(&tp->attributes); |
1611 | tty3270_reset_attributes(&tp->saved_attributes); | |
1da177e4 LT |
1612 | tty3270_erase_display(tp, 2); |
1613 | break; | |
1614 | } | |
1615 | return; | |
1616 | } | |
e4b57b93 SS |
1617 | |
1618 | switch (tp->esc_state) { | |
303bac9d SS |
1619 | case ES_PAREN: |
1620 | tp->esc_state = ES_NORMAL; | |
e4b57b93 SS |
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; | |
303bac9d SS |
1630 | case ES_SQUARE: |
1631 | tp->esc_state = ES_GETPARS; | |
1da177e4 LT |
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; | |
e4b57b93 | 1637 | fallthrough; |
303bac9d | 1638 | case ES_GETPARS: |
1da177e4 LT |
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 | } | |
e4b57b93 SS |
1648 | break; |
1649 | default: | |
1650 | break; | |
1da177e4 | 1651 | } |
303bac9d | 1652 | tp->esc_state = ES_NORMAL; |
1da177e4 LT |
1653 | if (ch == 'n' && !tp->esc_ques) { |
1654 | if (tp->esc_par[0] == 5) /* Status report. */ | |
ba186e7d | 1655 | kbd_puts_queue(&tp->port, "\033[0n"); |
1da177e4 LT |
1656 | else if (tp->esc_par[0] == 6) { /* Cursor report. */ |
1657 | char buf[40]; | |
7ef21387 | 1658 | |
1da177e4 | 1659 | sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1); |
ba186e7d | 1660 | kbd_puts_queue(&tp->port, buf); |
1da177e4 LT |
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, tty3270_getpar(tp, 1) - 1, | |
1673 | tty3270_getpar(tp, 0) - 1); | |
1674 | break; | |
1675 | case 'd': /* Set y position. */ | |
1676 | tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1); | |
1677 | break; | |
1678 | case 'A': /* Cursor up. */ | |
1679 | case 'F': | |
1680 | tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0)); | |
1681 | break; | |
1682 | case 'B': /* Cursor down. */ | |
1683 | case 'e': | |
1684 | case 'E': | |
1685 | tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0)); | |
1686 | break; | |
1687 | case 'C': /* Cursor forward. */ | |
1688 | case 'a': | |
1689 | tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy); | |
1690 | break; | |
1691 | case 'D': /* Cursor backward. */ | |
1692 | tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy); | |
1693 | break; | |
1694 | case 'G': /* Set x position. */ | |
1695 | case '`': | |
1696 | tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy); | |
1697 | break; | |
1698 | case 'X': /* Erase Characters. */ | |
1699 | tty3270_erase_characters(tp, tty3270_getpar(tp, 0)); | |
1700 | break; | |
1701 | case 'J': /* Erase display. */ | |
1702 | tty3270_erase_display(tp, tp->esc_par[0]); | |
1703 | break; | |
1704 | case 'K': /* Erase line. */ | |
1705 | tty3270_erase_line(tp, tp->esc_par[0]); | |
1706 | break; | |
1707 | case 'P': /* Delete characters. */ | |
1708 | tty3270_delete_characters(tp, tty3270_getpar(tp, 0)); | |
1709 | break; | |
1710 | case '@': /* Insert characters. */ | |
1711 | tty3270_insert_characters(tp, tty3270_getpar(tp, 0)); | |
1712 | break; | |
1713 | case 's': /* Save cursor position. */ | |
1714 | tp->saved_cx = tp->cx; | |
1715 | tp->saved_cy = tp->cy; | |
c2e9375e | 1716 | tp->saved_attributes = tp->attributes; |
1da177e4 LT |
1717 | break; |
1718 | case 'u': /* Restore cursor position. */ | |
1da177e4 | 1719 | tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); |
c2e9375e | 1720 | tp->attributes = tp->saved_attributes; |
1da177e4 LT |
1721 | break; |
1722 | } | |
1723 | } | |
1724 | ||
1725 | /* | |
1726 | * String write routine for 3270 ttys | |
1727 | */ | |
e6d98bb8 | 1728 | static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, |
e17934c1 | 1729 | const u8 *buf, size_t count) |
1da177e4 LT |
1730 | { |
1731 | int i_msg, i; | |
1732 | ||
c17fe081 | 1733 | spin_lock_irq(&tp->view.lock); |
6e94dbc7 | 1734 | for (i_msg = 0; !tty->flow.stopped && i_msg < count; i_msg++) { |
1da177e4 LT |
1735 | if (tp->esc_state != 0) { |
1736 | /* Continue escape sequence. */ | |
1737 | tty3270_escape_sequence(tp, buf[i_msg]); | |
1738 | continue; | |
1739 | } | |
1740 | ||
1741 | switch (buf[i_msg]) { | |
970cf9a9 SS |
1742 | case 0x00: |
1743 | break; | |
1da177e4 LT |
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, ' '); | |
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, ' '); | |
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, 2); | |
f8674930 SS |
1770 | tp->cx = 0; |
1771 | tp->cy = 0; | |
1da177e4 LT |
1772 | break; |
1773 | case 0x0d: /* '\r' -- Carriage Return */ | |
1774 | tp->cx = 0; | |
1775 | break; | |
94dbb0a7 SS |
1776 | case 0x0e: |
1777 | tp->attributes.alternate_charset = 1; | |
1778 | break; | |
1da177e4 | 1779 | case 0x0f: /* SuSE "exit alternate mode" */ |
94dbb0a7 | 1780 | tp->attributes.alternate_charset = 0; |
1da177e4 LT |
1781 | break; |
1782 | case 0x1b: /* Start escape sequence. */ | |
1783 | tty3270_escape_sequence(tp, 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, buf[i_msg]); | |
1791 | tp->cx++; | |
1792 | break; | |
1793 | } | |
1794 | } | |
1da177e4 | 1795 | /* Setup timer to update display after 1/10 second */ |
ba5c2e2a | 1796 | tp->update_flags |= TTY_UPDATE_LINES; |
1da177e4 | 1797 | if (!timer_pending(&tp->timer)) |
9e1d1d8e | 1798 | tty3270_set_timer(tp, msecs_to_jiffies(100)); |
1da177e4 | 1799 | |
c17fe081 | 1800 | spin_unlock_irq(&tp->view.lock); |
1da177e4 LT |
1801 | } |
1802 | ||
1803 | /* | |
1804 | * String write routine for 3270 ttys | |
1805 | */ | |
95713967 JSS |
1806 | static ssize_t tty3270_write(struct tty_struct *tty, const u8 *buf, |
1807 | size_t count) | |
1da177e4 LT |
1808 | { |
1809 | struct tty3270 *tp; | |
1810 | ||
1811 | tp = tty->driver_data; | |
1812 | if (!tp) | |
1813 | return 0; | |
1814 | if (tp->char_count > 0) { | |
20acdfa8 | 1815 | tty3270_do_write(tp, tty, tp->char_buf, tp->char_count); |
1da177e4 LT |
1816 | tp->char_count = 0; |
1817 | } | |
20acdfa8 | 1818 | tty3270_do_write(tp, tty, buf, count); |
1da177e4 LT |
1819 | return count; |
1820 | } | |
1821 | ||
1822 | /* | |
1823 | * Put single characters to the ttys character buffer | |
1824 | */ | |
dcaafbe6 | 1825 | static int tty3270_put_char(struct tty_struct *tty, u8 ch) |
1da177e4 LT |
1826 | { |
1827 | struct tty3270 *tp; | |
1828 | ||
1829 | tp = tty->driver_data; | |
74c76c84 HC |
1830 | if (!tp || tp->char_count >= TTY3270_CHAR_BUF_SIZE) |
1831 | return 0; | |
1832 | tp->char_buf[tp->char_count++] = ch; | |
1833 | return 1; | |
1da177e4 LT |
1834 | } |
1835 | ||
1836 | /* | |
1837 | * Flush all characters from the ttys characeter buffer put there | |
1838 | * by tty3270_put_char. | |
1839 | */ | |
e6d98bb8 | 1840 | static void tty3270_flush_chars(struct tty_struct *tty) |
1da177e4 LT |
1841 | { |
1842 | struct tty3270 *tp; | |
1843 | ||
1844 | tp = tty->driver_data; | |
1845 | if (!tp) | |
1846 | return; | |
1847 | if (tp->char_count > 0) { | |
20acdfa8 | 1848 | tty3270_do_write(tp, tty, tp->char_buf, tp->char_count); |
1da177e4 LT |
1849 | tp->char_count = 0; |
1850 | } | |
1851 | } | |
1852 | ||
1da177e4 LT |
1853 | /* |
1854 | * Check for visible/invisible input switches | |
1855 | */ | |
e6d98bb8 | 1856 | static void tty3270_set_termios(struct tty_struct *tty, const struct ktermios *old) |
1da177e4 LT |
1857 | { |
1858 | struct tty3270 *tp; | |
1859 | int new; | |
1860 | ||
1861 | tp = tty->driver_data; | |
1862 | if (!tp) | |
1863 | return; | |
c17fe081 | 1864 | spin_lock_irq(&tp->view.lock); |
1da177e4 | 1865 | if (L_ICANON(tty)) { |
7ef21387 | 1866 | new = L_ECHO(tty) ? TF_INPUT : TF_INPUTN; |
1da177e4 LT |
1867 | if (new != tp->inattr) { |
1868 | tp->inattr = new; | |
76485078 | 1869 | tty3270_update_prompt(tp, ""); |
1da177e4 LT |
1870 | tty3270_set_timer(tp, 1); |
1871 | } | |
1872 | } | |
c17fe081 | 1873 | spin_unlock_irq(&tp->view.lock); |
1da177e4 LT |
1874 | } |
1875 | ||
1876 | /* | |
1877 | * Disable reading from a 3270 tty | |
1878 | */ | |
e6d98bb8 | 1879 | static void tty3270_throttle(struct tty_struct *tty) |
1da177e4 LT |
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 | */ | |
e6d98bb8 | 1892 | static void tty3270_unthrottle(struct tty_struct *tty) |
1da177e4 LT |
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, 1); | |
1902 | } | |
1903 | ||
1904 | /* | |
1905 | * Hang up the tty device. | |
1906 | */ | |
e6d98bb8 | 1907 | static void tty3270_hangup(struct tty_struct *tty) |
1da177e4 | 1908 | { |
0c756914 MS |
1909 | struct tty3270 *tp; |
1910 | ||
1911 | tp = tty->driver_data; | |
1912 | if (!tp) | |
1913 | return; | |
c17fe081 | 1914 | spin_lock_irq(&tp->view.lock); |
f8674930 SS |
1915 | tp->cx = 0; |
1916 | tp->cy = 0; | |
1917 | tp->saved_cx = 0; | |
1918 | tp->saved_cy = 0; | |
c2e9375e SS |
1919 | tty3270_reset_attributes(&tp->attributes); |
1920 | tty3270_reset_attributes(&tp->saved_attributes); | |
0c756914 | 1921 | tty3270_blank_screen(tp); |
0c756914 | 1922 | tp->update_flags = TTY_UPDATE_ALL; |
c17fe081 | 1923 | spin_unlock_irq(&tp->view.lock); |
0c756914 | 1924 | tty3270_set_timer(tp, 1); |
1da177e4 LT |
1925 | } |
1926 | ||
e6d98bb8 | 1927 | static void tty3270_wait_until_sent(struct tty_struct *tty, int timeout) |
1da177e4 LT |
1928 | { |
1929 | } | |
1930 | ||
65c56e07 HC |
1931 | static int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd, |
1932 | unsigned long arg) | |
1da177e4 LT |
1933 | { |
1934 | struct tty3270 *tp; | |
1935 | ||
1936 | tp = tty->driver_data; | |
1937 | if (!tp) | |
1938 | return -ENODEV; | |
18900ca6 | 1939 | if (tty_io_error(tty)) |
1da177e4 | 1940 | return -EIO; |
65c56e07 | 1941 | return kbd_ioctl(tp->kbd, cmd, arg); |
1da177e4 LT |
1942 | } |
1943 | ||
9d4bfd41 | 1944 | #ifdef CONFIG_COMPAT |
65c56e07 HC |
1945 | static long tty3270_compat_ioctl(struct tty_struct *tty, |
1946 | unsigned int cmd, unsigned long arg) | |
9d4bfd41 AB |
1947 | { |
1948 | struct tty3270 *tp; | |
1949 | ||
1950 | tp = tty->driver_data; | |
1951 | if (!tp) | |
1952 | return -ENODEV; | |
18900ca6 | 1953 | if (tty_io_error(tty)) |
9d4bfd41 | 1954 | return -EIO; |
65c56e07 | 1955 | return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg)); |
9d4bfd41 AB |
1956 | } |
1957 | #endif | |
1958 | ||
b68e31d0 | 1959 | static const struct tty_operations tty3270_ops = { |
20cda6f2 JS |
1960 | .install = tty3270_install, |
1961 | .cleanup = tty3270_cleanup, | |
736c9fd2 | 1962 | .open = tty3270_open, |
1da177e4 LT |
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, | |
1da177e4 LT |
1968 | .throttle = tty3270_throttle, |
1969 | .unthrottle = tty3270_unthrottle, | |
1970 | .hangup = tty3270_hangup, | |
1971 | .wait_until_sent = tty3270_wait_until_sent, | |
1972 | .ioctl = tty3270_ioctl, | |
9d4bfd41 AB |
1973 | #ifdef CONFIG_COMPAT |
1974 | .compat_ioctl = tty3270_compat_ioctl, | |
1975 | #endif | |
1da177e4 LT |
1976 | .set_termios = tty3270_set_termios |
1977 | }; | |
1978 | ||
63df41d6 | 1979 | static void tty3270_create_cb(int minor) |
c95571e6 | 1980 | { |
1fcbba3d | 1981 | tty_register_device(tty3270_driver, minor - RAW3270_FIRSTMINOR, NULL); |
c95571e6 MS |
1982 | } |
1983 | ||
63df41d6 | 1984 | static void tty3270_destroy_cb(int minor) |
c95571e6 | 1985 | { |
1fcbba3d | 1986 | tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR); |
c95571e6 MS |
1987 | } |
1988 | ||
7ef21387 | 1989 | static struct raw3270_notifier tty3270_notifier = { |
c95571e6 MS |
1990 | .create = tty3270_create_cb, |
1991 | .destroy = tty3270_destroy_cb, | |
1992 | }; | |
1993 | ||
1da177e4 LT |
1994 | /* |
1995 | * 3270 tty registration code called from tty_init(). | |
1996 | * Most kernel services (incl. kmalloc) are available at this poimt. | |
1997 | */ | |
2b67fc46 | 1998 | static int __init tty3270_init(void) |
1da177e4 LT |
1999 | { |
2000 | struct tty_driver *driver; | |
2001 | int ret; | |
2002 | ||
c95571e6 MS |
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(driver)) | |
2008 | return PTR_ERR(driver); | |
1da177e4 LT |
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 | */ | |
c95571e6 MS |
2015 | driver->driver_name = "tty3270"; |
2016 | driver->name = "3270/tty"; | |
1da177e4 | 2017 | driver->major = IBM_TTY3270_MAJOR; |
1fcbba3d MS |
2018 | driver->minor_start = RAW3270_FIRSTMINOR; |
2019 | driver->name_base = RAW3270_FIRSTMINOR; | |
1da177e4 LT |
2020 | driver->type = TTY_DRIVER_TYPE_SYSTEM; |
2021 | driver->subtype = SYSTEM_TYPE_TTY; | |
2022 | driver->init_termios = tty_std_termios; | |
1da177e4 LT |
2023 | tty_set_operations(driver, &tty3270_ops); |
2024 | ret = tty_register_driver(driver); | |
2025 | if (ret) { | |
9f90a4dd | 2026 | tty_driver_kref_put(driver); |
1da177e4 LT |
2027 | return ret; |
2028 | } | |
2029 | tty3270_driver = driver; | |
c95571e6 | 2030 | raw3270_register_notifier(&tty3270_notifier); |
1da177e4 LT |
2031 | return 0; |
2032 | } | |
2033 | ||
e6d98bb8 | 2034 | static void __exit tty3270_exit(void) |
1da177e4 LT |
2035 | { |
2036 | struct tty_driver *driver; | |
2037 | ||
c95571e6 | 2038 | raw3270_unregister_notifier(&tty3270_notifier); |
1da177e4 | 2039 | driver = tty3270_driver; |
d2c993d8 | 2040 | tty3270_driver = NULL; |
1da177e4 | 2041 | tty_unregister_driver(driver); |
9f90a4dd | 2042 | tty_driver_kref_put(driver); |
1da177e4 LT |
2043 | tty3270_del_views(); |
2044 | } | |
2045 | ||
c17fe081 | 2046 | #if IS_ENABLED(CONFIG_TN3270_CONSOLE) |
9cab4f7d HC |
2047 | |
2048 | static struct tty3270 *condev; | |
2049 | ||
c17fe081 SS |
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; | |
e17934c1 | 2055 | u8 c; |
c17fe081 SS |
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; | |
9975fde0 | 2104 | int rc; |
c17fe081 SS |
2105 | |
2106 | tp = condev; | |
2107 | if (!tp->view.dev) | |
2108 | return NOTIFY_DONE; | |
9975fde0 SS |
2109 | if (!raw3270_view_lock_unavailable(&tp->view)) { |
2110 | rc = raw3270_activate_view(&tp->view); | |
2111 | if (rc) | |
2112 | return NOTIFY_DONE; | |
2113 | } | |
c17fe081 SS |
2114 | if (!spin_trylock_irqsave(&tp->view.lock, flags)) |
2115 | return NOTIFY_DONE; | |
2116 | con3270_wait_write(tp); | |
2117 | tp->nr_up = 0; | |
ba5c2e2a | 2118 | tp->update_flags = TTY_UPDATE_ALL; |
c17fe081 SS |
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 | ||
1da177e4 LT |
2188 | MODULE_LICENSE("GPL"); |
2189 | MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR); | |
2190 | ||
2191 | module_init(tty3270_init); | |
2192 | module_exit(tty3270_exit); |