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