19603cb33SSven Schnelle // SPDX-License-Identifier: GPL-2.0
29603cb33SSven Schnelle /*
39603cb33SSven Schnelle * IBM/3270 Driver - tty functions.
49603cb33SSven Schnelle *
59603cb33SSven Schnelle * Author(s):
69603cb33SSven Schnelle * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
79603cb33SSven Schnelle * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
89603cb33SSven Schnelle * -- Copyright IBM Corp. 2003
99603cb33SSven Schnelle */
109603cb33SSven Schnelle
119603cb33SSven Schnelle #include <linux/module.h>
129603cb33SSven Schnelle #include <linux/types.h>
139603cb33SSven Schnelle #include <linux/kdev_t.h>
149603cb33SSven Schnelle #include <linux/tty.h>
159603cb33SSven Schnelle #include <linux/vt_kern.h>
169603cb33SSven Schnelle #include <linux/init.h>
179603cb33SSven Schnelle #include <linux/console.h>
189603cb33SSven Schnelle #include <linux/interrupt.h>
199603cb33SSven Schnelle #include <linux/workqueue.h>
209603cb33SSven Schnelle #include <linux/panic_notifier.h>
219603cb33SSven Schnelle #include <linux/reboot.h>
229603cb33SSven Schnelle #include <linux/slab.h>
239603cb33SSven Schnelle #include <linux/memblock.h>
249603cb33SSven Schnelle #include <linux/compat.h>
259603cb33SSven Schnelle
269603cb33SSven Schnelle #include <asm/ccwdev.h>
279603cb33SSven Schnelle #include <asm/cio.h>
289603cb33SSven Schnelle #include <asm/ebcdic.h>
299603cb33SSven Schnelle #include <asm/cpcmd.h>
309603cb33SSven Schnelle #include <linux/uaccess.h>
319603cb33SSven Schnelle
329603cb33SSven Schnelle #include "raw3270.h"
339603cb33SSven Schnelle #include "keyboard.h"
349603cb33SSven Schnelle
359603cb33SSven Schnelle #define TTY3270_CHAR_BUF_SIZE 256
369c138af9SSven Schnelle #define TTY3270_OUTPUT_BUFFER_SIZE 4096
37b2057c87SSven Schnelle #define TTY3270_SCREEN_PAGES 8 /* has to be power-of-two */
3876485078SSven Schnelle #define TTY3270_RECALL_SIZE 16 /* has to be power-of-two */
39525c919dSSven Schnelle #define TTY3270_STATUS_AREA_SIZE 40
40b2057c87SSven Schnelle
419603cb33SSven Schnelle static struct tty_driver *tty3270_driver;
429603cb33SSven Schnelle static int tty3270_max_index;
439603cb33SSven Schnelle static struct raw3270_fn tty3270_fn;
449603cb33SSven Schnelle
450573fff2SSven Schnelle #define TTY3270_HIGHLIGHT_BLINK 1
460573fff2SSven Schnelle #define TTY3270_HIGHLIGHT_REVERSE 2
470573fff2SSven Schnelle #define TTY3270_HIGHLIGHT_UNDERSCORE 4
480573fff2SSven Schnelle
49c2e9375eSSven Schnelle struct tty3270_attribute {
5094dbb0a7SSven Schnelle unsigned char alternate_charset:1; /* Graphics charset */
510573fff2SSven Schnelle unsigned char highlight:3; /* Blink/reverse/underscore */
5218fc2e93SSven Schnelle unsigned char f_color:4; /* Foreground color */
5318fc2e93SSven Schnelle unsigned char b_color:4; /* Background color */
54c2e9375eSSven Schnelle };
55c2e9375eSSven Schnelle
569603cb33SSven Schnelle struct tty3270_cell {
579603cb33SSven Schnelle unsigned char character;
58c2e9375eSSven Schnelle struct tty3270_attribute attributes;
599603cb33SSven Schnelle };
609603cb33SSven Schnelle
619603cb33SSven Schnelle struct tty3270_line {
629603cb33SSven Schnelle struct tty3270_cell *cells;
639603cb33SSven Schnelle int len;
64b2057c87SSven Schnelle int dirty;
659603cb33SSven Schnelle };
669603cb33SSven Schnelle
67cbb36313SSven Schnelle static const unsigned char sfq_read_partition[] = {
68cbb36313SSven Schnelle 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81
69cbb36313SSven Schnelle };
70cbb36313SSven Schnelle
719603cb33SSven Schnelle #define ESCAPE_NPAR 8
729603cb33SSven Schnelle
739603cb33SSven Schnelle /*
749603cb33SSven Schnelle * The main tty view data structure.
759603cb33SSven Schnelle * FIXME:
769603cb33SSven Schnelle * 1) describe line orientation & lines list concept against screen
779603cb33SSven Schnelle * 2) describe conversion of screen to lines
789603cb33SSven Schnelle * 3) describe line format.
799603cb33SSven Schnelle */
809603cb33SSven Schnelle struct tty3270 {
819603cb33SSven Schnelle struct raw3270_view view;
829603cb33SSven Schnelle struct tty_port port;
839603cb33SSven Schnelle
849603cb33SSven Schnelle /* Output stuff. */
859603cb33SSven Schnelle unsigned char wcc; /* Write control character. */
869603cb33SSven Schnelle int nr_up; /* # lines up in history. */
879603cb33SSven Schnelle unsigned long update_flags; /* Update indication bits. */
889603cb33SSven Schnelle struct raw3270_request *write; /* Single write request. */
899603cb33SSven Schnelle struct timer_list timer; /* Output delay timer. */
90ec1b0a33SSven Schnelle char *converted_line; /* RAW 3270 data stream */
919c138af9SSven Schnelle unsigned int line_view_start; /* Start of visible area */
929c138af9SSven Schnelle unsigned int line_write_start; /* current write position */
93ba5c2e2aSSven Schnelle unsigned int oops_line; /* line counter used when print oops */
949603cb33SSven Schnelle
959603cb33SSven Schnelle /* Current tty screen. */
969603cb33SSven Schnelle unsigned int cx, cy; /* Current output position. */
97c2e9375eSSven Schnelle struct tty3270_attribute attributes;
98c2e9375eSSven Schnelle struct tty3270_attribute saved_attributes;
99b2057c87SSven Schnelle int allocated_lines;
1009603cb33SSven Schnelle struct tty3270_line *screen;
1019603cb33SSven Schnelle
1029603cb33SSven Schnelle /* Input stuff. */
103164eb669SSven Schnelle char *prompt; /* Output string for input area. */
104164eb669SSven Schnelle char *input; /* Input string for read request. */
1059603cb33SSven Schnelle struct raw3270_request *read; /* Single read request. */
1069603cb33SSven Schnelle struct raw3270_request *kreset; /* Single keyboard reset request. */
107cbb36313SSven Schnelle struct raw3270_request *readpartreq;
1089603cb33SSven Schnelle unsigned char inattr; /* Visible/invisible input. */
1099603cb33SSven Schnelle int throttle, attn; /* tty throttle/unthrottle. */
1109603cb33SSven Schnelle struct tasklet_struct readlet; /* Tasklet to issue read request. */
1119603cb33SSven Schnelle struct tasklet_struct hanglet; /* Tasklet to hang up the tty. */
1129603cb33SSven Schnelle struct kbd_data *kbd; /* key_maps stuff. */
1139603cb33SSven Schnelle
1149603cb33SSven Schnelle /* Escape sequence parsing. */
1159603cb33SSven Schnelle int esc_state, esc_ques, esc_npar;
1169603cb33SSven Schnelle int esc_par[ESCAPE_NPAR];
1179603cb33SSven Schnelle unsigned int saved_cx, saved_cy;
1189603cb33SSven Schnelle
1199603cb33SSven Schnelle /* Command recalling. */
12076485078SSven Schnelle char **rcl_lines; /* Array of recallable lines */
12176485078SSven Schnelle int rcl_write_index; /* Write index of recallable items */
12276485078SSven Schnelle int rcl_read_index; /* Read index of recallable items */
1239603cb33SSven Schnelle
1249603cb33SSven Schnelle /* Character array for put_char/flush_chars. */
1259603cb33SSven Schnelle unsigned int char_count;
1269603cb33SSven Schnelle char char_buf[TTY3270_CHAR_BUF_SIZE];
1279603cb33SSven Schnelle };
1289603cb33SSven Schnelle
1299603cb33SSven Schnelle /* tty3270->update_flags. See tty3270_update for details. */
130da4e272eSSven Schnelle #define TTY_UPDATE_INPUT 0x1 /* Update input line. */
131da4e272eSSven Schnelle #define TTY_UPDATE_STATUS 0x2 /* Update status line. */
132ba5c2e2aSSven Schnelle #define TTY_UPDATE_LINES 0x4 /* Update visible screen lines */
133ba5c2e2aSSven Schnelle #define TTY_UPDATE_ALL 0x7 /* Recreate screen. */
1349603cb33SSven Schnelle
1359eb99b94SSven Schnelle #define TTY3270_INPUT_AREA_ROWS 2
136f77f936aSSven Schnelle
1379603cb33SSven Schnelle /*
1389603cb33SSven Schnelle * Setup timeout for a device. On timeout trigger an update.
1399603cb33SSven Schnelle */
tty3270_set_timer(struct tty3270 * tp,int expires)1409603cb33SSven Schnelle static void tty3270_set_timer(struct tty3270 *tp, int expires)
1419603cb33SSven Schnelle {
1429603cb33SSven Schnelle mod_timer(&tp->timer, jiffies + expires);
1439603cb33SSven Schnelle }
1449603cb33SSven Schnelle
tty3270_tty_rows(struct tty3270 * tp)1459eb99b94SSven Schnelle static int tty3270_tty_rows(struct tty3270 *tp)
1469eb99b94SSven Schnelle {
1479eb99b94SSven Schnelle return tp->view.rows - TTY3270_INPUT_AREA_ROWS;
1489eb99b94SSven Schnelle }
1499eb99b94SSven Schnelle
tty3270_add_ba(struct tty3270 * tp,char * cp,char order,int x,int y)150ae657244SSven Schnelle static char *tty3270_add_ba(struct tty3270 *tp, char *cp, char order, int x, int y)
151ae657244SSven Schnelle {
152ae657244SSven Schnelle *cp++ = order;
153ae657244SSven Schnelle raw3270_buffer_address(tp->view.dev, cp, x, y);
154ae657244SSven Schnelle return cp + 2;
155ae657244SSven Schnelle }
156ae657244SSven Schnelle
tty3270_add_ra(struct tty3270 * tp,char * cp,int x,int y,char c)157ae657244SSven Schnelle static char *tty3270_add_ra(struct tty3270 *tp, char *cp, int x, int y, char c)
158ae657244SSven Schnelle {
159ae657244SSven Schnelle cp = tty3270_add_ba(tp, cp, TO_RA, x, y);
160ae657244SSven Schnelle *cp++ = c;
161ae657244SSven Schnelle return cp;
162ae657244SSven Schnelle }
163ae657244SSven Schnelle
tty3270_add_sa(struct tty3270 * tp,char * cp,char attr,char value)164ae657244SSven Schnelle static char *tty3270_add_sa(struct tty3270 *tp, char *cp, char attr, char value)
165ae657244SSven Schnelle {
166ae657244SSven Schnelle *cp++ = TO_SA;
167ae657244SSven Schnelle *cp++ = attr;
168ae657244SSven Schnelle *cp++ = value;
169ae657244SSven Schnelle return cp;
170ae657244SSven Schnelle }
171ae657244SSven Schnelle
tty3270_add_ge(struct tty3270 * tp,char * cp,char c)172ae657244SSven Schnelle static char *tty3270_add_ge(struct tty3270 *tp, char *cp, char c)
173ae657244SSven Schnelle {
174ae657244SSven Schnelle *cp++ = TO_GE;
175ae657244SSven Schnelle *cp++ = c;
176ae657244SSven Schnelle return cp;
177ae657244SSven Schnelle }
178ae657244SSven Schnelle
tty3270_add_sf(struct tty3270 * tp,char * cp,char type)179ec1b0a33SSven Schnelle static char *tty3270_add_sf(struct tty3270 *tp, char *cp, char type)
180ec1b0a33SSven Schnelle {
181ec1b0a33SSven Schnelle *cp++ = TO_SF;
182ec1b0a33SSven Schnelle *cp++ = type;
183ec1b0a33SSven Schnelle return cp;
184ec1b0a33SSven Schnelle }
185ec1b0a33SSven Schnelle
tty3270_line_increment(struct tty3270 * tp,unsigned int line,unsigned int incr)1869c138af9SSven Schnelle static int tty3270_line_increment(struct tty3270 *tp, unsigned int line, unsigned int incr)
1879c138af9SSven Schnelle {
1889c138af9SSven Schnelle return (line + incr) & (tp->allocated_lines - 1);
1899c138af9SSven Schnelle }
1909c138af9SSven Schnelle
tty3270_get_write_line(struct tty3270 * tp,unsigned int num)1919c138af9SSven Schnelle static struct tty3270_line *tty3270_get_write_line(struct tty3270 *tp, unsigned int num)
1929c138af9SSven Schnelle {
1939c138af9SSven Schnelle return tp->screen + tty3270_line_increment(tp, tp->line_write_start, num);
1949c138af9SSven Schnelle }
1959c138af9SSven Schnelle
tty3270_get_view_line(struct tty3270 * tp,unsigned int num)1969c138af9SSven Schnelle static struct tty3270_line *tty3270_get_view_line(struct tty3270 *tp, unsigned int num)
1979c138af9SSven Schnelle {
1989c138af9SSven Schnelle return tp->screen + tty3270_line_increment(tp, tp->line_view_start, num - tp->nr_up);
1999c138af9SSven Schnelle }
2009c138af9SSven Schnelle
tty3270_input_size(int cols)201164eb669SSven Schnelle static int tty3270_input_size(int cols)
202164eb669SSven Schnelle {
203164eb669SSven Schnelle return cols * 2 - 11;
204164eb669SSven Schnelle }
205164eb669SSven Schnelle
tty3270_update_prompt(struct tty3270 * tp,char * input)20676485078SSven Schnelle static void tty3270_update_prompt(struct tty3270 *tp, char *input)
207164eb669SSven Schnelle {
20876485078SSven Schnelle strcpy(tp->prompt, input);
209164eb669SSven Schnelle tp->update_flags |= TTY_UPDATE_INPUT;
210164eb669SSven Schnelle tty3270_set_timer(tp, 1);
211164eb669SSven Schnelle }
212164eb669SSven Schnelle
2139603cb33SSven Schnelle /*
2149603cb33SSven Schnelle * The input line are the two last lines of the screen.
2159603cb33SSven Schnelle */
tty3270_add_prompt(struct tty3270 * tp)216164eb669SSven Schnelle static int tty3270_add_prompt(struct tty3270 *tp)
2179603cb33SSven Schnelle {
218164eb669SSven Schnelle int count = 0;
219164eb669SSven Schnelle char *cp;
2209603cb33SSven Schnelle
221164eb669SSven Schnelle cp = tp->converted_line;
222164eb669SSven Schnelle cp = tty3270_add_ba(tp, cp, TO_SBA, 0, -2);
223164eb669SSven Schnelle *cp++ = tp->view.ascebc['>'];
224164eb669SSven Schnelle
225164eb669SSven Schnelle if (*tp->prompt) {
226164eb669SSven Schnelle cp = tty3270_add_sf(tp, cp, TF_INMDT);
227525c919dSSven Schnelle count = min_t(int, strlen(tp->prompt),
228525c919dSSven Schnelle tp->view.cols * 2 - TTY3270_STATUS_AREA_SIZE - 2);
229164eb669SSven Schnelle memcpy(cp, tp->prompt, count);
230164eb669SSven Schnelle cp += count;
231164eb669SSven Schnelle } else {
232164eb669SSven Schnelle cp = tty3270_add_sf(tp, cp, tp->inattr);
2339603cb33SSven Schnelle }
234164eb669SSven Schnelle *cp++ = TO_IC;
235164eb669SSven Schnelle /* Clear to end of input line. */
236164eb669SSven Schnelle if (count < tp->view.cols * 2 - 11)
237525c919dSSven Schnelle cp = tty3270_add_ra(tp, cp, -TTY3270_STATUS_AREA_SIZE, -1, 0);
238164eb669SSven Schnelle return cp - tp->converted_line;
2399603cb33SSven Schnelle }
2409603cb33SSven Schnelle
tty3270_ebcdic_convert(struct tty3270 * tp,char * d,char * s)241525c919dSSven Schnelle static char *tty3270_ebcdic_convert(struct tty3270 *tp, char *d, char *s)
242525c919dSSven Schnelle {
243525c919dSSven Schnelle while (*s)
244525c919dSSven Schnelle *d++ = tp->view.ascebc[(int)*s++];
245525c919dSSven Schnelle return d;
246525c919dSSven Schnelle }
247525c919dSSven Schnelle
2489603cb33SSven Schnelle /*
2499603cb33SSven Schnelle * The status line is the last line of the screen. It shows the string
250525c919dSSven Schnelle * "Running"/"History X" in the lower right corner of the screen.
2519603cb33SSven Schnelle */
tty3270_add_status(struct tty3270 * tp)252ec1b0a33SSven Schnelle static int tty3270_add_status(struct tty3270 *tp)
2539603cb33SSven Schnelle {
254ec1b0a33SSven Schnelle char *cp = tp->converted_line;
255ec1b0a33SSven Schnelle int len;
2569603cb33SSven Schnelle
257525c919dSSven Schnelle cp = tty3270_add_ba(tp, cp, TO_SBA, -TTY3270_STATUS_AREA_SIZE, -1);
258ec1b0a33SSven Schnelle cp = tty3270_add_sf(tp, cp, TF_LOG);
259ec1b0a33SSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_GREEN);
260525c919dSSven Schnelle cp = tty3270_ebcdic_convert(tp, cp, " 7");
261525c919dSSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER);
262525c919dSSven Schnelle cp = tty3270_ebcdic_convert(tp, cp, "PrevPg");
263525c919dSSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET);
264525c919dSSven Schnelle cp = tty3270_ebcdic_convert(tp, cp, " 8");
265525c919dSSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER);
266525c919dSSven Schnelle cp = tty3270_ebcdic_convert(tp, cp, "NextPg");
267525c919dSSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET);
268525c919dSSven Schnelle cp = tty3270_ebcdic_convert(tp, cp, " 12");
269525c919dSSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER);
270525c919dSSven Schnelle cp = tty3270_ebcdic_convert(tp, cp, "Recall");
271525c919dSSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET);
272525c919dSSven Schnelle cp = tty3270_ebcdic_convert(tp, cp, " ");
273525c919dSSven Schnelle if (tp->nr_up) {
274525c919dSSven Schnelle len = sprintf(cp, "History %d", -tp->nr_up);
275ec1b0a33SSven Schnelle codepage_convert(tp->view.ascebc, cp, len);
276ec1b0a33SSven Schnelle cp += len;
277525c919dSSven Schnelle } else {
278ba5c2e2aSSven Schnelle cp = tty3270_ebcdic_convert(tp, cp, oops_in_progress ? "Crashed" : "Running");
279525c919dSSven Schnelle }
280ec1b0a33SSven Schnelle cp = tty3270_add_sf(tp, cp, TF_LOG);
281ec1b0a33SSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_RESET);
282ec1b0a33SSven Schnelle return cp - (char *)tp->converted_line;
2839603cb33SSven Schnelle }
2849603cb33SSven Schnelle
tty3270_blank_screen(struct tty3270 * tp)285e6d98bb8SSven Schnelle static void tty3270_blank_screen(struct tty3270 *tp)
2869603cb33SSven Schnelle {
2879c138af9SSven Schnelle struct tty3270_line *line;
2889603cb33SSven Schnelle int i;
2899603cb33SSven Schnelle
2909c138af9SSven Schnelle for (i = 0; i < tty3270_tty_rows(tp); i++) {
2919c138af9SSven Schnelle line = tty3270_get_write_line(tp, i);
2929c138af9SSven Schnelle line->len = 0;
2939c138af9SSven Schnelle line->dirty = 1;
2949603cb33SSven Schnelle }
2959c138af9SSven Schnelle tp->nr_up = 0;
2969603cb33SSven Schnelle }
2979603cb33SSven Schnelle
2989603cb33SSven Schnelle /*
2999603cb33SSven Schnelle * Write request completion callback.
3009603cb33SSven Schnelle */
tty3270_write_callback(struct raw3270_request * rq,void * data)301e6d98bb8SSven Schnelle static void tty3270_write_callback(struct raw3270_request *rq, void *data)
3029603cb33SSven Schnelle {
3039603cb33SSven Schnelle struct tty3270 *tp = container_of(rq->view, struct tty3270, view);
3049603cb33SSven Schnelle
3059603cb33SSven Schnelle if (rq->rc != 0) {
3069603cb33SSven Schnelle /* Write wasn't successful. Refresh all. */
3079603cb33SSven Schnelle tp->update_flags = TTY_UPDATE_ALL;
3089603cb33SSven Schnelle tty3270_set_timer(tp, 1);
3099603cb33SSven Schnelle }
3109603cb33SSven Schnelle raw3270_request_reset(rq);
3119603cb33SSven Schnelle xchg(&tp->write, rq);
3129603cb33SSven Schnelle }
3139603cb33SSven Schnelle
tty3270_required_length(struct tty3270 * tp,struct tty3270_line * line)3149c138af9SSven Schnelle static int tty3270_required_length(struct tty3270 *tp, struct tty3270_line *line)
3152b62ba58SSven Schnelle {
3162b62ba58SSven Schnelle unsigned char f_color, b_color, highlight;
3172b62ba58SSven Schnelle struct tty3270_cell *cell;
3182b62ba58SSven Schnelle int i, flen = 3; /* Prefix (TO_SBA). */
3192b62ba58SSven Schnelle
3202b62ba58SSven Schnelle flen += line->len;
3210573fff2SSven Schnelle highlight = 0;
3222b62ba58SSven Schnelle f_color = TAC_RESET;
3232b62ba58SSven Schnelle b_color = TAC_RESET;
3242b62ba58SSven Schnelle
3252b62ba58SSven Schnelle for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
3262b62ba58SSven Schnelle if (cell->attributes.highlight != highlight) {
3272b62ba58SSven Schnelle flen += 3; /* TO_SA to switch highlight. */
3282b62ba58SSven Schnelle highlight = cell->attributes.highlight;
3292b62ba58SSven Schnelle }
3302b62ba58SSven Schnelle if (cell->attributes.f_color != f_color) {
3312b62ba58SSven Schnelle flen += 3; /* TO_SA to switch color. */
3322b62ba58SSven Schnelle f_color = cell->attributes.f_color;
3332b62ba58SSven Schnelle }
3342b62ba58SSven Schnelle if (cell->attributes.b_color != b_color) {
3352b62ba58SSven Schnelle flen += 3; /* TO_SA to switch color. */
3362b62ba58SSven Schnelle b_color = cell->attributes.b_color;
3372b62ba58SSven Schnelle }
3382b62ba58SSven Schnelle if (cell->attributes.alternate_charset)
3392b62ba58SSven Schnelle flen += 1; /* TO_GE to switch to graphics extensions */
3402b62ba58SSven Schnelle }
3410573fff2SSven Schnelle if (highlight)
3422b62ba58SSven Schnelle flen += 3; /* TO_SA to reset hightlight. */
3432b62ba58SSven Schnelle if (f_color != TAC_RESET)
3442b62ba58SSven Schnelle flen += 3; /* TO_SA to reset color. */
3452b62ba58SSven Schnelle if (b_color != TAC_RESET)
3462b62ba58SSven Schnelle flen += 3; /* TO_SA to reset color. */
3472b62ba58SSven Schnelle if (line->len < tp->view.cols)
3482b62ba58SSven Schnelle flen += 4; /* Postfix (TO_RA). */
3492b62ba58SSven Schnelle
3502b62ba58SSven Schnelle return flen;
3512b62ba58SSven Schnelle }
3522b62ba58SSven Schnelle
tty3270_add_reset_attributes(struct tty3270 * tp,struct tty3270_line * line,char * cp,struct tty3270_attribute * attr,int lineno)3532b62ba58SSven Schnelle static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_line *line,
354422a78eaSSven Schnelle char *cp, struct tty3270_attribute *attr, int lineno)
3552b62ba58SSven Schnelle {
3560573fff2SSven Schnelle if (attr->highlight)
357ae657244SSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET);
358ae657244SSven Schnelle if (attr->f_color != TAC_RESET)
359ae657244SSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAX_RESET);
360ae657244SSven Schnelle if (attr->b_color != TAC_RESET)
361ae657244SSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, TAX_RESET);
362ae657244SSven Schnelle if (line->len < tp->view.cols)
363422a78eaSSven Schnelle cp = tty3270_add_ra(tp, cp, 0, lineno + 1, 0);
3642b62ba58SSven Schnelle return cp;
3652b62ba58SSven Schnelle }
3662b62ba58SSven Schnelle
tty3270_graphics_translate(struct tty3270 * tp,char ch)3676e49017cSSven Schnelle static char tty3270_graphics_translate(struct tty3270 *tp, char ch)
3686e49017cSSven Schnelle {
3696e49017cSSven Schnelle switch (ch) {
3706e49017cSSven Schnelle case 'q': /* - */
3716e49017cSSven Schnelle return 0xa2;
3726e49017cSSven Schnelle case 'x': /* '|' */
3736e49017cSSven Schnelle return 0x85;
3746e49017cSSven Schnelle case 'l': /* |- */
3756e49017cSSven Schnelle return 0xc5;
3766e49017cSSven Schnelle case 't': /* |_ */
3776e49017cSSven Schnelle return 0xc6;
3786e49017cSSven Schnelle case 'u': /* _| */
3796e49017cSSven Schnelle return 0xd6;
3806e49017cSSven Schnelle case 'k': /* -| */
3816e49017cSSven Schnelle return 0xd5;
3826e49017cSSven Schnelle case 'j':
3836e49017cSSven Schnelle return 0xd4;
3846e49017cSSven Schnelle case 'm':
3856e49017cSSven Schnelle return 0xc4;
3866e49017cSSven Schnelle case 'n': /* + */
3876e49017cSSven Schnelle return 0xd3;
3886e49017cSSven Schnelle case 'v':
3896e49017cSSven Schnelle return 0xc7;
3906e49017cSSven Schnelle case 'w':
3916e49017cSSven Schnelle return 0xd7;
3926e49017cSSven Schnelle default:
3936e49017cSSven Schnelle return ch;
3946e49017cSSven Schnelle }
3956e49017cSSven Schnelle }
3966e49017cSSven Schnelle
tty3270_add_attributes(struct tty3270 * tp,struct tty3270_line * line,struct tty3270_attribute * attr,char * cp,int lineno)3976e49017cSSven Schnelle static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *line,
398422a78eaSSven Schnelle struct tty3270_attribute *attr, char *cp, int lineno)
3992b62ba58SSven Schnelle {
40018fc2e93SSven Schnelle const unsigned char colors[16] = {
40118fc2e93SSven Schnelle [0] = TAC_DEFAULT,
40218fc2e93SSven Schnelle [1] = TAC_RED,
40318fc2e93SSven Schnelle [2] = TAC_GREEN,
40418fc2e93SSven Schnelle [3] = TAC_YELLOW,
40518fc2e93SSven Schnelle [4] = TAC_BLUE,
40618fc2e93SSven Schnelle [5] = TAC_PINK,
40718fc2e93SSven Schnelle [6] = TAC_TURQ,
40818fc2e93SSven Schnelle [7] = TAC_WHITE,
40918fc2e93SSven Schnelle [9] = TAC_DEFAULT
41018fc2e93SSven Schnelle };
41118fc2e93SSven Schnelle
4120573fff2SSven Schnelle const unsigned char highlights[8] = {
4130573fff2SSven Schnelle [TTY3270_HIGHLIGHT_BLINK] = TAX_BLINK,
4140573fff2SSven Schnelle [TTY3270_HIGHLIGHT_REVERSE] = TAX_REVER,
4150573fff2SSven Schnelle [TTY3270_HIGHLIGHT_UNDERSCORE] = TAX_UNDER,
4160573fff2SSven Schnelle };
4170573fff2SSven Schnelle
4182b62ba58SSven Schnelle struct tty3270_cell *cell;
419ae657244SSven Schnelle int c, i;
4202b62ba58SSven Schnelle
421422a78eaSSven Schnelle cp = tty3270_add_ba(tp, cp, TO_SBA, 0, lineno);
4222b62ba58SSven Schnelle
4232b62ba58SSven Schnelle for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
4242b62ba58SSven Schnelle if (cell->attributes.highlight != attr->highlight) {
4252b62ba58SSven Schnelle attr->highlight = cell->attributes.highlight;
4260573fff2SSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_EXTHI, highlights[attr->highlight]);
4272b62ba58SSven Schnelle }
4282b62ba58SSven Schnelle if (cell->attributes.f_color != attr->f_color) {
4292b62ba58SSven Schnelle attr->f_color = cell->attributes.f_color;
43018fc2e93SSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, colors[attr->f_color]);
4312b62ba58SSven Schnelle }
4322b62ba58SSven Schnelle if (cell->attributes.b_color != attr->b_color) {
4332b62ba58SSven Schnelle attr->b_color = cell->attributes.b_color;
43418fc2e93SSven Schnelle cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, colors[attr->b_color]);
4352b62ba58SSven Schnelle }
436ae657244SSven Schnelle c = cell->character;
437ae657244SSven Schnelle if (cell->attributes.alternate_charset)
438ae657244SSven Schnelle cp = tty3270_add_ge(tp, cp, tty3270_graphics_translate(tp, c));
439ae657244SSven Schnelle else
440ae657244SSven Schnelle *cp++ = tp->view.ascebc[c];
4412b62ba58SSven Schnelle }
4422b62ba58SSven Schnelle return cp;
4432b62ba58SSven Schnelle }
4442b62ba58SSven Schnelle
tty3270_reset_attributes(struct tty3270_attribute * attr)4452b62ba58SSven Schnelle static void tty3270_reset_attributes(struct tty3270_attribute *attr)
4462b62ba58SSven Schnelle {
4472b62ba58SSven Schnelle attr->highlight = TAX_RESET;
4482b62ba58SSven Schnelle attr->f_color = TAC_RESET;
4492b62ba58SSven Schnelle attr->b_color = TAC_RESET;
4502b62ba58SSven Schnelle }
4512b62ba58SSven Schnelle
4522b62ba58SSven Schnelle /*
4532b62ba58SSven Schnelle * Convert a tty3270_line to a 3270 data fragment usable for output.
4542b62ba58SSven Schnelle */
tty3270_convert_line(struct tty3270 * tp,struct tty3270_line * line,int lineno)455422a78eaSSven Schnelle static unsigned int tty3270_convert_line(struct tty3270 *tp, struct tty3270_line *line, int lineno)
4562b62ba58SSven Schnelle {
4572b62ba58SSven Schnelle struct tty3270_attribute attr;
4589c138af9SSven Schnelle int flen;
4592b62ba58SSven Schnelle char *cp;
4602b62ba58SSven Schnelle
4612b62ba58SSven Schnelle /* Determine how long the fragment will be. */
4629c138af9SSven Schnelle flen = tty3270_required_length(tp, line);
4639c138af9SSven Schnelle if (flen > PAGE_SIZE)
4649c138af9SSven Schnelle return 0;
4652b62ba58SSven Schnelle /* Write 3270 data fragment. */
4662b62ba58SSven Schnelle tty3270_reset_attributes(&attr);
467422a78eaSSven Schnelle cp = tty3270_add_attributes(tp, line, &attr, tp->converted_line, lineno);
468422a78eaSSven Schnelle cp = tty3270_add_reset_attributes(tp, line, cp, &attr, lineno);
4699c138af9SSven Schnelle return cp - (char *)tp->converted_line;
4702b62ba58SSven Schnelle }
4712b62ba58SSven Schnelle
tty3270_update_lines_visible(struct tty3270 * tp,struct raw3270_request * rq)472ba5c2e2aSSven Schnelle static void tty3270_update_lines_visible(struct tty3270 *tp, struct raw3270_request *rq)
473ba5c2e2aSSven Schnelle {
474ba5c2e2aSSven Schnelle struct tty3270_line *line;
475ba5c2e2aSSven Schnelle int len, i;
476ba5c2e2aSSven Schnelle
477ba5c2e2aSSven Schnelle for (i = 0; i < tty3270_tty_rows(tp); i++) {
478ba5c2e2aSSven Schnelle line = tty3270_get_view_line(tp, i);
479ba5c2e2aSSven Schnelle if (!line->dirty)
480ba5c2e2aSSven Schnelle continue;
481ba5c2e2aSSven Schnelle len = tty3270_convert_line(tp, line, i);
482ba5c2e2aSSven Schnelle if (raw3270_request_add_data(rq, tp->converted_line, len))
483ba5c2e2aSSven Schnelle break;
484ba5c2e2aSSven Schnelle line->dirty = 0;
485ba5c2e2aSSven Schnelle }
486ba5c2e2aSSven Schnelle if (i == tty3270_tty_rows(tp)) {
487ba5c2e2aSSven Schnelle for (i = 0; i < tp->allocated_lines; i++)
488ba5c2e2aSSven Schnelle tp->screen[i].dirty = 0;
489ba5c2e2aSSven Schnelle tp->update_flags &= ~TTY_UPDATE_LINES;
490ba5c2e2aSSven Schnelle }
491ba5c2e2aSSven Schnelle }
492ba5c2e2aSSven Schnelle
tty3270_update_lines_all(struct tty3270 * tp,struct raw3270_request * rq)493ba5c2e2aSSven Schnelle static void tty3270_update_lines_all(struct tty3270 *tp, struct raw3270_request *rq)
494ba5c2e2aSSven Schnelle {
495ba5c2e2aSSven Schnelle struct tty3270_line *line;
496ba5c2e2aSSven Schnelle char buf[4];
497ba5c2e2aSSven Schnelle int len, i;
498ba5c2e2aSSven Schnelle
499ba5c2e2aSSven Schnelle for (i = 0; i < tp->allocated_lines; i++) {
500ba5c2e2aSSven Schnelle line = tty3270_get_write_line(tp, i + tp->cy + 1);
501ba5c2e2aSSven Schnelle if (!line->dirty)
502ba5c2e2aSSven Schnelle continue;
503ba5c2e2aSSven Schnelle len = tty3270_convert_line(tp, line, tp->oops_line);
504ba5c2e2aSSven Schnelle if (raw3270_request_add_data(rq, tp->converted_line, len))
505ba5c2e2aSSven Schnelle break;
506ba5c2e2aSSven Schnelle line->dirty = 0;
507ba5c2e2aSSven Schnelle if (++tp->oops_line >= tty3270_tty_rows(tp))
508ba5c2e2aSSven Schnelle tp->oops_line = 0;
509ba5c2e2aSSven Schnelle }
510ba5c2e2aSSven Schnelle
511ba5c2e2aSSven Schnelle if (i == tp->allocated_lines) {
512ba5c2e2aSSven Schnelle if (tp->oops_line < tty3270_tty_rows(tp)) {
513ba5c2e2aSSven Schnelle tty3270_add_ra(tp, buf, 0, tty3270_tty_rows(tp), 0);
514ba5c2e2aSSven Schnelle if (raw3270_request_add_data(rq, buf, sizeof(buf)))
515ba5c2e2aSSven Schnelle return;
516ba5c2e2aSSven Schnelle }
517ba5c2e2aSSven Schnelle tp->update_flags &= ~TTY_UPDATE_LINES;
518ba5c2e2aSSven Schnelle }
519ba5c2e2aSSven Schnelle }
520ba5c2e2aSSven Schnelle
5219603cb33SSven Schnelle /*
5229603cb33SSven Schnelle * Update 3270 display.
5239603cb33SSven Schnelle */
tty3270_update(struct timer_list * t)524e6d98bb8SSven Schnelle static void tty3270_update(struct timer_list *t)
5259603cb33SSven Schnelle {
5269603cb33SSven Schnelle struct tty3270 *tp = from_timer(tp, t, timer);
5279603cb33SSven Schnelle struct raw3270_request *wrq;
528da4e272eSSven Schnelle u8 cmd = TC_WRITE;
529ba5c2e2aSSven Schnelle int rc, len;
5309603cb33SSven Schnelle
5319603cb33SSven Schnelle wrq = xchg(&tp->write, 0);
5329603cb33SSven Schnelle if (!wrq) {
5339603cb33SSven Schnelle tty3270_set_timer(tp, 1);
5349603cb33SSven Schnelle return;
5359603cb33SSven Schnelle }
5369603cb33SSven Schnelle
5379603cb33SSven Schnelle spin_lock_irq(&tp->view.lock);
538da4e272eSSven Schnelle if (tp->update_flags == TTY_UPDATE_ALL)
539da4e272eSSven Schnelle cmd = TC_EWRITEA;
540da4e272eSSven Schnelle
541da4e272eSSven Schnelle raw3270_request_set_cmd(wrq, cmd);
5429603cb33SSven Schnelle raw3270_request_add_data(wrq, &tp->wcc, 1);
5439603cb33SSven Schnelle tp->wcc = TW_NONE;
5449603cb33SSven Schnelle
5459603cb33SSven Schnelle /*
5469603cb33SSven Schnelle * Update status line.
5479603cb33SSven Schnelle */
548ec1b0a33SSven Schnelle if (tp->update_flags & TTY_UPDATE_STATUS) {
549ec1b0a33SSven Schnelle len = tty3270_add_status(tp);
550ec1b0a33SSven Schnelle if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0)
551da4e272eSSven Schnelle tp->update_flags &= ~TTY_UPDATE_STATUS;
552ec1b0a33SSven Schnelle }
5539603cb33SSven Schnelle
5549603cb33SSven Schnelle /*
5559603cb33SSven Schnelle * Write input line.
5569603cb33SSven Schnelle */
557164eb669SSven Schnelle if (tp->update_flags & TTY_UPDATE_INPUT) {
558164eb669SSven Schnelle len = tty3270_add_prompt(tp);
559164eb669SSven Schnelle if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0)
560da4e272eSSven Schnelle tp->update_flags &= ~TTY_UPDATE_INPUT;
561164eb669SSven Schnelle }
5629603cb33SSven Schnelle
563ba5c2e2aSSven Schnelle if (tp->update_flags & TTY_UPDATE_LINES) {
564ba5c2e2aSSven Schnelle if (oops_in_progress)
565ba5c2e2aSSven Schnelle tty3270_update_lines_all(tp, wrq);
566ba5c2e2aSSven Schnelle else
567ba5c2e2aSSven Schnelle tty3270_update_lines_visible(tp, wrq);
5689603cb33SSven Schnelle }
5699c138af9SSven Schnelle
5709603cb33SSven Schnelle wrq->callback = tty3270_write_callback;
5719603cb33SSven Schnelle rc = raw3270_start(&tp->view, wrq);
5729603cb33SSven Schnelle if (rc == 0) {
5739603cb33SSven Schnelle if (tp->update_flags)
5749603cb33SSven Schnelle tty3270_set_timer(tp, 1);
5759603cb33SSven Schnelle } else {
5769603cb33SSven Schnelle raw3270_request_reset(wrq);
5779603cb33SSven Schnelle xchg(&tp->write, wrq);
5789603cb33SSven Schnelle }
5799603cb33SSven Schnelle spin_unlock_irq(&tp->view.lock);
5809603cb33SSven Schnelle }
5819603cb33SSven Schnelle
5829603cb33SSven Schnelle /*
5839603cb33SSven Schnelle * Command recalling.
5849603cb33SSven Schnelle */
tty3270_rcl_add(struct tty3270 * tp,char * input,int len)585e6d98bb8SSven Schnelle static void tty3270_rcl_add(struct tty3270 *tp, char *input, int len)
5869603cb33SSven Schnelle {
58776485078SSven Schnelle char *p;
5887ef21387SSven Schnelle
5899603cb33SSven Schnelle if (len <= 0)
5909603cb33SSven Schnelle return;
59176485078SSven Schnelle p = tp->rcl_lines[tp->rcl_write_index++];
59276485078SSven Schnelle tp->rcl_write_index &= TTY3270_RECALL_SIZE - 1;
59376485078SSven Schnelle memcpy(p, input, len);
59476485078SSven Schnelle p[len] = '\0';
59576485078SSven Schnelle tp->rcl_read_index = tp->rcl_write_index;
5969603cb33SSven Schnelle }
5979603cb33SSven Schnelle
tty3270_rcl_backward(struct kbd_data * kbd)598e6d98bb8SSven Schnelle static void tty3270_rcl_backward(struct kbd_data *kbd)
5999603cb33SSven Schnelle {
6009603cb33SSven Schnelle struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
60176485078SSven Schnelle int i = 0;
6029603cb33SSven Schnelle
6039603cb33SSven Schnelle spin_lock_irq(&tp->view.lock);
6049603cb33SSven Schnelle if (tp->inattr == TF_INPUT) {
60576485078SSven Schnelle do {
60676485078SSven Schnelle tp->rcl_read_index--;
60776485078SSven Schnelle tp->rcl_read_index &= TTY3270_RECALL_SIZE - 1;
60876485078SSven Schnelle } while (!*tp->rcl_lines[tp->rcl_read_index] &&
60976485078SSven Schnelle i++ < TTY3270_RECALL_SIZE - 1);
61076485078SSven Schnelle tty3270_update_prompt(tp, tp->rcl_lines[tp->rcl_read_index]);
6119603cb33SSven Schnelle }
6129603cb33SSven Schnelle spin_unlock_irq(&tp->view.lock);
6139603cb33SSven Schnelle }
6149603cb33SSven Schnelle
6159603cb33SSven Schnelle /*
6169603cb33SSven Schnelle * Deactivate tty view.
6179603cb33SSven Schnelle */
tty3270_exit_tty(struct kbd_data * kbd)618e6d98bb8SSven Schnelle static void tty3270_exit_tty(struct kbd_data *kbd)
6199603cb33SSven Schnelle {
6209603cb33SSven Schnelle struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
6219603cb33SSven Schnelle
6229603cb33SSven Schnelle raw3270_deactivate_view(&tp->view);
6239603cb33SSven Schnelle }
6249603cb33SSven Schnelle
tty3270_redraw(struct tty3270 * tp)6259c138af9SSven Schnelle static void tty3270_redraw(struct tty3270 *tp)
6269c138af9SSven Schnelle {
6279c138af9SSven Schnelle int i;
6289c138af9SSven Schnelle
6299c138af9SSven Schnelle for (i = 0; i < tty3270_tty_rows(tp); i++)
6309c138af9SSven Schnelle tty3270_get_view_line(tp, i)->dirty = 1;
6319c138af9SSven Schnelle tp->update_flags = TTY_UPDATE_ALL;
6329c138af9SSven Schnelle tty3270_set_timer(tp, 1);
6339c138af9SSven Schnelle }
6347ef21387SSven Schnelle
6359603cb33SSven Schnelle /*
6369603cb33SSven Schnelle * Scroll forward in history.
6379603cb33SSven Schnelle */
tty3270_scroll_forward(struct kbd_data * kbd)638e6d98bb8SSven Schnelle static void tty3270_scroll_forward(struct kbd_data *kbd)
6399603cb33SSven Schnelle {
6409603cb33SSven Schnelle struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
6419603cb33SSven Schnelle
6429603cb33SSven Schnelle spin_lock_irq(&tp->view.lock);
6439c138af9SSven Schnelle
6449c138af9SSven Schnelle if (tp->nr_up >= tty3270_tty_rows(tp))
6459c138af9SSven Schnelle tp->nr_up -= tty3270_tty_rows(tp) / 2;
6469c138af9SSven Schnelle else
6479c138af9SSven Schnelle tp->nr_up = 0;
6489c138af9SSven Schnelle tty3270_redraw(tp);
6499603cb33SSven Schnelle spin_unlock_irq(&tp->view.lock);
6509603cb33SSven Schnelle }
6519603cb33SSven Schnelle
6529603cb33SSven Schnelle /*
6539603cb33SSven Schnelle * Scroll backward in history.
6549603cb33SSven Schnelle */
tty3270_scroll_backward(struct kbd_data * kbd)655e6d98bb8SSven Schnelle static void tty3270_scroll_backward(struct kbd_data *kbd)
6569603cb33SSven Schnelle {
6579603cb33SSven Schnelle struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
6589603cb33SSven Schnelle
6599603cb33SSven Schnelle spin_lock_irq(&tp->view.lock);
6609c138af9SSven Schnelle tp->nr_up += tty3270_tty_rows(tp) / 2;
6619c138af9SSven Schnelle if (tp->nr_up > tp->allocated_lines - tty3270_tty_rows(tp))
6629c138af9SSven Schnelle tp->nr_up = tp->allocated_lines - tty3270_tty_rows(tp);
6639c138af9SSven Schnelle tty3270_redraw(tp);
6649603cb33SSven Schnelle spin_unlock_irq(&tp->view.lock);
6659603cb33SSven Schnelle }
6669603cb33SSven Schnelle
6679603cb33SSven Schnelle /*
6689603cb33SSven Schnelle * Pass input line to tty.
6699603cb33SSven Schnelle */
tty3270_read_tasklet(unsigned long data)670e6d98bb8SSven Schnelle static void tty3270_read_tasklet(unsigned long data)
6719603cb33SSven Schnelle {
6729603cb33SSven Schnelle struct raw3270_request *rrq = (struct raw3270_request *)data;
6739603cb33SSven Schnelle static char kreset_data = TW_KR;
6749603cb33SSven Schnelle struct tty3270 *tp = container_of(rrq->view, struct tty3270, view);
6759603cb33SSven Schnelle char *input;
6769603cb33SSven Schnelle int len;
6779603cb33SSven Schnelle
6789603cb33SSven Schnelle spin_lock_irq(&tp->view.lock);
6799603cb33SSven Schnelle /*
6809603cb33SSven Schnelle * Two AID keys are special: For 0x7d (enter) the input line
6819603cb33SSven Schnelle * has to be emitted to the tty and for 0x6d the screen
6829603cb33SSven Schnelle * needs to be redrawn.
6839603cb33SSven Schnelle */
6849603cb33SSven Schnelle input = NULL;
6859603cb33SSven Schnelle len = 0;
686164eb669SSven Schnelle switch (tp->input[0]) {
687e22de7d7SSven Schnelle case AID_ENTER:
6889603cb33SSven Schnelle /* Enter: write input to tty. */
689164eb669SSven Schnelle input = tp->input + 6;
690164eb669SSven Schnelle len = tty3270_input_size(tp->view.cols) - 6 - rrq->rescnt;
6919603cb33SSven Schnelle if (tp->inattr != TF_INPUTN)
6929603cb33SSven Schnelle tty3270_rcl_add(tp, input, len);
6939c138af9SSven Schnelle if (tp->nr_up > 0)
6949603cb33SSven Schnelle tp->nr_up = 0;
6959603cb33SSven Schnelle /* Clear input area. */
69676485078SSven Schnelle tty3270_update_prompt(tp, "");
6979603cb33SSven Schnelle tty3270_set_timer(tp, 1);
698e22de7d7SSven Schnelle break;
699e22de7d7SSven Schnelle case AID_CLEAR:
7009603cb33SSven Schnelle /* Display has been cleared. Redraw. */
7019603cb33SSven Schnelle tp->update_flags = TTY_UPDATE_ALL;
7029603cb33SSven Schnelle tty3270_set_timer(tp, 1);
703cbb36313SSven Schnelle if (!list_empty(&tp->readpartreq->list))
704cbb36313SSven Schnelle break;
705cbb36313SSven Schnelle raw3270_start_request(&tp->view, tp->readpartreq, TC_WRITESF,
706cbb36313SSven Schnelle (char *)sfq_read_partition, sizeof(sfq_read_partition));
707cbb36313SSven Schnelle break;
708cbb36313SSven Schnelle case AID_READ_PARTITION:
709164eb669SSven Schnelle raw3270_read_modified_cb(tp->readpartreq, tp->input);
710cbb36313SSven Schnelle break;
711e22de7d7SSven Schnelle default:
712e22de7d7SSven Schnelle break;
7139603cb33SSven Schnelle }
7149603cb33SSven Schnelle spin_unlock_irq(&tp->view.lock);
7159603cb33SSven Schnelle
7169603cb33SSven Schnelle /* Start keyboard reset command. */
717f08e3155SSven Schnelle raw3270_start_request(&tp->view, tp->kreset, TC_WRITE, &kreset_data, 1);
7189603cb33SSven Schnelle
7199603cb33SSven Schnelle while (len-- > 0)
7209603cb33SSven Schnelle kbd_keycode(tp->kbd, *input++);
7219603cb33SSven Schnelle /* Emit keycode for AID byte. */
722164eb669SSven Schnelle kbd_keycode(tp->kbd, 256 + tp->input[0]);
7239603cb33SSven Schnelle
7249603cb33SSven Schnelle raw3270_request_reset(rrq);
7259603cb33SSven Schnelle xchg(&tp->read, rrq);
7269603cb33SSven Schnelle raw3270_put_view(&tp->view);
7279603cb33SSven Schnelle }
7289603cb33SSven Schnelle
7299603cb33SSven Schnelle /*
7309603cb33SSven Schnelle * Read request completion callback.
7319603cb33SSven Schnelle */
tty3270_read_callback(struct raw3270_request * rq,void * data)732e6d98bb8SSven Schnelle static void tty3270_read_callback(struct raw3270_request *rq, void *data)
7339603cb33SSven Schnelle {
7349603cb33SSven Schnelle struct tty3270 *tp = container_of(rq->view, struct tty3270, view);
7357ef21387SSven Schnelle
7369603cb33SSven Schnelle raw3270_get_view(rq->view);
7379603cb33SSven Schnelle /* Schedule tasklet to pass input to tty. */
7389603cb33SSven Schnelle tasklet_schedule(&tp->readlet);
7399603cb33SSven Schnelle }
7409603cb33SSven Schnelle
7419603cb33SSven Schnelle /*
7429603cb33SSven Schnelle * Issue a read request. Call with device lock.
7439603cb33SSven Schnelle */
tty3270_issue_read(struct tty3270 * tp,int lock)744e6d98bb8SSven Schnelle static void tty3270_issue_read(struct tty3270 *tp, int lock)
7459603cb33SSven Schnelle {
7469603cb33SSven Schnelle struct raw3270_request *rrq;
7479603cb33SSven Schnelle int rc;
7489603cb33SSven Schnelle
7499603cb33SSven Schnelle rrq = xchg(&tp->read, 0);
7509603cb33SSven Schnelle if (!rrq)
7519603cb33SSven Schnelle /* Read already scheduled. */
7529603cb33SSven Schnelle return;
7539603cb33SSven Schnelle rrq->callback = tty3270_read_callback;
7549603cb33SSven Schnelle rrq->callback_data = tp;
7559603cb33SSven Schnelle raw3270_request_set_cmd(rrq, TC_READMOD);
756164eb669SSven Schnelle raw3270_request_set_data(rrq, tp->input, tty3270_input_size(tp->view.cols));
7579603cb33SSven Schnelle /* Issue the read modified request. */
7587ef21387SSven Schnelle if (lock)
7599603cb33SSven Schnelle rc = raw3270_start(&tp->view, rrq);
7607ef21387SSven Schnelle else
7619603cb33SSven Schnelle rc = raw3270_start_irq(&tp->view, rrq);
7629603cb33SSven Schnelle if (rc) {
7639603cb33SSven Schnelle raw3270_request_reset(rrq);
7649603cb33SSven Schnelle xchg(&tp->read, rrq);
7659603cb33SSven Schnelle }
7669603cb33SSven Schnelle }
7679603cb33SSven Schnelle
7689603cb33SSven Schnelle /*
7699603cb33SSven Schnelle * Hang up the tty
7709603cb33SSven Schnelle */
tty3270_hangup_tasklet(unsigned long data)771e6d98bb8SSven Schnelle static void tty3270_hangup_tasklet(unsigned long data)
7729603cb33SSven Schnelle {
7739603cb33SSven Schnelle struct tty3270 *tp = (struct tty3270 *)data;
7747ef21387SSven Schnelle
7759603cb33SSven Schnelle tty_port_tty_hangup(&tp->port, true);
7769603cb33SSven Schnelle raw3270_put_view(&tp->view);
7779603cb33SSven Schnelle }
7789603cb33SSven Schnelle
7799603cb33SSven Schnelle /*
7809603cb33SSven Schnelle * Switch to the tty view.
7819603cb33SSven Schnelle */
tty3270_activate(struct raw3270_view * view)782e6d98bb8SSven Schnelle static int tty3270_activate(struct raw3270_view *view)
7839603cb33SSven Schnelle {
7849603cb33SSven Schnelle struct tty3270 *tp = container_of(view, struct tty3270, view);
7859603cb33SSven Schnelle
7869603cb33SSven Schnelle tp->update_flags = TTY_UPDATE_ALL;
7879603cb33SSven Schnelle tty3270_set_timer(tp, 1);
7889603cb33SSven Schnelle return 0;
7899603cb33SSven Schnelle }
7909603cb33SSven Schnelle
tty3270_deactivate(struct raw3270_view * view)791e6d98bb8SSven Schnelle static void tty3270_deactivate(struct raw3270_view *view)
7929603cb33SSven Schnelle {
7939603cb33SSven Schnelle struct tty3270 *tp = container_of(view, struct tty3270, view);
7949603cb33SSven Schnelle
7959603cb33SSven Schnelle del_timer(&tp->timer);
7969603cb33SSven Schnelle }
7979603cb33SSven Schnelle
tty3270_irq(struct tty3270 * tp,struct raw3270_request * rq,struct irb * irb)798e6d98bb8SSven Schnelle static void tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
7999603cb33SSven Schnelle {
8009603cb33SSven Schnelle /* Handle ATTN. Schedule tasklet to read aid. */
8019603cb33SSven Schnelle if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
8029603cb33SSven Schnelle if (!tp->throttle)
8039603cb33SSven Schnelle tty3270_issue_read(tp, 0);
8049603cb33SSven Schnelle else
8059603cb33SSven Schnelle tp->attn = 1;
8069603cb33SSven Schnelle }
8079603cb33SSven Schnelle
8089603cb33SSven Schnelle if (rq) {
8099603cb33SSven Schnelle if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
8109603cb33SSven Schnelle rq->rc = -EIO;
8119603cb33SSven Schnelle raw3270_get_view(&tp->view);
8129603cb33SSven Schnelle tasklet_schedule(&tp->hanglet);
8139603cb33SSven Schnelle } else {
8149603cb33SSven Schnelle /* Normal end. Copy residual count. */
8159603cb33SSven Schnelle rq->rescnt = irb->scsw.cmd.count;
8169603cb33SSven Schnelle }
8179603cb33SSven Schnelle } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
8189603cb33SSven Schnelle /* Interrupt without an outstanding request -> update all */
8199603cb33SSven Schnelle tp->update_flags = TTY_UPDATE_ALL;
8209603cb33SSven Schnelle tty3270_set_timer(tp, 1);
8219603cb33SSven Schnelle }
8229603cb33SSven Schnelle }
8239603cb33SSven Schnelle
8249603cb33SSven Schnelle /*
8259603cb33SSven Schnelle * Allocate tty3270 structure.
8269603cb33SSven Schnelle */
tty3270_alloc_view(void)827e6d98bb8SSven Schnelle static struct tty3270 *tty3270_alloc_view(void)
8289603cb33SSven Schnelle {
8299603cb33SSven Schnelle struct tty3270 *tp;
8309603cb33SSven Schnelle
8317ef21387SSven Schnelle tp = kzalloc(sizeof(*tp), GFP_KERNEL);
8329603cb33SSven Schnelle if (!tp)
8339603cb33SSven Schnelle goto out_err;
8349603cb33SSven Schnelle
8359603cb33SSven Schnelle tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE);
8369603cb33SSven Schnelle if (IS_ERR(tp->write))
83776485078SSven Schnelle goto out_tp;
8389603cb33SSven Schnelle tp->read = raw3270_request_alloc(0);
8399603cb33SSven Schnelle if (IS_ERR(tp->read))
8409603cb33SSven Schnelle goto out_write;
8419603cb33SSven Schnelle tp->kreset = raw3270_request_alloc(1);
8429603cb33SSven Schnelle if (IS_ERR(tp->kreset))
8439603cb33SSven Schnelle goto out_read;
844cbb36313SSven Schnelle tp->readpartreq = raw3270_request_alloc(sizeof(sfq_read_partition));
845cbb36313SSven Schnelle if (IS_ERR(tp->readpartreq))
846cbb36313SSven Schnelle goto out_reset;
8479603cb33SSven Schnelle tp->kbd = kbd_alloc();
8489603cb33SSven Schnelle if (!tp->kbd)
849cbb36313SSven Schnelle goto out_readpartreq;
8509603cb33SSven Schnelle
8519603cb33SSven Schnelle tty_port_init(&tp->port);
8529603cb33SSven Schnelle timer_setup(&tp->timer, tty3270_update, 0);
8539603cb33SSven Schnelle tasklet_init(&tp->readlet, tty3270_read_tasklet,
8549603cb33SSven Schnelle (unsigned long)tp->read);
8559603cb33SSven Schnelle tasklet_init(&tp->hanglet, tty3270_hangup_tasklet,
8569603cb33SSven Schnelle (unsigned long)tp);
8579603cb33SSven Schnelle return tp;
8589603cb33SSven Schnelle
859cbb36313SSven Schnelle out_readpartreq:
860cbb36313SSven Schnelle raw3270_request_free(tp->readpartreq);
8619603cb33SSven Schnelle out_reset:
8629603cb33SSven Schnelle raw3270_request_free(tp->kreset);
8639603cb33SSven Schnelle out_read:
8649603cb33SSven Schnelle raw3270_request_free(tp->read);
8659603cb33SSven Schnelle out_write:
8669603cb33SSven Schnelle raw3270_request_free(tp->write);
8679603cb33SSven Schnelle out_tp:
8689603cb33SSven Schnelle kfree(tp);
8699603cb33SSven Schnelle out_err:
8709603cb33SSven Schnelle return ERR_PTR(-ENOMEM);
8719603cb33SSven Schnelle }
8729603cb33SSven Schnelle
8739603cb33SSven Schnelle /*
8749603cb33SSven Schnelle * Free tty3270 structure.
8759603cb33SSven Schnelle */
tty3270_free_view(struct tty3270 * tp)876e6d98bb8SSven Schnelle static void tty3270_free_view(struct tty3270 *tp)
8779603cb33SSven Schnelle {
8789603cb33SSven Schnelle kbd_free(tp->kbd);
8799603cb33SSven Schnelle raw3270_request_free(tp->kreset);
8809603cb33SSven Schnelle raw3270_request_free(tp->read);
8819603cb33SSven Schnelle raw3270_request_free(tp->write);
8829c138af9SSven Schnelle free_page((unsigned long)tp->converted_line);
8839603cb33SSven Schnelle tty_port_destroy(&tp->port);
8849603cb33SSven Schnelle kfree(tp);
8859603cb33SSven Schnelle }
8869603cb33SSven Schnelle
8879603cb33SSven Schnelle /*
8889603cb33SSven Schnelle * Allocate tty3270 screen.
8899603cb33SSven Schnelle */
tty3270_alloc_screen(struct tty3270 * tp,unsigned int rows,unsigned int cols,int * allocated_out)890b2057c87SSven Schnelle static struct tty3270_line *tty3270_alloc_screen(struct tty3270 *tp, unsigned int rows,
891b2057c87SSven Schnelle unsigned int cols, int *allocated_out)
8929603cb33SSven Schnelle {
8939603cb33SSven Schnelle struct tty3270_line *screen;
894b2057c87SSven Schnelle int allocated, lines;
8959603cb33SSven Schnelle
896b2057c87SSven Schnelle allocated = __roundup_pow_of_two(rows) * TTY3270_SCREEN_PAGES;
897b2057c87SSven Schnelle screen = kcalloc(allocated, sizeof(struct tty3270_line), GFP_KERNEL);
8989603cb33SSven Schnelle if (!screen)
8999603cb33SSven Schnelle goto out_err;
900b2057c87SSven Schnelle for (lines = 0; lines < allocated; lines++) {
901b2057c87SSven Schnelle screen[lines].cells = kcalloc(cols, sizeof(struct tty3270_cell), GFP_KERNEL);
9029603cb33SSven Schnelle if (!screen[lines].cells)
9039603cb33SSven Schnelle goto out_screen;
9049603cb33SSven Schnelle }
905b2057c87SSven Schnelle *allocated_out = allocated;
9069603cb33SSven Schnelle return screen;
9079603cb33SSven Schnelle out_screen:
9089603cb33SSven Schnelle while (lines--)
9099603cb33SSven Schnelle kfree(screen[lines].cells);
9109603cb33SSven Schnelle kfree(screen);
9119603cb33SSven Schnelle out_err:
9129603cb33SSven Schnelle return ERR_PTR(-ENOMEM);
9139603cb33SSven Schnelle }
9149603cb33SSven Schnelle
tty3270_alloc_recall(int cols)91576485078SSven Schnelle static char **tty3270_alloc_recall(int cols)
91676485078SSven Schnelle {
91776485078SSven Schnelle char **lines;
91876485078SSven Schnelle int i;
91976485078SSven Schnelle
92076485078SSven Schnelle lines = kmalloc_array(TTY3270_RECALL_SIZE, sizeof(char *), GFP_KERNEL);
92176485078SSven Schnelle if (!lines)
92276485078SSven Schnelle return NULL;
92376485078SSven Schnelle for (i = 0; i < TTY3270_RECALL_SIZE; i++) {
92476485078SSven Schnelle lines[i] = kcalloc(1, tty3270_input_size(cols) + 1, GFP_KERNEL);
92576485078SSven Schnelle if (!lines[i])
92676485078SSven Schnelle break;
92776485078SSven Schnelle }
92876485078SSven Schnelle
92976485078SSven Schnelle if (i == TTY3270_RECALL_SIZE)
93076485078SSven Schnelle return lines;
93176485078SSven Schnelle
93276485078SSven Schnelle while (i--)
93376485078SSven Schnelle kfree(lines[i]);
93476485078SSven Schnelle kfree(lines);
93576485078SSven Schnelle return NULL;
93676485078SSven Schnelle }
93776485078SSven Schnelle
tty3270_free_recall(char ** lines)93876485078SSven Schnelle static void tty3270_free_recall(char **lines)
93976485078SSven Schnelle {
94076485078SSven Schnelle int i;
94176485078SSven Schnelle
94276485078SSven Schnelle for (i = 0; i < TTY3270_RECALL_SIZE; i++)
94376485078SSven Schnelle kfree(lines[i]);
94476485078SSven Schnelle kfree(lines);
94576485078SSven Schnelle }
94676485078SSven Schnelle
9479603cb33SSven Schnelle /*
9489603cb33SSven Schnelle * Free tty3270 screen.
9499603cb33SSven Schnelle */
tty3270_free_screen(struct tty3270_line * screen,int old_lines)950b2057c87SSven Schnelle static void tty3270_free_screen(struct tty3270_line *screen, int old_lines)
9519603cb33SSven Schnelle {
9529603cb33SSven Schnelle int lines;
9539603cb33SSven Schnelle
954b2057c87SSven Schnelle for (lines = 0; lines < old_lines; lines++)
9559603cb33SSven Schnelle kfree(screen[lines].cells);
9569603cb33SSven Schnelle kfree(screen);
9579603cb33SSven Schnelle }
9589603cb33SSven Schnelle
9599603cb33SSven Schnelle /*
9609603cb33SSven Schnelle * Resize tty3270 screen
9619603cb33SSven Schnelle */
tty3270_resize(struct raw3270_view * view,int new_model,int new_rows,int new_cols,int old_model,int old_rows,int old_cols)96291621ba7SSven Schnelle static void tty3270_resize(struct raw3270_view *view,
96391621ba7SSven Schnelle int new_model, int new_rows, int new_cols,
96491621ba7SSven Schnelle int old_model, int old_rows, int old_cols)
9659603cb33SSven Schnelle {
96691621ba7SSven Schnelle struct tty3270 *tp = container_of(view, struct tty3270, view);
9679603cb33SSven Schnelle struct tty3270_line *screen, *oscreen;
96876485078SSven Schnelle char **old_rcl_lines, **new_rcl_lines;
969164eb669SSven Schnelle char *old_prompt, *new_prompt;
970164eb669SSven Schnelle char *old_input, *new_input;
9719603cb33SSven Schnelle struct tty_struct *tty;
9729603cb33SSven Schnelle struct winsize ws;
973b2057c87SSven Schnelle int new_allocated, old_allocated = tp->allocated_lines;
9749603cb33SSven Schnelle
97591621ba7SSven Schnelle if (old_model == new_model &&
97691621ba7SSven Schnelle old_cols == new_cols &&
97791621ba7SSven Schnelle old_rows == new_rows) {
97891621ba7SSven Schnelle spin_lock_irq(&tp->view.lock);
9799c138af9SSven Schnelle tty3270_redraw(tp);
98091621ba7SSven Schnelle spin_unlock_irq(&tp->view.lock);
98191621ba7SSven Schnelle return;
98291621ba7SSven Schnelle }
983164eb669SSven Schnelle
984164eb669SSven Schnelle new_input = kzalloc(tty3270_input_size(new_cols), GFP_KERNEL | GFP_DMA);
985164eb669SSven Schnelle if (!new_input)
986164eb669SSven Schnelle return;
987164eb669SSven Schnelle new_prompt = kzalloc(tty3270_input_size(new_cols), GFP_KERNEL);
988164eb669SSven Schnelle if (!new_prompt)
989164eb669SSven Schnelle goto out_input;
990b2057c87SSven Schnelle screen = tty3270_alloc_screen(tp, new_rows, new_cols, &new_allocated);
9919603cb33SSven Schnelle if (IS_ERR(screen))
992164eb669SSven Schnelle goto out_prompt;
99376485078SSven Schnelle new_rcl_lines = tty3270_alloc_recall(new_cols);
99476485078SSven Schnelle if (!new_rcl_lines)
99576485078SSven Schnelle goto out_screen;
996164eb669SSven Schnelle
9979603cb33SSven Schnelle /* Switch to new output size */
9989603cb33SSven Schnelle spin_lock_irq(&tp->view.lock);
9999603cb33SSven Schnelle tty3270_blank_screen(tp);
10009603cb33SSven Schnelle oscreen = tp->screen;
10019603cb33SSven Schnelle tp->screen = screen;
1002b2057c87SSven Schnelle tp->allocated_lines = new_allocated;
100391621ba7SSven Schnelle tp->view.rows = new_rows;
100491621ba7SSven Schnelle tp->view.cols = new_cols;
100591621ba7SSven Schnelle tp->view.model = new_model;
10069603cb33SSven Schnelle tp->update_flags = TTY_UPDATE_ALL;
1007164eb669SSven Schnelle old_input = tp->input;
1008164eb669SSven Schnelle old_prompt = tp->prompt;
100976485078SSven Schnelle old_rcl_lines = tp->rcl_lines;
1010164eb669SSven Schnelle tp->input = new_input;
1011164eb669SSven Schnelle tp->prompt = new_prompt;
101276485078SSven Schnelle tp->rcl_lines = new_rcl_lines;
101376485078SSven Schnelle tp->rcl_read_index = 0;
101476485078SSven Schnelle tp->rcl_write_index = 0;
10159603cb33SSven Schnelle spin_unlock_irq(&tp->view.lock);
1016b2057c87SSven Schnelle tty3270_free_screen(oscreen, old_allocated);
1017164eb669SSven Schnelle kfree(old_input);
1018164eb669SSven Schnelle kfree(old_prompt);
101976485078SSven Schnelle tty3270_free_recall(old_rcl_lines);
10209603cb33SSven Schnelle tty3270_set_timer(tp, 1);
10219603cb33SSven Schnelle /* Informat tty layer about new size */
10229603cb33SSven Schnelle tty = tty_port_tty_get(&tp->port);
10239603cb33SSven Schnelle if (!tty)
10249603cb33SSven Schnelle return;
10259eb99b94SSven Schnelle ws.ws_row = tty3270_tty_rows(tp);
10269603cb33SSven Schnelle ws.ws_col = tp->view.cols;
10279603cb33SSven Schnelle tty_do_resize(tty, &ws);
10289603cb33SSven Schnelle tty_kref_put(tty);
1029164eb669SSven Schnelle return;
103076485078SSven Schnelle out_screen:
103176485078SSven Schnelle tty3270_free_screen(screen, new_rows);
1032164eb669SSven Schnelle out_prompt:
1033164eb669SSven Schnelle kfree(new_prompt);
1034164eb669SSven Schnelle out_input:
1035164eb669SSven Schnelle kfree(new_input);
10369603cb33SSven Schnelle }
10379603cb33SSven Schnelle
10389603cb33SSven Schnelle /*
10399603cb33SSven Schnelle * Unlink tty3270 data structure from tty.
10409603cb33SSven Schnelle */
tty3270_release(struct raw3270_view * view)1041e6d98bb8SSven Schnelle static void tty3270_release(struct raw3270_view *view)
10429603cb33SSven Schnelle {
10439603cb33SSven Schnelle struct tty3270 *tp = container_of(view, struct tty3270, view);
10449603cb33SSven Schnelle struct tty_struct *tty = tty_port_tty_get(&tp->port);
10459603cb33SSven Schnelle
10469603cb33SSven Schnelle if (tty) {
10479603cb33SSven Schnelle tty->driver_data = NULL;
10489603cb33SSven Schnelle tty_port_tty_set(&tp->port, NULL);
10499603cb33SSven Schnelle tty_hangup(tty);
10509603cb33SSven Schnelle raw3270_put_view(&tp->view);
10519603cb33SSven Schnelle tty_kref_put(tty);
10529603cb33SSven Schnelle }
10539603cb33SSven Schnelle }
10549603cb33SSven Schnelle
10559603cb33SSven Schnelle /*
10569603cb33SSven Schnelle * Free tty3270 data structure
10579603cb33SSven Schnelle */
tty3270_free(struct raw3270_view * view)1058e6d98bb8SSven Schnelle static void tty3270_free(struct raw3270_view *view)
10599603cb33SSven Schnelle {
10609603cb33SSven Schnelle struct tty3270 *tp = container_of(view, struct tty3270, view);
10619603cb33SSven Schnelle
10629603cb33SSven Schnelle del_timer_sync(&tp->timer);
1063b2057c87SSven Schnelle tty3270_free_screen(tp->screen, tp->allocated_lines);
1064ec1b0a33SSven Schnelle free_page((unsigned long)tp->converted_line);
1065164eb669SSven Schnelle kfree(tp->input);
1066164eb669SSven Schnelle kfree(tp->prompt);
10679603cb33SSven Schnelle tty3270_free_view(tp);
10689603cb33SSven Schnelle }
10699603cb33SSven Schnelle
10709603cb33SSven Schnelle /*
10719603cb33SSven Schnelle * Delayed freeing of tty3270 views.
10729603cb33SSven Schnelle */
tty3270_del_views(void)1073e6d98bb8SSven Schnelle static void tty3270_del_views(void)
10749603cb33SSven Schnelle {
10759603cb33SSven Schnelle int i;
10769603cb33SSven Schnelle
10779603cb33SSven Schnelle for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) {
10789603cb33SSven Schnelle struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i);
10797ef21387SSven Schnelle
10809603cb33SSven Schnelle if (!IS_ERR(view))
10819603cb33SSven Schnelle raw3270_del_view(view);
10829603cb33SSven Schnelle }
10839603cb33SSven Schnelle }
10849603cb33SSven Schnelle
10859603cb33SSven Schnelle static struct raw3270_fn tty3270_fn = {
10869603cb33SSven Schnelle .activate = tty3270_activate,
10879603cb33SSven Schnelle .deactivate = tty3270_deactivate,
10889603cb33SSven Schnelle .intv = (void *)tty3270_irq,
10899603cb33SSven Schnelle .release = tty3270_release,
10909603cb33SSven Schnelle .free = tty3270_free,
10919603cb33SSven Schnelle .resize = tty3270_resize
10929603cb33SSven Schnelle };
10939603cb33SSven Schnelle
10949603cb33SSven Schnelle static int
tty3270_create_view(int index,struct tty3270 ** newtp)10959603cb33SSven Schnelle tty3270_create_view(int index, struct tty3270 **newtp)
10969603cb33SSven Schnelle {
10979603cb33SSven Schnelle struct tty3270 *tp;
10989c138af9SSven Schnelle int rc;
10999603cb33SSven Schnelle
11009603cb33SSven Schnelle if (tty3270_max_index < index + 1)
11019603cb33SSven Schnelle tty3270_max_index = index + 1;
11029603cb33SSven Schnelle
11039603cb33SSven Schnelle /* Allocate tty3270 structure on first open. */
11049603cb33SSven Schnelle tp = tty3270_alloc_view();
11059603cb33SSven Schnelle if (IS_ERR(tp))
11069603cb33SSven Schnelle return PTR_ERR(tp);
11079603cb33SSven Schnelle
11089603cb33SSven Schnelle rc = raw3270_add_view(&tp->view, &tty3270_fn,
11099603cb33SSven Schnelle index + RAW3270_FIRSTMINOR,
11109603cb33SSven Schnelle RAW3270_VIEW_LOCK_IRQ);
1111ec1b0a33SSven Schnelle if (rc)
1112ec1b0a33SSven Schnelle goto out_free_view;
11139603cb33SSven Schnelle
1114b2057c87SSven Schnelle tp->screen = tty3270_alloc_screen(tp, tp->view.rows, tp->view.cols,
1115b2057c87SSven Schnelle &tp->allocated_lines);
11169603cb33SSven Schnelle if (IS_ERR(tp->screen)) {
11179603cb33SSven Schnelle rc = PTR_ERR(tp->screen);
1118ec1b0a33SSven Schnelle goto out_put_view;
1119ec1b0a33SSven Schnelle }
1120ec1b0a33SSven Schnelle
1121ec1b0a33SSven Schnelle tp->converted_line = (void *)__get_free_page(GFP_KERNEL);
1122ec1b0a33SSven Schnelle if (!tp->converted_line) {
1123ec1b0a33SSven Schnelle rc = -ENOMEM;
1124ec1b0a33SSven Schnelle goto out_free_screen;
11259603cb33SSven Schnelle }
11269603cb33SSven Schnelle
1127164eb669SSven Schnelle tp->input = kzalloc(tty3270_input_size(tp->view.cols), GFP_KERNEL | GFP_DMA);
1128164eb669SSven Schnelle if (!tp->input) {
1129164eb669SSven Schnelle rc = -ENOMEM;
1130164eb669SSven Schnelle goto out_free_converted_line;
1131164eb669SSven Schnelle }
1132164eb669SSven Schnelle
1133164eb669SSven Schnelle tp->prompt = kzalloc(tty3270_input_size(tp->view.cols), GFP_KERNEL);
1134164eb669SSven Schnelle if (!tp->prompt) {
1135164eb669SSven Schnelle rc = -ENOMEM;
1136164eb669SSven Schnelle goto out_free_input;
1137164eb669SSven Schnelle }
11389603cb33SSven Schnelle
113976485078SSven Schnelle tp->rcl_lines = tty3270_alloc_recall(tp->view.cols);
114076485078SSven Schnelle if (!tp->rcl_lines) {
114176485078SSven Schnelle rc = -ENOMEM;
114276485078SSven Schnelle goto out_free_prompt;
114376485078SSven Schnelle }
114476485078SSven Schnelle
11459603cb33SSven Schnelle /* Create blank line for every line in the tty output area. */
11469c138af9SSven Schnelle tty3270_blank_screen(tp);
11479603cb33SSven Schnelle
11489603cb33SSven Schnelle tp->kbd->port = &tp->port;
11499603cb33SSven Schnelle tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty;
11509603cb33SSven Schnelle tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward;
11519603cb33SSven Schnelle tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward;
11529603cb33SSven Schnelle tp->kbd->fn_handler[KVAL(K_CONS)] = tty3270_rcl_backward;
11539603cb33SSven Schnelle kbd_ascebc(tp->kbd, tp->view.ascebc);
11549603cb33SSven Schnelle
11559603cb33SSven Schnelle raw3270_activate_view(&tp->view);
11569603cb33SSven Schnelle raw3270_put_view(&tp->view);
11579603cb33SSven Schnelle *newtp = tp;
11589603cb33SSven Schnelle return 0;
1159ec1b0a33SSven Schnelle
116076485078SSven Schnelle out_free_prompt:
116176485078SSven Schnelle kfree(tp->prompt);
1162164eb669SSven Schnelle out_free_input:
1163164eb669SSven Schnelle kfree(tp->input);
1164164eb669SSven Schnelle out_free_converted_line:
1165164eb669SSven Schnelle free_page((unsigned long)tp->converted_line);
1166ec1b0a33SSven Schnelle out_free_screen:
1167ec1b0a33SSven Schnelle tty3270_free_screen(tp->screen, tp->view.rows);
1168ec1b0a33SSven Schnelle out_put_view:
1169ec1b0a33SSven Schnelle raw3270_put_view(&tp->view);
1170ec1b0a33SSven Schnelle raw3270_del_view(&tp->view);
1171ec1b0a33SSven Schnelle out_free_view:
1172ec1b0a33SSven Schnelle tty3270_free_view(tp);
1173ec1b0a33SSven Schnelle return rc;
11749603cb33SSven Schnelle }
11759603cb33SSven Schnelle
11769603cb33SSven Schnelle /*
11779603cb33SSven Schnelle * This routine is called whenever a 3270 tty is opened first time.
11789603cb33SSven Schnelle */
11799603cb33SSven Schnelle static int
tty3270_install(struct tty_driver * driver,struct tty_struct * tty)11809603cb33SSven Schnelle tty3270_install(struct tty_driver *driver, struct tty_struct *tty)
11819603cb33SSven Schnelle {
11829603cb33SSven Schnelle struct raw3270_view *view;
11839603cb33SSven Schnelle struct tty3270 *tp;
11849603cb33SSven Schnelle int rc;
11859603cb33SSven Schnelle
11869603cb33SSven Schnelle /* Check if the tty3270 is already there. */
11879603cb33SSven Schnelle view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR);
11889603cb33SSven Schnelle if (IS_ERR(view)) {
11899603cb33SSven Schnelle rc = tty3270_create_view(tty->index, &tp);
11909603cb33SSven Schnelle if (rc)
11919603cb33SSven Schnelle return rc;
11929603cb33SSven Schnelle } else {
11939603cb33SSven Schnelle tp = container_of(view, struct tty3270, view);
11949603cb33SSven Schnelle tty->driver_data = tp;
11959603cb33SSven Schnelle tp->inattr = TF_INPUT;
11969603cb33SSven Schnelle }
11979603cb33SSven Schnelle
11989eb99b94SSven Schnelle tty->winsize.ws_row = tty3270_tty_rows(tp);
11999603cb33SSven Schnelle tty->winsize.ws_col = tp->view.cols;
12009603cb33SSven Schnelle rc = tty_port_install(&tp->port, driver, tty);
12019603cb33SSven Schnelle if (rc) {
12029603cb33SSven Schnelle raw3270_put_view(&tp->view);
12039603cb33SSven Schnelle return rc;
12049603cb33SSven Schnelle }
12059603cb33SSven Schnelle tty->driver_data = tp;
12069603cb33SSven Schnelle return 0;
12079603cb33SSven Schnelle }
12089603cb33SSven Schnelle
12099603cb33SSven Schnelle /*
12109603cb33SSven Schnelle * This routine is called whenever a 3270 tty is opened.
12119603cb33SSven Schnelle */
tty3270_open(struct tty_struct * tty,struct file * filp)1212e6d98bb8SSven Schnelle static int tty3270_open(struct tty_struct *tty, struct file *filp)
12139603cb33SSven Schnelle {
12149603cb33SSven Schnelle struct tty3270 *tp = tty->driver_data;
12159603cb33SSven Schnelle struct tty_port *port = &tp->port;
12169603cb33SSven Schnelle
12179603cb33SSven Schnelle port->count++;
12189603cb33SSven Schnelle tty_port_tty_set(port, tty);
12199603cb33SSven Schnelle return 0;
12209603cb33SSven Schnelle }
12219603cb33SSven Schnelle
12229603cb33SSven Schnelle /*
12239603cb33SSven Schnelle * This routine is called when the 3270 tty is closed. We wait
12249603cb33SSven Schnelle * for the remaining request to be completed. Then we clean up.
12259603cb33SSven Schnelle */
tty3270_close(struct tty_struct * tty,struct file * filp)1226e6d98bb8SSven Schnelle static void tty3270_close(struct tty_struct *tty, struct file *filp)
12279603cb33SSven Schnelle {
12289603cb33SSven Schnelle struct tty3270 *tp = tty->driver_data;
12299603cb33SSven Schnelle
12309603cb33SSven Schnelle if (tty->count > 1)
12319603cb33SSven Schnelle return;
12329603cb33SSven Schnelle if (tp)
12339603cb33SSven Schnelle tty_port_tty_set(&tp->port, NULL);
12349603cb33SSven Schnelle }
12359603cb33SSven Schnelle
tty3270_cleanup(struct tty_struct * tty)12369603cb33SSven Schnelle static void tty3270_cleanup(struct tty_struct *tty)
12379603cb33SSven Schnelle {
12389603cb33SSven Schnelle struct tty3270 *tp = tty->driver_data;
12399603cb33SSven Schnelle
12409603cb33SSven Schnelle if (tp) {
12419603cb33SSven Schnelle tty->driver_data = NULL;
12429603cb33SSven Schnelle raw3270_put_view(&tp->view);
12439603cb33SSven Schnelle }
12449603cb33SSven Schnelle }
12459603cb33SSven Schnelle
12469603cb33SSven Schnelle /*
12479603cb33SSven Schnelle * We always have room.
12489603cb33SSven Schnelle */
tty3270_write_room(struct tty_struct * tty)1249e6d98bb8SSven Schnelle static unsigned int tty3270_write_room(struct tty_struct *tty)
12509603cb33SSven Schnelle {
12519603cb33SSven Schnelle return INT_MAX;
12529603cb33SSven Schnelle }
12539603cb33SSven Schnelle
12549603cb33SSven Schnelle /*
12559603cb33SSven Schnelle * Insert character into the screen at the current position with the
12569603cb33SSven Schnelle * current color and highlight. This function does NOT do cursor movement.
12579603cb33SSven Schnelle */
tty3270_put_character(struct tty3270 * tp,char ch)12589603cb33SSven Schnelle static void tty3270_put_character(struct tty3270 *tp, char ch)
12599603cb33SSven Schnelle {
12609603cb33SSven Schnelle struct tty3270_line *line;
12619603cb33SSven Schnelle struct tty3270_cell *cell;
12629603cb33SSven Schnelle
12639c138af9SSven Schnelle line = tty3270_get_write_line(tp, tp->cy);
12649603cb33SSven Schnelle if (line->len <= tp->cx) {
12659603cb33SSven Schnelle while (line->len < tp->cx) {
12669603cb33SSven Schnelle cell = line->cells + line->len;
12676e49017cSSven Schnelle cell->character = ' ';
1268c2e9375eSSven Schnelle cell->attributes = tp->attributes;
12699603cb33SSven Schnelle line->len++;
12709603cb33SSven Schnelle }
12719603cb33SSven Schnelle line->len++;
12729603cb33SSven Schnelle }
12739603cb33SSven Schnelle cell = line->cells + tp->cx;
12746e49017cSSven Schnelle cell->character = ch;
1275c2e9375eSSven Schnelle cell->attributes = tp->attributes;
12769c138af9SSven Schnelle line->dirty = 1;
12779603cb33SSven Schnelle }
12789603cb33SSven Schnelle
12799603cb33SSven Schnelle /*
12809603cb33SSven Schnelle * Do carriage return.
12819603cb33SSven Schnelle */
tty3270_cr(struct tty3270 * tp)1282e6d98bb8SSven Schnelle static void tty3270_cr(struct tty3270 *tp)
12839603cb33SSven Schnelle {
12849603cb33SSven Schnelle tp->cx = 0;
12859603cb33SSven Schnelle }
12869603cb33SSven Schnelle
12879603cb33SSven Schnelle /*
12889603cb33SSven Schnelle * Do line feed.
12899603cb33SSven Schnelle */
tty3270_lf(struct tty3270 * tp)1290e6d98bb8SSven Schnelle static void tty3270_lf(struct tty3270 *tp)
12919603cb33SSven Schnelle {
12929c138af9SSven Schnelle struct tty3270_line *line;
12939603cb33SSven Schnelle int i;
12949603cb33SSven Schnelle
12959eb99b94SSven Schnelle if (tp->cy < tty3270_tty_rows(tp) - 1) {
12969603cb33SSven Schnelle tp->cy++;
12979c138af9SSven Schnelle } else {
12989c138af9SSven Schnelle tp->line_view_start = tty3270_line_increment(tp, tp->line_view_start, 1);
12999c138af9SSven Schnelle tp->line_write_start = tty3270_line_increment(tp, tp->line_write_start, 1);
13009c138af9SSven Schnelle for (i = 0; i < tty3270_tty_rows(tp); i++)
13019c138af9SSven Schnelle tty3270_get_view_line(tp, i)->dirty = 1;
13029603cb33SSven Schnelle }
13039c138af9SSven Schnelle
13049c138af9SSven Schnelle line = tty3270_get_write_line(tp, tp->cy);
13059c138af9SSven Schnelle line->len = 0;
13069c138af9SSven Schnelle line->dirty = 1;
13079603cb33SSven Schnelle }
13089603cb33SSven Schnelle
tty3270_ri(struct tty3270 * tp)1309e6d98bb8SSven Schnelle static void tty3270_ri(struct tty3270 *tp)
13109603cb33SSven Schnelle {
13119c138af9SSven Schnelle if (tp->cy > 0)
13129603cb33SSven Schnelle tp->cy--;
13139603cb33SSven Schnelle }
13149603cb33SSven Schnelle
tty3270_reset_cell(struct tty3270 * tp,struct tty3270_cell * cell)1315c2e9375eSSven Schnelle static void tty3270_reset_cell(struct tty3270 *tp, struct tty3270_cell *cell)
1316c2e9375eSSven Schnelle {
13176e49017cSSven Schnelle cell->character = ' ';
1318c2e9375eSSven Schnelle tty3270_reset_attributes(&cell->attributes);
1319c2e9375eSSven Schnelle }
1320c2e9375eSSven Schnelle
13219603cb33SSven Schnelle /*
13229603cb33SSven Schnelle * Insert characters at current position.
13239603cb33SSven Schnelle */
tty3270_insert_characters(struct tty3270 * tp,int n)1324e6d98bb8SSven Schnelle static void tty3270_insert_characters(struct tty3270 *tp, int n)
13259603cb33SSven Schnelle {
13269603cb33SSven Schnelle struct tty3270_line *line;
13279603cb33SSven Schnelle int k;
13289603cb33SSven Schnelle
13299c138af9SSven Schnelle line = tty3270_get_write_line(tp, tp->cy);
1330c2e9375eSSven Schnelle while (line->len < tp->cx)
1331c2e9375eSSven Schnelle tty3270_reset_cell(tp, &line->cells[line->len++]);
13329603cb33SSven Schnelle if (n > tp->view.cols - tp->cx)
13339603cb33SSven Schnelle n = tp->view.cols - tp->cx;
13349603cb33SSven Schnelle k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n);
13359603cb33SSven Schnelle while (k--)
13369603cb33SSven Schnelle line->cells[tp->cx + n + k] = line->cells[tp->cx + k];
13379603cb33SSven Schnelle line->len += n;
13389603cb33SSven Schnelle if (line->len > tp->view.cols)
13399603cb33SSven Schnelle line->len = tp->view.cols;
13409603cb33SSven Schnelle while (n-- > 0) {
13416e49017cSSven Schnelle line->cells[tp->cx + n].character = ' ';
1342c2e9375eSSven Schnelle line->cells[tp->cx + n].attributes = tp->attributes;
13439603cb33SSven Schnelle }
13449603cb33SSven Schnelle }
13459603cb33SSven Schnelle
13469603cb33SSven Schnelle /*
13479603cb33SSven Schnelle * Delete characters at current position.
13489603cb33SSven Schnelle */
tty3270_delete_characters(struct tty3270 * tp,int n)1349e6d98bb8SSven Schnelle static void tty3270_delete_characters(struct tty3270 *tp, int n)
13509603cb33SSven Schnelle {
13519603cb33SSven Schnelle struct tty3270_line *line;
13529603cb33SSven Schnelle int i;
13539603cb33SSven Schnelle
13549c138af9SSven Schnelle line = tty3270_get_write_line(tp, tp->cy);
13559603cb33SSven Schnelle if (line->len <= tp->cx)
13569603cb33SSven Schnelle return;
13579603cb33SSven Schnelle if (line->len - tp->cx <= n) {
13589603cb33SSven Schnelle line->len = tp->cx;
13599603cb33SSven Schnelle return;
13609603cb33SSven Schnelle }
13619603cb33SSven Schnelle for (i = tp->cx; i + n < line->len; i++)
13629603cb33SSven Schnelle line->cells[i] = line->cells[i + n];
13639603cb33SSven Schnelle line->len -= n;
13649603cb33SSven Schnelle }
13659603cb33SSven Schnelle
13669603cb33SSven Schnelle /*
13679603cb33SSven Schnelle * Erase characters at current position.
13689603cb33SSven Schnelle */
tty3270_erase_characters(struct tty3270 * tp,int n)1369e6d98bb8SSven Schnelle static void tty3270_erase_characters(struct tty3270 *tp, int n)
13709603cb33SSven Schnelle {
13719603cb33SSven Schnelle struct tty3270_line *line;
13729603cb33SSven Schnelle struct tty3270_cell *cell;
13739603cb33SSven Schnelle
13749c138af9SSven Schnelle line = tty3270_get_write_line(tp, tp->cy);
13759603cb33SSven Schnelle while (line->len > tp->cx && n-- > 0) {
13769603cb33SSven Schnelle cell = line->cells + tp->cx++;
1377c2e9375eSSven Schnelle tty3270_reset_cell(tp, cell);
13789603cb33SSven Schnelle }
13799603cb33SSven Schnelle tp->cx += n;
13809603cb33SSven Schnelle tp->cx = min_t(int, tp->cx, tp->view.cols - 1);
13819603cb33SSven Schnelle }
13829603cb33SSven Schnelle
13839603cb33SSven Schnelle /*
13849603cb33SSven Schnelle * Erase line, 3 different cases:
13859603cb33SSven Schnelle * Esc [ 0 K Erase from current position to end of line inclusive
13869603cb33SSven Schnelle * Esc [ 1 K Erase from beginning of line to current position inclusive
13879603cb33SSven Schnelle * Esc [ 2 K Erase entire line (without moving cursor)
13889603cb33SSven Schnelle */
tty3270_erase_line(struct tty3270 * tp,int mode)1389e6d98bb8SSven Schnelle static void tty3270_erase_line(struct tty3270 *tp, int mode)
13909603cb33SSven Schnelle {
13919603cb33SSven Schnelle struct tty3270_line *line;
13929603cb33SSven Schnelle struct tty3270_cell *cell;
13934043ea22SSven Schnelle int i, start, end;
13949603cb33SSven Schnelle
13959c138af9SSven Schnelle line = tty3270_get_write_line(tp, tp->cy);
13964043ea22SSven Schnelle
1397815f3eeeSSven Schnelle switch (mode) {
1398815f3eeeSSven Schnelle case 0:
13994043ea22SSven Schnelle start = tp->cx;
14004043ea22SSven Schnelle end = tp->view.cols;
1401815f3eeeSSven Schnelle break;
1402815f3eeeSSven Schnelle case 1:
14034043ea22SSven Schnelle start = 0;
14044043ea22SSven Schnelle end = tp->cx;
1405815f3eeeSSven Schnelle break;
1406815f3eeeSSven Schnelle case 2:
14074043ea22SSven Schnelle start = 0;
14084043ea22SSven Schnelle end = tp->view.cols;
1409815f3eeeSSven Schnelle break;
1410815f3eeeSSven Schnelle default:
1411815f3eeeSSven Schnelle return;
1412815f3eeeSSven Schnelle }
14134043ea22SSven Schnelle
14144043ea22SSven Schnelle for (i = start; i < end; i++) {
14154043ea22SSven Schnelle cell = line->cells + i;
14164043ea22SSven Schnelle tty3270_reset_cell(tp, cell);
14174043ea22SSven Schnelle cell->attributes.b_color = tp->attributes.b_color;
14184043ea22SSven Schnelle }
14194043ea22SSven Schnelle
14204043ea22SSven Schnelle if (line->len <= end)
14214043ea22SSven Schnelle line->len = end;
14229603cb33SSven Schnelle }
14239603cb33SSven Schnelle
14249603cb33SSven Schnelle /*
14259603cb33SSven Schnelle * Erase display, 3 different cases:
14269603cb33SSven Schnelle * Esc [ 0 J Erase from current position to bottom of screen inclusive
14279603cb33SSven Schnelle * Esc [ 1 J Erase from top of screen to current position inclusive
14289603cb33SSven Schnelle * Esc [ 2 J Erase entire screen (without moving the cursor)
14299603cb33SSven Schnelle */
tty3270_erase_display(struct tty3270 * tp,int mode)1430e6d98bb8SSven Schnelle static void tty3270_erase_display(struct tty3270 *tp, int mode)
14319603cb33SSven Schnelle {
14329c138af9SSven Schnelle struct tty3270_line *line;
143365b77ccbSSven Schnelle int i, start, end;
14349603cb33SSven Schnelle
143565b77ccbSSven Schnelle switch (mode) {
143665b77ccbSSven Schnelle case 0:
14379603cb33SSven Schnelle tty3270_erase_line(tp, 0);
143865b77ccbSSven Schnelle start = tp->cy + 1;
14399eb99b94SSven Schnelle end = tty3270_tty_rows(tp);
144065b77ccbSSven Schnelle break;
144165b77ccbSSven Schnelle case 1:
144265b77ccbSSven Schnelle start = 0;
144365b77ccbSSven Schnelle end = tp->cy;
14449603cb33SSven Schnelle tty3270_erase_line(tp, 1);
144565b77ccbSSven Schnelle break;
144665b77ccbSSven Schnelle case 2:
144765b77ccbSSven Schnelle start = 0;
14489eb99b94SSven Schnelle end = tty3270_tty_rows(tp);
144965b77ccbSSven Schnelle break;
145065b77ccbSSven Schnelle default:
145165b77ccbSSven Schnelle return;
145265b77ccbSSven Schnelle }
145365b77ccbSSven Schnelle for (i = start; i < end; i++) {
14549c138af9SSven Schnelle line = tty3270_get_write_line(tp, i);
14559c138af9SSven Schnelle line->len = 0;
14569c138af9SSven Schnelle line->dirty = 1;
14579603cb33SSven Schnelle }
14589603cb33SSven Schnelle }
14599603cb33SSven Schnelle
14609603cb33SSven Schnelle /*
14619603cb33SSven Schnelle * Set attributes found in an escape sequence.
14629603cb33SSven Schnelle * Esc [ <attr> ; <attr> ; ... m
14639603cb33SSven Schnelle */
tty3270_set_attributes(struct tty3270 * tp)1464e6d98bb8SSven Schnelle static void tty3270_set_attributes(struct tty3270 *tp)
14659603cb33SSven Schnelle {
14669603cb33SSven Schnelle int i, attr;
14679603cb33SSven Schnelle
14689603cb33SSven Schnelle for (i = 0; i <= tp->esc_npar; i++) {
14699603cb33SSven Schnelle attr = tp->esc_par[i];
14709603cb33SSven Schnelle switch (attr) {
14719603cb33SSven Schnelle case 0: /* Reset */
1472c2e9375eSSven Schnelle tty3270_reset_attributes(&tp->attributes);
14739603cb33SSven Schnelle break;
14749603cb33SSven Schnelle /* Highlight. */
14759603cb33SSven Schnelle case 4: /* Start underlining. */
14760573fff2SSven Schnelle tp->attributes.highlight = TTY3270_HIGHLIGHT_UNDERSCORE;
14779603cb33SSven Schnelle break;
14789603cb33SSven Schnelle case 5: /* Start blink. */
14790573fff2SSven Schnelle tp->attributes.highlight = TTY3270_HIGHLIGHT_BLINK;
14809603cb33SSven Schnelle break;
14819603cb33SSven Schnelle case 7: /* Start reverse. */
14820573fff2SSven Schnelle tp->attributes.highlight = TTY3270_HIGHLIGHT_REVERSE;
14839603cb33SSven Schnelle break;
14849603cb33SSven Schnelle case 24: /* End underlining */
14850573fff2SSven Schnelle tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_UNDERSCORE;
14869603cb33SSven Schnelle break;
14879603cb33SSven Schnelle case 25: /* End blink. */
14880573fff2SSven Schnelle tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_BLINK;
14899603cb33SSven Schnelle break;
14909603cb33SSven Schnelle case 27: /* End reverse. */
14910573fff2SSven Schnelle tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_REVERSE;
14929603cb33SSven Schnelle break;
14939603cb33SSven Schnelle /* Foreground color. */
14949603cb33SSven Schnelle case 30: /* Black */
14959603cb33SSven Schnelle case 31: /* Red */
14969603cb33SSven Schnelle case 32: /* Green */
14979603cb33SSven Schnelle case 33: /* Yellow */
14989603cb33SSven Schnelle case 34: /* Blue */
14999603cb33SSven Schnelle case 35: /* Magenta */
15009603cb33SSven Schnelle case 36: /* Cyan */
15019603cb33SSven Schnelle case 37: /* White */
15029603cb33SSven Schnelle case 39: /* Black */
150318fc2e93SSven Schnelle tp->attributes.f_color = attr - 30;
15044043ea22SSven Schnelle break;
15054043ea22SSven Schnelle /* Background color. */
15064043ea22SSven Schnelle case 40: /* Black */
15074043ea22SSven Schnelle case 41: /* Red */
15084043ea22SSven Schnelle case 42: /* Green */
15094043ea22SSven Schnelle case 43: /* Yellow */
15104043ea22SSven Schnelle case 44: /* Blue */
15114043ea22SSven Schnelle case 45: /* Magenta */
15124043ea22SSven Schnelle case 46: /* Cyan */
15134043ea22SSven Schnelle case 47: /* White */
15144043ea22SSven Schnelle case 49: /* Black */
151518fc2e93SSven Schnelle tp->attributes.b_color = attr - 40;
15169603cb33SSven Schnelle break;
15179603cb33SSven Schnelle }
15189603cb33SSven Schnelle }
15199603cb33SSven Schnelle }
15209603cb33SSven Schnelle
tty3270_getpar(struct tty3270 * tp,int ix)1521e6d98bb8SSven Schnelle static inline int tty3270_getpar(struct tty3270 *tp, int ix)
15229603cb33SSven Schnelle {
15239603cb33SSven Schnelle return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1;
15249603cb33SSven Schnelle }
15259603cb33SSven Schnelle
tty3270_goto_xy(struct tty3270 * tp,int cx,int cy)1526e6d98bb8SSven Schnelle static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy)
15279603cb33SSven Schnelle {
15284043ea22SSven Schnelle struct tty3270_line *line;
15294043ea22SSven Schnelle struct tty3270_cell *cell;
15309603cb33SSven Schnelle int max_cx = max(0, cx);
15319603cb33SSven Schnelle int max_cy = max(0, cy);
15329603cb33SSven Schnelle
15339603cb33SSven Schnelle tp->cx = min_t(int, tp->view.cols - 1, max_cx);
15349c138af9SSven Schnelle line = tty3270_get_write_line(tp, tp->cy);
15354043ea22SSven Schnelle while (line->len < tp->cx) {
15364043ea22SSven Schnelle cell = line->cells + line->len;
15374043ea22SSven Schnelle cell->character = ' ';
15384043ea22SSven Schnelle cell->attributes = tp->attributes;
15394043ea22SSven Schnelle line->len++;
15404043ea22SSven Schnelle }
15419c138af9SSven Schnelle tp->cy = min_t(int, tty3270_tty_rows(tp) - 1, max_cy);
15429603cb33SSven Schnelle }
15439603cb33SSven Schnelle
15449603cb33SSven Schnelle /*
15459603cb33SSven Schnelle * Process escape sequences. Known sequences:
15469603cb33SSven Schnelle * Esc 7 Save Cursor Position
15479603cb33SSven Schnelle * Esc 8 Restore Cursor Position
15489603cb33SSven Schnelle * Esc [ Pn ; Pn ; .. m Set attributes
15499603cb33SSven Schnelle * Esc [ Pn ; Pn H Cursor Position
15509603cb33SSven Schnelle * Esc [ Pn ; Pn f Cursor Position
15519603cb33SSven Schnelle * Esc [ Pn A Cursor Up
15529603cb33SSven Schnelle * Esc [ Pn B Cursor Down
15539603cb33SSven Schnelle * Esc [ Pn C Cursor Forward
15549603cb33SSven Schnelle * Esc [ Pn D Cursor Backward
15559603cb33SSven Schnelle * Esc [ Pn G Cursor Horizontal Absolute
15569603cb33SSven Schnelle * Esc [ Pn X Erase Characters
15579603cb33SSven Schnelle * Esc [ Ps J Erase in Display
15589603cb33SSven Schnelle * Esc [ Ps K Erase in Line
15599603cb33SSven Schnelle * // FIXME: add all the new ones.
15609603cb33SSven Schnelle *
15619603cb33SSven Schnelle * Pn is a numeric parameter, a string of zero or more decimal digits.
15629603cb33SSven Schnelle * Ps is a selective parameter.
15639603cb33SSven Schnelle */
tty3270_escape_sequence(struct tty3270 * tp,char ch)1564e6d98bb8SSven Schnelle static void tty3270_escape_sequence(struct tty3270 *tp, char ch)
15659603cb33SSven Schnelle {
1566303bac9dSSven Schnelle enum { ES_NORMAL, ES_ESC, ES_SQUARE, ES_PAREN, ES_GETPARS };
15679603cb33SSven Schnelle
1568303bac9dSSven Schnelle if (tp->esc_state == ES_NORMAL) {
15699603cb33SSven Schnelle if (ch == 0x1b)
15709603cb33SSven Schnelle /* Starting new escape sequence. */
1571303bac9dSSven Schnelle tp->esc_state = ES_ESC;
15729603cb33SSven Schnelle return;
15739603cb33SSven Schnelle }
1574303bac9dSSven Schnelle if (tp->esc_state == ES_ESC) {
1575303bac9dSSven Schnelle tp->esc_state = ES_NORMAL;
15769603cb33SSven Schnelle switch (ch) {
15779603cb33SSven Schnelle case '[':
1578303bac9dSSven Schnelle tp->esc_state = ES_SQUARE;
15799603cb33SSven Schnelle break;
1580e4b57b93SSven Schnelle case '(':
1581303bac9dSSven Schnelle tp->esc_state = ES_PAREN;
1582e4b57b93SSven Schnelle break;
15839603cb33SSven Schnelle case 'E':
15849603cb33SSven Schnelle tty3270_cr(tp);
15859603cb33SSven Schnelle tty3270_lf(tp);
15869603cb33SSven Schnelle break;
15879603cb33SSven Schnelle case 'M':
15889603cb33SSven Schnelle tty3270_ri(tp);
15899603cb33SSven Schnelle break;
15909603cb33SSven Schnelle case 'D':
15919603cb33SSven Schnelle tty3270_lf(tp);
15929603cb33SSven Schnelle break;
15939603cb33SSven Schnelle case 'Z': /* Respond ID. */
15949603cb33SSven Schnelle kbd_puts_queue(&tp->port, "\033[?6c");
15959603cb33SSven Schnelle break;
15969603cb33SSven Schnelle case '7': /* Save cursor position. */
15979603cb33SSven Schnelle tp->saved_cx = tp->cx;
15989603cb33SSven Schnelle tp->saved_cy = tp->cy;
1599c2e9375eSSven Schnelle tp->saved_attributes = tp->attributes;
16009603cb33SSven Schnelle break;
16019603cb33SSven Schnelle case '8': /* Restore cursor position. */
16029603cb33SSven Schnelle tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
1603c2e9375eSSven Schnelle tp->attributes = tp->saved_attributes;
16049603cb33SSven Schnelle break;
16059603cb33SSven Schnelle case 'c': /* Reset terminal. */
1606f8674930SSven Schnelle tp->cx = 0;
1607f8674930SSven Schnelle tp->cy = 0;
1608f8674930SSven Schnelle tp->saved_cx = 0;
1609f8674930SSven Schnelle tp->saved_cy = 0;
1610c2e9375eSSven Schnelle tty3270_reset_attributes(&tp->attributes);
1611c2e9375eSSven Schnelle tty3270_reset_attributes(&tp->saved_attributes);
16129603cb33SSven Schnelle tty3270_erase_display(tp, 2);
16139603cb33SSven Schnelle break;
16149603cb33SSven Schnelle }
16159603cb33SSven Schnelle return;
16169603cb33SSven Schnelle }
1617e4b57b93SSven Schnelle
1618e4b57b93SSven Schnelle switch (tp->esc_state) {
1619303bac9dSSven Schnelle case ES_PAREN:
1620303bac9dSSven Schnelle tp->esc_state = ES_NORMAL;
1621e4b57b93SSven Schnelle switch (ch) {
1622e4b57b93SSven Schnelle case 'B':
1623e4b57b93SSven Schnelle tp->attributes.alternate_charset = 0;
1624e4b57b93SSven Schnelle break;
1625e4b57b93SSven Schnelle case '0':
1626e4b57b93SSven Schnelle tp->attributes.alternate_charset = 1;
1627e4b57b93SSven Schnelle break;
1628e4b57b93SSven Schnelle }
1629e4b57b93SSven Schnelle return;
1630303bac9dSSven Schnelle case ES_SQUARE:
1631303bac9dSSven Schnelle tp->esc_state = ES_GETPARS;
16329603cb33SSven Schnelle memset(tp->esc_par, 0, sizeof(tp->esc_par));
16339603cb33SSven Schnelle tp->esc_npar = 0;
16349603cb33SSven Schnelle tp->esc_ques = (ch == '?');
16359603cb33SSven Schnelle if (tp->esc_ques)
16369603cb33SSven Schnelle return;
1637e4b57b93SSven Schnelle fallthrough;
1638303bac9dSSven Schnelle case ES_GETPARS:
16399603cb33SSven Schnelle if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) {
16409603cb33SSven Schnelle tp->esc_npar++;
16419603cb33SSven Schnelle return;
16429603cb33SSven Schnelle }
16439603cb33SSven Schnelle if (ch >= '0' && ch <= '9') {
16449603cb33SSven Schnelle tp->esc_par[tp->esc_npar] *= 10;
16459603cb33SSven Schnelle tp->esc_par[tp->esc_npar] += ch - '0';
16469603cb33SSven Schnelle return;
16479603cb33SSven Schnelle }
1648e4b57b93SSven Schnelle break;
1649e4b57b93SSven Schnelle default:
1650e4b57b93SSven Schnelle break;
16519603cb33SSven Schnelle }
1652303bac9dSSven Schnelle tp->esc_state = ES_NORMAL;
16539603cb33SSven Schnelle if (ch == 'n' && !tp->esc_ques) {
16549603cb33SSven Schnelle if (tp->esc_par[0] == 5) /* Status report. */
16559603cb33SSven Schnelle kbd_puts_queue(&tp->port, "\033[0n");
16569603cb33SSven Schnelle else if (tp->esc_par[0] == 6) { /* Cursor report. */
16579603cb33SSven Schnelle char buf[40];
16587ef21387SSven Schnelle
16599603cb33SSven Schnelle sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1);
16609603cb33SSven Schnelle kbd_puts_queue(&tp->port, buf);
16619603cb33SSven Schnelle }
16629603cb33SSven Schnelle return;
16639603cb33SSven Schnelle }
16649603cb33SSven Schnelle if (tp->esc_ques)
16659603cb33SSven Schnelle return;
16669603cb33SSven Schnelle switch (ch) {
16679603cb33SSven Schnelle case 'm':
16689603cb33SSven Schnelle tty3270_set_attributes(tp);
16699603cb33SSven Schnelle break;
16709603cb33SSven Schnelle case 'H': /* Set cursor position. */
16719603cb33SSven Schnelle case 'f':
16729603cb33SSven Schnelle tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1,
16739603cb33SSven Schnelle tty3270_getpar(tp, 0) - 1);
16749603cb33SSven Schnelle break;
16759603cb33SSven Schnelle case 'd': /* Set y position. */
16769603cb33SSven Schnelle tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1);
16779603cb33SSven Schnelle break;
16789603cb33SSven Schnelle case 'A': /* Cursor up. */
16799603cb33SSven Schnelle case 'F':
16809603cb33SSven Schnelle tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0));
16819603cb33SSven Schnelle break;
16829603cb33SSven Schnelle case 'B': /* Cursor down. */
16839603cb33SSven Schnelle case 'e':
16849603cb33SSven Schnelle case 'E':
16859603cb33SSven Schnelle tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0));
16869603cb33SSven Schnelle break;
16879603cb33SSven Schnelle case 'C': /* Cursor forward. */
16889603cb33SSven Schnelle case 'a':
16899603cb33SSven Schnelle tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy);
16909603cb33SSven Schnelle break;
16919603cb33SSven Schnelle case 'D': /* Cursor backward. */
16929603cb33SSven Schnelle tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy);
16939603cb33SSven Schnelle break;
16949603cb33SSven Schnelle case 'G': /* Set x position. */
16959603cb33SSven Schnelle case '`':
16969603cb33SSven Schnelle tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy);
16979603cb33SSven Schnelle break;
16989603cb33SSven Schnelle case 'X': /* Erase Characters. */
16999603cb33SSven Schnelle tty3270_erase_characters(tp, tty3270_getpar(tp, 0));
17009603cb33SSven Schnelle break;
17019603cb33SSven Schnelle case 'J': /* Erase display. */
17029603cb33SSven Schnelle tty3270_erase_display(tp, tp->esc_par[0]);
17039603cb33SSven Schnelle break;
17049603cb33SSven Schnelle case 'K': /* Erase line. */
17059603cb33SSven Schnelle tty3270_erase_line(tp, tp->esc_par[0]);
17069603cb33SSven Schnelle break;
17079603cb33SSven Schnelle case 'P': /* Delete characters. */
17089603cb33SSven Schnelle tty3270_delete_characters(tp, tty3270_getpar(tp, 0));
17099603cb33SSven Schnelle break;
17109603cb33SSven Schnelle case '@': /* Insert characters. */
17119603cb33SSven Schnelle tty3270_insert_characters(tp, tty3270_getpar(tp, 0));
17129603cb33SSven Schnelle break;
17139603cb33SSven Schnelle case 's': /* Save cursor position. */
17149603cb33SSven Schnelle tp->saved_cx = tp->cx;
17159603cb33SSven Schnelle tp->saved_cy = tp->cy;
1716c2e9375eSSven Schnelle tp->saved_attributes = tp->attributes;
17179603cb33SSven Schnelle break;
17189603cb33SSven Schnelle case 'u': /* Restore cursor position. */
17199603cb33SSven Schnelle tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
1720c2e9375eSSven Schnelle tp->attributes = tp->saved_attributes;
17219603cb33SSven Schnelle break;
17229603cb33SSven Schnelle }
17239603cb33SSven Schnelle }
17249603cb33SSven Schnelle
17259603cb33SSven Schnelle /*
17269603cb33SSven Schnelle * String write routine for 3270 ttys
17279603cb33SSven Schnelle */
tty3270_do_write(struct tty3270 * tp,struct tty_struct * tty,const unsigned char * buf,int count)1728e6d98bb8SSven Schnelle static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty,
17299603cb33SSven Schnelle const unsigned char *buf, int count)
17309603cb33SSven Schnelle {
17319603cb33SSven Schnelle int i_msg, i;
17329603cb33SSven Schnelle
17339603cb33SSven Schnelle spin_lock_irq(&tp->view.lock);
17349603cb33SSven Schnelle for (i_msg = 0; !tty->flow.stopped && i_msg < count; i_msg++) {
17359603cb33SSven Schnelle if (tp->esc_state != 0) {
17369603cb33SSven Schnelle /* Continue escape sequence. */
17379603cb33SSven Schnelle tty3270_escape_sequence(tp, buf[i_msg]);
17389603cb33SSven Schnelle continue;
17399603cb33SSven Schnelle }
17409603cb33SSven Schnelle
17419603cb33SSven Schnelle switch (buf[i_msg]) {
1742970cf9a9SSven Schnelle case 0x00:
1743970cf9a9SSven Schnelle break;
17449603cb33SSven Schnelle case 0x07: /* '\a' -- Alarm */
17459603cb33SSven Schnelle tp->wcc |= TW_PLUSALARM;
17469603cb33SSven Schnelle break;
17479603cb33SSven Schnelle case 0x08: /* Backspace. */
17489603cb33SSven Schnelle if (tp->cx > 0) {
17499603cb33SSven Schnelle tp->cx--;
17509603cb33SSven Schnelle tty3270_put_character(tp, ' ');
17519603cb33SSven Schnelle }
17529603cb33SSven Schnelle break;
17539603cb33SSven Schnelle case 0x09: /* '\t' -- Tabulate */
17549603cb33SSven Schnelle for (i = tp->cx % 8; i < 8; i++) {
17559603cb33SSven Schnelle if (tp->cx >= tp->view.cols) {
17569603cb33SSven Schnelle tty3270_cr(tp);
17579603cb33SSven Schnelle tty3270_lf(tp);
17589603cb33SSven Schnelle break;
17599603cb33SSven Schnelle }
17609603cb33SSven Schnelle tty3270_put_character(tp, ' ');
17619603cb33SSven Schnelle tp->cx++;
17629603cb33SSven Schnelle }
17639603cb33SSven Schnelle break;
17649603cb33SSven Schnelle case 0x0a: /* '\n' -- New Line */
17659603cb33SSven Schnelle tty3270_cr(tp);
17669603cb33SSven Schnelle tty3270_lf(tp);
17679603cb33SSven Schnelle break;
17689603cb33SSven Schnelle case 0x0c: /* '\f' -- Form Feed */
17699603cb33SSven Schnelle tty3270_erase_display(tp, 2);
1770f8674930SSven Schnelle tp->cx = 0;
1771f8674930SSven Schnelle tp->cy = 0;
17729603cb33SSven Schnelle break;
17739603cb33SSven Schnelle case 0x0d: /* '\r' -- Carriage Return */
17749603cb33SSven Schnelle tp->cx = 0;
17759603cb33SSven Schnelle break;
177694dbb0a7SSven Schnelle case 0x0e:
177794dbb0a7SSven Schnelle tp->attributes.alternate_charset = 1;
177894dbb0a7SSven Schnelle break;
17799603cb33SSven Schnelle case 0x0f: /* SuSE "exit alternate mode" */
178094dbb0a7SSven Schnelle tp->attributes.alternate_charset = 0;
17819603cb33SSven Schnelle break;
17829603cb33SSven Schnelle case 0x1b: /* Start escape sequence. */
17839603cb33SSven Schnelle tty3270_escape_sequence(tp, buf[i_msg]);
17849603cb33SSven Schnelle break;
17859603cb33SSven Schnelle default: /* Insert normal character. */
17869603cb33SSven Schnelle if (tp->cx >= tp->view.cols) {
17879603cb33SSven Schnelle tty3270_cr(tp);
17889603cb33SSven Schnelle tty3270_lf(tp);
17899603cb33SSven Schnelle }
17909603cb33SSven Schnelle tty3270_put_character(tp, buf[i_msg]);
17919603cb33SSven Schnelle tp->cx++;
17929603cb33SSven Schnelle break;
17939603cb33SSven Schnelle }
17949603cb33SSven Schnelle }
17959603cb33SSven Schnelle /* Setup timer to update display after 1/10 second */
1796ba5c2e2aSSven Schnelle tp->update_flags |= TTY_UPDATE_LINES;
17979603cb33SSven Schnelle if (!timer_pending(&tp->timer))
17989e1d1d8eSSven Schnelle tty3270_set_timer(tp, msecs_to_jiffies(100));
17999603cb33SSven Schnelle
18009603cb33SSven Schnelle spin_unlock_irq(&tp->view.lock);
18019603cb33SSven Schnelle }
18029603cb33SSven Schnelle
18039603cb33SSven Schnelle /*
18049603cb33SSven Schnelle * String write routine for 3270 ttys
18059603cb33SSven Schnelle */
tty3270_write(struct tty_struct * tty,const u8 * buf,size_t count)1806*95713967SJiri Slaby (SUSE) static ssize_t tty3270_write(struct tty_struct *tty, const u8 *buf,
1807*95713967SJiri Slaby (SUSE) size_t count)
18089603cb33SSven Schnelle {
18099603cb33SSven Schnelle struct tty3270 *tp;
18109603cb33SSven Schnelle
18119603cb33SSven Schnelle tp = tty->driver_data;
18129603cb33SSven Schnelle if (!tp)
18139603cb33SSven Schnelle return 0;
18149603cb33SSven Schnelle if (tp->char_count > 0) {
18159603cb33SSven Schnelle tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);
18169603cb33SSven Schnelle tp->char_count = 0;
18179603cb33SSven Schnelle }
18189603cb33SSven Schnelle tty3270_do_write(tp, tty, buf, count);
18199603cb33SSven Schnelle return count;
18209603cb33SSven Schnelle }
18219603cb33SSven Schnelle
18229603cb33SSven Schnelle /*
18239603cb33SSven Schnelle * Put single characters to the ttys character buffer
18249603cb33SSven Schnelle */
tty3270_put_char(struct tty_struct * tty,u8 ch)1825dcaafbe6SJiri Slaby (SUSE) static int tty3270_put_char(struct tty_struct *tty, u8 ch)
18269603cb33SSven Schnelle {
18279603cb33SSven Schnelle struct tty3270 *tp;
18289603cb33SSven Schnelle
18299603cb33SSven Schnelle tp = tty->driver_data;
18309603cb33SSven Schnelle if (!tp || tp->char_count >= TTY3270_CHAR_BUF_SIZE)
18319603cb33SSven Schnelle return 0;
18329603cb33SSven Schnelle tp->char_buf[tp->char_count++] = ch;
18339603cb33SSven Schnelle return 1;
18349603cb33SSven Schnelle }
18359603cb33SSven Schnelle
18369603cb33SSven Schnelle /*
18379603cb33SSven Schnelle * Flush all characters from the ttys characeter buffer put there
18389603cb33SSven Schnelle * by tty3270_put_char.
18399603cb33SSven Schnelle */
tty3270_flush_chars(struct tty_struct * tty)1840e6d98bb8SSven Schnelle static void tty3270_flush_chars(struct tty_struct *tty)
18419603cb33SSven Schnelle {
18429603cb33SSven Schnelle struct tty3270 *tp;
18439603cb33SSven Schnelle
18449603cb33SSven Schnelle tp = tty->driver_data;
18459603cb33SSven Schnelle if (!tp)
18469603cb33SSven Schnelle return;
18479603cb33SSven Schnelle if (tp->char_count > 0) {
18489603cb33SSven Schnelle tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);
18499603cb33SSven Schnelle tp->char_count = 0;
18509603cb33SSven Schnelle }
18519603cb33SSven Schnelle }
18529603cb33SSven Schnelle
18539603cb33SSven Schnelle /*
18549603cb33SSven Schnelle * Check for visible/invisible input switches
18559603cb33SSven Schnelle */
tty3270_set_termios(struct tty_struct * tty,const struct ktermios * old)1856e6d98bb8SSven Schnelle static void tty3270_set_termios(struct tty_struct *tty, const struct ktermios *old)
18579603cb33SSven Schnelle {
18589603cb33SSven Schnelle struct tty3270 *tp;
18599603cb33SSven Schnelle int new;
18609603cb33SSven Schnelle
18619603cb33SSven Schnelle tp = tty->driver_data;
18629603cb33SSven Schnelle if (!tp)
18639603cb33SSven Schnelle return;
18649603cb33SSven Schnelle spin_lock_irq(&tp->view.lock);
18659603cb33SSven Schnelle if (L_ICANON(tty)) {
18669603cb33SSven Schnelle new = L_ECHO(tty) ? TF_INPUT : TF_INPUTN;
18679603cb33SSven Schnelle if (new != tp->inattr) {
18689603cb33SSven Schnelle tp->inattr = new;
186976485078SSven Schnelle tty3270_update_prompt(tp, "");
18709603cb33SSven Schnelle tty3270_set_timer(tp, 1);
18719603cb33SSven Schnelle }
18729603cb33SSven Schnelle }
18739603cb33SSven Schnelle spin_unlock_irq(&tp->view.lock);
18749603cb33SSven Schnelle }
18759603cb33SSven Schnelle
18769603cb33SSven Schnelle /*
18779603cb33SSven Schnelle * Disable reading from a 3270 tty
18789603cb33SSven Schnelle */
tty3270_throttle(struct tty_struct * tty)1879e6d98bb8SSven Schnelle static void tty3270_throttle(struct tty_struct *tty)
18809603cb33SSven Schnelle {
18819603cb33SSven Schnelle struct tty3270 *tp;
18829603cb33SSven Schnelle
18839603cb33SSven Schnelle tp = tty->driver_data;
18849603cb33SSven Schnelle if (!tp)
18859603cb33SSven Schnelle return;
18869603cb33SSven Schnelle tp->throttle = 1;
18879603cb33SSven Schnelle }
18889603cb33SSven Schnelle
18899603cb33SSven Schnelle /*
18909603cb33SSven Schnelle * Enable reading from a 3270 tty
18919603cb33SSven Schnelle */
tty3270_unthrottle(struct tty_struct * tty)1892e6d98bb8SSven Schnelle static void tty3270_unthrottle(struct tty_struct *tty)
18939603cb33SSven Schnelle {
18949603cb33SSven Schnelle struct tty3270 *tp;
18959603cb33SSven Schnelle
18969603cb33SSven Schnelle tp = tty->driver_data;
18979603cb33SSven Schnelle if (!tp)
18989603cb33SSven Schnelle return;
18999603cb33SSven Schnelle tp->throttle = 0;
19009603cb33SSven Schnelle if (tp->attn)
19019603cb33SSven Schnelle tty3270_issue_read(tp, 1);
19029603cb33SSven Schnelle }
19039603cb33SSven Schnelle
19049603cb33SSven Schnelle /*
19059603cb33SSven Schnelle * Hang up the tty device.
19069603cb33SSven Schnelle */
tty3270_hangup(struct tty_struct * tty)1907e6d98bb8SSven Schnelle static void tty3270_hangup(struct tty_struct *tty)
19089603cb33SSven Schnelle {
19099603cb33SSven Schnelle struct tty3270 *tp;
19109603cb33SSven Schnelle
19119603cb33SSven Schnelle tp = tty->driver_data;
19129603cb33SSven Schnelle if (!tp)
19139603cb33SSven Schnelle return;
19149603cb33SSven Schnelle spin_lock_irq(&tp->view.lock);
1915f8674930SSven Schnelle tp->cx = 0;
1916f8674930SSven Schnelle tp->cy = 0;
1917f8674930SSven Schnelle tp->saved_cx = 0;
1918f8674930SSven Schnelle tp->saved_cy = 0;
1919c2e9375eSSven Schnelle tty3270_reset_attributes(&tp->attributes);
1920c2e9375eSSven Schnelle tty3270_reset_attributes(&tp->saved_attributes);
19219603cb33SSven Schnelle tty3270_blank_screen(tp);
19229603cb33SSven Schnelle tp->update_flags = TTY_UPDATE_ALL;
19239603cb33SSven Schnelle spin_unlock_irq(&tp->view.lock);
19249603cb33SSven Schnelle tty3270_set_timer(tp, 1);
19259603cb33SSven Schnelle }
19269603cb33SSven Schnelle
tty3270_wait_until_sent(struct tty_struct * tty,int timeout)1927e6d98bb8SSven Schnelle static void tty3270_wait_until_sent(struct tty_struct *tty, int timeout)
19289603cb33SSven Schnelle {
19299603cb33SSven Schnelle }
19309603cb33SSven Schnelle
tty3270_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)19319603cb33SSven Schnelle static int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd,
19329603cb33SSven Schnelle unsigned long arg)
19339603cb33SSven Schnelle {
19349603cb33SSven Schnelle struct tty3270 *tp;
19359603cb33SSven Schnelle
19369603cb33SSven Schnelle tp = tty->driver_data;
19379603cb33SSven Schnelle if (!tp)
19389603cb33SSven Schnelle return -ENODEV;
19399603cb33SSven Schnelle if (tty_io_error(tty))
19409603cb33SSven Schnelle return -EIO;
19419603cb33SSven Schnelle return kbd_ioctl(tp->kbd, cmd, arg);
19429603cb33SSven Schnelle }
19439603cb33SSven Schnelle
19449603cb33SSven Schnelle #ifdef CONFIG_COMPAT
tty3270_compat_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)19459603cb33SSven Schnelle static long tty3270_compat_ioctl(struct tty_struct *tty,
19469603cb33SSven Schnelle unsigned int cmd, unsigned long arg)
19479603cb33SSven Schnelle {
19489603cb33SSven Schnelle struct tty3270 *tp;
19499603cb33SSven Schnelle
19509603cb33SSven Schnelle tp = tty->driver_data;
19519603cb33SSven Schnelle if (!tp)
19529603cb33SSven Schnelle return -ENODEV;
19539603cb33SSven Schnelle if (tty_io_error(tty))
19549603cb33SSven Schnelle return -EIO;
19559603cb33SSven Schnelle return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg));
19569603cb33SSven Schnelle }
19579603cb33SSven Schnelle #endif
19589603cb33SSven Schnelle
19599603cb33SSven Schnelle static const struct tty_operations tty3270_ops = {
19609603cb33SSven Schnelle .install = tty3270_install,
19619603cb33SSven Schnelle .cleanup = tty3270_cleanup,
19629603cb33SSven Schnelle .open = tty3270_open,
19639603cb33SSven Schnelle .close = tty3270_close,
19649603cb33SSven Schnelle .write = tty3270_write,
19659603cb33SSven Schnelle .put_char = tty3270_put_char,
19669603cb33SSven Schnelle .flush_chars = tty3270_flush_chars,
19679603cb33SSven Schnelle .write_room = tty3270_write_room,
19689603cb33SSven Schnelle .throttle = tty3270_throttle,
19699603cb33SSven Schnelle .unthrottle = tty3270_unthrottle,
19709603cb33SSven Schnelle .hangup = tty3270_hangup,
19719603cb33SSven Schnelle .wait_until_sent = tty3270_wait_until_sent,
19729603cb33SSven Schnelle .ioctl = tty3270_ioctl,
19739603cb33SSven Schnelle #ifdef CONFIG_COMPAT
19749603cb33SSven Schnelle .compat_ioctl = tty3270_compat_ioctl,
19759603cb33SSven Schnelle #endif
19769603cb33SSven Schnelle .set_termios = tty3270_set_termios
19779603cb33SSven Schnelle };
19789603cb33SSven Schnelle
tty3270_create_cb(int minor)19799603cb33SSven Schnelle static void tty3270_create_cb(int minor)
19809603cb33SSven Schnelle {
19819603cb33SSven Schnelle tty_register_device(tty3270_driver, minor - RAW3270_FIRSTMINOR, NULL);
19829603cb33SSven Schnelle }
19839603cb33SSven Schnelle
tty3270_destroy_cb(int minor)19849603cb33SSven Schnelle static void tty3270_destroy_cb(int minor)
19859603cb33SSven Schnelle {
19869603cb33SSven Schnelle tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR);
19879603cb33SSven Schnelle }
19889603cb33SSven Schnelle
19897ef21387SSven Schnelle static struct raw3270_notifier tty3270_notifier = {
19909603cb33SSven Schnelle .create = tty3270_create_cb,
19919603cb33SSven Schnelle .destroy = tty3270_destroy_cb,
19929603cb33SSven Schnelle };
19939603cb33SSven Schnelle
19949603cb33SSven Schnelle /*
19959603cb33SSven Schnelle * 3270 tty registration code called from tty_init().
19969603cb33SSven Schnelle * Most kernel services (incl. kmalloc) are available at this poimt.
19979603cb33SSven Schnelle */
tty3270_init(void)19989603cb33SSven Schnelle static int __init tty3270_init(void)
19999603cb33SSven Schnelle {
20009603cb33SSven Schnelle struct tty_driver *driver;
20019603cb33SSven Schnelle int ret;
20029603cb33SSven Schnelle
20039603cb33SSven Schnelle driver = tty_alloc_driver(RAW3270_MAXDEVS,
20049603cb33SSven Schnelle TTY_DRIVER_REAL_RAW |
20059603cb33SSven Schnelle TTY_DRIVER_DYNAMIC_DEV |
20069603cb33SSven Schnelle TTY_DRIVER_RESET_TERMIOS);
20079603cb33SSven Schnelle if (IS_ERR(driver))
20089603cb33SSven Schnelle return PTR_ERR(driver);
20099603cb33SSven Schnelle
20109603cb33SSven Schnelle /*
20119603cb33SSven Schnelle * Initialize the tty_driver structure
20129603cb33SSven Schnelle * Entries in tty3270_driver that are NOT initialized:
20139603cb33SSven Schnelle * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
20149603cb33SSven Schnelle */
20159603cb33SSven Schnelle driver->driver_name = "tty3270";
20169603cb33SSven Schnelle driver->name = "3270/tty";
20179603cb33SSven Schnelle driver->major = IBM_TTY3270_MAJOR;
20189603cb33SSven Schnelle driver->minor_start = RAW3270_FIRSTMINOR;
20199603cb33SSven Schnelle driver->name_base = RAW3270_FIRSTMINOR;
20209603cb33SSven Schnelle driver->type = TTY_DRIVER_TYPE_SYSTEM;
20219603cb33SSven Schnelle driver->subtype = SYSTEM_TYPE_TTY;
20229603cb33SSven Schnelle driver->init_termios = tty_std_termios;
20239603cb33SSven Schnelle tty_set_operations(driver, &tty3270_ops);
20249603cb33SSven Schnelle ret = tty_register_driver(driver);
20259603cb33SSven Schnelle if (ret) {
20269603cb33SSven Schnelle tty_driver_kref_put(driver);
20279603cb33SSven Schnelle return ret;
20289603cb33SSven Schnelle }
20299603cb33SSven Schnelle tty3270_driver = driver;
20309603cb33SSven Schnelle raw3270_register_notifier(&tty3270_notifier);
20319603cb33SSven Schnelle return 0;
20329603cb33SSven Schnelle }
20339603cb33SSven Schnelle
tty3270_exit(void)2034e6d98bb8SSven Schnelle static void __exit tty3270_exit(void)
20359603cb33SSven Schnelle {
20369603cb33SSven Schnelle struct tty_driver *driver;
20379603cb33SSven Schnelle
20389603cb33SSven Schnelle raw3270_unregister_notifier(&tty3270_notifier);
20399603cb33SSven Schnelle driver = tty3270_driver;
20409603cb33SSven Schnelle tty3270_driver = NULL;
20419603cb33SSven Schnelle tty_unregister_driver(driver);
20429603cb33SSven Schnelle tty_driver_kref_put(driver);
20439603cb33SSven Schnelle tty3270_del_views();
20449603cb33SSven Schnelle }
20459603cb33SSven Schnelle
20469603cb33SSven Schnelle #if IS_ENABLED(CONFIG_TN3270_CONSOLE)
20479cab4f7dSHeiko Carstens
20489cab4f7dSHeiko Carstens static struct tty3270 *condev;
20499cab4f7dSHeiko Carstens
20509603cb33SSven Schnelle static void
con3270_write(struct console * co,const char * str,unsigned int count)20519603cb33SSven Schnelle con3270_write(struct console *co, const char *str, unsigned int count)
20529603cb33SSven Schnelle {
20539603cb33SSven Schnelle struct tty3270 *tp = co->data;
20549603cb33SSven Schnelle unsigned long flags;
20559603cb33SSven Schnelle char c;
20569603cb33SSven Schnelle
20579603cb33SSven Schnelle spin_lock_irqsave(&tp->view.lock, flags);
20589603cb33SSven Schnelle while (count--) {
20599603cb33SSven Schnelle c = *str++;
20609603cb33SSven Schnelle if (c == 0x0a) {
20619603cb33SSven Schnelle tty3270_cr(tp);
20629603cb33SSven Schnelle tty3270_lf(tp);
20639603cb33SSven Schnelle } else {
20649603cb33SSven Schnelle if (tp->cx >= tp->view.cols) {
20659603cb33SSven Schnelle tty3270_cr(tp);
20669603cb33SSven Schnelle tty3270_lf(tp);
20679603cb33SSven Schnelle }
20689603cb33SSven Schnelle tty3270_put_character(tp, c);
20699603cb33SSven Schnelle tp->cx++;
20709603cb33SSven Schnelle }
20719603cb33SSven Schnelle }
20729603cb33SSven Schnelle spin_unlock_irqrestore(&tp->view.lock, flags);
20739603cb33SSven Schnelle }
20749603cb33SSven Schnelle
20759603cb33SSven Schnelle static struct tty_driver *
con3270_device(struct console * c,int * index)20769603cb33SSven Schnelle con3270_device(struct console *c, int *index)
20779603cb33SSven Schnelle {
20789603cb33SSven Schnelle *index = c->index;
20799603cb33SSven Schnelle return tty3270_driver;
20809603cb33SSven Schnelle }
20819603cb33SSven Schnelle
20829603cb33SSven Schnelle static void
con3270_wait_write(struct tty3270 * tp)20839603cb33SSven Schnelle con3270_wait_write(struct tty3270 *tp)
20849603cb33SSven Schnelle {
20859603cb33SSven Schnelle while (!tp->write) {
20869603cb33SSven Schnelle raw3270_wait_cons_dev(tp->view.dev);
20879603cb33SSven Schnelle barrier();
20889603cb33SSven Schnelle }
20899603cb33SSven Schnelle }
20909603cb33SSven Schnelle
20919603cb33SSven Schnelle /*
20929603cb33SSven Schnelle * The below function is called as a panic/reboot notifier before the
20939603cb33SSven Schnelle * system enters a disabled, endless loop.
20949603cb33SSven Schnelle *
20959603cb33SSven Schnelle * Notice we must use the spin_trylock() alternative, to prevent lockups
20969603cb33SSven Schnelle * in atomic context (panic routine runs with secondary CPUs, local IRQs
20979603cb33SSven Schnelle * and preemption disabled).
20989603cb33SSven Schnelle */
con3270_notify(struct notifier_block * self,unsigned long event,void * data)20999603cb33SSven Schnelle static int con3270_notify(struct notifier_block *self,
21009603cb33SSven Schnelle unsigned long event, void *data)
21019603cb33SSven Schnelle {
21029603cb33SSven Schnelle struct tty3270 *tp;
21039603cb33SSven Schnelle unsigned long flags;
21049975fde0SSven Schnelle int rc;
21059603cb33SSven Schnelle
21069603cb33SSven Schnelle tp = condev;
21079603cb33SSven Schnelle if (!tp->view.dev)
21089603cb33SSven Schnelle return NOTIFY_DONE;
21099975fde0SSven Schnelle if (!raw3270_view_lock_unavailable(&tp->view)) {
21109975fde0SSven Schnelle rc = raw3270_activate_view(&tp->view);
21119975fde0SSven Schnelle if (rc)
21129975fde0SSven Schnelle return NOTIFY_DONE;
21139975fde0SSven Schnelle }
21149603cb33SSven Schnelle if (!spin_trylock_irqsave(&tp->view.lock, flags))
21159603cb33SSven Schnelle return NOTIFY_DONE;
21169603cb33SSven Schnelle con3270_wait_write(tp);
21179603cb33SSven Schnelle tp->nr_up = 0;
2118ba5c2e2aSSven Schnelle tp->update_flags = TTY_UPDATE_ALL;
21199603cb33SSven Schnelle while (tp->update_flags != 0) {
21209603cb33SSven Schnelle spin_unlock_irqrestore(&tp->view.lock, flags);
21219603cb33SSven Schnelle tty3270_update(&tp->timer);
21229603cb33SSven Schnelle spin_lock_irqsave(&tp->view.lock, flags);
21239603cb33SSven Schnelle con3270_wait_write(tp);
21249603cb33SSven Schnelle }
21259603cb33SSven Schnelle spin_unlock_irqrestore(&tp->view.lock, flags);
21269603cb33SSven Schnelle return NOTIFY_DONE;
21279603cb33SSven Schnelle }
21289603cb33SSven Schnelle
21299603cb33SSven Schnelle static struct notifier_block on_panic_nb = {
21309603cb33SSven Schnelle .notifier_call = con3270_notify,
21319603cb33SSven Schnelle .priority = INT_MIN + 1, /* run the callback late */
21329603cb33SSven Schnelle };
21339603cb33SSven Schnelle
21349603cb33SSven Schnelle static struct notifier_block on_reboot_nb = {
21359603cb33SSven Schnelle .notifier_call = con3270_notify,
21369603cb33SSven Schnelle .priority = INT_MIN + 1, /* run the callback late */
21379603cb33SSven Schnelle };
21389603cb33SSven Schnelle
21399603cb33SSven Schnelle static struct console con3270 = {
21409603cb33SSven Schnelle .name = "tty3270",
21419603cb33SSven Schnelle .write = con3270_write,
21429603cb33SSven Schnelle .device = con3270_device,
21439603cb33SSven Schnelle .flags = CON_PRINTBUFFER,
21449603cb33SSven Schnelle };
21459603cb33SSven Schnelle
21469603cb33SSven Schnelle static int __init
con3270_init(void)21479603cb33SSven Schnelle con3270_init(void)
21489603cb33SSven Schnelle {
21499603cb33SSven Schnelle struct raw3270_view *view;
21509603cb33SSven Schnelle struct raw3270 *rp;
21519603cb33SSven Schnelle struct tty3270 *tp;
21529603cb33SSven Schnelle int rc;
21539603cb33SSven Schnelle
21549603cb33SSven Schnelle /* Check if 3270 is to be the console */
21559603cb33SSven Schnelle if (!CONSOLE_IS_3270)
21569603cb33SSven Schnelle return -ENODEV;
21579603cb33SSven Schnelle
21589603cb33SSven Schnelle /* Set the console mode for VM */
21599603cb33SSven Schnelle if (MACHINE_IS_VM) {
21609603cb33SSven Schnelle cpcmd("TERM CONMODE 3270", NULL, 0, NULL);
21619603cb33SSven Schnelle cpcmd("TERM AUTOCR OFF", NULL, 0, NULL);
21629603cb33SSven Schnelle }
21639603cb33SSven Schnelle
21649603cb33SSven Schnelle rp = raw3270_setup_console();
21659603cb33SSven Schnelle if (IS_ERR(rp))
21669603cb33SSven Schnelle return PTR_ERR(rp);
21679603cb33SSven Schnelle
21689603cb33SSven Schnelle /* Check if the tty3270 is already there. */
21699603cb33SSven Schnelle view = raw3270_find_view(&tty3270_fn, RAW3270_FIRSTMINOR);
21709603cb33SSven Schnelle if (IS_ERR(view)) {
21719603cb33SSven Schnelle rc = tty3270_create_view(0, &tp);
21729603cb33SSven Schnelle if (rc)
21739603cb33SSven Schnelle return rc;
21749603cb33SSven Schnelle } else {
21759603cb33SSven Schnelle tp = container_of(view, struct tty3270, view);
21769603cb33SSven Schnelle tp->inattr = TF_INPUT;
21779603cb33SSven Schnelle }
21789603cb33SSven Schnelle con3270.data = tp;
21799603cb33SSven Schnelle condev = tp;
21809603cb33SSven Schnelle atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
21819603cb33SSven Schnelle register_reboot_notifier(&on_reboot_nb);
21829603cb33SSven Schnelle register_console(&con3270);
21839603cb33SSven Schnelle return 0;
21849603cb33SSven Schnelle }
21859603cb33SSven Schnelle console_initcall(con3270_init);
21869603cb33SSven Schnelle #endif
21879603cb33SSven Schnelle
21889603cb33SSven Schnelle MODULE_LICENSE("GPL");
21899603cb33SSven Schnelle MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR);
21909603cb33SSven Schnelle
21919603cb33SSven Schnelle module_init(tty3270_init);
21929603cb33SSven Schnelle module_exit(tty3270_exit);
2193