File Index
Files and directories
Sign in
FILE

con3270.c

File preview with syntax highlighting.

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