diff options
Diffstat (limited to 'drivers/s390/char/tty3270.c')
-rw-r--r-- | drivers/s390/char/tty3270.c | 1836 |
1 files changed, 1836 insertions, 0 deletions
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c new file mode 100644 index 000000000000..7db5ebce7f0f --- /dev/null +++ b/drivers/s390/char/tty3270.c | |||
@@ -0,0 +1,1836 @@ | |||
1 | /* | ||
2 | * drivers/s390/char/tty3270.c | ||
3 | * IBM/3270 Driver - tty functions. | ||
4 | * | ||
5 | * Author(s): | ||
6 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) | ||
7 | * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
8 | * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/kdev_t.h> | ||
15 | #include <linux/tty.h> | ||
16 | #include <linux/vt_kern.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/console.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | |||
21 | #include <linux/slab.h> | ||
22 | #include <linux/bootmem.h> | ||
23 | |||
24 | #include <asm/ccwdev.h> | ||
25 | #include <asm/cio.h> | ||
26 | #include <asm/ebcdic.h> | ||
27 | #include <asm/uaccess.h> | ||
28 | |||
29 | |||
30 | #include "raw3270.h" | ||
31 | #include "keyboard.h" | ||
32 | |||
33 | #define TTY3270_CHAR_BUF_SIZE 256 | ||
34 | #define TTY3270_OUTPUT_BUFFER_SIZE 1024 | ||
35 | #define TTY3270_STRING_PAGES 5 | ||
36 | |||
37 | struct tty_driver *tty3270_driver; | ||
38 | static int tty3270_max_index; | ||
39 | |||
40 | struct raw3270_fn tty3270_fn; | ||
41 | |||
42 | struct tty3270_cell { | ||
43 | unsigned char character; | ||
44 | unsigned char highlight; | ||
45 | unsigned char f_color; | ||
46 | }; | ||
47 | |||
48 | struct tty3270_line { | ||
49 | struct tty3270_cell *cells; | ||
50 | int len; | ||
51 | }; | ||
52 | |||
53 | #define ESCAPE_NPAR 8 | ||
54 | |||
55 | /* | ||
56 | * The main tty view data structure. | ||
57 | * FIXME: | ||
58 | * 1) describe line orientation & lines list concept against screen | ||
59 | * 2) describe conversion of screen to lines | ||
60 | * 3) describe line format. | ||
61 | */ | ||
62 | struct tty3270 { | ||
63 | struct raw3270_view view; | ||
64 | struct tty_struct *tty; /* Pointer to tty structure */ | ||
65 | void **freemem_pages; /* Array of pages used for freemem. */ | ||
66 | struct list_head freemem; /* List of free memory for strings. */ | ||
67 | |||
68 | /* Output stuff. */ | ||
69 | struct list_head lines; /* List of lines. */ | ||
70 | struct list_head update; /* List of lines to update. */ | ||
71 | unsigned char wcc; /* Write control character. */ | ||
72 | int nr_lines; /* # lines in list. */ | ||
73 | int nr_up; /* # lines up in history. */ | ||
74 | unsigned long update_flags; /* Update indication bits. */ | ||
75 | struct string *status; /* Lower right of display. */ | ||
76 | struct raw3270_request *write; /* Single write request. */ | ||
77 | struct timer_list timer; /* Output delay timer. */ | ||
78 | |||
79 | /* Current tty screen. */ | ||
80 | unsigned int cx, cy; /* Current output position. */ | ||
81 | unsigned int highlight; /* Blink/reverse/underscore */ | ||
82 | unsigned int f_color; /* Foreground color */ | ||
83 | struct tty3270_line *screen; | ||
84 | |||
85 | /* Input stuff. */ | ||
86 | struct string *prompt; /* Output string for input area. */ | ||
87 | struct string *input; /* Input string for read request. */ | ||
88 | struct raw3270_request *read; /* Single read request. */ | ||
89 | struct raw3270_request *kreset; /* Single keyboard reset request. */ | ||
90 | unsigned char inattr; /* Visible/invisible input. */ | ||
91 | int throttle, attn; /* tty throttle/unthrottle. */ | ||
92 | struct tasklet_struct readlet; /* Tasklet to issue read request. */ | ||
93 | struct kbd_data *kbd; /* key_maps stuff. */ | ||
94 | |||
95 | /* Escape sequence parsing. */ | ||
96 | int esc_state, esc_ques, esc_npar; | ||
97 | int esc_par[ESCAPE_NPAR]; | ||
98 | unsigned int saved_cx, saved_cy; | ||
99 | unsigned int saved_highlight, saved_f_color; | ||
100 | |||
101 | /* Command recalling. */ | ||
102 | struct list_head rcl_lines; /* List of recallable lines. */ | ||
103 | struct list_head *rcl_walk; /* Point in rcl_lines list. */ | ||
104 | int rcl_nr, rcl_max; /* Number/max number of rcl_lines. */ | ||
105 | |||
106 | /* Character array for put_char/flush_chars. */ | ||
107 | unsigned int char_count; | ||
108 | char char_buf[TTY3270_CHAR_BUF_SIZE]; | ||
109 | }; | ||
110 | |||
111 | /* tty3270->update_flags. See tty3270_update for details. */ | ||
112 | #define TTY_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */ | ||
113 | #define TTY_UPDATE_LIST 2 /* Update lines in tty3270->update. */ | ||
114 | #define TTY_UPDATE_INPUT 4 /* Update input line. */ | ||
115 | #define TTY_UPDATE_STATUS 8 /* Update status line. */ | ||
116 | #define TTY_UPDATE_ALL 15 | ||
117 | |||
118 | static void tty3270_update(struct tty3270 *); | ||
119 | |||
120 | /* | ||
121 | * Setup timeout for a device. On timeout trigger an update. | ||
122 | */ | ||
123 | void | ||
124 | tty3270_set_timer(struct tty3270 *tp, int expires) | ||
125 | { | ||
126 | if (expires == 0) { | ||
127 | if (timer_pending(&tp->timer) && del_timer(&tp->timer)) | ||
128 | raw3270_put_view(&tp->view); | ||
129 | return; | ||
130 | } | ||
131 | if (timer_pending(&tp->timer) && | ||
132 | mod_timer(&tp->timer, jiffies + expires)) | ||
133 | return; | ||
134 | raw3270_get_view(&tp->view); | ||
135 | tp->timer.function = (void (*)(unsigned long)) tty3270_update; | ||
136 | tp->timer.data = (unsigned long) tp; | ||
137 | tp->timer.expires = jiffies + expires; | ||
138 | add_timer(&tp->timer); | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * The input line are the two last lines of the screen. | ||
143 | */ | ||
144 | static void | ||
145 | tty3270_update_prompt(struct tty3270 *tp, char *input, int count) | ||
146 | { | ||
147 | struct string *line; | ||
148 | unsigned int off; | ||
149 | |||
150 | line = tp->prompt; | ||
151 | if (count != 0) | ||
152 | line->string[5] = TF_INMDT; | ||
153 | else | ||
154 | line->string[5] = tp->inattr; | ||
155 | if (count > tp->view.cols * 2 - 11) | ||
156 | count = tp->view.cols * 2 - 11; | ||
157 | memcpy(line->string + 6, input, count); | ||
158 | line->string[6 + count] = TO_IC; | ||
159 | /* Clear to end of input line. */ | ||
160 | if (count < tp->view.cols * 2 - 11) { | ||
161 | line->string[7 + count] = TO_RA; | ||
162 | line->string[10 + count] = 0; | ||
163 | off = tp->view.cols * tp->view.rows - 9; | ||
164 | raw3270_buffer_address(tp->view.dev, line->string+count+8, off); | ||
165 | line->len = 11 + count; | ||
166 | } else | ||
167 | line->len = 7 + count; | ||
168 | tp->update_flags |= TTY_UPDATE_INPUT; | ||
169 | } | ||
170 | |||
171 | static void | ||
172 | tty3270_create_prompt(struct tty3270 *tp) | ||
173 | { | ||
174 | static const unsigned char blueprint[] = | ||
175 | { TO_SBA, 0, 0, 0x6e, TO_SF, TF_INPUT, | ||
176 | /* empty input string */ | ||
177 | TO_IC, TO_RA, 0, 0, 0 }; | ||
178 | struct string *line; | ||
179 | unsigned int offset; | ||
180 | |||
181 | line = alloc_string(&tp->freemem, | ||
182 | sizeof(blueprint) + tp->view.cols * 2 - 9); | ||
183 | tp->prompt = line; | ||
184 | tp->inattr = TF_INPUT; | ||
185 | /* Copy blueprint to status line */ | ||
186 | memcpy(line->string, blueprint, sizeof(blueprint)); | ||
187 | line->len = sizeof(blueprint); | ||
188 | /* Set output offsets. */ | ||
189 | offset = tp->view.cols * (tp->view.rows - 2); | ||
190 | raw3270_buffer_address(tp->view.dev, line->string + 1, offset); | ||
191 | offset = tp->view.cols * tp->view.rows - 9; | ||
192 | raw3270_buffer_address(tp->view.dev, line->string + 8, offset); | ||
193 | |||
194 | /* Allocate input string for reading. */ | ||
195 | tp->input = alloc_string(&tp->freemem, tp->view.cols * 2 - 9 + 6); | ||
196 | } | ||
197 | |||
198 | /* | ||
199 | * The status line is the last line of the screen. It shows the string | ||
200 | * "Running"/"Holding" in the lower right corner of the screen. | ||
201 | */ | ||
202 | static void | ||
203 | tty3270_update_status(struct tty3270 * tp) | ||
204 | { | ||
205 | char *str; | ||
206 | |||
207 | str = (tp->nr_up != 0) ? "History" : "Running"; | ||
208 | memcpy(tp->status->string + 8, str, 7); | ||
209 | codepage_convert(tp->view.ascebc, tp->status->string + 8, 7); | ||
210 | tp->update_flags |= TTY_UPDATE_STATUS; | ||
211 | } | ||
212 | |||
213 | static void | ||
214 | tty3270_create_status(struct tty3270 * tp) | ||
215 | { | ||
216 | static const unsigned char blueprint[] = | ||
217 | { TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, TAC_GREEN, | ||
218 | 0, 0, 0, 0, 0, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, | ||
219 | TAC_RESET }; | ||
220 | struct string *line; | ||
221 | unsigned int offset; | ||
222 | |||
223 | line = alloc_string(&tp->freemem,sizeof(blueprint)); | ||
224 | tp->status = line; | ||
225 | /* Copy blueprint to status line */ | ||
226 | memcpy(line->string, blueprint, sizeof(blueprint)); | ||
227 | /* Set address to start of status string (= last 9 characters). */ | ||
228 | offset = tp->view.cols * tp->view.rows - 9; | ||
229 | raw3270_buffer_address(tp->view.dev, line->string + 1, offset); | ||
230 | } | ||
231 | |||
232 | /* | ||
233 | * Set output offsets to 3270 datastream fragment of a tty string. | ||
234 | * (TO_SBA offset at the start and TO_RA offset at the end of the string) | ||
235 | */ | ||
236 | static void | ||
237 | tty3270_update_string(struct tty3270 *tp, struct string *line, int nr) | ||
238 | { | ||
239 | unsigned char *cp; | ||
240 | |||
241 | raw3270_buffer_address(tp->view.dev, line->string + 1, | ||
242 | tp->view.cols * nr); | ||
243 | cp = line->string + line->len - 4; | ||
244 | if (*cp == TO_RA) | ||
245 | raw3270_buffer_address(tp->view.dev, cp + 1, | ||
246 | tp->view.cols * (nr + 1)); | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * Rebuild update list to print all lines. | ||
251 | */ | ||
252 | static void | ||
253 | tty3270_rebuild_update(struct tty3270 *tp) | ||
254 | { | ||
255 | struct string *s, *n; | ||
256 | int line, nr_up; | ||
257 | |||
258 | /* | ||
259 | * Throw away update list and create a new one, | ||
260 | * containing all lines that will fit on the screen. | ||
261 | */ | ||
262 | list_for_each_entry_safe(s, n, &tp->update, update) | ||
263 | list_del_init(&s->update); | ||
264 | line = tp->view.rows - 3; | ||
265 | nr_up = tp->nr_up; | ||
266 | list_for_each_entry_reverse(s, &tp->lines, list) { | ||
267 | if (nr_up > 0) { | ||
268 | nr_up--; | ||
269 | continue; | ||
270 | } | ||
271 | tty3270_update_string(tp, s, line); | ||
272 | list_add(&s->update, &tp->update); | ||
273 | if (--line < 0) | ||
274 | break; | ||
275 | } | ||
276 | tp->update_flags |= TTY_UPDATE_LIST; | ||
277 | } | ||
278 | |||
279 | /* | ||
280 | * Alloc string for size bytes. If there is not enough room in | ||
281 | * freemem, free strings until there is room. | ||
282 | */ | ||
283 | static struct string * | ||
284 | tty3270_alloc_string(struct tty3270 *tp, size_t size) | ||
285 | { | ||
286 | struct string *s, *n; | ||
287 | |||
288 | s = alloc_string(&tp->freemem, size); | ||
289 | if (s) | ||
290 | return s; | ||
291 | list_for_each_entry_safe(s, n, &tp->lines, list) { | ||
292 | BUG_ON(tp->nr_lines <= tp->view.rows - 2); | ||
293 | list_del(&s->list); | ||
294 | if (!list_empty(&s->update)) | ||
295 | list_del(&s->update); | ||
296 | tp->nr_lines--; | ||
297 | if (free_string(&tp->freemem, s) >= size) | ||
298 | break; | ||
299 | } | ||
300 | s = alloc_string(&tp->freemem, size); | ||
301 | BUG_ON(!s); | ||
302 | if (tp->nr_up != 0 && | ||
303 | tp->nr_up + tp->view.rows - 2 >= tp->nr_lines) { | ||
304 | tp->nr_up = tp->nr_lines - tp->view.rows + 2; | ||
305 | tty3270_rebuild_update(tp); | ||
306 | tty3270_update_status(tp); | ||
307 | } | ||
308 | return s; | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * Add an empty line to the list. | ||
313 | */ | ||
314 | static void | ||
315 | tty3270_blank_line(struct tty3270 *tp) | ||
316 | { | ||
317 | static const unsigned char blueprint[] = | ||
318 | { TO_SBA, 0, 0, TO_SA, TAT_EXTHI, TAX_RESET, | ||
319 | TO_SA, TAT_COLOR, TAC_RESET, TO_RA, 0, 0, 0 }; | ||
320 | struct string *s; | ||
321 | |||
322 | s = tty3270_alloc_string(tp, sizeof(blueprint)); | ||
323 | memcpy(s->string, blueprint, sizeof(blueprint)); | ||
324 | s->len = sizeof(blueprint); | ||
325 | list_add_tail(&s->list, &tp->lines); | ||
326 | tp->nr_lines++; | ||
327 | if (tp->nr_up != 0) | ||
328 | tp->nr_up++; | ||
329 | } | ||
330 | |||
331 | /* | ||
332 | * Write request completion callback. | ||
333 | */ | ||
334 | static void | ||
335 | tty3270_write_callback(struct raw3270_request *rq, void *data) | ||
336 | { | ||
337 | struct tty3270 *tp; | ||
338 | |||
339 | tp = (struct tty3270 *) rq->view; | ||
340 | if (rq->rc != 0) { | ||
341 | /* Write wasn't successfull. Refresh all. */ | ||
342 | tty3270_rebuild_update(tp); | ||
343 | tp->update_flags = TTY_UPDATE_ALL; | ||
344 | tty3270_set_timer(tp, 1); | ||
345 | } | ||
346 | raw3270_request_reset(rq); | ||
347 | xchg(&tp->write, rq); | ||
348 | } | ||
349 | |||
350 | /* | ||
351 | * Update 3270 display. | ||
352 | */ | ||
353 | static void | ||
354 | tty3270_update(struct tty3270 *tp) | ||
355 | { | ||
356 | static char invalid_sba[2] = { 0xff, 0xff }; | ||
357 | struct raw3270_request *wrq; | ||
358 | unsigned long updated; | ||
359 | struct string *s, *n; | ||
360 | char *sba, *str; | ||
361 | int rc, len; | ||
362 | |||
363 | wrq = xchg(&tp->write, 0); | ||
364 | if (!wrq) { | ||
365 | tty3270_set_timer(tp, 1); | ||
366 | return; | ||
367 | } | ||
368 | |||
369 | spin_lock(&tp->view.lock); | ||
370 | updated = 0; | ||
371 | if (tp->update_flags & TTY_UPDATE_ERASE) { | ||
372 | /* Use erase write alternate to erase display. */ | ||
373 | raw3270_request_set_cmd(wrq, TC_EWRITEA); | ||
374 | updated |= TTY_UPDATE_ERASE; | ||
375 | } else | ||
376 | raw3270_request_set_cmd(wrq, TC_WRITE); | ||
377 | |||
378 | raw3270_request_add_data(wrq, &tp->wcc, 1); | ||
379 | tp->wcc = TW_NONE; | ||
380 | |||
381 | /* | ||
382 | * Update status line. | ||
383 | */ | ||
384 | if (tp->update_flags & TTY_UPDATE_STATUS) | ||
385 | if (raw3270_request_add_data(wrq, tp->status->string, | ||
386 | tp->status->len) == 0) | ||
387 | updated |= TTY_UPDATE_STATUS; | ||
388 | |||
389 | /* | ||
390 | * Write input line. | ||
391 | */ | ||
392 | if (tp->update_flags & TTY_UPDATE_INPUT) | ||
393 | if (raw3270_request_add_data(wrq, tp->prompt->string, | ||
394 | tp->prompt->len) == 0) | ||
395 | updated |= TTY_UPDATE_INPUT; | ||
396 | |||
397 | sba = invalid_sba; | ||
398 | |||
399 | if (tp->update_flags & TTY_UPDATE_LIST) { | ||
400 | /* Write strings in the update list to the screen. */ | ||
401 | list_for_each_entry_safe(s, n, &tp->update, update) { | ||
402 | str = s->string; | ||
403 | len = s->len; | ||
404 | /* | ||
405 | * Skip TO_SBA at the start of the string if the | ||
406 | * last output position matches the start address | ||
407 | * of this line. | ||
408 | */ | ||
409 | if (s->string[1] == sba[0] && s->string[2] == sba[1]) | ||
410 | str += 3, len -= 3; | ||
411 | if (raw3270_request_add_data(wrq, str, len) != 0) | ||
412 | break; | ||
413 | list_del_init(&s->update); | ||
414 | sba = s->string + s->len - 3; | ||
415 | } | ||
416 | if (list_empty(&tp->update)) | ||
417 | updated |= TTY_UPDATE_LIST; | ||
418 | } | ||
419 | wrq->callback = tty3270_write_callback; | ||
420 | rc = raw3270_start(&tp->view, wrq); | ||
421 | if (rc == 0) { | ||
422 | tp->update_flags &= ~updated; | ||
423 | if (tp->update_flags) | ||
424 | tty3270_set_timer(tp, 1); | ||
425 | } else { | ||
426 | raw3270_request_reset(wrq); | ||
427 | xchg(&tp->write, wrq); | ||
428 | } | ||
429 | spin_unlock(&tp->view.lock); | ||
430 | raw3270_put_view(&tp->view); | ||
431 | } | ||
432 | |||
433 | /* | ||
434 | * Command recalling. | ||
435 | */ | ||
436 | static void | ||
437 | tty3270_rcl_add(struct tty3270 *tp, char *input, int len) | ||
438 | { | ||
439 | struct string *s; | ||
440 | |||
441 | tp->rcl_walk = 0; | ||
442 | if (len <= 0) | ||
443 | return; | ||
444 | if (tp->rcl_nr >= tp->rcl_max) { | ||
445 | s = list_entry(tp->rcl_lines.next, struct string, list); | ||
446 | list_del(&s->list); | ||
447 | free_string(&tp->freemem, s); | ||
448 | tp->rcl_nr--; | ||
449 | } | ||
450 | s = tty3270_alloc_string(tp, len); | ||
451 | memcpy(s->string, input, len); | ||
452 | list_add_tail(&s->list, &tp->rcl_lines); | ||
453 | tp->rcl_nr++; | ||
454 | } | ||
455 | |||
456 | static void | ||
457 | tty3270_rcl_backward(struct kbd_data *kbd) | ||
458 | { | ||
459 | struct tty3270 *tp; | ||
460 | struct string *s; | ||
461 | |||
462 | tp = kbd->tty->driver_data; | ||
463 | spin_lock_bh(&tp->view.lock); | ||
464 | if (tp->inattr == TF_INPUT) { | ||
465 | if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines) | ||
466 | tp->rcl_walk = tp->rcl_walk->prev; | ||
467 | else if (!list_empty(&tp->rcl_lines)) | ||
468 | tp->rcl_walk = tp->rcl_lines.prev; | ||
469 | s = tp->rcl_walk ? | ||
470 | list_entry(tp->rcl_walk, struct string, list) : 0; | ||
471 | if (tp->rcl_walk) { | ||
472 | s = list_entry(tp->rcl_walk, struct string, list); | ||
473 | tty3270_update_prompt(tp, s->string, s->len); | ||
474 | } else | ||
475 | tty3270_update_prompt(tp, 0, 0); | ||
476 | tty3270_set_timer(tp, 1); | ||
477 | } | ||
478 | spin_unlock_bh(&tp->view.lock); | ||
479 | } | ||
480 | |||
481 | /* | ||
482 | * Deactivate tty view. | ||
483 | */ | ||
484 | static void | ||
485 | tty3270_exit_tty(struct kbd_data *kbd) | ||
486 | { | ||
487 | struct tty3270 *tp; | ||
488 | |||
489 | tp = kbd->tty->driver_data; | ||
490 | raw3270_deactivate_view(&tp->view); | ||
491 | } | ||
492 | |||
493 | /* | ||
494 | * Scroll forward in history. | ||
495 | */ | ||
496 | static void | ||
497 | tty3270_scroll_forward(struct kbd_data *kbd) | ||
498 | { | ||
499 | struct tty3270 *tp; | ||
500 | int nr_up; | ||
501 | |||
502 | tp = kbd->tty->driver_data; | ||
503 | spin_lock_bh(&tp->view.lock); | ||
504 | nr_up = tp->nr_up - tp->view.rows + 2; | ||
505 | if (nr_up < 0) | ||
506 | nr_up = 0; | ||
507 | if (nr_up != tp->nr_up) { | ||
508 | tp->nr_up = nr_up; | ||
509 | tty3270_rebuild_update(tp); | ||
510 | tty3270_update_status(tp); | ||
511 | tty3270_set_timer(tp, 1); | ||
512 | } | ||
513 | spin_unlock_bh(&tp->view.lock); | ||
514 | } | ||
515 | |||
516 | /* | ||
517 | * Scroll backward in history. | ||
518 | */ | ||
519 | static void | ||
520 | tty3270_scroll_backward(struct kbd_data *kbd) | ||
521 | { | ||
522 | struct tty3270 *tp; | ||
523 | int nr_up; | ||
524 | |||
525 | tp = kbd->tty->driver_data; | ||
526 | spin_lock_bh(&tp->view.lock); | ||
527 | nr_up = tp->nr_up + tp->view.rows - 2; | ||
528 | if (nr_up + tp->view.rows - 2 > tp->nr_lines) | ||
529 | nr_up = tp->nr_lines - tp->view.rows + 2; | ||
530 | if (nr_up != tp->nr_up) { | ||
531 | tp->nr_up = nr_up; | ||
532 | tty3270_rebuild_update(tp); | ||
533 | tty3270_update_status(tp); | ||
534 | tty3270_set_timer(tp, 1); | ||
535 | } | ||
536 | spin_unlock_bh(&tp->view.lock); | ||
537 | } | ||
538 | |||
539 | /* | ||
540 | * Pass input line to tty. | ||
541 | */ | ||
542 | static void | ||
543 | tty3270_read_tasklet(struct raw3270_request *rrq) | ||
544 | { | ||
545 | static char kreset_data = TW_KR; | ||
546 | struct tty3270 *tp; | ||
547 | char *input; | ||
548 | int len; | ||
549 | |||
550 | tp = (struct tty3270 *) rrq->view; | ||
551 | spin_lock_bh(&tp->view.lock); | ||
552 | /* | ||
553 | * Two AID keys are special: For 0x7d (enter) the input line | ||
554 | * has to be emitted to the tty and for 0x6d the screen | ||
555 | * needs to be redrawn. | ||
556 | */ | ||
557 | input = 0; | ||
558 | len = 0; | ||
559 | if (tp->input->string[0] == 0x7d) { | ||
560 | /* Enter: write input to tty. */ | ||
561 | input = tp->input->string + 6; | ||
562 | len = tp->input->len - 6 - rrq->rescnt; | ||
563 | if (tp->inattr != TF_INPUTN) | ||
564 | tty3270_rcl_add(tp, input, len); | ||
565 | if (tp->nr_up > 0) { | ||
566 | tp->nr_up = 0; | ||
567 | tty3270_rebuild_update(tp); | ||
568 | tty3270_update_status(tp); | ||
569 | } | ||
570 | /* Clear input area. */ | ||
571 | tty3270_update_prompt(tp, 0, 0); | ||
572 | tty3270_set_timer(tp, 1); | ||
573 | } else if (tp->input->string[0] == 0x6d) { | ||
574 | /* Display has been cleared. Redraw. */ | ||
575 | tty3270_rebuild_update(tp); | ||
576 | tp->update_flags = TTY_UPDATE_ALL; | ||
577 | tty3270_set_timer(tp, 1); | ||
578 | } | ||
579 | spin_unlock_bh(&tp->view.lock); | ||
580 | |||
581 | /* Start keyboard reset command. */ | ||
582 | raw3270_request_reset(tp->kreset); | ||
583 | raw3270_request_set_cmd(tp->kreset, TC_WRITE); | ||
584 | raw3270_request_add_data(tp->kreset, &kreset_data, 1); | ||
585 | raw3270_start(&tp->view, tp->kreset); | ||
586 | |||
587 | /* Emit input string. */ | ||
588 | if (tp->tty) { | ||
589 | while (len-- > 0) | ||
590 | kbd_keycode(tp->kbd, *input++); | ||
591 | /* Emit keycode for AID byte. */ | ||
592 | kbd_keycode(tp->kbd, 256 + tp->input->string[0]); | ||
593 | } | ||
594 | |||
595 | raw3270_request_reset(rrq); | ||
596 | xchg(&tp->read, rrq); | ||
597 | raw3270_put_view(&tp->view); | ||
598 | } | ||
599 | |||
600 | /* | ||
601 | * Read request completion callback. | ||
602 | */ | ||
603 | static void | ||
604 | tty3270_read_callback(struct raw3270_request *rq, void *data) | ||
605 | { | ||
606 | raw3270_get_view(rq->view); | ||
607 | /* Schedule tasklet to pass input to tty. */ | ||
608 | tasklet_schedule(&((struct tty3270 *) rq->view)->readlet); | ||
609 | } | ||
610 | |||
611 | /* | ||
612 | * Issue a read request. Call with device lock. | ||
613 | */ | ||
614 | static void | ||
615 | tty3270_issue_read(struct tty3270 *tp, int lock) | ||
616 | { | ||
617 | struct raw3270_request *rrq; | ||
618 | int rc; | ||
619 | |||
620 | rrq = xchg(&tp->read, 0); | ||
621 | if (!rrq) | ||
622 | /* Read already scheduled. */ | ||
623 | return; | ||
624 | rrq->callback = tty3270_read_callback; | ||
625 | rrq->callback_data = tp; | ||
626 | raw3270_request_set_cmd(rrq, TC_READMOD); | ||
627 | raw3270_request_set_data(rrq, tp->input->string, tp->input->len); | ||
628 | /* Issue the read modified request. */ | ||
629 | if (lock) { | ||
630 | rc = raw3270_start(&tp->view, rrq); | ||
631 | } else | ||
632 | rc = raw3270_start_irq(&tp->view, rrq); | ||
633 | if (rc) { | ||
634 | raw3270_request_reset(rrq); | ||
635 | xchg(&tp->read, rrq); | ||
636 | } | ||
637 | } | ||
638 | |||
639 | /* | ||
640 | * Switch to the tty view. | ||
641 | */ | ||
642 | static int | ||
643 | tty3270_activate(struct raw3270_view *view) | ||
644 | { | ||
645 | struct tty3270 *tp; | ||
646 | unsigned long flags; | ||
647 | |||
648 | tp = (struct tty3270 *) view; | ||
649 | spin_lock_irqsave(&tp->view.lock, flags); | ||
650 | tp->nr_up = 0; | ||
651 | tty3270_rebuild_update(tp); | ||
652 | tty3270_update_status(tp); | ||
653 | tp->update_flags = TTY_UPDATE_ALL; | ||
654 | tty3270_set_timer(tp, 1); | ||
655 | spin_unlock_irqrestore(&tp->view.lock, flags); | ||
656 | start_tty(tp->tty); | ||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | static void | ||
661 | tty3270_deactivate(struct raw3270_view *view) | ||
662 | { | ||
663 | struct tty3270 *tp; | ||
664 | |||
665 | tp = (struct tty3270 *) view; | ||
666 | if (tp && tp->tty) | ||
667 | stop_tty(tp->tty); | ||
668 | } | ||
669 | |||
670 | static int | ||
671 | tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) | ||
672 | { | ||
673 | /* Handle ATTN. Schedule tasklet to read aid. */ | ||
674 | if (irb->scsw.dstat & DEV_STAT_ATTENTION) { | ||
675 | if (!tp->throttle) | ||
676 | tty3270_issue_read(tp, 0); | ||
677 | else | ||
678 | tp->attn = 1; | ||
679 | } | ||
680 | |||
681 | if (rq) { | ||
682 | if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) | ||
683 | rq->rc = -EIO; | ||
684 | else | ||
685 | /* Normal end. Copy residual count. */ | ||
686 | rq->rescnt = irb->scsw.count; | ||
687 | } | ||
688 | return RAW3270_IO_DONE; | ||
689 | } | ||
690 | |||
691 | /* | ||
692 | * Allocate tty3270 structure. | ||
693 | */ | ||
694 | static struct tty3270 * | ||
695 | tty3270_alloc_view(void) | ||
696 | { | ||
697 | struct tty3270 *tp; | ||
698 | int pages; | ||
699 | |||
700 | tp = kmalloc(sizeof(struct tty3270),GFP_KERNEL); | ||
701 | if (!tp) | ||
702 | goto out_err; | ||
703 | memset(tp, 0, sizeof(struct tty3270)); | ||
704 | tp->freemem_pages = | ||
705 | kmalloc(sizeof(void *) * TTY3270_STRING_PAGES, GFP_KERNEL); | ||
706 | if (!tp->freemem_pages) | ||
707 | goto out_tp; | ||
708 | INIT_LIST_HEAD(&tp->freemem); | ||
709 | init_timer(&tp->timer); | ||
710 | for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) { | ||
711 | tp->freemem_pages[pages] = (void *) | ||
712 | __get_free_pages(GFP_KERNEL|GFP_DMA, 0); | ||
713 | if (!tp->freemem_pages[pages]) | ||
714 | goto out_pages; | ||
715 | add_string_memory(&tp->freemem, | ||
716 | tp->freemem_pages[pages], PAGE_SIZE); | ||
717 | } | ||
718 | tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE); | ||
719 | if (!tp->write) | ||
720 | goto out_pages; | ||
721 | tp->read = raw3270_request_alloc(0); | ||
722 | if (!tp->read) | ||
723 | goto out_write; | ||
724 | tp->kreset = raw3270_request_alloc(1); | ||
725 | if (!tp->kreset) | ||
726 | goto out_read; | ||
727 | tp->kbd = kbd_alloc(); | ||
728 | if (!tp->kbd) | ||
729 | goto out_reset; | ||
730 | return tp; | ||
731 | |||
732 | out_reset: | ||
733 | raw3270_request_free(tp->kreset); | ||
734 | out_read: | ||
735 | raw3270_request_free(tp->read); | ||
736 | out_write: | ||
737 | raw3270_request_free(tp->write); | ||
738 | out_pages: | ||
739 | while (pages--) | ||
740 | free_pages((unsigned long) tp->freemem_pages[pages], 0); | ||
741 | kfree(tp->freemem_pages); | ||
742 | out_tp: | ||
743 | kfree(tp); | ||
744 | out_err: | ||
745 | return ERR_PTR(-ENOMEM); | ||
746 | } | ||
747 | |||
748 | /* | ||
749 | * Free tty3270 structure. | ||
750 | */ | ||
751 | static void | ||
752 | tty3270_free_view(struct tty3270 *tp) | ||
753 | { | ||
754 | int pages; | ||
755 | |||
756 | kbd_free(tp->kbd); | ||
757 | raw3270_request_free(tp->kreset); | ||
758 | raw3270_request_free(tp->read); | ||
759 | raw3270_request_free(tp->write); | ||
760 | for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) | ||
761 | free_pages((unsigned long) tp->freemem_pages[pages], 0); | ||
762 | kfree(tp->freemem_pages); | ||
763 | kfree(tp); | ||
764 | } | ||
765 | |||
766 | /* | ||
767 | * Allocate tty3270 screen. | ||
768 | */ | ||
769 | static int | ||
770 | tty3270_alloc_screen(struct tty3270 *tp) | ||
771 | { | ||
772 | unsigned long size; | ||
773 | int lines; | ||
774 | |||
775 | size = sizeof(struct tty3270_line) * (tp->view.rows - 2); | ||
776 | tp->screen = kmalloc(size, GFP_KERNEL); | ||
777 | if (!tp->screen) | ||
778 | goto out_err; | ||
779 | memset(tp->screen, 0, size); | ||
780 | for (lines = 0; lines < tp->view.rows - 2; lines++) { | ||
781 | size = sizeof(struct tty3270_cell) * tp->view.cols; | ||
782 | tp->screen[lines].cells = kmalloc(size, GFP_KERNEL); | ||
783 | if (!tp->screen[lines].cells) | ||
784 | goto out_screen; | ||
785 | memset(tp->screen[lines].cells, 0, size); | ||
786 | } | ||
787 | return 0; | ||
788 | out_screen: | ||
789 | while (lines--) | ||
790 | kfree(tp->screen[lines].cells); | ||
791 | kfree(tp->screen); | ||
792 | out_err: | ||
793 | return -ENOMEM; | ||
794 | } | ||
795 | |||
796 | /* | ||
797 | * Free tty3270 screen. | ||
798 | */ | ||
799 | static void | ||
800 | tty3270_free_screen(struct tty3270 *tp) | ||
801 | { | ||
802 | int lines; | ||
803 | |||
804 | for (lines = 0; lines < tp->view.rows - 2; lines++) | ||
805 | kfree(tp->screen[lines].cells); | ||
806 | kfree(tp->screen); | ||
807 | } | ||
808 | |||
809 | /* | ||
810 | * Unlink tty3270 data structure from tty. | ||
811 | */ | ||
812 | static void | ||
813 | tty3270_release(struct raw3270_view *view) | ||
814 | { | ||
815 | struct tty3270 *tp; | ||
816 | struct tty_struct *tty; | ||
817 | |||
818 | tp = (struct tty3270 *) view; | ||
819 | tty = tp->tty; | ||
820 | if (tty) { | ||
821 | tty->driver_data = 0; | ||
822 | tp->tty = tp->kbd->tty = 0; | ||
823 | tty_hangup(tty); | ||
824 | raw3270_put_view(&tp->view); | ||
825 | } | ||
826 | } | ||
827 | |||
828 | /* | ||
829 | * Free tty3270 data structure | ||
830 | */ | ||
831 | static void | ||
832 | tty3270_free(struct raw3270_view *view) | ||
833 | { | ||
834 | tty3270_free_screen((struct tty3270 *) view); | ||
835 | tty3270_free_view((struct tty3270 *) view); | ||
836 | } | ||
837 | |||
838 | /* | ||
839 | * Delayed freeing of tty3270 views. | ||
840 | */ | ||
841 | static void | ||
842 | tty3270_del_views(void) | ||
843 | { | ||
844 | struct tty3270 *tp; | ||
845 | int i; | ||
846 | |||
847 | for (i = 0; i < tty3270_max_index; i++) { | ||
848 | tp = (struct tty3270 *) raw3270_find_view(&tty3270_fn, i); | ||
849 | if (!IS_ERR(tp)) | ||
850 | raw3270_del_view(&tp->view); | ||
851 | } | ||
852 | } | ||
853 | |||
854 | struct raw3270_fn tty3270_fn = { | ||
855 | .activate = tty3270_activate, | ||
856 | .deactivate = tty3270_deactivate, | ||
857 | .intv = (void *) tty3270_irq, | ||
858 | .release = tty3270_release, | ||
859 | .free = tty3270_free | ||
860 | }; | ||
861 | |||
862 | /* | ||
863 | * This routine is called whenever a 3270 tty is opened. | ||
864 | */ | ||
865 | static int | ||
866 | tty3270_open(struct tty_struct *tty, struct file * filp) | ||
867 | { | ||
868 | struct tty3270 *tp; | ||
869 | int i, rc; | ||
870 | |||
871 | if (tty->count > 1) | ||
872 | return 0; | ||
873 | /* Check if the tty3270 is already there. */ | ||
874 | tp = (struct tty3270 *) raw3270_find_view(&tty3270_fn, tty->index); | ||
875 | if (!IS_ERR(tp)) { | ||
876 | tty->driver_data = tp; | ||
877 | tty->winsize.ws_row = tp->view.rows - 2; | ||
878 | tty->winsize.ws_col = tp->view.cols; | ||
879 | tty->low_latency = 0; | ||
880 | tp->tty = tty; | ||
881 | tp->kbd->tty = tty; | ||
882 | tp->inattr = TF_INPUT; | ||
883 | return 0; | ||
884 | } | ||
885 | if (tty3270_max_index < tty->index + 1) | ||
886 | tty3270_max_index = tty->index + 1; | ||
887 | |||
888 | /* Quick exit if there is no device for tty->index. */ | ||
889 | if (PTR_ERR(tp) == -ENODEV) | ||
890 | return -ENODEV; | ||
891 | |||
892 | /* Allocate tty3270 structure on first open. */ | ||
893 | tp = tty3270_alloc_view(); | ||
894 | if (IS_ERR(tp)) | ||
895 | return PTR_ERR(tp); | ||
896 | |||
897 | INIT_LIST_HEAD(&tp->lines); | ||
898 | INIT_LIST_HEAD(&tp->update); | ||
899 | INIT_LIST_HEAD(&tp->rcl_lines); | ||
900 | tp->rcl_max = 20; | ||
901 | init_timer(&tp->timer); | ||
902 | tasklet_init(&tp->readlet, | ||
903 | (void (*)(unsigned long)) tty3270_read_tasklet, | ||
904 | (unsigned long) tp->read); | ||
905 | |||
906 | rc = raw3270_add_view(&tp->view, &tty3270_fn, tty->index); | ||
907 | if (rc) { | ||
908 | tty3270_free_view(tp); | ||
909 | return rc; | ||
910 | } | ||
911 | |||
912 | rc = tty3270_alloc_screen(tp); | ||
913 | if (rc) { | ||
914 | raw3270_del_view(&tp->view); | ||
915 | raw3270_put_view(&tp->view); | ||
916 | return rc; | ||
917 | } | ||
918 | |||
919 | tp->tty = tty; | ||
920 | tty->low_latency = 0; | ||
921 | tty->driver_data = tp; | ||
922 | tty->winsize.ws_row = tp->view.rows - 2; | ||
923 | tty->winsize.ws_col = tp->view.cols; | ||
924 | |||
925 | tty3270_create_prompt(tp); | ||
926 | tty3270_create_status(tp); | ||
927 | tty3270_update_status(tp); | ||
928 | |||
929 | /* Create blank line for every line in the tty output area. */ | ||
930 | for (i = 0; i < tp->view.rows - 2; i++) | ||
931 | tty3270_blank_line(tp); | ||
932 | |||
933 | tp->kbd->tty = tty; | ||
934 | tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty; | ||
935 | tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward; | ||
936 | tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward; | ||
937 | tp->kbd->fn_handler[KVAL(K_CONS)] = tty3270_rcl_backward; | ||
938 | kbd_ascebc(tp->kbd, tp->view.ascebc); | ||
939 | |||
940 | raw3270_activate_view(&tp->view); | ||
941 | return 0; | ||
942 | } | ||
943 | |||
944 | /* | ||
945 | * This routine is called when the 3270 tty is closed. We wait | ||
946 | * for the remaining request to be completed. Then we clean up. | ||
947 | */ | ||
948 | static void | ||
949 | tty3270_close(struct tty_struct *tty, struct file * filp) | ||
950 | { | ||
951 | struct tty3270 *tp; | ||
952 | |||
953 | if (tty->count > 1) | ||
954 | return; | ||
955 | tp = (struct tty3270 *) tty->driver_data; | ||
956 | if (tp) { | ||
957 | tty->driver_data = 0; | ||
958 | tp->tty = tp->kbd->tty = 0; | ||
959 | raw3270_put_view(&tp->view); | ||
960 | } | ||
961 | } | ||
962 | |||
963 | /* | ||
964 | * We always have room. | ||
965 | */ | ||
966 | static int | ||
967 | tty3270_write_room(struct tty_struct *tty) | ||
968 | { | ||
969 | return INT_MAX; | ||
970 | } | ||
971 | |||
972 | /* | ||
973 | * Insert character into the screen at the current position with the | ||
974 | * current color and highlight. This function does NOT do cursor movement. | ||
975 | */ | ||
976 | static void | ||
977 | tty3270_put_character(struct tty3270 *tp, char ch) | ||
978 | { | ||
979 | struct tty3270_line *line; | ||
980 | struct tty3270_cell *cell; | ||
981 | |||
982 | line = tp->screen + tp->cy; | ||
983 | if (line->len <= tp->cx) { | ||
984 | while (line->len < tp->cx) { | ||
985 | cell = line->cells + line->len; | ||
986 | cell->character = tp->view.ascebc[' ']; | ||
987 | cell->highlight = tp->highlight; | ||
988 | cell->f_color = tp->f_color; | ||
989 | line->len++; | ||
990 | } | ||
991 | line->len++; | ||
992 | } | ||
993 | cell = line->cells + tp->cx; | ||
994 | cell->character = tp->view.ascebc[(unsigned int) ch]; | ||
995 | cell->highlight = tp->highlight; | ||
996 | cell->f_color = tp->f_color; | ||
997 | } | ||
998 | |||
999 | /* | ||
1000 | * Convert a tty3270_line to a 3270 data fragment usable for output. | ||
1001 | */ | ||
1002 | static void | ||
1003 | tty3270_convert_line(struct tty3270 *tp, int line_nr) | ||
1004 | { | ||
1005 | struct tty3270_line *line; | ||
1006 | struct tty3270_cell *cell; | ||
1007 | struct string *s, *n; | ||
1008 | unsigned char highlight; | ||
1009 | unsigned char f_color; | ||
1010 | char *cp; | ||
1011 | int flen, i; | ||
1012 | |||
1013 | /* Determine how long the fragment will be. */ | ||
1014 | flen = 3; /* Prefix (TO_SBA). */ | ||
1015 | line = tp->screen + line_nr; | ||
1016 | flen += line->len; | ||
1017 | highlight = TAX_RESET; | ||
1018 | f_color = TAC_RESET; | ||
1019 | for (i = 0, cell = line->cells; i < line->len; i++, cell++) { | ||
1020 | if (cell->highlight != highlight) { | ||
1021 | flen += 3; /* TO_SA to switch highlight. */ | ||
1022 | highlight = cell->highlight; | ||
1023 | } | ||
1024 | if (cell->f_color != f_color) { | ||
1025 | flen += 3; /* TO_SA to switch color. */ | ||
1026 | f_color = cell->f_color; | ||
1027 | } | ||
1028 | } | ||
1029 | if (highlight != TAX_RESET) | ||
1030 | flen += 3; /* TO_SA to reset hightlight. */ | ||
1031 | if (f_color != TAC_RESET) | ||
1032 | flen += 3; /* TO_SA to reset color. */ | ||
1033 | if (line->len < tp->view.cols) | ||
1034 | flen += 4; /* Postfix (TO_RA). */ | ||
1035 | |||
1036 | /* Find the line in the list. */ | ||
1037 | i = tp->view.rows - 2 - line_nr; | ||
1038 | list_for_each_entry_reverse(s, &tp->lines, list) | ||
1039 | if (--i <= 0) | ||
1040 | break; | ||
1041 | /* | ||
1042 | * Check if the line needs to get reallocated. | ||
1043 | */ | ||
1044 | if (s->len != flen) { | ||
1045 | /* Reallocate string. */ | ||
1046 | n = tty3270_alloc_string(tp, flen); | ||
1047 | list_add(&n->list, &s->list); | ||
1048 | list_del_init(&s->list); | ||
1049 | if (!list_empty(&s->update)) | ||
1050 | list_del_init(&s->update); | ||
1051 | free_string(&tp->freemem, s); | ||
1052 | s = n; | ||
1053 | } | ||
1054 | |||
1055 | /* Write 3270 data fragment. */ | ||
1056 | cp = s->string; | ||
1057 | *cp++ = TO_SBA; | ||
1058 | *cp++ = 0; | ||
1059 | *cp++ = 0; | ||
1060 | |||
1061 | highlight = TAX_RESET; | ||
1062 | f_color = TAC_RESET; | ||
1063 | for (i = 0, cell = line->cells; i < line->len; i++, cell++) { | ||
1064 | if (cell->highlight != highlight) { | ||
1065 | *cp++ = TO_SA; | ||
1066 | *cp++ = TAT_EXTHI; | ||
1067 | *cp++ = cell->highlight; | ||
1068 | highlight = cell->highlight; | ||
1069 | } | ||
1070 | if (cell->f_color != f_color) { | ||
1071 | *cp++ = TO_SA; | ||
1072 | *cp++ = TAT_COLOR; | ||
1073 | *cp++ = cell->f_color; | ||
1074 | f_color = cell->f_color; | ||
1075 | } | ||
1076 | *cp++ = cell->character; | ||
1077 | } | ||
1078 | if (highlight != TAX_RESET) { | ||
1079 | *cp++ = TO_SA; | ||
1080 | *cp++ = TAT_EXTHI; | ||
1081 | *cp++ = TAX_RESET; | ||
1082 | } | ||
1083 | if (f_color != TAC_RESET) { | ||
1084 | *cp++ = TO_SA; | ||
1085 | *cp++ = TAT_COLOR; | ||
1086 | *cp++ = TAC_RESET; | ||
1087 | } | ||
1088 | if (line->len < tp->view.cols) { | ||
1089 | *cp++ = TO_RA; | ||
1090 | *cp++ = 0; | ||
1091 | *cp++ = 0; | ||
1092 | *cp++ = 0; | ||
1093 | } | ||
1094 | |||
1095 | if (tp->nr_up + line_nr < tp->view.rows - 2) { | ||
1096 | /* Line is currently visible on screen. */ | ||
1097 | tty3270_update_string(tp, s, line_nr); | ||
1098 | /* Add line to update list. */ | ||
1099 | if (list_empty(&s->update)) { | ||
1100 | list_add_tail(&s->update, &tp->update); | ||
1101 | tp->update_flags |= TTY_UPDATE_LIST; | ||
1102 | } | ||
1103 | } | ||
1104 | } | ||
1105 | |||
1106 | /* | ||
1107 | * Do carriage return. | ||
1108 | */ | ||
1109 | static void | ||
1110 | tty3270_cr(struct tty3270 *tp) | ||
1111 | { | ||
1112 | tp->cx = 0; | ||
1113 | } | ||
1114 | |||
1115 | /* | ||
1116 | * Do line feed. | ||
1117 | */ | ||
1118 | static void | ||
1119 | tty3270_lf(struct tty3270 *tp) | ||
1120 | { | ||
1121 | struct tty3270_line temp; | ||
1122 | int i; | ||
1123 | |||
1124 | tty3270_convert_line(tp, tp->cy); | ||
1125 | if (tp->cy < tp->view.rows - 3) { | ||
1126 | tp->cy++; | ||
1127 | return; | ||
1128 | } | ||
1129 | /* Last line just filled up. Add new, blank line. */ | ||
1130 | tty3270_blank_line(tp); | ||
1131 | temp = tp->screen[0]; | ||
1132 | temp.len = 0; | ||
1133 | for (i = 0; i < tp->view.rows - 3; i++) | ||
1134 | tp->screen[i] = tp->screen[i+1]; | ||
1135 | tp->screen[tp->view.rows - 3] = temp; | ||
1136 | tty3270_rebuild_update(tp); | ||
1137 | } | ||
1138 | |||
1139 | static void | ||
1140 | tty3270_ri(struct tty3270 *tp) | ||
1141 | { | ||
1142 | if (tp->cy > 0) { | ||
1143 | tty3270_convert_line(tp, tp->cy); | ||
1144 | tp->cy--; | ||
1145 | } | ||
1146 | } | ||
1147 | |||
1148 | /* | ||
1149 | * Insert characters at current position. | ||
1150 | */ | ||
1151 | static void | ||
1152 | tty3270_insert_characters(struct tty3270 *tp, int n) | ||
1153 | { | ||
1154 | struct tty3270_line *line; | ||
1155 | int k; | ||
1156 | |||
1157 | line = tp->screen + tp->cy; | ||
1158 | while (line->len < tp->cx) { | ||
1159 | line->cells[line->len].character = tp->view.ascebc[' ']; | ||
1160 | line->cells[line->len].highlight = TAX_RESET; | ||
1161 | line->cells[line->len].f_color = TAC_RESET; | ||
1162 | line->len++; | ||
1163 | } | ||
1164 | if (n > tp->view.cols - tp->cx) | ||
1165 | n = tp->view.cols - tp->cx; | ||
1166 | k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n); | ||
1167 | while (k--) | ||
1168 | line->cells[tp->cx + n + k] = line->cells[tp->cx + k]; | ||
1169 | line->len += n; | ||
1170 | if (line->len > tp->view.cols) | ||
1171 | line->len = tp->view.cols; | ||
1172 | while (n-- > 0) { | ||
1173 | line->cells[tp->cx + n].character = tp->view.ascebc[' ']; | ||
1174 | line->cells[tp->cx + n].highlight = tp->highlight; | ||
1175 | line->cells[tp->cx + n].f_color = tp->f_color; | ||
1176 | } | ||
1177 | } | ||
1178 | |||
1179 | /* | ||
1180 | * Delete characters at current position. | ||
1181 | */ | ||
1182 | static void | ||
1183 | tty3270_delete_characters(struct tty3270 *tp, int n) | ||
1184 | { | ||
1185 | struct tty3270_line *line; | ||
1186 | int i; | ||
1187 | |||
1188 | line = tp->screen + tp->cy; | ||
1189 | if (line->len <= tp->cx) | ||
1190 | return; | ||
1191 | if (line->len - tp->cx <= n) { | ||
1192 | line->len = tp->cx; | ||
1193 | return; | ||
1194 | } | ||
1195 | for (i = tp->cx; i + n < line->len; i++) | ||
1196 | line->cells[i] = line->cells[i + n]; | ||
1197 | line->len -= n; | ||
1198 | } | ||
1199 | |||
1200 | /* | ||
1201 | * Erase characters at current position. | ||
1202 | */ | ||
1203 | static void | ||
1204 | tty3270_erase_characters(struct tty3270 *tp, int n) | ||
1205 | { | ||
1206 | struct tty3270_line *line; | ||
1207 | struct tty3270_cell *cell; | ||
1208 | |||
1209 | line = tp->screen + tp->cy; | ||
1210 | while (line->len > tp->cx && n-- > 0) { | ||
1211 | cell = line->cells + tp->cx++; | ||
1212 | cell->character = ' '; | ||
1213 | cell->highlight = TAX_RESET; | ||
1214 | cell->f_color = TAC_RESET; | ||
1215 | } | ||
1216 | tp->cx += n; | ||
1217 | tp->cx = min_t(int, tp->cx, tp->view.cols - 1); | ||
1218 | } | ||
1219 | |||
1220 | /* | ||
1221 | * Erase line, 3 different cases: | ||
1222 | * Esc [ 0 K Erase from current position to end of line inclusive | ||
1223 | * Esc [ 1 K Erase from beginning of line to current position inclusive | ||
1224 | * Esc [ 2 K Erase entire line (without moving cursor) | ||
1225 | */ | ||
1226 | static void | ||
1227 | tty3270_erase_line(struct tty3270 *tp, int mode) | ||
1228 | { | ||
1229 | struct tty3270_line *line; | ||
1230 | struct tty3270_cell *cell; | ||
1231 | int i; | ||
1232 | |||
1233 | line = tp->screen + tp->cy; | ||
1234 | if (mode == 0) | ||
1235 | line->len = tp->cx; | ||
1236 | else if (mode == 1) { | ||
1237 | for (i = 0; i < tp->cx; i++) { | ||
1238 | cell = line->cells + i; | ||
1239 | cell->character = ' '; | ||
1240 | cell->highlight = TAX_RESET; | ||
1241 | cell->f_color = TAC_RESET; | ||
1242 | } | ||
1243 | if (line->len <= tp->cx) | ||
1244 | line->len = tp->cx + 1; | ||
1245 | } else if (mode == 2) | ||
1246 | line->len = 0; | ||
1247 | tty3270_convert_line(tp, tp->cy); | ||
1248 | } | ||
1249 | |||
1250 | /* | ||
1251 | * Erase display, 3 different cases: | ||
1252 | * Esc [ 0 J Erase from current position to bottom of screen inclusive | ||
1253 | * Esc [ 1 J Erase from top of screen to current position inclusive | ||
1254 | * Esc [ 2 J Erase entire screen (without moving the cursor) | ||
1255 | */ | ||
1256 | static void | ||
1257 | tty3270_erase_display(struct tty3270 *tp, int mode) | ||
1258 | { | ||
1259 | int i; | ||
1260 | |||
1261 | if (mode == 0) { | ||
1262 | tty3270_erase_line(tp, 0); | ||
1263 | for (i = tp->cy + 1; i < tp->view.rows - 2; i++) { | ||
1264 | tp->screen[i].len = 0; | ||
1265 | tty3270_convert_line(tp, i); | ||
1266 | } | ||
1267 | } else if (mode == 1) { | ||
1268 | for (i = 0; i < tp->cy; i++) { | ||
1269 | tp->screen[i].len = 0; | ||
1270 | tty3270_convert_line(tp, i); | ||
1271 | } | ||
1272 | tty3270_erase_line(tp, 1); | ||
1273 | } else if (mode == 2) { | ||
1274 | for (i = 0; i < tp->view.rows - 2; i++) { | ||
1275 | tp->screen[i].len = 0; | ||
1276 | tty3270_convert_line(tp, i); | ||
1277 | } | ||
1278 | } | ||
1279 | tty3270_rebuild_update(tp); | ||
1280 | } | ||
1281 | |||
1282 | /* | ||
1283 | * Set attributes found in an escape sequence. | ||
1284 | * Esc [ <attr> ; <attr> ; ... m | ||
1285 | */ | ||
1286 | static void | ||
1287 | tty3270_set_attributes(struct tty3270 *tp) | ||
1288 | { | ||
1289 | static unsigned char f_colors[] = { | ||
1290 | TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE, | ||
1291 | TAC_PINK, TAC_TURQ, TAC_WHITE, 0, TAC_DEFAULT | ||
1292 | }; | ||
1293 | int i, attr; | ||
1294 | |||
1295 | for (i = 0; i <= tp->esc_npar; i++) { | ||
1296 | attr = tp->esc_par[i]; | ||
1297 | switch (attr) { | ||
1298 | case 0: /* Reset */ | ||
1299 | tp->highlight = TAX_RESET; | ||
1300 | tp->f_color = TAC_RESET; | ||
1301 | break; | ||
1302 | /* Highlight. */ | ||
1303 | case 4: /* Start underlining. */ | ||
1304 | tp->highlight = TAX_UNDER; | ||
1305 | break; | ||
1306 | case 5: /* Start blink. */ | ||
1307 | tp->highlight = TAX_BLINK; | ||
1308 | break; | ||
1309 | case 7: /* Start reverse. */ | ||
1310 | tp->highlight = TAX_REVER; | ||
1311 | break; | ||
1312 | case 24: /* End underlining */ | ||
1313 | if (tp->highlight == TAX_UNDER) | ||
1314 | tp->highlight = TAX_RESET; | ||
1315 | break; | ||
1316 | case 25: /* End blink. */ | ||
1317 | if (tp->highlight == TAX_BLINK) | ||
1318 | tp->highlight = TAX_RESET; | ||
1319 | break; | ||
1320 | case 27: /* End reverse. */ | ||
1321 | if (tp->highlight == TAX_REVER) | ||
1322 | tp->highlight = TAX_RESET; | ||
1323 | break; | ||
1324 | /* Foreground color. */ | ||
1325 | case 30: /* Black */ | ||
1326 | case 31: /* Red */ | ||
1327 | case 32: /* Green */ | ||
1328 | case 33: /* Yellow */ | ||
1329 | case 34: /* Blue */ | ||
1330 | case 35: /* Magenta */ | ||
1331 | case 36: /* Cyan */ | ||
1332 | case 37: /* White */ | ||
1333 | case 39: /* Black */ | ||
1334 | tp->f_color = f_colors[attr - 30]; | ||
1335 | break; | ||
1336 | } | ||
1337 | } | ||
1338 | } | ||
1339 | |||
1340 | static inline int | ||
1341 | tty3270_getpar(struct tty3270 *tp, int ix) | ||
1342 | { | ||
1343 | return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1; | ||
1344 | } | ||
1345 | |||
1346 | static void | ||
1347 | tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) | ||
1348 | { | ||
1349 | tp->cx = min_t(int, tp->view.cols - 1, max_t(int, 0, cx)); | ||
1350 | cy = min_t(int, tp->view.rows - 3, max_t(int, 0, cy)); | ||
1351 | if (cy != tp->cy) { | ||
1352 | tty3270_convert_line(tp, tp->cy); | ||
1353 | tp->cy = cy; | ||
1354 | } | ||
1355 | } | ||
1356 | |||
1357 | /* | ||
1358 | * Process escape sequences. Known sequences: | ||
1359 | * Esc 7 Save Cursor Position | ||
1360 | * Esc 8 Restore Cursor Position | ||
1361 | * Esc [ Pn ; Pn ; .. m Set attributes | ||
1362 | * Esc [ Pn ; Pn H Cursor Position | ||
1363 | * Esc [ Pn ; Pn f Cursor Position | ||
1364 | * Esc [ Pn A Cursor Up | ||
1365 | * Esc [ Pn B Cursor Down | ||
1366 | * Esc [ Pn C Cursor Forward | ||
1367 | * Esc [ Pn D Cursor Backward | ||
1368 | * Esc [ Pn G Cursor Horizontal Absolute | ||
1369 | * Esc [ Pn X Erase Characters | ||
1370 | * Esc [ Ps J Erase in Display | ||
1371 | * Esc [ Ps K Erase in Line | ||
1372 | * // FIXME: add all the new ones. | ||
1373 | * | ||
1374 | * Pn is a numeric parameter, a string of zero or more decimal digits. | ||
1375 | * Ps is a selective parameter. | ||
1376 | */ | ||
1377 | static void | ||
1378 | tty3270_escape_sequence(struct tty3270 *tp, char ch) | ||
1379 | { | ||
1380 | enum { ESnormal, ESesc, ESsquare, ESgetpars }; | ||
1381 | |||
1382 | if (tp->esc_state == ESnormal) { | ||
1383 | if (ch == 0x1b) | ||
1384 | /* Starting new escape sequence. */ | ||
1385 | tp->esc_state = ESesc; | ||
1386 | return; | ||
1387 | } | ||
1388 | if (tp->esc_state == ESesc) { | ||
1389 | tp->esc_state = ESnormal; | ||
1390 | switch (ch) { | ||
1391 | case '[': | ||
1392 | tp->esc_state = ESsquare; | ||
1393 | break; | ||
1394 | case 'E': | ||
1395 | tty3270_cr(tp); | ||
1396 | tty3270_lf(tp); | ||
1397 | break; | ||
1398 | case 'M': | ||
1399 | tty3270_ri(tp); | ||
1400 | break; | ||
1401 | case 'D': | ||
1402 | tty3270_lf(tp); | ||
1403 | break; | ||
1404 | case 'Z': /* Respond ID. */ | ||
1405 | kbd_puts_queue(tp->tty, "\033[?6c"); | ||
1406 | break; | ||
1407 | case '7': /* Save cursor position. */ | ||
1408 | tp->saved_cx = tp->cx; | ||
1409 | tp->saved_cy = tp->cy; | ||
1410 | tp->saved_highlight = tp->highlight; | ||
1411 | tp->saved_f_color = tp->f_color; | ||
1412 | break; | ||
1413 | case '8': /* Restore cursor position. */ | ||
1414 | tty3270_convert_line(tp, tp->cy); | ||
1415 | tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); | ||
1416 | tp->highlight = tp->saved_highlight; | ||
1417 | tp->f_color = tp->saved_f_color; | ||
1418 | break; | ||
1419 | case 'c': /* Reset terminal. */ | ||
1420 | tp->cx = tp->saved_cx = 0; | ||
1421 | tp->cy = tp->saved_cy = 0; | ||
1422 | tp->highlight = tp->saved_highlight = TAX_RESET; | ||
1423 | tp->f_color = tp->saved_f_color = TAC_RESET; | ||
1424 | tty3270_erase_display(tp, 2); | ||
1425 | break; | ||
1426 | } | ||
1427 | return; | ||
1428 | } | ||
1429 | if (tp->esc_state == ESsquare) { | ||
1430 | tp->esc_state = ESgetpars; | ||
1431 | memset(tp->esc_par, 0, sizeof(tp->esc_par)); | ||
1432 | tp->esc_npar = 0; | ||
1433 | tp->esc_ques = (ch == '?'); | ||
1434 | if (tp->esc_ques) | ||
1435 | return; | ||
1436 | } | ||
1437 | if (tp->esc_state == ESgetpars) { | ||
1438 | if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) { | ||
1439 | tp->esc_npar++; | ||
1440 | return; | ||
1441 | } | ||
1442 | if (ch >= '0' && ch <= '9') { | ||
1443 | tp->esc_par[tp->esc_npar] *= 10; | ||
1444 | tp->esc_par[tp->esc_npar] += ch - '0'; | ||
1445 | return; | ||
1446 | } | ||
1447 | } | ||
1448 | tp->esc_state = ESnormal; | ||
1449 | if (ch == 'n' && !tp->esc_ques) { | ||
1450 | if (tp->esc_par[0] == 5) /* Status report. */ | ||
1451 | kbd_puts_queue(tp->tty, "\033[0n"); | ||
1452 | else if (tp->esc_par[0] == 6) { /* Cursor report. */ | ||
1453 | char buf[40]; | ||
1454 | sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1); | ||
1455 | kbd_puts_queue(tp->tty, buf); | ||
1456 | } | ||
1457 | return; | ||
1458 | } | ||
1459 | if (tp->esc_ques) | ||
1460 | return; | ||
1461 | switch (ch) { | ||
1462 | case 'm': | ||
1463 | tty3270_set_attributes(tp); | ||
1464 | break; | ||
1465 | case 'H': /* Set cursor position. */ | ||
1466 | case 'f': | ||
1467 | tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1, | ||
1468 | tty3270_getpar(tp, 0) - 1); | ||
1469 | break; | ||
1470 | case 'd': /* Set y position. */ | ||
1471 | tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1); | ||
1472 | break; | ||
1473 | case 'A': /* Cursor up. */ | ||
1474 | case 'F': | ||
1475 | tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0)); | ||
1476 | break; | ||
1477 | case 'B': /* Cursor down. */ | ||
1478 | case 'e': | ||
1479 | case 'E': | ||
1480 | tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0)); | ||
1481 | break; | ||
1482 | case 'C': /* Cursor forward. */ | ||
1483 | case 'a': | ||
1484 | tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy); | ||
1485 | break; | ||
1486 | case 'D': /* Cursor backward. */ | ||
1487 | tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy); | ||
1488 | break; | ||
1489 | case 'G': /* Set x position. */ | ||
1490 | case '`': | ||
1491 | tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy); | ||
1492 | break; | ||
1493 | case 'X': /* Erase Characters. */ | ||
1494 | tty3270_erase_characters(tp, tty3270_getpar(tp, 0)); | ||
1495 | break; | ||
1496 | case 'J': /* Erase display. */ | ||
1497 | tty3270_erase_display(tp, tp->esc_par[0]); | ||
1498 | break; | ||
1499 | case 'K': /* Erase line. */ | ||
1500 | tty3270_erase_line(tp, tp->esc_par[0]); | ||
1501 | break; | ||
1502 | case 'P': /* Delete characters. */ | ||
1503 | tty3270_delete_characters(tp, tty3270_getpar(tp, 0)); | ||
1504 | break; | ||
1505 | case '@': /* Insert characters. */ | ||
1506 | tty3270_insert_characters(tp, tty3270_getpar(tp, 0)); | ||
1507 | break; | ||
1508 | case 's': /* Save cursor position. */ | ||
1509 | tp->saved_cx = tp->cx; | ||
1510 | tp->saved_cy = tp->cy; | ||
1511 | tp->saved_highlight = tp->highlight; | ||
1512 | tp->saved_f_color = tp->f_color; | ||
1513 | break; | ||
1514 | case 'u': /* Restore cursor position. */ | ||
1515 | tty3270_convert_line(tp, tp->cy); | ||
1516 | tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); | ||
1517 | tp->highlight = tp->saved_highlight; | ||
1518 | tp->f_color = tp->saved_f_color; | ||
1519 | break; | ||
1520 | } | ||
1521 | } | ||
1522 | |||
1523 | /* | ||
1524 | * String write routine for 3270 ttys | ||
1525 | */ | ||
1526 | static void | ||
1527 | tty3270_do_write(struct tty3270 *tp, const unsigned char *buf, int count) | ||
1528 | { | ||
1529 | int i_msg, i; | ||
1530 | |||
1531 | spin_lock_bh(&tp->view.lock); | ||
1532 | for (i_msg = 0; !tp->tty->stopped && i_msg < count; i_msg++) { | ||
1533 | if (tp->esc_state != 0) { | ||
1534 | /* Continue escape sequence. */ | ||
1535 | tty3270_escape_sequence(tp, buf[i_msg]); | ||
1536 | continue; | ||
1537 | } | ||
1538 | |||
1539 | switch (buf[i_msg]) { | ||
1540 | case 0x07: /* '\a' -- Alarm */ | ||
1541 | tp->wcc |= TW_PLUSALARM; | ||
1542 | break; | ||
1543 | case 0x08: /* Backspace. */ | ||
1544 | if (tp->cx > 0) { | ||
1545 | tp->cx--; | ||
1546 | tty3270_put_character(tp, ' '); | ||
1547 | } | ||
1548 | break; | ||
1549 | case 0x09: /* '\t' -- Tabulate */ | ||
1550 | for (i = tp->cx % 8; i < 8; i++) { | ||
1551 | if (tp->cx >= tp->view.cols) { | ||
1552 | tty3270_cr(tp); | ||
1553 | tty3270_lf(tp); | ||
1554 | break; | ||
1555 | } | ||
1556 | tty3270_put_character(tp, ' '); | ||
1557 | tp->cx++; | ||
1558 | } | ||
1559 | break; | ||
1560 | case 0x0a: /* '\n' -- New Line */ | ||
1561 | tty3270_cr(tp); | ||
1562 | tty3270_lf(tp); | ||
1563 | break; | ||
1564 | case 0x0c: /* '\f' -- Form Feed */ | ||
1565 | tty3270_erase_display(tp, 2); | ||
1566 | tp->cx = tp->cy = 0; | ||
1567 | break; | ||
1568 | case 0x0d: /* '\r' -- Carriage Return */ | ||
1569 | tp->cx = 0; | ||
1570 | break; | ||
1571 | case 0x0f: /* SuSE "exit alternate mode" */ | ||
1572 | break; | ||
1573 | case 0x1b: /* Start escape sequence. */ | ||
1574 | tty3270_escape_sequence(tp, buf[i_msg]); | ||
1575 | break; | ||
1576 | default: /* Insert normal character. */ | ||
1577 | if (tp->cx >= tp->view.cols) { | ||
1578 | tty3270_cr(tp); | ||
1579 | tty3270_lf(tp); | ||
1580 | } | ||
1581 | tty3270_put_character(tp, buf[i_msg]); | ||
1582 | tp->cx++; | ||
1583 | break; | ||
1584 | } | ||
1585 | } | ||
1586 | /* Convert current line to 3270 data fragment. */ | ||
1587 | tty3270_convert_line(tp, tp->cy); | ||
1588 | |||
1589 | /* Setup timer to update display after 1/10 second */ | ||
1590 | if (!timer_pending(&tp->timer)) | ||
1591 | tty3270_set_timer(tp, HZ/10); | ||
1592 | |||
1593 | spin_unlock_bh(&tp->view.lock); | ||
1594 | } | ||
1595 | |||
1596 | /* | ||
1597 | * String write routine for 3270 ttys | ||
1598 | */ | ||
1599 | static int | ||
1600 | tty3270_write(struct tty_struct * tty, | ||
1601 | const unsigned char *buf, int count) | ||
1602 | { | ||
1603 | struct tty3270 *tp; | ||
1604 | |||
1605 | tp = tty->driver_data; | ||
1606 | if (!tp) | ||
1607 | return 0; | ||
1608 | if (tp->char_count > 0) { | ||
1609 | tty3270_do_write(tp, tp->char_buf, tp->char_count); | ||
1610 | tp->char_count = 0; | ||
1611 | } | ||
1612 | tty3270_do_write(tp, buf, count); | ||
1613 | return count; | ||
1614 | } | ||
1615 | |||
1616 | /* | ||
1617 | * Put single characters to the ttys character buffer | ||
1618 | */ | ||
1619 | static void | ||
1620 | tty3270_put_char(struct tty_struct *tty, unsigned char ch) | ||
1621 | { | ||
1622 | struct tty3270 *tp; | ||
1623 | |||
1624 | tp = tty->driver_data; | ||
1625 | if (!tp) | ||
1626 | return; | ||
1627 | if (tp->char_count < TTY3270_CHAR_BUF_SIZE) | ||
1628 | tp->char_buf[tp->char_count++] = ch; | ||
1629 | } | ||
1630 | |||
1631 | /* | ||
1632 | * Flush all characters from the ttys characeter buffer put there | ||
1633 | * by tty3270_put_char. | ||
1634 | */ | ||
1635 | static void | ||
1636 | tty3270_flush_chars(struct tty_struct *tty) | ||
1637 | { | ||
1638 | struct tty3270 *tp; | ||
1639 | |||
1640 | tp = tty->driver_data; | ||
1641 | if (!tp) | ||
1642 | return; | ||
1643 | if (tp->char_count > 0) { | ||
1644 | tty3270_do_write(tp, tp->char_buf, tp->char_count); | ||
1645 | tp->char_count = 0; | ||
1646 | } | ||
1647 | } | ||
1648 | |||
1649 | /* | ||
1650 | * Returns the number of characters in the output buffer. This is | ||
1651 | * used in tty_wait_until_sent to wait until all characters have | ||
1652 | * appeared on the screen. | ||
1653 | */ | ||
1654 | static int | ||
1655 | tty3270_chars_in_buffer(struct tty_struct *tty) | ||
1656 | { | ||
1657 | return 0; | ||
1658 | } | ||
1659 | |||
1660 | static void | ||
1661 | tty3270_flush_buffer(struct tty_struct *tty) | ||
1662 | { | ||
1663 | } | ||
1664 | |||
1665 | /* | ||
1666 | * Check for visible/invisible input switches | ||
1667 | */ | ||
1668 | static void | ||
1669 | tty3270_set_termios(struct tty_struct *tty, struct termios *old) | ||
1670 | { | ||
1671 | struct tty3270 *tp; | ||
1672 | int new; | ||
1673 | |||
1674 | tp = tty->driver_data; | ||
1675 | if (!tp) | ||
1676 | return; | ||
1677 | spin_lock_bh(&tp->view.lock); | ||
1678 | if (L_ICANON(tty)) { | ||
1679 | new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN; | ||
1680 | if (new != tp->inattr) { | ||
1681 | tp->inattr = new; | ||
1682 | tty3270_update_prompt(tp, 0, 0); | ||
1683 | tty3270_set_timer(tp, 1); | ||
1684 | } | ||
1685 | } | ||
1686 | spin_unlock_bh(&tp->view.lock); | ||
1687 | } | ||
1688 | |||
1689 | /* | ||
1690 | * Disable reading from a 3270 tty | ||
1691 | */ | ||
1692 | static void | ||
1693 | tty3270_throttle(struct tty_struct * tty) | ||
1694 | { | ||
1695 | struct tty3270 *tp; | ||
1696 | |||
1697 | tp = tty->driver_data; | ||
1698 | if (!tp) | ||
1699 | return; | ||
1700 | tp->throttle = 1; | ||
1701 | } | ||
1702 | |||
1703 | /* | ||
1704 | * Enable reading from a 3270 tty | ||
1705 | */ | ||
1706 | static void | ||
1707 | tty3270_unthrottle(struct tty_struct * tty) | ||
1708 | { | ||
1709 | struct tty3270 *tp; | ||
1710 | |||
1711 | tp = tty->driver_data; | ||
1712 | if (!tp) | ||
1713 | return; | ||
1714 | tp->throttle = 0; | ||
1715 | if (tp->attn) | ||
1716 | tty3270_issue_read(tp, 1); | ||
1717 | } | ||
1718 | |||
1719 | /* | ||
1720 | * Hang up the tty device. | ||
1721 | */ | ||
1722 | static void | ||
1723 | tty3270_hangup(struct tty_struct *tty) | ||
1724 | { | ||
1725 | // FIXME: implement | ||
1726 | } | ||
1727 | |||
1728 | static void | ||
1729 | tty3270_wait_until_sent(struct tty_struct *tty, int timeout) | ||
1730 | { | ||
1731 | } | ||
1732 | |||
1733 | static int | ||
1734 | tty3270_ioctl(struct tty_struct *tty, struct file *file, | ||
1735 | unsigned int cmd, unsigned long arg) | ||
1736 | { | ||
1737 | struct tty3270 *tp; | ||
1738 | |||
1739 | tp = tty->driver_data; | ||
1740 | if (!tp) | ||
1741 | return -ENODEV; | ||
1742 | if (tty->flags & (1 << TTY_IO_ERROR)) | ||
1743 | return -EIO; | ||
1744 | return kbd_ioctl(tp->kbd, file, cmd, arg); | ||
1745 | } | ||
1746 | |||
1747 | static struct tty_operations tty3270_ops = { | ||
1748 | .open = tty3270_open, | ||
1749 | .close = tty3270_close, | ||
1750 | .write = tty3270_write, | ||
1751 | .put_char = tty3270_put_char, | ||
1752 | .flush_chars = tty3270_flush_chars, | ||
1753 | .write_room = tty3270_write_room, | ||
1754 | .chars_in_buffer = tty3270_chars_in_buffer, | ||
1755 | .flush_buffer = tty3270_flush_buffer, | ||
1756 | .throttle = tty3270_throttle, | ||
1757 | .unthrottle = tty3270_unthrottle, | ||
1758 | .hangup = tty3270_hangup, | ||
1759 | .wait_until_sent = tty3270_wait_until_sent, | ||
1760 | .ioctl = tty3270_ioctl, | ||
1761 | .set_termios = tty3270_set_termios | ||
1762 | }; | ||
1763 | |||
1764 | void | ||
1765 | tty3270_notifier(int index, int active) | ||
1766 | { | ||
1767 | if (active) | ||
1768 | tty_register_device(tty3270_driver, index, 0); | ||
1769 | else | ||
1770 | tty_unregister_device(tty3270_driver, index); | ||
1771 | } | ||
1772 | |||
1773 | /* | ||
1774 | * 3270 tty registration code called from tty_init(). | ||
1775 | * Most kernel services (incl. kmalloc) are available at this poimt. | ||
1776 | */ | ||
1777 | int __init | ||
1778 | tty3270_init(void) | ||
1779 | { | ||
1780 | struct tty_driver *driver; | ||
1781 | int ret; | ||
1782 | |||
1783 | driver = alloc_tty_driver(256); | ||
1784 | if (!driver) | ||
1785 | return -ENOMEM; | ||
1786 | |||
1787 | /* | ||
1788 | * Initialize the tty_driver structure | ||
1789 | * Entries in tty3270_driver that are NOT initialized: | ||
1790 | * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc | ||
1791 | */ | ||
1792 | driver->owner = THIS_MODULE; | ||
1793 | driver->devfs_name = "ttyTUB/"; | ||
1794 | driver->driver_name = "ttyTUB"; | ||
1795 | driver->name = "ttyTUB"; | ||
1796 | driver->major = IBM_TTY3270_MAJOR; | ||
1797 | driver->type = TTY_DRIVER_TYPE_SYSTEM; | ||
1798 | driver->subtype = SYSTEM_TYPE_TTY; | ||
1799 | driver->init_termios = tty_std_termios; | ||
1800 | driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_NO_DEVFS; | ||
1801 | tty_set_operations(driver, &tty3270_ops); | ||
1802 | ret = tty_register_driver(driver); | ||
1803 | if (ret) { | ||
1804 | printk(KERN_ERR "tty3270 registration failed with %d\n", ret); | ||
1805 | put_tty_driver(driver); | ||
1806 | return ret; | ||
1807 | } | ||
1808 | tty3270_driver = driver; | ||
1809 | ret = raw3270_register_notifier(tty3270_notifier); | ||
1810 | if (ret) { | ||
1811 | printk(KERN_ERR "tty3270 notifier registration failed " | ||
1812 | "with %d\n", ret); | ||
1813 | put_tty_driver(driver); | ||
1814 | return ret; | ||
1815 | |||
1816 | } | ||
1817 | return 0; | ||
1818 | } | ||
1819 | |||
1820 | static void __exit | ||
1821 | tty3270_exit(void) | ||
1822 | { | ||
1823 | struct tty_driver *driver; | ||
1824 | |||
1825 | raw3270_unregister_notifier(tty3270_notifier); | ||
1826 | driver = tty3270_driver; | ||
1827 | tty3270_driver = 0; | ||
1828 | tty_unregister_driver(driver); | ||
1829 | tty3270_del_views(); | ||
1830 | } | ||
1831 | |||
1832 | MODULE_LICENSE("GPL"); | ||
1833 | MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR); | ||
1834 | |||
1835 | module_init(tty3270_init); | ||
1836 | module_exit(tty3270_exit); | ||