diff options
Diffstat (limited to 'drivers/s390/char')
-rw-r--r-- | drivers/s390/char/Kconfig | 11 | ||||
-rw-r--r-- | drivers/s390/char/Makefile | 4 | ||||
-rw-r--r-- | drivers/s390/char/con3215.c | 4 | ||||
-rw-r--r-- | drivers/s390/char/con3270.c | 2331 | ||||
-rw-r--r-- | drivers/s390/char/diag_ftp.c | 4 | ||||
-rw-r--r-- | drivers/s390/char/fs3270.c | 124 | ||||
-rw-r--r-- | drivers/s390/char/raw3270.c | 376 | ||||
-rw-r--r-- | drivers/s390/char/raw3270.h | 227 | ||||
-rw-r--r-- | drivers/s390/char/sclp_early.c | 2 | ||||
-rw-r--r-- | drivers/s390/char/sclp_ftp.c | 6 | ||||
-rw-r--r-- | drivers/s390/char/tty3270.c | 1963 | ||||
-rw-r--r-- | drivers/s390/char/tty3270.h | 15 |
12 files changed, 2245 insertions, 2822 deletions
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 7d1749b0d378..80c4e5101c97 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -5,17 +5,10 @@ comment "S/390 character device drivers" config TN3270 def_tristate y prompt "Support for locally attached 3270 terminals" - depends on CCW + depends on CCW && TTY help Include support for IBM 3270 terminals. -config TN3270_TTY - def_tristate y - prompt "Support for tty input/output on 3270 terminals" - depends on TN3270 && TTY - help - Include support for using an IBM 3270 terminal as a Linux tty. - config TN3270_FS def_tristate m prompt "Support for fullscreen applications on 3270 terminals" @@ -26,7 +19,7 @@ config TN3270_FS config TN3270_CONSOLE def_bool y prompt "Support for console on 3270 terminal" - depends on TN3270=y && TN3270_TTY=y + depends on TN3270=y help Include support for using an IBM 3270 terminal as a Linux system console. Available only if 3270 support is compiled in statically. diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index ce32270082f5..b0f6b3201636 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -21,9 +21,7 @@ obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \ sclp_early.o sclp_early_core.o sclp_sd.o -obj-$(CONFIG_TN3270) += raw3270.o -obj-$(CONFIG_TN3270_CONSOLE) += con3270.o -obj-$(CONFIG_TN3270_TTY) += tty3270.o +obj-$(CONFIG_TN3270) += raw3270.o con3270.o obj-$(CONFIG_TN3270_FS) += fs3270.o obj-$(CONFIG_TN3215) += con3215.o diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 72ba83c1bc79..0b05cd76b7d0 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -629,7 +629,7 @@ static int raw3215_startup(struct raw3215_info *raw) if (tty_port_initialized(&raw->port)) return 0; raw->line_pos = 0; - tty_port_set_initialized(&raw->port, 1); + tty_port_set_initialized(&raw->port, true); spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw3215_try_io(raw); spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); @@ -659,7 +659,7 @@ static void raw3215_shutdown(struct raw3215_info *raw) spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); remove_wait_queue(&raw->empty_wait, &wait); set_current_state(TASK_RUNNING); - tty_port_set_initialized(&raw->port, 1); + tty_port_set_initialized(&raw->port, true); } spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); } diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 10f6a37fb153..d9983550062d 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -1,518 +1,2075 @@ // SPDX-License-Identifier: GPL-2.0 /* - * IBM/3270 Driver - console view. + * IBM/3270 Driver - tty functions. * - * Author(s): - * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) - * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> - * Copyright IBM Corp. 2003, 2009 + * Author(s): + * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) + * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> + * -- Copyright IBM Corp. 2003 */ #include <linux/module.h> -#include <linux/console.h> +#include <linux/types.h> +#include <linux/kdev_t.h> +#include <linux/tty.h> +#include <linux/vt_kern.h> #include <linux/init.h> +#include <linux/console.h> #include <linux/interrupt.h> -#include <linux/list.h> +#include <linux/workqueue.h> #include <linux/panic_notifier.h> -#include <linux/types.h> -#include <linux/slab.h> -#include <linux/err.h> #include <linux/reboot.h> +#include <linux/slab.h> +#include <linux/memblock.h> +#include <linux/compat.h> #include <asm/ccwdev.h> #include <asm/cio.h> -#include <asm/cpcmd.h> #include <asm/ebcdic.h> +#include <asm/cpcmd.h> +#include <linux/uaccess.h> #include "raw3270.h" -#include "tty3270.h" -#include "ctrlchar.h" +#include "keyboard.h" + +#define TTY3270_CHAR_BUF_SIZE 256 +#define TTY3270_OUTPUT_BUFFER_SIZE 4096 +#define TTY3270_SCREEN_PAGES 8 /* has to be power-of-two */ +#define TTY3270_RECALL_SIZE 16 /* has to be power-of-two */ +#define TTY3270_STATUS_AREA_SIZE 40 + +static struct tty_driver *tty3270_driver; +static int tty3270_max_index; +static struct raw3270_fn tty3270_fn; + +#define TTY3270_HIGHLIGHT_BLINK 1 +#define TTY3270_HIGHLIGHT_REVERSE 2 +#define TTY3270_HIGHLIGHT_UNDERSCORE 4 + +struct tty3270_attribute { + unsigned char alternate_charset:1; /* Graphics charset */ + unsigned char highlight:3; /* Blink/reverse/underscore */ + unsigned char f_color:4; /* Foreground color */ + unsigned char b_color:4; /* Background color */ +}; -#define CON3270_OUTPUT_BUFFER_SIZE 1024 -#define CON3270_STRING_PAGES 4 +struct tty3270_cell { + unsigned char character; + struct tty3270_attribute attributes; +}; -static struct raw3270_fn con3270_fn; +struct tty3270_line { + struct tty3270_cell *cells; + int len; + int dirty; +}; -static bool auto_update = true; -module_param(auto_update, bool, 0); +static const unsigned char sfq_read_partition[] = { + 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 +}; + +#define ESCAPE_NPAR 8 /* - * Main 3270 console view data structure. + * The main tty view data structure. + * FIXME: + * 1) describe line orientation & lines list concept against screen + * 2) describe conversion of screen to lines + * 3) describe line format. */ -struct con3270 { +struct tty3270 { struct raw3270_view view; - struct list_head freemem; /* list of free memory for strings. */ + struct tty_port port; /* Output stuff. */ - struct list_head lines; /* list of lines. */ - struct list_head update; /* list of lines to update. */ - int line_nr; /* line number for next update. */ - int nr_lines; /* # lines in list. */ + unsigned char wcc; /* Write control character. */ int nr_up; /* # lines up in history. */ unsigned long update_flags; /* Update indication bits. */ - struct string *cline; /* current output line. */ - struct string *status; /* last line of display. */ - struct raw3270_request *write; /* single write request. */ - struct timer_list timer; + struct raw3270_request *write; /* Single write request. */ + struct timer_list timer; /* Output delay timer. */ + char *converted_line; /* RAW 3270 data stream */ + unsigned int line_view_start; /* Start of visible area */ + unsigned int line_write_start; /* current write position */ + unsigned int oops_line; /* line counter used when print oops */ + + /* Current tty screen. */ + unsigned int cx, cy; /* Current output position. */ + struct tty3270_attribute attributes; + struct tty3270_attribute saved_attributes; + int allocated_lines; + struct tty3270_line *screen; /* Input stuff. */ - struct string *input; /* input string for read request. */ - struct raw3270_request *read; /* single read request. */ - struct raw3270_request *kreset; /* single keyboard reset request. */ - struct tasklet_struct readlet; /* tasklet to issue read request. */ + char *prompt; /* Output string for input area. */ + char *input; /* Input string for read request. */ + struct raw3270_request *read; /* Single read request. */ + struct raw3270_request *kreset; /* Single keyboard reset request. */ + struct raw3270_request *readpartreq; + unsigned char inattr; /* Visible/invisible input. */ + int throttle, attn; /* tty throttle/unthrottle. */ + struct tasklet_struct readlet; /* Tasklet to issue read request. */ + struct tasklet_struct hanglet; /* Tasklet to hang up the tty. */ + struct kbd_data *kbd; /* key_maps stuff. */ + + /* Escape sequence parsing. */ + int esc_state, esc_ques, esc_npar; + int esc_par[ESCAPE_NPAR]; + unsigned int saved_cx, saved_cy; + + /* Command recalling. */ + char **rcl_lines; /* Array of recallable lines */ + int rcl_write_index; /* Write index of recallable items */ + int rcl_read_index; /* Read index of recallable items */ + + /* Character array for put_char/flush_chars. */ + unsigned int char_count; + char char_buf[TTY3270_CHAR_BUF_SIZE]; }; -static struct con3270 *condev; - -/* con3270->update_flags. See con3270_update for details. */ -#define CON_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */ -#define CON_UPDATE_LIST 2 /* Update lines in tty3270->update. */ -#define CON_UPDATE_STATUS 4 /* Update status line. */ -#define CON_UPDATE_ALL 8 /* Recreate screen. */ +/* tty3270->update_flags. See tty3270_update for details. */ +#define TTY_UPDATE_INPUT 0x1 /* Update input line. */ +#define TTY_UPDATE_STATUS 0x2 /* Update status line. */ +#define TTY_UPDATE_LINES 0x4 /* Update visible screen lines */ +#define TTY_UPDATE_ALL 0x7 /* Recreate screen. */ -static void con3270_update(struct timer_list *); +#define TTY3270_INPUT_AREA_ROWS 2 /* * Setup timeout for a device. On timeout trigger an update. */ -static void con3270_set_timer(struct con3270 *cp, int expires) +static void tty3270_set_timer(struct tty3270 *tp, int expires) { - if (expires == 0) - del_timer(&cp->timer); - else - mod_timer(&cp->timer, jiffies + expires); + mod_timer(&tp->timer, jiffies + expires); } -/* - * The status line is the last line of the screen. It shows the string - * "console view" in the lower left corner and "Running"/"More..."/"Holding" - * in the lower right corner of the screen. - */ -static void -con3270_update_status(struct con3270 *cp) +static int tty3270_tty_rows(struct tty3270 *tp) { - char *str; + return tp->view.rows - TTY3270_INPUT_AREA_ROWS; +} - str = (cp->nr_up != 0) ? "History" : "Running"; - memcpy(cp->status->string + 24, str, 7); - codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); - cp->update_flags |= CON_UPDATE_STATUS; +static char *tty3270_add_ba(struct tty3270 *tp, char *cp, char order, int x, int y) +{ + *cp++ = order; + raw3270_buffer_address(tp->view.dev, cp, x, y); + return cp + 2; } -static void -con3270_create_status(struct con3270 *cp) +static char *tty3270_add_ra(struct tty3270 *tp, char *cp, int x, int y, char c) +{ + cp = tty3270_add_ba(tp, cp, TO_RA, x, y); + *cp++ = c; + return cp; +} + +static char *tty3270_add_sa(struct tty3270 *tp, char *cp, char attr, char value) +{ + *cp++ = TO_SA; + *cp++ = attr; + *cp++ = value; + return cp; +} + +static char *tty3270_add_ge(struct tty3270 *tp, char *cp, char c) +{ + *cp++ = TO_GE; + *cp++ = c; + return cp; +} + +static char *tty3270_add_sf(struct tty3270 *tp, char *cp, char type) +{ + *cp++ = TO_SF; + *cp++ = type; + return cp; +} + +static int tty3270_line_increment(struct tty3270 *tp, unsigned int line, unsigned int incr) +{ + return (line + incr) & (tp->allocated_lines - 1); +} + +static struct tty3270_line *tty3270_get_write_line(struct tty3270 *tp, unsigned int num) { - static const unsigned char blueprint[] = - { TO_SBA, 0, 0, TO_SF,TF_LOG,TO_SA,TAT_COLOR, TAC_GREEN, - 'c','o','n','s','o','l','e',' ','v','i','e','w', - TO_RA,0,0,0,'R','u','n','n','i','n','g',TO_SF,TF_LOG }; + return tp->screen + tty3270_line_increment(tp, tp->line_write_start, num); +} + +static struct tty3270_line *tty3270_get_view_line(struct tty3270 *tp, unsigned int num) +{ + return tp->screen + tty3270_line_increment(tp, tp->line_view_start, num - tp->nr_up); +} - cp->status = alloc_string(&cp->freemem, sizeof(blueprint)); - /* Copy blueprint to status line */ - memcpy(cp->status->string, blueprint, sizeof(blueprint)); - /* Set TO_RA addresses. */ - raw3270_buffer_address(cp->view.dev, cp->status->string + 1, - cp->view.cols * (cp->view.rows - 1)); - raw3270_buffer_address(cp->view.dev, cp->status->string + 21, - cp->view.cols * cp->view.rows - 8); - /* Convert strings to ebcdic. */ - codepage_convert(cp->view.ascebc, cp->status->string + 8, 12); - codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); +static int tty3270_input_size(int cols) +{ + return cols * 2 - 11; +} + +static void tty3270_update_prompt(struct tty3270 *tp, char *input) +{ + strcpy(tp->prompt, input); + tp->update_flags |= TTY_UPDATE_INPUT; + tty3270_set_timer(tp, 1); } /* - * Set output offsets to 3270 datastream fragment of a console string. + * The input line are the two last lines of the screen. */ -static void -con3270_update_string(struct con3270 *cp, struct string *s, int nr) +static int tty3270_add_prompt(struct tty3270 *tp) { - if (s->len < 4) { - /* This indicates a bug, but printing a warning would - * cause a deadlock. */ - return; + int count = 0; + char *cp; + + cp = tp->converted_line; + cp = tty3270_add_ba(tp, cp, TO_SBA, 0, -2); + *cp++ = tp->view.ascebc['>']; + + if (*tp->prompt) { + cp = tty3270_add_sf(tp, cp, TF_INMDT); + count = min_t(int, strlen(tp->prompt), + tp->view.cols * 2 - TTY3270_STATUS_AREA_SIZE - 2); + memcpy(cp, tp->prompt, count); + cp += count; + } else { + cp = tty3270_add_sf(tp, cp, tp->inattr); } - if (s->string[s->len - 4] != TO_RA) - return; - raw3270_buffer_address(cp->view.dev, s->string + s->len - 3, - cp->view.cols * (nr + 1)); + *cp++ = TO_IC; + /* Clear to end of input line. */ + if (count < tp->view.cols * 2 - 11) + cp = tty3270_add_ra(tp, cp, -TTY3270_STATUS_AREA_SIZE, -1, 0); + return cp - tp->converted_line; +} + +static char *tty3270_ebcdic_convert(struct tty3270 *tp, char *d, char *s) +{ + while (*s) + *d++ = tp->view.ascebc[(int)*s++]; + return d; } /* - * Rebuild update list to print all lines. + * The status line is the last line of the screen. It shows the string + * "Running"/"History X" in the lower right corner of the screen. */ -static void -con3270_rebuild_update(struct con3270 *cp) +static int tty3270_add_status(struct tty3270 *tp) { - struct string *s, *n; - int nr; + char *cp = tp->converted_line; + int len; + + cp = tty3270_add_ba(tp, cp, TO_SBA, -TTY3270_STATUS_AREA_SIZE, -1); + cp = tty3270_add_sf(tp, cp, TF_LOG); + cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_GREEN); + cp = tty3270_ebcdic_convert(tp, cp, " 7"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER); + cp = tty3270_ebcdic_convert(tp, cp, "PrevPg"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); + cp = tty3270_ebcdic_convert(tp, cp, " 8"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER); + cp = tty3270_ebcdic_convert(tp, cp, "NextPg"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); + cp = tty3270_ebcdic_convert(tp, cp, " 12"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER); + cp = tty3270_ebcdic_convert(tp, cp, "Recall"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); + cp = tty3270_ebcdic_convert(tp, cp, " "); + if (tp->nr_up) { + len = sprintf(cp, "History %d", -tp->nr_up); + codepage_convert(tp->view.ascebc, cp, len); + cp += len; + } else { + cp = tty3270_ebcdic_convert(tp, cp, oops_in_progress ? "Crashed" : "Running"); + } + cp = tty3270_add_sf(tp, cp, TF_LOG); + cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_RESET); + return cp - (char *)tp->converted_line; +} - /* - * Throw away update list and create a new one, - * containing all lines that will fit on the screen. - */ - list_for_each_entry_safe(s, n, &cp->update, update) - list_del_init(&s->update); - nr = cp->view.rows - 2 + cp->nr_up; - list_for_each_entry_reverse(s, &cp->lines, list) { - if (nr < cp->view.rows - 1) - list_add(&s->update, &cp->update); - if (--nr < 0) - break; +static void tty3270_blank_screen(struct tty3270 *tp) +{ + struct tty3270_line *line; + int i; + + for (i = 0; i < tty3270_tty_rows(tp); i++) { + line = tty3270_get_write_line(tp, i); + line->len = 0; + line->dirty = 1; } - cp->line_nr = 0; - cp->update_flags |= CON_UPDATE_LIST; + tp->nr_up = 0; } /* - * Alloc string for size bytes. Free strings from history if necessary. + * Write request completion callback. */ -static struct string * -con3270_alloc_string(struct con3270 *cp, size_t size) +static void tty3270_write_callback(struct raw3270_request *rq, void *data) { - struct string *s, *n; + struct tty3270 *tp = container_of(rq->view, struct tty3270, view); - s = alloc_string(&cp->freemem, size); - if (s) - return s; - list_for_each_entry_safe(s, n, &cp->lines, list) { - list_del(&s->list); - if (!list_empty(&s->update)) - list_del(&s->update); - cp->nr_lines--; - if (free_string(&cp->freemem, s) >= size) - break; + if (rq->rc != 0) { + /* Write wasn't successful. Refresh all. */ + tp->update_flags = TTY_UPDATE_ALL; + tty3270_set_timer(tp, 1); } - s = alloc_string(&cp->freemem, size); - BUG_ON(!s); - if (cp->nr_up != 0 && cp->nr_up + cp->view.rows > cp->nr_lines) { - cp->nr_up = cp->nr_lines - cp->view.rows + 1; - con3270_rebuild_update(cp); - con3270_update_status(cp); + raw3270_request_reset(rq); + xchg(&tp->write, rq); +} + +static int tty3270_required_length(struct tty3270 *tp, struct tty3270_line *line) +{ + unsigned char f_color, b_color, highlight; + struct tty3270_cell *cell; + int i, flen = 3; /* Prefix (TO_SBA). */ + + flen += line->len; + highlight = 0; + f_color = TAC_RESET; + b_color = TAC_RESET; + + for (i = 0, cell = line->cells; i < line->len; i++, cell++) { + if (cell->attributes.highlight != highlight) { + flen += 3; /* TO_SA to switch highlight. */ + highlight = cell->attributes.highlight; + } + if (cell->attributes.f_color != f_color) { + flen += 3; /* TO_SA to switch color. */ + f_color = cell->attributes.f_color; + } + if (cell->attributes.b_color != b_color) { + flen += 3; /* TO_SA to switch color. */ + b_color = cell->attributes.b_color; + } + if (cell->attributes.alternate_charset) + flen += 1; /* TO_GE to switch to graphics extensions */ } - return s; + if (highlight) + flen += 3; /* TO_SA to reset hightlight. */ + if (f_color != TAC_RESET) + flen += 3; /* TO_SA to reset color. */ + if (b_color != TAC_RESET) + flen += 3; /* TO_SA to reset color. */ + if (line->len < tp->view.cols) + flen += 4; /* Postfix (TO_RA). */ + + return flen; +} + +static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_line *line, + char *cp, struct tty3270_attribute *attr, int lineno) +{ + if (attr->highlight) + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); + if (attr->f_color != TAC_RESET) + cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAX_RESET); + if (attr->b_color != TAC_RESET) + cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, TAX_RESET); + if (line->len < tp->view.cols) + cp = tty3270_add_ra(tp, cp, 0, lineno + 1, 0); + return cp; +} + +static char tty3270_graphics_translate(struct tty3270 *tp, char ch) +{ + switch (ch) { + case 'q': /* - */ + return 0xa2; + case 'x': /* '|' */ + return 0x85; + case 'l': /* |- */ + return 0xc5; + case 't': /* |_ */ + return 0xc6; + case 'u': /* _| */ + return 0xd6; + case 'k': /* -| */ + return 0xd5; + case 'j': + return 0xd4; + case 'm': + return 0xc4; + case 'n': /* + */ + return 0xd3; + case 'v': + return 0xc7; + case 'w': + return 0xd7; + default: + return ch; + } +} + +static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *line, + struct tty3270_attribute *attr, char *cp, int lineno) +{ + const unsigned char colors[16] = { + [0] = TAC_DEFAULT, + [1] = TAC_RED, + [2] = TAC_GREEN, + [3] = TAC_YELLOW, + [4] = TAC_BLUE, + [5] = TAC_PINK, + [6] = TAC_TURQ, + [7] = TAC_WHITE, + [9] = TAC_DEFAULT + }; + + const unsigned char highlights[8] = { + [TTY3270_HIGHLIGHT_BLINK] = TAX_BLINK, + [TTY3270_HIGHLIGHT_REVERSE] = TAX_REVER, + [TTY3270_HIGHLIGHT_UNDERSCORE] = TAX_UNDER, + }; + + struct tty3270_cell *cell; + int c, i; + + cp = tty3270_add_ba(tp, cp, TO_SBA, 0, lineno); + + for (i = 0, cell = line->cells; i < line->len; i++, cell++) { + if (cell->attributes.highlight != attr->highlight) { + attr->highlight = cell->attributes.highlight; + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, highlights[attr->highlight]); + } + if (cell->attributes.f_color != attr->f_color) { + attr->f_color = cell->attributes.f_color; + cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, colors[attr->f_color]); + } + if (cell->attributes.b_color != attr->b_color) { + attr->b_color = cell->attributes.b_color; + cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, colors[attr->b_color]); + } + c = cell->character; + if (cell->attributes.alternate_charset) + cp = tty3270_add_ge(tp, cp, tty3270_graphics_translate(tp, c)); + else + *cp++ = tp->view.ascebc[c]; + } + return cp; +} + +static void tty3270_reset_attributes(struct tty3270_attribute *attr) +{ + attr->highlight = TAX_RESET; + attr->f_color = TAC_RESET; + attr->b_color = TAC_RESET; } /* - * Write completion callback. + * Convert a tty3270_line to a 3270 data fragment usable for output. */ -static void -con3270_write_callback(struct raw3270_request *rq, void *data) +static unsigned int tty3270_convert_line(struct tty3270 *tp, struct tty3270_line *line, int lineno) { - raw3270_request_reset(rq); - xchg(&((struct con3270 *) rq->view)->write, rq); + struct tty3270_attribute attr; + int flen; + char *cp; + + /* Determine how long the fragment will be. */ + flen = tty3270_required_length(tp, line); + if (flen > PAGE_SIZE) + return 0; + /* Write 3270 data fragment. */ + tty3270_reset_attributes(&attr); + cp = tty3270_add_attributes(tp, line, &attr, tp->converted_line, lineno); + cp = tty3270_add_reset_attributes(tp, line, cp, &attr, lineno); + return cp - (char *)tp->converted_line; +} + +static void tty3270_update_lines_visible(struct tty3270 *tp, struct raw3270_request *rq) +{ + struct tty3270_line *line; + int len, i; + + for (i = 0; i < tty3270_tty_rows(tp); i++) { + line = tty3270_get_view_line(tp, i); + if (!line->dirty) + continue; + len = tty3270_convert_line(tp, line, i); + if (raw3270_request_add_data(rq, tp->converted_line, len)) + break; + line->dirty = 0; + } + if (i == tty3270_tty_rows(tp)) { + for (i = 0; i < tp->allocated_lines; i++) + tp->screen[i].dirty = 0; + tp->update_flags &= ~TTY_UPDATE_LINES; + } +} + +static void tty3270_update_lines_all(struct tty3270 *tp, struct raw3270_request *rq) +{ + struct tty3270_line *line; + char buf[4]; + int len, i; + + for (i = 0; i < tp->allocated_lines; i++) { + line = tty3270_get_write_line(tp, i + tp->cy + 1); + if (!line->dirty) + continue; + len = tty3270_convert_line(tp, line, tp->oops_line); + if (raw3270_request_add_data(rq, tp->converted_line, len)) + break; + line->dirty = 0; + if (++tp->oops_line >= tty3270_tty_rows(tp)) + tp->oops_line = 0; + } + + if (i == tp->allocated_lines) { + if (tp->oops_line < tty3270_tty_rows(tp)) { + tty3270_add_ra(tp, buf, 0, tty3270_tty_rows(tp), 0); + if (raw3270_request_add_data(rq, buf, sizeof(buf))) + return; + } + tp->update_flags &= ~TTY_UPDATE_LINES; + } } /* - * Update console display. + * Update 3270 display. */ -static void -con3270_update(struct timer_list *t) +static void tty3270_update(struct timer_list *t) { - struct con3270 *cp = from_timer(cp, t, timer); + struct tty3270 *tp = from_timer(tp, t, timer); struct raw3270_request *wrq; - char wcc, prolog[6]; - unsigned long flags; - unsigned long updated; - struct string *s, *n; - int rc; + u8 cmd = TC_WRITE; + int rc, len; - if (!auto_update && !raw3270_view_active(&cp->view)) - return; - if (cp->view.dev) - raw3270_activate_view(&cp->view); - - wrq = xchg(&cp->write, 0); + wrq = xchg(&tp->write, 0); if (!wrq) { - con3270_set_timer(cp, 1); + tty3270_set_timer(tp, 1); return; } - spin_lock_irqsave(&cp->view.lock, flags); - updated = 0; - if (cp->update_flags & CON_UPDATE_ALL) { - con3270_rebuild_update(cp); - con3270_update_status(cp); - cp->update_flags = CON_UPDATE_ERASE | CON_UPDATE_LIST | - CON_UPDATE_STATUS; - } - if (cp->update_flags & CON_UPDATE_ERASE) { - /* Use erase write alternate to initialize display. */ - raw3270_request_set_cmd(wrq, TC_EWRITEA); - updated |= CON_UPDATE_ERASE; - } else - raw3270_request_set_cmd(wrq, TC_WRITE); + spin_lock_irq(&tp->view.lock); + if (tp->update_flags == TTY_UPDATE_ALL) + cmd = TC_EWRITEA; - wcc = TW_NONE; - raw3270_request_add_data(wrq, &wcc, 1); + raw3270_request_set_cmd(wrq, cmd); + raw3270_request_add_data(wrq, &tp->wcc, 1); + tp->wcc = TW_NONE; /* * Update status line. */ - if (cp->update_flags & CON_UPDATE_STATUS) - if (raw3270_request_add_data(wrq, cp->status->string, - cp->status->len) == 0) - updated |= CON_UPDATE_STATUS; - - if (cp->update_flags & CON_UPDATE_LIST) { - prolog[0] = TO_SBA; - prolog[3] = TO_SA; - prolog[4] = TAT_COLOR; - prolog[5] = TAC_TURQ; - raw3270_buffer_address(cp->view.dev, prolog + 1, - cp->view.cols * cp->line_nr); - raw3270_request_add_data(wrq, prolog, 6); - /* Write strings in the update list to the screen. */ - list_for_each_entry_safe(s, n, &cp->update, update) { - if (s != cp->cline) - con3270_update_string(cp, s, cp->line_nr); - if (raw3270_request_add_data(wrq, s->string, - s->len) != 0) - break; - list_del_init(&s->update); - if (s != cp->cline) - cp->line_nr++; - } - if (list_empty(&cp->update)) - updated |= CON_UPDATE_LIST; + if (tp->update_flags & TTY_UPDATE_STATUS) { + len = tty3270_add_status(tp); + if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0) + tp->update_flags &= ~TTY_UPDATE_STATUS; + } + + /* + * Write input line. + */ + if (tp->update_flags & TTY_UPDATE_INPUT) { + len = tty3270_add_prompt(tp); + if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0) + tp->update_flags &= ~TTY_UPDATE_INPUT; + } + + if (tp->update_flags & TTY_UPDATE_LINES) { + if (oops_in_progress) + tty3270_update_lines_all(tp, wrq); + else + tty3270_update_lines_visible(tp, wrq); } - wrq->callback = con3270_write_callback; - rc = raw3270_start(&cp->view, wrq); + + wrq->callback = tty3270_write_callback; + rc = raw3270_start(&tp->view, wrq); if (rc == 0) { - cp->update_flags &= ~updated; - if (cp->update_flags) - con3270_set_timer(cp, 1); + if (tp->update_flags) + tty3270_set_timer(tp, 1); } else { raw3270_request_reset(wrq); - xchg(&cp->write, wrq); + xchg(&tp->write, wrq); } - spin_unlock_irqrestore(&cp->view.lock, flags); + spin_unlock_irq(&tp->view.lock); } /* - * Read tasklet. + * Command recalling. */ -static void -con3270_read_tasklet(unsigned long data) +static void tty3270_rcl_add(struct tty3270 *tp, char *input, int len) +{ + char *p; + + if (len <= 0) + return; + p = tp->rcl_lines[tp->rcl_write_index++]; + tp->rcl_write_index &= TTY3270_RECALL_SIZE - 1; + memcpy(p, input, len); + p[len] = '\0'; + tp->rcl_read_index = tp->rcl_write_index; +} + +static void tty3270_rcl_backward(struct kbd_data *kbd) +{ + struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); + int i = 0; + + spin_lock_irq(&tp->view.lock); + if (tp->inattr == TF_INPUT) { + do { + tp->rcl_read_index--; + tp->rcl_read_index &= TTY3270_RECALL_SIZE - 1; + } while (!*tp->rcl_lines[tp->rcl_read_index] && + i++ < TTY3270_RECALL_SIZE - 1); + tty3270_update_prompt(tp, tp->rcl_lines[tp->rcl_read_index]); + } + spin_unlock_irq(&tp->view.lock); +} + +/* + * Deactivate tty view. + */ +static void tty3270_exit_tty(struct kbd_data *kbd) +{ + struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); + + raw3270_deactivate_view(&tp->view); +} + +static void tty3270_redraw(struct tty3270 *tp) +{ + int i; + + for (i = 0; i < tty3270_tty_rows(tp); i++) + tty3270_get_view_line(tp, i)->dirty = 1; + tp->update_flags = TTY_UPDATE_ALL; + tty3270_set_timer(tp, 1); +} + +/* + * Scroll forward in history. + */ +static void tty3270_scroll_forward(struct kbd_data *kbd) +{ + struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); + + spin_lock_irq(&tp->view.lock); + + if (tp->nr_up >= tty3270_tty_rows(tp)) + tp->nr_up -= tty3270_tty_rows(tp) / 2; + else + tp->nr_up = 0; + tty3270_redraw(tp); + spin_unlock_irq(&tp->view.lock); +} + +/* + * Scroll backward in history. + */ +static void tty3270_scroll_backward(struct kbd_data *kbd) +{ + struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); + + spin_lock_irq(&tp->view.lock); + tp->nr_up += tty3270_tty_rows(tp) / 2; + if (tp->nr_up > tp->allocated_lines - tty3270_tty_rows(tp)) + tp->nr_up = tp->allocated_lines - tty3270_tty_rows(tp); + tty3270_redraw(tp); + spin_unlock_irq(&tp->view.lock); +} + +/* + * Pass input line to tty. + */ +static void tty3270_read_tasklet(unsigned long data) { + struct raw3270_request *rrq = (struct raw3270_request *)data; static char kreset_data = TW_KR; - struct raw3270_request *rrq; - struct con3270 *cp; - unsigned long flags; - int nr_up, deactivate; - - rrq = (struct raw3270_request *)data; - cp = (struct con3270 *) rrq->view; - spin_lock_irqsave(&cp->view.lock, flags); - nr_up = cp->nr_up; - deactivate = 0; - /* Check aid byte. */ - switch (cp->input->string[0]) { - case 0x7d: /* enter: jump to bottom. */ - nr_up = 0; - break; - case 0xf3: /* PF3: deactivate the console view. */ - deactivate = 1; + struct tty3270 *tp = container_of(rrq->view, struct tty3270, view); + char *input; + int len; + + spin_lock_irq(&tp->view.lock); + /* + * Two AID keys are special: For 0x7d (enter) the input line + * has to be emitted to the tty and for 0x6d the screen + * needs to be redrawn. + */ + input = NULL; + len = 0; + switch (tp->input[0]) { + case AID_ENTER: + /* Enter: write input to tty. */ + input = tp->input + 6; + len = tty3270_input_size(tp->view.cols) - 6 - rrq->rescnt; + if (tp->inattr != TF_INPUTN) + tty3270_rcl_add(tp, input, len); + if (tp->nr_up > 0) + tp->nr_up = 0; + /* Clear input area. */ + tty3270_update_prompt(tp, ""); + tty3270_set_timer(tp, 1); break; - case 0x6d: /* clear: start from scratch. */ - cp->update_flags = CON_UPDATE_ALL; - con3270_set_timer(cp, 1); + case AID_CLEAR: + /* Display has been cleared. Redraw. */ + tp->update_flags = TTY_UPDATE_ALL; + tty3270_set_timer(tp, 1); + if (!list_empty(&tp->readpartreq->list)) + break; + raw3270_start_request(&tp->view, tp->readpartreq, TC_WRITESF, + (char *)sfq_read_partition, sizeof(sfq_read_partition)); break; - case 0xf7: /* PF7: do a page up in the console log. */ - nr_up += cp->view.rows - 2; - if (nr_up + cp->view.rows - 1 > cp->nr_lines) { - nr_up = cp->nr_lines - cp->view.rows + 1; - if (nr_up < 0) - nr_up = 0; - } + case AID_READ_PARTITION: + raw3270_read_modified_cb(tp->readpartreq, tp->input); break; - case 0xf8: /* PF8: do a page down in the console log. */ - nr_up -= cp->view.rows - 2; - if (nr_up < 0) - nr_up = 0; + default: break; } - if (nr_up != cp->nr_up) { - cp->nr_up = nr_up; - con3270_rebuild_update(cp); - con3270_update_status(cp); - con3270_set_timer(cp, 1); - } - spin_unlock_irqrestore(&cp->view.lock, flags); + spin_unlock_irq(&tp->view.lock); /* Start keyboard reset command. */ - raw3270_request_reset(cp->kreset); - raw3270_request_set_cmd(cp->kreset, TC_WRITE); - raw3270_request_add_data(cp->kreset, &kreset_data, 1); - raw3270_start(&cp->view, cp->kreset); + raw3270_start_request(&tp->view, tp->kreset, TC_WRITE, &kreset_data, 1); - if (deactivate) - raw3270_deactivate_view(&cp->view); + while (len-- > 0) + kbd_keycode(tp->kbd, *input++); + /* Emit keycode for AID byte. */ + kbd_keycode(tp->kbd, 256 + tp->input[0]); raw3270_request_reset(rrq); - xchg(&cp->read, rrq); - raw3270_put_view(&cp->view); + xchg(&tp->read, rrq); + raw3270_put_view(&tp->view); } /* * Read request completion callback. */ -static void -con3270_read_callback(struct raw3270_request *rq, void *data) +static void tty3270_read_callback(struct raw3270_request *rq, void *data) { + struct tty3270 *tp = container_of(rq->view, struct tty3270, view); + raw3270_get_view(rq->view); /* Schedule tasklet to pass input to tty. */ - tasklet_schedule(&((struct con3270 *) rq->view)->readlet); + tasklet_schedule(&tp->readlet); } /* - * Issue a read request. Called only from interrupt function. + * Issue a read request. Call with device lock. */ -static void -con3270_issue_read(struct con3270 *cp) +static void tty3270_issue_read(struct tty3270 *tp, int lock) { struct raw3270_request *rrq; int rc; - rrq = xchg(&cp->read, 0); + rrq = xchg(&tp->read, 0); if (!rrq) /* Read already scheduled. */ return; - rrq->callback = con3270_read_callback; - rrq->callback_data = cp; + rrq->callback = tty3270_read_callback; + rrq->callback_data = tp; raw3270_request_set_cmd(rrq, TC_READMOD); - raw3270_request_set_data(rrq, cp->input->string, cp->input->len); + raw3270_request_set_data(rrq, tp->input, tty3270_input_size(tp->view.cols)); /* Issue the read modified request. */ - rc = raw3270_start_irq(&cp->view, rrq); - if (rc) + if (lock) + rc = raw3270_start(&tp->view, rrq); + else + rc = raw3270_start_irq(&tp->view, rrq); + if (rc) { raw3270_request_reset(rrq); + xchg(&tp->read, rrq); + } } /* - * Switch to the console view. + * Hang up the tty */ -static int -con3270_activate(struct raw3270_view *view) +static void tty3270_hangup_tasklet(unsigned long data) { - struct con3270 *cp; + struct tty3270 *tp = (struct tty3270 *)data; + + tty_port_tty_hangup(&tp->port, true); + raw3270_put_view(&tp->view); +} - cp = (struct con3270 *) view; - cp->update_flags = CON_UPDATE_ALL; - con3270_set_timer(cp, 1); +/* + * Switch to the tty view. + */ +static int tty3270_activate(struct raw3270_view *view) +{ + struct tty3270 *tp = container_of(view, struct tty3270, view); + + tp->update_flags = TTY_UPDATE_ALL; + tty3270_set_timer(tp, 1); return 0; } -static void -con3270_deactivate(struct raw3270_view *view) +static void tty3270_deactivate(struct raw3270_view *view) { - struct con3270 *cp; + struct tty3270 *tp = container_of(view, struct tty3270, view); - cp = (struct con3270 *) view; - del_timer(&cp->timer); + del_timer(&tp->timer); } -static void -con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) +static void tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) { /* Handle ATTN. Schedule tasklet to read aid. */ - if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) - con3270_issue_read(cp); + if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { + if (!tp->throttle) + tty3270_issue_read(tp, 0); + else + tp->attn = 1; + } if (rq) { - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { rq->rc = -EIO; - else + raw3270_get_view(&tp->view); + tasklet_schedule(&tp->hanglet); + } else { /* Normal end. Copy residual count. */ rq->rescnt = irb->scsw.cmd.count; + } } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { /* Interrupt without an outstanding request -> update all */ - cp->update_flags = CON_UPDATE_ALL; - con3270_set_timer(cp, 1); + tp->update_flags = TTY_UPDATE_ALL; + tty3270_set_timer(tp, 1); + } +} + +/* + * Allocate tty3270 structure. + */ +static struct tty3270 *tty3270_alloc_view(void) +{ + struct tty3270 *tp; + + tp = kzalloc(sizeof(*tp), GFP_KERNEL); + if (!tp) + goto out_err; + + tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE); + if (IS_ERR(tp->write)) + goto out_tp; + tp->read = raw3270_request_alloc(0); + if (IS_ERR(tp->read)) + goto out_write; + tp->kreset = raw3270_request_alloc(1); + if (IS_ERR(tp->kreset)) + goto out_read; + tp->readpartreq = raw3270_request_alloc(sizeof(sfq_read_partition)); + if (IS_ERR(tp->readpartreq)) + goto out_reset; + tp->kbd = kbd_alloc(); + if (!tp->kbd) + goto out_readpartreq; + + tty_port_init(&tp->port); + timer_setup(&tp->timer, tty3270_update, 0); + tasklet_init(&tp->readlet, tty3270_read_tasklet, + (unsigned long)tp->read); + tasklet_init(&tp->hanglet, tty3270_hangup_tasklet, + (unsigned long)tp); + return tp; + +out_readpartreq: + raw3270_request_free(tp->readpartreq); +out_reset: + raw3270_request_free(tp->kreset); +out_read: + raw3270_request_free(tp->read); +out_write: + raw3270_request_free(tp->write); +out_tp: + kfree(tp); +out_err: + return ERR_PTR(-ENOMEM); +} + +/* + * Free tty3270 structure. + */ +static void tty3270_free_view(struct tty3270 *tp) +{ + kbd_free(tp->kbd); + raw3270_request_free(tp->kreset); + raw3270_request_free(tp->read); + raw3270_request_free(tp->write); + free_page((unsigned long)tp->converted_line); + tty_port_destroy(&tp->port); + kfree(tp); +} + +/* + * Allocate tty3270 screen. + */ +static struct tty3270_line *tty3270_alloc_screen(struct tty3270 *tp, unsigned int rows, + unsigned int cols, int *allocated_out) +{ + struct tty3270_line *screen; + int allocated, lines; + + allocated = __roundup_pow_of_two(rows) * TTY3270_SCREEN_PAGES; + screen = kcalloc(allocated, sizeof(struct tty3270_line), GFP_KERNEL); + if (!screen) + goto out_err; + for (lines = 0; lines < allocated; lines++) { + screen[lines].cells = kcalloc(cols, sizeof(struct tty3270_cell), GFP_KERNEL); + if (!screen[lines].cells) + goto out_screen; + } + *allocated_out = allocated; + return screen; +out_screen: + while (lines--) + kfree(screen[lines].cells); + kfree(screen); +out_err: + return ERR_PTR(-ENOMEM); +} + +static char **tty3270_alloc_recall(int cols) +{ + char **lines; + int i; + + lines = kmalloc_array(TTY3270_RECALL_SIZE, sizeof(char *), GFP_KERNEL); + if (!lines) + return NULL; + for (i = 0; i < TTY3270_RECALL_SIZE; i++) { + lines[i] = kcalloc(1, tty3270_input_size(cols) + 1, GFP_KERNEL); + if (!lines[i]) + break; } + + if (i == TTY3270_RECALL_SIZE) + return lines; + + while (i--) + kfree(lines[i]); + kfree(lines); + return NULL; } -/* Console view to a 3270 device. */ -static struct raw3270_fn con3270_fn = { - .activate = con3270_activate, - .deactivate = con3270_deactivate, - .intv = (void *) con3270_irq +static void tty3270_free_recall(char **lines) +{ + int i; + + for (i = 0; i < TTY3270_RECALL_SIZE; i++) + kfree(lines[i]); + kfree(lines); +} + +/* + * Free tty3270 screen. + */ +static void tty3270_free_screen(struct tty3270_line *screen, int old_lines) +{ + int lines; + + for (lines = 0; lines < old_lines; lines++) + kfree(screen[lines].cells); + kfree(screen); +} + +/* + * Resize tty3270 screen + */ +static void tty3270_resize(struct raw3270_view *view, + int new_model, int new_rows, int new_cols, + int old_model, int old_rows, int old_cols) +{ + struct tty3270 *tp = container_of(view, struct tty3270, view); + struct tty3270_line *screen, *oscreen; + char **old_rcl_lines, **new_rcl_lines; + char *old_prompt, *new_prompt; + char *old_input, *new_input; + struct tty_struct *tty; + struct winsize ws; + int new_allocated, old_allocated = tp->allocated_lines; + + if (old_model == new_model && + old_cols == new_cols && + old_rows == new_rows) { + spin_lock_irq(&tp->view.lock); + tty3270_redraw(tp); + spin_unlock_irq(&tp->view.lock); + return; + } + + new_input = kzalloc(tty3270_input_size(new_cols), GFP_KERNEL | GFP_DMA); + if (!new_input) + return; + new_prompt = kzalloc(tty3270_input_size(new_cols), GFP_KERNEL); + if (!new_prompt) + goto out_input; + screen = tty3270_alloc_screen(tp, new_rows, new_cols, &new_allocated); + if (IS_ERR(screen)) + goto out_prompt; + new_rcl_lines = tty3270_alloc_recall(new_cols); + if (!new_rcl_lines) + goto out_screen; + + /* Switch to new output size */ + spin_lock_irq(&tp->view.lock); + tty3270_blank_screen(tp); + oscreen = tp->screen; + tp->screen = screen; + tp->allocated_lines = new_allocated; + tp->view.rows = new_rows; + tp->view.cols = new_cols; + tp->view.model = new_model; + tp->update_flags = TTY_UPDATE_ALL; + old_input = tp->input; + old_prompt = tp->prompt; + old_rcl_lines = tp->rcl_lines; + tp->input = new_input; + tp->prompt = new_prompt; + tp->rcl_lines = new_rcl_lines; + tp->rcl_read_index = 0; + tp->rcl_write_index = 0; + spin_unlock_irq(&tp->view.lock); + tty3270_free_screen(oscreen, old_allocated); + kfree(old_input); + kfree(old_prompt); + tty3270_free_recall(old_rcl_lines); + tty3270_set_timer(tp, 1); + /* Informat tty layer about new size */ + tty = tty_port_tty_get(&tp->port); + if (!tty) + return; + ws.ws_row = tty3270_tty_rows(tp); + ws.ws_col = tp->view.cols; + tty_do_resize(tty, &ws); + tty_kref_put(tty); + return; +out_screen: + tty3270_free_screen(screen, new_rows); +out_prompt: + kfree(new_prompt); +out_input: + kfree(new_input); +} + +/* + * Unlink tty3270 data structure from tty. + */ +static void tty3270_release(struct raw3270_view *view) +{ + struct tty3270 *tp = container_of(view, struct tty3270, view); + struct tty_struct *tty = tty_port_tty_get(&tp->port); + + if (tty) { + tty->driver_data = NULL; + tty_port_tty_set(&tp->port, NULL); + tty_hangup(tty); + raw3270_put_view(&tp->view); + tty_kref_put(tty); + } +} + +/* + * Free tty3270 data structure + */ +static void tty3270_free(struct raw3270_view *view) +{ + struct tty3270 *tp = container_of(view, struct tty3270, view); + + del_timer_sync(&tp->timer); + tty3270_free_screen(tp->screen, tp->allocated_lines); + free_page((unsigned long)tp->converted_line); + kfree(tp->input); + kfree(tp->prompt); + tty3270_free_view(tp); +} + +/* + * Delayed freeing of tty3270 views. + */ +static void tty3270_del_views(void) +{ + int i; + + for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) { + struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i); + + if (!IS_ERR(view)) + raw3270_del_view(view); + } +} + +static struct raw3270_fn tty3270_fn = { + .activate = tty3270_activate, + .deactivate = tty3270_deactivate, + .intv = (void *)tty3270_irq, + .release = tty3270_release, + .free = tty3270_free, + .resize = tty3270_resize }; -static inline void -con3270_cline_add(struct con3270 *cp) +static int +tty3270_create_view(int index, struct tty3270 **newtp) +{ + struct tty3270 *tp; + int rc; + + if (tty3270_max_index < index + 1) + tty3270_max_index = index + 1; + + /* Allocate tty3270 structure on first open. */ + tp = tty3270_alloc_view(); + if (IS_ERR(tp)) + return PTR_ERR(tp); + + rc = raw3270_add_view(&tp->view, &tty3270_fn, + index + RAW3270_FIRSTMINOR, + RAW3270_VIEW_LOCK_IRQ); + if (rc) + goto out_free_view; + + tp->screen = tty3270_alloc_screen(tp, tp->view.rows, tp->view.cols, + &tp->allocated_lines); + if (IS_ERR(tp->screen)) { + rc = PTR_ERR(tp->screen); + goto out_put_view; + } + + tp->converted_line = (void *)__get_free_page(GFP_KERNEL); + if (!tp->converted_line) { + rc = -ENOMEM; + goto out_free_screen; + } + + tp->input = kzalloc(tty3270_input_size(tp->view.cols), GFP_KERNEL | GFP_DMA); + if (!tp->input) { + rc = -ENOMEM; + goto out_free_converted_line; + } + + tp->prompt = kzalloc(tty3270_input_size(tp->view.cols), GFP_KERNEL); + if (!tp->prompt) { + rc = -ENOMEM; + goto out_free_input; + } + + tp->rcl_lines = tty3270_alloc_recall(tp->view.cols); + if (!tp->rcl_lines) { + rc = -ENOMEM; + goto out_free_prompt; + } + + /* Create blank line for every line in the tty output area. */ + tty3270_blank_screen(tp); + + tp->kbd->port = &tp->port; + tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty; + tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward; + tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward; + tp->kbd->fn_handler[KVAL(K_CONS)] = tty3270_rcl_backward; + kbd_ascebc(tp->kbd, tp->view.ascebc); + + raw3270_activate_view(&tp->view); + raw3270_put_view(&tp->view); + *newtp = tp; + return 0; + +out_free_prompt: + kfree(tp->prompt); +out_free_input: + kfree(tp->input); +out_free_converted_line: + free_page((unsigned long)tp->converted_line); +out_free_screen: + tty3270_free_screen(tp->screen, tp->view.rows); +out_put_view: + raw3270_put_view(&tp->view); + raw3270_del_view(&tp->view); +out_free_view: + tty3270_free_view(tp); + return rc; +} + +/* + * This routine is called whenever a 3270 tty is opened first time. + */ +static int +tty3270_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct raw3270_view *view; + struct tty3270 *tp; + int rc; + + /* Check if the tty3270 is already there. */ + view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR); + if (IS_ERR(view)) { + rc = tty3270_create_view(tty->index, &tp); + if (rc) + return rc; + } else { + tp = container_of(view, struct tty3270, view); + tty->driver_data = tp; + tp->inattr = TF_INPUT; + } + + tty->winsize.ws_row = tty3270_tty_rows(tp); + tty->winsize.ws_col = tp->view.cols; + rc = tty_port_install(&tp->port, driver, tty); + if (rc) { + raw3270_put_view(&tp->view); + return rc; + } + tty->driver_data = tp; + return 0; +} + +/* + * This routine is called whenever a 3270 tty is opened. + */ +static int tty3270_open(struct tty_struct *tty, struct file *filp) +{ + struct tty3270 *tp = tty->driver_data; + struct tty_port *port = &tp->port; + + port->count++; + tty_port_tty_set(port, tty); + return 0; +} + +/* + * This routine is called when the 3270 tty is closed. We wait + * for the remaining request to be completed. Then we clean up. + */ +static void tty3270_close(struct tty_struct *tty, struct file *filp) { - if (!list_empty(&cp->cline->list)) - /* Already added. */ + struct tty3270 *tp = tty->driver_data; + + if (tty->count > 1) return; - list_add_tail(&cp->cline->list, &cp->lines); - cp->nr_lines++; - con3270_rebuild_update(cp); + if (tp) + tty_port_tty_set(&tp->port, NULL); +} + +static void tty3270_cleanup(struct tty_struct *tty) +{ + struct tty3270 *tp = tty->driver_data; + + if (tp) { + tty->driver_data = NULL; + raw3270_put_view(&tp->view); + } +} + +/* + * We always have room. + */ +static unsigned int tty3270_write_room(struct tty_struct *tty) +{ + return INT_MAX; } -static inline void -con3270_cline_insert(struct con3270 *cp, unsigned char c) +/* + * Insert character into the screen at the current position with the + * current color and highlight. This function does NOT do cursor movement. + */ +static void tty3270_put_character(struct tty3270 *tp, char ch) { - cp->cline->string[cp->cline->len++] = - cp->view.ascebc[(c < ' ') ? ' ' : c]; - if (list_empty(&cp->cline->update)) { - list_add_tail(&cp->cline->update, &cp->update); - cp->update_flags |= CON_UPDATE_LIST; + struct tty3270_line *line; + struct tty3270_cell *cell; + + line = tty3270_get_write_line(tp, tp->cy); + if (line->len <= tp->cx) { + while (line->len < tp->cx) { + cell = line->cells + line->len; + cell->character = ' '; + cell->attributes = tp->attributes; + line->len++; + } + line->len++; } + cell = line->cells + tp->cx; + cell->character = ch; + cell->attributes = tp->attributes; + line->dirty = 1; +} + +/* + * Do carriage return. + */ +static void tty3270_cr(struct tty3270 *tp) +{ + tp->cx = 0; } -static inline void -con3270_cline_end(struct con3270 *cp) +/* + * Do line feed. + */ +static void tty3270_lf(struct tty3270 *tp) { - struct string *s; - unsigned int size; + struct tty3270_line *line; + int i; - /* Copy cline. */ - size = (cp->cline->len < cp->view.cols - 5) ? - cp->cline->len + 4 : cp->view.cols; - s = con3270_alloc_string(cp, size); - memcpy(s->string, cp->cline->string, cp->cline->len); - if (cp->cline->len < cp->view.cols - 5) { - s->string[s->len - 4] = TO_RA; - s->string[s->len - 1] = 0; + if (tp->cy < tty3270_tty_rows(tp) - 1) { + tp->cy++; } else { - while (--size >= cp->cline->len) - s->string[size] = cp->view.ascebc[' ']; + tp->line_view_start = tty3270_line_increment(tp, tp->line_view_start, 1); + tp->line_write_start = tty3270_line_increment(tp, tp->line_write_start, 1); + for (i = 0; i < tty3270_tty_rows(tp); i++) + tty3270_get_view_line(tp, i)->dirty = 1; + } + + line = tty3270_get_write_line(tp, tp->cy); + line->len = 0; + line->dirty = 1; +} + +static void tty3270_ri(struct tty3270 *tp) +{ + if (tp->cy > 0) + tp->cy--; +} + +static void tty3270_reset_cell(struct tty3270 *tp, struct tty3270_cell *cell) +{ + cell->character = ' '; + tty3270_reset_attributes(&cell->attributes); +} + +/* + * Insert characters at current position. + */ +static void tty3270_insert_characters(struct tty3270 *tp, int n) +{ + struct tty3270_line *line; + int k; + + line = tty3270_get_write_line(tp, tp->cy); + while (line->len < tp->cx) + tty3270_reset_cell(tp, &line->cells[line->len++]); + if (n > tp->view.cols - tp->cx) + n = tp->view.cols - tp->cx; + k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n); + while (k--) + line->cells[tp->cx + n + k] = line->cells[tp->cx + k]; + line->len += n; + if (line->len > tp->view.cols) + line->len = tp->view.cols; + while (n-- > 0) { + line->cells[tp->cx + n].character = ' '; + line->cells[tp->cx + n].attributes = tp->attributes; + } +} + +/* + * Delete characters at current position. + */ +static void tty3270_delete_characters(struct tty3270 *tp, int n) +{ + struct tty3270_line *line; + int i; + + line = tty3270_get_write_line(tp, tp->cy); + if (line->len <= tp->cx) + return; + if (line->len - tp->cx <= n) { + line->len = tp->cx; + return; + } + for (i = tp->cx; i + n < line->len; i++) + line->cells[i] = line->cells[i + n]; + line->len -= n; +} + +/* + * Erase characters at current position. + */ +static void tty3270_erase_characters(struct tty3270 *tp, int n) +{ + struct tty3270_line *line; + struct tty3270_cell *cell; + + line = tty3270_get_write_line(tp, tp->cy); + while (line->len > tp->cx && n-- > 0) { + cell = line->cells + tp->cx++; + tty3270_reset_cell(tp, cell); + } + tp->cx += n; + tp->cx = min_t(int, tp->cx, tp->view.cols - 1); +} + +/* + * Erase line, 3 different cases: + * Esc [ 0 K Erase from current position to end of line inclusive + * Esc [ 1 K Erase from beginning of line to current position inclusive + * Esc [ 2 K Erase entire line (without moving cursor) + */ +static void tty3270_erase_line(struct tty3270 *tp, int mode) +{ + struct tty3270_line *line; + struct tty3270_cell *cell; + int i, start, end; + + line = tty3270_get_write_line(tp, tp->cy); + + switch (mode) { + case 0: + start = tp->cx; + end = tp->view.cols; + break; + case 1: + start = 0; + end = tp->cx; + break; + case 2: + start = 0; + end = tp->view.cols; + break; + default: + return; + } + + for (i = start; i < end; i++) { + cell = line->cells + i; + tty3270_reset_cell(tp, cell); + cell->attributes.b_color = tp->attributes.b_color; + } + + if (line->len <= end) + line->len = end; +} + +/* + * Erase display, 3 different cases: + * Esc [ 0 J Erase from current position to bottom of screen inclusive + * Esc [ 1 J Erase from top of screen to current position inclusive + * Esc [ 2 J Erase entire screen (without moving the cursor) + */ +static void tty3270_erase_display(struct tty3270 *tp, int mode) +{ + struct tty3270_line *line; + int i, start, end; + + switch (mode) { + case 0: + tty3270_erase_line(tp, 0); + start = tp->cy + 1; + end = tty3270_tty_rows(tp); + break; + case 1: + start = 0; + end = tp->cy; + tty3270_erase_line(tp, 1); + break; + case 2: + start = 0; + end = tty3270_tty_rows(tp); + break; + default: + return; + } + for (i = start; i < end; i++) { + line = tty3270_get_write_line(tp, i); + line->len = 0; + line->dirty = 1; + } +} + +/* + * Set attributes found in an escape sequence. + * Esc [ <attr> ; <attr> ; ... m + */ +static void tty3270_set_attributes(struct tty3270 *tp) +{ + int i, attr; + + for (i = 0; i <= tp->esc_npar; i++) { + attr = tp->esc_par[i]; + switch (attr) { + case 0: /* Reset */ + tty3270_reset_attributes(&tp->attributes); + break; + /* Highlight. */ + case 4: /* Start underlining. */ + tp->attributes.highlight = TTY3270_HIGHLIGHT_UNDERSCORE; + break; + case 5: /* Start blink. */ + tp->attributes.highlight = TTY3270_HIGHLIGHT_BLINK; + break; + case 7: /* Start reverse. */ + tp->attributes.highlight = TTY3270_HIGHLIGHT_REVERSE; + break; + case 24: /* End underlining */ + tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_UNDERSCORE; + break; + case 25: /* End blink. */ + tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_BLINK; + break; + case 27: /* End reverse. */ + tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_REVERSE; + break; + /* Foreground color. */ + case 30: /* Black */ + case 31: /* Red */ + case 32: /* Green */ + case 33: /* Yellow */ + case 34: /* Blue */ + case 35: /* Magenta */ + case 36: /* Cyan */ + case 37: /* White */ + case 39: /* Black */ + tp->attributes.f_color = attr - 30; + break; + /* Background color. */ + case 40: /* Black */ + case 41: /* Red */ + case 42: /* Green */ + case 43: /* Yellow */ + case 44: /* Blue */ + case 45: /* Magenta */ + case 46: /* Cyan */ + case 47: /* White */ + case 49: /* Black */ + tp->attributes.b_color = attr - 40; + break; + } + } +} + +static inline int tty3270_getpar(struct tty3270 *tp, int ix) +{ + return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1; +} + +static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) +{ + struct tty3270_line *line; + struct tty3270_cell *cell; + int max_cx = max(0, cx); + int max_cy = max(0, cy); + + tp->cx = min_t(int, tp->view.cols - 1, max_cx); + line = tty3270_get_write_line(tp, tp->cy); + while (line->len < tp->cx) { + cell = line->cells + line->len; + cell->character = ' '; + cell->attributes = tp->attributes; + line->len++; + } + tp->cy = min_t(int, tty3270_tty_rows(tp) - 1, max_cy); +} + +/* + * Process escape sequences. Known sequences: + * Esc 7 Save Cursor Position + * Esc 8 Restore Cursor Position + * Esc [ Pn ; Pn ; .. m Set attributes + * Esc [ Pn ; Pn H Cursor Position + * Esc [ Pn ; Pn f Cursor Position + * Esc [ Pn A Cursor Up + * Esc [ Pn B Cursor Down + * Esc [ Pn C Cursor Forward + * Esc [ Pn D Cursor Backward + * Esc [ Pn G Cursor Horizontal Absolute + * Esc [ Pn X Erase Characters + * Esc [ Ps J Erase in Display + * Esc [ Ps K Erase in Line + * // FIXME: add all the new ones. + * + * Pn is a numeric parameter, a string of zero or more decimal digits. + * Ps is a selective parameter. + */ +static void tty3270_escape_sequence(struct tty3270 *tp, char ch) +{ + enum { ES_NORMAL, ES_ESC, ES_SQUARE, ES_PAREN, ES_GETPARS }; + + if (tp->esc_state == ES_NORMAL) { + if (ch == 0x1b) + /* Starting new escape sequence. */ + tp->esc_state = ES_ESC; + return; + } + if (tp->esc_state == ES_ESC) { + tp->esc_state = ES_NORMAL; + switch (ch) { + case '[': + tp->esc_state = ES_SQUARE; + break; + case '(': + tp->esc_state = ES_PAREN; + break; + case 'E': + tty3270_cr(tp); + tty3270_lf(tp); + break; + case 'M': + tty3270_ri(tp); + break; + case 'D': + tty3270_lf(tp); + break; + case 'Z': /* Respond ID. */ + kbd_puts_queue(&tp->port, "\033[?6c"); + break; + case '7': /* Save cursor position. */ + tp->saved_cx = tp->cx; + tp->saved_cy = tp->cy; + tp->saved_attributes = tp->attributes; + break; + case '8': /* Restore cursor position. */ + tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); + tp->attributes = tp->saved_attributes; + break; + case 'c': /* Reset terminal. */ + tp->cx = 0; + tp->cy = 0; + tp->saved_cx = 0; + tp->saved_cy = 0; + tty3270_reset_attributes(&tp->attributes); + tty3270_reset_attributes(&tp->saved_attributes); + tty3270_erase_display(tp, 2); + break; + } + return; + } + + switch (tp->esc_state) { + case ES_PAREN: + tp->esc_state = ES_NORMAL; + switch (ch) { + case 'B': + tp->attributes.alternate_charset = 0; + break; + case '0': + tp->attributes.alternate_charset = 1; + break; + } + return; + case ES_SQUARE: + tp->esc_state = ES_GETPARS; + memset(tp->esc_par, 0, sizeof(tp->esc_par)); + tp->esc_npar = 0; + tp->esc_ques = (ch == '?'); + if (tp->esc_ques) + return; + fallthrough; + case ES_GETPARS: + if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) { + tp->esc_npar++; + return; + } + if (ch >= '0' && ch <= '9') { + tp->esc_par[tp->esc_npar] *= 10; + tp->esc_par[tp->esc_npar] += ch - '0'; + return; + } + break; + default: + break; + } + tp->esc_state = ES_NORMAL; + if (ch == 'n' && !tp->esc_ques) { + if (tp->esc_par[0] == 5) /* Status report. */ + kbd_puts_queue(&tp->port, "\033[0n"); + else if (tp->esc_par[0] == 6) { /* Cursor report. */ + char buf[40]; + + sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1); + kbd_puts_queue(&tp->port, buf); + } + return; + } + if (tp->esc_ques) + return; + switch (ch) { + case 'm': + tty3270_set_attributes(tp); + break; + case 'H': /* Set cursor position. */ + case 'f': + tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1, + tty3270_getpar(tp, 0) - 1); + break; + case 'd': /* Set y position. */ + tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1); + break; + case 'A': /* Cursor up. */ + case 'F': + tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0)); + break; + case 'B': /* Cursor down. */ + case 'e': + case 'E': + tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0)); + break; + case 'C': /* Cursor forward. */ + case 'a': + tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy); + break; + case 'D': /* Cursor backward. */ + tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy); + break; + case 'G': /* Set x position. */ + case '`': + tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy); + break; + case 'X': /* Erase Characters. */ + tty3270_erase_characters(tp, tty3270_getpar(tp, 0)); + break; + case 'J': /* Erase display. */ + tty3270_erase_display(tp, tp->esc_par[0]); + break; + case 'K': /* Erase line. */ + tty3270_erase_line(tp, tp->esc_par[0]); + break; + case 'P': /* Delete characters. */ + tty3270_delete_characters(tp, tty3270_getpar(tp, 0)); + break; + case '@': /* Insert characters. */ + tty3270_insert_characters(tp, tty3270_getpar(tp, 0)); + break; + case 's': /* Save cursor position. */ + tp->saved_cx = tp->cx; + tp->saved_cy = tp->cy; + tp->saved_attributes = tp->attributes; + break; + case 'u': /* Restore cursor position. */ + tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); + tp->attributes = tp->saved_attributes; + break; + } +} + +/* + * String write routine for 3270 ttys + */ +static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, + const unsigned char *buf, int count) +{ + int i_msg, i; + + spin_lock_irq(&tp->view.lock); + for (i_msg = 0; !tty->flow.stopped && i_msg < count; i_msg++) { + if (tp->esc_state != 0) { + /* Continue escape sequence. */ + tty3270_escape_sequence(tp, buf[i_msg]); + continue; + } + + switch (buf[i_msg]) { + case 0x00: + break; + case 0x07: /* '\a' -- Alarm */ + tp->wcc |= TW_PLUSALARM; + break; + case 0x08: /* Backspace. */ + if (tp->cx > 0) { + tp->cx--; + tty3270_put_character(tp, ' '); + } + break; + case 0x09: /* '\t' -- Tabulate */ + for (i = tp->cx % 8; i < 8; i++) { + if (tp->cx >= tp->view.cols) { + tty3270_cr(tp); + tty3270_lf(tp); + break; + } + tty3270_put_character(tp, ' '); + tp->cx++; + } + break; + case 0x0a: /* '\n' -- New Line */ + tty3270_cr(tp); + tty3270_lf(tp); + break; + case 0x0c: /* '\f' -- Form Feed */ + tty3270_erase_display(tp, 2); + tp->cx = 0; + tp->cy = 0; + break; + case 0x0d: /* '\r' -- Carriage Return */ + tp->cx = 0; + break; + case 0x0e: + tp->attributes.alternate_charset = 1; + break; + case 0x0f: /* SuSE "exit alternate mode" */ + tp->attributes.alternate_charset = 0; + break; + case 0x1b: /* Start escape sequence. */ + tty3270_escape_sequence(tp, buf[i_msg]); + break; + default: /* Insert normal character. */ + if (tp->cx >= tp->view.cols) { + tty3270_cr(tp); + tty3270_lf(tp); + } + tty3270_put_character(tp, buf[i_msg]); + tp->cx++; + break; + } + } + /* Setup timer to update display after 1/10 second */ + tp->update_flags |= TTY_UPDATE_LINES; + if (!timer_pending(&tp->timer)) + tty3270_set_timer(tp, msecs_to_jiffies(100)); + + spin_unlock_irq(&tp->view.lock); +} + +/* + * String write routine for 3270 ttys + */ +static int tty3270_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return 0; + if (tp->char_count > 0) { + tty3270_do_write(tp, tty, tp->char_buf, tp->char_count); + tp->char_count = 0; + } + tty3270_do_write(tp, tty, buf, count); + return count; +} + +/* + * Put single characters to the ttys character buffer + */ +static int tty3270_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp || tp->char_count >= TTY3270_CHAR_BUF_SIZE) + return 0; + tp->char_buf[tp->char_count++] = ch; + return 1; +} + +/* + * Flush all characters from the ttys characeter buffer put there + * by tty3270_put_char. + */ +static void tty3270_flush_chars(struct tty_struct *tty) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return; + if (tp->char_count > 0) { + tty3270_do_write(tp, tty, tp->char_buf, tp->char_count); + tp->char_count = 0; } - /* Replace cline with allocated line s and reset cline. */ - list_add(&s->list, &cp->cline->list); - list_del_init(&cp->cline->list); - if (!list_empty(&cp->cline->update)) { - list_add(&s->update, &cp->cline->update); - list_del_init(&cp->cline->update); +} + +/* + * Check for visible/invisible input switches + */ +static void tty3270_set_termios(struct tty_struct *tty, const struct ktermios *old) +{ + struct tty3270 *tp; + int new; + + tp = tty->driver_data; + if (!tp) + return; + spin_lock_irq(&tp->view.lock); + if (L_ICANON(tty)) { + new = L_ECHO(tty) ? TF_INPUT : TF_INPUTN; + if (new != tp->inattr) { + tp->inattr = new; + tty3270_update_prompt(tp, ""); + tty3270_set_timer(tp, 1); + } } - cp->cline->len = 0; + spin_unlock_irq(&tp->view.lock); } /* - * Write a string to the 3270 console + * Disable reading from a 3270 tty */ +static void tty3270_throttle(struct tty_struct *tty) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return; + tp->throttle = 1; +} + +/* + * Enable reading from a 3270 tty + */ +static void tty3270_unthrottle(struct tty_struct *tty) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return; + tp->throttle = 0; + if (tp->attn) + tty3270_issue_read(tp, 1); +} + +/* + * Hang up the tty device. + */ +static void tty3270_hangup(struct tty_struct *tty) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return; + spin_lock_irq(&tp->view.lock); + tp->cx = 0; + tp->cy = 0; + tp->saved_cx = 0; + tp->saved_cy = 0; + tty3270_reset_attributes(&tp->attributes); + tty3270_reset_attributes(&tp->saved_attributes); + tty3270_blank_screen(tp); + tp->update_flags = TTY_UPDATE_ALL; + spin_unlock_irq(&tp->view.lock); + tty3270_set_timer(tp, 1); +} + +static void tty3270_wait_until_sent(struct tty_struct *tty, int timeout) +{ +} + +static int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd, + unsigned long arg) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return -ENODEV; + if (tty_io_error(tty)) + return -EIO; + return kbd_ioctl(tp->kbd, cmd, arg); +} + +#ifdef CONFIG_COMPAT +static long tty3270_compat_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return -ENODEV; + if (tty_io_error(tty)) + return -EIO; + return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static const struct tty_operations tty3270_ops = { + .install = tty3270_install, + .cleanup = tty3270_cleanup, + .open = tty3270_open, + .close = tty3270_close, + .write = tty3270_write, + .put_char = tty3270_put_char, + .flush_chars = tty3270_flush_chars, + .write_room = tty3270_write_room, + .throttle = tty3270_throttle, + .unthrottle = tty3270_unthrottle, + .hangup = tty3270_hangup, + .wait_until_sent = tty3270_wait_until_sent, + .ioctl = tty3270_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = tty3270_compat_ioctl, +#endif + .set_termios = tty3270_set_termios +}; + +static void tty3270_create_cb(int minor) +{ + tty_register_device(tty3270_driver, minor - RAW3270_FIRSTMINOR, NULL); +} + +static void tty3270_destroy_cb(int minor) +{ + tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR); +} + +static struct raw3270_notifier tty3270_notifier = { + .create = tty3270_create_cb, + .destroy = tty3270_destroy_cb, +}; + +/* + * 3270 tty registration code called from tty_init(). + * Most kernel services (incl. kmalloc) are available at this poimt. + */ +static int __init tty3270_init(void) +{ + struct tty_driver *driver; + int ret; + + driver = tty_alloc_driver(RAW3270_MAXDEVS, + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV | + TTY_DRIVER_RESET_TERMIOS); + if (IS_ERR(driver)) + return PTR_ERR(driver); + + /* + * Initialize the tty_driver structure + * Entries in tty3270_driver that are NOT initialized: + * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc + */ + driver->driver_name = "tty3270"; + driver->name = "3270/tty"; + driver->major = IBM_TTY3270_MAJOR; + driver->minor_start = RAW3270_FIRSTMINOR; + driver->name_base = RAW3270_FIRSTMINOR; + driver->type = TTY_DRIVER_TYPE_SYSTEM; + driver->subtype = SYSTEM_TYPE_TTY; + driver->init_termios = tty_std_termios; + tty_set_operations(driver, &tty3270_ops); + ret = tty_register_driver(driver); + if (ret) { + tty_driver_kref_put(driver); + return ret; + } + tty3270_driver = driver; + raw3270_register_notifier(&tty3270_notifier); + return 0; +} + +static void __exit tty3270_exit(void) +{ + struct tty_driver *driver; + + raw3270_unregister_notifier(&tty3270_notifier); + driver = tty3270_driver; + tty3270_driver = NULL; + tty_unregister_driver(driver); + tty_driver_kref_put(driver); + tty3270_del_views(); +} + +#if IS_ENABLED(CONFIG_TN3270_CONSOLE) + +static struct tty3270 *condev; + static void con3270_write(struct console *co, const char *str, unsigned int count) { - struct con3270 *cp; + struct tty3270 *tp = co->data; unsigned long flags; - unsigned char c; + char c; - cp = condev; - spin_lock_irqsave(&cp->view.lock, flags); - while (count-- > 0) { + spin_lock_irqsave(&tp->view.lock, flags); + while (count--) { c = *str++; - if (cp->cline->len == 0) - con3270_cline_add(cp); - if (c != '\n') - con3270_cline_insert(cp, c); - if (c == '\n' || cp->cline->len >= cp->view.cols) - con3270_cline_end(cp); + if (c == 0x0a) { + tty3270_cr(tp); + tty3270_lf(tp); + } else { + if (tp->cx >= tp->view.cols) { + tty3270_cr(tp); + tty3270_lf(tp); + } + tty3270_put_character(tp, c); + tp->cx++; + } } - /* Setup timer to output current console buffer after 1/10 second */ - cp->nr_up = 0; - if (cp->view.dev && !timer_pending(&cp->timer)) - con3270_set_timer(cp, HZ/10); - spin_unlock_irqrestore(&cp->view.lock,flags); + spin_unlock_irqrestore(&tp->view.lock, flags); } static struct tty_driver * @@ -522,14 +2079,11 @@ con3270_device(struct console *c, int *index) return tty3270_driver; } -/* - * Wait for end of write request. - */ static void -con3270_wait_write(struct con3270 *cp) +con3270_wait_write(struct tty3270 *tp) { - while (!cp->write) { - raw3270_wait_cons_dev(cp->view.dev); + while (!tp->write) { + raw3270_wait_cons_dev(tp->view.dev); barrier(); } } @@ -545,28 +2099,30 @@ con3270_wait_write(struct con3270 *cp) static int con3270_notify(struct notifier_block *self, unsigned long event, void *data) { - struct con3270 *cp; + struct tty3270 *tp; unsigned long flags; + int rc; - cp = condev; - if (!cp->view.dev) + tp = condev; + if (!tp->view.dev) return NOTIFY_DONE; - if (!raw3270_view_lock_unavailable(&cp->view)) - raw3270_activate_view(&cp->view); - if (!spin_trylock_irqsave(&cp->view.lock, flags)) + if (!raw3270_view_lock_unavailable(&tp->view)) { + rc = raw3270_activate_view(&tp->view); + if (rc) + return NOTIFY_DONE; + } + if (!spin_trylock_irqsave(&tp->view.lock, flags)) return NOTIFY_DONE; - con3270_wait_write(cp); - cp->nr_up = 0; - con3270_rebuild_update(cp); - con3270_update_status(cp); - while (cp->update_flags != 0) { - spin_unlock_irqrestore(&cp->view.lock, flags); - con3270_update(&cp->timer); - spin_lock_irqsave(&cp->view.lock, flags); - con3270_wait_write(cp); - } - spin_unlock_irqrestore(&cp->view.lock, flags); - + con3270_wait_write(tp); + tp->nr_up = 0; + tp->update_flags = TTY_UPDATE_ALL; + while (tp->update_flags != 0) { + spin_unlock_irqrestore(&tp->view.lock, flags); + tty3270_update(&tp->timer); + spin_lock_irqsave(&tp->view.lock, flags); + con3270_wait_write(tp); + } + spin_unlock_irqrestore(&tp->view.lock, flags); return NOTIFY_DONE; } @@ -580,9 +2136,6 @@ static struct notifier_block on_reboot_nb = { .priority = INT_MIN + 1, /* run the callback late */ }; -/* - * The console structure for the 3270 console - */ static struct console con3270 = { .name = "tty3270", .write = con3270_write, @@ -590,15 +2143,13 @@ static struct console con3270 = { .flags = CON_PRINTBUFFER, }; -/* - * 3270 console initialization code called from console_init(). - */ static int __init con3270_init(void) { + struct raw3270_view *view; struct raw3270 *rp; - void *cbuf; - int i; + struct tty3270 *tp; + int rc; /* Check if 3270 is to be the console */ if (!CONSOLE_IS_3270) @@ -614,38 +2165,28 @@ con3270_init(void) if (IS_ERR(rp)) return PTR_ERR(rp); - condev = kzalloc(sizeof(struct con3270), GFP_KERNEL | GFP_DMA); - if (!condev) - return -ENOMEM; - condev->view.dev = rp; - - condev->read = raw3270_request_alloc(0); - condev->read->callback = con3270_read_callback; - condev->read->callback_data = condev; - condev->write = raw3270_request_alloc(CON3270_OUTPUT_BUFFER_SIZE); - condev->kreset = raw3270_request_alloc(1); - - INIT_LIST_HEAD(&condev->lines); - INIT_LIST_HEAD(&condev->update); - timer_setup(&condev->timer, con3270_update, 0); - tasklet_init(&condev->readlet, con3270_read_tasklet, - (unsigned long) condev->read); - - raw3270_add_view(&condev->view, &con3270_fn, 1, RAW3270_VIEW_LOCK_IRQ); - - INIT_LIST_HEAD(&condev->freemem); - for (i = 0; i < CON3270_STRING_PAGES; i++) { - cbuf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); - add_string_memory(&condev->freemem, cbuf, PAGE_SIZE); - } - condev->cline = alloc_string(&condev->freemem, condev->view.cols); - condev->cline->len = 0; - con3270_create_status(condev); - condev->input = alloc_string(&condev->freemem, 80); + /* Check if the tty3270 is already there. */ + view = raw3270_find_view(&tty3270_fn, RAW3270_FIRSTMINOR); + if (IS_ERR(view)) { + rc = tty3270_create_view(0, &tp); + if (rc) + return rc; + } else { + tp = container_of(view, struct tty3270, view); + tp->inattr = TF_INPUT; + } + con3270.data = tp; + condev = tp; atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); register_reboot_notifier(&on_reboot_nb); register_console(&con3270); return 0; } - console_initcall(con3270_init); +#endif + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR); + +module_init(tty3270_init); +module_exit(tty3270_exit); diff --git a/drivers/s390/char/diag_ftp.c b/drivers/s390/char/diag_ftp.c index 36bbd6b6e210..65c7f2d565d8 100644 --- a/drivers/s390/char/diag_ftp.c +++ b/drivers/s390/char/diag_ftp.c @@ -159,8 +159,8 @@ ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) goto out; } - len = strlcpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident)); - if (len >= HMCDRV_FTP_FIDENT_MAX) { + len = strscpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident)); + if (len < 0) { len = -EINVAL; goto out_free; } diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 4c4683d8784a..4f26b0a55620 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -19,6 +19,7 @@ #include <linux/slab.h> #include <linux/types.h> +#include <uapi/asm/fs3270.h> #include <asm/ccwdev.h> #include <asm/cio.h> #include <asm/ebcdic.h> @@ -44,14 +45,12 @@ struct fs3270 { static DEFINE_MUTEX(fs3270_mutex); -static void -fs3270_wake_up(struct raw3270_request *rq, void *data) +static void fs3270_wake_up(struct raw3270_request *rq, void *data) { - wake_up((wait_queue_head_t *) data); + wake_up((wait_queue_head_t *)data); } -static inline int -fs3270_working(struct fs3270 *fp) +static inline int fs3270_working(struct fs3270 *fp) { /* * The fullscreen view is in working order if the view @@ -60,13 +59,12 @@ fs3270_working(struct fs3270 *fp) return fp->active && raw3270_request_final(fp->init); } -static int -fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) +static int fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) { struct fs3270 *fp; int rc; - fp = (struct fs3270 *) view; + fp = (struct fs3270 *)view; rq->callback = fs3270_wake_up; rq->callback_data = &fp->wait; @@ -90,22 +88,20 @@ fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) /* * Switch to the fullscreen view. */ -static void -fs3270_reset_callback(struct raw3270_request *rq, void *data) +static void fs3270_reset_callback(struct raw3270_request *rq, void *data) { struct fs3270 *fp; - fp = (struct fs3270 *) rq->view; + fp = (struct fs3270 *)rq->view; raw3270_request_reset(rq); wake_up(&fp->wait); } -static void -fs3270_restore_callback(struct raw3270_request *rq, void *data) +static void fs3270_restore_callback(struct raw3270_request *rq, void *data) { struct fs3270 *fp; - fp = (struct fs3270 *) rq->view; + fp = (struct fs3270 *)rq->view; if (rq->rc != 0 || rq->rescnt != 0) { if (fp->fs_pid) kill_pid(fp->fs_pid, SIGHUP, 1); @@ -115,29 +111,31 @@ fs3270_restore_callback(struct raw3270_request *rq, void *data) wake_up(&fp->wait); } -static int -fs3270_activate(struct raw3270_view *view) +static int fs3270_activate(struct raw3270_view *view) { struct fs3270 *fp; char *cp; int rc; - fp = (struct fs3270 *) view; + fp = (struct fs3270 *)view; /* If an old init command is still running just return. */ if (!raw3270_request_final(fp->init)) return 0; + raw3270_request_set_cmd(fp->init, TC_EWRITEA); + raw3270_request_set_idal(fp->init, fp->rdbuf); + fp->init->rescnt = 0; + cp = fp->rdbuf->data[0]; if (fp->rdbuf_size == 0) { /* No saved buffer. Just clear the screen. */ - raw3270_request_set_cmd(fp->init, TC_EWRITEA); + fp->init->ccw.count = 1; fp->init->callback = fs3270_reset_callback; + cp[0] = 0; } else { /* Restore fullscreen buffer saved by fs3270_deactivate. */ - raw3270_request_set_cmd(fp->init, TC_EWRITEA); - raw3270_request_set_idal(fp->init, fp->rdbuf); fp->init->ccw.count = fp->rdbuf_size; - cp = fp->rdbuf->data[0]; + fp->init->callback = fs3270_restore_callback; cp[0] = TW_KR; cp[1] = TO_SBA; cp[2] = cp[6]; @@ -146,10 +144,9 @@ fs3270_activate(struct raw3270_view *view) cp[5] = TO_SBA; cp[6] = 0x40; cp[7] = 0x40; - fp->init->rescnt = 0; - fp->init->callback = fs3270_restore_callback; } - rc = fp->init->rc = raw3270_start_locked(view, fp->init); + rc = raw3270_start_locked(view, fp->init); + fp->init->rc = rc; if (rc) fp->init->callback(fp->init, NULL); else @@ -160,12 +157,11 @@ fs3270_activate(struct raw3270_view *view) /* * Shutdown fullscreen view. */ -static void -fs3270_save_callback(struct raw3270_request *rq, void *data) +static void fs3270_save_callback(struct raw3270_request *rq, void *data) { struct fs3270 *fp; - fp = (struct fs3270 *) rq->view; + fp = (struct fs3270 *)rq->view; /* Correct idal buffer element 0 address. */ fp->rdbuf->data[0] -= 5; @@ -181,18 +177,18 @@ fs3270_save_callback(struct raw3270_request *rq, void *data) if (fp->fs_pid) kill_pid(fp->fs_pid, SIGHUP, 1); fp->rdbuf_size = 0; - } else + } else { fp->rdbuf_size = fp->rdbuf->size - rq->rescnt; + } raw3270_request_reset(rq); wake_up(&fp->wait); } -static void -fs3270_deactivate(struct raw3270_view *view) +static void fs3270_deactivate(struct raw3270_view *view) { struct fs3270 *fp; - fp = (struct fs3270 *) view; + fp = (struct fs3270 *)view; fp->active = 0; /* If an old init command is still running just return. */ @@ -218,8 +214,8 @@ fs3270_deactivate(struct raw3270_view *view) fp->init->callback(fp->init, NULL); } -static void -fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) +static void fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, + struct irb *irb) { /* Handle ATTN. Set indication and wake waiters for attention. */ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { @@ -239,14 +235,14 @@ fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) /* * Process reads from fullscreen 3270. */ -static ssize_t -fs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off) +static ssize_t fs3270_read(struct file *filp, char __user *data, + size_t count, loff_t *off) { struct fs3270 *fp; struct raw3270_request *rq; struct idal_buffer *ib; ssize_t rc; - + if (count == 0 || count > 65535) return -EINVAL; fp = filp->private_data; @@ -271,12 +267,12 @@ fs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off) rc = -EFAULT; else rc = count; - } } raw3270_request_free(rq); - } else + } else { rc = PTR_ERR(rq); + } idal_buffer_free(ib); return rc; } @@ -284,8 +280,8 @@ fs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off) /* * Process writes to fullscreen 3270. */ -static ssize_t -fs3270_write(struct file *filp, const char __user *data, size_t count, loff_t *off) +static ssize_t fs3270_write(struct file *filp, const char __user *data, + size_t count, loff_t *off) { struct fs3270 *fp; struct raw3270_request *rq; @@ -310,11 +306,13 @@ fs3270_write(struct file *filp, const char __user *data, size_t count, loff_t *o rc = fs3270_do_io(&fp->view, rq); if (rc == 0) rc = count - rq->rescnt; - } else + } else { rc = -EFAULT; + } raw3270_request_free(rq); - } else + } else { rc = PTR_ERR(rq); + } idal_buffer_free(ib); return rc; } @@ -322,8 +320,7 @@ fs3270_write(struct file *filp, const char __user *data, size_t count, loff_t *o /* * process ioctl commands for the tube driver */ -static long -fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +static long fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { char __user *argp; struct fs3270 *fp; @@ -370,12 +367,11 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) /* * Allocate fs3270 structure. */ -static struct fs3270 * -fs3270_alloc_view(void) +static struct fs3270 *fs3270_alloc_view(void) { struct fs3270 *fp; - fp = kzalloc(sizeof(struct fs3270),GFP_KERNEL); + fp = kzalloc(sizeof(*fp), GFP_KERNEL); if (!fp) return ERR_PTR(-ENOMEM); fp->init = raw3270_request_alloc(0); @@ -389,27 +385,25 @@ fs3270_alloc_view(void) /* * Free fs3270 structure. */ -static void -fs3270_free_view(struct raw3270_view *view) +static void fs3270_free_view(struct raw3270_view *view) { struct fs3270 *fp; - fp = (struct fs3270 *) view; + fp = (struct fs3270 *)view; if (fp->rdbuf) idal_buffer_free(fp->rdbuf); - raw3270_request_free(((struct fs3270 *) view)->init); + raw3270_request_free(((struct fs3270 *)view)->init); kfree(view); } /* * Unlink fs3270 data structure from filp. */ -static void -fs3270_release(struct raw3270_view *view) +static void fs3270_release(struct raw3270_view *view) { struct fs3270 *fp; - fp = (struct fs3270 *) view; + fp = (struct fs3270 *)view; if (fp->fs_pid) kill_pid(fp->fs_pid, SIGHUP, 1); } @@ -418,7 +412,7 @@ fs3270_release(struct raw3270_view *view) static struct raw3270_fn fs3270_fn = { .activate = fs3270_activate, .deactivate = fs3270_deactivate, - .intv = (void *) fs3270_irq, + .intv = (void *)fs3270_irq, .release = fs3270_release, .free = fs3270_free_view }; @@ -426,8 +420,7 @@ static struct raw3270_fn fs3270_fn = { /* * This routine is called whenever a 3270 fullscreen device is opened. */ -static int -fs3270_open(struct inode *inode, struct file *filp) +static int fs3270_open(struct inode *inode, struct file *filp) { struct fs3270 *fp; struct idal_buffer *ib; @@ -439,6 +432,7 @@ fs3270_open(struct inode *inode, struct file *filp) /* Check for minor 0 multiplexer. */ if (minor == 0) { struct tty_struct *tty = get_current_tty(); + if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) { tty_kref_put(tty); return -ENODEV; @@ -448,7 +442,7 @@ fs3270_open(struct inode *inode, struct file *filp) } mutex_lock(&fs3270_mutex); /* Check if some other program is already using fullscreen mode. */ - fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); + fp = (struct fs3270 *)raw3270_find_view(&fs3270_fn, minor); if (!IS_ERR(fp)) { raw3270_put_view(&fp->view); rc = -EBUSY; @@ -471,7 +465,7 @@ fs3270_open(struct inode *inode, struct file *filp) } /* Allocate idal-buffer. */ - ib = idal_buffer_alloc(2*fp->view.rows*fp->view.cols + 5, 0); + ib = idal_buffer_alloc(2 * fp->view.rows * fp->view.cols + 5, 0); if (IS_ERR(ib)) { raw3270_put_view(&fp->view); raw3270_del_view(&fp->view); @@ -497,8 +491,7 @@ out: * This routine is called when the 3270 tty is closed. We wait * for the remaining request to be completed. Then we clean up. */ -static int -fs3270_close(struct inode *inode, struct file *filp) +static int fs3270_close(struct inode *inode, struct file *filp) { struct fs3270 *fp; @@ -538,8 +531,7 @@ static void fs3270_destroy_cb(int minor) __unregister_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub"); } -static struct raw3270_notifier fs3270_notifier = -{ +static struct raw3270_notifier fs3270_notifier = { .create = fs3270_create_cb, .destroy = fs3270_destroy_cb, }; @@ -547,8 +539,7 @@ static struct raw3270_notifier fs3270_notifier = /* * 3270 fullscreen driver initialization. */ -static int __init -fs3270_init(void) +static int __init fs3270_init(void) { int rc; @@ -561,8 +552,7 @@ fs3270_init(void) return 0; } -static void __exit -fs3270_exit(void) +static void __exit fs3270_exit(void) { raw3270_unregister_notifier(&fs3270_notifier); device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, 0)); diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index fb3f62ac8be4..09d7570d3b7d 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -30,6 +30,7 @@ #include <linux/mutex.h> struct class *class3270; +EXPORT_SYMBOL(class3270); /* The main 3270 data structure. */ struct raw3270 { @@ -37,7 +38,8 @@ struct raw3270 { struct ccw_device *cdev; int minor; - short model, rows, cols; + int model, rows, cols; + int old_model, old_rows, old_cols; unsigned int state; unsigned long flags; @@ -54,6 +56,7 @@ struct raw3270 { struct raw3270_request init_readpart; struct raw3270_request init_readmod; unsigned char init_data[256]; + struct work_struct resize_work; }; /* raw3270->state */ @@ -89,6 +92,7 @@ module_param(tubxcorrect, bool, 0); * Wait queue for device init/delete, view delete. */ DECLARE_WAIT_QUEUE_HEAD(raw3270_wait_queue); +EXPORT_SYMBOL(raw3270_wait_queue); static void __raw3270_disconnect(struct raw3270 *rp); @@ -111,9 +115,15 @@ static inline int raw3270_state_ready(struct raw3270 *rp) return rp->state == RAW3270_STATE_READY; } -void -raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr) +void raw3270_buffer_address(struct raw3270 *rp, char *cp, int x, int y) { + int addr; + + if (x < 0) + x = max_t(int, 0, rp->view->cols + x); + if (y < 0) + y = max_t(int, 0, rp->view->rows + y); + addr = (y * rp->view->cols) + x; if (test_bit(RAW3270_FLAGS_14BITADDR, &rp->flags)) { cp[0] = (addr >> 8) & 0x3f; cp[1] = addr & 0xff; @@ -122,17 +132,17 @@ raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr) cp[1] = raw3270_ebcgraf[addr & 0x3f]; } } +EXPORT_SYMBOL(raw3270_buffer_address); /* * Allocate a new 3270 ccw request */ -struct raw3270_request * -raw3270_request_alloc(size_t size) +struct raw3270_request *raw3270_request_alloc(size_t size) { struct raw3270_request *rq; /* Allocate request structure */ - rq = kzalloc(sizeof(struct raw3270_request), GFP_KERNEL | GFP_DMA); + rq = kzalloc(sizeof(*rq), GFP_KERNEL | GFP_DMA); if (!rq) return ERR_PTR(-ENOMEM); @@ -155,46 +165,48 @@ raw3270_request_alloc(size_t size) return rq; } +EXPORT_SYMBOL(raw3270_request_alloc); /* * Free 3270 ccw request */ -void -raw3270_request_free (struct raw3270_request *rq) +void raw3270_request_free(struct raw3270_request *rq) { kfree(rq->buffer); kfree(rq); } +EXPORT_SYMBOL(raw3270_request_free); /* * Reset request to initial state. */ -void -raw3270_request_reset(struct raw3270_request *rq) +int raw3270_request_reset(struct raw3270_request *rq) { - BUG_ON(!list_empty(&rq->list)); + if (WARN_ON_ONCE(!list_empty(&rq->list))) + return -EBUSY; rq->ccw.cmd_code = 0; rq->ccw.count = 0; rq->ccw.cda = __pa(rq->buffer); rq->ccw.flags = CCW_FLAG_SLI; rq->rescnt = 0; rq->rc = 0; + return 0; } +EXPORT_SYMBOL(raw3270_request_reset); /* * Set command code to ccw of a request. */ -void -raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd) +void raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd) { rq->ccw.cmd_code = cmd; } +EXPORT_SYMBOL(raw3270_request_set_cmd); /* * Add data fragment to output buffer. */ -int -raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size) +int raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size) { if (size + rq->ccw.count > rq->size) return -E2BIG; @@ -202,35 +214,35 @@ raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size) rq->ccw.count += size; return 0; } +EXPORT_SYMBOL(raw3270_request_add_data); /* * Set address/length pair to ccw of a request. */ -void -raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size) +void raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size) { rq->ccw.cda = __pa(data); rq->ccw.count = size; } +EXPORT_SYMBOL(raw3270_request_set_data); /* * Set idal buffer to ccw of a request. */ -void -raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) +void raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) { rq->ccw.cda = __pa(ib->data); rq->ccw.count = ib->size; rq->ccw.flags |= CCW_FLAG_IDA; } +EXPORT_SYMBOL(raw3270_request_set_idal); /* * Add the request to the request queue, try to start it if the * 3270 device is idle. Return without waiting for end of i/o. */ -static int -__raw3270_start(struct raw3270 *rp, struct raw3270_view *view, - struct raw3270_request *rq) +static int __raw3270_start(struct raw3270 *rp, struct raw3270_view *view, + struct raw3270_request *rq) { rq->view = view; raw3270_get_view(view); @@ -238,7 +250,7 @@ __raw3270_start(struct raw3270 *rp, struct raw3270_view *view, !test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) { /* No other requests are on the queue. Start this one. */ rq->rc = ccw_device_start(rp->cdev, &rq->ccw, - (unsigned long) rq, 0, 0); + (unsigned long)rq, 0, 0); if (rq->rc) { raw3270_put_view(view); return rq->rc; @@ -248,16 +260,14 @@ __raw3270_start(struct raw3270 *rp, struct raw3270_view *view, return 0; } -int -raw3270_view_active(struct raw3270_view *view) +int raw3270_view_active(struct raw3270_view *view) { struct raw3270 *rp = view->dev; return rp && rp->view == view; } -int -raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) +int raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) { unsigned long flags; struct raw3270 *rp; @@ -274,9 +284,25 @@ raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); return rc; } +EXPORT_SYMBOL(raw3270_start); -int -raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) +int raw3270_start_request(struct raw3270_view *view, struct raw3270_request *rq, + int cmd, void *data, size_t len) +{ + int rc; + + rc = raw3270_request_reset(rq); + if (rc) + return rc; + raw3270_request_set_cmd(rq, cmd); + rc = raw3270_request_add_data(rq, data, len); + if (rc) + return rc; + return raw3270_start(view, rq); +} +EXPORT_SYMBOL(raw3270_start_request); + +int raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) { struct raw3270 *rp; int rc; @@ -290,9 +316,9 @@ raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) rc = __raw3270_start(rp, view, rq); return rc; } +EXPORT_SYMBOL(raw3270_start_locked); -int -raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq) +int raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq) { struct raw3270 *rp; @@ -302,12 +328,12 @@ raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq) list_add_tail(&rq->list, &rp->req_queue); return 0; } +EXPORT_SYMBOL(raw3270_start_irq); /* * 3270 interrupt routine, called from the ccw_device layer */ -static void -raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) +static void raw3270_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) { struct raw3270 *rp; struct raw3270_view *view; @@ -316,7 +342,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) rp = dev_get_drvdata(&cdev->dev); if (!rp) return; - rq = (struct raw3270_request *) intparm; + rq = (struct raw3270_request *)intparm; view = rq ? rq->view : rp->view; if (!IS_ERR(irb)) { @@ -357,9 +383,9 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) * started successful. */ while (!list_empty(&rp->req_queue)) { - rq = list_entry(rp->req_queue.next,struct raw3270_request,list); + rq = list_entry(rp->req_queue.next, struct raw3270_request, list); rq->rc = ccw_device_start(rp->cdev, &rq->ccw, - (unsigned long) rq, 0, 0); + (unsigned long)rq, 0, 0); if (rq->rc == 0) break; /* Start failed. Remove request and do callback. */ @@ -399,7 +425,7 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */ char ymin; char xmax; char ymax; - } __attribute__ ((packed)) uab; + } __packed uab; struct { /* Alternate Usable Area Self-Defining Parameter */ char l; /* Length of this Self-Defining Parm */ char sdpid; /* 0x02 if Alternate Usable Area */ @@ -412,17 +438,27 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */ int auayr; char awauai; char ahauai; - } __attribute__ ((packed)) aua; -} __attribute__ ((packed)); + } __packed aua; +} __packed; -static void -raw3270_size_device_vm(struct raw3270 *rp) +static void raw3270_size_device_vm(struct raw3270 *rp) { int rc, model; struct ccw_dev_id dev_id; struct diag210 diag_data; + struct diag8c diag8c_data; ccw_device_get_id(rp->cdev, &dev_id); + rc = diag8c(&diag8c_data, &dev_id); + if (!rc) { + rp->model = 2; + rp->rows = diag8c_data.height; + rp->cols = diag8c_data.width; + if (diag8c_data.flags & 1) + set_bit(RAW3270_FLAGS_14BITADDR, &rp->flags); + return; + } + diag_data.vrdcdvno = dev_id.devno; diag_data.vrdclen = sizeof(struct diag210); rc = diag210(&diag_data); @@ -454,16 +490,14 @@ raw3270_size_device_vm(struct raw3270 *rp) } } -static void -raw3270_size_device(struct raw3270 *rp) +static void raw3270_size_device(struct raw3270 *rp, char *init_data) { struct raw3270_ua *uap; /* Got a Query Reply */ - uap = (struct raw3270_ua *) (rp->init_data + 1); + uap = (struct raw3270_ua *)(init_data + 1); /* Paranoia check. */ - if (rp->init_readmod.rc || rp->init_data[0] != 0x88 || - uap->uab.qcode != 0x81) { + if (init_data[0] != 0x88 || uap->uab.qcode != 0x81) { /* Couldn't detect size. Use default model 2. */ rp->model = 2; rp->rows = 24; @@ -494,17 +528,20 @@ raw3270_size_device(struct raw3270 *rp) rp->model = 5; } -static void -raw3270_size_device_done(struct raw3270 *rp) +static void raw3270_resize_work(struct work_struct *work) { + struct raw3270 *rp = container_of(work, struct raw3270, resize_work); struct raw3270_view *view; - rp->view = NULL; - rp->state = RAW3270_STATE_READY; /* Notify views about new size */ - list_for_each_entry(view, &rp->view_list, list) + list_for_each_entry(view, &rp->view_list, list) { if (view->fn->resize) - view->fn->resize(view, rp->model, rp->rows, rp->cols); + view->fn->resize(view, rp->model, rp->rows, rp->cols, + rp->old_model, rp->old_rows, rp->old_cols); + } + rp->old_cols = rp->cols; + rp->old_rows = rp->rows; + rp->old_model = rp->model; /* Setup processing done, now activate a view */ list_for_each_entry(view, &rp->view_list, list) { rp->view = view; @@ -514,17 +551,23 @@ raw3270_size_device_done(struct raw3270 *rp) } } -static void -raw3270_read_modified_cb(struct raw3270_request *rq, void *data) +static void raw3270_size_device_done(struct raw3270 *rp) +{ + rp->view = NULL; + rp->state = RAW3270_STATE_READY; + schedule_work(&rp->resize_work); +} + +void raw3270_read_modified_cb(struct raw3270_request *rq, void *data) { struct raw3270 *rp = rq->view->dev; - raw3270_size_device(rp); + raw3270_size_device(rp, data); raw3270_size_device_done(rp); } +EXPORT_SYMBOL(raw3270_read_modified_cb); -static void -raw3270_read_modified(struct raw3270 *rp) +static void raw3270_read_modified(struct raw3270 *rp) { if (rp->state != RAW3270_STATE_W4ATTN) return; @@ -534,17 +577,18 @@ raw3270_read_modified(struct raw3270 *rp) rp->init_readmod.ccw.cmd_code = TC_READMOD; rp->init_readmod.ccw.flags = CCW_FLAG_SLI; rp->init_readmod.ccw.count = sizeof(rp->init_data); - rp->init_readmod.ccw.cda = (__u32) __pa(rp->init_data); + rp->init_readmod.ccw.cda = (__u32)__pa(rp->init_data); rp->init_readmod.callback = raw3270_read_modified_cb; + rp->init_readmod.callback_data = rp->init_data; rp->state = RAW3270_STATE_READMOD; raw3270_start_irq(&rp->init_view, &rp->init_readmod); } -static void -raw3270_writesf_readpart(struct raw3270 *rp) +static void raw3270_writesf_readpart(struct raw3270 *rp) { - static const unsigned char wbuf[] = - { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; + static const unsigned char wbuf[] = { + 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 + }; /* Store 'read partition' data stream to init_data */ memset(&rp->init_readpart, 0, sizeof(rp->init_readpart)); @@ -553,7 +597,7 @@ raw3270_writesf_readpart(struct raw3270 *rp) rp->init_readpart.ccw.cmd_code = TC_WRITESF; rp->init_readpart.ccw.flags = CCW_FLAG_SLI; rp->init_readpart.ccw.count = sizeof(wbuf); - rp->init_readpart.ccw.cda = (__u32) __pa(&rp->init_data); + rp->init_readpart.ccw.cda = (__u32)__pa(&rp->init_data); rp->state = RAW3270_STATE_W4ATTN; raw3270_start_irq(&rp->init_view, &rp->init_readpart); } @@ -561,8 +605,7 @@ raw3270_writesf_readpart(struct raw3270 *rp) /* * Device reset */ -static void -raw3270_reset_device_cb(struct raw3270_request *rq, void *data) +static void raw3270_reset_device_cb(struct raw3270_request *rq, void *data) { struct raw3270 *rp = rq->view->dev; @@ -574,13 +617,13 @@ raw3270_reset_device_cb(struct raw3270_request *rq, void *data) } else if (MACHINE_IS_VM) { raw3270_size_device_vm(rp); raw3270_size_device_done(rp); - } else + } else { raw3270_writesf_readpart(rp); + } memset(&rp->init_reset, 0, sizeof(rp->init_reset)); } -static int -__raw3270_reset_device(struct raw3270 *rp) +static int __raw3270_reset_device(struct raw3270 *rp) { int rc; @@ -592,7 +635,7 @@ __raw3270_reset_device(struct raw3270 *rp) rp->init_reset.ccw.cmd_code = TC_EWRITEA; rp->init_reset.ccw.flags = CCW_FLAG_SLI; rp->init_reset.ccw.count = 1; - rp->init_reset.ccw.cda = (__u32) __pa(rp->init_data); + rp->init_reset.ccw.cda = (__u32)__pa(rp->init_data); rp->init_reset.callback = raw3270_reset_device_cb; rc = __raw3270_start(rp, &rp->init_view, &rp->init_reset); if (rc == 0 && rp->state == RAW3270_STATE_INIT) @@ -600,8 +643,7 @@ __raw3270_reset_device(struct raw3270 *rp) return rc; } -static int -raw3270_reset_device(struct raw3270 *rp) +static int raw3270_reset_device(struct raw3270 *rp) { unsigned long flags; int rc; @@ -612,8 +654,7 @@ raw3270_reset_device(struct raw3270 *rp) return rc; } -int -raw3270_reset(struct raw3270_view *view) +int raw3270_reset(struct raw3270_view *view) { struct raw3270 *rp; int rc; @@ -627,9 +668,9 @@ raw3270_reset(struct raw3270_view *view) rc = raw3270_reset_device(view->dev); return rc; } +EXPORT_SYMBOL(raw3270_reset); -static void -__raw3270_disconnect(struct raw3270 *rp) +static void __raw3270_disconnect(struct raw3270 *rp) { struct raw3270_request *rq; struct raw3270_view *view; @@ -638,7 +679,7 @@ __raw3270_disconnect(struct raw3270 *rp) rp->view = &rp->init_view; /* Cancel all queued requests */ while (!list_empty(&rp->req_queue)) { - rq = list_entry(rp->req_queue.next,struct raw3270_request,list); + rq = list_entry(rp->req_queue.next, struct raw3270_request, list); view = rq->view; rq->rc = -EACCES; list_del_init(&rq->list); @@ -650,9 +691,8 @@ __raw3270_disconnect(struct raw3270 *rp) __raw3270_reset_device(rp); } -static void -raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, - struct irb *irb) +static void raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, + struct irb *irb) { struct raw3270 *rp; @@ -678,8 +718,8 @@ static struct raw3270_fn raw3270_init_fn = { /* * Setup new 3270 device. */ -static int -raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) +static int raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, + char *ascebc) { struct list_head *l; struct raw3270 *tmp; @@ -699,6 +739,8 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) /* Set defaults. */ rp->rows = 24; rp->cols = 80; + rp->old_rows = rp->rows; + rp->old_cols = rp->cols; INIT_LIST_HEAD(&rp->req_queue); INIT_LIST_HEAD(&rp->view_list); @@ -706,6 +748,7 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) rp->init_view.dev = rp; rp->init_view.fn = &raw3270_init_fn; rp->view = &rp->init_view; + INIT_WORK(&rp->resize_work, raw3270_resize_work); /* * Add device to list and find the smallest unused minor @@ -764,7 +807,7 @@ struct raw3270 __init *raw3270_setup_console(void) if (IS_ERR(cdev)) return ERR_CAST(cdev); - rp = kzalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA); + rp = kzalloc(sizeof(*rp), GFP_KERNEL | GFP_DMA); ascebc = kzalloc(256, GFP_KERNEL); rc = raw3270_setup_device(cdev, rp, ascebc); if (rc) @@ -789,8 +832,7 @@ struct raw3270 __init *raw3270_setup_console(void) return rp; } -void -raw3270_wait_cons_dev(struct raw3270 *rp) +void raw3270_wait_cons_dev(struct raw3270 *rp) { unsigned long flags; @@ -804,14 +846,13 @@ raw3270_wait_cons_dev(struct raw3270 *rp) /* * Create a 3270 device structure. */ -static struct raw3270 * -raw3270_create_device(struct ccw_device *cdev) +static struct raw3270 *raw3270_create_device(struct ccw_device *cdev) { struct raw3270 *rp; char *ascebc; int rc; - rp = kzalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA); + rp = kzalloc(sizeof(*rp), GFP_KERNEL | GFP_DMA); if (!rp) return ERR_PTR(-ENOMEM); ascebc = kmalloc(256, GFP_KERNEL); @@ -845,14 +886,57 @@ int raw3270_view_lock_unavailable(struct raw3270_view *view) return 0; } +static int raw3270_assign_activate_view(struct raw3270 *rp, struct raw3270_view *view) +{ + rp->view = view; + return view->fn->activate(view); +} + +static int __raw3270_activate_view(struct raw3270 *rp, struct raw3270_view *view) +{ + struct raw3270_view *oldview = NULL, *nv; + int rc; + + if (rp->view == view) + return 0; + + if (!raw3270_state_ready(rp)) + return -EBUSY; + + if (rp->view && rp->view->fn->deactivate) { + oldview = rp->view; + oldview->fn->deactivate(oldview); + } + + rc = raw3270_assign_activate_view(rp, view); + if (!rc) + return 0; + + /* Didn't work. Try to reactivate the old view. */ + if (oldview) { + rc = raw3270_assign_activate_view(rp, oldview); + if (!rc) + return 0; + } + + /* Didn't work as well. Try any other view. */ + list_for_each_entry(nv, &rp->view_list, list) { + if (nv == view || nv == oldview) + continue; + rc = raw3270_assign_activate_view(rp, nv); + if (!rc) + break; + rp->view = NULL; + } + return rc; +} + /* * Activate a view. */ -int -raw3270_activate_view(struct raw3270_view *view) +int raw3270_activate_view(struct raw3270_view *view) { struct raw3270 *rp; - struct raw3270_view *oldview, *nv; unsigned long flags; int rc; @@ -860,42 +944,16 @@ raw3270_activate_view(struct raw3270_view *view) if (!rp) return -ENODEV; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - if (rp->view == view) - rc = 0; - else if (!raw3270_state_ready(rp)) - rc = -EBUSY; - else { - oldview = NULL; - if (rp->view && rp->view->fn->deactivate) { - oldview = rp->view; - oldview->fn->deactivate(oldview); - } - rp->view = view; - rc = view->fn->activate(view); - if (rc) { - /* Didn't work. Try to reactivate the old view. */ - rp->view = oldview; - if (!oldview || oldview->fn->activate(oldview) != 0) { - /* Didn't work as well. Try any other view. */ - list_for_each_entry(nv, &rp->view_list, list) - if (nv != view && nv != oldview) { - rp->view = nv; - if (nv->fn->activate(nv) == 0) - break; - rp->view = NULL; - } - } - } - } + rc = __raw3270_activate_view(rp, view); spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); return rc; } +EXPORT_SYMBOL(raw3270_activate_view); /* * Deactivate current view. */ -void -raw3270_deactivate_view(struct raw3270_view *view) +void raw3270_deactivate_view(struct raw3270_view *view) { unsigned long flags; struct raw3270 *rp; @@ -922,12 +980,13 @@ raw3270_deactivate_view(struct raw3270_view *view) } spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); } +EXPORT_SYMBOL(raw3270_deactivate_view); /* * Add view to device with minor "minor". */ -int -raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor, int subclass) +int raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, + int minor, int subclass) { unsigned long flags; struct raw3270 *rp; @@ -958,12 +1017,12 @@ raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor, in mutex_unlock(&raw3270_mutex); return rc; } +EXPORT_SYMBOL(raw3270_add_view); /* * Find specific view of device with minor "minor". */ -struct raw3270_view * -raw3270_find_view(struct raw3270_fn *fn, int minor) +struct raw3270_view *raw3270_find_view(struct raw3270_fn *fn, int minor) { struct raw3270 *rp; struct raw3270_view *view, *tmp; @@ -988,12 +1047,12 @@ raw3270_find_view(struct raw3270_fn *fn, int minor) mutex_unlock(&raw3270_mutex); return view; } +EXPORT_SYMBOL(raw3270_find_view); /* * Remove view from device and free view structure via call to view->fn->free. */ -void -raw3270_del_view(struct raw3270_view *view) +void raw3270_del_view(struct raw3270_view *view) { unsigned long flags; struct raw3270 *rp; @@ -1022,12 +1081,12 @@ raw3270_del_view(struct raw3270_view *view) if (view->fn->free) view->fn->free(view); } +EXPORT_SYMBOL(raw3270_del_view); /* * Remove a 3270 device structure. */ -static void -raw3270_delete_device(struct raw3270 *rp) +static void raw3270_delete_device(struct raw3270 *rp) { struct ccw_device *cdev; @@ -1050,8 +1109,7 @@ raw3270_delete_device(struct raw3270 *rp) kfree(rp); } -static int -raw3270_probe (struct ccw_device *cdev) +static int raw3270_probe(struct ccw_device *cdev) { return 0; } @@ -1059,31 +1117,32 @@ raw3270_probe (struct ccw_device *cdev) /* * Additional attributes for a 3270 device */ -static ssize_t -raw3270_model_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t model_show(struct device *dev, struct device_attribute *attr, + char *buf) { return sysfs_emit(buf, "%i\n", ((struct raw3270 *)dev_get_drvdata(dev))->model); } -static DEVICE_ATTR(model, 0444, raw3270_model_show, NULL); +static DEVICE_ATTR_RO(model); -static ssize_t -raw3270_rows_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t rows_show(struct device *dev, struct device_attribute *attr, + char *buf) { return sysfs_emit(buf, "%i\n", ((struct raw3270 *)dev_get_drvdata(dev))->rows); } -static DEVICE_ATTR(rows, 0444, raw3270_rows_show, NULL); +static DEVICE_ATTR_RO(rows); static ssize_t -raw3270_columns_show(struct device *dev, struct device_attribute *attr, char *buf) +columns_show(struct device *dev, struct device_attribute *attr, + char *buf) { return sysfs_emit(buf, "%i\n", ((struct raw3270 *)dev_get_drvdata(dev))->cols); } -static DEVICE_ATTR(columns, 0444, raw3270_columns_show, NULL); +static DEVICE_ATTR_RO(columns); -static struct attribute * raw3270_attrs[] = { +static struct attribute *raw3270_attrs[] = { &dev_attr_model.attr, &dev_attr_rows.attr, &dev_attr_columns.attr, @@ -1115,6 +1174,7 @@ int raw3270_register_notifier(struct raw3270_notifier *notifier) mutex_unlock(&raw3270_mutex); return 0; } +EXPORT_SYMBOL(raw3270_register_notifier); void raw3270_unregister_notifier(struct raw3270_notifier *notifier) { @@ -1126,12 +1186,12 @@ void raw3270_unregister_notifier(struct raw3270_notifier *notifier) list_del(¬ifier->list); mutex_unlock(&raw3270_mutex); } +EXPORT_SYMBOL(raw3270_unregister_notifier); /* * Set 3270 device online. */ -static int -raw3270_set_online (struct ccw_device *cdev) +static int raw3270_set_online(struct ccw_device *cdev) { struct raw3270_notifier *np; struct raw3270 *rp; @@ -1158,8 +1218,7 @@ failure: /* * Remove 3270 device structure. */ -static void -raw3270_remove (struct ccw_device *cdev) +static void raw3270_remove(struct ccw_device *cdev) { unsigned long flags; struct raw3270 *rp; @@ -1173,7 +1232,7 @@ raw3270_remove (struct ccw_device *cdev) * devices even if they haven't been varied online. * Thus, rp may validly be NULL here. */ - if (rp == NULL) + if (!rp) return; sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group); @@ -1209,8 +1268,7 @@ raw3270_remove (struct ccw_device *cdev) /* * Set 3270 device offline. */ -static int -raw3270_set_offline (struct ccw_device *cdev) +static int raw3270_set_offline(struct ccw_device *cdev) { struct raw3270 *rp; @@ -1249,8 +1307,7 @@ static struct ccw_driver raw3270_ccw_driver = { .int_class = IRQIO_C70, }; -static int -raw3270_init(void) +static int raw3270_init(void) { struct raw3270 *rp; int rc; @@ -1272,8 +1329,7 @@ raw3270_init(void) return rc; } -static void -raw3270_exit(void) +static void raw3270_exit(void) { ccw_driver_unregister(&raw3270_ccw_driver); class_destroy(class3270); @@ -1283,25 +1339,3 @@ MODULE_LICENSE("GPL"); module_init(raw3270_init); module_exit(raw3270_exit); - -EXPORT_SYMBOL(class3270); -EXPORT_SYMBOL(raw3270_request_alloc); -EXPORT_SYMBOL(raw3270_request_free); -EXPORT_SYMBOL(raw3270_request_reset); -EXPORT_SYMBOL(raw3270_request_set_cmd); -EXPORT_SYMBOL(raw3270_request_add_data); -EXPORT_SYMBOL(raw3270_request_set_data); -EXPORT_SYMBOL(raw3270_request_set_idal); -EXPORT_SYMBOL(raw3270_buffer_address); -EXPORT_SYMBOL(raw3270_add_view); -EXPORT_SYMBOL(raw3270_del_view); -EXPORT_SYMBOL(raw3270_find_view); -EXPORT_SYMBOL(raw3270_activate_view); -EXPORT_SYMBOL(raw3270_deactivate_view); -EXPORT_SYMBOL(raw3270_start); -EXPORT_SYMBOL(raw3270_start_locked); -EXPORT_SYMBOL(raw3270_start_irq); -EXPORT_SYMBOL(raw3270_reset); -EXPORT_SYMBOL(raw3270_register_notifier); -EXPORT_SYMBOL(raw3270_unregister_notifier); -EXPORT_SYMBOL(raw3270_wait_queue); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 4cb6b5ee44ca..b1beecc7a0a9 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -8,88 +8,10 @@ * Copyright IBM Corp. 2003, 2009 */ +#include <uapi/asm/raw3270.h> #include <asm/idals.h> #include <asm/ioctl.h> -/* ioctls for fullscreen 3270 */ -#define TUBICMD _IO('3', 3) /* set ccw command for fs reads. */ -#define TUBOCMD _IO('3', 4) /* set ccw command for fs writes. */ -#define TUBGETI _IO('3', 7) /* get ccw command for fs reads. */ -#define TUBGETO _IO('3', 8) /* get ccw command for fs writes. */ -#define TUBSETMOD _IO('3',12) /* FIXME: what does it do ?*/ -#define TUBGETMOD _IO('3',13) /* FIXME: what does it do ?*/ - -/* Local Channel Commands */ -#define TC_WRITE 0x01 /* Write */ -#define TC_RDBUF 0x02 /* Read Buffer */ -#define TC_EWRITE 0x05 /* Erase write */ -#define TC_READMOD 0x06 /* Read modified */ -#define TC_EWRITEA 0x0d /* Erase write alternate */ -#define TC_WRITESF 0x11 /* Write structured field */ - -/* Buffer Control Orders */ -#define TO_SF 0x1d /* Start field */ -#define TO_SBA 0x11 /* Set buffer address */ -#define TO_IC 0x13 /* Insert cursor */ -#define TO_PT 0x05 /* Program tab */ -#define TO_RA 0x3c /* Repeat to address */ -#define TO_SFE 0x29 /* Start field extended */ -#define TO_EUA 0x12 /* Erase unprotected to address */ -#define TO_MF 0x2c /* Modify field */ -#define TO_SA 0x28 /* Set attribute */ - -/* Field Attribute Bytes */ -#define TF_INPUT 0x40 /* Visible input */ -#define TF_INPUTN 0x4c /* Invisible input */ -#define TF_INMDT 0xc1 /* Visible, Set-MDT */ -#define TF_LOG 0x60 - -/* Character Attribute Bytes */ -#define TAT_RESET 0x00 -#define TAT_FIELD 0xc0 -#define TAT_EXTHI 0x41 -#define TAT_COLOR 0x42 -#define TAT_CHARS 0x43 -#define TAT_TRANS 0x46 - -/* Extended-Highlighting Bytes */ -#define TAX_RESET 0x00 -#define TAX_BLINK 0xf1 -#define TAX_REVER 0xf2 -#define TAX_UNDER 0xf4 - -/* Reset value */ -#define TAR_RESET 0x00 - -/* Color values */ -#define TAC_RESET 0x00 -#define TAC_BLUE 0xf1 -#define TAC_RED 0xf2 -#define TAC_PINK 0xf3 -#define TAC_GREEN 0xf4 -#define TAC_TURQ 0xf5 -#define TAC_YELLOW 0xf6 -#define TAC_WHITE 0xf7 -#define TAC_DEFAULT 0x00 - -/* Write Control Characters */ -#define TW_NONE 0x40 /* No particular action */ -#define TW_KR 0xc2 /* Keyboard restore */ -#define TW_PLUSALARM 0x04 /* Add this bit for alarm */ - -#define RAW3270_FIRSTMINOR 1 /* First minor number */ -#define RAW3270_MAXDEVS 255 /* Max number of 3270 devices */ - -/* For TUBGETMOD and TUBSETMOD. Should include. */ -struct raw3270_iocb { - short model; - short line_cnt; - short col_cnt; - short pf_cnt; - short re_cnt; - short map; -}; - struct raw3270; struct raw3270_view; extern struct class *class3270; @@ -105,17 +27,17 @@ struct raw3270_request { int rc; /* return code for this request. */ /* Callback for delivering final status. */ - void (*callback)(struct raw3270_request *, void *); + void (*callback)(struct raw3270_request *rq, void *data); void *callback_data; }; struct raw3270_request *raw3270_request_alloc(size_t size); -void raw3270_request_free(struct raw3270_request *); -void raw3270_request_reset(struct raw3270_request *); -void raw3270_request_set_cmd(struct raw3270_request *, u8 cmd); -int raw3270_request_add_data(struct raw3270_request *, void *, size_t); -void raw3270_request_set_data(struct raw3270_request *, void *, size_t); -void raw3270_request_set_idal(struct raw3270_request *, struct idal_buffer *); +void raw3270_request_free(struct raw3270_request *rq); +int raw3270_request_reset(struct raw3270_request *rq); +void raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd); +int raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size); +void raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size); +void raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib); static inline int raw3270_request_final(struct raw3270_request *rq) @@ -123,19 +45,21 @@ raw3270_request_final(struct raw3270_request *rq) return list_empty(&rq->list); } -void raw3270_buffer_address(struct raw3270 *, char *, unsigned short); +void raw3270_buffer_address(struct raw3270 *, char *, int, int); /* * Functions of a 3270 view. */ struct raw3270_fn { - int (*activate)(struct raw3270_view *); - void (*deactivate)(struct raw3270_view *); - void (*intv)(struct raw3270_view *, - struct raw3270_request *, struct irb *); - void (*release)(struct raw3270_view *); - void (*free)(struct raw3270_view *); - void (*resize)(struct raw3270_view *, int, int, int); + int (*activate)(struct raw3270_view *rq); + void (*deactivate)(struct raw3270_view *rq); + void (*intv)(struct raw3270_view *view, + struct raw3270_request *rq, struct irb *ib); + void (*release)(struct raw3270_view *view); + void (*free)(struct raw3270_view *view); + void (*resize)(struct raw3270_view *view, + int new_model, int new_cols, int new_rows, + int old_model, int old_cols, int old_rows); }; /* @@ -148,7 +72,7 @@ struct raw3270_fn { */ struct raw3270_view { struct list_head list; - spinlock_t lock; + spinlock_t lock; /* protects members of view */ #define RAW3270_VIEW_LOCK_IRQ 0 #define RAW3270_VIEW_LOCK_BH 1 atomic_t ref_count; @@ -159,18 +83,21 @@ struct raw3270_view { unsigned char *ascebc; /* ascii -> ebcdic table */ }; -int raw3270_add_view(struct raw3270_view *, struct raw3270_fn *, int, int); +int raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor, int subclass); int raw3270_view_lock_unavailable(struct raw3270_view *view); -int raw3270_activate_view(struct raw3270_view *); -void raw3270_del_view(struct raw3270_view *); -void raw3270_deactivate_view(struct raw3270_view *); -struct raw3270_view *raw3270_find_view(struct raw3270_fn *, int); -int raw3270_start(struct raw3270_view *, struct raw3270_request *); -int raw3270_start_locked(struct raw3270_view *, struct raw3270_request *); -int raw3270_start_irq(struct raw3270_view *, struct raw3270_request *); -int raw3270_reset(struct raw3270_view *); -struct raw3270_view *raw3270_view(struct raw3270_view *); -int raw3270_view_active(struct raw3270_view *); +int raw3270_activate_view(struct raw3270_view *view); +void raw3270_del_view(struct raw3270_view *view); +void raw3270_deactivate_view(struct raw3270_view *view); +struct raw3270_view *raw3270_find_view(struct raw3270_fn *fn, int minor); +int raw3270_start(struct raw3270_view *view, struct raw3270_request *rq); +int raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq); +int raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq); +int raw3270_reset(struct raw3270_view *view); +struct raw3270_view *raw3270_view(struct raw3270_view *view); +int raw3270_view_active(struct raw3270_view *view); +int raw3270_start_request(struct raw3270_view *view, struct raw3270_request *rq, + int cmd, void *data, size_t len); +void raw3270_read_modified_cb(struct raw3270_request *rq, void *data); /* Reference count inliner for view structures. */ static inline void @@ -189,7 +116,7 @@ raw3270_put_view(struct raw3270_view *view) } struct raw3270 *raw3270_setup_console(void); -void raw3270_wait_cons_dev(struct raw3270 *); +void raw3270_wait_cons_dev(struct raw3270 *rp); /* Notifier for device addition/removal */ struct raw3270_notifier { @@ -198,87 +125,5 @@ struct raw3270_notifier { void (*destroy)(int minor); }; -int raw3270_register_notifier(struct raw3270_notifier *); -void raw3270_unregister_notifier(struct raw3270_notifier *); - -/* - * Little memory allocator for string objects. - */ -struct string -{ - struct list_head list; - struct list_head update; - unsigned long size; - unsigned long len; - char string[]; -} __attribute__ ((aligned(8))); - -static inline struct string * -alloc_string(struct list_head *free_list, unsigned long len) -{ - struct string *cs, *tmp; - unsigned long size; - - size = (len + 7L) & -8L; - list_for_each_entry(cs, free_list, list) { - if (cs->size < size) - continue; - if (cs->size > size + sizeof(struct string)) { - char *endaddr = (char *) (cs + 1) + cs->size; - tmp = (struct string *) (endaddr - size) - 1; - tmp->size = size; - cs->size -= size + sizeof(struct string); - cs = tmp; - } else - list_del(&cs->list); - cs->len = len; - INIT_LIST_HEAD(&cs->list); - INIT_LIST_HEAD(&cs->update); - return cs; - } - return NULL; -} - -static inline unsigned long -free_string(struct list_head *free_list, struct string *cs) -{ - struct string *tmp; - struct list_head *p, *left; - - /* Find out the left neighbour in free memory list. */ - left = free_list; - list_for_each(p, free_list) { - if (list_entry(p, struct string, list) > cs) - break; - left = p; - } - /* Try to merge with right neighbour = next element from left. */ - if (left->next != free_list) { - tmp = list_entry(left->next, struct string, list); - if ((char *) (cs + 1) + cs->size == (char *) tmp) { - list_del(&tmp->list); - cs->size += tmp->size + sizeof(struct string); - } - } - /* Try to merge with left neighbour. */ - if (left != free_list) { - tmp = list_entry(left, struct string, list); - if ((char *) (tmp + 1) + tmp->size == (char *) cs) { - tmp->size += cs->size + sizeof(struct string); - return tmp->size; - } - } - __list_add(&cs->list, left, left->next); - return cs->size; -} - -static inline void -add_string_memory(struct list_head *free_list, void *mem, unsigned long size) -{ - struct string *cs; - - cs = (struct string *) mem; - cs->size = size - sizeof(struct string); - free_string(free_list, cs); -} - +int raw3270_register_notifier(struct raw3270_notifier *notifier); +void raw3270_unregister_notifier(struct raw3270_notifier *notifier); diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index c1c70a161c0e..f480d6c7fd39 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c @@ -163,7 +163,7 @@ static void __init sclp_early_console_detect(struct init_sccb *sccb) sclp.has_linemode = 1; } -void __init sclp_early_adjust_va(void) +void __init __no_sanitize_address sclp_early_adjust_va(void) { sclp_early_sccb = __va((unsigned long)sclp_early_sccb); } diff --git a/drivers/s390/char/sclp_ftp.c b/drivers/s390/char/sclp_ftp.c index ec5a0e2b9255..d27e2cbfbccb 100644 --- a/drivers/s390/char/sclp_ftp.c +++ b/drivers/s390/char/sclp_ftp.c @@ -90,7 +90,7 @@ static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp) struct completion completion; struct sclp_diag_sccb *sccb; struct sclp_req *req; - size_t len; + ssize_t len; int rc; req = kzalloc(sizeof(*req), GFP_KERNEL); @@ -117,9 +117,9 @@ static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp) sccb->evbuf.mdd.ftp.length = ftp->len; sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf); - len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname, + len = strscpy(sccb->evbuf.mdd.ftp.fident, ftp->fname, HMCDRV_FTP_FIDENT_MAX); - if (len >= HMCDRV_FTP_FIDENT_MAX) { + if (len < 0) { rc = -EINVAL; goto out_free; } diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c deleted file mode 100644 index 26e3995ac062..000000000000 --- a/drivers/s390/char/tty3270.c +++ /dev/null @@ -1,1963 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * IBM/3270 Driver - tty functions. - * - * Author(s): - * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) - * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> - * -- Copyright IBM Corp. 2003 - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kdev_t.h> -#include <linux/tty.h> -#include <linux/vt_kern.h> -#include <linux/init.h> -#include <linux/console.h> -#include <linux/interrupt.h> -#include <linux/workqueue.h> - -#include <linux/slab.h> -#include <linux/memblock.h> -#include <linux/compat.h> - -#include <asm/ccwdev.h> -#include <asm/cio.h> -#include <asm/ebcdic.h> -#include <linux/uaccess.h> - -#include "raw3270.h" -#include "tty3270.h" -#include "keyboard.h" - -#define TTY3270_CHAR_BUF_SIZE 256 -#define TTY3270_OUTPUT_BUFFER_SIZE 1024 -#define TTY3270_STRING_PAGES 5 - -struct tty_driver *tty3270_driver; -static int tty3270_max_index; - -static struct raw3270_fn tty3270_fn; - -struct tty3270_cell { - unsigned char character; - unsigned char highlight; - unsigned char f_color; -}; - -struct tty3270_line { - struct tty3270_cell *cells; - int len; -}; - -#define ESCAPE_NPAR 8 - -/* - * The main tty view data structure. - * FIXME: - * 1) describe line orientation & lines list concept against screen - * 2) describe conversion of screen to lines - * 3) describe line format. - */ -struct tty3270 { - struct raw3270_view view; - struct tty_port port; - void **freemem_pages; /* Array of pages used for freemem. */ - struct list_head freemem; /* List of free memory for strings. */ - - /* Output stuff. */ - struct list_head lines; /* List of lines. */ - struct list_head update; /* List of lines to update. */ - unsigned char wcc; /* Write control character. */ - int nr_lines; /* # lines in list. */ - int nr_up; /* # lines up in history. */ - unsigned long update_flags; /* Update indication bits. */ - struct string *status; /* Lower right of display. */ - struct raw3270_request *write; /* Single write request. */ - struct timer_list timer; /* Output delay timer. */ - - /* Current tty screen. */ - unsigned int cx, cy; /* Current output position. */ - unsigned int highlight; /* Blink/reverse/underscore */ - unsigned int f_color; /* Foreground color */ - struct tty3270_line *screen; - unsigned int n_model, n_cols, n_rows; /* New model & size */ - struct work_struct resize_work; - - /* Input stuff. */ - struct string *prompt; /* Output string for input area. */ - struct string *input; /* Input string for read request. */ - struct raw3270_request *read; /* Single read request. */ - struct raw3270_request *kreset; /* Single keyboard reset request. */ - unsigned char inattr; /* Visible/invisible input. */ - int throttle, attn; /* tty throttle/unthrottle. */ - struct tasklet_struct readlet; /* Tasklet to issue read request. */ - struct tasklet_struct hanglet; /* Tasklet to hang up the tty. */ - struct kbd_data *kbd; /* key_maps stuff. */ - - /* Escape sequence parsing. */ - int esc_state, esc_ques, esc_npar; - int esc_par[ESCAPE_NPAR]; - unsigned int saved_cx, saved_cy; - unsigned int saved_highlight, saved_f_color; - - /* Command recalling. */ - struct list_head rcl_lines; /* List of recallable lines. */ - struct list_head *rcl_walk; /* Point in rcl_lines list. */ - int rcl_nr, rcl_max; /* Number/max number of rcl_lines. */ - - /* Character array for put_char/flush_chars. */ - unsigned int char_count; - char char_buf[TTY3270_CHAR_BUF_SIZE]; -}; - -/* tty3270->update_flags. See tty3270_update for details. */ -#define TTY_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */ -#define TTY_UPDATE_LIST 2 /* Update lines in tty3270->update. */ -#define TTY_UPDATE_INPUT 4 /* Update input line. */ -#define TTY_UPDATE_STATUS 8 /* Update status line. */ -#define TTY_UPDATE_ALL 16 /* Recreate screen. */ - -static void tty3270_update(struct timer_list *); -static void tty3270_resize_work(struct work_struct *work); - -/* - * Setup timeout for a device. On timeout trigger an update. - */ -static void tty3270_set_timer(struct tty3270 *tp, int expires) -{ - mod_timer(&tp->timer, jiffies + expires); -} - -/* - * The input line are the two last lines of the screen. - */ -static void -tty3270_update_prompt(struct tty3270 *tp, char *input, int count) -{ - struct string *line; - unsigned int off; - - line = tp->prompt; - if (count != 0) - line->string[5] = TF_INMDT; - else - line->string[5] = tp->inattr; - if (count > tp->view.cols * 2 - 11) - count = tp->view.cols * 2 - 11; - memcpy(line->string + 6, input, count); - line->string[6 + count] = TO_IC; - /* Clear to end of input line. */ - if (count < tp->view.cols * 2 - 11) { - line->string[7 + count] = TO_RA; - line->string[10 + count] = 0; - off = tp->view.cols * tp->view.rows - 9; - raw3270_buffer_address(tp->view.dev, line->string+count+8, off); - line->len = 11 + count; - } else - line->len = 7 + count; - tp->update_flags |= TTY_UPDATE_INPUT; -} - -static void -tty3270_create_prompt(struct tty3270 *tp) -{ - static const unsigned char blueprint[] = - { TO_SBA, 0, 0, 0x6e, TO_SF, TF_INPUT, - /* empty input string */ - TO_IC, TO_RA, 0, 0, 0 }; - struct string *line; - unsigned int offset; - - line = alloc_string(&tp->freemem, - sizeof(blueprint) + tp->view.cols * 2 - 9); - tp->prompt = line; - tp->inattr = TF_INPUT; - /* Copy blueprint to status line */ - memcpy(line->string, blueprint, sizeof(blueprint)); - line->len = sizeof(blueprint); - /* Set output offsets. */ - offset = tp->view.cols * (tp->view.rows - 2); - raw3270_buffer_address(tp->view.dev, line->string + 1, offset); - offset = tp->view.cols * tp->view.rows - 9; - raw3270_buffer_address(tp->view.dev, line->string + 8, offset); - - /* Allocate input string for reading. */ - tp->input = alloc_string(&tp->freemem, tp->view.cols * 2 - 9 + 6); -} - -/* - * The status line is the last line of the screen. It shows the string - * "Running"/"Holding" in the lower right corner of the screen. - */ -static void -tty3270_update_status(struct tty3270 * tp) -{ - char *str; - - str = (tp->nr_up != 0) ? "History" : "Running"; - memcpy(tp->status->string + 8, str, 7); - codepage_convert(tp->view.ascebc, tp->status->string + 8, 7); - tp->update_flags |= TTY_UPDATE_STATUS; -} - -static void -tty3270_create_status(struct tty3270 * tp) -{ - static const unsigned char blueprint[] = - { TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, TAC_GREEN, - 0, 0, 0, 0, 0, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, - TAC_RESET }; - struct string *line; - unsigned int offset; - - line = alloc_string(&tp->freemem,sizeof(blueprint)); - tp->status = line; - /* Copy blueprint to status line */ - memcpy(line->string, blueprint, sizeof(blueprint)); - /* Set address to start of status string (= last 9 characters). */ - offset = tp->view.cols * tp->view.rows - 9; - raw3270_buffer_address(tp->view.dev, line->string + 1, offset); -} - -/* - * Set output offsets to 3270 datastream fragment of a tty string. - * (TO_SBA offset at the start and TO_RA offset at the end of the string) - */ -static void -tty3270_update_string(struct tty3270 *tp, struct string *line, int nr) -{ - unsigned char *cp; - - raw3270_buffer_address(tp->view.dev, line->string + 1, - tp->view.cols * nr); - cp = line->string + line->len - 4; - if (*cp == TO_RA) - raw3270_buffer_address(tp->view.dev, cp + 1, - tp->view.cols * (nr + 1)); -} - -/* - * Rebuild update list to print all lines. - */ -static void -tty3270_rebuild_update(struct tty3270 *tp) -{ - struct string *s, *n; - int line, nr_up; - - /* - * Throw away update list and create a new one, - * containing all lines that will fit on the screen. - */ - list_for_each_entry_safe(s, n, &tp->update, update) - list_del_init(&s->update); - line = tp->view.rows - 3; - nr_up = tp->nr_up; - list_for_each_entry_reverse(s, &tp->lines, list) { - if (nr_up > 0) { - nr_up--; - continue; - } - tty3270_update_string(tp, s, line); - list_add(&s->update, &tp->update); - if (--line < 0) - break; - } - tp->update_flags |= TTY_UPDATE_LIST; -} - -/* - * Alloc string for size bytes. If there is not enough room in - * freemem, free strings until there is room. - */ -static struct string * -tty3270_alloc_string(struct tty3270 *tp, size_t size) -{ - struct string *s, *n; - - s = alloc_string(&tp->freemem, size); - if (s) - return s; - list_for_each_entry_safe(s, n, &tp->lines, list) { - BUG_ON(tp->nr_lines <= tp->view.rows - 2); - list_del(&s->list); - if (!list_empty(&s->update)) - list_del(&s->update); - tp->nr_lines--; - if (free_string(&tp->freemem, s) >= size) - break; - } - s = alloc_string(&tp->freemem, size); - BUG_ON(!s); - if (tp->nr_up != 0 && - tp->nr_up + tp->view.rows - 2 >= tp->nr_lines) { - tp->nr_up = tp->nr_lines - tp->view.rows + 2; - tty3270_rebuild_update(tp); - tty3270_update_status(tp); - } - return s; -} - -/* - * Add an empty line to the list. - */ -static void -tty3270_blank_line(struct tty3270 *tp) -{ - static const unsigned char blueprint[] = - { TO_SBA, 0, 0, TO_SA, TAT_EXTHI, TAX_RESET, - TO_SA, TAT_COLOR, TAC_RESET, TO_RA, 0, 0, 0 }; - struct string *s; - - s = tty3270_alloc_string(tp, sizeof(blueprint)); - memcpy(s->string, blueprint, sizeof(blueprint)); - s->len = sizeof(blueprint); - list_add_tail(&s->list, &tp->lines); - tp->nr_lines++; - if (tp->nr_up != 0) - tp->nr_up++; -} - -/* - * Create a blank screen and remove all lines from the history. - */ -static void -tty3270_blank_screen(struct tty3270 *tp) -{ - struct string *s, *n; - int i; - - for (i = 0; i < tp->view.rows - 2; i++) - tp->screen[i].len = 0; - tp->nr_up = 0; - list_for_each_entry_safe(s, n, &tp->lines, list) { - list_del(&s->list); - if (!list_empty(&s->update)) - list_del(&s->update); - tp->nr_lines--; - free_string(&tp->freemem, s); - } -} - -/* - * Write request completion callback. - */ -static void -tty3270_write_callback(struct raw3270_request *rq, void *data) -{ - struct tty3270 *tp = container_of(rq->view, struct tty3270, view); - - if (rq->rc != 0) { - /* Write wasn't successful. Refresh all. */ - tp->update_flags = TTY_UPDATE_ALL; - tty3270_set_timer(tp, 1); - } - raw3270_request_reset(rq); - xchg(&tp->write, rq); -} - -/* - * Update 3270 display. - */ -static void -tty3270_update(struct timer_list *t) -{ - struct tty3270 *tp = from_timer(tp, t, timer); - static char invalid_sba[2] = { 0xff, 0xff }; - struct raw3270_request *wrq; - unsigned long updated; - struct string *s, *n; - char *sba, *str; - int rc, len; - - wrq = xchg(&tp->write, 0); - if (!wrq) { - tty3270_set_timer(tp, 1); - return; - } - - spin_lock(&tp->view.lock); - updated = 0; - if (tp->update_flags & TTY_UPDATE_ALL) { - tty3270_rebuild_update(tp); - tty3270_update_status(tp); - tp->update_flags = TTY_UPDATE_ERASE | TTY_UPDATE_LIST | - TTY_UPDATE_INPUT | TTY_UPDATE_STATUS; - } - if (tp->update_flags & TTY_UPDATE_ERASE) { - /* Use erase write alternate to erase display. */ - raw3270_request_set_cmd(wrq, TC_EWRITEA); - updated |= TTY_UPDATE_ERASE; - } else - raw3270_request_set_cmd(wrq, TC_WRITE); - - raw3270_request_add_data(wrq, &tp->wcc, 1); - tp->wcc = TW_NONE; - - /* - * Update status line. - */ - if (tp->update_flags & TTY_UPDATE_STATUS) - if (raw3270_request_add_data(wrq, tp->status->string, - tp->status->len) == 0) - updated |= TTY_UPDATE_STATUS; - - /* - * Write input line. - */ - if (tp->update_flags & TTY_UPDATE_INPUT) - if (raw3270_request_add_data(wrq, tp->prompt->string, - tp->prompt->len) == 0) - updated |= TTY_UPDATE_INPUT; - - sba = invalid_sba; - - if (tp->update_flags & TTY_UPDATE_LIST) { - /* Write strings in the update list to the screen. */ - list_for_each_entry_safe(s, n, &tp->update, update) { - str = s->string; - len = s->len; - /* - * Skip TO_SBA at the start of the string if the - * last output position matches the start address - * of this line. - */ - if (s->string[1] == sba[0] && s->string[2] == sba[1]) { - str += 3; - len -= 3; - } - if (raw3270_request_add_data(wrq, str, len) != 0) - break; - list_del_init(&s->update); - if (s->string[s->len - 4] == TO_RA) - sba = s->string + s->len - 3; - else - sba = invalid_sba; - } - if (list_empty(&tp->update)) - updated |= TTY_UPDATE_LIST; - } - wrq->callback = tty3270_write_callback; - rc = raw3270_start(&tp->view, wrq); - if (rc == 0) { - tp->update_flags &= ~updated; - if (tp->update_flags) - tty3270_set_timer(tp, 1); - } else { - raw3270_request_reset(wrq); - xchg(&tp->write, wrq); - } - spin_unlock(&tp->view.lock); -} - -/* - * Command recalling. - */ -static void -tty3270_rcl_add(struct tty3270 *tp, char *input, int len) -{ - struct string *s; - - tp->rcl_walk = NULL; - if (len <= 0) - return; - if (tp->rcl_nr >= tp->rcl_max) { - s = list_entry(tp->rcl_lines.next, struct string, list); - list_del(&s->list); - free_string(&tp->freemem, s); - tp->rcl_nr--; - } - s = tty3270_alloc_string(tp, len); - memcpy(s->string, input, len); - list_add_tail(&s->list, &tp->rcl_lines); - tp->rcl_nr++; -} - -static void -tty3270_rcl_backward(struct kbd_data *kbd) -{ - struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); - struct string *s; - - spin_lock_bh(&tp->view.lock); - if (tp->inattr == TF_INPUT) { - if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines) - tp->rcl_walk = tp->rcl_walk->prev; - else if (!list_empty(&tp->rcl_lines)) - tp->rcl_walk = tp->rcl_lines.prev; - s = tp->rcl_walk ? - list_entry(tp->rcl_walk, struct string, list) : NULL; - if (tp->rcl_walk) { - s = list_entry(tp->rcl_walk, struct string, list); - tty3270_update_prompt(tp, s->string, s->len); - } else - tty3270_update_prompt(tp, NULL, 0); - tty3270_set_timer(tp, 1); - } - spin_unlock_bh(&tp->view.lock); -} - -/* - * Deactivate tty view. - */ -static void -tty3270_exit_tty(struct kbd_data *kbd) -{ - struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); - - raw3270_deactivate_view(&tp->view); -} - -/* - * Scroll forward in history. - */ -static void -tty3270_scroll_forward(struct kbd_data *kbd) -{ - struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); - int nr_up; - - spin_lock_bh(&tp->view.lock); - nr_up = tp->nr_up - tp->view.rows + 2; - if (nr_up < 0) - nr_up = 0; - if (nr_up != tp->nr_up) { - tp->nr_up = nr_up; - tty3270_rebuild_update(tp); - tty3270_update_status(tp); - tty3270_set_timer(tp, 1); - } - spin_unlock_bh(&tp->view.lock); -} - -/* - * Scroll backward in history. - */ -static void -tty3270_scroll_backward(struct kbd_data *kbd) -{ - struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); - int nr_up; - - spin_lock_bh(&tp->view.lock); - nr_up = tp->nr_up + tp->view.rows - 2; - if (nr_up + tp->view.rows - 2 > tp->nr_lines) - nr_up = tp->nr_lines - tp->view.rows + 2; - if (nr_up != tp->nr_up) { - tp->nr_up = nr_up; - tty3270_rebuild_update(tp); - tty3270_update_status(tp); - tty3270_set_timer(tp, 1); - } - spin_unlock_bh(&tp->view.lock); -} - -/* - * Pass input line to tty. - */ -static void -tty3270_read_tasklet(unsigned long data) -{ - struct raw3270_request *rrq = (struct raw3270_request *)data; - static char kreset_data = TW_KR; - struct tty3270 *tp = container_of(rrq->view, struct tty3270, view); - char *input; - int len; - - spin_lock_bh(&tp->view.lock); - /* - * Two AID keys are special: For 0x7d (enter) the input line - * has to be emitted to the tty and for 0x6d the screen - * needs to be redrawn. - */ - input = NULL; - len = 0; - if (tp->input->string[0] == 0x7d) { - /* Enter: write input to tty. */ - input = tp->input->string + 6; - len = tp->input->len - 6 - rrq->rescnt; - if (tp->inattr != TF_INPUTN) - tty3270_rcl_add(tp, input, len); - if (tp->nr_up > 0) { - tp->nr_up = 0; - tty3270_rebuild_update(tp); - tty3270_update_status(tp); - } - /* Clear input area. */ - tty3270_update_prompt(tp, NULL, 0); - tty3270_set_timer(tp, 1); - } else if (tp->input->string[0] == 0x6d) { - /* Display has been cleared. Redraw. */ - tp->update_flags = TTY_UPDATE_ALL; - tty3270_set_timer(tp, 1); - } - spin_unlock_bh(&tp->view.lock); - - /* Start keyboard reset command. */ - raw3270_request_reset(tp->kreset); - raw3270_request_set_cmd(tp->kreset, TC_WRITE); - raw3270_request_add_data(tp->kreset, &kreset_data, 1); - raw3270_start(&tp->view, tp->kreset); - - while (len-- > 0) - kbd_keycode(tp->kbd, *input++); - /* Emit keycode for AID byte. */ - kbd_keycode(tp->kbd, 256 + tp->input->string[0]); - - raw3270_request_reset(rrq); - xchg(&tp->read, rrq); - raw3270_put_view(&tp->view); -} - -/* - * Read request completion callback. - */ -static void -tty3270_read_callback(struct raw3270_request *rq, void *data) -{ - struct tty3270 *tp = container_of(rq->view, struct tty3270, view); - raw3270_get_view(rq->view); - /* Schedule tasklet to pass input to tty. */ - tasklet_schedule(&tp->readlet); -} - -/* - * Issue a read request. Call with device lock. - */ -static void -tty3270_issue_read(struct tty3270 *tp, int lock) -{ - struct raw3270_request *rrq; - int rc; - - rrq = xchg(&tp->read, 0); - if (!rrq) - /* Read already scheduled. */ - return; - rrq->callback = tty3270_read_callback; - rrq->callback_data = tp; - raw3270_request_set_cmd(rrq, TC_READMOD); - raw3270_request_set_data(rrq, tp->input->string, tp->input->len); - /* Issue the read modified request. */ - if (lock) { - rc = raw3270_start(&tp->view, rrq); - } else - rc = raw3270_start_irq(&tp->view, rrq); - if (rc) { - raw3270_request_reset(rrq); - xchg(&tp->read, rrq); - } -} - -/* - * Hang up the tty - */ -static void -tty3270_hangup_tasklet(unsigned long data) -{ - struct tty3270 *tp = (struct tty3270 *)data; - tty_port_tty_hangup(&tp->port, true); - raw3270_put_view(&tp->view); -} - -/* - * Switch to the tty view. - */ -static int -tty3270_activate(struct raw3270_view *view) -{ - struct tty3270 *tp = container_of(view, struct tty3270, view); - - tp->update_flags = TTY_UPDATE_ALL; - tty3270_set_timer(tp, 1); - return 0; -} - -static void -tty3270_deactivate(struct raw3270_view *view) -{ - struct tty3270 *tp = container_of(view, struct tty3270, view); - - del_timer(&tp->timer); -} - -static void -tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) -{ - /* Handle ATTN. Schedule tasklet to read aid. */ - if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { - if (!tp->throttle) - tty3270_issue_read(tp, 0); - else - tp->attn = 1; - } - - if (rq) { - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { - rq->rc = -EIO; - raw3270_get_view(&tp->view); - tasklet_schedule(&tp->hanglet); - } else { - /* Normal end. Copy residual count. */ - rq->rescnt = irb->scsw.cmd.count; - } - } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { - /* Interrupt without an outstanding request -> update all */ - tp->update_flags = TTY_UPDATE_ALL; - tty3270_set_timer(tp, 1); - } -} - -/* - * Allocate tty3270 structure. - */ -static struct tty3270 * -tty3270_alloc_view(void) -{ - struct tty3270 *tp; - int pages; - - tp = kzalloc(sizeof(struct tty3270), GFP_KERNEL); - if (!tp) - goto out_err; - tp->freemem_pages = - kmalloc_array(TTY3270_STRING_PAGES, sizeof(void *), - GFP_KERNEL); - if (!tp->freemem_pages) - goto out_tp; - INIT_LIST_HEAD(&tp->freemem); - INIT_LIST_HEAD(&tp->lines); - INIT_LIST_HEAD(&tp->update); - INIT_LIST_HEAD(&tp->rcl_lines); - tp->rcl_max = 20; - - for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) { - tp->freemem_pages[pages] = (void *) - __get_free_pages(GFP_KERNEL|GFP_DMA, 0); - if (!tp->freemem_pages[pages]) - goto out_pages; - add_string_memory(&tp->freemem, - tp->freemem_pages[pages], PAGE_SIZE); - } - tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE); - if (IS_ERR(tp->write)) - goto out_pages; - tp->read = raw3270_request_alloc(0); - if (IS_ERR(tp->read)) - goto out_write; - tp->kreset = raw3270_request_alloc(1); - if (IS_ERR(tp->kreset)) - goto out_read; - tp->kbd = kbd_alloc(); - if (!tp->kbd) - goto out_reset; - - tty_port_init(&tp->port); - timer_setup(&tp->timer, tty3270_update, 0); - tasklet_init(&tp->readlet, tty3270_read_tasklet, - (unsigned long) tp->read); - tasklet_init(&tp->hanglet, tty3270_hangup_tasklet, - (unsigned long) tp); - INIT_WORK(&tp->resize_work, tty3270_resize_work); - - return tp; - -out_reset: - raw3270_request_free(tp->kreset); -out_read: - raw3270_request_free(tp->read); -out_write: - raw3270_request_free(tp->write); -out_pages: - while (pages--) - free_pages((unsigned long) tp->freemem_pages[pages], 0); - kfree(tp->freemem_pages); - tty_port_destroy(&tp->port); -out_tp: - kfree(tp); -out_err: - return ERR_PTR(-ENOMEM); -} - -/* - * Free tty3270 structure. - */ -static void -tty3270_free_view(struct tty3270 *tp) -{ - int pages; - - kbd_free(tp->kbd); - raw3270_request_free(tp->kreset); - raw3270_request_free(tp->read); - raw3270_request_free(tp->write); - for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) - free_pages((unsigned long) tp->freemem_pages[pages], 0); - kfree(tp->freemem_pages); - tty_port_destroy(&tp->port); - kfree(tp); -} - -/* - * Allocate tty3270 screen. - */ -static struct tty3270_line * -tty3270_alloc_screen(unsigned int rows, unsigned int cols) -{ - struct tty3270_line *screen; - unsigned long size; - int lines; - - size = sizeof(struct tty3270_line) * (rows - 2); - screen = kzalloc(size, GFP_KERNEL); - if (!screen) - goto out_err; - for (lines = 0; lines < rows - 2; lines++) { - size = sizeof(struct tty3270_cell) * cols; - screen[lines].cells = kzalloc(size, GFP_KERNEL); - if (!screen[lines].cells) - goto out_screen; - } - return screen; -out_screen: - while (lines--) - kfree(screen[lines].cells); - kfree(screen); -out_err: - return ERR_PTR(-ENOMEM); -} - -/* - * Free tty3270 screen. - */ -static void -tty3270_free_screen(struct tty3270_line *screen, unsigned int rows) -{ - int lines; - - for (lines = 0; lines < rows - 2; lines++) - kfree(screen[lines].cells); - kfree(screen); -} - -/* - * Resize tty3270 screen - */ -static void tty3270_resize_work(struct work_struct *work) -{ - struct tty3270 *tp = container_of(work, struct tty3270, resize_work); - struct tty3270_line *screen, *oscreen; - struct tty_struct *tty; - unsigned int orows; - struct winsize ws; - - screen = tty3270_alloc_screen(tp->n_rows, tp->n_cols); - if (IS_ERR(screen)) - return; - /* Switch to new output size */ - spin_lock_bh(&tp->view.lock); - tty3270_blank_screen(tp); - oscreen = tp->screen; - orows = tp->view.rows; - tp->view.model = tp->n_model; - tp->view.rows = tp->n_rows; - tp->view.cols = tp->n_cols; - tp->screen = screen; - free_string(&tp->freemem, tp->prompt); - free_string(&tp->freemem, tp->status); - tty3270_create_prompt(tp); - tty3270_create_status(tp); - while (tp->nr_lines < tp->view.rows - 2) - tty3270_blank_line(tp); - tp->update_flags = TTY_UPDATE_ALL; - spin_unlock_bh(&tp->view.lock); - tty3270_free_screen(oscreen, orows); - tty3270_set_timer(tp, 1); - /* Informat tty layer about new size */ - tty = tty_port_tty_get(&tp->port); - if (!tty) - return; - ws.ws_row = tp->view.rows - 2; - ws.ws_col = tp->view.cols; - tty_do_resize(tty, &ws); - tty_kref_put(tty); -} - -static void -tty3270_resize(struct raw3270_view *view, int model, int rows, int cols) -{ - struct tty3270 *tp = container_of(view, struct tty3270, view); - - if (tp->n_model == model && tp->n_rows == rows && tp->n_cols == cols) - return; - tp->n_model = model; - tp->n_rows = rows; - tp->n_cols = cols; - schedule_work(&tp->resize_work); -} - -/* - * Unlink tty3270 data structure from tty. - */ -static void -tty3270_release(struct raw3270_view *view) -{ - struct tty3270 *tp = container_of(view, struct tty3270, view); - struct tty_struct *tty = tty_port_tty_get(&tp->port); - - if (tty) { - tty->driver_data = NULL; - tty_port_tty_set(&tp->port, NULL); - tty_hangup(tty); - raw3270_put_view(&tp->view); - tty_kref_put(tty); - } -} - -/* - * Free tty3270 data structure - */ -static void -tty3270_free(struct raw3270_view *view) -{ - struct tty3270 *tp = container_of(view, struct tty3270, view); - - del_timer_sync(&tp->timer); - tty3270_free_screen(tp->screen, tp->view.rows); - tty3270_free_view(tp); -} - -/* - * Delayed freeing of tty3270 views. - */ -static void -tty3270_del_views(void) -{ - int i; - - for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) { - struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i); - if (!IS_ERR(view)) - raw3270_del_view(view); - } -} - -static struct raw3270_fn tty3270_fn = { - .activate = tty3270_activate, - .deactivate = tty3270_deactivate, - .intv = (void *) tty3270_irq, - .release = tty3270_release, - .free = tty3270_free, - .resize = tty3270_resize -}; - -/* - * This routine is called whenever a 3270 tty is opened first time. - */ -static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct raw3270_view *view; - struct tty3270 *tp; - int i, rc; - - /* Check if the tty3270 is already there. */ - view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR); - if (!IS_ERR(view)) { - tp = container_of(view, struct tty3270, view); - tty->driver_data = tp; - tty->winsize.ws_row = tp->view.rows - 2; - tty->winsize.ws_col = tp->view.cols; - tp->inattr = TF_INPUT; - goto port_install; - } - if (tty3270_max_index < tty->index + 1) - tty3270_max_index = tty->index + 1; - - /* Allocate tty3270 structure on first open. */ - tp = tty3270_alloc_view(); - if (IS_ERR(tp)) - return PTR_ERR(tp); - - rc = raw3270_add_view(&tp->view, &tty3270_fn, - tty->index + RAW3270_FIRSTMINOR, - RAW3270_VIEW_LOCK_BH); - if (rc) { - tty3270_free_view(tp); - return rc; - } - - tp->screen = tty3270_alloc_screen(tp->view.rows, tp->view.cols); - if (IS_ERR(tp->screen)) { - rc = PTR_ERR(tp->screen); - raw3270_put_view(&tp->view); - raw3270_del_view(&tp->view); - tty3270_free_view(tp); - return rc; - } - - tty->winsize.ws_row = tp->view.rows - 2; - tty->winsize.ws_col = tp->view.cols; - - tty3270_create_prompt(tp); - tty3270_create_status(tp); - tty3270_update_status(tp); - - /* Create blank line for every line in the tty output area. */ - for (i = 0; i < tp->view.rows - 2; i++) - tty3270_blank_line(tp); - - tp->kbd->port = &tp->port; - tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty; - tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward; - tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward; - tp->kbd->fn_handler[KVAL(K_CONS)] = tty3270_rcl_backward; - kbd_ascebc(tp->kbd, tp->view.ascebc); - - raw3270_activate_view(&tp->view); - -port_install: - rc = tty_port_install(&tp->port, driver, tty); - if (rc) { - raw3270_put_view(&tp->view); - return rc; - } - - tty->driver_data = tp; - - return 0; -} - -/* - * This routine is called whenever a 3270 tty is opened. - */ -static int -tty3270_open(struct tty_struct *tty, struct file *filp) -{ - struct tty3270 *tp = tty->driver_data; - struct tty_port *port = &tp->port; - - port->count++; - tty_port_tty_set(port, tty); - return 0; -} - -/* - * This routine is called when the 3270 tty is closed. We wait - * for the remaining request to be completed. Then we clean up. - */ -static void -tty3270_close(struct tty_struct *tty, struct file * filp) -{ - struct tty3270 *tp = tty->driver_data; - - if (tty->count > 1) - return; - if (tp) - tty_port_tty_set(&tp->port, NULL); -} - -static void tty3270_cleanup(struct tty_struct *tty) -{ - struct tty3270 *tp = tty->driver_data; - - if (tp) { - tty->driver_data = NULL; - raw3270_put_view(&tp->view); - } -} - -/* - * We always have room. - */ -static unsigned int -tty3270_write_room(struct tty_struct *tty) -{ - return INT_MAX; -} - -/* - * Insert character into the screen at the current position with the - * current color and highlight. This function does NOT do cursor movement. - */ -static void tty3270_put_character(struct tty3270 *tp, char ch) -{ - struct tty3270_line *line; - struct tty3270_cell *cell; - - line = tp->screen + tp->cy; - if (line->len <= tp->cx) { - while (line->len < tp->cx) { - cell = line->cells + line->len; - cell->character = tp->view.ascebc[' ']; - cell->highlight = tp->highlight; - cell->f_color = tp->f_color; - line->len++; - } - line->len++; - } - cell = line->cells + tp->cx; - cell->character = tp->view.ascebc[(unsigned int) ch]; - cell->highlight = tp->highlight; - cell->f_color = tp->f_color; -} - -/* - * Convert a tty3270_line to a 3270 data fragment usable for output. - */ -static void -tty3270_convert_line(struct tty3270 *tp, int line_nr) -{ - struct tty3270_line *line; - struct tty3270_cell *cell; - struct string *s, *n; - unsigned char highlight; - unsigned char f_color; - char *cp; - int flen, i; - - /* Determine how long the fragment will be. */ - flen = 3; /* Prefix (TO_SBA). */ - line = tp->screen + line_nr; - flen += line->len; - highlight = TAX_RESET; - f_color = TAC_RESET; - for (i = 0, cell = line->cells; i < line->len; i++, cell++) { - if (cell->highlight != highlight) { - flen += 3; /* TO_SA to switch highlight. */ - highlight = cell->highlight; - } - if (cell->f_color != f_color) { - flen += 3; /* TO_SA to switch color. */ - f_color = cell->f_color; - } - } - if (highlight != TAX_RESET) - flen += 3; /* TO_SA to reset hightlight. */ - if (f_color != TAC_RESET) - flen += 3; /* TO_SA to reset color. */ - if (line->len < tp->view.cols) - flen += 4; /* Postfix (TO_RA). */ - - /* Find the line in the list. */ - i = tp->view.rows - 2 - line_nr; - list_for_each_entry_reverse(s, &tp->lines, list) - if (--i <= 0) - break; - /* - * Check if the line needs to get reallocated. - */ - if (s->len != flen) { - /* Reallocate string. */ - n = tty3270_alloc_string(tp, flen); - list_add(&n->list, &s->list); - list_del_init(&s->list); - if (!list_empty(&s->update)) - list_del_init(&s->update); - free_string(&tp->freemem, s); - s = n; - } - - /* Write 3270 data fragment. */ - cp = s->string; - *cp++ = TO_SBA; - *cp++ = 0; - *cp++ = 0; - - highlight = TAX_RESET; - f_color = TAC_RESET; - for (i = 0, cell = line->cells; i < line->len; i++, cell++) { - if (cell->highlight != highlight) { - *cp++ = TO_SA; - *cp++ = TAT_EXTHI; - *cp++ = cell->highlight; - highlight = cell->highlight; - } - if (cell->f_color != f_color) { - *cp++ = TO_SA; - *cp++ = TAT_COLOR; - *cp++ = cell->f_color; - f_color = cell->f_color; - } - *cp++ = cell->character; - } - if (highlight != TAX_RESET) { - *cp++ = TO_SA; - *cp++ = TAT_EXTHI; - *cp++ = TAX_RESET; - } - if (f_color != TAC_RESET) { - *cp++ = TO_SA; - *cp++ = TAT_COLOR; - *cp++ = TAC_RESET; - } - if (line->len < tp->view.cols) { - *cp++ = TO_RA; - *cp++ = 0; - *cp++ = 0; - *cp++ = 0; - } - - if (tp->nr_up + line_nr < tp->view.rows - 2) { - /* Line is currently visible on screen. */ - tty3270_update_string(tp, s, line_nr); - /* Add line to update list. */ - if (list_empty(&s->update)) { - list_add_tail(&s->update, &tp->update); - tp->update_flags |= TTY_UPDATE_LIST; - } - } -} - -/* - * Do carriage return. - */ -static void -tty3270_cr(struct tty3270 *tp) -{ - tp->cx = 0; -} - -/* - * Do line feed. - */ -static void -tty3270_lf(struct tty3270 *tp) -{ - struct tty3270_line temp; - int i; - - tty3270_convert_line(tp, tp->cy); - if (tp->cy < tp->view.rows - 3) { - tp->cy++; - return; - } - /* Last line just filled up. Add new, blank line. */ - tty3270_blank_line(tp); - temp = tp->screen[0]; - temp.len = 0; - for (i = 0; i < tp->view.rows - 3; i++) - tp->screen[i] = tp->screen[i+1]; - tp->screen[tp->view.rows - 3] = temp; - tty3270_rebuild_update(tp); -} - -static void -tty3270_ri(struct tty3270 *tp) -{ - if (tp->cy > 0) { - tty3270_convert_line(tp, tp->cy); - tp->cy--; - } -} - -/* - * Insert characters at current position. - */ -static void -tty3270_insert_characters(struct tty3270 *tp, int n) -{ - struct tty3270_line *line; - int k; - - line = tp->screen + tp->cy; - while (line->len < tp->cx) { - line->cells[line->len].character = tp->view.ascebc[' ']; - line->cells[line->len].highlight = TAX_RESET; - line->cells[line->len].f_color = TAC_RESET; - line->len++; - } - if (n > tp->view.cols - tp->cx) - n = tp->view.cols - tp->cx; - k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n); - while (k--) - line->cells[tp->cx + n + k] = line->cells[tp->cx + k]; - line->len += n; - if (line->len > tp->view.cols) - line->len = tp->view.cols; - while (n-- > 0) { - line->cells[tp->cx + n].character = tp->view.ascebc[' ']; - line->cells[tp->cx + n].highlight = tp->highlight; - line->cells[tp->cx + n].f_color = tp->f_color; - } -} - -/* - * Delete characters at current position. - */ -static void -tty3270_delete_characters(struct tty3270 *tp, int n) -{ - struct tty3270_line *line; - int i; - - line = tp->screen + tp->cy; - if (line->len <= tp->cx) - return; - if (line->len - tp->cx <= n) { - line->len = tp->cx; - return; - } - for (i = tp->cx; i + n < line->len; i++) - line->cells[i] = line->cells[i + n]; - line->len -= n; -} - -/* - * Erase characters at current position. - */ -static void -tty3270_erase_characters(struct tty3270 *tp, int n) -{ - struct tty3270_line *line; - struct tty3270_cell *cell; - - line = tp->screen + tp->cy; - while (line->len > tp->cx && n-- > 0) { - cell = line->cells + tp->cx++; - cell->character = ' '; - cell->highlight = TAX_RESET; - cell->f_color = TAC_RESET; - } - tp->cx += n; - tp->cx = min_t(int, tp->cx, tp->view.cols - 1); -} - -/* - * Erase line, 3 different cases: - * Esc [ 0 K Erase from current position to end of line inclusive - * Esc [ 1 K Erase from beginning of line to current position inclusive - * Esc [ 2 K Erase entire line (without moving cursor) - */ -static void -tty3270_erase_line(struct tty3270 *tp, int mode) -{ - struct tty3270_line *line; - struct tty3270_cell *cell; - int i; - - line = tp->screen + tp->cy; - if (mode == 0) - line->len = tp->cx; - else if (mode == 1) { - for (i = 0; i < tp->cx; i++) { - cell = line->cells + i; - cell->character = ' '; - cell->highlight = TAX_RESET; - cell->f_color = TAC_RESET; - } - if (line->len <= tp->cx) - line->len = tp->cx + 1; - } else if (mode == 2) - line->len = 0; - tty3270_convert_line(tp, tp->cy); -} - -/* - * Erase display, 3 different cases: - * Esc [ 0 J Erase from current position to bottom of screen inclusive - * Esc [ 1 J Erase from top of screen to current position inclusive - * Esc [ 2 J Erase entire screen (without moving the cursor) - */ -static void -tty3270_erase_display(struct tty3270 *tp, int mode) -{ - int i; - - if (mode == 0) { - tty3270_erase_line(tp, 0); - for (i = tp->cy + 1; i < tp->view.rows - 2; i++) { - tp->screen[i].len = 0; - tty3270_convert_line(tp, i); - } - } else if (mode == 1) { - for (i = 0; i < tp->cy; i++) { - tp->screen[i].len = 0; - tty3270_convert_line(tp, i); - } - tty3270_erase_line(tp, 1); - } else if (mode == 2) { - for (i = 0; i < tp->view.rows - 2; i++) { - tp->screen[i].len = 0; - tty3270_convert_line(tp, i); - } - } - tty3270_rebuild_update(tp); -} - -/* - * Set attributes found in an escape sequence. - * Esc [ <attr> ; <attr> ; ... m - */ -static void -tty3270_set_attributes(struct tty3270 *tp) -{ - static unsigned char f_colors[] = { - TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE, - TAC_PINK, TAC_TURQ, TAC_WHITE, 0, TAC_DEFAULT - }; - int i, attr; - - for (i = 0; i <= tp->esc_npar; i++) { - attr = tp->esc_par[i]; - switch (attr) { - case 0: /* Reset */ - tp->highlight = TAX_RESET; - tp->f_color = TAC_RESET; - break; - /* Highlight. */ - case 4: /* Start underlining. */ - tp->highlight = TAX_UNDER; - break; - case 5: /* Start blink. */ - tp->highlight = TAX_BLINK; - break; - case 7: /* Start reverse. */ - tp->highlight = TAX_REVER; - break; - case 24: /* End underlining */ - if (tp->highlight == TAX_UNDER) - tp->highlight = TAX_RESET; - break; - case 25: /* End blink. */ - if (tp->highlight == TAX_BLINK) - tp->highlight = TAX_RESET; - break; - case 27: /* End reverse. */ - if (tp->highlight == TAX_REVER) - tp->highlight = TAX_RESET; - break; - /* Foreground color. */ - case 30: /* Black */ - case 31: /* Red */ - case 32: /* Green */ - case 33: /* Yellow */ - case 34: /* Blue */ - case 35: /* Magenta */ - case 36: /* Cyan */ - case 37: /* White */ - case 39: /* Black */ - tp->f_color = f_colors[attr - 30]; - break; - } - } -} - -static inline int -tty3270_getpar(struct tty3270 *tp, int ix) -{ - return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1; -} - -static void -tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) -{ - int max_cx = max(0, cx); - int max_cy = max(0, cy); - - tp->cx = min_t(int, tp->view.cols - 1, max_cx); - cy = min_t(int, tp->view.rows - 3, max_cy); - if (cy != tp->cy) { - tty3270_convert_line(tp, tp->cy); - tp->cy = cy; - } -} - -/* - * Process escape sequences. Known sequences: - * Esc 7 Save Cursor Position - * Esc 8 Restore Cursor Position - * Esc [ Pn ; Pn ; .. m Set attributes - * Esc [ Pn ; Pn H Cursor Position - * Esc [ Pn ; Pn f Cursor Position - * Esc [ Pn A Cursor Up - * Esc [ Pn B Cursor Down - * Esc [ Pn C Cursor Forward - * Esc [ Pn D Cursor Backward - * Esc [ Pn G Cursor Horizontal Absolute - * Esc [ Pn X Erase Characters - * Esc [ Ps J Erase in Display - * Esc [ Ps K Erase in Line - * // FIXME: add all the new ones. - * - * Pn is a numeric parameter, a string of zero or more decimal digits. - * Ps is a selective parameter. - */ -static void -tty3270_escape_sequence(struct tty3270 *tp, char ch) -{ - enum { ESnormal, ESesc, ESsquare, ESgetpars }; - - if (tp->esc_state == ESnormal) { - if (ch == 0x1b) - /* Starting new escape sequence. */ - tp->esc_state = ESesc; - return; - } - if (tp->esc_state == ESesc) { - tp->esc_state = ESnormal; - switch (ch) { - case '[': - tp->esc_state = ESsquare; - break; - case 'E': - tty3270_cr(tp); - tty3270_lf(tp); - break; - case 'M': - tty3270_ri(tp); - break; - case 'D': - tty3270_lf(tp); - break; - case 'Z': /* Respond ID. */ - kbd_puts_queue(&tp->port, "\033[?6c"); - break; - case '7': /* Save cursor position. */ - tp->saved_cx = tp->cx; - tp->saved_cy = tp->cy; - tp->saved_highlight = tp->highlight; - tp->saved_f_color = tp->f_color; - break; - case '8': /* Restore cursor position. */ - tty3270_convert_line(tp, tp->cy); - tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); - tp->highlight = tp->saved_highlight; - tp->f_color = tp->saved_f_color; - break; - case 'c': /* Reset terminal. */ - tp->cx = tp->saved_cx = 0; - tp->cy = tp->saved_cy = 0; - tp->highlight = tp->saved_highlight = TAX_RESET; - tp->f_color = tp->saved_f_color = TAC_RESET; - tty3270_erase_display(tp, 2); - break; - } - return; - } - if (tp->esc_state == ESsquare) { - tp->esc_state = ESgetpars; - memset(tp->esc_par, 0, sizeof(tp->esc_par)); - tp->esc_npar = 0; - tp->esc_ques = (ch == '?'); - if (tp->esc_ques) - return; - } - if (tp->esc_state == ESgetpars) { - if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) { - tp->esc_npar++; - return; - } - if (ch >= '0' && ch <= '9') { - tp->esc_par[tp->esc_npar] *= 10; - tp->esc_par[tp->esc_npar] += ch - '0'; - return; - } - } - tp->esc_state = ESnormal; - if (ch == 'n' && !tp->esc_ques) { - if (tp->esc_par[0] == 5) /* Status report. */ - kbd_puts_queue(&tp->port, "\033[0n"); - else if (tp->esc_par[0] == 6) { /* Cursor report. */ - char buf[40]; - sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1); - kbd_puts_queue(&tp->port, buf); - } - return; - } - if (tp->esc_ques) - return; - switch (ch) { - case 'm': - tty3270_set_attributes(tp); - break; - case 'H': /* Set cursor position. */ - case 'f': - tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1, - tty3270_getpar(tp, 0) - 1); - break; - case 'd': /* Set y position. */ - tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1); - break; - case 'A': /* Cursor up. */ - case 'F': - tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0)); - break; - case 'B': /* Cursor down. */ - case 'e': - case 'E': - tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0)); - break; - case 'C': /* Cursor forward. */ - case 'a': - tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy); - break; - case 'D': /* Cursor backward. */ - tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy); - break; - case 'G': /* Set x position. */ - case '`': - tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy); - break; - case 'X': /* Erase Characters. */ - tty3270_erase_characters(tp, tty3270_getpar(tp, 0)); - break; - case 'J': /* Erase display. */ - tty3270_erase_display(tp, tp->esc_par[0]); - break; - case 'K': /* Erase line. */ - tty3270_erase_line(tp, tp->esc_par[0]); - break; - case 'P': /* Delete characters. */ - tty3270_delete_characters(tp, tty3270_getpar(tp, 0)); - break; - case '@': /* Insert characters. */ - tty3270_insert_characters(tp, tty3270_getpar(tp, 0)); - break; - case 's': /* Save cursor position. */ - tp->saved_cx = tp->cx; - tp->saved_cy = tp->cy; - tp->saved_highlight = tp->highlight; - tp->saved_f_color = tp->f_color; - break; - case 'u': /* Restore cursor position. */ - tty3270_convert_line(tp, tp->cy); - tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); - tp->highlight = tp->saved_highlight; - tp->f_color = tp->saved_f_color; - break; - } -} - -/* - * String write routine for 3270 ttys - */ -static void -tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, - const unsigned char *buf, int count) -{ - int i_msg, i; - - spin_lock_bh(&tp->view.lock); - for (i_msg = 0; !tty->flow.stopped && i_msg < count; i_msg++) { - if (tp->esc_state != 0) { - /* Continue escape sequence. */ - tty3270_escape_sequence(tp, buf[i_msg]); - continue; - } - - switch (buf[i_msg]) { - case 0x07: /* '\a' -- Alarm */ - tp->wcc |= TW_PLUSALARM; - break; - case 0x08: /* Backspace. */ - if (tp->cx > 0) { - tp->cx--; - tty3270_put_character(tp, ' '); - } - break; - case 0x09: /* '\t' -- Tabulate */ - for (i = tp->cx % 8; i < 8; i++) { - if (tp->cx >= tp->view.cols) { - tty3270_cr(tp); - tty3270_lf(tp); - break; - } - tty3270_put_character(tp, ' '); - tp->cx++; - } - break; - case 0x0a: /* '\n' -- New Line */ - tty3270_cr(tp); - tty3270_lf(tp); - break; - case 0x0c: /* '\f' -- Form Feed */ - tty3270_erase_display(tp, 2); - tp->cx = tp->cy = 0; - break; - case 0x0d: /* '\r' -- Carriage Return */ - tp->cx = 0; - break; - case 0x0f: /* SuSE "exit alternate mode" */ - break; - case 0x1b: /* Start escape sequence. */ - tty3270_escape_sequence(tp, buf[i_msg]); - break; - default: /* Insert normal character. */ - if (tp->cx >= tp->view.cols) { - tty3270_cr(tp); - tty3270_lf(tp); - } - tty3270_put_character(tp, buf[i_msg]); - tp->cx++; - break; - } - } - /* Convert current line to 3270 data fragment. */ - tty3270_convert_line(tp, tp->cy); - - /* Setup timer to update display after 1/10 second */ - if (!timer_pending(&tp->timer)) - tty3270_set_timer(tp, HZ/10); - - spin_unlock_bh(&tp->view.lock); -} - -/* - * String write routine for 3270 ttys - */ -static int -tty3270_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - struct tty3270 *tp; - - tp = tty->driver_data; - if (!tp) - return 0; - if (tp->char_count > 0) { - tty3270_do_write(tp, tty, tp->char_buf, tp->char_count); - tp->char_count = 0; - } - tty3270_do_write(tp, tty, buf, count); - return count; -} - -/* - * Put single characters to the ttys character buffer - */ -static int tty3270_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct tty3270 *tp; - - tp = tty->driver_data; - if (!tp || tp->char_count >= TTY3270_CHAR_BUF_SIZE) - return 0; - tp->char_buf[tp->char_count++] = ch; - return 1; -} - -/* - * Flush all characters from the ttys characeter buffer put there - * by tty3270_put_char. - */ -static void -tty3270_flush_chars(struct tty_struct *tty) -{ - struct tty3270 *tp; - - tp = tty->driver_data; - if (!tp) - return; - if (tp->char_count > 0) { - tty3270_do_write(tp, tty, tp->char_buf, tp->char_count); - tp->char_count = 0; - } -} - -/* - * Check for visible/invisible input switches - */ -static void -tty3270_set_termios(struct tty_struct *tty, const struct ktermios *old) -{ - struct tty3270 *tp; - int new; - - tp = tty->driver_data; - if (!tp) - return; - spin_lock_bh(&tp->view.lock); - if (L_ICANON(tty)) { - new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN; - if (new != tp->inattr) { - tp->inattr = new; - tty3270_update_prompt(tp, NULL, 0); - tty3270_set_timer(tp, 1); - } - } - spin_unlock_bh(&tp->view.lock); -} - -/* - * Disable reading from a 3270 tty - */ -static void -tty3270_throttle(struct tty_struct * tty) -{ - struct tty3270 *tp; - - tp = tty->driver_data; - if (!tp) - return; - tp->throttle = 1; -} - -/* - * Enable reading from a 3270 tty - */ -static void -tty3270_unthrottle(struct tty_struct * tty) -{ - struct tty3270 *tp; - - tp = tty->driver_data; - if (!tp) - return; - tp->throttle = 0; - if (tp->attn) - tty3270_issue_read(tp, 1); -} - -/* - * Hang up the tty device. - */ -static void -tty3270_hangup(struct tty_struct *tty) -{ - struct tty3270 *tp; - - tp = tty->driver_data; - if (!tp) - return; - spin_lock_bh(&tp->view.lock); - tp->cx = tp->saved_cx = 0; - tp->cy = tp->saved_cy = 0; - tp->highlight = tp->saved_highlight = TAX_RESET; - tp->f_color = tp->saved_f_color = TAC_RESET; - tty3270_blank_screen(tp); - while (tp->nr_lines < tp->view.rows - 2) - tty3270_blank_line(tp); - tp->update_flags = TTY_UPDATE_ALL; - spin_unlock_bh(&tp->view.lock); - tty3270_set_timer(tp, 1); -} - -static void -tty3270_wait_until_sent(struct tty_struct *tty, int timeout) -{ -} - -static int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct tty3270 *tp; - - tp = tty->driver_data; - if (!tp) - return -ENODEV; - if (tty_io_error(tty)) - return -EIO; - return kbd_ioctl(tp->kbd, cmd, arg); -} - -#ifdef CONFIG_COMPAT -static long tty3270_compat_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct tty3270 *tp; - - tp = tty->driver_data; - if (!tp) - return -ENODEV; - if (tty_io_error(tty)) - return -EIO; - return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static const struct tty_operations tty3270_ops = { - .install = tty3270_install, - .cleanup = tty3270_cleanup, - .open = tty3270_open, - .close = tty3270_close, - .write = tty3270_write, - .put_char = tty3270_put_char, - .flush_chars = tty3270_flush_chars, - .write_room = tty3270_write_room, - .throttle = tty3270_throttle, - .unthrottle = tty3270_unthrottle, - .hangup = tty3270_hangup, - .wait_until_sent = tty3270_wait_until_sent, - .ioctl = tty3270_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = tty3270_compat_ioctl, -#endif - .set_termios = tty3270_set_termios -}; - -static void tty3270_create_cb(int minor) -{ - tty_register_device(tty3270_driver, minor - RAW3270_FIRSTMINOR, NULL); -} - -static void tty3270_destroy_cb(int minor) -{ - tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR); -} - -static struct raw3270_notifier tty3270_notifier = -{ - .create = tty3270_create_cb, - .destroy = tty3270_destroy_cb, -}; - -/* - * 3270 tty registration code called from tty_init(). - * Most kernel services (incl. kmalloc) are available at this poimt. - */ -static int __init tty3270_init(void) -{ - struct tty_driver *driver; - int ret; - - driver = tty_alloc_driver(RAW3270_MAXDEVS, - TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | - TTY_DRIVER_RESET_TERMIOS); - if (IS_ERR(driver)) - return PTR_ERR(driver); - - /* - * Initialize the tty_driver structure - * Entries in tty3270_driver that are NOT initialized: - * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc - */ - driver->driver_name = "tty3270"; - driver->name = "3270/tty"; - driver->major = IBM_TTY3270_MAJOR; - driver->minor_start = RAW3270_FIRSTMINOR; - driver->name_base = RAW3270_FIRSTMINOR; - driver->type = TTY_DRIVER_TYPE_SYSTEM; - driver->subtype = SYSTEM_TYPE_TTY; - driver->init_termios = tty_std_termios; - tty_set_operations(driver, &tty3270_ops); - ret = tty_register_driver(driver); - if (ret) { - tty_driver_kref_put(driver); - return ret; - } - tty3270_driver = driver; - raw3270_register_notifier(&tty3270_notifier); - return 0; -} - -static void __exit -tty3270_exit(void) -{ - struct tty_driver *driver; - - raw3270_unregister_notifier(&tty3270_notifier); - driver = tty3270_driver; - tty3270_driver = NULL; - tty_unregister_driver(driver); - tty_driver_kref_put(driver); - tty3270_del_views(); -} - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR); - -module_init(tty3270_init); -module_exit(tty3270_exit); diff --git a/drivers/s390/char/tty3270.h b/drivers/s390/char/tty3270.h deleted file mode 100644 index 52ceed6f8408..000000000000 --- a/drivers/s390/char/tty3270.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright IBM Corp. 2007 - * - */ - -#ifndef __DRIVERS_S390_CHAR_TTY3270_H -#define __DRIVERS_S390_CHAR_TTY3270_H - -#include <linux/tty.h> -#include <linux/tty_driver.h> - -extern struct tty_driver *tty3270_driver; - -#endif /* __DRIVERS_S390_CHAR_TTY3270_H */ |