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