brintos

brintos / linux-shallow public Read only

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