18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *    IBM/3270 Driver - tty functions.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Author(s):
68c2ecf20Sopenharmony_ci *    Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
78c2ecf20Sopenharmony_ci *    Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
88c2ecf20Sopenharmony_ci *	-- Copyright IBM Corp. 2003
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <linux/kdev_t.h>
148c2ecf20Sopenharmony_ci#include <linux/tty.h>
158c2ecf20Sopenharmony_ci#include <linux/vt_kern.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/console.h>
188c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
198c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/memblock.h>
238c2ecf20Sopenharmony_ci#include <linux/compat.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <asm/ccwdev.h>
268c2ecf20Sopenharmony_ci#include <asm/cio.h>
278c2ecf20Sopenharmony_ci#include <asm/ebcdic.h>
288c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "raw3270.h"
318c2ecf20Sopenharmony_ci#include "tty3270.h"
328c2ecf20Sopenharmony_ci#include "keyboard.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define TTY3270_CHAR_BUF_SIZE 256
358c2ecf20Sopenharmony_ci#define TTY3270_OUTPUT_BUFFER_SIZE 1024
368c2ecf20Sopenharmony_ci#define TTY3270_STRING_PAGES 5
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct tty_driver *tty3270_driver;
398c2ecf20Sopenharmony_cistatic int tty3270_max_index;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic struct raw3270_fn tty3270_fn;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct tty3270_cell {
448c2ecf20Sopenharmony_ci	unsigned char character;
458c2ecf20Sopenharmony_ci	unsigned char highlight;
468c2ecf20Sopenharmony_ci	unsigned char f_color;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistruct tty3270_line {
508c2ecf20Sopenharmony_ci	struct tty3270_cell *cells;
518c2ecf20Sopenharmony_ci	int len;
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define ESCAPE_NPAR 8
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * The main tty view data structure.
588c2ecf20Sopenharmony_ci * FIXME:
598c2ecf20Sopenharmony_ci * 1) describe line orientation & lines list concept against screen
608c2ecf20Sopenharmony_ci * 2) describe conversion of screen to lines
618c2ecf20Sopenharmony_ci * 3) describe line format.
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_cistruct tty3270 {
648c2ecf20Sopenharmony_ci	struct raw3270_view view;
658c2ecf20Sopenharmony_ci	struct tty_port port;
668c2ecf20Sopenharmony_ci	void **freemem_pages;		/* Array of pages used for freemem. */
678c2ecf20Sopenharmony_ci	struct list_head freemem;	/* List of free memory for strings. */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* Output stuff. */
708c2ecf20Sopenharmony_ci	struct list_head lines;		/* List of lines. */
718c2ecf20Sopenharmony_ci	struct list_head update;	/* List of lines to update. */
728c2ecf20Sopenharmony_ci	unsigned char wcc;		/* Write control character. */
738c2ecf20Sopenharmony_ci	int nr_lines;			/* # lines in list. */
748c2ecf20Sopenharmony_ci	int nr_up;			/* # lines up in history. */
758c2ecf20Sopenharmony_ci	unsigned long update_flags;	/* Update indication bits. */
768c2ecf20Sopenharmony_ci	struct string *status;		/* Lower right of display. */
778c2ecf20Sopenharmony_ci	struct raw3270_request *write;	/* Single write request. */
788c2ecf20Sopenharmony_ci	struct timer_list timer;	/* Output delay timer. */
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* Current tty screen. */
818c2ecf20Sopenharmony_ci	unsigned int cx, cy;		/* Current output position. */
828c2ecf20Sopenharmony_ci	unsigned int highlight;		/* Blink/reverse/underscore */
838c2ecf20Sopenharmony_ci	unsigned int f_color;		/* Foreground color */
848c2ecf20Sopenharmony_ci	struct tty3270_line *screen;
858c2ecf20Sopenharmony_ci	unsigned int n_model, n_cols, n_rows;	/* New model & size */
868c2ecf20Sopenharmony_ci	struct work_struct resize_work;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* Input stuff. */
898c2ecf20Sopenharmony_ci	struct string *prompt;		/* Output string for input area. */
908c2ecf20Sopenharmony_ci	struct string *input;		/* Input string for read request. */
918c2ecf20Sopenharmony_ci	struct raw3270_request *read;	/* Single read request. */
928c2ecf20Sopenharmony_ci	struct raw3270_request *kreset;	/* Single keyboard reset request. */
938c2ecf20Sopenharmony_ci	unsigned char inattr;		/* Visible/invisible input. */
948c2ecf20Sopenharmony_ci	int throttle, attn;		/* tty throttle/unthrottle. */
958c2ecf20Sopenharmony_ci	struct tasklet_struct readlet;	/* Tasklet to issue read request. */
968c2ecf20Sopenharmony_ci	struct tasklet_struct hanglet;	/* Tasklet to hang up the tty. */
978c2ecf20Sopenharmony_ci	struct kbd_data *kbd;		/* key_maps stuff. */
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* Escape sequence parsing. */
1008c2ecf20Sopenharmony_ci	int esc_state, esc_ques, esc_npar;
1018c2ecf20Sopenharmony_ci	int esc_par[ESCAPE_NPAR];
1028c2ecf20Sopenharmony_ci	unsigned int saved_cx, saved_cy;
1038c2ecf20Sopenharmony_ci	unsigned int saved_highlight, saved_f_color;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* Command recalling. */
1068c2ecf20Sopenharmony_ci	struct list_head rcl_lines;	/* List of recallable lines. */
1078c2ecf20Sopenharmony_ci	struct list_head *rcl_walk;	/* Point in rcl_lines list. */
1088c2ecf20Sopenharmony_ci	int rcl_nr, rcl_max;		/* Number/max number of rcl_lines. */
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* Character array for put_char/flush_chars. */
1118c2ecf20Sopenharmony_ci	unsigned int char_count;
1128c2ecf20Sopenharmony_ci	char char_buf[TTY3270_CHAR_BUF_SIZE];
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* tty3270->update_flags. See tty3270_update for details. */
1168c2ecf20Sopenharmony_ci#define TTY_UPDATE_ERASE	1	/* Use EWRITEA instead of WRITE. */
1178c2ecf20Sopenharmony_ci#define TTY_UPDATE_LIST		2	/* Update lines in tty3270->update. */
1188c2ecf20Sopenharmony_ci#define TTY_UPDATE_INPUT	4	/* Update input line. */
1198c2ecf20Sopenharmony_ci#define TTY_UPDATE_STATUS	8	/* Update status line. */
1208c2ecf20Sopenharmony_ci#define TTY_UPDATE_ALL		16	/* Recreate screen. */
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic void tty3270_update(struct timer_list *);
1238c2ecf20Sopenharmony_cistatic void tty3270_resize_work(struct work_struct *work);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/*
1268c2ecf20Sopenharmony_ci * Setup timeout for a device. On timeout trigger an update.
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_cistatic void tty3270_set_timer(struct tty3270 *tp, int expires)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	mod_timer(&tp->timer, jiffies + expires);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/*
1348c2ecf20Sopenharmony_ci * The input line are the two last lines of the screen.
1358c2ecf20Sopenharmony_ci */
1368c2ecf20Sopenharmony_cistatic void
1378c2ecf20Sopenharmony_citty3270_update_prompt(struct tty3270 *tp, char *input, int count)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct string *line;
1408c2ecf20Sopenharmony_ci	unsigned int off;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	line = tp->prompt;
1438c2ecf20Sopenharmony_ci	if (count != 0)
1448c2ecf20Sopenharmony_ci		line->string[5] = TF_INMDT;
1458c2ecf20Sopenharmony_ci	else
1468c2ecf20Sopenharmony_ci		line->string[5] = tp->inattr;
1478c2ecf20Sopenharmony_ci	if (count > tp->view.cols * 2 - 11)
1488c2ecf20Sopenharmony_ci		count = tp->view.cols * 2 - 11;
1498c2ecf20Sopenharmony_ci	memcpy(line->string + 6, input, count);
1508c2ecf20Sopenharmony_ci	line->string[6 + count] = TO_IC;
1518c2ecf20Sopenharmony_ci	/* Clear to end of input line. */
1528c2ecf20Sopenharmony_ci	if (count < tp->view.cols * 2 - 11) {
1538c2ecf20Sopenharmony_ci		line->string[7 + count] = TO_RA;
1548c2ecf20Sopenharmony_ci		line->string[10 + count] = 0;
1558c2ecf20Sopenharmony_ci		off = tp->view.cols * tp->view.rows - 9;
1568c2ecf20Sopenharmony_ci		raw3270_buffer_address(tp->view.dev, line->string+count+8, off);
1578c2ecf20Sopenharmony_ci		line->len = 11 + count;
1588c2ecf20Sopenharmony_ci	} else
1598c2ecf20Sopenharmony_ci		line->len = 7 + count;
1608c2ecf20Sopenharmony_ci	tp->update_flags |= TTY_UPDATE_INPUT;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic void
1648c2ecf20Sopenharmony_citty3270_create_prompt(struct tty3270 *tp)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	static const unsigned char blueprint[] =
1678c2ecf20Sopenharmony_ci		{ TO_SBA, 0, 0, 0x6e, TO_SF, TF_INPUT,
1688c2ecf20Sopenharmony_ci		  /* empty input string */
1698c2ecf20Sopenharmony_ci		  TO_IC, TO_RA, 0, 0, 0 };
1708c2ecf20Sopenharmony_ci	struct string *line;
1718c2ecf20Sopenharmony_ci	unsigned int offset;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	line = alloc_string(&tp->freemem,
1748c2ecf20Sopenharmony_ci			    sizeof(blueprint) + tp->view.cols * 2 - 9);
1758c2ecf20Sopenharmony_ci	tp->prompt = line;
1768c2ecf20Sopenharmony_ci	tp->inattr = TF_INPUT;
1778c2ecf20Sopenharmony_ci	/* Copy blueprint to status line */
1788c2ecf20Sopenharmony_ci	memcpy(line->string, blueprint, sizeof(blueprint));
1798c2ecf20Sopenharmony_ci	line->len = sizeof(blueprint);
1808c2ecf20Sopenharmony_ci	/* Set output offsets. */
1818c2ecf20Sopenharmony_ci	offset = tp->view.cols * (tp->view.rows - 2);
1828c2ecf20Sopenharmony_ci	raw3270_buffer_address(tp->view.dev, line->string + 1, offset);
1838c2ecf20Sopenharmony_ci	offset = tp->view.cols * tp->view.rows - 9;
1848c2ecf20Sopenharmony_ci	raw3270_buffer_address(tp->view.dev, line->string + 8, offset);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* Allocate input string for reading. */
1878c2ecf20Sopenharmony_ci	tp->input = alloc_string(&tp->freemem, tp->view.cols * 2 - 9 + 6);
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/*
1918c2ecf20Sopenharmony_ci * The status line is the last line of the screen. It shows the string
1928c2ecf20Sopenharmony_ci * "Running"/"Holding" in the lower right corner of the screen.
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_cistatic void
1958c2ecf20Sopenharmony_citty3270_update_status(struct tty3270 * tp)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	char *str;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	str = (tp->nr_up != 0) ? "History" : "Running";
2008c2ecf20Sopenharmony_ci	memcpy(tp->status->string + 8, str, 7);
2018c2ecf20Sopenharmony_ci	codepage_convert(tp->view.ascebc, tp->status->string + 8, 7);
2028c2ecf20Sopenharmony_ci	tp->update_flags |= TTY_UPDATE_STATUS;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void
2068c2ecf20Sopenharmony_citty3270_create_status(struct tty3270 * tp)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	static const unsigned char blueprint[] =
2098c2ecf20Sopenharmony_ci		{ TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, TAC_GREEN,
2108c2ecf20Sopenharmony_ci		  0, 0, 0, 0, 0, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR,
2118c2ecf20Sopenharmony_ci		  TAC_RESET };
2128c2ecf20Sopenharmony_ci	struct string *line;
2138c2ecf20Sopenharmony_ci	unsigned int offset;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	line = alloc_string(&tp->freemem,sizeof(blueprint));
2168c2ecf20Sopenharmony_ci	tp->status = line;
2178c2ecf20Sopenharmony_ci	/* Copy blueprint to status line */
2188c2ecf20Sopenharmony_ci	memcpy(line->string, blueprint, sizeof(blueprint));
2198c2ecf20Sopenharmony_ci	/* Set address to start of status string (= last 9 characters). */
2208c2ecf20Sopenharmony_ci	offset = tp->view.cols * tp->view.rows - 9;
2218c2ecf20Sopenharmony_ci	raw3270_buffer_address(tp->view.dev, line->string + 1, offset);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/*
2258c2ecf20Sopenharmony_ci * Set output offsets to 3270 datastream fragment of a tty string.
2268c2ecf20Sopenharmony_ci * (TO_SBA offset at the start and TO_RA offset at the end of the string)
2278c2ecf20Sopenharmony_ci */
2288c2ecf20Sopenharmony_cistatic void
2298c2ecf20Sopenharmony_citty3270_update_string(struct tty3270 *tp, struct string *line, int nr)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	unsigned char *cp;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	raw3270_buffer_address(tp->view.dev, line->string + 1,
2348c2ecf20Sopenharmony_ci			       tp->view.cols * nr);
2358c2ecf20Sopenharmony_ci	cp = line->string + line->len - 4;
2368c2ecf20Sopenharmony_ci	if (*cp == TO_RA)
2378c2ecf20Sopenharmony_ci		raw3270_buffer_address(tp->view.dev, cp + 1,
2388c2ecf20Sopenharmony_ci				       tp->view.cols * (nr + 1));
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/*
2428c2ecf20Sopenharmony_ci * Rebuild update list to print all lines.
2438c2ecf20Sopenharmony_ci */
2448c2ecf20Sopenharmony_cistatic void
2458c2ecf20Sopenharmony_citty3270_rebuild_update(struct tty3270 *tp)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct string *s, *n;
2488c2ecf20Sopenharmony_ci	int line, nr_up;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/*
2518c2ecf20Sopenharmony_ci	 * Throw away update list and create a new one,
2528c2ecf20Sopenharmony_ci	 * containing all lines that will fit on the screen.
2538c2ecf20Sopenharmony_ci	 */
2548c2ecf20Sopenharmony_ci	list_for_each_entry_safe(s, n, &tp->update, update)
2558c2ecf20Sopenharmony_ci		list_del_init(&s->update);
2568c2ecf20Sopenharmony_ci	line = tp->view.rows - 3;
2578c2ecf20Sopenharmony_ci	nr_up = tp->nr_up;
2588c2ecf20Sopenharmony_ci	list_for_each_entry_reverse(s, &tp->lines, list) {
2598c2ecf20Sopenharmony_ci		if (nr_up > 0) {
2608c2ecf20Sopenharmony_ci			nr_up--;
2618c2ecf20Sopenharmony_ci			continue;
2628c2ecf20Sopenharmony_ci		}
2638c2ecf20Sopenharmony_ci		tty3270_update_string(tp, s, line);
2648c2ecf20Sopenharmony_ci		list_add(&s->update, &tp->update);
2658c2ecf20Sopenharmony_ci		if (--line < 0)
2668c2ecf20Sopenharmony_ci			break;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci	tp->update_flags |= TTY_UPDATE_LIST;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci/*
2728c2ecf20Sopenharmony_ci * Alloc string for size bytes. If there is not enough room in
2738c2ecf20Sopenharmony_ci * freemem, free strings until there is room.
2748c2ecf20Sopenharmony_ci */
2758c2ecf20Sopenharmony_cistatic struct string *
2768c2ecf20Sopenharmony_citty3270_alloc_string(struct tty3270 *tp, size_t size)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct string *s, *n;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	s = alloc_string(&tp->freemem, size);
2818c2ecf20Sopenharmony_ci	if (s)
2828c2ecf20Sopenharmony_ci		return s;
2838c2ecf20Sopenharmony_ci	list_for_each_entry_safe(s, n, &tp->lines, list) {
2848c2ecf20Sopenharmony_ci		BUG_ON(tp->nr_lines <= tp->view.rows - 2);
2858c2ecf20Sopenharmony_ci		list_del(&s->list);
2868c2ecf20Sopenharmony_ci		if (!list_empty(&s->update))
2878c2ecf20Sopenharmony_ci			list_del(&s->update);
2888c2ecf20Sopenharmony_ci		tp->nr_lines--;
2898c2ecf20Sopenharmony_ci		if (free_string(&tp->freemem, s) >= size)
2908c2ecf20Sopenharmony_ci			break;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci	s = alloc_string(&tp->freemem, size);
2938c2ecf20Sopenharmony_ci	BUG_ON(!s);
2948c2ecf20Sopenharmony_ci	if (tp->nr_up != 0 &&
2958c2ecf20Sopenharmony_ci	    tp->nr_up + tp->view.rows - 2 >= tp->nr_lines) {
2968c2ecf20Sopenharmony_ci		tp->nr_up = tp->nr_lines - tp->view.rows + 2;
2978c2ecf20Sopenharmony_ci		tty3270_rebuild_update(tp);
2988c2ecf20Sopenharmony_ci		tty3270_update_status(tp);
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci	return s;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/*
3048c2ecf20Sopenharmony_ci * Add an empty line to the list.
3058c2ecf20Sopenharmony_ci */
3068c2ecf20Sopenharmony_cistatic void
3078c2ecf20Sopenharmony_citty3270_blank_line(struct tty3270 *tp)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	static const unsigned char blueprint[] =
3108c2ecf20Sopenharmony_ci		{ TO_SBA, 0, 0, TO_SA, TAT_EXTHI, TAX_RESET,
3118c2ecf20Sopenharmony_ci		  TO_SA, TAT_COLOR, TAC_RESET, TO_RA, 0, 0, 0 };
3128c2ecf20Sopenharmony_ci	struct string *s;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	s = tty3270_alloc_string(tp, sizeof(blueprint));
3158c2ecf20Sopenharmony_ci	memcpy(s->string, blueprint, sizeof(blueprint));
3168c2ecf20Sopenharmony_ci	s->len = sizeof(blueprint);
3178c2ecf20Sopenharmony_ci	list_add_tail(&s->list, &tp->lines);
3188c2ecf20Sopenharmony_ci	tp->nr_lines++;
3198c2ecf20Sopenharmony_ci	if (tp->nr_up != 0)
3208c2ecf20Sopenharmony_ci		tp->nr_up++;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci/*
3248c2ecf20Sopenharmony_ci * Create a blank screen and remove all lines from the history.
3258c2ecf20Sopenharmony_ci */
3268c2ecf20Sopenharmony_cistatic void
3278c2ecf20Sopenharmony_citty3270_blank_screen(struct tty3270 *tp)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct string *s, *n;
3308c2ecf20Sopenharmony_ci	int i;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	for (i = 0; i < tp->view.rows - 2; i++)
3338c2ecf20Sopenharmony_ci		tp->screen[i].len = 0;
3348c2ecf20Sopenharmony_ci	tp->nr_up = 0;
3358c2ecf20Sopenharmony_ci	list_for_each_entry_safe(s, n, &tp->lines, list) {
3368c2ecf20Sopenharmony_ci		list_del(&s->list);
3378c2ecf20Sopenharmony_ci		if (!list_empty(&s->update))
3388c2ecf20Sopenharmony_ci			list_del(&s->update);
3398c2ecf20Sopenharmony_ci		tp->nr_lines--;
3408c2ecf20Sopenharmony_ci		free_string(&tp->freemem, s);
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci/*
3458c2ecf20Sopenharmony_ci * Write request completion callback.
3468c2ecf20Sopenharmony_ci */
3478c2ecf20Sopenharmony_cistatic void
3488c2ecf20Sopenharmony_citty3270_write_callback(struct raw3270_request *rq, void *data)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(rq->view, struct tty3270, view);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	if (rq->rc != 0) {
3538c2ecf20Sopenharmony_ci		/* Write wasn't successful. Refresh all. */
3548c2ecf20Sopenharmony_ci		tp->update_flags = TTY_UPDATE_ALL;
3558c2ecf20Sopenharmony_ci		tty3270_set_timer(tp, 1);
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci	raw3270_request_reset(rq);
3588c2ecf20Sopenharmony_ci	xchg(&tp->write, rq);
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci/*
3628c2ecf20Sopenharmony_ci * Update 3270 display.
3638c2ecf20Sopenharmony_ci */
3648c2ecf20Sopenharmony_cistatic void
3658c2ecf20Sopenharmony_citty3270_update(struct timer_list *t)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct tty3270 *tp = from_timer(tp, t, timer);
3688c2ecf20Sopenharmony_ci	static char invalid_sba[2] = { 0xff, 0xff };
3698c2ecf20Sopenharmony_ci	struct raw3270_request *wrq;
3708c2ecf20Sopenharmony_ci	unsigned long updated;
3718c2ecf20Sopenharmony_ci	struct string *s, *n;
3728c2ecf20Sopenharmony_ci	char *sba, *str;
3738c2ecf20Sopenharmony_ci	int rc, len;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	wrq = xchg(&tp->write, 0);
3768c2ecf20Sopenharmony_ci	if (!wrq) {
3778c2ecf20Sopenharmony_ci		tty3270_set_timer(tp, 1);
3788c2ecf20Sopenharmony_ci		return;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	spin_lock(&tp->view.lock);
3828c2ecf20Sopenharmony_ci	updated = 0;
3838c2ecf20Sopenharmony_ci	if (tp->update_flags & TTY_UPDATE_ALL) {
3848c2ecf20Sopenharmony_ci		tty3270_rebuild_update(tp);
3858c2ecf20Sopenharmony_ci		tty3270_update_status(tp);
3868c2ecf20Sopenharmony_ci		tp->update_flags = TTY_UPDATE_ERASE | TTY_UPDATE_LIST |
3878c2ecf20Sopenharmony_ci			TTY_UPDATE_INPUT | TTY_UPDATE_STATUS;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci	if (tp->update_flags & TTY_UPDATE_ERASE) {
3908c2ecf20Sopenharmony_ci		/* Use erase write alternate to erase display. */
3918c2ecf20Sopenharmony_ci		raw3270_request_set_cmd(wrq, TC_EWRITEA);
3928c2ecf20Sopenharmony_ci		updated |= TTY_UPDATE_ERASE;
3938c2ecf20Sopenharmony_ci	} else
3948c2ecf20Sopenharmony_ci		raw3270_request_set_cmd(wrq, TC_WRITE);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	raw3270_request_add_data(wrq, &tp->wcc, 1);
3978c2ecf20Sopenharmony_ci	tp->wcc = TW_NONE;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/*
4008c2ecf20Sopenharmony_ci	 * Update status line.
4018c2ecf20Sopenharmony_ci	 */
4028c2ecf20Sopenharmony_ci	if (tp->update_flags & TTY_UPDATE_STATUS)
4038c2ecf20Sopenharmony_ci		if (raw3270_request_add_data(wrq, tp->status->string,
4048c2ecf20Sopenharmony_ci					     tp->status->len) == 0)
4058c2ecf20Sopenharmony_ci			updated |= TTY_UPDATE_STATUS;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	/*
4088c2ecf20Sopenharmony_ci	 * Write input line.
4098c2ecf20Sopenharmony_ci	 */
4108c2ecf20Sopenharmony_ci	if (tp->update_flags & TTY_UPDATE_INPUT)
4118c2ecf20Sopenharmony_ci		if (raw3270_request_add_data(wrq, tp->prompt->string,
4128c2ecf20Sopenharmony_ci					     tp->prompt->len) == 0)
4138c2ecf20Sopenharmony_ci			updated |= TTY_UPDATE_INPUT;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	sba = invalid_sba;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	if (tp->update_flags & TTY_UPDATE_LIST) {
4188c2ecf20Sopenharmony_ci		/* Write strings in the update list to the screen. */
4198c2ecf20Sopenharmony_ci		list_for_each_entry_safe(s, n, &tp->update, update) {
4208c2ecf20Sopenharmony_ci			str = s->string;
4218c2ecf20Sopenharmony_ci			len = s->len;
4228c2ecf20Sopenharmony_ci			/*
4238c2ecf20Sopenharmony_ci			 * Skip TO_SBA at the start of the string if the
4248c2ecf20Sopenharmony_ci			 * last output position matches the start address
4258c2ecf20Sopenharmony_ci			 * of this line.
4268c2ecf20Sopenharmony_ci			 */
4278c2ecf20Sopenharmony_ci			if (s->string[1] == sba[0] && s->string[2] == sba[1])
4288c2ecf20Sopenharmony_ci				str += 3, len -= 3;
4298c2ecf20Sopenharmony_ci			if (raw3270_request_add_data(wrq, str, len) != 0)
4308c2ecf20Sopenharmony_ci				break;
4318c2ecf20Sopenharmony_ci			list_del_init(&s->update);
4328c2ecf20Sopenharmony_ci			if (s->string[s->len - 4] == TO_RA)
4338c2ecf20Sopenharmony_ci				sba = s->string + s->len - 3;
4348c2ecf20Sopenharmony_ci			else
4358c2ecf20Sopenharmony_ci				sba = invalid_sba;
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci		if (list_empty(&tp->update))
4388c2ecf20Sopenharmony_ci			updated |= TTY_UPDATE_LIST;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci	wrq->callback = tty3270_write_callback;
4418c2ecf20Sopenharmony_ci	rc = raw3270_start(&tp->view, wrq);
4428c2ecf20Sopenharmony_ci	if (rc == 0) {
4438c2ecf20Sopenharmony_ci		tp->update_flags &= ~updated;
4448c2ecf20Sopenharmony_ci		if (tp->update_flags)
4458c2ecf20Sopenharmony_ci			tty3270_set_timer(tp, 1);
4468c2ecf20Sopenharmony_ci	} else {
4478c2ecf20Sopenharmony_ci		raw3270_request_reset(wrq);
4488c2ecf20Sopenharmony_ci		xchg(&tp->write, wrq);
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	spin_unlock(&tp->view.lock);
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci/*
4548c2ecf20Sopenharmony_ci * Command recalling.
4558c2ecf20Sopenharmony_ci */
4568c2ecf20Sopenharmony_cistatic void
4578c2ecf20Sopenharmony_citty3270_rcl_add(struct tty3270 *tp, char *input, int len)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	struct string *s;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	tp->rcl_walk = NULL;
4628c2ecf20Sopenharmony_ci	if (len <= 0)
4638c2ecf20Sopenharmony_ci		return;
4648c2ecf20Sopenharmony_ci	if (tp->rcl_nr >= tp->rcl_max) {
4658c2ecf20Sopenharmony_ci		s = list_entry(tp->rcl_lines.next, struct string, list);
4668c2ecf20Sopenharmony_ci		list_del(&s->list);
4678c2ecf20Sopenharmony_ci		free_string(&tp->freemem, s);
4688c2ecf20Sopenharmony_ci		tp->rcl_nr--;
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci	s = tty3270_alloc_string(tp, len);
4718c2ecf20Sopenharmony_ci	memcpy(s->string, input, len);
4728c2ecf20Sopenharmony_ci	list_add_tail(&s->list, &tp->rcl_lines);
4738c2ecf20Sopenharmony_ci	tp->rcl_nr++;
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic void
4778c2ecf20Sopenharmony_citty3270_rcl_backward(struct kbd_data *kbd)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
4808c2ecf20Sopenharmony_ci	struct string *s;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	spin_lock_bh(&tp->view.lock);
4838c2ecf20Sopenharmony_ci	if (tp->inattr == TF_INPUT) {
4848c2ecf20Sopenharmony_ci		if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines)
4858c2ecf20Sopenharmony_ci			tp->rcl_walk = tp->rcl_walk->prev;
4868c2ecf20Sopenharmony_ci		else if (!list_empty(&tp->rcl_lines))
4878c2ecf20Sopenharmony_ci			tp->rcl_walk = tp->rcl_lines.prev;
4888c2ecf20Sopenharmony_ci		s = tp->rcl_walk ?
4898c2ecf20Sopenharmony_ci			list_entry(tp->rcl_walk, struct string, list) : NULL;
4908c2ecf20Sopenharmony_ci		if (tp->rcl_walk) {
4918c2ecf20Sopenharmony_ci			s = list_entry(tp->rcl_walk, struct string, list);
4928c2ecf20Sopenharmony_ci			tty3270_update_prompt(tp, s->string, s->len);
4938c2ecf20Sopenharmony_ci		} else
4948c2ecf20Sopenharmony_ci			tty3270_update_prompt(tp, NULL, 0);
4958c2ecf20Sopenharmony_ci		tty3270_set_timer(tp, 1);
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci	spin_unlock_bh(&tp->view.lock);
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci/*
5018c2ecf20Sopenharmony_ci * Deactivate tty view.
5028c2ecf20Sopenharmony_ci */
5038c2ecf20Sopenharmony_cistatic void
5048c2ecf20Sopenharmony_citty3270_exit_tty(struct kbd_data *kbd)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	raw3270_deactivate_view(&tp->view);
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci/*
5128c2ecf20Sopenharmony_ci * Scroll forward in history.
5138c2ecf20Sopenharmony_ci */
5148c2ecf20Sopenharmony_cistatic void
5158c2ecf20Sopenharmony_citty3270_scroll_forward(struct kbd_data *kbd)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
5188c2ecf20Sopenharmony_ci	int nr_up;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	spin_lock_bh(&tp->view.lock);
5218c2ecf20Sopenharmony_ci	nr_up = tp->nr_up - tp->view.rows + 2;
5228c2ecf20Sopenharmony_ci	if (nr_up < 0)
5238c2ecf20Sopenharmony_ci		nr_up = 0;
5248c2ecf20Sopenharmony_ci	if (nr_up != tp->nr_up) {
5258c2ecf20Sopenharmony_ci		tp->nr_up = nr_up;
5268c2ecf20Sopenharmony_ci		tty3270_rebuild_update(tp);
5278c2ecf20Sopenharmony_ci		tty3270_update_status(tp);
5288c2ecf20Sopenharmony_ci		tty3270_set_timer(tp, 1);
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci	spin_unlock_bh(&tp->view.lock);
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci/*
5348c2ecf20Sopenharmony_ci * Scroll backward in history.
5358c2ecf20Sopenharmony_ci */
5368c2ecf20Sopenharmony_cistatic void
5378c2ecf20Sopenharmony_citty3270_scroll_backward(struct kbd_data *kbd)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
5408c2ecf20Sopenharmony_ci	int nr_up;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	spin_lock_bh(&tp->view.lock);
5438c2ecf20Sopenharmony_ci	nr_up = tp->nr_up + tp->view.rows - 2;
5448c2ecf20Sopenharmony_ci	if (nr_up + tp->view.rows - 2 > tp->nr_lines)
5458c2ecf20Sopenharmony_ci		nr_up = tp->nr_lines - tp->view.rows + 2;
5468c2ecf20Sopenharmony_ci	if (nr_up != tp->nr_up) {
5478c2ecf20Sopenharmony_ci		tp->nr_up = nr_up;
5488c2ecf20Sopenharmony_ci		tty3270_rebuild_update(tp);
5498c2ecf20Sopenharmony_ci		tty3270_update_status(tp);
5508c2ecf20Sopenharmony_ci		tty3270_set_timer(tp, 1);
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci	spin_unlock_bh(&tp->view.lock);
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci/*
5568c2ecf20Sopenharmony_ci * Pass input line to tty.
5578c2ecf20Sopenharmony_ci */
5588c2ecf20Sopenharmony_cistatic void
5598c2ecf20Sopenharmony_citty3270_read_tasklet(unsigned long data)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct raw3270_request *rrq = (struct raw3270_request *)data;
5628c2ecf20Sopenharmony_ci	static char kreset_data = TW_KR;
5638c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(rrq->view, struct tty3270, view);
5648c2ecf20Sopenharmony_ci	char *input;
5658c2ecf20Sopenharmony_ci	int len;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	spin_lock_bh(&tp->view.lock);
5688c2ecf20Sopenharmony_ci	/*
5698c2ecf20Sopenharmony_ci	 * Two AID keys are special: For 0x7d (enter) the input line
5708c2ecf20Sopenharmony_ci	 * has to be emitted to the tty and for 0x6d the screen
5718c2ecf20Sopenharmony_ci	 * needs to be redrawn.
5728c2ecf20Sopenharmony_ci	 */
5738c2ecf20Sopenharmony_ci	input = NULL;
5748c2ecf20Sopenharmony_ci	len = 0;
5758c2ecf20Sopenharmony_ci	if (tp->input->string[0] == 0x7d) {
5768c2ecf20Sopenharmony_ci		/* Enter: write input to tty. */
5778c2ecf20Sopenharmony_ci		input = tp->input->string + 6;
5788c2ecf20Sopenharmony_ci		len = tp->input->len - 6 - rrq->rescnt;
5798c2ecf20Sopenharmony_ci		if (tp->inattr != TF_INPUTN)
5808c2ecf20Sopenharmony_ci			tty3270_rcl_add(tp, input, len);
5818c2ecf20Sopenharmony_ci		if (tp->nr_up > 0) {
5828c2ecf20Sopenharmony_ci			tp->nr_up = 0;
5838c2ecf20Sopenharmony_ci			tty3270_rebuild_update(tp);
5848c2ecf20Sopenharmony_ci			tty3270_update_status(tp);
5858c2ecf20Sopenharmony_ci		}
5868c2ecf20Sopenharmony_ci		/* Clear input area. */
5878c2ecf20Sopenharmony_ci		tty3270_update_prompt(tp, NULL, 0);
5888c2ecf20Sopenharmony_ci		tty3270_set_timer(tp, 1);
5898c2ecf20Sopenharmony_ci	} else if (tp->input->string[0] == 0x6d) {
5908c2ecf20Sopenharmony_ci		/* Display has been cleared. Redraw. */
5918c2ecf20Sopenharmony_ci		tp->update_flags = TTY_UPDATE_ALL;
5928c2ecf20Sopenharmony_ci		tty3270_set_timer(tp, 1);
5938c2ecf20Sopenharmony_ci	}
5948c2ecf20Sopenharmony_ci	spin_unlock_bh(&tp->view.lock);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	/* Start keyboard reset command. */
5978c2ecf20Sopenharmony_ci	raw3270_request_reset(tp->kreset);
5988c2ecf20Sopenharmony_ci	raw3270_request_set_cmd(tp->kreset, TC_WRITE);
5998c2ecf20Sopenharmony_ci	raw3270_request_add_data(tp->kreset, &kreset_data, 1);
6008c2ecf20Sopenharmony_ci	raw3270_start(&tp->view, tp->kreset);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	while (len-- > 0)
6038c2ecf20Sopenharmony_ci		kbd_keycode(tp->kbd, *input++);
6048c2ecf20Sopenharmony_ci	/* Emit keycode for AID byte. */
6058c2ecf20Sopenharmony_ci	kbd_keycode(tp->kbd, 256 + tp->input->string[0]);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	raw3270_request_reset(rrq);
6088c2ecf20Sopenharmony_ci	xchg(&tp->read, rrq);
6098c2ecf20Sopenharmony_ci	raw3270_put_view(&tp->view);
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci/*
6138c2ecf20Sopenharmony_ci * Read request completion callback.
6148c2ecf20Sopenharmony_ci */
6158c2ecf20Sopenharmony_cistatic void
6168c2ecf20Sopenharmony_citty3270_read_callback(struct raw3270_request *rq, void *data)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(rq->view, struct tty3270, view);
6198c2ecf20Sopenharmony_ci	raw3270_get_view(rq->view);
6208c2ecf20Sopenharmony_ci	/* Schedule tasklet to pass input to tty. */
6218c2ecf20Sopenharmony_ci	tasklet_schedule(&tp->readlet);
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci/*
6258c2ecf20Sopenharmony_ci * Issue a read request. Call with device lock.
6268c2ecf20Sopenharmony_ci */
6278c2ecf20Sopenharmony_cistatic void
6288c2ecf20Sopenharmony_citty3270_issue_read(struct tty3270 *tp, int lock)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	struct raw3270_request *rrq;
6318c2ecf20Sopenharmony_ci	int rc;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	rrq = xchg(&tp->read, 0);
6348c2ecf20Sopenharmony_ci	if (!rrq)
6358c2ecf20Sopenharmony_ci		/* Read already scheduled. */
6368c2ecf20Sopenharmony_ci		return;
6378c2ecf20Sopenharmony_ci	rrq->callback = tty3270_read_callback;
6388c2ecf20Sopenharmony_ci	rrq->callback_data = tp;
6398c2ecf20Sopenharmony_ci	raw3270_request_set_cmd(rrq, TC_READMOD);
6408c2ecf20Sopenharmony_ci	raw3270_request_set_data(rrq, tp->input->string, tp->input->len);
6418c2ecf20Sopenharmony_ci	/* Issue the read modified request. */
6428c2ecf20Sopenharmony_ci	if (lock) {
6438c2ecf20Sopenharmony_ci		rc = raw3270_start(&tp->view, rrq);
6448c2ecf20Sopenharmony_ci	} else
6458c2ecf20Sopenharmony_ci		rc = raw3270_start_irq(&tp->view, rrq);
6468c2ecf20Sopenharmony_ci	if (rc) {
6478c2ecf20Sopenharmony_ci		raw3270_request_reset(rrq);
6488c2ecf20Sopenharmony_ci		xchg(&tp->read, rrq);
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci/*
6538c2ecf20Sopenharmony_ci * Hang up the tty
6548c2ecf20Sopenharmony_ci */
6558c2ecf20Sopenharmony_cistatic void
6568c2ecf20Sopenharmony_citty3270_hangup_tasklet(unsigned long data)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	struct tty3270 *tp = (struct tty3270 *)data;
6598c2ecf20Sopenharmony_ci	tty_port_tty_hangup(&tp->port, true);
6608c2ecf20Sopenharmony_ci	raw3270_put_view(&tp->view);
6618c2ecf20Sopenharmony_ci}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci/*
6648c2ecf20Sopenharmony_ci * Switch to the tty view.
6658c2ecf20Sopenharmony_ci */
6668c2ecf20Sopenharmony_cistatic int
6678c2ecf20Sopenharmony_citty3270_activate(struct raw3270_view *view)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(view, struct tty3270, view);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	tp->update_flags = TTY_UPDATE_ALL;
6728c2ecf20Sopenharmony_ci	tty3270_set_timer(tp, 1);
6738c2ecf20Sopenharmony_ci	return 0;
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cistatic void
6778c2ecf20Sopenharmony_citty3270_deactivate(struct raw3270_view *view)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(view, struct tty3270, view);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	del_timer(&tp->timer);
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_cistatic void
6858c2ecf20Sopenharmony_citty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	/* Handle ATTN. Schedule tasklet to read aid. */
6888c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
6898c2ecf20Sopenharmony_ci		if (!tp->throttle)
6908c2ecf20Sopenharmony_ci			tty3270_issue_read(tp, 0);
6918c2ecf20Sopenharmony_ci		else
6928c2ecf20Sopenharmony_ci			tp->attn = 1;
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if (rq) {
6968c2ecf20Sopenharmony_ci		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
6978c2ecf20Sopenharmony_ci			rq->rc = -EIO;
6988c2ecf20Sopenharmony_ci			raw3270_get_view(&tp->view);
6998c2ecf20Sopenharmony_ci			tasklet_schedule(&tp->hanglet);
7008c2ecf20Sopenharmony_ci		} else {
7018c2ecf20Sopenharmony_ci			/* Normal end. Copy residual count. */
7028c2ecf20Sopenharmony_ci			rq->rescnt = irb->scsw.cmd.count;
7038c2ecf20Sopenharmony_ci		}
7048c2ecf20Sopenharmony_ci	} else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
7058c2ecf20Sopenharmony_ci		/* Interrupt without an outstanding request -> update all */
7068c2ecf20Sopenharmony_ci		tp->update_flags = TTY_UPDATE_ALL;
7078c2ecf20Sopenharmony_ci		tty3270_set_timer(tp, 1);
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci/*
7128c2ecf20Sopenharmony_ci * Allocate tty3270 structure.
7138c2ecf20Sopenharmony_ci */
7148c2ecf20Sopenharmony_cistatic struct tty3270 *
7158c2ecf20Sopenharmony_citty3270_alloc_view(void)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	struct tty3270 *tp;
7188c2ecf20Sopenharmony_ci	int pages;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	tp = kzalloc(sizeof(struct tty3270), GFP_KERNEL);
7218c2ecf20Sopenharmony_ci	if (!tp)
7228c2ecf20Sopenharmony_ci		goto out_err;
7238c2ecf20Sopenharmony_ci	tp->freemem_pages =
7248c2ecf20Sopenharmony_ci		kmalloc_array(TTY3270_STRING_PAGES, sizeof(void *),
7258c2ecf20Sopenharmony_ci			      GFP_KERNEL);
7268c2ecf20Sopenharmony_ci	if (!tp->freemem_pages)
7278c2ecf20Sopenharmony_ci		goto out_tp;
7288c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tp->freemem);
7298c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tp->lines);
7308c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tp->update);
7318c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tp->rcl_lines);
7328c2ecf20Sopenharmony_ci	tp->rcl_max = 20;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) {
7358c2ecf20Sopenharmony_ci		tp->freemem_pages[pages] = (void *)
7368c2ecf20Sopenharmony_ci			__get_free_pages(GFP_KERNEL|GFP_DMA, 0);
7378c2ecf20Sopenharmony_ci		if (!tp->freemem_pages[pages])
7388c2ecf20Sopenharmony_ci			goto out_pages;
7398c2ecf20Sopenharmony_ci		add_string_memory(&tp->freemem,
7408c2ecf20Sopenharmony_ci				  tp->freemem_pages[pages], PAGE_SIZE);
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci	tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE);
7438c2ecf20Sopenharmony_ci	if (IS_ERR(tp->write))
7448c2ecf20Sopenharmony_ci		goto out_pages;
7458c2ecf20Sopenharmony_ci	tp->read = raw3270_request_alloc(0);
7468c2ecf20Sopenharmony_ci	if (IS_ERR(tp->read))
7478c2ecf20Sopenharmony_ci		goto out_write;
7488c2ecf20Sopenharmony_ci	tp->kreset = raw3270_request_alloc(1);
7498c2ecf20Sopenharmony_ci	if (IS_ERR(tp->kreset))
7508c2ecf20Sopenharmony_ci		goto out_read;
7518c2ecf20Sopenharmony_ci	tp->kbd = kbd_alloc();
7528c2ecf20Sopenharmony_ci	if (!tp->kbd)
7538c2ecf20Sopenharmony_ci		goto out_reset;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	tty_port_init(&tp->port);
7568c2ecf20Sopenharmony_ci	timer_setup(&tp->timer, tty3270_update, 0);
7578c2ecf20Sopenharmony_ci	tasklet_init(&tp->readlet, tty3270_read_tasklet,
7588c2ecf20Sopenharmony_ci		     (unsigned long) tp->read);
7598c2ecf20Sopenharmony_ci	tasklet_init(&tp->hanglet, tty3270_hangup_tasklet,
7608c2ecf20Sopenharmony_ci		     (unsigned long) tp);
7618c2ecf20Sopenharmony_ci	INIT_WORK(&tp->resize_work, tty3270_resize_work);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	return tp;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ciout_reset:
7668c2ecf20Sopenharmony_ci	raw3270_request_free(tp->kreset);
7678c2ecf20Sopenharmony_ciout_read:
7688c2ecf20Sopenharmony_ci	raw3270_request_free(tp->read);
7698c2ecf20Sopenharmony_ciout_write:
7708c2ecf20Sopenharmony_ci	raw3270_request_free(tp->write);
7718c2ecf20Sopenharmony_ciout_pages:
7728c2ecf20Sopenharmony_ci	while (pages--)
7738c2ecf20Sopenharmony_ci		free_pages((unsigned long) tp->freemem_pages[pages], 0);
7748c2ecf20Sopenharmony_ci	kfree(tp->freemem_pages);
7758c2ecf20Sopenharmony_ci	tty_port_destroy(&tp->port);
7768c2ecf20Sopenharmony_ciout_tp:
7778c2ecf20Sopenharmony_ci	kfree(tp);
7788c2ecf20Sopenharmony_ciout_err:
7798c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOMEM);
7808c2ecf20Sopenharmony_ci}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci/*
7838c2ecf20Sopenharmony_ci * Free tty3270 structure.
7848c2ecf20Sopenharmony_ci */
7858c2ecf20Sopenharmony_cistatic void
7868c2ecf20Sopenharmony_citty3270_free_view(struct tty3270 *tp)
7878c2ecf20Sopenharmony_ci{
7888c2ecf20Sopenharmony_ci	int pages;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	kbd_free(tp->kbd);
7918c2ecf20Sopenharmony_ci	raw3270_request_free(tp->kreset);
7928c2ecf20Sopenharmony_ci	raw3270_request_free(tp->read);
7938c2ecf20Sopenharmony_ci	raw3270_request_free(tp->write);
7948c2ecf20Sopenharmony_ci	for (pages = 0; pages < TTY3270_STRING_PAGES; pages++)
7958c2ecf20Sopenharmony_ci		free_pages((unsigned long) tp->freemem_pages[pages], 0);
7968c2ecf20Sopenharmony_ci	kfree(tp->freemem_pages);
7978c2ecf20Sopenharmony_ci	tty_port_destroy(&tp->port);
7988c2ecf20Sopenharmony_ci	kfree(tp);
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci/*
8028c2ecf20Sopenharmony_ci * Allocate tty3270 screen.
8038c2ecf20Sopenharmony_ci */
8048c2ecf20Sopenharmony_cistatic struct tty3270_line *
8058c2ecf20Sopenharmony_citty3270_alloc_screen(unsigned int rows, unsigned int cols)
8068c2ecf20Sopenharmony_ci{
8078c2ecf20Sopenharmony_ci	struct tty3270_line *screen;
8088c2ecf20Sopenharmony_ci	unsigned long size;
8098c2ecf20Sopenharmony_ci	int lines;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	size = sizeof(struct tty3270_line) * (rows - 2);
8128c2ecf20Sopenharmony_ci	screen = kzalloc(size, GFP_KERNEL);
8138c2ecf20Sopenharmony_ci	if (!screen)
8148c2ecf20Sopenharmony_ci		goto out_err;
8158c2ecf20Sopenharmony_ci	for (lines = 0; lines < rows - 2; lines++) {
8168c2ecf20Sopenharmony_ci		size = sizeof(struct tty3270_cell) * cols;
8178c2ecf20Sopenharmony_ci		screen[lines].cells = kzalloc(size, GFP_KERNEL);
8188c2ecf20Sopenharmony_ci		if (!screen[lines].cells)
8198c2ecf20Sopenharmony_ci			goto out_screen;
8208c2ecf20Sopenharmony_ci	}
8218c2ecf20Sopenharmony_ci	return screen;
8228c2ecf20Sopenharmony_ciout_screen:
8238c2ecf20Sopenharmony_ci	while (lines--)
8248c2ecf20Sopenharmony_ci		kfree(screen[lines].cells);
8258c2ecf20Sopenharmony_ci	kfree(screen);
8268c2ecf20Sopenharmony_ciout_err:
8278c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOMEM);
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci/*
8318c2ecf20Sopenharmony_ci * Free tty3270 screen.
8328c2ecf20Sopenharmony_ci */
8338c2ecf20Sopenharmony_cistatic void
8348c2ecf20Sopenharmony_citty3270_free_screen(struct tty3270_line *screen, unsigned int rows)
8358c2ecf20Sopenharmony_ci{
8368c2ecf20Sopenharmony_ci	int lines;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	for (lines = 0; lines < rows - 2; lines++)
8398c2ecf20Sopenharmony_ci		kfree(screen[lines].cells);
8408c2ecf20Sopenharmony_ci	kfree(screen);
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci/*
8448c2ecf20Sopenharmony_ci * Resize tty3270 screen
8458c2ecf20Sopenharmony_ci */
8468c2ecf20Sopenharmony_cistatic void tty3270_resize_work(struct work_struct *work)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(work, struct tty3270, resize_work);
8498c2ecf20Sopenharmony_ci	struct tty3270_line *screen, *oscreen;
8508c2ecf20Sopenharmony_ci	struct tty_struct *tty;
8518c2ecf20Sopenharmony_ci	unsigned int orows;
8528c2ecf20Sopenharmony_ci	struct winsize ws;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	screen = tty3270_alloc_screen(tp->n_rows, tp->n_cols);
8558c2ecf20Sopenharmony_ci	if (IS_ERR(screen))
8568c2ecf20Sopenharmony_ci		return;
8578c2ecf20Sopenharmony_ci	/* Switch to new output size */
8588c2ecf20Sopenharmony_ci	spin_lock_bh(&tp->view.lock);
8598c2ecf20Sopenharmony_ci	tty3270_blank_screen(tp);
8608c2ecf20Sopenharmony_ci	oscreen = tp->screen;
8618c2ecf20Sopenharmony_ci	orows = tp->view.rows;
8628c2ecf20Sopenharmony_ci	tp->view.model = tp->n_model;
8638c2ecf20Sopenharmony_ci	tp->view.rows = tp->n_rows;
8648c2ecf20Sopenharmony_ci	tp->view.cols = tp->n_cols;
8658c2ecf20Sopenharmony_ci	tp->screen = screen;
8668c2ecf20Sopenharmony_ci	free_string(&tp->freemem, tp->prompt);
8678c2ecf20Sopenharmony_ci	free_string(&tp->freemem, tp->status);
8688c2ecf20Sopenharmony_ci	tty3270_create_prompt(tp);
8698c2ecf20Sopenharmony_ci	tty3270_create_status(tp);
8708c2ecf20Sopenharmony_ci	while (tp->nr_lines < tp->view.rows - 2)
8718c2ecf20Sopenharmony_ci		tty3270_blank_line(tp);
8728c2ecf20Sopenharmony_ci	tp->update_flags = TTY_UPDATE_ALL;
8738c2ecf20Sopenharmony_ci	spin_unlock_bh(&tp->view.lock);
8748c2ecf20Sopenharmony_ci	tty3270_free_screen(oscreen, orows);
8758c2ecf20Sopenharmony_ci	tty3270_set_timer(tp, 1);
8768c2ecf20Sopenharmony_ci	/* Informat tty layer about new size */
8778c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(&tp->port);
8788c2ecf20Sopenharmony_ci	if (!tty)
8798c2ecf20Sopenharmony_ci		return;
8808c2ecf20Sopenharmony_ci	ws.ws_row = tp->view.rows - 2;
8818c2ecf20Sopenharmony_ci	ws.ws_col = tp->view.cols;
8828c2ecf20Sopenharmony_ci	tty_do_resize(tty, &ws);
8838c2ecf20Sopenharmony_ci	tty_kref_put(tty);
8848c2ecf20Sopenharmony_ci}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_cistatic void
8878c2ecf20Sopenharmony_citty3270_resize(struct raw3270_view *view, int model, int rows, int cols)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(view, struct tty3270, view);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	if (tp->n_model == model && tp->n_rows == rows && tp->n_cols == cols)
8928c2ecf20Sopenharmony_ci		return;
8938c2ecf20Sopenharmony_ci	tp->n_model = model;
8948c2ecf20Sopenharmony_ci	tp->n_rows = rows;
8958c2ecf20Sopenharmony_ci	tp->n_cols = cols;
8968c2ecf20Sopenharmony_ci	schedule_work(&tp->resize_work);
8978c2ecf20Sopenharmony_ci}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci/*
9008c2ecf20Sopenharmony_ci * Unlink tty3270 data structure from tty.
9018c2ecf20Sopenharmony_ci */
9028c2ecf20Sopenharmony_cistatic void
9038c2ecf20Sopenharmony_citty3270_release(struct raw3270_view *view)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(view, struct tty3270, view);
9068c2ecf20Sopenharmony_ci	struct tty_struct *tty = tty_port_tty_get(&tp->port);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	if (tty) {
9098c2ecf20Sopenharmony_ci		tty->driver_data = NULL;
9108c2ecf20Sopenharmony_ci		tty_port_tty_set(&tp->port, NULL);
9118c2ecf20Sopenharmony_ci		tty_hangup(tty);
9128c2ecf20Sopenharmony_ci		raw3270_put_view(&tp->view);
9138c2ecf20Sopenharmony_ci		tty_kref_put(tty);
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci/*
9188c2ecf20Sopenharmony_ci * Free tty3270 data structure
9198c2ecf20Sopenharmony_ci */
9208c2ecf20Sopenharmony_cistatic void
9218c2ecf20Sopenharmony_citty3270_free(struct raw3270_view *view)
9228c2ecf20Sopenharmony_ci{
9238c2ecf20Sopenharmony_ci	struct tty3270 *tp = container_of(view, struct tty3270, view);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	del_timer_sync(&tp->timer);
9268c2ecf20Sopenharmony_ci	tty3270_free_screen(tp->screen, tp->view.rows);
9278c2ecf20Sopenharmony_ci	tty3270_free_view(tp);
9288c2ecf20Sopenharmony_ci}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci/*
9318c2ecf20Sopenharmony_ci * Delayed freeing of tty3270 views.
9328c2ecf20Sopenharmony_ci */
9338c2ecf20Sopenharmony_cistatic void
9348c2ecf20Sopenharmony_citty3270_del_views(void)
9358c2ecf20Sopenharmony_ci{
9368c2ecf20Sopenharmony_ci	int i;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) {
9398c2ecf20Sopenharmony_ci		struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i);
9408c2ecf20Sopenharmony_ci		if (!IS_ERR(view))
9418c2ecf20Sopenharmony_ci			raw3270_del_view(view);
9428c2ecf20Sopenharmony_ci	}
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_cistatic struct raw3270_fn tty3270_fn = {
9468c2ecf20Sopenharmony_ci	.activate = tty3270_activate,
9478c2ecf20Sopenharmony_ci	.deactivate = tty3270_deactivate,
9488c2ecf20Sopenharmony_ci	.intv = (void *) tty3270_irq,
9498c2ecf20Sopenharmony_ci	.release = tty3270_release,
9508c2ecf20Sopenharmony_ci	.free = tty3270_free,
9518c2ecf20Sopenharmony_ci	.resize = tty3270_resize
9528c2ecf20Sopenharmony_ci};
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci/*
9558c2ecf20Sopenharmony_ci * This routine is called whenever a 3270 tty is opened first time.
9568c2ecf20Sopenharmony_ci */
9578c2ecf20Sopenharmony_cistatic int tty3270_install(struct tty_driver *driver, struct tty_struct *tty)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	struct raw3270_view *view;
9608c2ecf20Sopenharmony_ci	struct tty3270 *tp;
9618c2ecf20Sopenharmony_ci	int i, rc;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	/* Check if the tty3270 is already there. */
9648c2ecf20Sopenharmony_ci	view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR);
9658c2ecf20Sopenharmony_ci	if (!IS_ERR(view)) {
9668c2ecf20Sopenharmony_ci		tp = container_of(view, struct tty3270, view);
9678c2ecf20Sopenharmony_ci		tty->driver_data = tp;
9688c2ecf20Sopenharmony_ci		tty->winsize.ws_row = tp->view.rows - 2;
9698c2ecf20Sopenharmony_ci		tty->winsize.ws_col = tp->view.cols;
9708c2ecf20Sopenharmony_ci		tp->port.low_latency = 0;
9718c2ecf20Sopenharmony_ci		tp->inattr = TF_INPUT;
9728c2ecf20Sopenharmony_ci		goto port_install;
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci	if (tty3270_max_index < tty->index + 1)
9758c2ecf20Sopenharmony_ci		tty3270_max_index = tty->index + 1;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	/* Allocate tty3270 structure on first open. */
9788c2ecf20Sopenharmony_ci	tp = tty3270_alloc_view();
9798c2ecf20Sopenharmony_ci	if (IS_ERR(tp))
9808c2ecf20Sopenharmony_ci		return PTR_ERR(tp);
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	rc = raw3270_add_view(&tp->view, &tty3270_fn,
9838c2ecf20Sopenharmony_ci			      tty->index + RAW3270_FIRSTMINOR,
9848c2ecf20Sopenharmony_ci			      RAW3270_VIEW_LOCK_BH);
9858c2ecf20Sopenharmony_ci	if (rc) {
9868c2ecf20Sopenharmony_ci		tty3270_free_view(tp);
9878c2ecf20Sopenharmony_ci		return rc;
9888c2ecf20Sopenharmony_ci	}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	tp->screen = tty3270_alloc_screen(tp->view.rows, tp->view.cols);
9918c2ecf20Sopenharmony_ci	if (IS_ERR(tp->screen)) {
9928c2ecf20Sopenharmony_ci		rc = PTR_ERR(tp->screen);
9938c2ecf20Sopenharmony_ci		raw3270_put_view(&tp->view);
9948c2ecf20Sopenharmony_ci		raw3270_del_view(&tp->view);
9958c2ecf20Sopenharmony_ci		tty3270_free_view(tp);
9968c2ecf20Sopenharmony_ci		return rc;
9978c2ecf20Sopenharmony_ci	}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	tp->port.low_latency = 0;
10008c2ecf20Sopenharmony_ci	tty->winsize.ws_row = tp->view.rows - 2;
10018c2ecf20Sopenharmony_ci	tty->winsize.ws_col = tp->view.cols;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	tty3270_create_prompt(tp);
10048c2ecf20Sopenharmony_ci	tty3270_create_status(tp);
10058c2ecf20Sopenharmony_ci	tty3270_update_status(tp);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	/* Create blank line for every line in the tty output area. */
10088c2ecf20Sopenharmony_ci	for (i = 0; i < tp->view.rows - 2; i++)
10098c2ecf20Sopenharmony_ci		tty3270_blank_line(tp);
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	tp->kbd->port = &tp->port;
10128c2ecf20Sopenharmony_ci	tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty;
10138c2ecf20Sopenharmony_ci	tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward;
10148c2ecf20Sopenharmony_ci	tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward;
10158c2ecf20Sopenharmony_ci	tp->kbd->fn_handler[KVAL(K_CONS)] = tty3270_rcl_backward;
10168c2ecf20Sopenharmony_ci	kbd_ascebc(tp->kbd, tp->view.ascebc);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	raw3270_activate_view(&tp->view);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ciport_install:
10218c2ecf20Sopenharmony_ci	rc = tty_port_install(&tp->port, driver, tty);
10228c2ecf20Sopenharmony_ci	if (rc) {
10238c2ecf20Sopenharmony_ci		raw3270_put_view(&tp->view);
10248c2ecf20Sopenharmony_ci		return rc;
10258c2ecf20Sopenharmony_ci	}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	tty->driver_data = tp;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	return 0;
10308c2ecf20Sopenharmony_ci}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci/*
10338c2ecf20Sopenharmony_ci * This routine is called whenever a 3270 tty is opened.
10348c2ecf20Sopenharmony_ci */
10358c2ecf20Sopenharmony_cistatic int
10368c2ecf20Sopenharmony_citty3270_open(struct tty_struct *tty, struct file *filp)
10378c2ecf20Sopenharmony_ci{
10388c2ecf20Sopenharmony_ci	struct tty3270 *tp = tty->driver_data;
10398c2ecf20Sopenharmony_ci	struct tty_port *port = &tp->port;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	port->count++;
10428c2ecf20Sopenharmony_ci	tty_port_tty_set(port, tty);
10438c2ecf20Sopenharmony_ci	return 0;
10448c2ecf20Sopenharmony_ci}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci/*
10478c2ecf20Sopenharmony_ci * This routine is called when the 3270 tty is closed. We wait
10488c2ecf20Sopenharmony_ci * for the remaining request to be completed. Then we clean up.
10498c2ecf20Sopenharmony_ci */
10508c2ecf20Sopenharmony_cistatic void
10518c2ecf20Sopenharmony_citty3270_close(struct tty_struct *tty, struct file * filp)
10528c2ecf20Sopenharmony_ci{
10538c2ecf20Sopenharmony_ci	struct tty3270 *tp = tty->driver_data;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	if (tty->count > 1)
10568c2ecf20Sopenharmony_ci		return;
10578c2ecf20Sopenharmony_ci	if (tp)
10588c2ecf20Sopenharmony_ci		tty_port_tty_set(&tp->port, NULL);
10598c2ecf20Sopenharmony_ci}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_cistatic void tty3270_cleanup(struct tty_struct *tty)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	struct tty3270 *tp = tty->driver_data;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	if (tp) {
10668c2ecf20Sopenharmony_ci		tty->driver_data = NULL;
10678c2ecf20Sopenharmony_ci		raw3270_put_view(&tp->view);
10688c2ecf20Sopenharmony_ci	}
10698c2ecf20Sopenharmony_ci}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci/*
10728c2ecf20Sopenharmony_ci * We always have room.
10738c2ecf20Sopenharmony_ci */
10748c2ecf20Sopenharmony_cistatic int
10758c2ecf20Sopenharmony_citty3270_write_room(struct tty_struct *tty)
10768c2ecf20Sopenharmony_ci{
10778c2ecf20Sopenharmony_ci	return INT_MAX;
10788c2ecf20Sopenharmony_ci}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci/*
10818c2ecf20Sopenharmony_ci * Insert character into the screen at the current position with the
10828c2ecf20Sopenharmony_ci * current color and highlight. This function does NOT do cursor movement.
10838c2ecf20Sopenharmony_ci */
10848c2ecf20Sopenharmony_cistatic void tty3270_put_character(struct tty3270 *tp, char ch)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	struct tty3270_line *line;
10878c2ecf20Sopenharmony_ci	struct tty3270_cell *cell;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	line = tp->screen + tp->cy;
10908c2ecf20Sopenharmony_ci	if (line->len <= tp->cx) {
10918c2ecf20Sopenharmony_ci		while (line->len < tp->cx) {
10928c2ecf20Sopenharmony_ci			cell = line->cells + line->len;
10938c2ecf20Sopenharmony_ci			cell->character = tp->view.ascebc[' '];
10948c2ecf20Sopenharmony_ci			cell->highlight = tp->highlight;
10958c2ecf20Sopenharmony_ci			cell->f_color = tp->f_color;
10968c2ecf20Sopenharmony_ci			line->len++;
10978c2ecf20Sopenharmony_ci		}
10988c2ecf20Sopenharmony_ci		line->len++;
10998c2ecf20Sopenharmony_ci	}
11008c2ecf20Sopenharmony_ci	cell = line->cells + tp->cx;
11018c2ecf20Sopenharmony_ci	cell->character = tp->view.ascebc[(unsigned int) ch];
11028c2ecf20Sopenharmony_ci	cell->highlight = tp->highlight;
11038c2ecf20Sopenharmony_ci	cell->f_color = tp->f_color;
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci/*
11078c2ecf20Sopenharmony_ci * Convert a tty3270_line to a 3270 data fragment usable for output.
11088c2ecf20Sopenharmony_ci */
11098c2ecf20Sopenharmony_cistatic void
11108c2ecf20Sopenharmony_citty3270_convert_line(struct tty3270 *tp, int line_nr)
11118c2ecf20Sopenharmony_ci{
11128c2ecf20Sopenharmony_ci	struct tty3270_line *line;
11138c2ecf20Sopenharmony_ci	struct tty3270_cell *cell;
11148c2ecf20Sopenharmony_ci	struct string *s, *n;
11158c2ecf20Sopenharmony_ci	unsigned char highlight;
11168c2ecf20Sopenharmony_ci	unsigned char f_color;
11178c2ecf20Sopenharmony_ci	char *cp;
11188c2ecf20Sopenharmony_ci	int flen, i;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	/* Determine how long the fragment will be. */
11218c2ecf20Sopenharmony_ci	flen = 3;		/* Prefix (TO_SBA). */
11228c2ecf20Sopenharmony_ci	line = tp->screen + line_nr;
11238c2ecf20Sopenharmony_ci	flen += line->len;
11248c2ecf20Sopenharmony_ci	highlight = TAX_RESET;
11258c2ecf20Sopenharmony_ci	f_color = TAC_RESET;
11268c2ecf20Sopenharmony_ci	for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
11278c2ecf20Sopenharmony_ci		if (cell->highlight != highlight) {
11288c2ecf20Sopenharmony_ci			flen += 3;	/* TO_SA to switch highlight. */
11298c2ecf20Sopenharmony_ci			highlight = cell->highlight;
11308c2ecf20Sopenharmony_ci		}
11318c2ecf20Sopenharmony_ci		if (cell->f_color != f_color) {
11328c2ecf20Sopenharmony_ci			flen += 3;	/* TO_SA to switch color. */
11338c2ecf20Sopenharmony_ci			f_color = cell->f_color;
11348c2ecf20Sopenharmony_ci		}
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci	if (highlight != TAX_RESET)
11378c2ecf20Sopenharmony_ci		flen += 3;	/* TO_SA to reset hightlight. */
11388c2ecf20Sopenharmony_ci	if (f_color != TAC_RESET)
11398c2ecf20Sopenharmony_ci		flen += 3;	/* TO_SA to reset color. */
11408c2ecf20Sopenharmony_ci	if (line->len < tp->view.cols)
11418c2ecf20Sopenharmony_ci		flen += 4;	/* Postfix (TO_RA). */
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	/* Find the line in the list. */
11448c2ecf20Sopenharmony_ci	i = tp->view.rows - 2 - line_nr;
11458c2ecf20Sopenharmony_ci	list_for_each_entry_reverse(s, &tp->lines, list)
11468c2ecf20Sopenharmony_ci		if (--i <= 0)
11478c2ecf20Sopenharmony_ci			break;
11488c2ecf20Sopenharmony_ci	/*
11498c2ecf20Sopenharmony_ci	 * Check if the line needs to get reallocated.
11508c2ecf20Sopenharmony_ci	 */
11518c2ecf20Sopenharmony_ci	if (s->len != flen) {
11528c2ecf20Sopenharmony_ci		/* Reallocate string. */
11538c2ecf20Sopenharmony_ci		n = tty3270_alloc_string(tp, flen);
11548c2ecf20Sopenharmony_ci		list_add(&n->list, &s->list);
11558c2ecf20Sopenharmony_ci		list_del_init(&s->list);
11568c2ecf20Sopenharmony_ci		if (!list_empty(&s->update))
11578c2ecf20Sopenharmony_ci			list_del_init(&s->update);
11588c2ecf20Sopenharmony_ci		free_string(&tp->freemem, s);
11598c2ecf20Sopenharmony_ci		s = n;
11608c2ecf20Sopenharmony_ci	}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	/* Write 3270 data fragment. */
11638c2ecf20Sopenharmony_ci	cp = s->string;
11648c2ecf20Sopenharmony_ci	*cp++ = TO_SBA;
11658c2ecf20Sopenharmony_ci	*cp++ = 0;
11668c2ecf20Sopenharmony_ci	*cp++ = 0;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	highlight = TAX_RESET;
11698c2ecf20Sopenharmony_ci	f_color = TAC_RESET;
11708c2ecf20Sopenharmony_ci	for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
11718c2ecf20Sopenharmony_ci		if (cell->highlight != highlight) {
11728c2ecf20Sopenharmony_ci			*cp++ = TO_SA;
11738c2ecf20Sopenharmony_ci			*cp++ = TAT_EXTHI;
11748c2ecf20Sopenharmony_ci			*cp++ = cell->highlight;
11758c2ecf20Sopenharmony_ci			highlight = cell->highlight;
11768c2ecf20Sopenharmony_ci		}
11778c2ecf20Sopenharmony_ci		if (cell->f_color != f_color) {
11788c2ecf20Sopenharmony_ci			*cp++ = TO_SA;
11798c2ecf20Sopenharmony_ci			*cp++ = TAT_COLOR;
11808c2ecf20Sopenharmony_ci			*cp++ = cell->f_color;
11818c2ecf20Sopenharmony_ci			f_color = cell->f_color;
11828c2ecf20Sopenharmony_ci		}
11838c2ecf20Sopenharmony_ci		*cp++ = cell->character;
11848c2ecf20Sopenharmony_ci	}
11858c2ecf20Sopenharmony_ci	if (highlight != TAX_RESET) {
11868c2ecf20Sopenharmony_ci		*cp++ = TO_SA;
11878c2ecf20Sopenharmony_ci		*cp++ = TAT_EXTHI;
11888c2ecf20Sopenharmony_ci		*cp++ = TAX_RESET;
11898c2ecf20Sopenharmony_ci	}
11908c2ecf20Sopenharmony_ci	if (f_color != TAC_RESET) {
11918c2ecf20Sopenharmony_ci		*cp++ = TO_SA;
11928c2ecf20Sopenharmony_ci		*cp++ = TAT_COLOR;
11938c2ecf20Sopenharmony_ci		*cp++ = TAC_RESET;
11948c2ecf20Sopenharmony_ci	}
11958c2ecf20Sopenharmony_ci	if (line->len < tp->view.cols) {
11968c2ecf20Sopenharmony_ci		*cp++ = TO_RA;
11978c2ecf20Sopenharmony_ci		*cp++ = 0;
11988c2ecf20Sopenharmony_ci		*cp++ = 0;
11998c2ecf20Sopenharmony_ci		*cp++ = 0;
12008c2ecf20Sopenharmony_ci	}
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	if (tp->nr_up + line_nr < tp->view.rows - 2) {
12038c2ecf20Sopenharmony_ci		/* Line is currently visible on screen. */
12048c2ecf20Sopenharmony_ci		tty3270_update_string(tp, s, line_nr);
12058c2ecf20Sopenharmony_ci		/* Add line to update list. */
12068c2ecf20Sopenharmony_ci		if (list_empty(&s->update)) {
12078c2ecf20Sopenharmony_ci			list_add_tail(&s->update, &tp->update);
12088c2ecf20Sopenharmony_ci			tp->update_flags |= TTY_UPDATE_LIST;
12098c2ecf20Sopenharmony_ci		}
12108c2ecf20Sopenharmony_ci	}
12118c2ecf20Sopenharmony_ci}
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci/*
12148c2ecf20Sopenharmony_ci * Do carriage return.
12158c2ecf20Sopenharmony_ci */
12168c2ecf20Sopenharmony_cistatic void
12178c2ecf20Sopenharmony_citty3270_cr(struct tty3270 *tp)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	tp->cx = 0;
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci/*
12238c2ecf20Sopenharmony_ci * Do line feed.
12248c2ecf20Sopenharmony_ci */
12258c2ecf20Sopenharmony_cistatic void
12268c2ecf20Sopenharmony_citty3270_lf(struct tty3270 *tp)
12278c2ecf20Sopenharmony_ci{
12288c2ecf20Sopenharmony_ci	struct tty3270_line temp;
12298c2ecf20Sopenharmony_ci	int i;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	tty3270_convert_line(tp, tp->cy);
12328c2ecf20Sopenharmony_ci	if (tp->cy < tp->view.rows - 3) {
12338c2ecf20Sopenharmony_ci		tp->cy++;
12348c2ecf20Sopenharmony_ci		return;
12358c2ecf20Sopenharmony_ci	}
12368c2ecf20Sopenharmony_ci	/* Last line just filled up. Add new, blank line. */
12378c2ecf20Sopenharmony_ci	tty3270_blank_line(tp);
12388c2ecf20Sopenharmony_ci	temp = tp->screen[0];
12398c2ecf20Sopenharmony_ci	temp.len = 0;
12408c2ecf20Sopenharmony_ci	for (i = 0; i < tp->view.rows - 3; i++)
12418c2ecf20Sopenharmony_ci		tp->screen[i] = tp->screen[i+1];
12428c2ecf20Sopenharmony_ci	tp->screen[tp->view.rows - 3] = temp;
12438c2ecf20Sopenharmony_ci	tty3270_rebuild_update(tp);
12448c2ecf20Sopenharmony_ci}
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_cistatic void
12478c2ecf20Sopenharmony_citty3270_ri(struct tty3270 *tp)
12488c2ecf20Sopenharmony_ci{
12498c2ecf20Sopenharmony_ci	if (tp->cy > 0) {
12508c2ecf20Sopenharmony_ci	    tty3270_convert_line(tp, tp->cy);
12518c2ecf20Sopenharmony_ci	    tp->cy--;
12528c2ecf20Sopenharmony_ci	}
12538c2ecf20Sopenharmony_ci}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci/*
12568c2ecf20Sopenharmony_ci * Insert characters at current position.
12578c2ecf20Sopenharmony_ci */
12588c2ecf20Sopenharmony_cistatic void
12598c2ecf20Sopenharmony_citty3270_insert_characters(struct tty3270 *tp, int n)
12608c2ecf20Sopenharmony_ci{
12618c2ecf20Sopenharmony_ci	struct tty3270_line *line;
12628c2ecf20Sopenharmony_ci	int k;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	line = tp->screen + tp->cy;
12658c2ecf20Sopenharmony_ci	while (line->len < tp->cx) {
12668c2ecf20Sopenharmony_ci		line->cells[line->len].character = tp->view.ascebc[' '];
12678c2ecf20Sopenharmony_ci		line->cells[line->len].highlight = TAX_RESET;
12688c2ecf20Sopenharmony_ci		line->cells[line->len].f_color = TAC_RESET;
12698c2ecf20Sopenharmony_ci		line->len++;
12708c2ecf20Sopenharmony_ci	}
12718c2ecf20Sopenharmony_ci	if (n > tp->view.cols - tp->cx)
12728c2ecf20Sopenharmony_ci		n = tp->view.cols - tp->cx;
12738c2ecf20Sopenharmony_ci	k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n);
12748c2ecf20Sopenharmony_ci	while (k--)
12758c2ecf20Sopenharmony_ci		line->cells[tp->cx + n + k] = line->cells[tp->cx + k];
12768c2ecf20Sopenharmony_ci	line->len += n;
12778c2ecf20Sopenharmony_ci	if (line->len > tp->view.cols)
12788c2ecf20Sopenharmony_ci		line->len = tp->view.cols;
12798c2ecf20Sopenharmony_ci	while (n-- > 0) {
12808c2ecf20Sopenharmony_ci		line->cells[tp->cx + n].character = tp->view.ascebc[' '];
12818c2ecf20Sopenharmony_ci		line->cells[tp->cx + n].highlight = tp->highlight;
12828c2ecf20Sopenharmony_ci		line->cells[tp->cx + n].f_color = tp->f_color;
12838c2ecf20Sopenharmony_ci	}
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci/*
12878c2ecf20Sopenharmony_ci * Delete characters at current position.
12888c2ecf20Sopenharmony_ci */
12898c2ecf20Sopenharmony_cistatic void
12908c2ecf20Sopenharmony_citty3270_delete_characters(struct tty3270 *tp, int n)
12918c2ecf20Sopenharmony_ci{
12928c2ecf20Sopenharmony_ci	struct tty3270_line *line;
12938c2ecf20Sopenharmony_ci	int i;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	line = tp->screen + tp->cy;
12968c2ecf20Sopenharmony_ci	if (line->len <= tp->cx)
12978c2ecf20Sopenharmony_ci		return;
12988c2ecf20Sopenharmony_ci	if (line->len - tp->cx <= n) {
12998c2ecf20Sopenharmony_ci		line->len = tp->cx;
13008c2ecf20Sopenharmony_ci		return;
13018c2ecf20Sopenharmony_ci	}
13028c2ecf20Sopenharmony_ci	for (i = tp->cx; i + n < line->len; i++)
13038c2ecf20Sopenharmony_ci		line->cells[i] = line->cells[i + n];
13048c2ecf20Sopenharmony_ci	line->len -= n;
13058c2ecf20Sopenharmony_ci}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci/*
13088c2ecf20Sopenharmony_ci * Erase characters at current position.
13098c2ecf20Sopenharmony_ci */
13108c2ecf20Sopenharmony_cistatic void
13118c2ecf20Sopenharmony_citty3270_erase_characters(struct tty3270 *tp, int n)
13128c2ecf20Sopenharmony_ci{
13138c2ecf20Sopenharmony_ci	struct tty3270_line *line;
13148c2ecf20Sopenharmony_ci	struct tty3270_cell *cell;
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	line = tp->screen + tp->cy;
13178c2ecf20Sopenharmony_ci	while (line->len > tp->cx && n-- > 0) {
13188c2ecf20Sopenharmony_ci		cell = line->cells + tp->cx++;
13198c2ecf20Sopenharmony_ci		cell->character = ' ';
13208c2ecf20Sopenharmony_ci		cell->highlight = TAX_RESET;
13218c2ecf20Sopenharmony_ci		cell->f_color = TAC_RESET;
13228c2ecf20Sopenharmony_ci	}
13238c2ecf20Sopenharmony_ci	tp->cx += n;
13248c2ecf20Sopenharmony_ci	tp->cx = min_t(int, tp->cx, tp->view.cols - 1);
13258c2ecf20Sopenharmony_ci}
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci/*
13288c2ecf20Sopenharmony_ci * Erase line, 3 different cases:
13298c2ecf20Sopenharmony_ci *  Esc [ 0 K	Erase from current position to end of line inclusive
13308c2ecf20Sopenharmony_ci *  Esc [ 1 K	Erase from beginning of line to current position inclusive
13318c2ecf20Sopenharmony_ci *  Esc [ 2 K	Erase entire line (without moving cursor)
13328c2ecf20Sopenharmony_ci */
13338c2ecf20Sopenharmony_cistatic void
13348c2ecf20Sopenharmony_citty3270_erase_line(struct tty3270 *tp, int mode)
13358c2ecf20Sopenharmony_ci{
13368c2ecf20Sopenharmony_ci	struct tty3270_line *line;
13378c2ecf20Sopenharmony_ci	struct tty3270_cell *cell;
13388c2ecf20Sopenharmony_ci	int i;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	line = tp->screen + tp->cy;
13418c2ecf20Sopenharmony_ci	if (mode == 0)
13428c2ecf20Sopenharmony_ci		line->len = tp->cx;
13438c2ecf20Sopenharmony_ci	else if (mode == 1) {
13448c2ecf20Sopenharmony_ci		for (i = 0; i < tp->cx; i++) {
13458c2ecf20Sopenharmony_ci			cell = line->cells + i;
13468c2ecf20Sopenharmony_ci			cell->character = ' ';
13478c2ecf20Sopenharmony_ci			cell->highlight = TAX_RESET;
13488c2ecf20Sopenharmony_ci			cell->f_color = TAC_RESET;
13498c2ecf20Sopenharmony_ci		}
13508c2ecf20Sopenharmony_ci		if (line->len <= tp->cx)
13518c2ecf20Sopenharmony_ci			line->len = tp->cx + 1;
13528c2ecf20Sopenharmony_ci	} else if (mode == 2)
13538c2ecf20Sopenharmony_ci		line->len = 0;
13548c2ecf20Sopenharmony_ci	tty3270_convert_line(tp, tp->cy);
13558c2ecf20Sopenharmony_ci}
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci/*
13588c2ecf20Sopenharmony_ci * Erase display, 3 different cases:
13598c2ecf20Sopenharmony_ci *  Esc [ 0 J	Erase from current position to bottom of screen inclusive
13608c2ecf20Sopenharmony_ci *  Esc [ 1 J	Erase from top of screen to current position inclusive
13618c2ecf20Sopenharmony_ci *  Esc [ 2 J	Erase entire screen (without moving the cursor)
13628c2ecf20Sopenharmony_ci */
13638c2ecf20Sopenharmony_cistatic void
13648c2ecf20Sopenharmony_citty3270_erase_display(struct tty3270 *tp, int mode)
13658c2ecf20Sopenharmony_ci{
13668c2ecf20Sopenharmony_ci	int i;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	if (mode == 0) {
13698c2ecf20Sopenharmony_ci		tty3270_erase_line(tp, 0);
13708c2ecf20Sopenharmony_ci		for (i = tp->cy + 1; i < tp->view.rows - 2; i++) {
13718c2ecf20Sopenharmony_ci			tp->screen[i].len = 0;
13728c2ecf20Sopenharmony_ci			tty3270_convert_line(tp, i);
13738c2ecf20Sopenharmony_ci		}
13748c2ecf20Sopenharmony_ci	} else if (mode == 1) {
13758c2ecf20Sopenharmony_ci		for (i = 0; i < tp->cy; i++) {
13768c2ecf20Sopenharmony_ci			tp->screen[i].len = 0;
13778c2ecf20Sopenharmony_ci			tty3270_convert_line(tp, i);
13788c2ecf20Sopenharmony_ci		}
13798c2ecf20Sopenharmony_ci		tty3270_erase_line(tp, 1);
13808c2ecf20Sopenharmony_ci	} else if (mode == 2) {
13818c2ecf20Sopenharmony_ci		for (i = 0; i < tp->view.rows - 2; i++) {
13828c2ecf20Sopenharmony_ci			tp->screen[i].len = 0;
13838c2ecf20Sopenharmony_ci			tty3270_convert_line(tp, i);
13848c2ecf20Sopenharmony_ci		}
13858c2ecf20Sopenharmony_ci	}
13868c2ecf20Sopenharmony_ci	tty3270_rebuild_update(tp);
13878c2ecf20Sopenharmony_ci}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci/*
13908c2ecf20Sopenharmony_ci * Set attributes found in an escape sequence.
13918c2ecf20Sopenharmony_ci *  Esc [ <attr> ; <attr> ; ... m
13928c2ecf20Sopenharmony_ci */
13938c2ecf20Sopenharmony_cistatic void
13948c2ecf20Sopenharmony_citty3270_set_attributes(struct tty3270 *tp)
13958c2ecf20Sopenharmony_ci{
13968c2ecf20Sopenharmony_ci	static unsigned char f_colors[] = {
13978c2ecf20Sopenharmony_ci		TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE,
13988c2ecf20Sopenharmony_ci		TAC_PINK, TAC_TURQ, TAC_WHITE, 0, TAC_DEFAULT
13998c2ecf20Sopenharmony_ci	};
14008c2ecf20Sopenharmony_ci	int i, attr;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	for (i = 0; i <= tp->esc_npar; i++) {
14038c2ecf20Sopenharmony_ci		attr = tp->esc_par[i];
14048c2ecf20Sopenharmony_ci		switch (attr) {
14058c2ecf20Sopenharmony_ci		case 0:		/* Reset */
14068c2ecf20Sopenharmony_ci			tp->highlight = TAX_RESET;
14078c2ecf20Sopenharmony_ci			tp->f_color = TAC_RESET;
14088c2ecf20Sopenharmony_ci			break;
14098c2ecf20Sopenharmony_ci		/* Highlight. */
14108c2ecf20Sopenharmony_ci		case 4:		/* Start underlining. */
14118c2ecf20Sopenharmony_ci			tp->highlight = TAX_UNDER;
14128c2ecf20Sopenharmony_ci			break;
14138c2ecf20Sopenharmony_ci		case 5:		/* Start blink. */
14148c2ecf20Sopenharmony_ci			tp->highlight = TAX_BLINK;
14158c2ecf20Sopenharmony_ci			break;
14168c2ecf20Sopenharmony_ci		case 7:		/* Start reverse. */
14178c2ecf20Sopenharmony_ci			tp->highlight = TAX_REVER;
14188c2ecf20Sopenharmony_ci			break;
14198c2ecf20Sopenharmony_ci		case 24:	/* End underlining */
14208c2ecf20Sopenharmony_ci			if (tp->highlight == TAX_UNDER)
14218c2ecf20Sopenharmony_ci				tp->highlight = TAX_RESET;
14228c2ecf20Sopenharmony_ci			break;
14238c2ecf20Sopenharmony_ci		case 25:	/* End blink. */
14248c2ecf20Sopenharmony_ci			if (tp->highlight == TAX_BLINK)
14258c2ecf20Sopenharmony_ci				tp->highlight = TAX_RESET;
14268c2ecf20Sopenharmony_ci			break;
14278c2ecf20Sopenharmony_ci		case 27:	/* End reverse. */
14288c2ecf20Sopenharmony_ci			if (tp->highlight == TAX_REVER)
14298c2ecf20Sopenharmony_ci				tp->highlight = TAX_RESET;
14308c2ecf20Sopenharmony_ci			break;
14318c2ecf20Sopenharmony_ci		/* Foreground color. */
14328c2ecf20Sopenharmony_ci		case 30:	/* Black */
14338c2ecf20Sopenharmony_ci		case 31:	/* Red */
14348c2ecf20Sopenharmony_ci		case 32:	/* Green */
14358c2ecf20Sopenharmony_ci		case 33:	/* Yellow */
14368c2ecf20Sopenharmony_ci		case 34:	/* Blue */
14378c2ecf20Sopenharmony_ci		case 35:	/* Magenta */
14388c2ecf20Sopenharmony_ci		case 36:	/* Cyan */
14398c2ecf20Sopenharmony_ci		case 37:	/* White */
14408c2ecf20Sopenharmony_ci		case 39:	/* Black */
14418c2ecf20Sopenharmony_ci			tp->f_color = f_colors[attr - 30];
14428c2ecf20Sopenharmony_ci			break;
14438c2ecf20Sopenharmony_ci		}
14448c2ecf20Sopenharmony_ci	}
14458c2ecf20Sopenharmony_ci}
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_cistatic inline int
14488c2ecf20Sopenharmony_citty3270_getpar(struct tty3270 *tp, int ix)
14498c2ecf20Sopenharmony_ci{
14508c2ecf20Sopenharmony_ci	return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1;
14518c2ecf20Sopenharmony_ci}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_cistatic void
14548c2ecf20Sopenharmony_citty3270_goto_xy(struct tty3270 *tp, int cx, int cy)
14558c2ecf20Sopenharmony_ci{
14568c2ecf20Sopenharmony_ci	int max_cx = max(0, cx);
14578c2ecf20Sopenharmony_ci	int max_cy = max(0, cy);
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	tp->cx = min_t(int, tp->view.cols - 1, max_cx);
14608c2ecf20Sopenharmony_ci	cy = min_t(int, tp->view.rows - 3, max_cy);
14618c2ecf20Sopenharmony_ci	if (cy != tp->cy) {
14628c2ecf20Sopenharmony_ci		tty3270_convert_line(tp, tp->cy);
14638c2ecf20Sopenharmony_ci		tp->cy = cy;
14648c2ecf20Sopenharmony_ci	}
14658c2ecf20Sopenharmony_ci}
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci/*
14688c2ecf20Sopenharmony_ci * Process escape sequences. Known sequences:
14698c2ecf20Sopenharmony_ci *  Esc 7			Save Cursor Position
14708c2ecf20Sopenharmony_ci *  Esc 8			Restore Cursor Position
14718c2ecf20Sopenharmony_ci *  Esc [ Pn ; Pn ; .. m	Set attributes
14728c2ecf20Sopenharmony_ci *  Esc [ Pn ; Pn H		Cursor Position
14738c2ecf20Sopenharmony_ci *  Esc [ Pn ; Pn f		Cursor Position
14748c2ecf20Sopenharmony_ci *  Esc [ Pn A			Cursor Up
14758c2ecf20Sopenharmony_ci *  Esc [ Pn B			Cursor Down
14768c2ecf20Sopenharmony_ci *  Esc [ Pn C			Cursor Forward
14778c2ecf20Sopenharmony_ci *  Esc [ Pn D			Cursor Backward
14788c2ecf20Sopenharmony_ci *  Esc [ Pn G			Cursor Horizontal Absolute
14798c2ecf20Sopenharmony_ci *  Esc [ Pn X			Erase Characters
14808c2ecf20Sopenharmony_ci *  Esc [ Ps J			Erase in Display
14818c2ecf20Sopenharmony_ci *  Esc [ Ps K			Erase in Line
14828c2ecf20Sopenharmony_ci * // FIXME: add all the new ones.
14838c2ecf20Sopenharmony_ci *
14848c2ecf20Sopenharmony_ci *  Pn is a numeric parameter, a string of zero or more decimal digits.
14858c2ecf20Sopenharmony_ci *  Ps is a selective parameter.
14868c2ecf20Sopenharmony_ci */
14878c2ecf20Sopenharmony_cistatic void
14888c2ecf20Sopenharmony_citty3270_escape_sequence(struct tty3270 *tp, char ch)
14898c2ecf20Sopenharmony_ci{
14908c2ecf20Sopenharmony_ci	enum { ESnormal, ESesc, ESsquare, ESgetpars };
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	if (tp->esc_state == ESnormal) {
14938c2ecf20Sopenharmony_ci		if (ch == 0x1b)
14948c2ecf20Sopenharmony_ci			/* Starting new escape sequence. */
14958c2ecf20Sopenharmony_ci			tp->esc_state = ESesc;
14968c2ecf20Sopenharmony_ci		return;
14978c2ecf20Sopenharmony_ci	}
14988c2ecf20Sopenharmony_ci	if (tp->esc_state == ESesc) {
14998c2ecf20Sopenharmony_ci		tp->esc_state = ESnormal;
15008c2ecf20Sopenharmony_ci		switch (ch) {
15018c2ecf20Sopenharmony_ci		case '[':
15028c2ecf20Sopenharmony_ci			tp->esc_state = ESsquare;
15038c2ecf20Sopenharmony_ci			break;
15048c2ecf20Sopenharmony_ci		case 'E':
15058c2ecf20Sopenharmony_ci			tty3270_cr(tp);
15068c2ecf20Sopenharmony_ci			tty3270_lf(tp);
15078c2ecf20Sopenharmony_ci			break;
15088c2ecf20Sopenharmony_ci		case 'M':
15098c2ecf20Sopenharmony_ci			tty3270_ri(tp);
15108c2ecf20Sopenharmony_ci			break;
15118c2ecf20Sopenharmony_ci		case 'D':
15128c2ecf20Sopenharmony_ci			tty3270_lf(tp);
15138c2ecf20Sopenharmony_ci			break;
15148c2ecf20Sopenharmony_ci		case 'Z':		/* Respond ID. */
15158c2ecf20Sopenharmony_ci			kbd_puts_queue(&tp->port, "\033[?6c");
15168c2ecf20Sopenharmony_ci			break;
15178c2ecf20Sopenharmony_ci		case '7':		/* Save cursor position. */
15188c2ecf20Sopenharmony_ci			tp->saved_cx = tp->cx;
15198c2ecf20Sopenharmony_ci			tp->saved_cy = tp->cy;
15208c2ecf20Sopenharmony_ci			tp->saved_highlight = tp->highlight;
15218c2ecf20Sopenharmony_ci			tp->saved_f_color = tp->f_color;
15228c2ecf20Sopenharmony_ci			break;
15238c2ecf20Sopenharmony_ci		case '8':		/* Restore cursor position. */
15248c2ecf20Sopenharmony_ci			tty3270_convert_line(tp, tp->cy);
15258c2ecf20Sopenharmony_ci			tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
15268c2ecf20Sopenharmony_ci			tp->highlight = tp->saved_highlight;
15278c2ecf20Sopenharmony_ci			tp->f_color = tp->saved_f_color;
15288c2ecf20Sopenharmony_ci			break;
15298c2ecf20Sopenharmony_ci		case 'c':		/* Reset terminal. */
15308c2ecf20Sopenharmony_ci			tp->cx = tp->saved_cx = 0;
15318c2ecf20Sopenharmony_ci			tp->cy = tp->saved_cy = 0;
15328c2ecf20Sopenharmony_ci			tp->highlight = tp->saved_highlight = TAX_RESET;
15338c2ecf20Sopenharmony_ci			tp->f_color = tp->saved_f_color = TAC_RESET;
15348c2ecf20Sopenharmony_ci			tty3270_erase_display(tp, 2);
15358c2ecf20Sopenharmony_ci			break;
15368c2ecf20Sopenharmony_ci		}
15378c2ecf20Sopenharmony_ci		return;
15388c2ecf20Sopenharmony_ci	}
15398c2ecf20Sopenharmony_ci	if (tp->esc_state == ESsquare) {
15408c2ecf20Sopenharmony_ci		tp->esc_state = ESgetpars;
15418c2ecf20Sopenharmony_ci		memset(tp->esc_par, 0, sizeof(tp->esc_par));
15428c2ecf20Sopenharmony_ci		tp->esc_npar = 0;
15438c2ecf20Sopenharmony_ci		tp->esc_ques = (ch == '?');
15448c2ecf20Sopenharmony_ci		if (tp->esc_ques)
15458c2ecf20Sopenharmony_ci			return;
15468c2ecf20Sopenharmony_ci	}
15478c2ecf20Sopenharmony_ci	if (tp->esc_state == ESgetpars) {
15488c2ecf20Sopenharmony_ci		if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) {
15498c2ecf20Sopenharmony_ci			tp->esc_npar++;
15508c2ecf20Sopenharmony_ci			return;
15518c2ecf20Sopenharmony_ci		}
15528c2ecf20Sopenharmony_ci		if (ch >= '0' && ch <= '9') {
15538c2ecf20Sopenharmony_ci			tp->esc_par[tp->esc_npar] *= 10;
15548c2ecf20Sopenharmony_ci			tp->esc_par[tp->esc_npar] += ch - '0';
15558c2ecf20Sopenharmony_ci			return;
15568c2ecf20Sopenharmony_ci		}
15578c2ecf20Sopenharmony_ci	}
15588c2ecf20Sopenharmony_ci	tp->esc_state = ESnormal;
15598c2ecf20Sopenharmony_ci	if (ch == 'n' && !tp->esc_ques) {
15608c2ecf20Sopenharmony_ci		if (tp->esc_par[0] == 5)		/* Status report. */
15618c2ecf20Sopenharmony_ci			kbd_puts_queue(&tp->port, "\033[0n");
15628c2ecf20Sopenharmony_ci		else if (tp->esc_par[0] == 6) {	/* Cursor report. */
15638c2ecf20Sopenharmony_ci			char buf[40];
15648c2ecf20Sopenharmony_ci			sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1);
15658c2ecf20Sopenharmony_ci			kbd_puts_queue(&tp->port, buf);
15668c2ecf20Sopenharmony_ci		}
15678c2ecf20Sopenharmony_ci		return;
15688c2ecf20Sopenharmony_ci	}
15698c2ecf20Sopenharmony_ci	if (tp->esc_ques)
15708c2ecf20Sopenharmony_ci		return;
15718c2ecf20Sopenharmony_ci	switch (ch) {
15728c2ecf20Sopenharmony_ci	case 'm':
15738c2ecf20Sopenharmony_ci		tty3270_set_attributes(tp);
15748c2ecf20Sopenharmony_ci		break;
15758c2ecf20Sopenharmony_ci	case 'H':	/* Set cursor position. */
15768c2ecf20Sopenharmony_ci	case 'f':
15778c2ecf20Sopenharmony_ci		tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1,
15788c2ecf20Sopenharmony_ci				tty3270_getpar(tp, 0) - 1);
15798c2ecf20Sopenharmony_ci		break;
15808c2ecf20Sopenharmony_ci	case 'd':	/* Set y position. */
15818c2ecf20Sopenharmony_ci		tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1);
15828c2ecf20Sopenharmony_ci		break;
15838c2ecf20Sopenharmony_ci	case 'A':	/* Cursor up. */
15848c2ecf20Sopenharmony_ci	case 'F':
15858c2ecf20Sopenharmony_ci		tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0));
15868c2ecf20Sopenharmony_ci		break;
15878c2ecf20Sopenharmony_ci	case 'B':	/* Cursor down. */
15888c2ecf20Sopenharmony_ci	case 'e':
15898c2ecf20Sopenharmony_ci	case 'E':
15908c2ecf20Sopenharmony_ci		tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0));
15918c2ecf20Sopenharmony_ci		break;
15928c2ecf20Sopenharmony_ci	case 'C':	/* Cursor forward. */
15938c2ecf20Sopenharmony_ci	case 'a':
15948c2ecf20Sopenharmony_ci		tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy);
15958c2ecf20Sopenharmony_ci		break;
15968c2ecf20Sopenharmony_ci	case 'D':	/* Cursor backward. */
15978c2ecf20Sopenharmony_ci		tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy);
15988c2ecf20Sopenharmony_ci		break;
15998c2ecf20Sopenharmony_ci	case 'G':	/* Set x position. */
16008c2ecf20Sopenharmony_ci	case '`':
16018c2ecf20Sopenharmony_ci		tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy);
16028c2ecf20Sopenharmony_ci		break;
16038c2ecf20Sopenharmony_ci	case 'X':	/* Erase Characters. */
16048c2ecf20Sopenharmony_ci		tty3270_erase_characters(tp, tty3270_getpar(tp, 0));
16058c2ecf20Sopenharmony_ci		break;
16068c2ecf20Sopenharmony_ci	case 'J':	/* Erase display. */
16078c2ecf20Sopenharmony_ci		tty3270_erase_display(tp, tp->esc_par[0]);
16088c2ecf20Sopenharmony_ci		break;
16098c2ecf20Sopenharmony_ci	case 'K':	/* Erase line. */
16108c2ecf20Sopenharmony_ci		tty3270_erase_line(tp, tp->esc_par[0]);
16118c2ecf20Sopenharmony_ci		break;
16128c2ecf20Sopenharmony_ci	case 'P':	/* Delete characters. */
16138c2ecf20Sopenharmony_ci		tty3270_delete_characters(tp, tty3270_getpar(tp, 0));
16148c2ecf20Sopenharmony_ci		break;
16158c2ecf20Sopenharmony_ci	case '@':	/* Insert characters. */
16168c2ecf20Sopenharmony_ci		tty3270_insert_characters(tp, tty3270_getpar(tp, 0));
16178c2ecf20Sopenharmony_ci		break;
16188c2ecf20Sopenharmony_ci	case 's':	/* Save cursor position. */
16198c2ecf20Sopenharmony_ci		tp->saved_cx = tp->cx;
16208c2ecf20Sopenharmony_ci		tp->saved_cy = tp->cy;
16218c2ecf20Sopenharmony_ci		tp->saved_highlight = tp->highlight;
16228c2ecf20Sopenharmony_ci		tp->saved_f_color = tp->f_color;
16238c2ecf20Sopenharmony_ci		break;
16248c2ecf20Sopenharmony_ci	case 'u':	/* Restore cursor position. */
16258c2ecf20Sopenharmony_ci		tty3270_convert_line(tp, tp->cy);
16268c2ecf20Sopenharmony_ci		tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
16278c2ecf20Sopenharmony_ci		tp->highlight = tp->saved_highlight;
16288c2ecf20Sopenharmony_ci		tp->f_color = tp->saved_f_color;
16298c2ecf20Sopenharmony_ci		break;
16308c2ecf20Sopenharmony_ci	}
16318c2ecf20Sopenharmony_ci}
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci/*
16348c2ecf20Sopenharmony_ci * String write routine for 3270 ttys
16358c2ecf20Sopenharmony_ci */
16368c2ecf20Sopenharmony_cistatic void
16378c2ecf20Sopenharmony_citty3270_do_write(struct tty3270 *tp, struct tty_struct *tty,
16388c2ecf20Sopenharmony_ci		const unsigned char *buf, int count)
16398c2ecf20Sopenharmony_ci{
16408c2ecf20Sopenharmony_ci	int i_msg, i;
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	spin_lock_bh(&tp->view.lock);
16438c2ecf20Sopenharmony_ci	for (i_msg = 0; !tty->stopped && i_msg < count; i_msg++) {
16448c2ecf20Sopenharmony_ci		if (tp->esc_state != 0) {
16458c2ecf20Sopenharmony_ci			/* Continue escape sequence. */
16468c2ecf20Sopenharmony_ci			tty3270_escape_sequence(tp, buf[i_msg]);
16478c2ecf20Sopenharmony_ci			continue;
16488c2ecf20Sopenharmony_ci		}
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci		switch (buf[i_msg]) {
16518c2ecf20Sopenharmony_ci		case 0x07:		/* '\a' -- Alarm */
16528c2ecf20Sopenharmony_ci			tp->wcc |= TW_PLUSALARM;
16538c2ecf20Sopenharmony_ci			break;
16548c2ecf20Sopenharmony_ci		case 0x08:		/* Backspace. */
16558c2ecf20Sopenharmony_ci			if (tp->cx > 0) {
16568c2ecf20Sopenharmony_ci				tp->cx--;
16578c2ecf20Sopenharmony_ci				tty3270_put_character(tp, ' ');
16588c2ecf20Sopenharmony_ci			}
16598c2ecf20Sopenharmony_ci			break;
16608c2ecf20Sopenharmony_ci		case 0x09:		/* '\t' -- Tabulate */
16618c2ecf20Sopenharmony_ci			for (i = tp->cx % 8; i < 8; i++) {
16628c2ecf20Sopenharmony_ci				if (tp->cx >= tp->view.cols) {
16638c2ecf20Sopenharmony_ci					tty3270_cr(tp);
16648c2ecf20Sopenharmony_ci					tty3270_lf(tp);
16658c2ecf20Sopenharmony_ci					break;
16668c2ecf20Sopenharmony_ci				}
16678c2ecf20Sopenharmony_ci				tty3270_put_character(tp, ' ');
16688c2ecf20Sopenharmony_ci				tp->cx++;
16698c2ecf20Sopenharmony_ci			}
16708c2ecf20Sopenharmony_ci			break;
16718c2ecf20Sopenharmony_ci		case 0x0a:		/* '\n' -- New Line */
16728c2ecf20Sopenharmony_ci			tty3270_cr(tp);
16738c2ecf20Sopenharmony_ci			tty3270_lf(tp);
16748c2ecf20Sopenharmony_ci			break;
16758c2ecf20Sopenharmony_ci		case 0x0c:		/* '\f' -- Form Feed */
16768c2ecf20Sopenharmony_ci			tty3270_erase_display(tp, 2);
16778c2ecf20Sopenharmony_ci			tp->cx = tp->cy = 0;
16788c2ecf20Sopenharmony_ci			break;
16798c2ecf20Sopenharmony_ci		case 0x0d:		/* '\r' -- Carriage Return */
16808c2ecf20Sopenharmony_ci			tp->cx = 0;
16818c2ecf20Sopenharmony_ci			break;
16828c2ecf20Sopenharmony_ci		case 0x0f:		/* SuSE "exit alternate mode" */
16838c2ecf20Sopenharmony_ci			break;
16848c2ecf20Sopenharmony_ci		case 0x1b:		/* Start escape sequence. */
16858c2ecf20Sopenharmony_ci			tty3270_escape_sequence(tp, buf[i_msg]);
16868c2ecf20Sopenharmony_ci			break;
16878c2ecf20Sopenharmony_ci		default:		/* Insert normal character. */
16888c2ecf20Sopenharmony_ci			if (tp->cx >= tp->view.cols) {
16898c2ecf20Sopenharmony_ci				tty3270_cr(tp);
16908c2ecf20Sopenharmony_ci				tty3270_lf(tp);
16918c2ecf20Sopenharmony_ci			}
16928c2ecf20Sopenharmony_ci			tty3270_put_character(tp, buf[i_msg]);
16938c2ecf20Sopenharmony_ci			tp->cx++;
16948c2ecf20Sopenharmony_ci			break;
16958c2ecf20Sopenharmony_ci		}
16968c2ecf20Sopenharmony_ci	}
16978c2ecf20Sopenharmony_ci	/* Convert current line to 3270 data fragment. */
16988c2ecf20Sopenharmony_ci	tty3270_convert_line(tp, tp->cy);
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	/* Setup timer to update display after 1/10 second */
17018c2ecf20Sopenharmony_ci	if (!timer_pending(&tp->timer))
17028c2ecf20Sopenharmony_ci		tty3270_set_timer(tp, HZ/10);
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	spin_unlock_bh(&tp->view.lock);
17058c2ecf20Sopenharmony_ci}
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci/*
17088c2ecf20Sopenharmony_ci * String write routine for 3270 ttys
17098c2ecf20Sopenharmony_ci */
17108c2ecf20Sopenharmony_cistatic int
17118c2ecf20Sopenharmony_citty3270_write(struct tty_struct * tty,
17128c2ecf20Sopenharmony_ci	      const unsigned char *buf, int count)
17138c2ecf20Sopenharmony_ci{
17148c2ecf20Sopenharmony_ci	struct tty3270 *tp;
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	tp = tty->driver_data;
17178c2ecf20Sopenharmony_ci	if (!tp)
17188c2ecf20Sopenharmony_ci		return 0;
17198c2ecf20Sopenharmony_ci	if (tp->char_count > 0) {
17208c2ecf20Sopenharmony_ci		tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);
17218c2ecf20Sopenharmony_ci		tp->char_count = 0;
17228c2ecf20Sopenharmony_ci	}
17238c2ecf20Sopenharmony_ci	tty3270_do_write(tp, tty, buf, count);
17248c2ecf20Sopenharmony_ci	return count;
17258c2ecf20Sopenharmony_ci}
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci/*
17288c2ecf20Sopenharmony_ci * Put single characters to the ttys character buffer
17298c2ecf20Sopenharmony_ci */
17308c2ecf20Sopenharmony_cistatic int tty3270_put_char(struct tty_struct *tty, unsigned char ch)
17318c2ecf20Sopenharmony_ci{
17328c2ecf20Sopenharmony_ci	struct tty3270 *tp;
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	tp = tty->driver_data;
17358c2ecf20Sopenharmony_ci	if (!tp || tp->char_count >= TTY3270_CHAR_BUF_SIZE)
17368c2ecf20Sopenharmony_ci		return 0;
17378c2ecf20Sopenharmony_ci	tp->char_buf[tp->char_count++] = ch;
17388c2ecf20Sopenharmony_ci	return 1;
17398c2ecf20Sopenharmony_ci}
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci/*
17428c2ecf20Sopenharmony_ci * Flush all characters from the ttys characeter buffer put there
17438c2ecf20Sopenharmony_ci * by tty3270_put_char.
17448c2ecf20Sopenharmony_ci */
17458c2ecf20Sopenharmony_cistatic void
17468c2ecf20Sopenharmony_citty3270_flush_chars(struct tty_struct *tty)
17478c2ecf20Sopenharmony_ci{
17488c2ecf20Sopenharmony_ci	struct tty3270 *tp;
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci	tp = tty->driver_data;
17518c2ecf20Sopenharmony_ci	if (!tp)
17528c2ecf20Sopenharmony_ci		return;
17538c2ecf20Sopenharmony_ci	if (tp->char_count > 0) {
17548c2ecf20Sopenharmony_ci		tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);
17558c2ecf20Sopenharmony_ci		tp->char_count = 0;
17568c2ecf20Sopenharmony_ci	}
17578c2ecf20Sopenharmony_ci}
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci/*
17608c2ecf20Sopenharmony_ci * Returns the number of characters in the output buffer. This is
17618c2ecf20Sopenharmony_ci * used in tty_wait_until_sent to wait until all characters have
17628c2ecf20Sopenharmony_ci * appeared on the screen.
17638c2ecf20Sopenharmony_ci */
17648c2ecf20Sopenharmony_cistatic int
17658c2ecf20Sopenharmony_citty3270_chars_in_buffer(struct tty_struct *tty)
17668c2ecf20Sopenharmony_ci{
17678c2ecf20Sopenharmony_ci	return 0;
17688c2ecf20Sopenharmony_ci}
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_cistatic void
17718c2ecf20Sopenharmony_citty3270_flush_buffer(struct tty_struct *tty)
17728c2ecf20Sopenharmony_ci{
17738c2ecf20Sopenharmony_ci}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci/*
17768c2ecf20Sopenharmony_ci * Check for visible/invisible input switches
17778c2ecf20Sopenharmony_ci */
17788c2ecf20Sopenharmony_cistatic void
17798c2ecf20Sopenharmony_citty3270_set_termios(struct tty_struct *tty, struct ktermios *old)
17808c2ecf20Sopenharmony_ci{
17818c2ecf20Sopenharmony_ci	struct tty3270 *tp;
17828c2ecf20Sopenharmony_ci	int new;
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	tp = tty->driver_data;
17858c2ecf20Sopenharmony_ci	if (!tp)
17868c2ecf20Sopenharmony_ci		return;
17878c2ecf20Sopenharmony_ci	spin_lock_bh(&tp->view.lock);
17888c2ecf20Sopenharmony_ci	if (L_ICANON(tty)) {
17898c2ecf20Sopenharmony_ci		new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN;
17908c2ecf20Sopenharmony_ci		if (new != tp->inattr) {
17918c2ecf20Sopenharmony_ci			tp->inattr = new;
17928c2ecf20Sopenharmony_ci			tty3270_update_prompt(tp, NULL, 0);
17938c2ecf20Sopenharmony_ci			tty3270_set_timer(tp, 1);
17948c2ecf20Sopenharmony_ci		}
17958c2ecf20Sopenharmony_ci	}
17968c2ecf20Sopenharmony_ci	spin_unlock_bh(&tp->view.lock);
17978c2ecf20Sopenharmony_ci}
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci/*
18008c2ecf20Sopenharmony_ci * Disable reading from a 3270 tty
18018c2ecf20Sopenharmony_ci */
18028c2ecf20Sopenharmony_cistatic void
18038c2ecf20Sopenharmony_citty3270_throttle(struct tty_struct * tty)
18048c2ecf20Sopenharmony_ci{
18058c2ecf20Sopenharmony_ci	struct tty3270 *tp;
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	tp = tty->driver_data;
18088c2ecf20Sopenharmony_ci	if (!tp)
18098c2ecf20Sopenharmony_ci		return;
18108c2ecf20Sopenharmony_ci	tp->throttle = 1;
18118c2ecf20Sopenharmony_ci}
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci/*
18148c2ecf20Sopenharmony_ci * Enable reading from a 3270 tty
18158c2ecf20Sopenharmony_ci */
18168c2ecf20Sopenharmony_cistatic void
18178c2ecf20Sopenharmony_citty3270_unthrottle(struct tty_struct * tty)
18188c2ecf20Sopenharmony_ci{
18198c2ecf20Sopenharmony_ci	struct tty3270 *tp;
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	tp = tty->driver_data;
18228c2ecf20Sopenharmony_ci	if (!tp)
18238c2ecf20Sopenharmony_ci		return;
18248c2ecf20Sopenharmony_ci	tp->throttle = 0;
18258c2ecf20Sopenharmony_ci	if (tp->attn)
18268c2ecf20Sopenharmony_ci		tty3270_issue_read(tp, 1);
18278c2ecf20Sopenharmony_ci}
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci/*
18308c2ecf20Sopenharmony_ci * Hang up the tty device.
18318c2ecf20Sopenharmony_ci */
18328c2ecf20Sopenharmony_cistatic void
18338c2ecf20Sopenharmony_citty3270_hangup(struct tty_struct *tty)
18348c2ecf20Sopenharmony_ci{
18358c2ecf20Sopenharmony_ci	struct tty3270 *tp;
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	tp = tty->driver_data;
18388c2ecf20Sopenharmony_ci	if (!tp)
18398c2ecf20Sopenharmony_ci		return;
18408c2ecf20Sopenharmony_ci	spin_lock_bh(&tp->view.lock);
18418c2ecf20Sopenharmony_ci	tp->cx = tp->saved_cx = 0;
18428c2ecf20Sopenharmony_ci	tp->cy = tp->saved_cy = 0;
18438c2ecf20Sopenharmony_ci	tp->highlight = tp->saved_highlight = TAX_RESET;
18448c2ecf20Sopenharmony_ci	tp->f_color = tp->saved_f_color = TAC_RESET;
18458c2ecf20Sopenharmony_ci	tty3270_blank_screen(tp);
18468c2ecf20Sopenharmony_ci	while (tp->nr_lines < tp->view.rows - 2)
18478c2ecf20Sopenharmony_ci		tty3270_blank_line(tp);
18488c2ecf20Sopenharmony_ci	tp->update_flags = TTY_UPDATE_ALL;
18498c2ecf20Sopenharmony_ci	spin_unlock_bh(&tp->view.lock);
18508c2ecf20Sopenharmony_ci	tty3270_set_timer(tp, 1);
18518c2ecf20Sopenharmony_ci}
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_cistatic void
18548c2ecf20Sopenharmony_citty3270_wait_until_sent(struct tty_struct *tty, int timeout)
18558c2ecf20Sopenharmony_ci{
18568c2ecf20Sopenharmony_ci}
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_cistatic int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd,
18598c2ecf20Sopenharmony_ci			 unsigned long arg)
18608c2ecf20Sopenharmony_ci{
18618c2ecf20Sopenharmony_ci	struct tty3270 *tp;
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	tp = tty->driver_data;
18648c2ecf20Sopenharmony_ci	if (!tp)
18658c2ecf20Sopenharmony_ci		return -ENODEV;
18668c2ecf20Sopenharmony_ci	if (tty_io_error(tty))
18678c2ecf20Sopenharmony_ci		return -EIO;
18688c2ecf20Sopenharmony_ci	return kbd_ioctl(tp->kbd, cmd, arg);
18698c2ecf20Sopenharmony_ci}
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
18728c2ecf20Sopenharmony_cistatic long tty3270_compat_ioctl(struct tty_struct *tty,
18738c2ecf20Sopenharmony_ci				 unsigned int cmd, unsigned long arg)
18748c2ecf20Sopenharmony_ci{
18758c2ecf20Sopenharmony_ci	struct tty3270 *tp;
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	tp = tty->driver_data;
18788c2ecf20Sopenharmony_ci	if (!tp)
18798c2ecf20Sopenharmony_ci		return -ENODEV;
18808c2ecf20Sopenharmony_ci	if (tty_io_error(tty))
18818c2ecf20Sopenharmony_ci		return -EIO;
18828c2ecf20Sopenharmony_ci	return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg));
18838c2ecf20Sopenharmony_ci}
18848c2ecf20Sopenharmony_ci#endif
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_cistatic const struct tty_operations tty3270_ops = {
18878c2ecf20Sopenharmony_ci	.install = tty3270_install,
18888c2ecf20Sopenharmony_ci	.cleanup = tty3270_cleanup,
18898c2ecf20Sopenharmony_ci	.open = tty3270_open,
18908c2ecf20Sopenharmony_ci	.close = tty3270_close,
18918c2ecf20Sopenharmony_ci	.write = tty3270_write,
18928c2ecf20Sopenharmony_ci	.put_char = tty3270_put_char,
18938c2ecf20Sopenharmony_ci	.flush_chars = tty3270_flush_chars,
18948c2ecf20Sopenharmony_ci	.write_room = tty3270_write_room,
18958c2ecf20Sopenharmony_ci	.chars_in_buffer = tty3270_chars_in_buffer,
18968c2ecf20Sopenharmony_ci	.flush_buffer = tty3270_flush_buffer,
18978c2ecf20Sopenharmony_ci	.throttle = tty3270_throttle,
18988c2ecf20Sopenharmony_ci	.unthrottle = tty3270_unthrottle,
18998c2ecf20Sopenharmony_ci	.hangup = tty3270_hangup,
19008c2ecf20Sopenharmony_ci	.wait_until_sent = tty3270_wait_until_sent,
19018c2ecf20Sopenharmony_ci	.ioctl = tty3270_ioctl,
19028c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
19038c2ecf20Sopenharmony_ci	.compat_ioctl = tty3270_compat_ioctl,
19048c2ecf20Sopenharmony_ci#endif
19058c2ecf20Sopenharmony_ci	.set_termios = tty3270_set_termios
19068c2ecf20Sopenharmony_ci};
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_cistatic void tty3270_create_cb(int minor)
19098c2ecf20Sopenharmony_ci{
19108c2ecf20Sopenharmony_ci	tty_register_device(tty3270_driver, minor - RAW3270_FIRSTMINOR, NULL);
19118c2ecf20Sopenharmony_ci}
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_cistatic void tty3270_destroy_cb(int minor)
19148c2ecf20Sopenharmony_ci{
19158c2ecf20Sopenharmony_ci	tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR);
19168c2ecf20Sopenharmony_ci}
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_cistatic struct raw3270_notifier tty3270_notifier =
19198c2ecf20Sopenharmony_ci{
19208c2ecf20Sopenharmony_ci	.create = tty3270_create_cb,
19218c2ecf20Sopenharmony_ci	.destroy = tty3270_destroy_cb,
19228c2ecf20Sopenharmony_ci};
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci/*
19258c2ecf20Sopenharmony_ci * 3270 tty registration code called from tty_init().
19268c2ecf20Sopenharmony_ci * Most kernel services (incl. kmalloc) are available at this poimt.
19278c2ecf20Sopenharmony_ci */
19288c2ecf20Sopenharmony_cistatic int __init tty3270_init(void)
19298c2ecf20Sopenharmony_ci{
19308c2ecf20Sopenharmony_ci	struct tty_driver *driver;
19318c2ecf20Sopenharmony_ci	int ret;
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	driver = tty_alloc_driver(RAW3270_MAXDEVS,
19348c2ecf20Sopenharmony_ci				  TTY_DRIVER_REAL_RAW |
19358c2ecf20Sopenharmony_ci				  TTY_DRIVER_DYNAMIC_DEV |
19368c2ecf20Sopenharmony_ci				  TTY_DRIVER_RESET_TERMIOS);
19378c2ecf20Sopenharmony_ci	if (IS_ERR(driver))
19388c2ecf20Sopenharmony_ci		return PTR_ERR(driver);
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci	/*
19418c2ecf20Sopenharmony_ci	 * Initialize the tty_driver structure
19428c2ecf20Sopenharmony_ci	 * Entries in tty3270_driver that are NOT initialized:
19438c2ecf20Sopenharmony_ci	 * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
19448c2ecf20Sopenharmony_ci	 */
19458c2ecf20Sopenharmony_ci	driver->driver_name = "tty3270";
19468c2ecf20Sopenharmony_ci	driver->name = "3270/tty";
19478c2ecf20Sopenharmony_ci	driver->major = IBM_TTY3270_MAJOR;
19488c2ecf20Sopenharmony_ci	driver->minor_start = RAW3270_FIRSTMINOR;
19498c2ecf20Sopenharmony_ci	driver->name_base = RAW3270_FIRSTMINOR;
19508c2ecf20Sopenharmony_ci	driver->type = TTY_DRIVER_TYPE_SYSTEM;
19518c2ecf20Sopenharmony_ci	driver->subtype = SYSTEM_TYPE_TTY;
19528c2ecf20Sopenharmony_ci	driver->init_termios = tty_std_termios;
19538c2ecf20Sopenharmony_ci	tty_set_operations(driver, &tty3270_ops);
19548c2ecf20Sopenharmony_ci	ret = tty_register_driver(driver);
19558c2ecf20Sopenharmony_ci	if (ret) {
19568c2ecf20Sopenharmony_ci		put_tty_driver(driver);
19578c2ecf20Sopenharmony_ci		return ret;
19588c2ecf20Sopenharmony_ci	}
19598c2ecf20Sopenharmony_ci	tty3270_driver = driver;
19608c2ecf20Sopenharmony_ci	raw3270_register_notifier(&tty3270_notifier);
19618c2ecf20Sopenharmony_ci	return 0;
19628c2ecf20Sopenharmony_ci}
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_cistatic void __exit
19658c2ecf20Sopenharmony_citty3270_exit(void)
19668c2ecf20Sopenharmony_ci{
19678c2ecf20Sopenharmony_ci	struct tty_driver *driver;
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci	raw3270_unregister_notifier(&tty3270_notifier);
19708c2ecf20Sopenharmony_ci	driver = tty3270_driver;
19718c2ecf20Sopenharmony_ci	tty3270_driver = NULL;
19728c2ecf20Sopenharmony_ci	tty_unregister_driver(driver);
19738c2ecf20Sopenharmony_ci	put_tty_driver(driver);
19748c2ecf20Sopenharmony_ci	tty3270_del_views();
19758c2ecf20Sopenharmony_ci}
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
19788c2ecf20Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR);
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_cimodule_init(tty3270_init);
19818c2ecf20Sopenharmony_cimodule_exit(tty3270_exit);
1982