18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * IBM/3270 Driver - console view.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author(s):
68c2ecf20Sopenharmony_ci *   Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
78c2ecf20Sopenharmony_ci *   Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
88c2ecf20Sopenharmony_ci *     Copyright IBM Corp. 2003, 2009
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/console.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/list.h>
168c2ecf20Sopenharmony_ci#include <linux/types.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/err.h>
198c2ecf20Sopenharmony_ci#include <linux/reboot.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <asm/ccwdev.h>
228c2ecf20Sopenharmony_ci#include <asm/cio.h>
238c2ecf20Sopenharmony_ci#include <asm/cpcmd.h>
248c2ecf20Sopenharmony_ci#include <asm/ebcdic.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "raw3270.h"
278c2ecf20Sopenharmony_ci#include "tty3270.h"
288c2ecf20Sopenharmony_ci#include "ctrlchar.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define CON3270_OUTPUT_BUFFER_SIZE 1024
318c2ecf20Sopenharmony_ci#define CON3270_STRING_PAGES 4
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic struct raw3270_fn con3270_fn;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic bool auto_update = true;
368c2ecf20Sopenharmony_cimodule_param(auto_update, bool, 0);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * Main 3270 console view data structure.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_cistruct con3270 {
428c2ecf20Sopenharmony_ci	struct raw3270_view view;
438c2ecf20Sopenharmony_ci	struct list_head freemem;	/* list of free memory for strings. */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/* Output stuff. */
468c2ecf20Sopenharmony_ci	struct list_head lines;		/* list of lines. */
478c2ecf20Sopenharmony_ci	struct list_head update;	/* list of lines to update. */
488c2ecf20Sopenharmony_ci	int line_nr;			/* line number for next update. */
498c2ecf20Sopenharmony_ci	int nr_lines;			/* # lines in list. */
508c2ecf20Sopenharmony_ci	int nr_up;			/* # lines up in history. */
518c2ecf20Sopenharmony_ci	unsigned long update_flags;	/* Update indication bits. */
528c2ecf20Sopenharmony_ci	struct string *cline;		/* current output line. */
538c2ecf20Sopenharmony_ci	struct string *status;		/* last line of display. */
548c2ecf20Sopenharmony_ci	struct raw3270_request *write;	/* single write request. */
558c2ecf20Sopenharmony_ci	struct timer_list timer;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* Input stuff. */
588c2ecf20Sopenharmony_ci	struct string *input;		/* input string for read request. */
598c2ecf20Sopenharmony_ci	struct raw3270_request *read;	/* single read request. */
608c2ecf20Sopenharmony_ci	struct raw3270_request *kreset;	/* single keyboard reset request. */
618c2ecf20Sopenharmony_ci	struct tasklet_struct readlet;	/* tasklet to issue read request. */
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic struct con3270 *condev;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* con3270->update_flags. See con3270_update for details. */
678c2ecf20Sopenharmony_ci#define CON_UPDATE_ERASE	1	/* Use EWRITEA instead of WRITE. */
688c2ecf20Sopenharmony_ci#define CON_UPDATE_LIST		2	/* Update lines in tty3270->update. */
698c2ecf20Sopenharmony_ci#define CON_UPDATE_STATUS	4	/* Update status line. */
708c2ecf20Sopenharmony_ci#define CON_UPDATE_ALL		8	/* Recreate screen. */
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic void con3270_update(struct timer_list *);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * Setup timeout for a device. On timeout trigger an update.
768c2ecf20Sopenharmony_ci */
778c2ecf20Sopenharmony_cistatic void con3270_set_timer(struct con3270 *cp, int expires)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	if (expires == 0)
808c2ecf20Sopenharmony_ci		del_timer(&cp->timer);
818c2ecf20Sopenharmony_ci	else
828c2ecf20Sopenharmony_ci		mod_timer(&cp->timer, jiffies + expires);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/*
868c2ecf20Sopenharmony_ci * The status line is the last line of the screen. It shows the string
878c2ecf20Sopenharmony_ci * "console view" in the lower left corner and "Running"/"More..."/"Holding"
888c2ecf20Sopenharmony_ci * in the lower right corner of the screen.
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_cistatic void
918c2ecf20Sopenharmony_cicon3270_update_status(struct con3270 *cp)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	char *str;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	str = (cp->nr_up != 0) ? "History" : "Running";
968c2ecf20Sopenharmony_ci	memcpy(cp->status->string + 24, str, 7);
978c2ecf20Sopenharmony_ci	codepage_convert(cp->view.ascebc, cp->status->string + 24, 7);
988c2ecf20Sopenharmony_ci	cp->update_flags |= CON_UPDATE_STATUS;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void
1028c2ecf20Sopenharmony_cicon3270_create_status(struct con3270 *cp)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	static const unsigned char blueprint[] =
1058c2ecf20Sopenharmony_ci		{ TO_SBA, 0, 0, TO_SF,TF_LOG,TO_SA,TAT_COLOR, TAC_GREEN,
1068c2ecf20Sopenharmony_ci		  'c','o','n','s','o','l','e',' ','v','i','e','w',
1078c2ecf20Sopenharmony_ci		  TO_RA,0,0,0,'R','u','n','n','i','n','g',TO_SF,TF_LOG };
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	cp->status = alloc_string(&cp->freemem, sizeof(blueprint));
1108c2ecf20Sopenharmony_ci	/* Copy blueprint to status line */
1118c2ecf20Sopenharmony_ci	memcpy(cp->status->string, blueprint, sizeof(blueprint));
1128c2ecf20Sopenharmony_ci	/* Set TO_RA addresses. */
1138c2ecf20Sopenharmony_ci	raw3270_buffer_address(cp->view.dev, cp->status->string + 1,
1148c2ecf20Sopenharmony_ci			       cp->view.cols * (cp->view.rows - 1));
1158c2ecf20Sopenharmony_ci	raw3270_buffer_address(cp->view.dev, cp->status->string + 21,
1168c2ecf20Sopenharmony_ci			       cp->view.cols * cp->view.rows - 8);
1178c2ecf20Sopenharmony_ci	/* Convert strings to ebcdic. */
1188c2ecf20Sopenharmony_ci	codepage_convert(cp->view.ascebc, cp->status->string + 8, 12);
1198c2ecf20Sopenharmony_ci	codepage_convert(cp->view.ascebc, cp->status->string + 24, 7);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/*
1238c2ecf20Sopenharmony_ci * Set output offsets to 3270 datastream fragment of a console string.
1248c2ecf20Sopenharmony_ci */
1258c2ecf20Sopenharmony_cistatic void
1268c2ecf20Sopenharmony_cicon3270_update_string(struct con3270 *cp, struct string *s, int nr)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	if (s->len < 4) {
1298c2ecf20Sopenharmony_ci		/* This indicates a bug, but printing a warning would
1308c2ecf20Sopenharmony_ci		 * cause a deadlock. */
1318c2ecf20Sopenharmony_ci		return;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci	if (s->string[s->len - 4] != TO_RA)
1348c2ecf20Sopenharmony_ci		return;
1358c2ecf20Sopenharmony_ci	raw3270_buffer_address(cp->view.dev, s->string + s->len - 3,
1368c2ecf20Sopenharmony_ci			       cp->view.cols * (nr + 1));
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/*
1408c2ecf20Sopenharmony_ci * Rebuild update list to print all lines.
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_cistatic void
1438c2ecf20Sopenharmony_cicon3270_rebuild_update(struct con3270 *cp)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct string *s, *n;
1468c2ecf20Sopenharmony_ci	int nr;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/*
1498c2ecf20Sopenharmony_ci	 * Throw away update list and create a new one,
1508c2ecf20Sopenharmony_ci	 * containing all lines that will fit on the screen.
1518c2ecf20Sopenharmony_ci	 */
1528c2ecf20Sopenharmony_ci	list_for_each_entry_safe(s, n, &cp->update, update)
1538c2ecf20Sopenharmony_ci		list_del_init(&s->update);
1548c2ecf20Sopenharmony_ci	nr = cp->view.rows - 2 + cp->nr_up;
1558c2ecf20Sopenharmony_ci	list_for_each_entry_reverse(s, &cp->lines, list) {
1568c2ecf20Sopenharmony_ci		if (nr < cp->view.rows - 1)
1578c2ecf20Sopenharmony_ci			list_add(&s->update, &cp->update);
1588c2ecf20Sopenharmony_ci		if (--nr < 0)
1598c2ecf20Sopenharmony_ci			break;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci	cp->line_nr = 0;
1628c2ecf20Sopenharmony_ci	cp->update_flags |= CON_UPDATE_LIST;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/*
1668c2ecf20Sopenharmony_ci * Alloc string for size bytes. Free strings from history if necessary.
1678c2ecf20Sopenharmony_ci */
1688c2ecf20Sopenharmony_cistatic struct string *
1698c2ecf20Sopenharmony_cicon3270_alloc_string(struct con3270 *cp, size_t size)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct string *s, *n;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	s = alloc_string(&cp->freemem, size);
1748c2ecf20Sopenharmony_ci	if (s)
1758c2ecf20Sopenharmony_ci		return s;
1768c2ecf20Sopenharmony_ci	list_for_each_entry_safe(s, n, &cp->lines, list) {
1778c2ecf20Sopenharmony_ci		list_del(&s->list);
1788c2ecf20Sopenharmony_ci		if (!list_empty(&s->update))
1798c2ecf20Sopenharmony_ci			list_del(&s->update);
1808c2ecf20Sopenharmony_ci		cp->nr_lines--;
1818c2ecf20Sopenharmony_ci		if (free_string(&cp->freemem, s) >= size)
1828c2ecf20Sopenharmony_ci			break;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci	s = alloc_string(&cp->freemem, size);
1858c2ecf20Sopenharmony_ci	BUG_ON(!s);
1868c2ecf20Sopenharmony_ci	if (cp->nr_up != 0 && cp->nr_up + cp->view.rows > cp->nr_lines) {
1878c2ecf20Sopenharmony_ci		cp->nr_up = cp->nr_lines - cp->view.rows + 1;
1888c2ecf20Sopenharmony_ci		con3270_rebuild_update(cp);
1898c2ecf20Sopenharmony_ci		con3270_update_status(cp);
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	return s;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/*
1958c2ecf20Sopenharmony_ci * Write completion callback.
1968c2ecf20Sopenharmony_ci */
1978c2ecf20Sopenharmony_cistatic void
1988c2ecf20Sopenharmony_cicon3270_write_callback(struct raw3270_request *rq, void *data)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	raw3270_request_reset(rq);
2018c2ecf20Sopenharmony_ci	xchg(&((struct con3270 *) rq->view)->write, rq);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/*
2058c2ecf20Sopenharmony_ci * Update console display.
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_cistatic void
2088c2ecf20Sopenharmony_cicon3270_update(struct timer_list *t)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct con3270 *cp = from_timer(cp, t, timer);
2118c2ecf20Sopenharmony_ci	struct raw3270_request *wrq;
2128c2ecf20Sopenharmony_ci	char wcc, prolog[6];
2138c2ecf20Sopenharmony_ci	unsigned long flags;
2148c2ecf20Sopenharmony_ci	unsigned long updated;
2158c2ecf20Sopenharmony_ci	struct string *s, *n;
2168c2ecf20Sopenharmony_ci	int rc;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (!auto_update && !raw3270_view_active(&cp->view))
2198c2ecf20Sopenharmony_ci		return;
2208c2ecf20Sopenharmony_ci	if (cp->view.dev)
2218c2ecf20Sopenharmony_ci		raw3270_activate_view(&cp->view);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	wrq = xchg(&cp->write, 0);
2248c2ecf20Sopenharmony_ci	if (!wrq) {
2258c2ecf20Sopenharmony_ci		con3270_set_timer(cp, 1);
2268c2ecf20Sopenharmony_ci		return;
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cp->view.lock, flags);
2308c2ecf20Sopenharmony_ci	updated = 0;
2318c2ecf20Sopenharmony_ci	if (cp->update_flags & CON_UPDATE_ALL) {
2328c2ecf20Sopenharmony_ci		con3270_rebuild_update(cp);
2338c2ecf20Sopenharmony_ci		con3270_update_status(cp);
2348c2ecf20Sopenharmony_ci		cp->update_flags = CON_UPDATE_ERASE | CON_UPDATE_LIST |
2358c2ecf20Sopenharmony_ci			CON_UPDATE_STATUS;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci	if (cp->update_flags & CON_UPDATE_ERASE) {
2388c2ecf20Sopenharmony_ci		/* Use erase write alternate to initialize display. */
2398c2ecf20Sopenharmony_ci		raw3270_request_set_cmd(wrq, TC_EWRITEA);
2408c2ecf20Sopenharmony_ci		updated |= CON_UPDATE_ERASE;
2418c2ecf20Sopenharmony_ci	} else
2428c2ecf20Sopenharmony_ci		raw3270_request_set_cmd(wrq, TC_WRITE);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	wcc = TW_NONE;
2458c2ecf20Sopenharmony_ci	raw3270_request_add_data(wrq, &wcc, 1);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/*
2488c2ecf20Sopenharmony_ci	 * Update status line.
2498c2ecf20Sopenharmony_ci	 */
2508c2ecf20Sopenharmony_ci	if (cp->update_flags & CON_UPDATE_STATUS)
2518c2ecf20Sopenharmony_ci		if (raw3270_request_add_data(wrq, cp->status->string,
2528c2ecf20Sopenharmony_ci					     cp->status->len) == 0)
2538c2ecf20Sopenharmony_ci			updated |= CON_UPDATE_STATUS;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (cp->update_flags & CON_UPDATE_LIST) {
2568c2ecf20Sopenharmony_ci		prolog[0] = TO_SBA;
2578c2ecf20Sopenharmony_ci		prolog[3] = TO_SA;
2588c2ecf20Sopenharmony_ci		prolog[4] = TAT_COLOR;
2598c2ecf20Sopenharmony_ci		prolog[5] = TAC_TURQ;
2608c2ecf20Sopenharmony_ci		raw3270_buffer_address(cp->view.dev, prolog + 1,
2618c2ecf20Sopenharmony_ci				       cp->view.cols * cp->line_nr);
2628c2ecf20Sopenharmony_ci		raw3270_request_add_data(wrq, prolog, 6);
2638c2ecf20Sopenharmony_ci		/* Write strings in the update list to the screen. */
2648c2ecf20Sopenharmony_ci		list_for_each_entry_safe(s, n, &cp->update, update) {
2658c2ecf20Sopenharmony_ci			if (s != cp->cline)
2668c2ecf20Sopenharmony_ci				con3270_update_string(cp, s, cp->line_nr);
2678c2ecf20Sopenharmony_ci			if (raw3270_request_add_data(wrq, s->string,
2688c2ecf20Sopenharmony_ci						     s->len) != 0)
2698c2ecf20Sopenharmony_ci				break;
2708c2ecf20Sopenharmony_ci			list_del_init(&s->update);
2718c2ecf20Sopenharmony_ci			if (s != cp->cline)
2728c2ecf20Sopenharmony_ci				cp->line_nr++;
2738c2ecf20Sopenharmony_ci		}
2748c2ecf20Sopenharmony_ci		if (list_empty(&cp->update))
2758c2ecf20Sopenharmony_ci			updated |= CON_UPDATE_LIST;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci	wrq->callback = con3270_write_callback;
2788c2ecf20Sopenharmony_ci	rc = raw3270_start(&cp->view, wrq);
2798c2ecf20Sopenharmony_ci	if (rc == 0) {
2808c2ecf20Sopenharmony_ci		cp->update_flags &= ~updated;
2818c2ecf20Sopenharmony_ci		if (cp->update_flags)
2828c2ecf20Sopenharmony_ci			con3270_set_timer(cp, 1);
2838c2ecf20Sopenharmony_ci	} else {
2848c2ecf20Sopenharmony_ci		raw3270_request_reset(wrq);
2858c2ecf20Sopenharmony_ci		xchg(&cp->write, wrq);
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cp->view.lock, flags);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/*
2918c2ecf20Sopenharmony_ci * Read tasklet.
2928c2ecf20Sopenharmony_ci */
2938c2ecf20Sopenharmony_cistatic void
2948c2ecf20Sopenharmony_cicon3270_read_tasklet(struct raw3270_request *rrq)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	static char kreset_data = TW_KR;
2978c2ecf20Sopenharmony_ci	struct con3270 *cp;
2988c2ecf20Sopenharmony_ci	unsigned long flags;
2998c2ecf20Sopenharmony_ci	int nr_up, deactivate;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	cp = (struct con3270 *) rrq->view;
3028c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cp->view.lock, flags);
3038c2ecf20Sopenharmony_ci	nr_up = cp->nr_up;
3048c2ecf20Sopenharmony_ci	deactivate = 0;
3058c2ecf20Sopenharmony_ci	/* Check aid byte. */
3068c2ecf20Sopenharmony_ci	switch (cp->input->string[0]) {
3078c2ecf20Sopenharmony_ci	case 0x7d:	/* enter: jump to bottom. */
3088c2ecf20Sopenharmony_ci		nr_up = 0;
3098c2ecf20Sopenharmony_ci		break;
3108c2ecf20Sopenharmony_ci	case 0xf3:	/* PF3: deactivate the console view. */
3118c2ecf20Sopenharmony_ci		deactivate = 1;
3128c2ecf20Sopenharmony_ci		break;
3138c2ecf20Sopenharmony_ci	case 0x6d:	/* clear: start from scratch. */
3148c2ecf20Sopenharmony_ci		cp->update_flags = CON_UPDATE_ALL;
3158c2ecf20Sopenharmony_ci		con3270_set_timer(cp, 1);
3168c2ecf20Sopenharmony_ci		break;
3178c2ecf20Sopenharmony_ci	case 0xf7:	/* PF7: do a page up in the console log. */
3188c2ecf20Sopenharmony_ci		nr_up += cp->view.rows - 2;
3198c2ecf20Sopenharmony_ci		if (nr_up + cp->view.rows - 1 > cp->nr_lines) {
3208c2ecf20Sopenharmony_ci			nr_up = cp->nr_lines - cp->view.rows + 1;
3218c2ecf20Sopenharmony_ci			if (nr_up < 0)
3228c2ecf20Sopenharmony_ci				nr_up = 0;
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci		break;
3258c2ecf20Sopenharmony_ci	case 0xf8:	/* PF8: do a page down in the console log. */
3268c2ecf20Sopenharmony_ci		nr_up -= cp->view.rows - 2;
3278c2ecf20Sopenharmony_ci		if (nr_up < 0)
3288c2ecf20Sopenharmony_ci			nr_up = 0;
3298c2ecf20Sopenharmony_ci		break;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci	if (nr_up != cp->nr_up) {
3328c2ecf20Sopenharmony_ci		cp->nr_up = nr_up;
3338c2ecf20Sopenharmony_ci		con3270_rebuild_update(cp);
3348c2ecf20Sopenharmony_ci		con3270_update_status(cp);
3358c2ecf20Sopenharmony_ci		con3270_set_timer(cp, 1);
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cp->view.lock, flags);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/* Start keyboard reset command. */
3408c2ecf20Sopenharmony_ci	raw3270_request_reset(cp->kreset);
3418c2ecf20Sopenharmony_ci	raw3270_request_set_cmd(cp->kreset, TC_WRITE);
3428c2ecf20Sopenharmony_ci	raw3270_request_add_data(cp->kreset, &kreset_data, 1);
3438c2ecf20Sopenharmony_ci	raw3270_start(&cp->view, cp->kreset);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (deactivate)
3468c2ecf20Sopenharmony_ci		raw3270_deactivate_view(&cp->view);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	raw3270_request_reset(rrq);
3498c2ecf20Sopenharmony_ci	xchg(&cp->read, rrq);
3508c2ecf20Sopenharmony_ci	raw3270_put_view(&cp->view);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci/*
3548c2ecf20Sopenharmony_ci * Read request completion callback.
3558c2ecf20Sopenharmony_ci */
3568c2ecf20Sopenharmony_cistatic void
3578c2ecf20Sopenharmony_cicon3270_read_callback(struct raw3270_request *rq, void *data)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	raw3270_get_view(rq->view);
3608c2ecf20Sopenharmony_ci	/* Schedule tasklet to pass input to tty. */
3618c2ecf20Sopenharmony_ci	tasklet_schedule(&((struct con3270 *) rq->view)->readlet);
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci/*
3658c2ecf20Sopenharmony_ci * Issue a read request. Called only from interrupt function.
3668c2ecf20Sopenharmony_ci */
3678c2ecf20Sopenharmony_cistatic void
3688c2ecf20Sopenharmony_cicon3270_issue_read(struct con3270 *cp)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct raw3270_request *rrq;
3718c2ecf20Sopenharmony_ci	int rc;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	rrq = xchg(&cp->read, 0);
3748c2ecf20Sopenharmony_ci	if (!rrq)
3758c2ecf20Sopenharmony_ci		/* Read already scheduled. */
3768c2ecf20Sopenharmony_ci		return;
3778c2ecf20Sopenharmony_ci	rrq->callback = con3270_read_callback;
3788c2ecf20Sopenharmony_ci	rrq->callback_data = cp;
3798c2ecf20Sopenharmony_ci	raw3270_request_set_cmd(rrq, TC_READMOD);
3808c2ecf20Sopenharmony_ci	raw3270_request_set_data(rrq, cp->input->string, cp->input->len);
3818c2ecf20Sopenharmony_ci	/* Issue the read modified request. */
3828c2ecf20Sopenharmony_ci	rc = raw3270_start_irq(&cp->view, rrq);
3838c2ecf20Sopenharmony_ci	if (rc)
3848c2ecf20Sopenharmony_ci		raw3270_request_reset(rrq);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci/*
3888c2ecf20Sopenharmony_ci * Switch to the console view.
3898c2ecf20Sopenharmony_ci */
3908c2ecf20Sopenharmony_cistatic int
3918c2ecf20Sopenharmony_cicon3270_activate(struct raw3270_view *view)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct con3270 *cp;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	cp = (struct con3270 *) view;
3968c2ecf20Sopenharmony_ci	cp->update_flags = CON_UPDATE_ALL;
3978c2ecf20Sopenharmony_ci	con3270_set_timer(cp, 1);
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic void
4028c2ecf20Sopenharmony_cicon3270_deactivate(struct raw3270_view *view)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct con3270 *cp;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	cp = (struct con3270 *) view;
4078c2ecf20Sopenharmony_ci	del_timer(&cp->timer);
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic void
4118c2ecf20Sopenharmony_cicon3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	/* Handle ATTN. Schedule tasklet to read aid. */
4148c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
4158c2ecf20Sopenharmony_ci		con3270_issue_read(cp);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	if (rq) {
4188c2ecf20Sopenharmony_ci		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
4198c2ecf20Sopenharmony_ci			rq->rc = -EIO;
4208c2ecf20Sopenharmony_ci		else
4218c2ecf20Sopenharmony_ci			/* Normal end. Copy residual count. */
4228c2ecf20Sopenharmony_ci			rq->rescnt = irb->scsw.cmd.count;
4238c2ecf20Sopenharmony_ci	} else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
4248c2ecf20Sopenharmony_ci		/* Interrupt without an outstanding request -> update all */
4258c2ecf20Sopenharmony_ci		cp->update_flags = CON_UPDATE_ALL;
4268c2ecf20Sopenharmony_ci		con3270_set_timer(cp, 1);
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci/* Console view to a 3270 device. */
4318c2ecf20Sopenharmony_cistatic struct raw3270_fn con3270_fn = {
4328c2ecf20Sopenharmony_ci	.activate = con3270_activate,
4338c2ecf20Sopenharmony_ci	.deactivate = con3270_deactivate,
4348c2ecf20Sopenharmony_ci	.intv = (void *) con3270_irq
4358c2ecf20Sopenharmony_ci};
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic inline void
4388c2ecf20Sopenharmony_cicon3270_cline_add(struct con3270 *cp)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	if (!list_empty(&cp->cline->list))
4418c2ecf20Sopenharmony_ci		/* Already added. */
4428c2ecf20Sopenharmony_ci		return;
4438c2ecf20Sopenharmony_ci	list_add_tail(&cp->cline->list, &cp->lines);
4448c2ecf20Sopenharmony_ci	cp->nr_lines++;
4458c2ecf20Sopenharmony_ci	con3270_rebuild_update(cp);
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic inline void
4498c2ecf20Sopenharmony_cicon3270_cline_insert(struct con3270 *cp, unsigned char c)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	cp->cline->string[cp->cline->len++] =
4528c2ecf20Sopenharmony_ci		cp->view.ascebc[(c < ' ') ? ' ' : c];
4538c2ecf20Sopenharmony_ci	if (list_empty(&cp->cline->update)) {
4548c2ecf20Sopenharmony_ci		list_add_tail(&cp->cline->update, &cp->update);
4558c2ecf20Sopenharmony_ci		cp->update_flags |= CON_UPDATE_LIST;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic inline void
4608c2ecf20Sopenharmony_cicon3270_cline_end(struct con3270 *cp)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	struct string *s;
4638c2ecf20Sopenharmony_ci	unsigned int size;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	/* Copy cline. */
4668c2ecf20Sopenharmony_ci	size = (cp->cline->len < cp->view.cols - 5) ?
4678c2ecf20Sopenharmony_ci		cp->cline->len + 4 : cp->view.cols;
4688c2ecf20Sopenharmony_ci	s = con3270_alloc_string(cp, size);
4698c2ecf20Sopenharmony_ci	memcpy(s->string, cp->cline->string, cp->cline->len);
4708c2ecf20Sopenharmony_ci	if (cp->cline->len < cp->view.cols - 5) {
4718c2ecf20Sopenharmony_ci		s->string[s->len - 4] = TO_RA;
4728c2ecf20Sopenharmony_ci		s->string[s->len - 1] = 0;
4738c2ecf20Sopenharmony_ci	} else {
4748c2ecf20Sopenharmony_ci		while (--size >= cp->cline->len)
4758c2ecf20Sopenharmony_ci			s->string[size] = cp->view.ascebc[' '];
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci	/* Replace cline with allocated line s and reset cline. */
4788c2ecf20Sopenharmony_ci	list_add(&s->list, &cp->cline->list);
4798c2ecf20Sopenharmony_ci	list_del_init(&cp->cline->list);
4808c2ecf20Sopenharmony_ci	if (!list_empty(&cp->cline->update)) {
4818c2ecf20Sopenharmony_ci		list_add(&s->update, &cp->cline->update);
4828c2ecf20Sopenharmony_ci		list_del_init(&cp->cline->update);
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci	cp->cline->len = 0;
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci/*
4888c2ecf20Sopenharmony_ci * Write a string to the 3270 console
4898c2ecf20Sopenharmony_ci */
4908c2ecf20Sopenharmony_cistatic void
4918c2ecf20Sopenharmony_cicon3270_write(struct console *co, const char *str, unsigned int count)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct con3270 *cp;
4948c2ecf20Sopenharmony_ci	unsigned long flags;
4958c2ecf20Sopenharmony_ci	unsigned char c;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	cp = condev;
4988c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cp->view.lock, flags);
4998c2ecf20Sopenharmony_ci	while (count-- > 0) {
5008c2ecf20Sopenharmony_ci		c = *str++;
5018c2ecf20Sopenharmony_ci		if (cp->cline->len == 0)
5028c2ecf20Sopenharmony_ci			con3270_cline_add(cp);
5038c2ecf20Sopenharmony_ci		if (c != '\n')
5048c2ecf20Sopenharmony_ci			con3270_cline_insert(cp, c);
5058c2ecf20Sopenharmony_ci		if (c == '\n' || cp->cline->len >= cp->view.cols)
5068c2ecf20Sopenharmony_ci			con3270_cline_end(cp);
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci	/* Setup timer to output current console buffer after 1/10 second */
5098c2ecf20Sopenharmony_ci	cp->nr_up = 0;
5108c2ecf20Sopenharmony_ci	if (cp->view.dev && !timer_pending(&cp->timer))
5118c2ecf20Sopenharmony_ci		con3270_set_timer(cp, HZ/10);
5128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cp->view.lock,flags);
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic struct tty_driver *
5168c2ecf20Sopenharmony_cicon3270_device(struct console *c, int *index)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	*index = c->index;
5198c2ecf20Sopenharmony_ci	return tty3270_driver;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci/*
5238c2ecf20Sopenharmony_ci * Wait for end of write request.
5248c2ecf20Sopenharmony_ci */
5258c2ecf20Sopenharmony_cistatic void
5268c2ecf20Sopenharmony_cicon3270_wait_write(struct con3270 *cp)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	while (!cp->write) {
5298c2ecf20Sopenharmony_ci		raw3270_wait_cons_dev(cp->view.dev);
5308c2ecf20Sopenharmony_ci		barrier();
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci/*
5358c2ecf20Sopenharmony_ci * panic() calls con3270_flush through a panic_notifier
5368c2ecf20Sopenharmony_ci * before the system enters a disabled, endless loop.
5378c2ecf20Sopenharmony_ci */
5388c2ecf20Sopenharmony_cistatic void
5398c2ecf20Sopenharmony_cicon3270_flush(void)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	struct con3270 *cp;
5428c2ecf20Sopenharmony_ci	unsigned long flags;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	cp = condev;
5458c2ecf20Sopenharmony_ci	if (!cp->view.dev)
5468c2ecf20Sopenharmony_ci		return;
5478c2ecf20Sopenharmony_ci	raw3270_pm_unfreeze(&cp->view);
5488c2ecf20Sopenharmony_ci	raw3270_activate_view(&cp->view);
5498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cp->view.lock, flags);
5508c2ecf20Sopenharmony_ci	con3270_wait_write(cp);
5518c2ecf20Sopenharmony_ci	cp->nr_up = 0;
5528c2ecf20Sopenharmony_ci	con3270_rebuild_update(cp);
5538c2ecf20Sopenharmony_ci	con3270_update_status(cp);
5548c2ecf20Sopenharmony_ci	while (cp->update_flags != 0) {
5558c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&cp->view.lock, flags);
5568c2ecf20Sopenharmony_ci		con3270_update(&cp->timer);
5578c2ecf20Sopenharmony_ci		spin_lock_irqsave(&cp->view.lock, flags);
5588c2ecf20Sopenharmony_ci		con3270_wait_write(cp);
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cp->view.lock, flags);
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic int con3270_notify(struct notifier_block *self,
5648c2ecf20Sopenharmony_ci			  unsigned long event, void *data)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	con3270_flush();
5678c2ecf20Sopenharmony_ci	return NOTIFY_OK;
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic struct notifier_block on_panic_nb = {
5718c2ecf20Sopenharmony_ci	.notifier_call = con3270_notify,
5728c2ecf20Sopenharmony_ci	.priority = 0,
5738c2ecf20Sopenharmony_ci};
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cistatic struct notifier_block on_reboot_nb = {
5768c2ecf20Sopenharmony_ci	.notifier_call = con3270_notify,
5778c2ecf20Sopenharmony_ci	.priority = 0,
5788c2ecf20Sopenharmony_ci};
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci/*
5818c2ecf20Sopenharmony_ci *  The console structure for the 3270 console
5828c2ecf20Sopenharmony_ci */
5838c2ecf20Sopenharmony_cistatic struct console con3270 = {
5848c2ecf20Sopenharmony_ci	.name	 = "tty3270",
5858c2ecf20Sopenharmony_ci	.write	 = con3270_write,
5868c2ecf20Sopenharmony_ci	.device	 = con3270_device,
5878c2ecf20Sopenharmony_ci	.flags	 = CON_PRINTBUFFER,
5888c2ecf20Sopenharmony_ci};
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci/*
5918c2ecf20Sopenharmony_ci * 3270 console initialization code called from console_init().
5928c2ecf20Sopenharmony_ci */
5938c2ecf20Sopenharmony_cistatic int __init
5948c2ecf20Sopenharmony_cicon3270_init(void)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	struct raw3270 *rp;
5978c2ecf20Sopenharmony_ci	void *cbuf;
5988c2ecf20Sopenharmony_ci	int i;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	/* Check if 3270 is to be the console */
6018c2ecf20Sopenharmony_ci	if (!CONSOLE_IS_3270)
6028c2ecf20Sopenharmony_ci		return -ENODEV;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	/* Set the console mode for VM */
6058c2ecf20Sopenharmony_ci	if (MACHINE_IS_VM) {
6068c2ecf20Sopenharmony_ci		cpcmd("TERM CONMODE 3270", NULL, 0, NULL);
6078c2ecf20Sopenharmony_ci		cpcmd("TERM AUTOCR OFF", NULL, 0, NULL);
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	rp = raw3270_setup_console();
6118c2ecf20Sopenharmony_ci	if (IS_ERR(rp))
6128c2ecf20Sopenharmony_ci		return PTR_ERR(rp);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	condev = kzalloc(sizeof(struct con3270), GFP_KERNEL | GFP_DMA);
6158c2ecf20Sopenharmony_ci	if (!condev)
6168c2ecf20Sopenharmony_ci		return -ENOMEM;
6178c2ecf20Sopenharmony_ci	condev->view.dev = rp;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	condev->read = raw3270_request_alloc(0);
6208c2ecf20Sopenharmony_ci	condev->read->callback = con3270_read_callback;
6218c2ecf20Sopenharmony_ci	condev->read->callback_data = condev;
6228c2ecf20Sopenharmony_ci	condev->write = raw3270_request_alloc(CON3270_OUTPUT_BUFFER_SIZE);
6238c2ecf20Sopenharmony_ci	condev->kreset = raw3270_request_alloc(1);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&condev->lines);
6268c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&condev->update);
6278c2ecf20Sopenharmony_ci	timer_setup(&condev->timer, con3270_update, 0);
6288c2ecf20Sopenharmony_ci	tasklet_init(&condev->readlet,
6298c2ecf20Sopenharmony_ci		     (void (*)(unsigned long)) con3270_read_tasklet,
6308c2ecf20Sopenharmony_ci		     (unsigned long) condev->read);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	raw3270_add_view(&condev->view, &con3270_fn, 1, RAW3270_VIEW_LOCK_IRQ);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&condev->freemem);
6358c2ecf20Sopenharmony_ci	for (i = 0; i < CON3270_STRING_PAGES; i++) {
6368c2ecf20Sopenharmony_ci		cbuf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
6378c2ecf20Sopenharmony_ci		add_string_memory(&condev->freemem, cbuf, PAGE_SIZE);
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci	condev->cline = alloc_string(&condev->freemem, condev->view.cols);
6408c2ecf20Sopenharmony_ci	condev->cline->len = 0;
6418c2ecf20Sopenharmony_ci	con3270_create_status(condev);
6428c2ecf20Sopenharmony_ci	condev->input = alloc_string(&condev->freemem, 80);
6438c2ecf20Sopenharmony_ci	atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
6448c2ecf20Sopenharmony_ci	register_reboot_notifier(&on_reboot_nb);
6458c2ecf20Sopenharmony_ci	register_console(&con3270);
6468c2ecf20Sopenharmony_ci	return 0;
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ciconsole_initcall(con3270_init);
650