From: Martin Schwidefsky <schwidefsky@de.ibm.com>

New 3270 device driver.



---

 /dev/null                               | 3854 --------------------------------
 25-akpm/Documentation/devices.txt       |   12 
 25-akpm/arch/s390/defconfig             |    3 
 25-akpm/drivers/s390/char/Makefile      |   12 
 25-akpm/drivers/s390/char/con3270.c     |  639 +++++
 25-akpm/drivers/s390/char/defkeymap.c   |  156 +
 25-akpm/drivers/s390/char/defkeymap.map |  191 +
 25-akpm/drivers/s390/char/fs3270.c      |  373 +++
 25-akpm/drivers/s390/char/keyboard.c    |  516 ++++
 25-akpm/drivers/s390/char/keyboard.h    |   57 
 25-akpm/drivers/s390/char/raw3270.c     | 1256 ++++++++++
 25-akpm/drivers/s390/char/raw3270.h     |  273 ++
 25-akpm/drivers/s390/char/tty3270.c     | 1828 +++++++++++++++
 13 files changed, 5304 insertions(+), 3866 deletions(-)

diff -puN arch/s390/defconfig~s390-08-new-3270-driver arch/s390/defconfig
--- 25/arch/s390/defconfig~s390-08-new-3270-driver	Thu Jan  8 14:11:35 2004
+++ 25-akpm/arch/s390/defconfig	Thu Jan  8 14:11:35 2004
@@ -159,7 +159,8 @@ CONFIG_UNIX98_PTY_COUNT=2048
 #
 # S/390 character device drivers
 #
-# CONFIG_TN3270 is not set
+CONFIG_TN3270=y
+CONFIG_TN3270_CONSOLE=y
 CONFIG_TN3215=y
 CONFIG_TN3215_CONSOLE=y
 CONFIG_CCW_CONSOLE=y
diff -puN Documentation/devices.txt~s390-08-new-3270-driver Documentation/devices.txt
--- 25/Documentation/devices.txt~s390-08-new-3270-driver	Thu Jan  8 14:11:35 2004
+++ 25-akpm/Documentation/devices.txt	Thu Jan  8 14:11:35 2004
@@ -2526,17 +2526,17 @@ Your cooperation is appreciated.
 		  1 = /dev/dri/card1		Second graphics card
 		    ...
 
-227 char	IBM 3270 terminal block-mode access
+227 char	IBM 3270 terminal Unix tty access
+		  1 = /dev/3270/tty1		First 3270 terminal
+		  2 = /dev/3270/tty2		Seconds 3270 terminal
+		    ...
+
+228 char	IBM 3270 terminal block-mode access
 		  0 = /dev/3270/tub		Controlling interface
 		  1 = /dev/3270/tub1		First 3270 terminal
 		  2 = /dev/3270/tub2		Second 3270 terminal
 		    ...
 
-228 char	IBM 3270 terminal Unix tty access
-		  1 = /dev/3270/tty1		First 3270 terminal
-		  2 = /dev/3270/tty2		Seconds 3270 terminal
-		    ...
-
 229 char	IBM iSeries virtual console
 		  0 = /dev/iseries/vtty0	First console port
 		  1 = /dev/iseries/vtty1	Second console port
diff -puN /dev/null drivers/s390/char/con3270.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/s390/char/con3270.c	Thu Jan  8 14:11:35 2004
@@ -0,0 +1,639 @@
+/*
+ *  drivers/s390/char/con3270.c
+ *    IBM/3270 Driver - console view.
+ *
+ *  Author(s):
+ *    Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
+ *    Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *	-- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ */
+
+#include <linux/config.h>
+#include <linux/bootmem.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/types.h>
+
+#include <asm/ccwdev.h>
+#include <asm/cio.h>
+#include <asm/cpcmd.h>
+#include <asm/ebcdic.h>
+
+#include "raw3270.h"
+#include "ctrlchar.h"
+
+#define CON3270_OUTPUT_BUFFER_SIZE 1024
+#define CON3270_STRING_PAGES 4
+
+static struct raw3270_fn con3270_fn;
+
+/*
+ * Main 3270 console view data structure.
+ */
+struct con3270 {
+	struct raw3270_view view;
+	spinlock_t lock;
+	struct list_head freemem;	/* list of free memory for strings. */
+
+	/* Output stuff. */
+	struct list_head lines;		/* list of lines. */
+	struct list_head update;	/* list of lines to update. */
+	int line_nr;			/* line number for next update. */
+	int nr_lines;			/* # lines in list. */
+	int nr_up;			/* # lines up in history. */
+	unsigned long update_flags;	/* Update indication bits. */
+	struct string *cline;		/* current output line. */
+	struct string *status;		/* last line of display. */
+	struct raw3270_request *write;	/* single write request. */
+	struct timer_list timer;
+
+	/* Input stuff. */
+	struct string *input;		/* input string for read request. */
+	struct raw3270_request *read;	/* single read request. */
+	struct raw3270_request *kreset;	/* single keyboard reset request. */
+	struct tasklet_struct readlet;	/* tasklet to issue read request. */
+};
+
+static struct con3270 *condev;
+
+/* con3270->update_flags. See con3270_update for details. */
+#define CON_UPDATE_ERASE	1	/* Use EWRITEA instead of WRITE. */
+#define CON_UPDATE_LIST		2	/* Update lines in tty3270->update. */
+#define CON_UPDATE_STATUS	4	/* Update status line. */
+#define CON_UPDATE_ALL		7
+
+static void con3270_update(struct con3270 *);
+
+/*
+ * Setup timeout for a device. On timeout trigger an update.
+ */
+void
+con3270_set_timer(struct con3270 *cp, int expires)
+{
+	if (expires == 0) {
+		if (timer_pending(&cp->timer))
+			del_timer(&cp->timer);
+		return;
+	}
+	if (timer_pending(&cp->timer)) {
+		if (mod_timer(&cp->timer, jiffies + expires))
+			return;
+	}
+	cp->timer.function = (void (*)(unsigned long)) con3270_update;
+	cp->timer.data = (unsigned long) cp;
+	cp->timer.expires = jiffies + expires;
+	add_timer(&cp->timer);
+}
+
+/*
+ * The status line is the last line of the screen. It shows the string
+ * "console view" in the lower left corner and "Running"/"More..."/"Holding"
+ * in the lower right corner of the screen.
+ */
+static void
+con3270_update_status(struct con3270 *cp)
+{
+	char *str;
+
+	str = (cp->nr_up != 0) ? "History" : "Running";
+	memcpy(cp->status->string + 24, str, 7);
+	codepage_convert(cp->view.ascebc, cp->status->string + 24, 7);
+	cp->update_flags |= CON_UPDATE_STATUS;
+}
+
+static void
+con3270_create_status(struct con3270 *cp)
+{
+	static const unsigned char blueprint[] =
+		{ TO_SBA, 0, 0, TO_SF,TF_LOG,TO_SA,TAT_COLOR, TAC_GREEN,
+		  'c','o','n','s','o','l','e',' ','v','i','e','w',
+		  TO_RA,0,0,0,'R','u','n','n','i','n','g',TO_SF,TF_LOG };
+
+	cp->status = alloc_string(&cp->freemem, sizeof(blueprint));
+	/* Copy blueprint to status line */
+	memcpy(cp->status->string, blueprint, sizeof(blueprint));
+	/* Set TO_RA addresses. */
+	raw3270_buffer_address(cp->view.dev, cp->status->string + 1,
+			       cp->view.cols * (cp->view.rows - 1));
+	raw3270_buffer_address(cp->view.dev, cp->status->string + 21,
+			       cp->view.cols * cp->view.rows - 8);
+	/* Convert strings to ebcdic. */
+	codepage_convert(cp->view.ascebc, cp->status->string + 8, 12);
+	codepage_convert(cp->view.ascebc, cp->status->string + 24, 7);
+}
+
+/*
+ * Set output offsets to 3270 datastream fragment of a console string.
+ */
+static void
+con3270_update_string(struct con3270 *cp, struct string *s, int nr)
+{
+	if (s->len >= cp->view.cols - 5)
+		return;
+	raw3270_buffer_address(cp->view.dev, s->string + s->len - 3,
+			       cp->view.cols * (nr + 1));
+}
+
+/*
+ * Rebuild update list to print all lines.
+ */
+static void
+con3270_rebuild_update(struct con3270 *cp)
+{
+	struct string *s, *n;
+	int nr;
+
+	/* 
+	 * Throw away update list and create a new one,
+	 * containing all lines that will fit on the screen.
+	 */
+	list_for_each_entry_safe(s, n, &cp->update, update)
+		list_del_init(&s->update);
+	nr = cp->view.rows - 2 + cp->nr_up;
+	list_for_each_entry_reverse(s, &cp->lines, list) {
+		if (nr < cp->view.rows - 1)
+			list_add(&s->update, &cp->update);
+		if (--nr < 0)
+			break;
+	}
+	cp->line_nr = 0;
+	cp->update_flags |= CON_UPDATE_LIST;
+}
+
+/*
+ * Alloc string for size bytes. Free strings from history if necessary.
+ */
+static struct string *
+con3270_alloc_string(struct con3270 *cp, size_t size)
+{
+	struct string *s, *n;
+
+	s = alloc_string(&cp->freemem, size);
+	if (s)
+		return s;
+	list_for_each_entry_safe(s, n, &cp->lines, list) {
+		list_del(&s->list);
+		if (!list_empty(&s->update))
+			list_del(&s->update);
+		cp->nr_lines--;
+		if (free_string(&cp->freemem, s) >= size)
+			break;
+	}
+	s = alloc_string(&cp->freemem, size);
+	BUG_ON(!s);
+	if (cp->nr_up != 0 && cp->nr_up + cp->view.rows > cp->nr_lines) {
+		cp->nr_up = cp->nr_lines - cp->view.rows + 1;
+		con3270_rebuild_update(cp);
+		con3270_update_status(cp);
+	}
+	return s;
+}
+
+/*
+ * Write completion callback.
+ */
+static void
+con3270_write_callback(struct raw3270_request *rq, void *data)
+{
+	raw3270_request_reset(rq);
+	xchg(&((struct con3270 *) rq->view)->write, rq);
+}
+
+/*
+ * Update console display.
+ */
+static void
+con3270_update(struct con3270 *cp)
+{
+	struct raw3270_request *wrq;
+	char wcc, prolog[6];
+	unsigned long flags;
+	unsigned long updated;
+	struct string *s, *n;
+	int rc;
+
+	wrq = xchg(&cp->write, 0);
+	if (!wrq) {
+		con3270_set_timer(cp, 1);
+		return;
+	}
+
+	spin_lock_irqsave(&cp->view.lock, flags);
+	updated = 0;
+	if (cp->update_flags & CON_UPDATE_ERASE) {
+		/* Use erase write alternate to initialize display. */
+		raw3270_request_set_cmd(wrq, TC_EWRITEA);
+		updated |= CON_UPDATE_ERASE;
+	} else
+		raw3270_request_set_cmd(wrq, TC_WRITE);
+
+	wcc = TW_NONE;
+	raw3270_request_add_data(wrq, &wcc, 1);
+
+	/*
+	 * Update status line.
+	 */
+	if (cp->update_flags & CON_UPDATE_STATUS)
+		if (raw3270_request_add_data(wrq, cp->status->string,
+					     cp->status->len) == 0)
+			updated |= CON_UPDATE_STATUS;
+
+	if (cp->update_flags & CON_UPDATE_LIST) {
+		prolog[0] = TO_SBA;
+		prolog[3] = TO_SA;
+		prolog[4] = TAT_COLOR;
+		prolog[5] = TAC_TURQ;
+		raw3270_buffer_address(cp->view.dev, prolog + 1,
+				       cp->view.cols * cp->line_nr);
+		raw3270_request_add_data(wrq, prolog, 6);
+		/* Write strings in the update list to the screen. */
+		list_for_each_entry_safe(s, n, &cp->update, update) {
+			if (s != cp->cline)
+				con3270_update_string(cp, s, cp->line_nr);
+			if (raw3270_request_add_data(wrq, s->string,
+						     s->len) != 0)
+				break;
+			list_del_init(&s->update);
+			if (s != cp->cline)
+				cp->line_nr++;
+		}
+		if (list_empty(&cp->update))
+			updated |= CON_UPDATE_LIST;
+	}
+	wrq->callback = con3270_write_callback;
+	rc = raw3270_start(&cp->view, wrq);
+	if (rc == 0) {
+		cp->update_flags &= ~updated;
+		if (cp->update_flags)
+			con3270_set_timer(cp, 1);
+	} else {
+		raw3270_request_reset(wrq);
+		xchg(&cp->write, wrq);
+	}
+	spin_unlock_irqrestore(&cp->view.lock, flags);
+}
+
+/*
+ * Read tasklet.
+ */
+static void
+con3270_read_tasklet(struct raw3270_request *rrq)
+{
+	static char kreset_data = TW_KR;
+	struct con3270 *cp;
+	unsigned long flags;
+	int nr_up, deactivate;
+
+	cp = (struct con3270 *) rrq->view;
+	spin_lock_irqsave(&cp->view.lock, flags);
+	nr_up = cp->nr_up;
+	deactivate = 0;
+	/* Check aid byte. */
+	switch (cp->input->string[0]) {
+	case 0x7d:	/* enter: jump to bottom. */
+		nr_up = 0;
+		break;
+	case 0xf3:	/* PF3: deactivate the console view. */
+		deactivate = 1;
+		break;
+	case 0x6d:	/* clear: start from scratch. */
+		con3270_rebuild_update(cp);
+		cp->update_flags = CON_UPDATE_ALL;
+		con3270_set_timer(cp, 1);
+		break;
+	case 0xf7:	/* PF7: do a page up in the console log. */
+		nr_up += cp->view.rows - 2;
+		if (nr_up + cp->view.rows - 1 > cp->nr_lines) {
+			nr_up = cp->nr_lines - cp->view.rows + 1;
+			if (nr_up < 0)
+				nr_up = 0;
+		}
+		break;
+	case 0xf8:	/* PF8: do a page down in the console log. */
+		nr_up -= cp->view.rows - 2;
+		if (nr_up < 0)
+			nr_up = 0;
+		break;
+	}
+	if (nr_up != cp->nr_up) {
+		cp->nr_up = nr_up;
+		con3270_rebuild_update(cp);
+		con3270_update_status(cp);
+		con3270_set_timer(cp, 1);
+	}
+	spin_unlock_irqrestore(&cp->view.lock, flags);
+
+	/* Start keyboard reset command. */
+	raw3270_request_reset(cp->kreset);
+	raw3270_request_set_cmd(cp->kreset, TC_WRITE);
+	raw3270_request_add_data(cp->kreset, &kreset_data, 1);
+	raw3270_start(&cp->view, cp->kreset);
+
+	if (deactivate)
+		raw3270_deactivate_view(&cp->view);
+
+	raw3270_request_reset(rrq);
+	xchg(&cp->read, rrq);
+	raw3270_put_view(&cp->view);
+}
+
+/*
+ * Read request completion callback.
+ */
+static void
+con3270_read_callback(struct raw3270_request *rq, void *data)
+{
+	raw3270_get_view(rq->view);
+	/* Schedule tasklet to pass input to tty. */
+	tasklet_schedule(&((struct con3270 *) rq->view)->readlet);
+}
+
+/*
+ * Issue a read request. Called only from interrupt function.
+ */
+static void
+con3270_issue_read(struct con3270 *cp)
+{
+	struct raw3270_request *rrq;
+	int rc;
+
+	rrq = xchg(&cp->read, 0);
+	if (!rrq)
+		/* Read already scheduled. */
+		return;
+	rrq->callback = con3270_read_callback;
+	rrq->callback_data = cp;
+	raw3270_request_set_cmd(rrq, TC_READMOD);
+	raw3270_request_set_data(rrq, cp->input->string, cp->input->len);
+	/* Issue the read modified request. */
+	rc = raw3270_start_irq(&cp->view, rrq);
+	if (rc)
+		raw3270_request_reset(rrq);
+}
+
+/*
+ * Switch to the console view.
+ */
+static int
+con3270_activate(struct raw3270_view *view)
+{
+	unsigned long flags;
+	struct con3270 *cp;
+
+	cp = (struct con3270 *) view;
+	spin_lock_irqsave(&cp->view.lock, flags);
+	cp->nr_up = 0;
+	con3270_rebuild_update(cp);
+	con3270_update_status(cp);
+	cp->update_flags = CON_UPDATE_ALL;
+	con3270_set_timer(cp, 1);
+	spin_unlock_irqrestore(&cp->view.lock, flags);
+	return 0;
+}
+
+static void
+con3270_deactivate(struct raw3270_view *view)
+{
+	unsigned long flags;
+	struct con3270 *cp;
+
+	cp = (struct con3270 *) view;
+	spin_lock_irqsave(&cp->view.lock, flags);
+	del_timer(&cp->timer);
+	spin_unlock_irqrestore(&cp->view.lock, flags);
+}
+
+static int
+con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb)
+{
+	/* Handle ATTN. Schedule tasklet to read aid. */
+	if (irb->scsw.dstat & DEV_STAT_ATTENTION)
+		con3270_issue_read(cp);
+
+	if (rq) {
+		if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+			rq->rc = -EIO;
+		else
+			/* Normal end. Copy residual count. */
+			rq->rescnt = irb->scsw.count;
+	}
+	return RAW3270_IO_DONE;
+}
+
+/* Console view to a 3270 device. */
+static struct raw3270_fn con3270_fn = {
+	.activate = con3270_activate,
+	.deactivate = con3270_deactivate,
+	.intv = (void *) con3270_irq
+};
+
+static inline void
+con3270_cline_add(struct con3270 *cp)
+{
+	if (!list_empty(&cp->cline->list))
+		/* Already added. */
+		return;
+	list_add_tail(&cp->cline->list, &cp->lines);
+	cp->nr_lines++;
+	con3270_rebuild_update(cp);
+}
+
+static inline void
+con3270_cline_insert(struct con3270 *cp, unsigned char c)
+{
+	cp->cline->string[cp->cline->len++] = 
+		cp->view.ascebc[(c < ' ') ? ' ' : c];
+	if (list_empty(&cp->cline->update)) {
+		list_add_tail(&cp->cline->update, &cp->update);
+		cp->update_flags |= CON_UPDATE_LIST;
+	}
+}
+
+static inline void
+con3270_cline_end(struct con3270 *cp)
+{
+	struct string *s;
+	unsigned int size;
+
+	/* Copy cline. */
+	size = (cp->cline->len < cp->view.cols - 5) ?
+		cp->cline->len + 4 : cp->view.cols;
+	s = con3270_alloc_string(cp, size);
+	memcpy(s->string, cp->cline->string, cp->cline->len);
+	if (s->len < cp->view.cols - 5) {
+		s->string[s->len - 4] = TO_RA;
+		s->string[s->len - 1] = 0;
+	} else {
+		while (--size > cp->cline->len)
+			s->string[size] = cp->view.ascebc[' '];
+	}
+	/* Replace cline with allocated line s and reset cline. */
+	list_add(&s->list, &cp->cline->list);
+	list_del_init(&cp->cline->list);
+	if (!list_empty(&cp->cline->update)) {
+		list_add(&s->update, &cp->cline->update);
+		list_del_init(&cp->cline->update);
+	}
+	cp->cline->len = 0;
+}
+
+/*
+ * Write a string to the 3270 console
+ */
+static void
+con3270_write(struct console *co, const char *str, unsigned int count)
+{
+	struct con3270 *cp;
+	unsigned long flags;
+	unsigned char c;
+
+	cp = condev;
+	if (cp->view.dev)
+		raw3270_activate_view(&cp->view);
+	spin_lock_irqsave(&cp->view.lock, flags);
+	while (count-- > 0) {
+		c = *str++;
+		if (cp->cline->len == 0)
+			con3270_cline_add(cp);
+		if (c != '\n')
+			con3270_cline_insert(cp, c);
+		if (c == '\n' || cp->cline->len >= cp->view.cols)
+			con3270_cline_end(cp);
+	}
+	/* Setup timer to output current console buffer after 1/10 second */
+	if (cp->view.dev && !timer_pending(&cp->timer))
+		con3270_set_timer(cp, HZ/10);
+	spin_unlock_irqrestore(&cp->view.lock,flags);
+}
+
+extern struct tty_driver *tty3270_driver;
+
+static struct tty_driver *
+con3270_device(struct console *c, int *index)
+{
+	*index = c->index;
+	return tty3270_driver;
+}
+
+/*
+ * Wait for end of write request.
+ */
+static void
+con3270_wait_write(struct con3270 *cp)
+{
+	while (!cp->write) {
+		raw3270_wait_cons_dev(cp->view.dev);
+		barrier();
+	}
+}
+
+/*
+ * panic() calls console_unblank before the system enters a
+ * disabled, endless loop.
+ */
+static void
+con3270_unblank(void)
+{
+	struct con3270 *cp;
+	unsigned long flags;
+
+	cp = condev;
+	if (!cp->view.dev)
+		return;
+	spin_lock_irqsave(&cp->view.lock, flags);
+	con3270_wait_write(cp);
+	cp->nr_up = 0;
+	con3270_rebuild_update(cp);
+	con3270_update_status(cp);
+	while (cp->update_flags != 0) {
+		spin_unlock_irqrestore(&cp->view.lock, flags);
+		con3270_update(cp);
+		spin_lock_irqsave(&cp->view.lock, flags);
+		con3270_wait_write(cp);
+	}
+	spin_unlock_irqrestore(&cp->view.lock, flags);
+}
+
+static int __init 
+con3270_consetup(struct console *co, char *options)
+{
+	return 0;
+}
+
+/*
+ *  The console structure for the 3270 console
+ */
+static struct console con3270 = {
+	.name	 = "tty3270",
+	.write	 = con3270_write,
+	.device	 = con3270_device,
+	.unblank = con3270_unblank,
+	.setup	 = con3270_consetup,
+	.flags	 = CON_PRINTBUFFER,
+};
+
+/*
+ * 3270 console initialization code called from console_init().
+ * NOTE: This is called before kmalloc is available.
+ */
+static int __init
+con3270_init(void)
+{
+	struct ccw_device *cdev;
+	struct raw3270 *rp;
+	void *cbuf;
+	int i;
+
+	/* Check if 3270 is to be the console */
+	if (!CONSOLE_IS_3270)
+		return -ENODEV;
+
+	/* Set the console mode for VM */
+	if (MACHINE_IS_VM) {
+		cpcmd("TERM CONMODE 3270", 0, 0);
+		cpcmd("TERM AUTOCR OFF", 0, 0);
+	}
+
+	cdev = ccw_device_probe_console();
+	if (!cdev)
+		return -ENODEV;
+	rp = raw3270_setup_console(cdev);
+	if (IS_ERR(rp))
+		return PTR_ERR(rp);
+
+	condev = (struct con3270 *) alloc_bootmem_low(sizeof(struct con3270));
+	memset(condev, 0, sizeof(struct con3270));
+	condev->view.dev = rp;
+
+	condev->read = raw3270_request_alloc_bootmem(0);
+	condev->read->callback = con3270_read_callback;
+	condev->read->callback_data = condev;
+	condev->write = 
+		raw3270_request_alloc_bootmem(CON3270_OUTPUT_BUFFER_SIZE);
+	condev->kreset = raw3270_request_alloc_bootmem(1);
+
+	INIT_LIST_HEAD(&condev->lines);
+	INIT_LIST_HEAD(&condev->update);
+	init_timer(&condev->timer);
+	tasklet_init(&condev->readlet, 
+		     (void (*)(unsigned long)) con3270_read_tasklet,
+		     (unsigned long) condev->read);
+
+	raw3270_add_view(&condev->view, &con3270_fn, 0);
+
+	INIT_LIST_HEAD(&condev->freemem);
+	for (i = 0; i < CON3270_STRING_PAGES; i++) {
+		cbuf = (void *) alloc_bootmem_low_pages(PAGE_SIZE);
+		add_string_memory(&condev->freemem, cbuf, PAGE_SIZE);
+	}
+	condev->cline = alloc_string(&condev->freemem, condev->view.cols);
+	condev->cline->len = 0;
+	con3270_create_status(condev);
+	condev->input = alloc_string(&condev->freemem, 80);
+	register_console(&con3270);
+	return 0;
+}
+
+console_initcall(con3270_init);
diff -puN /dev/null drivers/s390/char/defkeymap.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/s390/char/defkeymap.c	Thu Jan  8 14:11:35 2004
@@ -0,0 +1,156 @@
+
+/* Do not edit this file! It was automatically generated by   */
+/*    loadkeys --mktable defkeymap.map > defkeymap.c          */
+
+#include <linux/types.h>
+#include <linux/keyboard.h>
+#include <linux/kd.h>
+
+u_short plain_map[NR_KEYS] = {
+	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,
+	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,
+	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,
+	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,
+	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,
+	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,
+	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,
+	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,	0xf000,
+	0xf020,	0xf000,	0xf0e2,	0xf0e4,	0xf0e0,	0xf0e1,	0xf0e3,	0xf0e5,
+	0xf0e7,	0xf0f1,	0xf0a2,	0xf02e,	0xf03c,	0xf028,	0xf02b,	0xf07c,
+	0xf026,	0xf0e9,	0xf0e2,	0xf0eb,	0xf0e8,	0xf0ed,	0xf0ee,	0xf0ef,
+	0xf0ec,	0xf0df,	0xf021,	0xf024,	0xf02a,	0xf029,	0xf03b,	0xf0ac,
+	0xf02d,	0xf02f,	0xf0c2,	0xf0c4,	0xf0c0,	0xf0c1,	0xf0c3,	0xf0c5,
+	0xf0c7,	0xf0d1,	0xf0a6,	0xf02c,	0xf025,	0xf05f,	0xf03e,	0xf03f,
+	0xf0f8,	0xf0c9,	0xf0ca,	0xf0cb,	0xf0c8,	0xf0cd,	0xf0ce,	0xf0cf,
+	0xf0cc,	0xf060,	0xf03a,	0xf023,	0xf040,	0xf027,	0xf03d,	0xf022,
+};
+
+static u_short shift_map[NR_KEYS] = {
+	0xf0d8,	0xf061,	0xf062,	0xf063,	0xf064,	0xf065,	0xf066,	0xf067,
+	0xf068,	0xf069,	0xf0ab,	0xf0bb,	0xf0f0,	0xf0fd,	0xf0fe,	0xf0b1,
+	0xf0b0,	0xf06a,	0xf06b,	0xf06c,	0xf06d,	0xf06e,	0xf06f,	0xf070,
+	0xf071,	0xf072,	0xf000,	0xf000,	0xf0e6,	0xf0b8,	0xf0c6,	0xf0a4,
+	0xf0b5,	0xf07e,	0xf073,	0xf074,	0xf075,	0xf076,	0xf077,	0xf078,
+	0xf079,	0xf07a,	0xf0a1,	0xf0bf,	0xf0d0,	0xf0dd,	0xf0de,	0xf0ae,
+	0xf402,	0xf0a3,	0xf0a5,	0xf0b7,	0xf0a9,	0xf0a7,	0xf0b6,	0xf0bc,
+	0xf0bd,	0xf0be,	0xf05b,	0xf05d,	0xf000,	0xf0a8,	0xf0b4,	0xf0d7,
+	0xf07b,	0xf041,	0xf042,	0xf043,	0xf044,	0xf045,	0xf046,	0xf047,
+	0xf048,	0xf049,	0xf000,	0xf0f4,	0xf0f6,	0xf0f2,	0xf0f3,	0xf0f5,
+	0xf07d,	0xf04a,	0xf04b,	0xf04c,	0xf04d,	0xf04e,	0xf04f,	0xf050,
+	0xf051,	0xf052,	0xf0b9,	0xf0fb,	0xf0fc,	0xf0f9,	0xf0fa,	0xf0ff,
+	0xf05c,	0xf0f7,	0xf053,	0xf054,	0xf055,	0xf056,	0xf057,	0xf058,
+	0xf059,	0xf05a,	0xf0b2,	0xf0d4,	0xf0d6,	0xf0d2,	0xf0d3,	0xf0d5,
+	0xf030,	0xf031,	0xf032,	0xf033,	0xf034,	0xf035,	0xf036,	0xf037,
+	0xf038,	0xf039,	0xf0b3,	0xf0db,	0xf0dc,	0xf0d9,	0xf0da,	0xf000,
+};
+
+static u_short ctrl_map[NR_KEYS] = {
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf11f,	0xf120,	0xf121,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf01a,	0xf003,	0xf212,	0xf004,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf109,	0xf10a,	0xf206,	0xf00a,	0xf200,	0xf200,
+};
+
+static u_short shift_ctrl_map[NR_KEYS] = {
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf10c,	0xf10d,	0xf10e,	0xf10f,	0xf110,	0xf111,	0xf112,
+	0xf113,	0xf11e,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf100,	0xf101,	0xf211,	0xf103,	0xf104,	0xf105,	0xf20b,
+	0xf20a,	0xf108,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+};
+
+ushort *key_maps[MAX_NR_KEYMAPS] = {
+	plain_map, shift_map, 0, 0,
+	ctrl_map, shift_ctrl_map,	0
+};
+
+unsigned int keymap_count = 4;
+
+
+/*
+ * Philosophy: most people do not define more strings, but they who do
+ * often want quite a lot of string space. So, we statically allocate
+ * the default and allocate dynamically in chunks of 512 bytes.
+ */
+
+char func_buf[] = {
+	'\033', '[', '[', 'A', 0, 
+	'\033', '[', '[', 'B', 0, 
+	'\033', '[', '[', 'C', 0, 
+	'\033', '[', '[', 'D', 0, 
+	'\033', '[', '[', 'E', 0, 
+	'\033', '[', '1', '7', '~', 0, 
+	'\033', '[', '1', '8', '~', 0, 
+	'\033', '[', '1', '9', '~', 0, 
+	'\033', '[', '2', '0', '~', 0, 
+	'\033', '[', '2', '1', '~', 0, 
+	'\033', '[', '2', '3', '~', 0, 
+	'\033', '[', '2', '4', '~', 0, 
+	'\033', '[', '2', '5', '~', 0, 
+	'\033', '[', '2', '6', '~', 0, 
+	'\033', '[', '2', '8', '~', 0, 
+	'\033', '[', '2', '9', '~', 0, 
+	'\033', '[', '3', '1', '~', 0, 
+	'\033', '[', '3', '2', '~', 0, 
+	'\033', '[', '3', '3', '~', 0, 
+	'\033', '[', '3', '4', '~', 0, 
+};
+
+
+char *funcbufptr = func_buf;
+int funcbufsize = sizeof(func_buf);
+int funcbufleft = 0;          /* space left */
+
+char *func_table[MAX_NR_FUNC] = {
+	func_buf + 0,
+	func_buf + 5,
+	func_buf + 10,
+	func_buf + 15,
+	func_buf + 20,
+	func_buf + 25,
+	func_buf + 31,
+	func_buf + 37,
+	func_buf + 43,
+	func_buf + 49,
+	func_buf + 55,
+	func_buf + 61,
+	func_buf + 67,
+	func_buf + 73,
+	func_buf + 79,
+	func_buf + 85,
+	func_buf + 91,
+	func_buf + 97,
+	func_buf + 103,
+	func_buf + 109,
+	0,
+};
+
+struct kbdiacr accent_table[MAX_DIACR] = {
+	{'^', 'c', '\003'},	{'^', 'd', '\004'},
+	{'^', 'z', '\032'},	{'^', '\012', '\000'},
+};
+
+unsigned int accent_table_size = 4;
diff -puN /dev/null drivers/s390/char/defkeymap.map
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/s390/char/defkeymap.map	Thu Jan  8 14:11:35 2004
@@ -0,0 +1,191 @@
+# Default keymap for 3270 (ebcdic codepage 037).
+keymaps 0-1,4-5
+
+keycode   0 = nul		Oslash
+keycode   1 = nul		a
+keycode   2 = nul		b
+keycode   3 = nul		c
+keycode   4 = nul		d
+keycode   5 = nul		e
+keycode   6 = nul		f
+keycode   7 = nul		g
+keycode   8 = nul		h
+keycode   9 = nul		i
+keycode  10 = nul		guillemotleft
+keycode  11 = nul		guillemotright
+keycode  12 = nul		eth
+keycode  13 = nul		yacute
+keycode  14 = nul		thorn
+keycode  15 = nul		plusminus
+keycode  16 = nul		degree
+keycode  17 = nul		j
+keycode  18 = nul		k
+keycode  19 = nul		l
+keycode  20 = nul		m
+keycode  21 = nul		n
+keycode  22 = nul		o
+keycode  23 = nul		p
+keycode  24 = nul		q
+keycode  25 = nul		r
+keycode  26 = nul		nul
+keycode  27 = nul		nul
+keycode  28 = nul		ae
+keycode  29 = nul		cedilla
+keycode  30 = nul		AE
+keycode  31 = nul		currency
+keycode  32 = nul		mu
+keycode  33 = nul		tilde
+keycode  34 = nul		s
+keycode  35 = nul		t
+keycode  36 = nul		u
+keycode  37 = nul		v
+keycode  38 = nul		w
+keycode  39 = nul		x
+keycode  40 = nul		y
+keycode  41 = nul		z
+keycode  42 = nul		exclamdown
+keycode  43 = nul		questiondown
+keycode  44 = nul		ETH
+keycode  45 = nul		Yacute
+keycode  46 = nul		THORN
+keycode  47 = nul		registered
+keycode  48 = nul		dead_circumflex
+keycode  49 = nul		sterling
+keycode  50 = nul		yen
+keycode  51 = nul		periodcentered
+keycode  52 = nul		copyright
+keycode  53 = nul		section
+keycode  54 = nul		paragraph
+keycode  55 = nul		onequarter
+keycode  56 = nul		onehalf
+keycode  57 = nul		threequarters
+keycode  58 = nul		bracketleft
+keycode  59 = nul		bracketright
+keycode  60 = nul		nul
+keycode  61 = nul		diaeresis
+keycode  62 = nul		acute
+keycode  63 = nul		multiply
+keycode  64 = space		braceleft
+keycode  65 = nul		A
+keycode  66 = acircumflex	B
+keycode  67 = adiaeresis	C
+keycode  68 = agrave		D
+keycode  69 = aacute		E
+keycode  70 = atilde		F
+keycode  71 = aring		G
+keycode  72 = ccedilla		H
+keycode  73 = ntilde		I
+keycode  74 = cent		nul
+keycode  75 = period		ocircumflex
+keycode  76 = less		odiaeresis
+keycode  77 = parenleft		ograve
+keycode  78 = plus		oacute
+keycode  79 = bar		otilde
+keycode  80 = ampersand		braceright
+keycode  81 = eacute		J
+keycode  82 = acircumflex	K
+keycode  83 = ediaeresis	L
+keycode  84 = egrave		M
+keycode  85 = iacute		N
+keycode  86 = icircumflex	O
+keycode  87 = idiaeresis	P
+keycode  88 = igrave		Q
+keycode  89 = ssharp		R
+keycode  90 = exclam		onesuperior
+keycode  91 = dollar		ucircumflex
+keycode  92 = asterisk		udiaeresis
+keycode  93 = parenright	ugrave
+keycode  94 = semicolon		uacute
+keycode  95 = notsign		ydiaeresis
+keycode  96 = minus		backslash
+keycode  97 = slash		division
+keycode  98 = Acircumflex	S
+keycode  99 = Adiaeresis	T
+keycode 100 = Agrave		U
+keycode 101 = Aacute		V
+keycode 102 = Atilde		W
+keycode 103 = Aring		X
+keycode 104 = Ccedilla		Y
+keycode 105 = Ntilde		Z
+keycode 106 = brokenbar		twosuperior
+keycode 107 = comma		Ocircumflex
+keycode 108 = percent		Odiaeresis
+keycode 109 = underscore	Ograve
+keycode 110 = greater		Oacute
+keycode 111 = question		Otilde
+keycode 112 = oslash		zero
+keycode 113 = Eacute		one
+keycode 114 = Ecircumflex	two
+keycode 115 = Ediaeresis	three
+keycode 116 = Egrave		four
+keycode 117 = Iacute		five
+keycode 118 = Icircumflex	six
+keycode 119 = Idiaeresis	seven
+keycode 120 = Igrave		eight
+keycode 121 = grave		nine
+keycode 122 = colon		threesuperior
+keycode 123 = numbersign	Ucircumflex
+keycode 124 = at		Udiaeresis
+keycode 125 = apostrophe	Ugrave
+keycode 126 = equal		Uacute
+keycode 127 = quotedbl		nul
+
+# AID keys
+control keycode  74 = F22
+control keycode  75 = F23
+control keycode  76 = F24
+control keycode 107 = Control_z		# PA3
+control keycode 108 = Control_c		# PA1
+control keycode 109 = KeyboardSignal	# Clear
+control keycode 110 = Control_d		# PA2
+control keycode 122 = F10
+control keycode 123 = F11		# F11
+control keycode 124 = Last_Console	# F12
+control keycode 125 = Linefeed
+shift control keycode  65 = F13
+shift control keycode  66 = F14
+shift control keycode  67 = F15
+shift control keycode  68 = F16
+shift control keycode  69 = F17
+shift control keycode  70 = F18
+shift control keycode  71 = F19
+shift control keycode  72 = F20
+shift control keycode  73 = F21
+shift control keycode 113 = F1
+shift control keycode 114 = F2
+shift control keycode 115 = Incr_Console
+shift control keycode 116 = F4
+shift control keycode 117 = F5
+shift control keycode 118 = F6
+shift control keycode 119 = Scroll_Backward
+shift control keycode 120 = Scroll_Forward
+shift control keycode 121 = F9
+
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+# string F21 ??
+# string F22 ??
+# string F23 ??
+# string F24 ??
+compose '^' 'c' to Control_c
+compose '^' 'd' to Control_d
+compose '^' 'z' to Control_z
+compose '^' '\012' to nul
diff -puN /dev/null drivers/s390/char/fs3270.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/s390/char/fs3270.c	Thu Jan  8 14:11:35 2004
@@ -0,0 +1,373 @@
+/*
+ *  drivers/s390/char/fs3270.c
+ *    IBM/3270 Driver - fullscreen driver.
+ *
+ *  Author(s):
+ *    Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
+ *    Rewritten for 2.5/2.6 by Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *	-- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ */
+
+#include <linux/config.h>
+#include <linux/bootmem.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/types.h>
+
+#include <asm/ccwdev.h>
+#include <asm/cio.h>
+#include <asm/cpcmd.h>
+#include <asm/ebcdic.h>
+#include <asm/idals.h>
+
+#include "raw3270.h"
+#include "ctrlchar.h"
+
+struct raw3270_fn fs3270_fn;
+
+struct fs3270 {
+	struct raw3270_view view;
+	pid_t fs_pid;			/* Pid of controlling program. */
+	int read_command;		/* ccw command to use for reads. */
+	int write_command;		/* ccw command to use for writes. */
+	int attention;			/* Got attention. */
+	struct raw3270_request *clear;	/* single clear request. */
+	wait_queue_head_t attn_wait;	/* Attention wait queue. */
+};
+
+static void
+fs3270_wake_up(struct raw3270_request *rq, void *data)
+{
+	wake_up((wait_queue_head_t *) data);
+}
+
+static int
+fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq)
+{
+	wait_queue_head_t wq;
+	int rc;
+
+	init_waitqueue_head(&wq);
+	rq->callback = fs3270_wake_up;
+	rq->callback_data = &wq;
+	rc = raw3270_start(view, rq);
+	if (rc)
+		return rc;
+	/* Started sucessfully. Now wait for completion. */
+	wait_event(wq, raw3270_request_final(rq));
+	return rq->rc;
+}
+
+static void
+fs3270_reset_callback(struct raw3270_request *rq, void *data)
+{
+	raw3270_request_reset(rq);
+}
+
+/*
+ * Switch to the fullscreen view.
+ */
+static int
+fs3270_activate(struct raw3270_view *view)
+{
+	struct fs3270 *fp;
+
+	fp = (struct fs3270 *) view;
+	raw3270_request_set_cmd(fp->clear, TC_EWRITEA);
+	fp->clear->callback = fs3270_reset_callback;
+	return raw3270_start(view, fp->clear);
+}
+
+/*
+ * Shutdown fullscreen view.
+ */
+static void
+fs3270_deactivate(struct raw3270_view *view)
+{
+	// FIXME: is this a good idea? The user program using fullscreen 3270
+	// will die just because a console message appeared. On the other
+	// hand the fullscreen device is unoperational now.
+	struct fs3270 *fp;
+
+	fp = (struct fs3270 *) view;
+	if (fp->fs_pid != 0)
+		kill_proc(fp->fs_pid, SIGHUP, 1);
+	fp->fs_pid = 0;
+}
+
+static int
+fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb)
+{
+	/* Handle ATTN. Set indication and wake waiters for attention. */
+	if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+		fp->attention = 1;
+		wake_up(&fp->attn_wait);
+	}
+
+	if (rq) {
+		if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+			rq->rc = -EIO;
+		else
+			/* Normal end. Copy residual count. */
+			rq->rescnt = irb->scsw.count;
+	}
+	return RAW3270_IO_DONE;
+}
+
+/*
+ * Process reads from fullscreen 3270.
+ */
+static ssize_t
+fs3270_read(struct file *filp, char *data, size_t count, loff_t *off)
+{
+	struct fs3270 *fp;
+	struct raw3270_request *rq;
+	struct idal_buffer *ib;
+	int rc;
+	
+	if (count == 0 || count > 65535)
+		return -EINVAL;
+	fp = filp->private_data;
+	if (!fp)
+		return -ENODEV;
+	ib = idal_buffer_alloc(count, 0);
+	if (!ib)
+		return -ENOMEM;
+	rq = raw3270_request_alloc(0);
+	if (!IS_ERR(rq)) {
+		if (fp->read_command == 0 && fp->write_command != 0)
+			fp->read_command = 6;
+		raw3270_request_set_cmd(rq, fp->read_command ? : 2);
+		raw3270_request_set_idal(rq, ib);
+		wait_event(fp->attn_wait, fp->attention);
+		rc = fs3270_do_io(&fp->view, rq);
+		if (rc == 0 && idal_buffer_to_user(ib, data, count))
+			rc = -EFAULT;
+		raw3270_request_free(rq);
+	} else
+		rc = PTR_ERR(rq);
+	idal_buffer_free(ib);
+	return rc;
+}
+
+/*
+ * Process writes to fullscreen 3270.
+ */
+static ssize_t
+fs3270_write(struct file *filp, const char *data, size_t count, loff_t *off)
+{
+	struct fs3270 *fp;
+	struct raw3270_request *rq;
+	struct idal_buffer *ib;
+	int write_command;
+	int rc;
+
+	fp = filp->private_data;
+	if (!fp)
+		return -ENODEV;
+	ib = idal_buffer_alloc(count, 0);
+	if (!ib)
+		return -ENOMEM;
+	rq = raw3270_request_alloc(0);
+	if (!IS_ERR(rq)) {
+		if (idal_buffer_from_user(ib, data, count) == 0) {
+			write_command = fp->write_command ? : 1;
+			if (write_command == 5)
+				write_command = 13;
+			raw3270_request_set_cmd(rq, write_command);
+			raw3270_request_set_idal(rq, ib);
+			rc = fs3270_do_io(&fp->view, rq);
+		} else
+			rc = -EFAULT;
+		raw3270_request_free(rq);
+	} else
+		rc = PTR_ERR(rq);
+	idal_buffer_free(ib);
+	return rc;
+}
+
+/*
+ * process ioctl commands for the tube driver
+ */
+static int
+fs3270_ioctl(struct inode *inode, struct file *filp,
+	     unsigned int cmd, unsigned long arg)
+{
+	struct fs3270 *fp;
+	struct raw3270_iocb iocb;
+	int rc;
+
+	fp = filp->private_data;
+	if (!fp)
+		return -ENODEV;
+	rc = 0;
+	switch (cmd) {
+	case TUBICMD:
+		fp->read_command = arg;
+		break;
+	case TUBOCMD:
+		fp->write_command = arg;
+		break;
+	case TUBGETI:
+		rc = put_user(fp->read_command, (char *) arg);
+		break;
+	case TUBGETO:
+		rc = put_user(fp->write_command,(char *) arg);
+		break;
+	case TUBGETMOD:
+		iocb.model = fp->view.model;
+		iocb.line_cnt = fp->view.rows;
+		iocb.col_cnt = fp->view.cols;
+		iocb.pf_cnt = 24;
+		iocb.re_cnt = 20;
+		iocb.map = 0;
+		if (copy_to_user((char *) arg, &iocb,
+				 sizeof(struct raw3270_iocb)))
+			rc = -EFAULT;
+		break;
+	}
+	return rc;
+}
+
+/*
+ * Allocate tty3270 structure.
+ */
+static struct fs3270 *
+fs3270_alloc_view(void)
+{
+	struct fs3270 *fp;
+
+	fp = (struct fs3270 *) kmalloc(sizeof(struct fs3270),GFP_KERNEL);
+	if (!fp)
+		return ERR_PTR(-ENOMEM);
+	memset(fp, 0, sizeof(struct fs3270));
+	fp->clear = raw3270_request_alloc(0);
+	if (!IS_ERR(fp->clear)) {
+		kfree(fp);
+		return ERR_PTR(-ENOMEM);
+	}
+	return fp;
+}
+
+/*
+ * Free tty3270 structure.
+ */
+static void
+fs3270_free_view(struct raw3270_view *view)
+{
+	raw3270_request_free(((struct fs3270 *) view)->clear);
+	kfree(view);
+}
+
+/*
+ * Unlink fs3270 data structure from filp.
+ */
+static void
+fs3270_release(struct raw3270_view *view)
+{
+}
+
+/* View to a 3270 device. Can be console, tty or fullscreen. */
+struct raw3270_fn fs3270_fn = {
+	.activate = fs3270_activate,
+	.deactivate = fs3270_deactivate,
+	.intv = (void *) fs3270_irq,
+	.release = fs3270_release,
+	.free = fs3270_free_view
+};
+
+/*
+ * This routine is called whenever a 3270 fullscreen device is opened.
+ */
+static int
+fs3270_open(struct inode *inode, struct file *filp)
+{
+	struct fs3270 *fp;
+	int minor, rc;
+
+	if (imajor(filp->f_dentry->d_inode) != IBM_FS3270_MAJOR)
+		return -ENODEV;
+	minor = iminor(filp->f_dentry->d_inode);
+	/* Check if some other program is already using fullscreen mode. */
+	fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor);
+	if (!IS_ERR(fp)) {
+		raw3270_put_view(&fp->view);
+		return -EBUSY;
+	}
+	/* Allocate fullscreen view structure. */
+	fp = fs3270_alloc_view();
+	if (IS_ERR(fp))
+		return PTR_ERR(fp);
+
+	init_waitqueue_head(&fp->attn_wait);
+	fp->fs_pid = current->pid;
+	rc = raw3270_add_view(&fp->view, &fs3270_fn, minor);
+	if (rc) {
+		fs3270_free_view(&fp->view);
+		return rc;
+	}
+
+	rc = raw3270_activate_view(&fp->view);
+	if (rc) {
+		raw3270_del_view(&fp->view);
+		return rc;
+	}
+	filp->private_data = fp;
+	return 0;
+}
+
+/*
+ * This routine is called when the 3270 tty is closed. We wait
+ * for the remaining request to be completed. Then we clean up.
+ */
+static int
+fs3270_close(struct inode *inode, struct file *filp)
+{
+	struct fs3270 *fp;
+
+	fp = filp->private_data;
+	filp->private_data = 0;
+	if (fp)
+		raw3270_del_view(&fp->view);
+	return 0;
+}
+
+static struct file_operations fs3270_fops = {
+	.owner	 = THIS_MODULE,		/* owner */
+	.read	 = fs3270_read,		/* read */
+	.write	 = fs3270_write,	/* write */
+	.ioctl	 = fs3270_ioctl,	/* ioctl */
+	.open	 = fs3270_open,		/* open */
+	.release = fs3270_close,	/* release */
+};
+
+/*
+ * 3270 fullscreen driver initialization.
+ */
+static int __init
+fs3270_init(void)
+{
+	int rc;
+
+	rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops);
+	if (rc) {
+		printk(KERN_ERR "fs3270 can't get major number %d: errno %d\n",
+		       IBM_FS3270_MAJOR, rc);
+		return rc;
+	}
+	return 0;
+}
+
+static void __exit
+fs3270_exit(void)
+{
+	unregister_chrdev(IBM_FS3270_MAJOR, "fs3270");
+}
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(IBM_FS3270_MAJOR);
+
+module_init(fs3270_init);
+module_exit(fs3270_exit);
diff -puN /dev/null drivers/s390/char/keyboard.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/s390/char/keyboard.c	Thu Jan  8 14:11:35 2004
@@ -0,0 +1,516 @@
+/*
+ *  drivers/s390/char/keyboard.c
+ *    ebcdic keycode functions for s390 console drivers
+ *
+ *  S390 version
+ *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/sysrq.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/kbd_diacr.h>
+#include <asm/uaccess.h>
+
+#include "keyboard.h"
+
+/*
+ * Handler Tables.
+ */
+#define K_HANDLERS\
+	k_self,		k_fn,		k_spec,		k_ignore,\
+	k_dead,		k_ignore,	k_ignore,	k_ignore,\
+	k_ignore,	k_ignore,	k_ignore,	k_ignore,\
+	k_ignore,	k_ignore,	k_ignore,	k_ignore
+
+typedef void (k_handler_fn)(struct kbd_data *, unsigned char);
+static k_handler_fn K_HANDLERS;
+static k_handler_fn *k_handler[16] = { K_HANDLERS };
+
+/* maximum values each key_handler can handle */
+static const int max_vals[] = {
+	255, ARRAY_SIZE(func_table) - 1, NR_FN_HANDLER - 1, 0,
+	NR_DEAD - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+static const int NR_TYPES = ARRAY_SIZE(max_vals);
+
+static unsigned char ret_diacr[NR_DEAD] = {
+	'`', '\'', '^', '~', '"', ','
+};
+
+/*
+ * Alloc/free of kbd_data structures.
+ */
+struct kbd_data *
+kbd_alloc(void) {
+	struct kbd_data *kbd;
+	int i, len;
+
+	kbd = kmalloc(sizeof(struct kbd_data), GFP_KERNEL);
+	if (!kbd)
+		goto out;
+	memset(kbd, 0, sizeof(struct kbd_data));
+	kbd->key_maps = kmalloc(sizeof(key_maps), GFP_KERNEL);
+	if (!key_maps)
+		goto out_kbd;
+	memset(kbd->key_maps, 0, sizeof(key_maps));
+	for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
+		if (key_maps[i]) {
+			kbd->key_maps[i] =
+				kmalloc(sizeof(u_short)*NR_KEYS, GFP_KERNEL);
+			if (!kbd->key_maps[i])
+				goto out_maps;
+			memcpy(kbd->key_maps[i], key_maps[i],
+			       sizeof(u_short)*NR_KEYS);
+		}
+	}
+	kbd->func_table = kmalloc(sizeof(func_table), GFP_KERNEL);
+	if (!kbd->func_table)
+		goto out_maps;
+	memset(kbd->func_table, 0, sizeof(func_table));
+	for (i = 0; i < ARRAY_SIZE(func_table); i++) {
+		if (func_table[i]) {
+			len = strlen(func_table[i]) + 1;
+			kbd->func_table[i] = kmalloc(len, GFP_KERNEL);
+			if (!kbd->func_table[i])
+				goto out_func;
+			memcpy(kbd->func_table[i], func_table[i], len);
+		}
+	}
+	kbd->fn_handler =
+		kmalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL);
+	if (!kbd->fn_handler)
+		goto out_func;
+	memset(kbd->fn_handler, 0, sizeof(fn_handler_fn *) * NR_FN_HANDLER);
+	kbd->accent_table =
+		kmalloc(sizeof(struct kbdiacr)*MAX_DIACR, GFP_KERNEL);
+	if (!kbd->accent_table)
+		goto out_fn_handler;
+	memcpy(kbd->accent_table, accent_table,
+	       sizeof(struct kbdiacr)*MAX_DIACR);
+	kbd->accent_table_size = accent_table_size;
+	return kbd;
+
+out_fn_handler:
+	kfree(kbd->fn_handler);
+out_func:
+	for (i = 0; i < ARRAY_SIZE(func_table); i++)
+		if (kbd->func_table[i])
+			kfree(kbd->func_table[i]);
+	kfree(kbd->func_table);
+out_maps:
+	for (i = 0; i < ARRAY_SIZE(key_maps); i++)
+		if (kbd->key_maps[i])
+			kfree(kbd->key_maps[i]);
+	kfree(kbd->key_maps);
+out_kbd:
+	kfree(kbd);
+out:
+	return 0;
+}
+
+void
+kbd_free(struct kbd_data *kbd)
+{
+	int i;
+
+	kfree(kbd->accent_table);
+	kfree(kbd->fn_handler);
+	for (i = 0; i < ARRAY_SIZE(func_table); i++)
+		if (kbd->func_table[i])
+			kfree(kbd->func_table[i]);
+	kfree(kbd->func_table);
+	for (i = 0; i < ARRAY_SIZE(key_maps); i++)
+		if (kbd->key_maps[i])
+			kfree(kbd->key_maps[i]);
+	kfree(kbd->key_maps);
+	kfree(kbd);
+}
+
+/*
+ * Generate ascii -> ebcdic translation table from kbd_data.
+ */
+void
+kbd_ascebc(struct kbd_data *kbd, unsigned char *ascebc)
+{
+	unsigned short *keymap, keysym;
+	int i, j, k;
+
+	memset(ascebc, 0x40, 256);
+	for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
+		keymap = kbd->key_maps[i];
+		if (!keymap)
+			continue;
+		for (j = 0; j < NR_KEYS; j++) {
+			k = ((i & 1) << 7) + j;
+			keysym = keymap[j];
+			if (KTYP(keysym) == (KT_LATIN | 0xf0) ||
+			    KTYP(keysym) == (KT_LETTER | 0xf0))
+				ascebc[KVAL(keysym)] = k;
+			else if (KTYP(keysym) == (KT_DEAD | 0xf0))
+				ascebc[ret_diacr[KVAL(keysym)]] = k;
+		}
+	}
+}
+
+/*
+ * Generate ebcdic -> ascii translation table from kbd_data.
+ */
+void
+kbd_ebcasc(struct kbd_data *kbd, unsigned char *ebcasc)
+{
+	unsigned short *keymap, keysym;
+	int i, j, k;
+
+	memset(ebcasc, ' ', 256);
+	for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
+		keymap = kbd->key_maps[i];
+		if (!keymap)
+			continue;
+		for (j = 0; j < NR_KEYS; j++) {
+			keysym = keymap[j];
+			k = ((i & 1) << 7) + j;
+			if (KTYP(keysym) == (KT_LATIN | 0xf0) ||
+			    KTYP(keysym) == (KT_LETTER | 0xf0))
+				ebcasc[k] = KVAL(keysym);
+			else if (KTYP(keysym) == (KT_DEAD | 0xf0))
+				ebcasc[k] = ret_diacr[KVAL(keysym)];
+		}
+	}
+}
+
+/*
+ * We have a combining character DIACR here, followed by the character CH.
+ * If the combination occurs in the table, return the corresponding value.
+ * Otherwise, if CH is a space or equals DIACR, return DIACR.
+ * Otherwise, conclude that DIACR was not combining after all,
+ * queue it and return CH.
+ */
+static unsigned char
+handle_diacr(struct kbd_data *kbd, unsigned char ch)
+{
+	int i, d;
+
+	d = kbd->diacr;
+	kbd->diacr = 0;
+
+	for (i = 0; i < kbd->accent_table_size; i++) {
+		if (kbd->accent_table[i].diacr == d &&
+		    kbd->accent_table[i].base == ch)
+			return kbd->accent_table[i].result;
+	}
+
+	if (ch == ' ' || ch == d)
+		return d;
+
+	kbd_put_queue(kbd->tty, d);
+	return ch;
+}
+
+/*
+ * Handle dead key.
+ */
+static void
+k_dead(struct kbd_data *kbd, unsigned char value)
+{
+	value = ret_diacr[value];
+	kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
+}
+
+/*
+ * Normal character handler.
+ */
+static void
+k_self(struct kbd_data *kbd, unsigned char value)
+{
+	if (kbd->diacr)
+		value = handle_diacr(kbd, value);
+	kbd_put_queue(kbd->tty, value);
+}
+
+/*
+ * Special key handlers
+ */
+static void
+k_ignore(struct kbd_data *kbd, unsigned char value)
+{
+}
+
+/*
+ * Function key handler.
+ */
+static void
+k_fn(struct kbd_data *kbd, unsigned char value)
+{
+	if (kbd->func_table[value])
+		kbd_puts_queue(kbd->tty, kbd->func_table[value]);
+}
+
+static void
+k_spec(struct kbd_data *kbd, unsigned char value)
+{
+	if (value >= NR_FN_HANDLER)
+		return;
+	if (kbd->fn_handler[value])
+		kbd->fn_handler[value](kbd);
+}
+
+/*
+ * Put utf8 character to tty flip buffer.
+ * UTF-8 is defined for words of up to 31 bits,
+ * but we need only 16 bits here
+ */
+static void
+to_utf8(struct tty_struct *tty, ushort c) 
+{
+	if (c < 0x80)
+		/*  0******* */
+		kbd_put_queue(tty, c);
+	else if (c < 0x800) {
+		/* 110***** 10****** */
+		kbd_put_queue(tty, 0xc0 | (c >> 6));
+		kbd_put_queue(tty, 0x80 | (c & 0x3f));
+	} else {
+		/* 1110**** 10****** 10****** */
+		kbd_put_queue(tty, 0xe0 | (c >> 12));
+		kbd_put_queue(tty, 0x80 | ((c >> 6) & 0x3f));
+		kbd_put_queue(tty, 0x80 | (c & 0x3f));
+	}
+}
+
+/*
+ * Process keycode.
+ */
+void
+kbd_keycode(struct kbd_data *kbd, unsigned int keycode)
+{
+	unsigned short keysym;
+	unsigned char type, value;
+
+	if (!kbd || !kbd->tty)
+		return;
+
+	if (keycode >= 384)
+		keysym = kbd->key_maps[5][keycode - 384];
+	else if (keycode >= 256)
+		keysym = kbd->key_maps[4][keycode - 256];
+	else if (keycode >= 128)
+		keysym = kbd->key_maps[1][keycode - 128];
+	else
+		keysym = kbd->key_maps[0][keycode];
+
+	type = KTYP(keysym);
+	if (type >= 0xf0) {
+		type -= 0xf0;
+		if (type == KT_LETTER)
+			type = KT_LATIN;
+		value = KVAL(keysym);
+#ifdef CONFIG_MAGIC_SYSRQ	       /* Handle the SysRq Hack */
+		if (kbd->sysrq) {
+			if (kbd->sysrq == K(KT_LATIN, '-')) {
+				kbd->sysrq = 0;
+				handle_sysrq(value, 0, kbd->tty);
+				return;
+			}
+			if (value == '-') {
+				kbd->sysrq = K(KT_LATIN, '-');
+				return;
+			}
+			/* Incomplete sysrq sequence. */
+			(*k_handler[KTYP(kbd->sysrq)])(kbd, KVAL(kbd->sysrq));
+			kbd->sysrq = 0;
+		} else if ((type == KT_LATIN && value == '^') ||
+			   (type == KT_DEAD && ret_diacr[value] == '^')) {
+			kbd->sysrq = K(type, value);
+			return;
+		}
+#endif
+		(*k_handler[type])(kbd, value);
+	} else
+		to_utf8(kbd->tty, keysym);
+}
+
+/*
+ * Ioctl stuff.
+ */
+static int
+do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry *user_kbe,
+	      int cmd, int perm)
+{
+	struct kbentry tmp;
+	ushort *key_map, val, ov;
+
+	if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
+		return -EFAULT;
+#if NR_KEYS < 256
+	if (tmp.kb_index >= NR_KEYS)
+		return -EINVAL;
+#endif
+#if MAX_NR_KEYMAPS < 256
+	if (tmp.kb_table >= MAX_NR_KEYMAPS)
+		return -EINVAL;	
+#endif
+
+	switch (cmd) {
+	case KDGKBENT:
+		key_map = kbd->key_maps[tmp.kb_table];
+		if (key_map) {
+		    val = U(key_map[tmp.kb_index]);
+		    if (KTYP(val) >= NR_TYPES)
+			val = K_HOLE;
+		} else
+		    val = (tmp.kb_index ? K_HOLE : K_NOSUCHMAP);
+		return put_user(val, &user_kbe->kb_value);
+	case KDSKBENT:
+		if (!perm)
+			return -EPERM;
+		if (!tmp.kb_index && tmp.kb_value == K_NOSUCHMAP) {
+			/* disallocate map */
+			key_map = kbd->key_maps[tmp.kb_table];
+			if (key_map) {
+			    kbd->key_maps[tmp.kb_table] = 0;
+			    kfree(key_map);
+			}
+			break;
+		}
+
+		if (KTYP(tmp.kb_value) >= NR_TYPES)
+			return -EINVAL;
+		if (KVAL(tmp.kb_value) > max_vals[KTYP(tmp.kb_value)])
+			return -EINVAL;
+
+		if (!(key_map = kbd->key_maps[tmp.kb_table])) {
+			int j;
+
+			key_map = (ushort *) kmalloc(sizeof(plain_map),
+						     GFP_KERNEL);
+			if (!key_map)
+				return -ENOMEM;
+			kbd->key_maps[tmp.kb_table] = key_map;
+			for (j = 0; j < NR_KEYS; j++)
+				key_map[j] = U(K_HOLE);
+		}
+		ov = U(key_map[tmp.kb_index]);
+		if (tmp.kb_value == ov)
+			break;	/* nothing to do */
+		/*
+		 * Attention Key.
+		 */
+		if (((ov == K_SAK) || (tmp.kb_value == K_SAK)) &&
+		    !capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		key_map[tmp.kb_index] = U(tmp.kb_value);
+		break;
+	}
+	return 0;
+}
+
+static int
+do_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry *u_kbs,
+	       int cmd, int perm)
+{
+	unsigned char kb_func;
+	char *p;
+	int len;
+
+	/* Get u_kbs->kb_func. */
+	if (get_user(kb_func, &u_kbs->kb_func))
+		return -EFAULT;
+#if MAX_NR_FUNC < 256
+	if (kb_func >= MAX_NR_FUNC)
+		return -EINVAL;
+#endif
+
+	switch (cmd) {
+	case KDGKBSENT:
+		p = kbd->func_table[kb_func];
+		if (p) {
+			len = strlen(p);
+			if (len >= sizeof(u_kbs->kb_string))
+				len = sizeof(u_kbs->kb_string) - 1;
+			if (copy_to_user(u_kbs->kb_string, p, len))
+				return -EFAULT;
+		} else
+			len = 0;
+		if (put_user('\0', u_kbs->kb_string + len))
+			return -EFAULT;
+		break;
+	case KDSKBSENT:
+		if (!perm)
+			return -EPERM;
+		len = strnlen_user(u_kbs->kb_string,
+				   sizeof(u_kbs->kb_string) - 1);
+		p = kmalloc(len, GFP_KERNEL);
+		if (!p)
+			return -ENOMEM;
+		if (copy_from_user(p, u_kbs->kb_string, len)) {
+			kfree(p);
+			return -EFAULT;
+		}
+		p[len] = 0;
+		if (kbd->func_table[kb_func])
+			kfree(kbd->func_table[kb_func]);
+		kbd->func_table[kb_func] = p;
+		break;
+	}
+	return 0;
+}
+
+int
+kbd_ioctl(struct kbd_data *kbd, struct file *file,
+	  unsigned int cmd, unsigned long arg)
+{
+	struct kbdiacrs *a;
+	int ct, perm;
+
+	/*
+	 * To have permissions to do most of the vt ioctls, we either have
+	 * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
+	 */
+	perm = current->tty == kbd->tty || capable(CAP_SYS_TTY_CONFIG);
+	switch (cmd) {
+	case KDGKBTYPE:
+		return put_user(KB_101, (char*) arg);
+	case KDGKBENT:
+	case KDSKBENT:
+		return do_kdsk_ioctl(kbd, (struct kbentry *)arg, cmd, perm);
+	case KDGKBSENT:
+	case KDSKBSENT:
+		return do_kdgkb_ioctl(kbd, (struct kbsentry *)arg, cmd, perm);
+	case KDGKBDIACR:
+		a = (struct kbdiacrs *) arg;
+
+		if (put_user(kbd->accent_table_size, &a->kb_cnt))
+			return -EFAULT;
+		ct = kbd->accent_table_size;
+		if (copy_to_user(a->kbdiacr, kbd->accent_table,
+				 ct * sizeof(struct kbdiacr)))
+			return -EFAULT;
+		return 0;
+	case KDSKBDIACR:
+		a = (struct kbdiacrs *) arg;
+		if (!perm)
+			return -EPERM;
+		if (get_user(ct, &a->kb_cnt))
+			return -EFAULT;
+		if (ct >= MAX_DIACR)
+			return -EINVAL;
+		kbd->accent_table_size = ct;
+		if (copy_from_user(kbd->accent_table, a->kbdiacr,
+				   ct * sizeof(struct kbdiacr)))
+			return -EFAULT;
+		return 0;
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+EXPORT_SYMBOL(kbd_ioctl);
+EXPORT_SYMBOL(kbd_ascebc);
+EXPORT_SYMBOL(kbd_free);
+EXPORT_SYMBOL(kbd_alloc);
+EXPORT_SYMBOL(kbd_keycode);
diff -puN /dev/null drivers/s390/char/keyboard.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/s390/char/keyboard.h	Thu Jan  8 14:11:35 2004
@@ -0,0 +1,57 @@
+/*
+ *  drivers/s390/char/keyboard.h
+ *    ebcdic keycode functions for s390 console drivers
+ *
+ *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ */
+
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/keyboard.h>
+
+#define NR_FN_HANDLER	20
+
+struct kbd_data;
+
+typedef void (fn_handler_fn)(struct kbd_data *);
+
+/*
+ * FIXME: explain key_maps tricks.
+ */
+
+struct kbd_data {
+	struct tty_struct *tty;
+	unsigned short **key_maps;
+	char **func_table;
+	fn_handler_fn **fn_handler;
+	struct kbdiacr *accent_table;
+	unsigned int accent_table_size;
+	unsigned char diacr;
+	unsigned short sysrq;
+};
+
+struct kbd_data *kbd_alloc(void);
+void kbd_free(struct kbd_data *);
+void kbd_ascebc(struct kbd_data *, unsigned char *);
+
+void kbd_keycode(struct kbd_data *, unsigned int);
+int kbd_ioctl(struct kbd_data *, struct file *, unsigned int, unsigned long);
+
+/*
+ * Helper Functions.
+ */
+extern inline void
+kbd_put_queue(struct tty_struct *tty, int ch)
+{
+	tty_insert_flip_char(tty, ch, 0);
+	tty_schedule_flip(tty);
+}
+
+extern inline void
+kbd_puts_queue(struct tty_struct *tty, char *cp)
+{
+	while (*cp)
+		tty_insert_flip_char(tty, *cp++, 0);
+	tty_schedule_flip(tty);
+}
diff -puN drivers/s390/char/Makefile~s390-08-new-3270-driver drivers/s390/char/Makefile
--- 25/drivers/s390/char/Makefile~s390-08-new-3270-driver	Thu Jan  8 14:11:35 2004
+++ 25-akpm/drivers/s390/char/Makefile	Thu Jan  8 14:11:35 2004
@@ -2,18 +2,20 @@
 # S/390 character devices
 #
 
-tub3270-objs := tuball.o tubfs.o tubtty.o \
-                     tubttyaid.o tubttybld.o tubttyscl.o \
-                     tubttyrcl.o tubttysiz.o
+obj-y += ctrlchar.o keyboard.o defkeymap.o
+
+tub3270-objs := raw3270.o tty3270.o fs3270.o
+obj-$(CONFIG_TN3270) += tub3270.o
+obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
 
-obj-y += ctrlchar.o
 obj-$(CONFIG_TN3215) += con3215.o
+
 obj-$(CONFIG_SCLP) += sclp.o sclp_rw.o
 obj-$(CONFIG_SCLP_TTY) += sclp_tty.o
 obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
 obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o
 obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o
-obj-$(CONFIG_TN3270) += tub3270.o
+
 tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o
 tape-$(CONFIG_PROC_FS) += tape_proc.o
 tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y)
diff -puN /dev/null drivers/s390/char/raw3270.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/s390/char/raw3270.c	Thu Jan  8 14:11:35 2004
@@ -0,0 +1,1256 @@
+/*
+ *  drivers/s390/char/raw3270.c
+ *    IBM/3270 Driver - core functions.
+ *
+ *  Author(s):
+ *    Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
+ *    Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *	-- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ */
+
+#include <linux/config.h>
+#include <linux/bootmem.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include <asm/ccwdev.h>
+#include <asm/cio.h>
+#include <asm/ebcdic.h>
+
+#include "raw3270.h"
+
+/* The main 3270 data structure. */
+struct raw3270 {
+	struct list_head list;
+	struct ccw_device *cdev;
+	int minor;
+
+	short model, rows, cols;
+	unsigned long flags;
+
+	struct list_head req_queue;	/* Request queue. */
+	struct list_head view_list;	/* List of available views. */
+	struct raw3270_view *view;	/* Active view. */
+
+	struct timer_list timer;	/* Device timer. */
+
+	unsigned char *ascebc;		/* ascii -> ebcdic table */
+};
+
+/* raw3270->flags */
+#define RAW3270_FLAGS_14BITADDR	0	/* 14-bit buffer addresses */
+#define RAW3270_FLAGS_BUSY	1	/* Device busy, leave it alone */
+#define RAW3270_FLAGS_ATTN	2	/* Device sent an ATTN interrupt */
+#define RAW3270_FLAGS_SHUTDOWN	4	/* Device is in offline processing */
+#define RAW3270_FLAGS_CONSOLE	8	/* Device is the console. */
+
+/* Lock to protect global data of raw3270 (devices, views, etc). */
+static spinlock_t raw3270_lock = SPIN_LOCK_UNLOCKED;
+
+/* List of 3270 devices. */
+static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices);
+
+/*
+ * Flag to indicate if the driver has been registered. Some operations
+ * like waiting for the end of i/o need to be done differently as long
+ * as the kernel is still starting up (console support).
+ */
+static int raw3270_registered;
+
+/* Module parameters */
+static int tubxcorrect = 0;
+MODULE_PARM(tubxcorrect, "i");
+
+/*
+ * Wait queue for device init/delete, view delete.
+ */
+DECLARE_WAIT_QUEUE_HEAD(raw3270_wait_queue);
+
+/*
+ * Encode array for 12 bit 3270 addresses.
+ */
+unsigned char raw3270_ebcgraf[64] =	{
+	0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+	0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+	0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+	0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+	0x60, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+	0xe8, 0xe9, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+	0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
+};
+
+void
+raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr)
+{
+	if (test_bit(RAW3270_FLAGS_14BITADDR, &rp->flags)) {
+		cp[0] = (addr >> 8) & 0x3f;
+		cp[1] = addr & 0xff;
+	} else {
+		cp[0] = raw3270_ebcgraf[(addr >> 6) & 0x3f];
+		cp[1] = raw3270_ebcgraf[addr & 0x3f];
+	}
+}
+
+/*
+ * Allocate a new 3270 ccw request
+ */
+struct raw3270_request *
+raw3270_request_alloc(size_t size)
+{
+	struct raw3270_request *rq;
+
+	/* Allocate request structure */
+	rq = kmalloc(sizeof(struct raw3270_request), GFP_KERNEL | GFP_DMA);
+	if (!rq)
+		return ERR_PTR(-ENOMEM);
+	memset(rq, 0, sizeof(struct raw3270_request));
+
+	/* alloc output buffer. */
+	if (size > 0) {
+		rq->buffer = kmalloc(size, GFP_KERNEL | GFP_DMA);
+		if (!rq->buffer) {
+			kfree(rq);
+			return ERR_PTR(-ENOMEM);
+		}
+	}
+	rq->size = size;
+	INIT_LIST_HEAD(&rq->list);
+
+	/*
+	 * Setup ccw.
+	 */
+	rq->ccw.cda = __pa(rq->buffer);
+	rq->ccw.flags = CCW_FLAG_SLI;
+
+	return rq;
+}
+
+#ifdef CONFIG_TN3270_CONSOLE
+/*
+ * Allocate a new 3270 ccw request from bootmem. Only works very
+ * early in the boot process. Only con3270.c should be using this.
+ */
+struct raw3270_request *
+raw3270_request_alloc_bootmem(size_t size)
+{
+	struct raw3270_request *rq;
+
+	rq = alloc_bootmem_low(sizeof(struct raw3270));
+	if (!rq)
+		return ERR_PTR(-ENOMEM);
+	memset(rq, 0, sizeof(struct raw3270_request));
+
+	/* alloc output buffer. */
+	if (size > 0) {
+		rq->buffer = alloc_bootmem_low(size);
+		if (!rq->buffer) {
+			free_bootmem((unsigned long) rq,
+				     sizeof(struct raw3270));
+			return ERR_PTR(-ENOMEM);
+		}
+	}
+	rq->size = size;
+	INIT_LIST_HEAD(&rq->list);
+
+	/*
+	 * Setup ccw.
+	 */
+	rq->ccw.cda = __pa(rq->buffer);
+	rq->ccw.flags = CCW_FLAG_SLI;
+
+	return rq;
+}
+#endif
+
+/*
+ * Free 3270 ccw request
+ */
+void
+raw3270_request_free (struct raw3270_request *rq)
+{
+	if (rq->buffer)
+		kfree(rq->buffer);
+	kfree(rq);
+}
+
+/*
+ * Reset request to initial state.
+ */
+void
+raw3270_request_reset(struct raw3270_request *rq)
+{
+	BUG_ON(!list_empty(&rq->list));
+	rq->ccw.cmd_code = 0;
+	rq->ccw.count = 0;
+	rq->ccw.cda = __pa(rq->buffer);
+	rq->ccw.flags = CCW_FLAG_SLI;
+	rq->rescnt = 0;
+	rq->rc = 0;
+}
+
+/*
+ * Set command code to ccw of a request.
+ */
+void
+raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd)
+{
+	rq->ccw.cmd_code = cmd;
+}
+
+/*
+ * Add data fragment to output buffer.
+ */
+int
+raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size)
+{
+	if (size + rq->ccw.count > rq->size)
+		return -E2BIG;
+	memcpy(rq->buffer + rq->ccw.count, data, size);
+	rq->ccw.count += size;
+	return 0;
+}
+
+/*
+ * Set address/length pair to ccw of a request.
+ */
+void
+raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size)
+{
+	rq->ccw.cda = __pa(data);
+	rq->ccw.count = size;
+}
+
+/*
+ * Set idal buffer to ccw of a request.
+ */
+void
+raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib)
+{
+	rq->ccw.cda = __pa(ib->data);
+	rq->ccw.count = ib->size;
+	rq->ccw.flags |= CCW_FLAG_IDA;
+}
+
+/*
+ * Stop running ccw.
+ */
+static int
+raw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq)
+{
+	int retries;
+	int rc;
+
+	if (raw3270_request_final(rq))
+		return 0;
+	/* Check if interrupt has already been processed */
+	for (retries = 0; retries < 5; retries++) {
+		if (retries < 2)
+			rc = ccw_device_halt(rp->cdev, (long) rq);
+		else
+			rc = ccw_device_clear(rp->cdev, (long) rq);
+		if (rc == 0)
+			break;		/* termination successful */
+	}
+	return rc;
+}
+
+static int
+raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq)
+{
+	unsigned long flags;
+	int rc;
+
+	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
+	rc = raw3270_halt_io_nolock(rp, rq);
+	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
+	return rc;
+}
+
+/*
+ * Add the request to the request queue, try to start it if the
+ * 3270 device is idle. Return without waiting for end of i/o.
+ */
+static int
+__raw3270_start(struct raw3270 *rp, struct raw3270_view *view,
+		struct raw3270_request *rq)
+{
+	rq->view = view;
+	raw3270_get_view(view);
+	if (list_empty(&rp->req_queue) &&
+	    !test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) {
+		/* No other requests are on the queue. Start this one. */
+		rq->rc = ccw_device_start(rp->cdev, &rq->ccw,
+					       (unsigned long) rq, 0, 0);
+		if (rq->rc) {
+			raw3270_put_view(view);
+			return rq->rc;
+		}
+	}
+	list_add_tail(&rq->list, &rp->req_queue);
+	return 0;
+}
+
+int
+raw3270_start(struct raw3270_view *view, struct raw3270_request *rq)
+{
+	unsigned long flags;
+	struct raw3270 *rp;
+	int rc;
+
+	spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags);
+	rp = view->dev;
+	if (!rp || rp->view != view)
+		rc = -EACCES;
+	else if (test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags))
+		rc = -ENODEV;
+	else
+		rc =  __raw3270_start(rp, view, rq);
+	spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags);
+	return rc;
+}
+
+int
+raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq)
+{
+	struct raw3270 *rp;
+
+	rp = view->dev;
+	rq->view = view;
+	raw3270_get_view(view);
+	list_add_tail(&rq->list, &rp->req_queue);
+	return 0;
+}
+
+/*
+ * 3270 interrupt routine, called from the ccw_device layer
+ */
+static void
+raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
+{
+	struct raw3270 *rp;
+	struct raw3270_view *view;
+	struct raw3270_request *rq;
+	int rc;
+
+	rp = (struct raw3270 *) cdev->dev.driver_data;
+	if (!rp)
+		return;
+	rq = (struct raw3270_request *) intparm;
+	view = rq ? rq->view : rp->view;
+
+	if (irb->scsw.dstat == 
+	    (DEV_STAT_CHN_END | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP)) {
+		/* Handle CE-DE-UE and subsequent UDE */
+		set_bit(RAW3270_FLAGS_BUSY, &rp->flags);
+		rc = RAW3270_IO_BUSY;
+	} else if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) {
+		/* Wait for UDE if busy flag is set. */
+		if (irb->scsw.dstat & DEV_STAT_DEV_END) {
+			clear_bit(RAW3270_FLAGS_BUSY, &rp->flags);
+			/* Got it, now retry. */
+			rc = RAW3270_IO_RETRY;
+		} else
+			rc = RAW3270_IO_BUSY;
+	} else if (view)
+		rc = view->fn->intv(view, rq, irb);
+	else
+		rc = RAW3270_IO_DONE;
+
+	switch (rc) {
+	case RAW3270_IO_DONE:
+		break;
+	case RAW3270_IO_BUSY:
+		/* 
+		 * Intervention required by the operator. We have to wait
+		 * for unsolicited device end.
+		 */
+		return;
+	case RAW3270_IO_RETRY:
+		if (!rq)
+			break;
+		rq->rc = ccw_device_start(rp->cdev, &rq->ccw,
+					  (unsigned long) rq, 0, 0);
+		if (rq->rc == 0)
+			return;	/* Sucessfully restarted. */
+		break;
+	case RAW3270_IO_STOP:
+		raw3270_halt_io_nolock(rp, rq);
+		rq->rc = -EIO;
+		break;
+	default:
+		BUG();
+	}
+	if (rq) {
+		BUG_ON(list_empty(&rq->list));
+		/* The request completed, remove from queue and do callback. */
+		list_del_init(&rq->list);
+		if (rq->callback)
+			rq->callback(rq, rq->callback_data);
+		/* Do put_device for get_device in raw3270_start. */
+		raw3270_put_view(view);
+	}
+	/*
+	 * Try to start each request on request queue until one is
+	 * started successful.
+	 */
+	while (!list_empty(&rp->req_queue)) {
+		rq = list_entry(rp->req_queue.next,struct raw3270_request,list);
+		rq->rc = ccw_device_start(rp->cdev, &rq->ccw,
+					  (unsigned long) rq, 0, 0);
+		if (rq->rc == 0)
+			break;
+		/* Start failed. Remove request and do callback. */
+		list_del_init(&rq->list);
+		if (rq->callback)
+			rq->callback(rq, rq->callback_data);
+		/* Do put_device for get_device in raw3270_start. */
+		raw3270_put_view(view);
+	}
+}
+
+/*
+ * Size sensing.
+ */
+
+struct raw3270_ua {	/* Query Reply structure for Usable Area */
+	struct {	/* Usable Area Query Reply Base */
+		short l;	/* Length of this structured field */
+		char  sfid;	/* 0x81 if Query Reply */
+		char  qcode;	/* 0x81 if Usable Area */
+		char  flags0;
+		char  flags1;
+		short w;	/* Width of usable area */
+		short h;	/* Heigth of usavle area */
+		char  units;	/* 0x00:in; 0x01:mm */
+		int   xr;
+		int   yr;
+		char  aw;
+		char  ah;
+		short buffsz;	/* Character buffer size, bytes */
+		char  xmin;
+		char  ymin;
+		char  xmax;
+		char  ymax;
+	} __attribute__ ((packed)) uab;
+	struct {	/* Alternate Usable Area Self-Defining Parameter */
+		char  l;	/* Length of this Self-Defining Parm */
+		char  sdpid;	/* 0x02 if Alternate Usable Area */
+		char  res;
+		char  auaid;	/* 0x01 is Id for the A U A */
+		short wauai;	/* Width of AUAi */
+		short hauai;	/* Height of AUAi */
+		char  auaunits;	/* 0x00:in, 0x01:mm */
+		int   auaxr;
+		int   auayr;
+		char  awauai;
+		char  ahauai;
+	} __attribute__ ((packed)) aua;
+} __attribute__ ((packed));
+
+static unsigned char raw3270_init_data[256];
+static struct raw3270_request raw3270_init_request;
+static struct diag210 raw3270_init_diag210;
+static DECLARE_MUTEX(raw3270_init_sem);
+
+static int
+raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq,
+		 struct irb *irb)
+{
+	/*
+	 * Unit-Check Processing:
+	 * Expect Command Reject or Intervention Required.
+	 */
+	if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+		/* Request finished abnormally. */
+		if (irb->ecw[0] & SNS0_INTERVENTION_REQ) {
+			set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags);
+			return RAW3270_IO_BUSY;
+		}
+	}
+	if (rq) {
+		if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+			if (irb->ecw[0] & SNS0_CMD_REJECT)
+				rq->rc = -EOPNOTSUPP;
+			else
+				rq->rc = -EIO;
+		} else
+			/* Request finished normally. Copy residual count. */
+			rq->rescnt = irb->scsw.count;
+	}
+	if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+		set_bit(RAW3270_FLAGS_ATTN, &view->dev->flags);
+		wake_up(&raw3270_wait_queue);
+	}
+	return RAW3270_IO_DONE;
+}
+
+static struct raw3270_fn raw3270_init_fn = {
+	.intv = raw3270_init_irq
+};
+
+static struct raw3270_view raw3270_init_view = {
+	.fn = &raw3270_init_fn
+};
+
+/*
+ * raw3270_wait/raw3270_wait_interruptible/__raw3270_wakeup
+ * Wait for end of request. The request must have been started
+ * with raw3270_start, rc = 0. The device lock may NOT have been
+ * released between calling raw3270_start and raw3270_wait.
+ */
+static void
+raw3270_wake_init(struct raw3270_request *rq, void *data)
+{
+	wake_up((wait_queue_head_t *) data);
+}
+
+/*
+ * Special wait function that can cope with console initialization.
+ */
+static int
+raw3270_start_init(struct raw3270 *rp, struct raw3270_view *view,
+		   struct raw3270_request *rq)
+{
+	unsigned long flags;
+	wait_queue_head_t wq;
+	int rc;
+
+#ifdef CONFIG_TN3270_CONSOLE
+	if (raw3270_registered == 0) {
+		spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags);
+		rq->callback = 0;
+		rc = __raw3270_start(rp, view, rq);
+		if (rc == 0)
+			while (!raw3270_request_final(rq)) {
+				wait_cons_dev();
+				barrier();
+			}
+		spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags);
+		return rq->rc;
+	}
+#endif
+	init_waitqueue_head(&wq);
+	rq->callback = raw3270_wake_init;
+	rq->callback_data = &wq;
+	spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags);
+	rc = __raw3270_start(rp, view, rq);
+	spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags);
+	if (rc)
+		return rc;
+	/* Now wait for the completion. */
+	rc = wait_event_interruptible(wq, raw3270_request_final(rq));
+	if (rc == -ERESTARTSYS) {	/* Interrupted by a signal. */
+		raw3270_halt_io(view->dev, rq);
+		return -ERESTARTSYS;
+	}
+	return rq->rc;
+}
+
+static int
+__raw3270_size_device_vm(struct raw3270 *rp)
+{
+	int rc, model;
+
+	raw3270_init_diag210.vrdcdvno = 
+		_ccw_device_get_device_number(rp->cdev);
+	raw3270_init_diag210.vrdclen = sizeof(struct diag210);
+	rc = diag210(&raw3270_init_diag210);
+	if (rc)
+		return rc;
+	model = raw3270_init_diag210.vrdccrmd;
+	switch (model) {
+	case 2:
+		rp->model = model;
+		rp->rows = 24;
+		rp->cols = 80;
+		break;
+	case 3:
+		rp->model = model;
+		rp->rows = 32;
+		rp->cols = 80;
+		break;
+	case 4:
+		rp->model = model;
+		rp->rows = 43;
+		rp->cols = 80;
+		break;
+	case 5:
+		rp->model = model;
+		rp->rows = 27;
+		rp->cols = 132;
+		break;
+	default:
+		printk(KERN_WARNING "vrdccrmd is 0x%.8x\n", model);
+		rc = -EOPNOTSUPP;
+		break;
+	}
+	return rc;
+}
+
+static int
+__raw3270_size_device(struct raw3270 *rp)
+{
+	static const unsigned char wbuf[] =
+		{ 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 };
+	unsigned long flags;
+	struct raw3270_ua *uap;
+	unsigned short count;
+	int rc;
+
+	/*
+	 * To determine the size of the 3270 device we need to do:
+	 * 1) send a 'read partition' data stream to the device
+	 * 2) wait for the attn interrupt that preceeds the query reply
+	 * 3) do a read modified to get the query reply
+	 * To make things worse we have to cope with intervention
+	 * required (3270 device switched to 'stand-by') and command
+	 * rejects (old devices that can't do 'read partition').
+	 */
+	memset(&raw3270_init_request, 0, sizeof(raw3270_init_request));
+	memset(raw3270_init_data, 0, sizeof(raw3270_init_data));
+	/* Store 'read partition' data stream to raw3270_init_data */
+	memcpy(raw3270_init_data, wbuf, sizeof(wbuf));
+	INIT_LIST_HEAD(&raw3270_init_request.list);
+	raw3270_init_request.ccw.cmd_code = TC_WRITESF;
+	raw3270_init_request.ccw.flags = CCW_FLAG_SLI;
+	raw3270_init_request.ccw.count = sizeof(wbuf);
+	raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data);
+
+	rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request);
+	if (rc) {
+		/* Check error cases: -ERESTARTSYS, -EIO and -EOPNOTSUPP */
+		if (rc == -EOPNOTSUPP && MACHINE_IS_VM)
+			return __raw3270_size_device_vm(rp);
+		return rc;
+	}
+
+	/* Wait for attention interrupt. */
+#ifdef CONFIG_TN3270_CONSOLE
+	if (raw3270_registered == 0) {
+		spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
+		while (!test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags))
+			wait_cons_dev();
+		spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
+	} else
+#endif
+		rc = wait_event_interruptible(raw3270_wait_queue,
+			test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags));
+	if (rc)
+		return rc;
+
+	/*
+	 * The device accepted the 'read partition' command. Now
+	 * set up a read ccw and issue it.
+	 */
+	raw3270_init_request.ccw.cmd_code = TC_READMOD;
+	raw3270_init_request.ccw.flags = CCW_FLAG_SLI;
+	raw3270_init_request.ccw.count = sizeof(raw3270_init_data);
+	raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data);
+	rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request);
+	if (rc)
+		return rc;
+	/* Got a Query Reply */
+	count = sizeof(raw3270_init_data) - raw3270_init_request.rescnt;
+	uap = (struct raw3270_ua *) (raw3270_init_data + 1);
+	/* Paranoia check. */
+	if (raw3270_init_data[0] != 0x88 || uap->uab.qcode != 0x81)
+		return -EOPNOTSUPP;
+	/* Copy rows/columns of default Usable Area */
+	rp->rows = uap->uab.h;
+	rp->cols = uap->uab.w;
+	/* Check for 14 bit addressing */
+	if ((uap->uab.flags0 & 0x0d) == 0x01)
+		set_bit(RAW3270_FLAGS_14BITADDR, &rp->flags);
+	/* Check for Alternate Usable Area */
+	if (uap->uab.l == sizeof(struct raw3270_ua) &&
+	    uap->aua.sdpid == 0x02) {
+		rp->rows = uap->aua.hauai;
+		rp->cols = uap->aua.wauai;
+	}
+	return 0;
+}
+
+static int
+raw3270_size_device(struct raw3270 *rp)
+{
+	int rc;
+
+	down(&raw3270_init_sem);
+	rp->view = &raw3270_init_view;
+	raw3270_init_view.dev = rp;
+	rc = __raw3270_size_device(rp);
+	raw3270_init_view.dev = 0;
+	rp->view = 0;
+	up(&raw3270_init_sem);
+	if (rc == 0) {	/* Found something. */
+		/* Try to find a model. */
+		rp->model = 0;
+		if (rp->rows == 24 && rp->cols == 80)
+			rp->model = 2;
+		if (rp->rows == 32 && rp->cols == 80)
+			rp->model = 3;
+		if (rp->rows == 43 && rp->cols == 80)
+			rp->model = 4;
+		if (rp->rows == 27 && rp->cols == 132)
+			rp->model = 5;
+	}
+	return rc;
+}
+
+static int
+raw3270_reset_device(struct raw3270 *rp)
+{
+	int rc;
+
+	down(&raw3270_init_sem);
+	memset(&raw3270_init_request, 0, sizeof(raw3270_init_request));
+	memset(raw3270_init_data, 0, sizeof(raw3270_init_data));
+	/* Store reset data stream to raw3270_init_data/raw3270_init_request */
+	raw3270_init_data[0] = TW_KR;
+	INIT_LIST_HEAD(&raw3270_init_request.list);
+	raw3270_init_request.ccw.cmd_code = TC_EWRITEA;
+	raw3270_init_request.ccw.flags = CCW_FLAG_SLI;
+	raw3270_init_request.ccw.count = 1;
+	raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data);
+	rp->view = &raw3270_init_view;
+	raw3270_init_view.dev = rp;
+	rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request);
+	raw3270_init_view.dev = 0;
+	rp->view = 0;
+	up(&raw3270_init_sem);
+	return rc;
+}
+
+/*
+ * Setup new 3270 device.
+ */
+static int
+raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc)
+{
+	struct list_head *l;
+	struct raw3270 *tmp;
+	int minor;
+
+	memset(rp, 0, sizeof(struct raw3270));
+	/* Copy ebcdic -> ascii translation table. */
+	memcpy(ascebc, _ascebc, 256);
+	if (tubxcorrect) {
+		/* correct brackets and circumflex */
+		ascebc['['] = 0xad;
+		ascebc[']'] = 0xbd;
+		ascebc['^'] = 0xb0;
+	}
+	rp->ascebc = ascebc;
+
+	/* Set defaults. */
+	rp->rows = 24;
+	rp->cols = 80;
+
+	INIT_LIST_HEAD(&rp->req_queue);
+	INIT_LIST_HEAD(&rp->view_list);
+
+	/*
+	 * Add device to list and find the smallest unused minor
+	 * number for it.
+	 */
+	spin_lock(&raw3270_lock);
+	/* Keep the list sorted. */
+	minor = 0;
+	rp->minor = -1;
+	list_for_each(l, &raw3270_devices) {
+		tmp = list_entry(l, struct raw3270, list);
+		if (tmp->minor > minor) {
+			rp->minor = minor;
+			__list_add(&rp->list, l->prev, l);
+			break;
+		}
+		minor++;
+	}
+	if (rp->minor == -1 && minor < RAW3270_MAXDEVS) {
+		rp->minor = minor;
+		list_add_tail(&rp->list, &raw3270_devices);
+	}
+	spin_unlock(&raw3270_lock);
+	/* No free minor number? Then give up. */
+	if (rp->minor == -1)
+		return -EUSERS;
+	rp->cdev = cdev;
+	cdev->dev.driver_data = rp;
+	cdev->handler = raw3270_irq;
+	return 0;
+}
+
+#ifdef CONFIG_TN3270_CONSOLE
+/*
+ * Setup 3270 device configured as console.
+ */
+struct raw3270 *
+raw3270_setup_console(struct ccw_device *cdev)
+{
+	struct raw3270 *rp;
+	char *ascebc;
+	int rc;
+
+	rp = (struct raw3270 *) alloc_bootmem(sizeof(struct raw3270));
+	ascebc = (char *) alloc_bootmem(256);
+	rc = raw3270_setup_device(cdev, rp, ascebc);
+	if (rc)
+		return ERR_PTR(rc);
+	set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags);
+	raw3270_reset_device(rp);
+	raw3270_size_device(rp);
+	raw3270_reset_device(rp);
+	return rp;
+}
+
+void
+raw3270_wait_cons_dev(struct raw3270 *rp)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
+	wait_cons_dev();
+	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
+}
+
+#endif
+
+/*
+ * Create a 3270 device structure.
+ */
+static struct raw3270 *
+raw3270_create_device(struct ccw_device *cdev)
+{
+	struct raw3270 *rp;
+	char *ascebc;
+	int rc;
+
+	rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL);
+	if (!rp)
+		return ERR_PTR(-ENOMEM);
+	ascebc = kmalloc(256, GFP_KERNEL);
+	if (!ascebc) {
+		kfree(rp);
+		return ERR_PTR(-ENOMEM);
+	}
+	rc = raw3270_setup_device(cdev, rp, ascebc);
+	if (rc) {
+		kfree(rp->ascebc);
+		kfree(rp);
+		rp = ERR_PTR(rc);
+	}
+	/* Get reference to ccw_device structure. */
+	get_device(&cdev->dev);
+	return rp;
+}
+
+/*
+ * Activate a view.
+ */
+int
+raw3270_activate_view(struct raw3270_view *view)
+{
+	struct raw3270 *rp;
+	struct raw3270_view *oldview, *nv;
+	unsigned long flags;
+	int rc;
+
+	rp = view->dev;
+	if (!rp)
+		return -ENODEV;
+	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
+	if (rp->view == view)
+		rc = 0;
+	else if (test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags))
+		rc = -ENODEV;
+	else {
+		oldview = 0;
+		if (rp->view) {
+			oldview = rp->view;
+			oldview->fn->deactivate(oldview);
+		}
+		rp->view = view;
+		rc = view->fn->activate(view);
+		if (rc) {
+			/* Didn't work. Try to reactivate the old view. */
+			rp->view = oldview;
+			if (oldview->fn->activate(oldview) != 0) {
+				/* Didn't work as well. Try any other view. */
+				list_for_each_entry(nv, &rp->view_list, list)
+					if (nv != view && nv != oldview) {
+						rp->view = nv;
+						if (nv->fn->activate(nv) == 0)
+							break;
+						rp->view = 0;
+					}
+			}
+		}
+	}
+	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
+	return rc;
+}
+
+/*
+ * Deactivate current view.
+ */
+void
+raw3270_deactivate_view(struct raw3270_view *view)
+{
+	unsigned long flags;
+	struct raw3270 *rp;
+
+	rp = view->dev;
+	if (!rp)
+		return;
+	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
+	if (rp->view == view) {
+		view->fn->deactivate(view);
+		rp->view = 0;
+		/* Move deactivated view to end of list. */
+		list_del_init(&view->list);
+		list_add_tail(&view->list, &rp->view_list);
+		/* Try to activate another view. */
+		if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
+			list_for_each_entry(view, &rp->view_list, list)
+				if (view->fn->activate(view) == 0) {
+					rp->view = view;
+					break;
+				}
+		}
+	}
+	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
+}
+
+/*
+ * Add view to device with minor "minor".
+ */
+int
+raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor)
+{
+	unsigned long flags;
+	struct raw3270 *rp;
+	int rc;
+
+	spin_lock(&raw3270_lock);
+	rc = -ENODEV;
+	list_for_each_entry(rp, &raw3270_devices, list) {
+		if (rp->minor != minor)
+			continue;
+		spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
+		if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
+			atomic_set(&view->ref_count, 2);
+			view->dev = rp;
+			view->fn = fn;
+			view->model = rp->model;
+			view->rows = rp->rows;
+			view->cols = rp->cols;
+			view->ascebc = rp->ascebc;
+			spin_lock_init(&view->lock);
+			list_add_tail(&view->list, &rp->view_list);
+			rc = 0;
+		}
+		spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
+		break;
+	}
+	spin_unlock(&raw3270_lock);
+	return rc;
+}
+
+/*
+ * Find specific view of device with minor "minor".
+ */
+struct raw3270_view *
+raw3270_find_view(struct raw3270_fn *fn, int minor)
+{
+	struct raw3270 *rp;
+	struct raw3270_view *view, *tmp;
+	unsigned long flags;
+
+	spin_lock(&raw3270_lock);
+	view = ERR_PTR(-ENODEV);
+	list_for_each_entry(rp, &raw3270_devices, list) {
+		if (rp->minor != minor)
+			continue;
+		spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
+		if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
+			list_for_each_entry(tmp, &rp->view_list, list) {
+				if (tmp->fn == fn) {
+					raw3270_get_view(tmp);
+					view = tmp;
+				}
+			}
+		} else
+			view = ERR_PTR(-ENOENT);
+		spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
+		break;
+	}
+	spin_unlock(&raw3270_lock);
+	return view;
+}
+
+/*
+ * Remove view from device and free view structure via call to view->fn->free.
+ */
+void
+raw3270_del_view(struct raw3270_view *view)
+{
+	unsigned long flags;
+	struct raw3270 *rp;
+
+	rp = view->dev;
+	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
+	if (rp->view == view) {
+		view->fn->deactivate(view);
+		rp->view = 0;
+	}
+	list_del_init(&view->list);
+	if (!rp->view && !test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
+		/* Try to activate another view. */
+		list_for_each_entry(view, &rp->view_list, list)
+			if (view->fn->activate(view) == 0) {
+				rp->view = view;
+				break;
+			}
+	}
+	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
+	/* Wait for reference counter to drop to zero. */
+	atomic_dec(&view->ref_count);
+	wait_event(raw3270_wait_queue, atomic_read(&view->ref_count) == 0);
+	if (view->fn->free)
+		view->fn->free(view);
+}
+
+/*
+ * Remove a 3270 device structure.
+ */
+static void
+raw3270_delete_device(struct raw3270 *rp)
+{
+	struct ccw_device *cdev;
+
+	/* Remove from device chain. */
+	spin_lock(&raw3270_lock);
+	list_del_init(&rp->list);
+	spin_unlock(&raw3270_lock);
+
+	/* Disconnect from ccw_device. */
+	cdev = rp->cdev;
+	rp->cdev = 0;
+	cdev->dev.driver_data = 0;
+	cdev->handler = 0;
+
+	/* Put ccw_device structure. */
+	put_device(&cdev->dev);
+
+	/* Now free raw3270 structure. */
+	kfree(rp->ascebc);
+	kfree(rp);
+}
+
+static int
+raw3270_probe (struct ccw_device *cdev)
+{
+	return 0;
+}
+
+/*
+ * Additional attributes for a 3270 device
+ */
+static ssize_t
+raw3270_model_show(struct device *dev, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%i\n",
+			((struct raw3270 *) dev->driver_data)->model);
+}
+static DEVICE_ATTR(model, 0444, raw3270_model_show, 0);
+
+static ssize_t
+raw3270_rows_show(struct device *dev, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%i\n",
+			((struct raw3270 *) dev->driver_data)->rows);
+}
+static DEVICE_ATTR(rows, 0444, raw3270_rows_show, 0);
+
+static ssize_t
+raw3270_columns_show(struct device *dev, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%i\n",
+			((struct raw3270 *) dev->driver_data)->cols);
+}
+static DEVICE_ATTR(columns, 0444, raw3270_columns_show, 0);
+
+static struct attribute * raw3270_attrs[] = {
+	&dev_attr_model.attr,
+	&dev_attr_rows.attr,
+	&dev_attr_columns.attr,
+	NULL,
+};
+
+static struct attribute_group raw3270_attr_group = {
+	.attrs = raw3270_attrs,
+};
+
+static void
+raw3270_create_attributes(struct raw3270 *rp)
+{
+	//FIXME: check return code
+	sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group);
+}
+
+/* Hackish. A notifier chain would be cleaner. */
+extern void tty3270_notifier(int index, int active);
+
+/*
+ * Set 3270 device online.
+ */
+static int
+raw3270_set_online (struct ccw_device *cdev)
+{
+	struct raw3270 *rp;
+
+	rp = raw3270_create_device(cdev);
+	if (IS_ERR(rp))
+		return PTR_ERR(rp);
+	raw3270_reset_device(rp);
+	raw3270_size_device(rp);
+	raw3270_reset_device(rp);
+	raw3270_create_attributes(rp);
+	tty3270_notifier(rp->minor, 1);
+	return 0;
+}
+
+/*
+ * Remove 3270 device structure.
+ */
+static void
+raw3270_remove (struct ccw_device *cdev)
+{
+	unsigned long flags;
+	struct raw3270 *rp;
+	struct raw3270_view *v;
+
+	rp = cdev->dev.driver_data;
+	set_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags);
+
+	sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group);
+
+	/* Deactivate current view and remove all views. */
+	spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+	if (rp->view) {
+		rp->view->fn->deactivate(rp->view);
+		rp->view = 0;
+	}
+	while (!list_empty(&rp->view_list)) {
+		v = list_entry(rp->view_list.next, struct raw3270_view, list);
+		if (v->fn->release)
+			v->fn->release(v);
+		spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+		raw3270_del_view(v);
+		spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+	}
+	spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+
+	tty3270_notifier(rp->minor, 0);
+
+	/* Reset 3270 device. */
+	raw3270_reset_device(rp);
+	/* And finally remove it. */
+	raw3270_delete_device(rp);
+}
+
+/*
+ * Set 3270 device offline.
+ */
+static int
+raw3270_set_offline (struct ccw_device *cdev)
+{
+	struct raw3270 *rp;
+
+	rp = cdev->dev.driver_data;
+	if (test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags))
+		return -EBUSY;
+	raw3270_remove(cdev);
+	return 0;
+}
+
+static struct ccw_device_id raw3270_id[] = {
+	{ CCW_DEVICE(0x3270, 0) },
+	{ CCW_DEVICE(0x3271, 0) },
+	{ CCW_DEVICE(0x3272, 0) },
+	{ CCW_DEVICE(0x3273, 0) },
+	{ CCW_DEVICE(0x3274, 0) },
+	{ CCW_DEVICE(0x3275, 0) },
+	{ CCW_DEVICE(0x3276, 0) },
+	{ CCW_DEVICE(0x3277, 0) },
+	{ CCW_DEVICE(0x3278, 0) },
+	{ CCW_DEVICE(0x3279, 0) },
+	{ CCW_DEVICE(0x3174, 0) },
+	{ /* end of list */ },
+};
+
+static struct ccw_driver raw3270_ccw_driver = {
+	.name		= "3270",
+	.owner		= THIS_MODULE,
+	.ids		= raw3270_id,
+	.probe		= &raw3270_probe,
+	.remove		= &raw3270_remove,
+	.set_online	= &raw3270_set_online,
+	.set_offline	= &raw3270_set_offline,
+};
+
+int
+raw3270_init(void)
+{
+	struct raw3270 *rp;
+	int rc;
+
+	if (raw3270_registered)
+		return 0;
+	raw3270_registered = 1;
+	rc = ccw_driver_register(&raw3270_ccw_driver);
+	if (rc == 0) {
+		/* Create attributes for early (= console) device. */
+		spin_lock(&raw3270_lock);
+		list_for_each_entry(rp, &raw3270_devices, list) {
+			get_device(&rp->cdev->dev);
+			raw3270_create_attributes(rp);
+			tty3270_notifier(rp->minor, 1);
+		}
+		spin_unlock(&raw3270_lock);
+	}
+	return rc;
+}
+
+void
+raw3270_exit(void)
+{
+	ccw_driver_unregister(&raw3270_ccw_driver);
+}
+
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(raw3270_init);
+EXPORT_SYMBOL(raw3270_exit);
+EXPORT_SYMBOL(raw3270_request_alloc);
+EXPORT_SYMBOL(raw3270_request_free);
+EXPORT_SYMBOL(raw3270_request_reset);
+EXPORT_SYMBOL(raw3270_request_set_cmd);
+EXPORT_SYMBOL(raw3270_request_add_data);
+EXPORT_SYMBOL(raw3270_request_set_data);
+EXPORT_SYMBOL(raw3270_request_set_idal);
+EXPORT_SYMBOL(raw3270_buffer_address);
+EXPORT_SYMBOL(raw3270_add_view);
+EXPORT_SYMBOL(raw3270_del_view);
+EXPORT_SYMBOL(raw3270_find_view);
+EXPORT_SYMBOL(raw3270_activate_view);
+EXPORT_SYMBOL(raw3270_deactivate_view);
+EXPORT_SYMBOL(raw3270_start);
+EXPORT_SYMBOL(raw3270_start_irq);
diff -puN /dev/null drivers/s390/char/raw3270.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/s390/char/raw3270.h	Thu Jan  8 14:11:35 2004
@@ -0,0 +1,273 @@
+/*
+ *  drivers/s390/char/raw3270.h
+ *    IBM/3270 Driver
+ *
+ *  Author(s):
+ *    Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
+ *    Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *	-- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ */
+
+#include <asm/idals.h>
+#include <asm/ioctl.h>
+
+/* ioctls for fullscreen 3270 */
+#define TUBICMD		_IO('3', 3)	/* set ccw command for fs reads. */
+#define TUBOCMD		_IO('3', 4)	/* set ccw command for fs writes. */
+#define TUBGETI		_IO('3', 7)	/* get ccw command for fs reads. */
+#define TUBGETO		_IO('3', 8)	/* get ccw command for fs writes. */
+#define TUBSETMOD	_IO('3',12)	/* FIXME: what does it do ?*/
+#define TUBGETMOD	_IO('3',13)	/* FIXME: what does it do ?*/
+
+/* Local Channel Commands */
+#define TC_WRITE	0x01		/* Write */
+#define TC_EWRITE	0x05		/* Erase write */
+#define TC_READMOD	0x06		/* Read modified */
+#define TC_EWRITEA	0x0d		/* Erase write alternate */
+#define TC_WRITESF	0x11		/* Write structured field */
+
+/* Buffer Control Orders */
+#define TO_SF		0x1d		/* Start field */
+#define TO_SBA		0x11		/* Set buffer address */
+#define TO_IC		0x13		/* Insert cursor */
+#define TO_PT		0x05		/* Program tab */
+#define TO_RA		0x3c		/* Repeat to address */
+#define TO_SFE		0x29		/* Start field extended */
+#define TO_EUA		0x12		/* Erase unprotected to address */
+#define TO_MF		0x2c		/* Modify field */
+#define TO_SA		0x28		/* Set attribute */
+
+/* Field Attribute Bytes */
+#define TF_INPUT	0x40		/* Visible input */
+#define TF_INPUTN	0x4c		/* Invisible input */
+#define TF_INMDT	0xc1		/* Visible, Set-MDT */
+#define TF_LOG		0x60
+
+/* Character Attribute Bytes */
+#define TAT_RESET	0x00
+#define TAT_FIELD	0xc0
+#define TAT_EXTHI	0x41
+#define TAT_COLOR	0x42
+#define TAT_CHARS	0x43
+#define TAT_TRANS	0x46
+
+/* Extended-Highlighting Bytes */
+#define TAX_RESET	0x00
+#define TAX_BLINK	0xf1
+#define TAX_REVER	0xf2
+#define TAX_UNDER	0xf4
+
+/* Reset value */
+#define TAR_RESET	0x00
+
+/* Color values */
+#define TAC_RESET	0x00
+#define TAC_BLUE	0xf1
+#define TAC_RED		0xf2
+#define TAC_PINK	0xf3
+#define TAC_GREEN	0xf4
+#define TAC_TURQ	0xf5
+#define TAC_YELLOW	0xf6
+#define TAC_WHITE	0xf7
+#define TAC_DEFAULT	0x00
+
+/* Write Control Characters */
+#define TW_NONE		0x40		/* No particular action */
+#define TW_KR		0xc2		/* Keyboard restore */
+#define TW_PLUSALARM	0x04		/* Add this bit for alarm */
+
+#define RAW3270_MAXDEVS	256
+
+int raw3270_init(void);
+void raw3270_exit(void);
+
+/* For TUBGETMOD and TUBSETMOD. Should include. */
+struct raw3270_iocb {
+	short model;
+	short line_cnt;
+	short col_cnt;
+	short pf_cnt;
+	short re_cnt;
+	short map;
+};
+
+struct raw3270;
+struct raw3270_view;
+
+/* 3270 CCW request */
+struct raw3270_request {
+	struct list_head list;		/* list head for request queueing. */
+	struct raw3270_view *view;	/* view of this request */
+	struct ccw1 ccw;		/* single ccw. */
+	void *buffer;			/* output buffer. */
+	size_t size;			/* size of output buffer. */
+	int rescnt;			/* residual count from devstat. */
+	int rc;				/* return code for this request. */
+
+	/* Callback for delivering final status. */
+	void (*callback)(struct raw3270_request *, void *);
+	void *callback_data;
+};
+
+struct raw3270_request *raw3270_request_alloc(size_t size);
+struct raw3270_request *raw3270_request_alloc_bootmem(size_t size);
+void raw3270_request_free(struct raw3270_request *);
+void raw3270_request_reset(struct raw3270_request *);
+void raw3270_request_set_cmd(struct raw3270_request *, u8 cmd);
+int  raw3270_request_add_data(struct raw3270_request *, void *, size_t);
+void raw3270_request_set_data(struct raw3270_request *, void *, size_t);
+void raw3270_request_set_idal(struct raw3270_request *, struct idal_buffer *);
+
+static inline int
+raw3270_request_final(struct raw3270_request *rq)
+{
+	return list_empty(&rq->list);
+}
+
+void raw3270_buffer_address(struct raw3270 *, char *, unsigned short);
+
+/* Return value of *intv (see raw3270_fn below) can be one of the following: */
+#define RAW3270_IO_DONE		0	/* request finished */
+#define RAW3270_IO_BUSY		1	/* request still active */
+#define RAW3270_IO_RETRY	2	/* retry current request */
+#define RAW3270_IO_STOP		3	/* kill current request */
+
+/*
+ * Functions of a 3270 view.
+ */
+struct raw3270_fn {
+	int  (*activate)(struct raw3270_view *);
+	void (*deactivate)(struct raw3270_view *);
+	int  (*intv)(struct raw3270_view *,
+		     struct raw3270_request *, struct irb *);
+	void (*release)(struct raw3270_view *);
+	void (*free)(struct raw3270_view *);
+};
+
+/*
+ * View structure chaining. The raw3270_view structure is meant to
+ * be embedded at the start of the real view data structure, e.g.:
+ *   struct example {
+ *     struct raw3270_view view;
+ *     ...
+ *   };
+ */
+struct raw3270_view {
+	struct list_head list;
+	spinlock_t lock;
+	atomic_t ref_count;
+	struct raw3270 *dev;
+	struct raw3270_fn *fn;
+	unsigned int model;
+	unsigned int rows, cols;	/* # of rows & colums of the view */
+	unsigned char *ascebc;		/* ascii -> ebcdic table */
+};
+
+int raw3270_add_view(struct raw3270_view *, struct raw3270_fn *, int);
+int raw3270_activate_view(struct raw3270_view *);
+void raw3270_del_view(struct raw3270_view *);
+void raw3270_deactivate_view(struct raw3270_view *);
+struct raw3270_view *raw3270_find_view(struct raw3270_fn *, int);
+int raw3270_start(struct raw3270_view *, struct raw3270_request *);
+int raw3270_start_irq(struct raw3270_view *, struct raw3270_request *);
+
+/* Reference count inliner for view structures. */
+static inline void
+raw3270_get_view(struct raw3270_view *view)
+{
+	atomic_inc(&view->ref_count);
+}
+
+extern wait_queue_head_t raw3270_wait_queue;
+
+static inline void
+raw3270_put_view(struct raw3270_view *view)
+{
+	if (atomic_dec_return(&view->ref_count) == 0)
+		wake_up(&raw3270_wait_queue);
+}
+
+struct raw3270 *raw3270_setup_console(struct ccw_device *cdev);
+void raw3270_wait_cons_dev(struct raw3270 *);
+
+/*
+ * Little memory allocator for string objects. 
+ */
+struct string
+{
+	struct list_head list;
+	struct list_head update;
+	unsigned long size;
+	unsigned long len;
+	char string[0];
+} __attribute__ ((aligned(8)));
+
+static inline struct string *
+alloc_string(struct list_head *free_list, unsigned long len)
+{
+	struct string *cs, *tmp;
+	unsigned long size;
+
+	size = (len + 7L) & -8L;
+	list_for_each_entry(cs, free_list, list) {
+		if (cs->size < size)
+			continue;
+		if (cs->size > size + sizeof(struct string)) {
+			char *endaddr = (char *) (cs + 1) + cs->size;
+			tmp = (struct string *) (endaddr - size) - 1;
+			tmp->size = size;
+			cs->size -= size + sizeof(struct string);
+			cs = tmp;
+		} else
+			list_del(&cs->list);
+		cs->len = len;
+		INIT_LIST_HEAD(&cs->list);
+		INIT_LIST_HEAD(&cs->update);
+		return cs;
+	}
+	return 0;
+}
+
+static inline unsigned long
+free_string(struct list_head *free_list, struct string *cs)
+{
+	struct string *tmp;
+	struct list_head *p, *left;
+
+	/* Find out the left neighbour in free memory list. */
+	left = free_list;
+	list_for_each(p, free_list) {
+		if (list_entry(p, struct string, list) > cs)
+			break;
+		left = p;
+	}
+	/* Try to merge with right neighbour = next element from left. */
+	if (left->next != free_list) {
+		tmp = list_entry(left->next, struct string, list);
+		if ((char *) (cs + 1) + cs->size == (char *) tmp) {
+			list_del(&tmp->list);
+			cs->size += tmp->size + sizeof(struct string);
+		}
+	}
+	/* Try to merge with left neighbour. */
+	if (left != free_list) {
+		tmp = list_entry(left, struct string, list);
+		if ((char *) (tmp + 1) + tmp->size == (char *) cs) {
+			tmp->size += cs->size + sizeof(struct string);
+			return tmp->size;
+		}
+	}
+	__list_add(&cs->list, left, left->next);
+	return cs->size;
+}
+
+static inline void
+add_string_memory(struct list_head *free_list, void *mem, unsigned long size)
+{
+	struct string *cs;
+
+	cs = (struct string *) mem;
+	cs->size = size - sizeof(struct string);
+	free_string(free_list, cs);
+}
+
diff -puN /dev/null drivers/s390/char/tty3270.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/s390/char/tty3270.c	Thu Jan  8 14:11:35 2004
@@ -0,0 +1,1828 @@
+/*
+ *  drivers/s390/char/tty3270.c
+ *    IBM/3270 Driver - tty functions.
+ *
+ *  Author(s):
+ *    Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
+ *    Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *	-- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/tty.h>
+#include <linux/vt_kern.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/interrupt.h>
+
+#include <linux/slab.h>
+#include <linux/bootmem.h>
+
+#include <asm/ccwdev.h>
+#include <asm/cio.h>
+#include <asm/ebcdic.h>
+#include <asm/uaccess.h>
+
+
+#include "raw3270.h"
+#include "keyboard.h"
+
+#define TTY3270_CHAR_BUF_SIZE 256
+#define TTY3270_OUTPUT_BUFFER_SIZE 1024
+#define TTY3270_STRING_PAGES 5
+
+struct tty_driver *tty3270_driver;
+
+struct raw3270_fn tty3270_fn;
+
+struct tty3270_cell {
+	unsigned char character;
+	unsigned char highlight;
+	unsigned char f_color;
+};
+
+struct tty3270_line {
+	struct tty3270_cell *cells;
+	int len;
+};
+
+#define ESCAPE_NPAR 8
+
+/*
+ * The main tty view data structure.
+ * FIXME:
+ * 1) describe line orientation & lines list concept against screen
+ * 2) describe conversion of screen to lines
+ * 3) describe line format.
+ */
+struct tty3270 {
+	struct raw3270_view view;
+	struct tty_struct *tty;		/* Pointer to tty structure */
+	void **freemem_pages;		/* Array of pages used for freemem. */
+	struct list_head freemem;	/* List of free memory for strings. */
+
+	/* Output stuff. */
+	struct list_head lines;		/* List of lines. */
+	struct list_head update;	/* List of lines to update. */
+	unsigned char wcc;		/* Write control character. */
+	int nr_lines;			/* # lines in list. */
+	int nr_up;			/* # lines up in history. */
+	unsigned long update_flags;	/* Update indication bits. */
+	struct string *status;		/* Lower right of display. */
+	struct raw3270_request *write;	/* Single write request. */
+	struct timer_list timer;	/* Output delay timer. */
+
+	/* Current tty screen. */
+	unsigned int cx, cy;		/* Current output position. */
+	unsigned int highlight;		/* Blink/reverse/underscore */
+	unsigned int f_color;		/* Foreground color */
+	struct tty3270_line *screen;
+
+	/* Input stuff. */
+	struct string *prompt;		/* Output string for input area. */
+	struct string *input;		/* Input string for read request. */
+	struct raw3270_request *read;	/* Single read request. */
+	struct raw3270_request *kreset;	/* Single keyboard reset request. */
+	unsigned char inattr;		/* Visible/invisible input. */
+	int throttle, attn;		/* tty throttle/unthrottle. */
+	struct tasklet_struct readlet;	/* Tasklet to issue read request. */
+	struct kbd_data *kbd;		/* key_maps stuff. */
+
+	/* Escape sequence parsing. */
+	int esc_state, esc_ques, esc_npar;
+	int esc_par[ESCAPE_NPAR];
+	unsigned int saved_cx, saved_cy;
+	unsigned int saved_highlight, saved_f_color;
+
+	/* Command recalling. */
+	struct list_head rcl_lines;	/* List of recallable lines. */
+	struct list_head *rcl_walk;	/* Point in rcl_lines list. */
+	int rcl_nr, rcl_max;		/* Number/max number of rcl_lines. */
+
+	/* Character array for put_char/flush_chars. */
+	unsigned int char_count;
+	char char_buf[TTY3270_CHAR_BUF_SIZE];
+};
+
+/* tty3270->update_flags. See tty3270_update for details. */
+#define TTY_UPDATE_ERASE	1	/* Use EWRITEA instead of WRITE. */
+#define TTY_UPDATE_LIST		2	/* Update lines in tty3270->update. */
+#define TTY_UPDATE_INPUT	4	/* Update input line. */
+#define TTY_UPDATE_STATUS	8	/* Update status line. */
+#define TTY_UPDATE_ALL		15
+
+static void tty3270_update(struct tty3270 *);
+
+/*
+ * Setup timeout for a device. On timeout trigger an update.
+ */
+void
+tty3270_set_timer(struct tty3270 *tp, int expires)
+{
+	if (expires == 0) {
+		if (timer_pending(&tp->timer)) {
+			raw3270_put_view(&tp->view);
+			del_timer(&tp->timer);
+		}
+		return;
+	}
+	if (timer_pending(&tp->timer)) {
+		if (mod_timer(&tp->timer, jiffies + expires))
+			return;
+	}
+	raw3270_get_view(&tp->view);
+	tp->timer.function = (void (*)(unsigned long)) tty3270_update;
+	tp->timer.data = (unsigned long) tp;
+	tp->timer.expires = jiffies + expires;
+	add_timer(&tp->timer);
+}
+
+/*
+ * The input line are the two last lines of the screen.
+ */
+static void
+tty3270_update_prompt(struct tty3270 *tp, char *input, int count)
+{
+	struct string *line;
+	unsigned int off;
+
+	line = tp->prompt;
+	if (count != 0)
+		line->string[5] = TF_INMDT;
+	else
+		line->string[5] = tp->inattr;
+	if (count > tp->view.cols * 2 - 11)
+		count = tp->view.cols * 2 - 11;
+	memcpy(line->string + 6, input, count);
+	line->string[6 + count] = TO_IC;
+	/* Clear to end of input line. */
+	if (count < tp->view.cols * 2 - 11) {
+		line->string[7 + count] = TO_RA;
+		line->string[10 + count] = 0;
+		off = tp->view.cols * tp->view.rows - 9;
+		raw3270_buffer_address(tp->view.dev, line->string+count+8, off);
+		line->len = 11 + count;
+	} else
+		line->len = 7 + count;
+	tp->update_flags |= TTY_UPDATE_INPUT;
+}
+
+static void
+tty3270_create_prompt(struct tty3270 *tp)
+{
+	static const unsigned char blueprint[] =
+		{ TO_SBA, 0, 0, 0x6e, TO_SF, TF_INPUT,
+		  /* empty input string */
+		  TO_IC, TO_RA, 0, 0, 0 };
+	struct string *line;
+	unsigned int offset;
+
+	line = alloc_string(&tp->freemem,
+			    sizeof(blueprint) + tp->view.cols * 2 - 9);
+	tp->prompt = line;
+	tp->inattr = TF_INPUT;
+	/* Copy blueprint to status line */
+	memcpy(line->string, blueprint, sizeof(blueprint));
+	line->len = sizeof(blueprint);
+	/* Set output offsets. */
+	offset = tp->view.cols * (tp->view.rows - 2);
+	raw3270_buffer_address(tp->view.dev, line->string + 1, offset);
+	offset = tp->view.cols * tp->view.rows - 9;
+	raw3270_buffer_address(tp->view.dev, line->string + 8, offset);
+
+	/* Allocate input string for reading. */
+	tp->input = alloc_string(&tp->freemem, tp->view.cols * 2 - 9 + 6);
+}
+
+/*
+ * The status line is the last line of the screen. It shows the string
+ * "Running"/"Holding" in the lower right corner of the screen.
+ */
+static void
+tty3270_update_status(struct tty3270 * tp)
+{
+	char *str;
+
+	str = (tp->nr_up != 0) ? "History" : "Running";
+	memcpy(tp->status->string + 8, str, 7);
+	codepage_convert(tp->view.ascebc, tp->status->string + 8, 7);
+	tp->update_flags |= TTY_UPDATE_STATUS;
+}
+
+static void
+tty3270_create_status(struct tty3270 * tp)
+{
+	static const unsigned char blueprint[] =
+		{ TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, TAC_GREEN,
+		  0, 0, 0, 0, 0, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR,
+		  TAC_RESET };
+	struct string *line;
+	unsigned int offset;
+
+	line = alloc_string(&tp->freemem,sizeof(blueprint));
+	tp->status = line;
+	/* Copy blueprint to status line */
+	memcpy(line->string, blueprint, sizeof(blueprint));
+	/* Set address to start of status string (= last 9 characters). */
+	offset = tp->view.cols * tp->view.rows - 9;
+	raw3270_buffer_address(tp->view.dev, line->string + 1, offset);
+}
+
+/*
+ * Set output offsets to 3270 datastream fragment of a tty string.
+ * (TO_SBA offset at the start and TO_RA offset at the end of the string)
+ */
+static void
+tty3270_update_string(struct tty3270 *tp, struct string *line, int nr)
+{
+	unsigned char *cp;
+
+	raw3270_buffer_address(tp->view.dev, line->string + 1,
+			       tp->view.cols * nr);
+	cp = line->string + line->len - 4;
+	if (*cp == TO_RA)
+		raw3270_buffer_address(tp->view.dev, cp + 1,
+				       tp->view.cols * (nr + 1));
+}
+
+/*
+ * Rebuild update list to print all lines.
+ */
+static void
+tty3270_rebuild_update(struct tty3270 *tp)
+{
+	struct string *s, *n;
+	int line, nr_up;
+
+	/* 
+	 * Throw away update list and create a new one,
+	 * containing all lines that will fit on the screen.
+	 */
+	list_for_each_entry_safe(s, n, &tp->update, update)
+		list_del_init(&s->update);
+	line = tp->view.rows - 3;
+	nr_up = tp->nr_up;
+	list_for_each_entry_reverse(s, &tp->lines, list) {
+		if (nr_up > 0) {
+			nr_up--;
+			continue;
+		}
+		tty3270_update_string(tp, s, line);
+		list_add(&s->update, &tp->update);
+		if (--line < 0)
+			break;
+	}
+	tp->update_flags |= TTY_UPDATE_LIST;
+}
+
+/*
+ * Alloc string for size bytes. If there is not enough room in
+ * freemem, free strings until there is room.
+ */
+static struct string *
+tty3270_alloc_string(struct tty3270 *tp, size_t size)
+{
+	struct string *s, *n;
+
+	s = alloc_string(&tp->freemem, size);
+	if (s)
+		return s;
+	list_for_each_entry_safe(s, n, &tp->lines, list) {
+		BUG_ON(tp->nr_lines <= tp->view.rows - 2);
+		list_del(&s->list);
+		if (!list_empty(&s->update))
+			list_del(&s->update);
+		tp->nr_lines--;
+		if (free_string(&tp->freemem, s) >= size)
+			break;
+	}
+	s = alloc_string(&tp->freemem, size);
+	BUG_ON(!s);
+	if (tp->nr_up != 0 &&
+	    tp->nr_up + tp->view.rows - 2 >= tp->nr_lines) {
+		tp->nr_up = tp->nr_lines - tp->view.rows + 2;
+		tty3270_rebuild_update(tp);
+		tty3270_update_status(tp);
+	}
+	return s;
+}
+
+/*
+ * Add an empty line to the list.
+ */
+static void
+tty3270_blank_line(struct tty3270 *tp)
+{
+	static const unsigned char blueprint[] =
+		{ TO_SBA, 0, 0, TO_SA, TAT_EXTHI, TAX_RESET,
+		  TO_SA, TAT_COLOR, TAC_RESET, TO_RA, 0, 0, 0 };
+	struct string *s;
+
+	s = tty3270_alloc_string(tp, sizeof(blueprint));
+	memcpy(s->string, blueprint, sizeof(blueprint));
+	s->len = sizeof(blueprint);
+	list_add_tail(&s->list, &tp->lines);
+	tp->nr_lines++;
+	if (tp->nr_up != 0)
+		tp->nr_up++;
+}
+
+/*
+ * Write request completion callback.
+ */
+static void
+tty3270_write_callback(struct raw3270_request *rq, void *data)
+{
+	struct tty3270 *tp;
+
+	tp = (struct tty3270 *) rq->view;
+	if (rq->rc != 0) {
+		/* Write wasn't successfull. Refresh all. */
+		tty3270_rebuild_update(tp);
+		tp->update_flags = TTY_UPDATE_ALL;
+		tty3270_set_timer(tp, 1);
+	}
+	raw3270_request_reset(rq);
+	xchg(&tp->write, rq);
+}
+
+/*
+ * Update 3270 display.
+ */
+static void
+tty3270_update(struct tty3270 *tp)
+{
+	static char invalid_sba[2] = { 0xff, 0xff };
+	struct raw3270_request *wrq;
+	unsigned long updated;
+	struct string *s, *n;
+	char *sba, *str;
+	int rc, len;
+
+	wrq = xchg(&tp->write, 0);
+	if (!wrq) {
+		tty3270_set_timer(tp, 1);
+		return;
+	}
+
+	spin_lock(&tp->view.lock);
+	updated = 0;
+	if (tp->update_flags & TTY_UPDATE_ERASE) {
+		/* Use erase write alternate to erase display. */
+		raw3270_request_set_cmd(wrq, TC_EWRITEA);
+		updated |= TTY_UPDATE_ERASE;
+	} else
+		raw3270_request_set_cmd(wrq, TC_WRITE);
+
+	raw3270_request_add_data(wrq, &tp->wcc, 1);
+	tp->wcc = TW_NONE;
+
+	/*
+	 * Update status line.
+	 */
+	if (tp->update_flags & TTY_UPDATE_STATUS)
+		if (raw3270_request_add_data(wrq, tp->status->string,
+					     tp->status->len) == 0)
+			updated |= TTY_UPDATE_STATUS;
+
+	/*
+	 * Write input line.
+	 */
+	if (tp->update_flags & TTY_UPDATE_INPUT)
+		if (raw3270_request_add_data(wrq, tp->prompt->string,
+					     tp->prompt->len) == 0)
+			updated |= TTY_UPDATE_INPUT;
+
+	sba = invalid_sba;
+	
+	if (tp->update_flags & TTY_UPDATE_LIST) {
+		/* Write strings in the update list to the screen. */
+		list_for_each_entry_safe(s, n, &tp->update, update) {
+			str = s->string;
+			len = s->len;
+			/*
+			 * Skip TO_SBA at the start of the string if the
+			 * last output position matches the start address
+			 * of this line.
+			 */
+			if (s->string[1] == sba[0] && s->string[2] == sba[1])
+				str += 3, len -= 3;
+			if (raw3270_request_add_data(wrq, str, len) != 0)
+				break;
+			list_del_init(&s->update);
+			sba = s->string + s->len - 3;
+		}
+		if (list_empty(&tp->update))
+			updated |= TTY_UPDATE_LIST;
+	}
+	wrq->callback = tty3270_write_callback;
+	rc = raw3270_start(&tp->view, wrq);
+	if (rc == 0) {
+		tp->update_flags &= ~updated;
+		if (tp->update_flags)
+			tty3270_set_timer(tp, 1);
+	} else {
+		raw3270_request_reset(wrq);
+		xchg(&tp->write, wrq);
+	}
+	spin_unlock(&tp->view.lock);
+	raw3270_put_view(&tp->view);
+}
+
+/*
+ * Command recalling.
+ */
+static void
+tty3270_rcl_add(struct tty3270 *tp, char *input, int len)
+{
+	struct string *s;
+
+	tp->rcl_walk = 0;
+	if (len <= 0)
+		return;
+	if (tp->rcl_nr >= tp->rcl_max) {
+		s = list_entry(tp->rcl_lines.next, struct string, list);
+		list_del(&s->list);
+		free_string(&tp->freemem, s);
+		tp->rcl_nr--;
+	}
+	s = tty3270_alloc_string(tp, len);
+	memcpy(s->string, input, len);
+	list_add_tail(&s->list, &tp->rcl_lines);
+	tp->rcl_nr++;
+}
+
+static void
+tty3270_rcl_backward(struct kbd_data *kbd)
+{
+	struct tty3270 *tp;
+	struct string *s;
+
+	tp = kbd->tty->driver_data;
+	spin_lock_bh(&tp->view.lock);
+	if (tp->inattr == TF_INPUT) {
+		if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines)
+			tp->rcl_walk = tp->rcl_walk->prev;
+		else if (!list_empty(&tp->rcl_lines))
+			tp->rcl_walk = tp->rcl_lines.prev;
+		s = tp->rcl_walk ? 
+			list_entry(tp->rcl_walk, struct string, list) : 0;
+		if (tp->rcl_walk) {
+			s = list_entry(tp->rcl_walk, struct string, list);
+			tty3270_update_prompt(tp, s->string, s->len);
+		} else
+			tty3270_update_prompt(tp, 0, 0);
+		tty3270_set_timer(tp, 1);
+	}
+	spin_unlock_bh(&tp->view.lock);
+}
+
+/*
+ * Deactivate tty view.
+ */
+static void
+tty3270_exit_tty(struct kbd_data *kbd)
+{
+	struct tty3270 *tp;
+
+	tp = kbd->tty->driver_data;
+	raw3270_deactivate_view(&tp->view);
+}
+
+/*
+ * Scroll forward in history.
+ */
+static void
+tty3270_scroll_forward(struct kbd_data *kbd)
+{
+	struct tty3270 *tp;
+	int nr_up;
+
+	tp = kbd->tty->driver_data;
+	spin_lock_bh(&tp->view.lock);
+	nr_up = tp->nr_up - tp->view.rows + 2;
+	if (nr_up < 0)
+		nr_up = 0;
+	if (nr_up != tp->nr_up) {
+		tp->nr_up = nr_up;
+		tty3270_rebuild_update(tp);
+		tty3270_update_status(tp);
+		tty3270_set_timer(tp, 1);
+	}
+	spin_unlock_bh(&tp->view.lock);
+}
+
+/*
+ * Scroll backward in history.
+ */
+static void
+tty3270_scroll_backward(struct kbd_data *kbd)
+{
+	struct tty3270 *tp;
+	int nr_up;
+
+	tp = kbd->tty->driver_data;
+	spin_lock_bh(&tp->view.lock);
+	nr_up = tp->nr_up + tp->view.rows - 2;
+	if (nr_up + tp->view.rows - 2 > tp->nr_lines)
+		nr_up = tp->nr_lines - tp->view.rows + 2;
+	if (nr_up != tp->nr_up) {
+		tp->nr_up = nr_up;
+		tty3270_rebuild_update(tp);
+		tty3270_update_status(tp);
+		tty3270_set_timer(tp, 1);
+	}
+	spin_unlock_bh(&tp->view.lock);
+}
+
+/*
+ * Pass input line to tty.
+ */
+static void
+tty3270_read_tasklet(struct raw3270_request *rrq)
+{
+	static char kreset_data = TW_KR;
+	struct tty3270 *tp;
+	char *input;
+	int len;
+
+	tp = (struct tty3270 *) rrq->view;
+	spin_lock_bh(&tp->view.lock);
+	/*
+	 * Two AID keys are special: For 0x7d (enter) the input line
+	 * has to be emitted to the tty and for 0x6d the screen
+	 * needs to be redrawn.
+	 */
+	input = 0;
+	len = 0;
+	if (tp->input->string[0] == 0x7d) {
+		/* Enter: write input to tty. */
+		input = tp->input->string + 6;
+		len = tp->input->len - 6 - rrq->rescnt;
+		if (tp->inattr != TF_INPUTN)
+			tty3270_rcl_add(tp, input, len);
+		if (tp->nr_up > 0) {
+			tp->nr_up = 0;
+			tty3270_rebuild_update(tp);
+			tty3270_update_status(tp);
+		}
+		/* Clear input area. */
+		tty3270_update_prompt(tp, 0, 0);
+		tty3270_set_timer(tp, 1);
+	} else if (tp->input->string[0] == 0x6d) {
+		/* Display has been cleared. Redraw. */
+		tty3270_rebuild_update(tp);
+		tp->update_flags = TTY_UPDATE_ALL;
+		tty3270_set_timer(tp, 1);
+	}
+	spin_unlock_bh(&tp->view.lock);
+
+	/* Start keyboard reset command. */
+	raw3270_request_reset(tp->kreset);
+	raw3270_request_set_cmd(tp->kreset, TC_WRITE);
+	raw3270_request_add_data(tp->kreset, &kreset_data, 1);
+	raw3270_start(&tp->view, tp->kreset);
+
+	/* Emit input string. */
+	if (tp->tty) {
+		while (len-- > 0)
+			kbd_keycode(tp->kbd, *input++);
+		/* Emit keycode for AID byte. */
+		kbd_keycode(tp->kbd, 256 + tp->input->string[0]);
+	}
+
+	raw3270_request_reset(rrq);
+	xchg(&tp->read, rrq);
+	raw3270_put_view(&tp->view);
+}
+
+/*
+ * Read request completion callback.
+ */
+static void
+tty3270_read_callback(struct raw3270_request *rq, void *data)
+{
+	raw3270_get_view(rq->view);
+	/* Schedule tasklet to pass input to tty. */
+	tasklet_schedule(&((struct tty3270 *) rq->view)->readlet);
+}
+
+/*
+ * Issue a read request. Call with device lock.
+ */
+static void
+tty3270_issue_read(struct tty3270 *tp, int lock)
+{
+	struct raw3270_request *rrq;
+	int rc;
+
+	rrq = xchg(&tp->read, 0);
+	if (!rrq)
+		/* Read already scheduled. */
+		return;
+	rrq->callback = tty3270_read_callback;
+	rrq->callback_data = tp;
+	raw3270_request_set_cmd(rrq, TC_READMOD);
+	raw3270_request_set_data(rrq, tp->input->string, tp->input->len);
+	/* Issue the read modified request. */
+	if (lock) {
+		rc = raw3270_start(&tp->view, rrq);
+	} else
+		rc = raw3270_start_irq(&tp->view, rrq);
+	if (rc) {
+		raw3270_request_reset(rrq);
+		xchg(&tp->read, rrq);
+	}
+}
+
+/*
+ * Switch to the tty view.
+ */
+static int
+tty3270_activate(struct raw3270_view *view)
+{
+	struct tty3270 *tp;
+	unsigned long flags;
+
+	tp = (struct tty3270 *) view;
+	spin_lock_irqsave(&tp->view.lock, flags);
+	tp->nr_up = 0;
+	tty3270_rebuild_update(tp);
+	tty3270_update_status(tp);
+	tp->update_flags = TTY_UPDATE_ALL;
+	tty3270_set_timer(tp, 1);
+	spin_unlock_irqrestore(&tp->view.lock, flags);
+	start_tty(tp->tty);
+	return 0;
+}
+
+static void
+tty3270_deactivate(struct raw3270_view *view)
+{
+	struct tty3270 *tp;
+
+	tp = (struct tty3270 *) view;
+	if (tp && tp->tty)
+		stop_tty(tp->tty);
+}
+
+static int
+tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
+{
+	/* Handle ATTN. Schedule tasklet to read aid. */
+	if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+		if (!tp->throttle)
+			tty3270_issue_read(tp, 0);
+		else
+			tp->attn = 1;
+	}
+
+	if (rq) {
+		if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+			rq->rc = -EIO;
+		else
+			/* Normal end. Copy residual count. */
+			rq->rescnt = irb->scsw.count;
+	}
+	return RAW3270_IO_DONE;
+}
+
+/*
+ * Allocate tty3270 structure.
+ */
+static struct tty3270 *
+tty3270_alloc_view(void)
+{
+	struct tty3270 *tp;
+	int pages;
+
+	tp = kmalloc(sizeof(struct tty3270),GFP_KERNEL);
+	if (!tp)
+		goto out_err;
+	memset(tp, 0, sizeof(struct tty3270));
+	tp->freemem_pages =
+		kmalloc(sizeof(void *) * TTY3270_STRING_PAGES, GFP_KERNEL);
+	if (!tp->freemem_pages)
+		goto out_tp;
+	INIT_LIST_HEAD(&tp->freemem);
+	for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) {
+		tp->freemem_pages[pages] = (void *)
+			__get_free_pages(GFP_KERNEL|GFP_DMA, 0);
+		if (!tp->freemem_pages[pages])
+			goto out_pages;
+		add_string_memory(&tp->freemem,
+				  tp->freemem_pages[pages], PAGE_SIZE);
+	}
+	tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE);
+	if (!tp->write)
+		goto out_pages;
+	tp->read = raw3270_request_alloc(0);
+	if (!tp->read)
+		goto out_write;
+	tp->kreset = raw3270_request_alloc(1);
+	if (!tp->kreset)
+		goto out_read;
+	tp->kbd = kbd_alloc();
+	if (!tp->kbd)
+		goto out_reset;
+	return tp;
+
+out_reset:
+	raw3270_request_free(tp->kreset);
+out_read:
+	raw3270_request_free(tp->read);
+out_write:
+	raw3270_request_free(tp->write);
+out_pages:
+	while (pages--)
+		free_pages((unsigned long) tp->freemem_pages[pages], 0);
+	kfree(tp->freemem_pages);
+out_tp:
+	kfree(tp);
+out_err:
+	return ERR_PTR(-ENOMEM);
+}
+
+/*
+ * Free tty3270 structure.
+ */
+static void
+tty3270_free_view(struct tty3270 *tp)
+{
+	int pages;
+
+	kbd_free(tp->kbd);
+	raw3270_request_free(tp->kreset);
+	raw3270_request_free(tp->read);
+	raw3270_request_free(tp->write);
+	for (pages = 0; pages < TTY3270_STRING_PAGES; pages++)
+		free_pages((unsigned long) tp->freemem_pages[pages], 0);
+	kfree(tp->freemem_pages);
+	kfree(tp);
+}
+
+/*
+ * Allocate tty3270 screen.
+ */
+static int
+tty3270_alloc_screen(struct tty3270 *tp)
+{
+	unsigned long size;
+	int lines;
+
+	size = sizeof(struct tty3270_line) * (tp->view.rows - 2);
+	tp->screen = kmalloc(size, GFP_KERNEL);
+	if (!tp->screen)
+		goto out_err;
+	memset(tp->screen, 0, size);
+	for (lines = 0; lines < tp->view.rows - 2; lines++) {
+		size = sizeof(struct tty3270_cell) * tp->view.cols;
+		tp->screen[lines].cells = kmalloc(size, GFP_KERNEL);
+		if (!tp->screen[lines].cells)
+			goto out_screen;
+		memset(tp->screen[lines].cells, 0, size);
+	}
+	return 0;
+out_screen:
+	while (lines--)
+		kfree(tp->screen[lines].cells);
+	kfree(tp->screen);
+out_err:
+	return -ENOMEM;
+}
+
+/*
+ * Free tty3270 screen.
+ */
+static void
+tty3270_free_screen(struct tty3270 *tp)
+{
+	int lines;
+
+	for (lines = 0; lines < tp->view.rows - 2; lines++)
+		kfree(tp->screen[lines].cells);
+	kfree(tp->screen);
+}
+
+/*
+ * Unlink tty3270 data structure from tty.
+ */
+static void
+tty3270_release(struct raw3270_view *view)
+{
+	struct tty3270 *tp;
+	struct tty_struct *tty;
+
+	tp = (struct tty3270 *) view;
+	tty = tp->tty;
+	if (tty) {
+		tty->driver_data = 0;
+		tp->tty = tp->kbd->tty = 0;
+		tty_hangup(tty);
+		raw3270_put_view(&tp->view);
+	}
+}
+
+/*
+ * Free tty3270 data structure
+ */
+static void
+tty3270_free(struct raw3270_view *view)
+{
+	tty3270_free_screen((struct tty3270 *) view);
+	tty3270_free_view((struct tty3270 *) view);
+}
+
+struct raw3270_fn tty3270_fn = {
+	.activate = tty3270_activate,
+	.deactivate = tty3270_deactivate,
+	.intv = (void *) tty3270_irq,
+	.release = tty3270_release,
+	.free = tty3270_free
+};
+
+/*
+ * This routine is called whenever a 3270 tty is opened.
+ */
+static int
+tty3270_open(struct tty_struct *tty, struct file * filp)
+{
+	struct tty3270 *tp;
+	int i, rc;
+
+	if (tty->count > 1)
+		return 0;
+	/* Check if the tty3270 is already there. */
+	tp = (struct tty3270 *) raw3270_find_view(&tty3270_fn, tty->index);
+	if (!IS_ERR(tp)) {
+		tty->driver_data = tp;
+		tty->winsize.ws_row = tp->view.rows - 2;
+		tty->winsize.ws_col = tp->view.cols;
+		tty->low_latency = 0;
+		tp->tty = tty;
+		tp->kbd->tty = tty;
+		tp->inattr = TF_INPUT;
+		return 0;
+	}
+
+	/* Allocate tty3270 structure on first open. */
+	tp = tty3270_alloc_view();
+	if (IS_ERR(tp))
+		return PTR_ERR(tp);
+
+	INIT_LIST_HEAD(&tp->lines);
+	INIT_LIST_HEAD(&tp->update);
+	INIT_LIST_HEAD(&tp->rcl_lines);
+	tp->rcl_max = 20;
+	init_timer(&tp->timer);
+	tasklet_init(&tp->readlet, 
+		     (void (*)(unsigned long)) tty3270_read_tasklet,
+		     (unsigned long) tp->read);
+
+	rc = raw3270_add_view(&tp->view, &tty3270_fn, tty->index);
+	if (rc) {
+		tty3270_free_view(tp);
+		return rc;
+	}
+
+	rc = tty3270_alloc_screen(tp);
+	if (rc) {
+		raw3270_del_view(&tp->view);
+		raw3270_put_view(&tp->view);
+		return rc;
+	}
+
+	tp->tty = tty;
+	tty->low_latency = 0;
+	tty->driver_data = tp;
+	tty->winsize.ws_row = tp->view.rows - 2;
+	tty->winsize.ws_col = tp->view.cols;
+
+	tty3270_create_prompt(tp);
+	tty3270_create_status(tp);
+	tty3270_update_status(tp);
+
+	/* Create blank line for every line in the tty output area. */
+	for (i = 0; i < tp->view.rows - 2; i++)
+		tty3270_blank_line(tp);
+
+	tp->kbd->tty = tty;
+	tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty;
+	tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward;
+	tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward;
+	tp->kbd->fn_handler[KVAL(K_CONS)] = tty3270_rcl_backward;
+	kbd_ascebc(tp->kbd, tp->view.ascebc);
+
+	raw3270_activate_view(&tp->view);
+	return 0;
+}
+
+/*
+ * This routine is called when the 3270 tty is closed. We wait
+ * for the remaining request to be completed. Then we clean up.
+ */
+static void
+tty3270_close(struct tty_struct *tty, struct file * filp)
+{
+	struct tty3270 *tp;
+
+	if (tty->count > 1)
+		return;
+	tp = (struct tty3270 *) tty->driver_data;
+	if (tp) {
+		tty->driver_data = 0;
+		tp->tty = tp->kbd->tty = 0;
+		raw3270_put_view(&tp->view);
+	}
+}
+
+/*
+ * We always have room.
+ */
+static int
+tty3270_write_room(struct tty_struct *tty)
+{
+	return INT_MAX;
+}
+
+/*
+ * Insert character into the screen at the current position with the
+ * current color and highlight. This function does NOT do cursor movement.
+ */
+static void
+tty3270_put_character(struct tty3270 *tp, char ch)
+{
+	struct tty3270_line *line;
+	struct tty3270_cell *cell;
+
+	line = tp->screen + tp->cy;
+	if (line->len <= tp->cx) {
+		while (line->len < tp->cx) {
+			cell = line->cells + line->len;
+			cell->character = tp->view.ascebc[' '];
+			cell->highlight = tp->highlight;
+			cell->f_color = tp->f_color;
+			line->len++;
+		}
+		line->len++;
+	}
+	cell = line->cells + tp->cx;
+	cell->character = tp->view.ascebc[(unsigned int) ch];
+	cell->highlight = tp->highlight;
+	cell->f_color = tp->f_color;
+}
+
+/*
+ * Convert a tty3270_line to a 3270 data fragment usable for output.
+ */
+static void
+tty3270_convert_line(struct tty3270 *tp, int line_nr)
+{
+	struct tty3270_line *line;
+	struct tty3270_cell *cell;
+	struct string *s, *n;
+	unsigned char highlight;
+	unsigned char f_color;
+	char *cp;
+	int flen, i;
+
+	/* Determine how long the fragment will be. */
+	flen = 3;		/* Prefix (TO_SBA). */
+	line = tp->screen + line_nr;
+	flen += line->len;
+	highlight = TAX_RESET;
+	f_color = TAC_RESET;
+	for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
+		if (cell->highlight != highlight) {
+			flen += 3;	/* TO_SA to switch highlight. */
+			highlight = cell->highlight;
+		}
+		if (cell->f_color != f_color) {
+			flen += 3;	/* TO_SA to switch color. */
+			f_color = cell->f_color;
+		}
+	}
+	if (highlight != TAX_RESET)
+		flen += 3;	/* TO_SA to reset hightlight. */
+	if (f_color != TAC_RESET)
+		flen += 3;	/* TO_SA to reset color. */
+	if (line->len < tp->view.cols)
+		flen += 4;	/* Postfix (TO_RA). */
+
+	/* Find the line in the list. */
+	i = tp->view.rows - 2 - line_nr;
+	list_for_each_entry_reverse(s, &tp->lines, list)
+		if (--i <= 0)
+			break;
+	/*
+	 * Check if the line needs to get reallocated.
+	 */
+	if (s->len != flen) {
+		/* Reallocate string. */
+		n = tty3270_alloc_string(tp, flen);
+		list_add(&n->list, &s->list);
+		list_del_init(&s->list);
+		if (!list_empty(&s->update))
+			list_del_init(&s->update);
+		free_string(&tp->freemem, s);
+		s = n;
+	}
+
+	/* Write 3270 data fragment. */
+	cp = s->string;
+	*cp++ = TO_SBA;
+	*cp++ = 0;
+	*cp++ = 0;
+
+	highlight = TAX_RESET;
+	f_color = TAC_RESET;
+	for (i = 0, cell = line->cells; i < line->len; i++, cell++) {
+		if (cell->highlight != highlight) {
+			*cp++ = TO_SA;
+			*cp++ = TAT_EXTHI;
+			*cp++ = cell->highlight;
+			highlight = cell->highlight;
+		}
+		if (cell->f_color != f_color) {
+			*cp++ = TO_SA;
+			*cp++ = TAT_COLOR;
+			*cp++ = cell->f_color;
+			f_color = cell->f_color;
+		}
+		*cp++ = cell->character;
+	}
+	if (highlight != TAX_RESET) {
+		*cp++ = TO_SA;
+		*cp++ = TAT_EXTHI;
+		*cp++ = TAX_RESET;
+	}
+	if (f_color != TAC_RESET) {
+		*cp++ = TO_SA;
+		*cp++ = TAT_COLOR;
+		*cp++ = TAC_RESET;
+	}
+	if (line->len < tp->view.cols) {
+		*cp++ = TO_RA;
+		*cp++ = 0;
+		*cp++ = 0;
+		*cp++ = 0;
+	}
+
+	if (tp->nr_up + line_nr < tp->view.rows - 2) {
+		/* Line is currently visible on screen. */
+		tty3270_update_string(tp, s, line_nr);
+		/* Add line to update list. */
+		if (list_empty(&s->update)) {
+			list_add_tail(&s->update, &tp->update);
+			tp->update_flags |= TTY_UPDATE_LIST;
+		}
+	}
+}
+
+/*
+ * Do carriage return.
+ */
+static void
+tty3270_cr(struct tty3270 *tp)
+{
+	tp->cx = 0;
+}
+
+/*
+ * Do line feed.
+ */
+static void
+tty3270_lf(struct tty3270 *tp)
+{
+	struct tty3270_line temp;
+	int i;
+
+	tty3270_convert_line(tp, tp->cy);
+	if (tp->cy < tp->view.rows - 3) {
+		tp->cy++;
+		return;
+	}
+	/* Last line just filled up. Add new, blank line. */
+	tty3270_blank_line(tp);
+	temp = tp->screen[0];
+	temp.len = 0;
+	for (i = 0; i < tp->view.rows - 3; i++)
+		tp->screen[i] = tp->screen[i+1];
+	tp->screen[tp->view.rows - 3] = temp;
+	tty3270_rebuild_update(tp);
+}
+
+static void
+tty3270_ri(struct tty3270 *tp)
+{
+	if (tp->cy > 0) {
+	    tty3270_convert_line(tp, tp->cy);
+	    tp->cy--;
+	}
+}
+
+/*
+ * Insert characters at current position.
+ */
+static void
+tty3270_insert_characters(struct tty3270 *tp, int n)
+{
+	struct tty3270_line *line;
+	int k;
+
+	line = tp->screen + tp->cy;
+	while (line->len < tp->cx) {
+		line->cells[line->len].character = tp->view.ascebc[' '];
+		line->cells[line->len].highlight = TAX_RESET;
+		line->cells[line->len].f_color = TAC_RESET;
+		line->len++;
+	}
+	if (n > tp->view.cols - tp->cx)
+		n = tp->view.cols - tp->cx;
+	k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n);
+	while (k--)
+		line->cells[tp->cx + n + k] = line->cells[tp->cx + k];
+	line->len += n;
+	if (line->len > tp->view.cols)
+		line->len = tp->view.cols;
+	while (n-- > 0) {
+		line->cells[tp->cx + n].character = tp->view.ascebc[' '];
+		line->cells[tp->cx + n].highlight = tp->highlight;
+		line->cells[tp->cx + n].f_color = tp->f_color;
+	}
+}
+
+/*
+ * Delete characters at current position.
+ */
+static void
+tty3270_delete_characters(struct tty3270 *tp, int n)
+{
+	struct tty3270_line *line;
+	int i;
+
+	line = tp->screen + tp->cy;
+	if (line->len <= tp->cx)
+		return;
+	if (line->len - tp->cx <= n) {
+		line->len = tp->cx;
+		return;
+	}
+	for (i = tp->cx; i + n < line->len; i++)
+		line->cells[i] = line->cells[i + n];
+	line->len -= n;
+}
+
+/*
+ * Erase characters at current position.
+ */
+static void
+tty3270_erase_characters(struct tty3270 *tp, int n)
+{
+	struct tty3270_line *line;
+	struct tty3270_cell *cell;
+
+	line = tp->screen + tp->cy;
+	while (line->len > tp->cx && n-- > 0) {
+		cell = line->cells + tp->cx++;
+		cell->character = ' ';
+		cell->highlight = TAX_RESET;
+		cell->f_color = TAC_RESET;
+	}
+	tp->cx += n;
+	tp->cx = min_t(int, tp->cx, tp->view.cols - 1);
+}
+
+/*
+ * Erase line, 3 different cases:
+ *  Esc [ 0 K	Erase from current position to end of line inclusive
+ *  Esc [ 1 K	Erase from beginning of line to current position inclusive
+ *  Esc [ 2 K	Erase entire line (without moving cursor)
+ */
+static void
+tty3270_erase_line(struct tty3270 *tp, int mode)
+{
+	struct tty3270_line *line;
+	struct tty3270_cell *cell;
+	int i;
+
+	line = tp->screen + tp->cy;
+	if (mode == 0)
+		line->len = tp->cx;
+	else if (mode == 1) {
+		for (i = 0; i < tp->cx; i++) {
+			cell = line->cells + i;
+			cell->character = ' ';
+			cell->highlight = TAX_RESET;
+			cell->f_color = TAC_RESET;
+		}
+		if (line->len <= tp->cx)
+			line->len = tp->cx + 1;
+	} else if (mode == 2)
+		line->len = 0;
+	tty3270_convert_line(tp, tp->cy);
+}
+
+/*
+ * Erase display, 3 different cases:
+ *  Esc [ 0 J	Erase from current position to bottom of screen inclusive
+ *  Esc [ 1 J	Erase from top of screen to current position inclusive
+ *  Esc [ 2 J	Erase entire screen (without moving the cursor)
+ */
+static void
+tty3270_erase_display(struct tty3270 *tp, int mode)
+{
+	int i;
+
+	if (mode == 0) {
+		tty3270_erase_line(tp, 0);
+		for (i = tp->cy + 1; i < tp->view.rows - 2; i++) {
+			tp->screen[i].len = 0;
+			tty3270_convert_line(tp, i);
+		}
+	} else if (mode == 1) {
+		for (i = 0; i < tp->cy; i++) {
+			tp->screen[i].len = 0;
+			tty3270_convert_line(tp, i);
+		}
+		tty3270_erase_line(tp, 1);
+	} else if (mode == 2) {
+		for (i = 0; i < tp->view.rows - 2; i++) {
+			tp->screen[i].len = 0;
+			tty3270_convert_line(tp, i);
+		}
+	}
+	tty3270_rebuild_update(tp);
+}
+
+/*
+ * Set attributes found in an escape sequence.
+ *  Esc [ <attr> ; <attr> ; ... m
+ */
+static void
+tty3270_set_attributes(struct tty3270 *tp)
+{
+	static unsigned char f_colors[] = {
+		TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE,
+		TAC_PINK, TAC_TURQ, TAC_WHITE, 0, TAC_DEFAULT
+	};
+	int i, attr;
+
+	for (i = 0; i <= tp->esc_npar; i++) {
+		attr = tp->esc_par[i];
+		switch (attr) {
+		case 0:		/* Reset */
+			tp->highlight = TAX_RESET;
+			tp->f_color = TAC_RESET;
+			break;
+		/* Highlight. */
+		case 4:		/* Start underlining. */
+			tp->highlight = TAX_UNDER;
+			break;
+		case 5:		/* Start blink. */
+			tp->highlight = TAX_BLINK;
+			break;
+		case 7:		/* Start reverse. */
+			tp->highlight = TAX_REVER;
+			break;
+		case 24:	/* End underlining */
+			if (tp->highlight == TAX_UNDER)
+				tp->highlight = TAX_RESET;
+			break;
+		case 25:	/* End blink. */
+			if (tp->highlight == TAX_BLINK)
+				tp->highlight = TAX_RESET;
+			break;
+		case 27:	/* End reverse. */
+			if (tp->highlight == TAX_REVER)
+				tp->highlight = TAX_RESET;
+			break;
+		/* Foreground color. */
+		case 30:	/* Black */
+		case 31:	/* Red */
+		case 32:	/* Green */
+		case 33:	/* Yellow */
+		case 34:	/* Blue */
+		case 35:	/* Magenta */
+		case 36:	/* Cyan */
+		case 37:	/* White */
+		case 39:	/* Black */
+			tp->f_color = f_colors[attr - 30];
+			break;
+		}
+	}
+}
+
+static inline int
+tty3270_getpar(struct tty3270 *tp, int ix)
+{
+	return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1;
+}
+
+static void
+tty3270_goto_xy(struct tty3270 *tp, int cx, int cy)
+{
+	tp->cx = min_t(int, tp->view.cols - 1, max_t(int, 0, cx));
+	cy = min_t(int, tp->view.rows - 3, max_t(int, 0, cy));
+	if (cy != tp->cy) {
+		tty3270_convert_line(tp, tp->cy);
+		tp->cy = cy;
+	}
+}
+
+/*
+ * Process escape sequences. Known sequences:
+ *  Esc 7			Save Cursor Position
+ *  Esc 8			Restore Cursor Position
+ *  Esc [ Pn ; Pn ; .. m	Set attributes
+ *  Esc [ Pn ; Pn H		Cursor Position
+ *  Esc [ Pn ; Pn f		Cursor Position
+ *  Esc [ Pn A			Cursor Up
+ *  Esc [ Pn B			Cursor Down
+ *  Esc [ Pn C			Cursor Forward
+ *  Esc [ Pn D			Cursor Backward
+ *  Esc [ Pn G			Cursor Horizontal Absolute
+ *  Esc [ Pn X			Erase Characters
+ *  Esc [ Ps J			Erase in Display
+ *  Esc [ Ps K			Erase in Line
+ * // FIXME: add all the new ones.
+ *
+ *  Pn is a numeric parameter, a string of zero or more decimal digits.
+ *  Ps is a selective parameter.
+ */
+static void
+tty3270_escape_sequence(struct tty3270 *tp, char ch)
+{
+	enum { ESnormal, ESesc, ESsquare, ESgetpars };
+
+	if (tp->esc_state == ESnormal) {
+		if (ch == 0x1b)
+			/* Starting new escape sequence. */
+			tp->esc_state = ESesc;
+		return;
+	}
+	if (tp->esc_state == ESesc) {
+		tp->esc_state = ESnormal;
+		switch (ch) {
+		case '[':
+			tp->esc_state = ESsquare;
+			break;
+		case 'E':
+			tty3270_cr(tp);
+			tty3270_lf(tp);
+			break;
+		case 'M':
+			tty3270_ri(tp);
+			break;
+		case 'D':
+			tty3270_lf(tp);
+			break;
+		case 'Z':		/* Respond ID. */
+			kbd_puts_queue(tp->tty, "\033[?6c");
+			break;
+		case '7':		/* Save cursor position. */
+			tp->saved_cx = tp->cx;
+			tp->saved_cy = tp->cy;
+			tp->saved_highlight = tp->highlight;
+			tp->saved_f_color = tp->f_color;
+			break;
+		case '8':		/* Restore cursor position. */
+			tty3270_convert_line(tp, tp->cy);
+			tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
+			tp->highlight = tp->saved_highlight;
+			tp->f_color = tp->saved_f_color;
+			break;
+		case 'c':		/* Reset terminal. */
+			tp->cx = tp->saved_cx = 0;
+			tp->cy = tp->saved_cy = 0;
+			tp->highlight = tp->saved_highlight = TAX_RESET;
+			tp->f_color = tp->saved_f_color = TAC_RESET;
+			tty3270_erase_display(tp, 2);
+			break;
+		}
+		return;
+	}
+	if (tp->esc_state == ESsquare) {
+		tp->esc_state = ESgetpars;
+		memset(tp->esc_par, 0, sizeof(tp->esc_par));
+		tp->esc_npar = 0;
+		tp->esc_ques = (ch == '?');
+		if (tp->esc_ques)
+			return;
+	}
+	if (tp->esc_state == ESgetpars) {
+		if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) {
+			tp->esc_npar++;
+			return;
+		}
+		if (ch >= '0' && ch <= '9') {
+			tp->esc_par[tp->esc_npar] *= 10;
+			tp->esc_par[tp->esc_npar] += ch - '0';
+			return;
+		}
+	}
+	tp->esc_state = ESnormal;
+	if (ch == 'n' && !tp->esc_ques) {
+		if (tp->esc_par[0] == 5)		/* Status report. */
+			kbd_puts_queue(tp->tty, "\033[0n");
+		else if (tp->esc_par[0] == 6) {	/* Cursor report. */
+			char buf[40];
+			sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1);
+			kbd_puts_queue(tp->tty, buf);
+		}
+		return;
+	}
+	if (tp->esc_ques)
+		return;
+	switch (ch) {
+	case 'm':
+		tty3270_set_attributes(tp);
+		break;
+	case 'H':	/* Set cursor position. */
+	case 'f':
+		tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1,
+				tty3270_getpar(tp, 0) - 1);
+		break;
+	case 'd':	/* Set y position. */
+		tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1);
+		break;
+	case 'A':	/* Cursor up. */
+	case 'F':
+		tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0));
+		break;
+	case 'B':	/* Cursor down. */
+	case 'e':
+	case 'E':
+		tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0));
+		break;
+	case 'C':	/* Cursor forward. */
+	case 'a':
+		tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy);
+		break;
+	case 'D':	/* Cursor backward. */
+		tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy);
+		break;
+	case 'G':	/* Set x position. */
+	case '`':
+		tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy);
+		break;
+	case 'X':	/* Erase Characters. */
+		tty3270_erase_characters(tp, tty3270_getpar(tp, 0));
+		break;
+	case 'J':	/* Erase display. */
+		tty3270_erase_display(tp, tp->esc_par[0]);
+		break;
+	case 'K':	/* Erase line. */
+		tty3270_erase_line(tp, tp->esc_par[0]);
+		break;
+	case 'P':	/* Delete characters. */
+		tty3270_delete_characters(tp, tty3270_getpar(tp, 0));
+		break;
+	case '@':	/* Insert characters. */
+		tty3270_insert_characters(tp, tty3270_getpar(tp, 0));
+		break;
+	case 's':	/* Save cursor position. */
+		tp->saved_cx = tp->cx;
+		tp->saved_cy = tp->cy;
+		tp->saved_highlight = tp->highlight;
+		tp->saved_f_color = tp->f_color;
+		break;
+	case 'u':	/* Restore cursor position. */
+		tty3270_convert_line(tp, tp->cy);
+		tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
+		tp->highlight = tp->saved_highlight;
+		tp->f_color = tp->saved_f_color;
+		break;
+	}
+}
+
+/*
+ * String write routine for 3270 ttys
+ */
+static void
+tty3270_do_write(struct tty3270 *tp, const unsigned char *buf, int count)
+{
+	int i_msg, i;
+
+	spin_lock_bh(&tp->view.lock);
+	for (i_msg = 0; !tp->tty->stopped && i_msg < count; i_msg++) {
+		if (tp->esc_state != 0) {
+			/* Continue escape sequence. */
+			tty3270_escape_sequence(tp, buf[i_msg]);
+			continue;
+		}
+
+		switch (buf[i_msg]) {
+		case 0x07:		/* '\a' -- Alarm */
+			tp->wcc |= TW_PLUSALARM;
+			break;
+		case 0x08:		/* Backspace. */
+			if (tp->cx > 0) {
+				tp->cx--;
+				tty3270_put_character(tp, ' ');
+			}
+			break;
+		case 0x09:		/* '\t' -- Tabulate */
+			for (i = tp->cx % 8; i < 8; i++) {
+				if (tp->cx >= tp->view.cols) {
+					tty3270_cr(tp);
+					tty3270_lf(tp);
+					break;
+				}
+				tty3270_put_character(tp, ' ');
+				tp->cx++;
+			}
+			break;
+		case 0x0a:		/* '\n' -- New Line */
+			tty3270_cr(tp);
+			tty3270_lf(tp);
+			break;
+		case 0x0c:		/* '\f' -- Form Feed */
+			tty3270_erase_display(tp, 2);
+			tp->cx = tp->cy = 0;
+			break;
+		case 0x0d:		/* '\r' -- Carriage Return */
+			tp->cx = 0;
+			break;
+		case 0x0f:		/* SuSE "exit alternate mode" */
+			break;
+		case 0x1b:		/* Start escape sequence. */
+			tty3270_escape_sequence(tp, buf[i_msg]);
+			break;
+		default:		/* Insert normal character. */
+			if (tp->cx >= tp->view.cols) {
+				tty3270_cr(tp);
+				tty3270_lf(tp);
+			}
+			tty3270_put_character(tp, buf[i_msg]);
+			tp->cx++;
+			break;
+		}
+	}
+	/* Convert current line to 3270 data fragment. */
+	tty3270_convert_line(tp, tp->cy);
+
+	/* Setup timer to update display after 1/10 second */
+	if (!timer_pending(&tp->timer))
+		tty3270_set_timer(tp, HZ/10);
+
+	spin_unlock_bh(&tp->view.lock);
+}
+
+/*
+ * String write routine for 3270 ttys
+ */
+static int
+tty3270_write(struct tty_struct * tty, int from_user,
+	      const unsigned char *buf, int count)
+{
+	struct tty3270 *tp;
+	int length, ret;
+
+	tp = tty->driver_data;
+	if (!tp)
+		return 0;
+	if (tp->char_count > 0) {
+		tty3270_do_write(tp, tp->char_buf, tp->char_count);
+		tp->char_count = 0;
+	}
+	if (!from_user) {
+		tty3270_do_write(tp, buf, count);
+		return count;
+	}
+	ret = 0;
+	while (count > 0) {
+		length = count < TTY3270_CHAR_BUF_SIZE ?
+			count : TTY3270_CHAR_BUF_SIZE;
+		length -= copy_from_user(tp->char_buf, buf, length);
+		if (length == 0) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		tty3270_do_write(tp, tp->char_buf, count);
+		buf += length;
+		count -= length;
+		ret += length;
+	}
+	return ret;
+}
+
+/*
+ * Put single characters to the ttys character buffer
+ */
+static void
+tty3270_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct tty3270 *tp;
+
+	tp = tty->driver_data;
+	if (!tp)
+		return;
+	if (tp->char_count < TTY3270_CHAR_BUF_SIZE)
+		tp->char_buf[tp->char_count++] = ch;
+}
+
+/*
+ * Flush all characters from the ttys characeter buffer put there
+ * by tty3270_put_char.
+ */
+static void
+tty3270_flush_chars(struct tty_struct *tty)
+{
+	struct tty3270 *tp;
+
+	tp = tty->driver_data;
+	if (!tp)
+		return;
+	if (tp->char_count > 0) {
+		tty3270_do_write(tp, tp->char_buf, tp->char_count);
+		tp->char_count = 0;
+	}
+}
+
+/*
+ * Returns the number of characters in the output buffer. This is
+ * used in tty_wait_until_sent to wait until all characters have
+ * appeared on the screen.
+ */
+static int
+tty3270_chars_in_buffer(struct tty_struct *tty)
+{
+	return 0;
+}
+
+static void
+tty3270_flush_buffer(struct tty_struct *tty)
+{
+}
+
+/*
+ * Check for visible/invisible input switches
+ */
+static void
+tty3270_set_termios(struct tty_struct *tty, struct termios *old)
+{
+	struct tty3270 *tp;
+	int new;
+
+	tp = tty->driver_data;
+	if (!tp)
+		return;
+	spin_lock_bh(&tp->view.lock);
+	if (L_ICANON(tty)) {
+		new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN;
+		if (new != tp->inattr) {
+			tp->inattr = new;
+			tty3270_update_prompt(tp, 0, 0);
+			tty3270_set_timer(tp, 1);
+		}
+	}
+	spin_unlock_bh(&tp->view.lock);
+}
+
+/*
+ * Disable reading from a 3270 tty
+ */
+static void
+tty3270_throttle(struct tty_struct * tty)
+{
+	struct tty3270 *tp;
+
+	tp = tty->driver_data;
+	if (!tp)
+		return;
+	tp->throttle = 1;
+}
+
+/*
+ * Enable reading from a 3270 tty
+ */
+static void
+tty3270_unthrottle(struct tty_struct * tty)
+{
+	struct tty3270 *tp;
+
+	tp = tty->driver_data;
+	if (!tp)
+		return;
+	tp->throttle = 0;
+	if (tp->attn)
+		tty3270_issue_read(tp, 1);
+}
+
+/*
+ * Hang up the tty device.
+ */
+static void
+tty3270_hangup(struct tty_struct *tty)
+{
+	// FIXME: implement
+}
+
+static void
+tty3270_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+}
+
+static int
+tty3270_ioctl(struct tty_struct *tty, struct file *file,
+	      unsigned int cmd, unsigned long arg)
+{
+	struct tty3270 *tp;
+
+	tp = tty->driver_data;
+	if (!tp)
+		return -ENODEV;
+	if (tty->flags & (1 << TTY_IO_ERROR))
+		return -EIO;
+	return kbd_ioctl(tp->kbd, file, cmd, arg);
+}
+
+static struct tty_operations tty3270_ops = {
+	.open = tty3270_open,
+	.close = tty3270_close,
+	.write = tty3270_write,
+	.put_char = tty3270_put_char,
+	.flush_chars = tty3270_flush_chars,
+	.write_room = tty3270_write_room,
+	.chars_in_buffer = tty3270_chars_in_buffer,
+	.flush_buffer = tty3270_flush_buffer,
+	.throttle = tty3270_throttle,
+	.unthrottle = tty3270_unthrottle,
+	.hangup = tty3270_hangup,
+	.wait_until_sent = tty3270_wait_until_sent,
+	.ioctl = tty3270_ioctl,
+	.set_termios = tty3270_set_termios
+};
+
+void
+tty3270_notifier(int index, int active)
+{
+	if (active)
+		tty_register_device(tty3270_driver, index, 0);
+	else
+		tty_unregister_device(tty3270_driver, index);
+}
+
+/*
+ * 3270 tty registration code called from tty_init().
+ * Most kernel services (incl. kmalloc) are available at this poimt.
+ */
+int __init
+tty3270_init(void)
+{
+	struct tty_driver *driver;
+	int ret;
+
+	ret = raw3270_init();
+	if (ret)
+		return ret;
+	driver = alloc_tty_driver(256);
+	if (!driver)
+		return -ENOMEM;
+
+	/*
+	 * Initialize the tty_driver structure
+	 * Entries in tty3270_driver that are NOT initialized:
+	 * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
+	 */
+	driver->owner = THIS_MODULE;
+	driver->devfs_name = "ttyTUB/";
+	driver->driver_name = "ttyTUB";
+	driver->name = "ttyTUB";
+	driver->major = IBM_TTY3270_MAJOR;
+	driver->type = TTY_DRIVER_TYPE_SYSTEM;
+	driver->subtype = SYSTEM_TYPE_TTY;
+	driver->init_termios = tty_std_termios;
+	driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_NO_DEVFS;
+	tty_set_operations(driver, &tty3270_ops);
+	ret = tty_register_driver(driver);
+	if (ret) {
+		printk(KERN_ERR "tty3270 registration failed with %d\n", ret);
+		put_tty_driver(driver);
+		return ret;
+	}
+	tty3270_driver = driver;
+	return 0;
+}
+
+static void __exit
+tty3270_exit(void)
+{
+	struct tty_driver *driver;
+
+	driver = tty3270_driver;
+	tty3270_driver = 0;
+	tty_unregister_driver(driver);
+	raw3270_exit();
+}
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR);
+
+module_init(tty3270_init);
+module_exit(tty3270_exit);
diff -puN -L drivers/s390/char/tuball.c drivers/s390/char/tuball.c~s390-08-new-3270-driver /dev/null
--- 25/drivers/s390/char/tuball.c
+++ /dev/null	Thu Apr 11 07:25:15 2002
@@ -1,621 +0,0 @@
-/*
- *  IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC
- *
- *  tuball.c -- Initialization, termination, irq lookup
- *
- *
- *
- *
- *
- *  Author:  Richard Hitt
- */
-#include <linux/config.h>
-#include "tubio.h"
-#ifndef MODULE
-#include <linux/init.h>
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
-#include <asm/cpcmd.h>
-#include <linux/bootmem.h>
-#else
-#include "../../../../arch/s390/kernel/cpcmd.h"
-#endif
-#endif
-
-/* Module parameters */
-int tubdebug;
-int tubscrolltime = -1;
-static int tubxcorrect = 1;            /* Do correct ebc<->asc tables */
-#ifdef MODULE
-MODULE_PARM(tubdebug, "i");
-MODULE_PARM(tubscrolltime, "i");
-MODULE_PARM(tubxcorrect, "i");
-#endif
-/*
- * Values for tubdebug and their effects:
- * 1 - print in hex on console the first 16 bytes received
- * 2 - print address at which array tubminors is allocated
- * 4 - attempt to register tty3270_driver
- */
-int tubnummins;
-tub_t *(*tubminors)[TUBMAXMINS];
-static tub_t *(*(*tubirqs)[256])[256];
-unsigned char tub_ascebc[256];
-unsigned char tub_ebcasc[256];
-static int tubinitminors(void);
-static void tubfiniminors(void);
-static void tubint(int, void *, struct pt_regs *);
-
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
-static int tubmakemin(int, dev_info_t *);
-#else
-static int tubmakemin(int, s390_dev_info_t *);
-#endif
-
-
-/* Lookup-by-irq functions */
-static int tubaddbyirq(tub_t *, int);
-static tub_t *tubfindbyirq(int);
-static void tubdelbyirq(tub_t *, int);
-static void tubfiniirqs(void);
-
-/* FIXME: put extern declarations in a header */
-extern int fs3270_init(void);
-extern void fs3270_fini(void);
-extern int tty3270_init(void);
-extern void tty3270_fini(void);
-
-unsigned char tub_ebcgraf[64] =
-	{ 0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
-	  0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
-	  0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
-	  0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
-	  0x60, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
-	  0xe8, 0xe9, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
-	  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
-	  0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f };
-
-#ifndef MODULE
-
-/*
- * Can't have this driver a module & support console at the same time
- */
-#ifdef CONFIG_TN3270_CONSOLE
-static struct tty_driver *tub3270_con_device(struct console *, int *);
-static void tub3270_con_unblank(void);
-static void tub3270_con_write(struct console *, const char *,
-	unsigned int);
-
-static struct console tub3270_con = {
-	"tub3270",		/* name */
-	tub3270_con_write,	/* write */
-	NULL,			/* read */
-	tub3270_con_device,	/* device */
-	tub3270_con_unblank,	/* unblank */
-	NULL,			/* setup */
-	CON_PRINTBUFFER,	/* flags */
-	0,			/* index */
-	0,			/* cflag */
-	NULL			/* next */
-};
-
-static bcb_t tub3270_con_bcb;		/* Buffer that receives con writes */
-static spinlock_t tub3270_con_bcblock;	/* Lock for the buffer */
-static int tub3270_con_irq = -1;		/* set nonneg by _activate() */
-tub_t *tub3270_con_tubp;		/* set nonzero by _activate() */
-struct tty_driver tty3270_con_driver;	/* for /dev/console at 4, 64 */
-
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
-int tub3270_con_devno = -1;		/* set by tub3270_con_setup() */
-__initfunc(void tub3270_con_setup(char *str, int *ints))
-{
-	int vdev;
-
-	vdev = simple_strtoul(str, 0, 16);
-	if (vdev >= 0 && vdev < 65536)
-		tub3270_con_devno = vdev;
-	return;
-}
-
-__initfunc (long tub3270_con_init(long kmem_start, long kmem_end))
-{
-	tub3270_con_bcb.bc_len = 65536;
-	if (!MACHINE_IS_VM && !MACHINE_IS_P390)
-		return kmem_start;
-	tub3270_con_bcb.bc_buf = (void *)kmem_start;
-	kmem_start += tub3270_con_bcb.bc_len;
-	register_console(&tub3270_con);
-	return kmem_start;
-}
-#else
-#define tub3270_con_devno console_device
-
-static void __init tub3270_con_init(void)
-{
-	tub3270_con_bcb.bc_len = 65536;
-	if (!CONSOLE_IS_3270)
-		return;
-	tub3270_con_bcb.bc_buf = (void *)alloc_bootmem_low(
-		tub3270_con_bcb.bc_len);
-	register_console(&tub3270_con);
-}
-console_initcall(tub3270_con_init);
-
-#endif
-
-static struct tty_driver *tub3270_con_device(struct console *conp, int *index)
-{
-	*index = conp->index + 1;
-	extern struct tty_driver *tty3270_driver;
-	return tty3270_driver;
-}
-
-static void
-tub3270_con_unblank(void)
-{
-	/* flush everything:  panic has occurred */
-}
-
-static void
-tub3270_con_write(struct console *conp,
-	const char *buf, unsigned int count)
-{
-	long flags;
-	tub_t *tubp = tub3270_con_tubp;
-	void tty3270_sched_bh(tub_t *);
-	int rc;
-	bcb_t obcb;
-
-	obcb.bc_buf = (char *)buf;
-	obcb.bc_len = obcb.bc_cnt = obcb.bc_wr =
-		MIN(count, tub3270_con_bcb.bc_len);
-	obcb.bc_rd = 0;
-
-	spin_lock_irqsave(&tub3270_con_bcblock, flags);
-	rc = tub3270_movedata(&obcb, &tub3270_con_bcb, 0);
-	spin_unlock_irqrestore(&tub3270_con_bcblock, flags);
-
-	if (tubp && rc && TUBTRYLOCK(tubp->irq, flags)) {
-		tty3270_sched_bh(tubp);
-		TUBUNLOCK(tubp->irq, flags);
-	}
-}
-	
-int tub3270_con_copy(tub_t *tubp)
-{
-	long flags;
-	int rc;
-
-	spin_lock_irqsave(&tub3270_con_bcblock, flags);
-	rc = tub3270_movedata(&tub3270_con_bcb, &tubp->tty_bcb, 0);
-	spin_unlock_irqrestore(&tub3270_con_bcblock, flags);
-	return rc;
-}
-#endif /* CONFIG_TN3270_CONSOLE */
-#endif
-
-/*
- * remove driver:  unregister the major number
- */
-static void __exit
-tub3270_exit(void)
-{
-	fs3270_fini();
-	tty3270_fini();
-	tubfiniminors();
-}
-
-static int
-tub3270_is_ours(s390_dev_info_t *dp)
-{
-	if ((dp->sid_data.cu_type & 0xfff0) == 0x3270)
-		return 1;
-	if (dp->sid_data.cu_type == 0x3174)
-		return 1;
-	return 0;
-}
-
-/*
- * tub3270_init() called by kernel or module initialization
- */
-static int __init
-tub3270_init(void)
-{
-	s390_dev_info_t d;
-	int i, rc;
-
-#ifdef MODULE
-	if (tubnummins != 0) {
-		printk(KERN_ERR "EEEK!!  Tube driver cobbigling!!\n");
-		return -1;
-	}
-#endif
-
-	/*
-	 * Copy and correct ebcdic - ascii translate tables
-	 */
-	memcpy(tub_ascebc, _ascebc, sizeof tub_ascebc);
-	memcpy(tub_ebcasc, _ebcasc, sizeof tub_ebcasc);
-	if (tubxcorrect) {
-		/* correct brackets and circumflex */
-		tub_ascebc['['] = 0xad;
-		tub_ascebc[']'] = 0xbd;
-		tub_ebcasc[0xad] = '[';
-		tub_ebcasc[0xbd] = ']';
-		tub_ascebc['^'] = 0xb0;
-		tub_ebcasc[0x5f] = '^';
-	}
-
-	rc = tubinitminors();
-	if (rc != 0)
-		return rc;
-
-	if (fs3270_init() || tty3270_init()) {
-		printk(KERN_ERR "fs3270_init() or tty3270_init() failed\n");
-		fs3270_fini();
-		tty3270_fini();
-		tubfiniminors();
-		return -1;
-	}
-
-	for (i = get_irq_first(); i >= 0; i = get_irq_next(i)) {
-		if ((rc = get_dev_info_by_irq(i, &d)))
-			continue;
-		if (d.status)
-			continue;
-
-#ifdef CONFIG_TN3270_CONSOLE
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
-		if (d.sid_data.cu_type == 0x3215 && MACHINE_IS_VM) {
-			cpcmd("TERM CONMODE 3270", NULL, 0);
-			d.sid_data.cu_type = 0x3270;
-		}
-#else
-		if (d.sid_data.cu_type == 0x3215 && CONSOLE_IS_3270) {
-			cpcmd("TERM CONMODE 3270", NULL, 0);
-			d.sid_data.cu_type = 0x3270;
-		}
-#endif /* LINUX_VERSION_CODE */
-#endif /* CONFIG_TN3270_CONSOLE */
-		if (!tub3270_is_ours(&d))
-			continue;
-
-		rc = tubmakemin(i, &d);
-		if (rc < 0) {
-			printk(KERN_WARNING 
-			       "3270 tube registration ran out of memory"
-			       " after %d devices\n", tubnummins - 1);
-			break;
-		} else {
-			printk(KERN_INFO "3270: %.4x on sch %d, minor %d\n",
-				d.devno, d.irq, rc);
-		}
-	}
-
-	return 0;
-}
-
-/*
- * tub3270_movedata(bcb_t *, bcb_t *) -- Move data stream
- */
-int
-tub3270_movedata(bcb_t *ib, bcb_t *ob, int fromuser)
-{
-	int count;			/* Total move length */
-	int rc;
-
-	rc = count = MIN(ib->bc_cnt, ob->bc_len - ob->bc_cnt);
-	while (count > 0) {
-		int len1;		/* Contig bytes avail in ib */
-
-		if (ib->bc_wr > ib->bc_rd)
-			len1 = ib->bc_wr - ib->bc_rd;
-		else
-			len1 = ib->bc_len - ib->bc_rd;
-		if (len1 > count)
-			len1 = count;
-
-		while (len1 > 0) {
-			int len2;	/* Contig space avail in ob */
-
-			if (ob->bc_rd > ob->bc_wr)
-				len2 = ob->bc_rd - ob->bc_wr;
-			else
-				len2 = ob->bc_len - ob->bc_wr;
-			if (len2 > len1)
-				len2 = len1;
-			
-			if (fromuser) {
-				len2 -= copy_from_user(ob->bc_buf + ob->bc_wr,
-						       ib->bc_buf + ib->bc_rd,
-						       len2);
-				if (len2 == 0) {
-					if (!rc)
-						rc = -EFAULT;
-					break;
-				}
-			} else
-				memcpy(ob->bc_buf + ob->bc_wr,
-				       ib->bc_buf + ib->bc_rd,
-				       len2);
-			
-			ib->bc_rd += len2;
-			if (ib->bc_rd == ib->bc_len)
-				ib->bc_rd = 0;
-			ib->bc_cnt -= len2;
-
-			ob->bc_wr += len2;
-			if (ob->bc_wr == ob->bc_len)
-				ob->bc_wr = 0;
-			ob->bc_cnt += len2;
-
-			len1 -= len2;
-			count -= len2;
-		}
-	}
-	return rc;
-}
-
-/*
- * receive an interrupt
- */
-static void
-tubint(int irq, void *ipp, struct pt_regs *prp)
-{
-	devstat_t *dsp = ipp;
-	tub_t *tubp;
-
-	if ((tubp = IRQ2TUB(irq)) && (tubp->intv))
-		(tubp->intv)(tubp, dsp);
-}
-
-/*
- * Initialize array of pointers to minor structures tub_t.
- * Returns 0 or -ENOMEM.
- */
-static int
-tubinitminors(void)
-{
-	tubminors = (tub_t *(*)[TUBMAXMINS])kmalloc(sizeof *tubminors,
-		GFP_KERNEL);
-	if (tubminors == NULL)
-		return -ENOMEM;
-	memset(tubminors, 0, sizeof *tubminors);
-	return 0;
-}
-
-/*
- * Add a minor 327x device.  Argument is an irq value.
- *
- * Point elements of two arrays to the newly created tub_t:
- * 1. (*tubminors)[minor]
- * 2. (*(*tubirqs)[irqhi])[irqlo]
- * The first looks up from minor number at context time; the second
- * looks up from irq at interrupt time.
- */
-static int
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
-tubmakemin(int irq, dev_info_t *dp)
-#else
-tubmakemin(int irq, s390_dev_info_t *dp)
-#endif
-{
-	tub_t *tubp;
-	int minor;
-	long flags;
-
-	if ((minor = ++tubnummins) == TUBMAXMINS)
-		return -ENODEV;
-
-	tubp = kmalloc(sizeof(tub_t), GFP_KERNEL);
-	if (tubp == NULL) {
-		return -ENOMEM;
-	}
-	if (tubaddbyirq(tubp, irq) != 0) {
-		kfree(tubp);
-		return -ENOMEM;
-	}
-	memset(tubp, 0, sizeof(tub_t));
-	tubp->minor = minor;
-	tubp->irq = irq;
-	TUBLOCK(tubp->irq, flags);
-	tubp->devno = dp->devno;
-	tubp->geom_rows = _GEOM_ROWS;
-	tubp->geom_cols = _GEOM_COLS;
-	init_waitqueue_head(&tubp->waitq);
-
-	tubp->tty_bcb.bc_len = TTY_OUTPUT_SIZE;
-	tubp->tty_bcb.bc_buf = (void *)kmalloc(tubp->tty_bcb.bc_len,
-		GFP_KERNEL|GFP_DMA);
-	if (tubp->tty_bcb.bc_buf == NULL) {
-		TUBUNLOCK(tubp->irq, flags);
-		tubdelbyirq(tubp, irq);
-		kfree(tubp);
-		return -ENOMEM;
-	}
-	tubp->tty_bcb.bc_cnt = 0;
-	tubp->tty_bcb.bc_wr = 0;
-	tubp->tty_bcb.bc_rd = 0;
-	(*tubminors)[minor] = tubp;
-
-#ifdef CONFIG_TN3270_CONSOLE
-	if (CONSOLE_IS_3270) {
-		if (tub3270_con_tubp == NULL && 
-		    tub3270_con_bcb.bc_buf != NULL &&
-		    (tub3270_con_devno == -1 ||
-		     tub3270_con_devno == dp->devno)) {
-			extern void tty3270_int(tub_t *, devstat_t *);
-			
-			tub3270_con_devno = dp->devno;
-			tubp->cmd = TBC_CONOPEN;
-			tubp->flags |= TUB_OPEN_STET | TUB_INPUT_HACK;
-			tty3270_size(tubp, &flags);
-			tty3270_aid_init(tubp);
-			tty3270_scl_init(tubp);
-			tub3270_con_irq = tubp->irq;
-			tub3270_con_tubp = tubp;
-			tubp->intv = tty3270_int;
-			tubp->cmd = TBC_UPDSTAT;
-			tty3270_build(tubp);
-		}
-	}
-#endif /* CONFIG_TN3270_CONSOLE */
-
-	devfs_mk_cdev(MKDEV(IBM_FS3270_MAJOR, tubp->minor),
-			S_IFCHR|S_IRUSR|S_IWUSR, "3270/tub%.4x");
-
-	TUBUNLOCK(tubp->irq, flags);
-	return minor;
-}
-
-/*
- * Release array of pointers to minor structures tub_t, but first
- * release any storage pointed to by them.
- */
-static void
-tubfiniminors(void)
-{
-	int i;
-	tub_t **tubpp, *tubp;
-
-	if (tubminors == NULL)
-		return;
-
-	for (i = 0; i < TUBMAXMINS; i++) {
-		tubpp = &(*tubminors)[i];
-		if ((tubp = *tubpp)) {
-			devfs_remove("3270/tub%.4x", tubp->devno);
-			tubdelbyirq(tubp, tubp->irq);
-			tty3270_rcl_fini(tubp);
-			kfree(tubp->tty_bcb.bc_buf);
-			tubp->tty_bcb.bc_buf = NULL;
-			if (tubp->tty_input) {
-				kfree(tubp->tty_input);
-				tubp->tty_input = NULL;
-			}
-			tubp->ttyscreen = NULL;
-			kfree(tubp);
-			*tubpp = NULL;
-		}
-	}
-	kfree(tubminors);
-	tubminors = NULL;
-	tubfiniirqs();
-}
-
-/*
- * tubaddbyirq() -- Add tub_t for irq lookup in tubint()
- */
-static int
-tubaddbyirq(tub_t *tubp, int irq)
-{
-	int irqhi = (irq >> 8) & 255;
-	int irqlo = irq & 255;
-	tub_t *(*itubpp)[256];
-
-	/* Allocate array (*tubirqs)[] if first time */
-	if (tubirqs == NULL) {
-		tubirqs = (tub_t *(*(*)[256])[256])
-			kmalloc(sizeof *tubirqs, GFP_KERNEL);
-		if (tubirqs == NULL)
-			return -ENOMEM;
-		memset(tubirqs, 0, sizeof *tubirqs);
-	}
-
-	/* Allocate subarray (*(*tubirqs)[])[] if first use */
-	if ((itubpp = (*tubirqs)[irqhi]) == NULL) {
-		itubpp = (tub_t *(*)[256])
-			kmalloc(sizeof(*itubpp), GFP_KERNEL);
-		if (itubpp == NULL) {
-			if (tubnummins == 1) {  /* if first time */
-				kfree(tubirqs);
-				tubirqs = NULL;
-			}
-			return -ENOMEM;
-		} else {
-			memset(itubpp, 0, sizeof(*itubpp));
-			(*tubirqs)[irqhi] = itubpp;
-		}
-	}
-
-	/* Request interrupt service */
-	if ((tubp->irqrc = request_irq(irq, tubint, SA_INTERRUPT,
-	    "3270 tube driver", &tubp->devstat)) != 0)
-		return tubp->irqrc;
-
-	/* Fill in the proper subarray element */
-	(*itubpp)[irqlo] = tubp;
-	return 0;
-}
-
-/*
- * tubfindbyirq(irq)
- */
-static tub_t *
-tubfindbyirq(int irq)
-{
-	int irqhi = (irq >> 8) & 255;
-	int irqlo = irq & 255;
-	tub_t *tubp;
-
-	if (tubirqs == NULL)
-		return NULL;
-	if ((*tubirqs)[irqhi] == NULL)
-		return NULL;
-	tubp = (*(*tubirqs)[irqhi])[irqlo];
-	if (tubp->irq == irq)
-		return tubp;
-	return NULL;
-}
-
-/*
- * tubdelbyirq(tub_t*, irq)
- */
-static void
-tubdelbyirq(tub_t *tubp, int irq)
-{
-	int irqhi = (irq >> 8) & 255;
-	int irqlo = irq & 255;
-	tub_t *(*itubpp)[256], *itubp;
-
-	if (tubirqs == NULL) {
-		printk(KERN_ERR "tubirqs is NULL\n");
-		return;
-	}
-	itubpp = (*tubirqs)[irqhi];
-	if (itubpp == NULL) {
-		printk(KERN_ERR "tubirqs[%d] is NULL\n", irqhi);
-		return;
-	}
-	itubp = (*itubpp)[irqlo];
-	if (itubp == NULL) {
-		printk(KERN_ERR "tubirqs[%d][%d] is NULL\n", irqhi, irqlo);
-		return;
-	}
-	if (itubp->irqrc == 0)
-		free_irq(irq, &itubp->devstat);
-	(*itubpp)[irqlo] = NULL;
-}
-
-/*
- * tubfiniirqs() -- clean up storage in tub_t *(*(*tubirqs)[256])[256]
- */
-static void
-tubfiniirqs(void)
-{
-	int i;
-	tub_t *(*itubpp)[256];
-
-	if (tubirqs != NULL) {
-		for (i = 0; i < 256; i++) {
-			if ((itubpp = (*tubirqs)[i])) {
-				kfree(itubpp);
-				(*tubirqs)[i] = NULL;
-			}
-		}
-		kfree(tubirqs);
-		tubirqs = NULL;
-	}
-}
-
-module_init(tub3270_init);
-module_exit(tub3270_exit);
diff -puN -L drivers/s390/char/tubfs.c drivers/s390/char/tubfs.c~s390-08-new-3270-driver /dev/null
--- 25/drivers/s390/char/tubfs.c
+++ /dev/null	Thu Apr 11 07:25:15 2002
@@ -1,473 +0,0 @@
-/*
- *  IBM/3270 Driver -- Copyright (C) UTS Global LLC
- *
- *  tubfs.c -- Fullscreen driver
- *
- *
- *
- *
- *
- *  Author:  Richard Hitt
- */
-#include "tubio.h"
-
-int fs3270_major = -1;			/* init to impossible -1 */
-
-static int fs3270_open(struct inode *, struct file *);
-static int fs3270_close(struct inode *, struct file *);
-static int fs3270_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
-static ssize_t fs3270_read(struct file *, char *, size_t, loff_t *);
-static ssize_t fs3270_write(struct file *, const char *, size_t, loff_t *);
-static int fs3270_wait(tub_t *, long *);
-static void fs3270_int(tub_t *tubp, devstat_t *dsp);
-extern void tty3270_refresh(tub_t *);
-
-static struct file_operations fs3270_fops = {
-	.owner = THIS_MODULE,		/* owner */
-	.read 	= fs3270_read,	/* read */
-	.write	= fs3270_write,	/* write */
-	.ioctl	= fs3270_ioctl,	/* ioctl */
-	.open 	= fs3270_open,	/* open */
-	.release = fs3270_close,	/* release */
-};
-
-/*
- * fs3270_init() -- Initialize fullscreen tubes
- */
-int
-fs3270_init(void)
-{
-	int rc;
-
-	rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops);
-	if (rc) {
-		printk(KERN_ERR "tubmod can't get major nbr %d: error %d\n",
-			IBM_FS3270_MAJOR, rc);
-		return -1;
-	}
-	devfs_mk_dir("3270");
-	devfs_mk_cdev(MKDEV(IBM_FS3270_MAJOR, 0),
-			S_IFCHR|S_IRUGO|S_IWUGO, "3270/tub");
-	fs3270_major = IBM_FS3270_MAJOR;
-	return 0;
-}
-
-/*
- * fs3270_fini() -- Uninitialize fullscreen tubes
- */
-void
-fs3270_fini(void)
-{
-	if (fs3270_major != -1) {
-		devfs_remove("3270");
-		devfs_remove("3270/tub");
-		unregister_chrdev(fs3270_major, "fs3270");
-		fs3270_major = -1;
-	}
-}
-
-/*
- * fs3270_open
- */
-static int
-fs3270_open(struct inode *ip, struct file *fp)
-{
-	tub_t *tubp;
-	long flags;
-
-	/* See INODE2TUB(ip) for handling of "/dev/3270/tub" */
-	if ((tubp = INODE2TUB(ip)) == NULL)
-		return -ENOENT;
-
-	TUBLOCK(tubp->irq, flags);
-	if (tubp->mode == TBM_FS || tubp->mode == TBM_FSLN) {
-		TUBUNLOCK(tubp->irq, flags);
-		return -EBUSY;
-	}
-
-	fp->private_data = ip;
-	tubp->mode = TBM_FS;
-	tubp->intv = fs3270_int;
-	tubp->dstat = 0;
-	tubp->fs_pid = current->pid;
-	tubp->fsopen = 1;
-	TUBUNLOCK(tubp->irq, flags);
-	return 0;
-}
-
-/*
- * fs3270_close aka release:  free the irq
- */
-static int
-fs3270_close(struct inode *ip, struct file *fp)
-{
-	tub_t *tubp;
-	long flags;
-
-	if ((tubp = INODE2TUB(ip)) == NULL)
-		return -ENODEV;
-
-	fs3270_wait(tubp, &flags);
-	tubp->fsopen = 0;
-	tubp->fs_pid = 0;
-	tubp->intv = NULL;
-	tubp->mode = 0;
-	tty3270_refresh(tubp);
-	TUBUNLOCK(tubp->irq, flags);
-	return 0;
-}
-
-/*
- * fs3270_release() called from tty3270_hangup()
- */
-void
-fs3270_release(tub_t *tubp)
-{
-	long flags;
-
-	if (tubp->mode != TBM_FS)
-		return;
-	fs3270_wait(tubp, &flags);
-	tubp->fsopen = 0;
-	tubp->fs_pid = 0;
-	tubp->intv = NULL;
-	tubp->mode = 0;
-	/*tty3270_refresh(tubp);*/
-	TUBUNLOCK(tubp->irq, flags);
-}
-
-/*
- * fs3270_wait(tub_t *tubp, int *flags) -- Wait to use tube
- * Entered without irq lock
- * On return:
- *      * Lock is held
- *      * Value is 0 or -ERESTARTSYS
- */
-static int
-fs3270_wait(tub_t *tubp, long *flags)
-{
-	DECLARE_WAITQUEUE(wait, current);
-
-	TUBLOCK(tubp->irq, *flags);
-	add_wait_queue(&tubp->waitq, &wait);
-	while (!signal_pending(current) &&
-	    ((tubp->mode != TBM_FS) ||
-	     (tubp->flags & (TUB_WORKING | TUB_RDPENDING)) != 0)) {
-#warning FIXME: [kj] use set_current_state instead of current->state=
-		current->state = TASK_INTERRUPTIBLE;
-		TUBUNLOCK(tubp->irq, *flags);
-		schedule();
-#warning FIXME: [kj] use set_current_state instead of current->state=
-		current->state = TASK_RUNNING;
-		TUBLOCK(tubp->irq, *flags);
-	}
-	remove_wait_queue(&tubp->waitq, &wait);
-	return signal_pending(current)? -ERESTARTSYS: 0;
-}
-
-/*
- * fs3270_io(tubp, ccw1_t*) -- start I/O on the tube
- * Entered with irq lock held, WORKING off
- */
-static int
-fs3270_io(tub_t *tubp, ccw1_t *ccwp)
-{
-	int rc;
-
-	rc = do_IO(tubp->irq, ccwp, tubp->irq, 0, 0);
-	tubp->flags |= TUB_WORKING;
-	tubp->dstat = 0;
-	return rc;
-}
-
-/*
- * fs3270_tasklet(tubp) -- Perform back-half processing
- */
-static void
-fs3270_tasklet(unsigned long data)
-{
-	long flags;
-	tub_t *tubp;
-	addr_t *ip;
-
-	tubp = (tub_t *) data;
-	TUBLOCK(tubp->irq, flags);
-	tubp->flags &= ~TUB_BHPENDING;
-
-	if (tubp->wbuf) {       /* if we were writing */
-		for (ip = tubp->wbuf; ip < tubp->wbuf+33; ip++) {
-			if (*ip == 0)
-				break;
-			kfree(phys_to_virt(*ip));
-		}
-		kfree(tubp->wbuf);
-		tubp->wbuf = NULL;
-	}
-
-	if ((tubp->flags & (TUB_ATTN | TUB_RDPENDING)) ==
-	    (TUB_ATTN | TUB_RDPENDING)) {
-		fs3270_io(tubp, &tubp->rccw);
-		tubp->flags &= ~(TUB_ATTN | TUB_RDPENDING);
-	}
-
-	if ((tubp->flags & TUB_WORKING) == 0)
-		wake_up_interruptible(&tubp->waitq);
-
-	TUBUNLOCK(tubp->irq, flags);
-}
-
-/*
- * fs3270_sched_tasklet(tubp) -- Schedule the back half
- * Irq lock must be held on entry and remains held on exit.
- */
-static void
-fs3270_sched_tasklet(tub_t *tubp)
-{
-	if (tubp->flags & TUB_BHPENDING)
-		return;
-	tubp->flags |= TUB_BHPENDING;
-	tasklet_init(&tubp->tasklet, fs3270_tasklet,
-		     (unsigned long) tubp);
-	tasklet_schedule(&tubp->tasklet);
-}
-
-/*
- * fs3270_int(tubp, prp) -- Process interrupt from tube in FS mode
- * This routine is entered with irq lock held (see do_IRQ in s390io.c)
- */
-static void
-fs3270_int(tub_t *tubp, devstat_t *dsp)
-{
-#define	DEV_UE_BUSY \
-	(DEV_STAT_CHN_END | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP)
-
-#ifdef RBHNOTYET
-	/* XXX needs more work; must save 2d arg to fs370_io() */
-	/* Handle CE-DE-UE and subsequent UDE */
-	if (dsp->dstat == DEV_UE_BUSY) {
-		tubp->flags |= TUB_UE_BUSY;
-		return;
-	} else if (tubp->flags & TUB_UE_BUSY) {
-		tubp->flags &= ~TUB_UE_BUSY;
-		if (dsp->dstat == DEV_STAT_DEV_END &&
-		    (tubp->flags & TUB_WORKING) != 0) {
-			fs3270_io(tubp);
-			return;
-		}
-	}
-#endif
-
-	/* Handle ATTN */
-	if (dsp->dstat & DEV_STAT_ATTENTION)
-		tubp->flags |= TUB_ATTN;
-
-	if (dsp->dstat & DEV_STAT_CHN_END) {
-		tubp->cswl = dsp->rescnt;
-		if ((dsp->dstat & DEV_STAT_DEV_END) == 0)
-			tubp->flags |= TUB_EXPECT_DE;
-		else
-			tubp->flags &= ~TUB_EXPECT_DE;
-	} else if (dsp->dstat & DEV_STAT_DEV_END) {
-		if ((tubp->flags & TUB_EXPECT_DE) == 0)
-			tubp->flags |= TUB_UNSOL_DE;
-		tubp->flags &= ~TUB_EXPECT_DE;
-	}
-	if (dsp->dstat & DEV_STAT_DEV_END)
-		tubp->flags &= ~TUB_WORKING;
-
-	if ((tubp->flags & TUB_WORKING) == 0)
-		fs3270_sched_tasklet(tubp);
-}
-
-/*
- * process ioctl commands for the tube driver
- */
-static int
-fs3270_ioctl(struct inode *ip, struct file *fp,
-	unsigned int cmd, unsigned long arg)
-{
-	tub_t *tubp;
-	int rc = 0;
-	long flags;
-
-	if ((tubp = INODE2TUB(ip)) == NULL)
-		return -ENODEV;
-	if ((rc = fs3270_wait(tubp, &flags))) {
-		TUBUNLOCK(tubp->irq, flags);
-		return rc;
-	}
-
-	switch(cmd) {
-	case TUBICMD: tubp->icmd = arg; break;
-	case TUBOCMD: tubp->ocmd = arg; break;
-	case TUBGETI: put_user(tubp->icmd, (char *)arg); break;
-	case TUBGETO: put_user(tubp->ocmd, (char *)arg); break;
-	case TUBGETMOD:
-		if (copy_to_user((char *)arg, &tubp->tubiocb,
-		    sizeof tubp->tubiocb))
-			rc = -EFAULT;
-		break;
-	}
-	TUBUNLOCK(tubp->irq, flags);
-	return rc;
-}
-
-/*
- * process read commands for the tube driver
- */
-static ssize_t
-fs3270_read(struct file *fp, char *dp, size_t len, loff_t *off)
-{
-	tub_t *tubp;
-	char *kp;
-	ccw1_t *cp;
-	int rc;
-	long flags;
-	addr_t *idalp, *ip;
-	char *tp;
-	int count, piece;
-	int size;
-
-	if (len == 0 || len > 65535) {
-		return -EINVAL;
-	}
-
-	if ((tubp = INODE2TUB((struct inode *)fp->private_data)) == NULL)
-		return -ENODEV;
-
-	ip = idalp = kmalloc(33*sizeof(addr_t), GFP_ATOMIC|GFP_DMA);
-	if (idalp == NULL)
-		return -EFAULT;
-	memset(idalp, 0, 33 * sizeof *idalp);
-	count = len;
-	while (count) {
-		piece = MIN(count, 0x800);
-		size = count == len? piece: 0x800;
-		if ((kp = kmalloc(size, GFP_KERNEL|GFP_DMA)) == NULL) {
-			len = -ENOMEM;
-			goto do_cleanup;
-		}
-		*ip++ = virt_to_phys(kp);
-		count -= piece;
-	}
-
-	if ((rc = fs3270_wait(tubp, &flags)) != 0) {
-		TUBUNLOCK(tubp->irq, flags);
-		len = rc;
-		goto do_cleanup;
-	}
-	cp = &tubp->rccw;
-	if (tubp->icmd == 0 && tubp->ocmd != 0)  tubp->icmd = 6;
-	cp->cmd_code = tubp->icmd?:2;
-	cp->flags = CCW_FLAG_SLI | CCW_FLAG_IDA;
-	cp->count = len;
-	cp->cda = virt_to_phys(idalp);
-	tubp->flags |= TUB_RDPENDING;
-	TUBUNLOCK(tubp->irq, flags);
-
-	if ((rc = fs3270_wait(tubp, &flags)) != 0) {
-		tubp->flags &= ~TUB_RDPENDING;
-		len = rc;
-		TUBUNLOCK(tubp->irq, flags);
-		goto do_cleanup;
-	}
-	TUBUNLOCK(tubp->irq, flags);
-
-	len -= tubp->cswl;
-	count = len;
-	tp = dp;
-	ip = idalp;
-	while (count) {
-		piece = MIN(count, 0x800);
-		if (copy_to_user(tp, phys_to_virt(*ip), piece) != 0) {
-			len = -EFAULT;
-			goto do_cleanup;
-		}
-		count -= piece;
-		tp += piece;
-		ip++;
-	}
-
-do_cleanup:
-	for (ip = idalp; ip < idalp+33; ip++) {
-		if (*ip == 0)
-			break;
-		kfree(phys_to_virt(*ip));
-	}
-	kfree(idalp);
-	return len;
-}
-
-/*
- * process write commands for the tube driver
- */
-static ssize_t
-fs3270_write(struct file *fp, const char *dp, size_t len, loff_t *off)
-{
-	tub_t *tubp;
-	ccw1_t *cp;
-	int rc;
-	long flags;
-	void *kb;
-	addr_t *idalp, *ip;
-	int count, piece;
-	int index;
-	int size;
-
-	if (len > 65535 || len == 0)
-		return -EINVAL;
-
-	/* Locate the tube */
-	if ((tubp = INODE2TUB((struct inode *)fp->private_data)) == NULL)
-		return -ENODEV;
-
-	ip = idalp = kmalloc(33*sizeof(addr_t), GFP_ATOMIC|GFP_DMA);
-	if (idalp == NULL)
-		return -EFAULT;
-	memset(idalp, 0, 33 * sizeof *idalp);
-
-	count = len;
-	index = 0;
-	while (count) {
-		piece = MIN(count, 0x800);
-		size = count == len? piece: 0x800;
-		if ((kb = kmalloc(size, GFP_KERNEL|GFP_DMA)) == NULL) {
-			len = -ENOMEM;
-			goto do_cleanup;
-		}
-		*ip++ = virt_to_phys(kb);
-		if (copy_from_user(kb, &dp[index], piece) != 0) {
-			len = -EFAULT;
-			goto do_cleanup;
-		}
-		count -= piece;
-		index += piece;
-	}
-
-	/* Wait till tube's not working or signal is pending */
-	if ((rc = fs3270_wait(tubp, &flags))) {
-		len = rc;
-		TUBUNLOCK(tubp->irq, flags);
-		goto do_cleanup;
-	}
-
-	/* Make CCW and start I/O.  Back end will free buffers & idal. */
-	tubp->wbuf = idalp;
-	cp = &tubp->wccw;
-	cp->cmd_code = tubp->ocmd? tubp->ocmd == 5? 13: tubp->ocmd: 1;
-	cp->flags = CCW_FLAG_SLI | CCW_FLAG_IDA;
-	cp->count = len;
-	cp->cda = virt_to_phys(tubp->wbuf);
-	fs3270_io(tubp, cp);
-	TUBUNLOCK(tubp->irq, flags);
-	return len;
-
-do_cleanup:
-	for (ip = idalp; ip < idalp+33; ip++) {
-		if (*ip == 0)
-			break;
-		kfree(phys_to_virt(*ip));
-	}
-	kfree(idalp);
-	return len;
-}
diff -puN -L drivers/s390/char/tubio.h drivers/s390/char/tubio.h~s390-08-new-3270-driver /dev/null
--- 25/drivers/s390/char/tubio.h
+++ /dev/null	Thu Apr 11 07:25:15 2002
@@ -1,417 +0,0 @@
-/*
- *  IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC
- *
- *  tubio.h -- All-Purpose header file
- *
- *
- *
- *
- *
- *  Author:  Richard Hitt
- */
-#include <linux/config.h>
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/tty.h>
-
-#include <linux/major.h>
-#ifndef IBM_TTY3270_MAJOR
-#  define IBM_TTY3270_MAJOR 212
-#endif /* IBM_TTY3270_MAJOR */
-#ifndef IBM_FS3270_MAJOR
-#  define IBM_FS3270_MAJOR 213
-#endif /* IBM_FS3270_MAJOR */
-
-
-#include <linux/slab.h>
-#include <asm/irq.h>
-#include <asm/io.h>
-#include <asm/idals.h>
-#include <linux/console.h>
-#include <linux/interrupt.h>
-#include <asm/ebcdic.h>
-#include <asm/uaccess.h>
-#include <linux/proc_fs.h>
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
-#include <linux/devfs_fs_kernel.h>
-#endif
-
-#define TUB(x) (('3'<<8)|(x))
-#define TUBICMD TUB(3)
-#define TUBOCMD TUB(4)
-#define TUBGETI TUB(7)
-#define TUBGETO TUB(8)
-#define TUBSETMOD TUB(12)
-#define TUBGETMOD TUB(13)
-#define TIOPOLL TUB(32)
-#define TIOPOKE TUB(33)
-#define TIONPOKE TUB(34)
-#define TIOTNORM TUB(35)
-
-/* Local Channel Commands */
-#define TC_WRITE   0x01
-#define TC_EWRITE  0x05
-#define TC_READMOD 0x06
-#define TC_EWRITEA 0x0d
-#define TC_WRITESF 0x11
-
-/* Buffer Control Orders */
-#define TO_SF 0x1d
-#define TO_SBA 0x11
-#define TO_IC 0x13
-#define TO_PT 0x05
-#define TO_RA 0x3c
-#define TO_SFE 0x29
-#define TO_EUA 0x12
-#define TO_MF 0x2c
-#define TO_SA 0x28
-
-/* Field Attribute Bytes */
-#define TF_INPUT 0x40           /* Visible input */
-#define TF_INPUTN 0x4c          /* Invisible input */
-#define TF_INMDT 0xc1           /* Visible, Set-MDT */
-#define TF_LOG 0x60
-#define TF_STAT 0x60
-
-/* Character Attribute Bytes */
-#define TAT_RESET 0x00
-#define TAT_FIELD 0xc0
-#define TAT_EXTHI 0x41
-#define TAT_COLOR 0x42
-#define TAT_CHARS 0x43
-#define TAT_TRANS 0x46
-
-/* Extended-Highlighting Bytes */
-#define TAX_RESET 0x00
-#define TAX_BLINK 0xf1
-#define TAX_REVER 0xf2
-#define TAX_UNDER 0xf4
-
-/* Reset value */
-#define TAR_RESET 0x00
-
-/* Color values */
-#define TAC_RESET 0x00
-#define TAC_BLUE 0xf1
-#define TAC_RED 0xf2
-#define TAC_PINK 0xf3
-#define TAC_GREEN 0xf4
-#define TAC_TURQ 0xf5
-#define TAC_YELLOW 0xf6
-#define TAC_WHITE 0xf7
-#define TAC_DEFAULT 0x00
-
-/* Write Control Characters */
-#define TW_NONE 0x40            /* No particular action */
-#define TW_KR 0xc2              /* Keyboard restore */
-#define TW_PLUSALARM 0x04       /* Add this bit for alarm */
-
-/* Attention-ID (AID) Characters */
-#define TA_CLEAR 0x6d
-#define TA_PA2 0x6e
-#define TA_ENTER 0x7d
-/* more to come */
-
-#define MIN(a, b) ((a) < (b)? (a): (b))
-
-#define TUB_BUFADR(adr, cpp) \
-	tty3270_tub_bufadr(tubp, adr, cpp)
-
-#define TUB_EBCASC(addr, nr) codepage_convert(tub_ebcasc, addr, nr)
-#define TUB_ASCEBC(addr, nr) codepage_convert(tub_ascebc, addr, nr)
-
-/*
- *
- * General global values for the tube driver
- *
- */
-enum tubmode {
-	TBM_LN,                 /* Line mode */
-	TBM_FS,                 /* Fullscreen mode */
-	TBM_FSLN                /* Line mode shelled out of fullscreen */
-};
-enum tubstat {              /* normal-mode status */
-	TBS_RUNNING,            /* none of the following */
-	TBS_MORE,               /* timed "MORE..." in status */
-	TBS_HOLD                /* untimed "HOLDING" in status */
-};
-enum tubcmd {           /* normal-mode actions to do */
-	TBC_CONOPEN,		/* Erase-write the console */
-	TBC_OPEN,               /* Open the tty screen */
-	TBC_UPDATE,             /* Add lines to the log, clear cmdline */
-	TBC_UPDLOG,             /* Add lines to log */
-	TBC_KRUPDLOG,           /* Add lines to log, reset kbd */
-	TBC_CLEAR,              /* Build screen from scratch */
-	TBC_CLRUPDLOG,          /* Do log & status, not cmdline */
-	TBC_UPDSTAT,            /* Do status update only */
-	TBC_CLRINPUT,           /* Clear input area only */
-	TBC_UPDINPUT            /* Update input area only */
-};
-enum tubwhat {          /* echo what= proc actions */
-	TW_BOGUS,               /* Nothing at all */
-	TW_CONFIG               /* Output configuration info */
-};
-
-
-
-
-
-#define TUBMAXMINS      256
-#define _GEOM_ROWS 24
-#define _GEOM_COLS 80
-#define GEOM_ROWS (tubp->geom_rows)
-#define GEOM_COLS (tubp->geom_cols)
-#define GEOM_MAXROWS 127
-#define GEOM_MAXCOLS 132
-#define GEOM_INPLEN (GEOM_COLS * 2 - 20)
-#define GEOM_MAXINPLEN (GEOM_MAXCOLS * 2 - 20)
-#define GEOM_INPUT (GEOM_COLS * (GEOM_ROWS - 2) - 1)  /* input atr posn */
-#define GEOM_STAT (GEOM_INPUT + 1 + GEOM_INPLEN)
-#define GEOM_LOG   (GEOM_COLS * GEOM_ROWS - 1)   /* log atr posn */
-#define TS_RUNNING "Linux Running     "
-#define TS_MORE    "Linux More...     "
-#define DEFAULT_SCROLLTIME 5
-#define TS_HOLD    "Linux Holding     "
-/* data length used by tty3270_set_status_area: SBA (3), SF (2), data */
-#define TS_LENGTH (sizeof TS_RUNNING + 3 + 2)
-
-typedef struct {
-	int aid;                        /* What-to-do flags */
-	char *string;                   /* Optional input string */
-} aid_t;
-#define AIDENTRY(ch, tubp)  (&((tubp)->tty_aid[(ch) & 0x3f]))
-
-/* For TUBGETMOD and TUBSETMOD.  Should include. */
-typedef struct tubiocb {
-	short model;
-	short line_cnt;
-	short col_cnt;
-	short pf_cnt;
-	short re_cnt;
-	short map;
-} tubiocb_t;
-
-/* Flags that go in int aid, above */
-#define TA_CLEARKEY     0x01            /* Key does hardware CLEAR */
-#define TA_SHORTREAD    0x02            /* Key does hardware shortread */
-/* If both are off, key does hardware Read Modified. */
-#define TA_DOENTER      0x04            /* Treat key like ENTER */
-#define TA_DOSTRING     0x08            /* Use string and ENTER */
-#define TA_DOSTRINGD    0x10            /* Display string & set MDT */
-#define TA_CLEARLOG     0x20            /* Make key cause clear of log */
-
-/*
- * Tube driver buffer control block
- */
-typedef struct bcb_s {
-	char	*bc_buf;		/* Pointer to buffer */
-	int	bc_len;			/* Length of buffer */
-	int	bc_cnt;			/* Count of bytes buffered */
-	int	bc_wr;			/* Posn to write next byte into */
-	int	bc_rd;			/* Posn to read next byte from */
-} bcb_t;
-
-typedef struct tub_s {
-	int             minor;
-	int             irq;
-	int             irqrc;
-	int             devno;
-	int             geom_rows;
-	int             geom_cols;
-	tubiocb_t       tubiocb;
-	int             lnopen;
-	int             fsopen;
-	int             icmd;
-	int             ocmd;
-	devstat_t       devstat;
-	ccw1_t          rccw;
-	ccw1_t          wccw;
-	addr_t		*wbuf;
-	int             cswl;
-	void            (*intv)(struct tub_s *, devstat_t *);
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
-	struct wait_queue	*waitq;
-#else
-	wait_queue_head_t waitq;
-#endif
-	int             dstat;
-	sense_t         sense;
-	enum tubmode    mode;
-	enum tubstat    stat;
-	enum tubcmd     cmd;
-	int             flags;		/* See below for values */
-	struct tasklet_struct tasklet;
-
-	/* Stuff for fs-driver support */
-	pid_t           fs_pid;         /* Pid if TBM_FS */
-
-
-	/* Stuff for tty-driver support */
-	struct tty_struct *tty;
-	char *tty_input;		/* tty input area */
-	int tty_inattr;         	/* input-area field attribute */
-#define TTY_OUTPUT_SIZE 1024
-	bcb_t tty_bcb;			/* Output buffer control info */
-	int tty_oucol;                  /* Kludge */
-	int tty_nextlogx;               /* next screen-log position */
-	int tty_savecursor;		/* saved cursor position */
-	int tty_scrolltime;             /* scrollforward wait time, sec */
-	struct timer_list tty_stimer;   /* timer for scrolltime */
-	aid_t tty_aid[64];              /* Aid descriptors */
-	int tty_aidinit;                /* Boolean */
-	int tty_showaidx;               /* Last aid x to set_aid */
-	int tty_14bitadr;               /* 14-bit bufadrs okay */
-#define MAX_TTY_ESCA 24			/* Set-Attribute-Order array */
-	char tty_esca[MAX_TTY_ESCA];	/* SA array */
-	int tty_escx;			/* Current index within it */
-
-	/* For command recall --- */
-	char *(*tty_rclbufs)[];         /* Array of ptrs to recall bufs */
-	int tty_rclk;                   /* Size of array tty_rclbufs */
-	int tty_rclp;                   /* Index for most-recent cmd */
-	int tty_rclb;                   /* Index for backscrolling */
-
-	/* Work area to contain the hardware write stream */
-	char (*ttyscreen)[];            /* ptr to data stream area */
-	int ttyscreenl;			/* its length */
-	ccw1_t ttyccw;
-} tub_t;
-
-/* values for flags: */
-#define	TUB_WORKING	0x0001
-#define	TUB_BHPENDING	0x0002
-#define	TUB_RDPENDING	0x0004
-#define	TUB_ALARM	0x0008
-#define	TUB_SCROLLTIMING  0x0010
-#define	TUB_ATTN	0x0020
-#define	TUB_IACTIVE	0x0040
-#define	TUB_SIZED	0x0080
-#define	TUB_EXPECT_DE	0x0100
-#define	TUB_UNSOL_DE	0x0200
-#define	TUB_OPEN_STET	0x0400		/* No screen clear on open */
-#define	TUB_UE_BUSY	0x0800
-#define	TUB_INPUT_HACK	0x1000		/* Early init of command line */
-
-/*
- * Extra stuff for 3270 console support
- */
-#ifdef CONFIG_TN3270_CONSOLE
-extern int tub3270_con_devno;
-extern char (*tub3270_con_output)[];
-extern int tub3270_con_outputl;
-extern int tub3270_con_ouwr;
-extern int tub3270_con_oucount;
-extern int tub3270_con_irq;
-extern tub_t *tub3270_con_tubp;
-extern struct tty_driver tty3270_con_driver;
-#endif /* CONFIG_TN3270_CONSOLE */
-
-extern int tubnummins;
-extern tub_t *(*tubminors)[TUBMAXMINS];
-extern tub_t *(*(*tubirqs)[256])[256];
-extern unsigned char tub_ascebc[256];
-extern unsigned char tub_ebcasc[256];
-extern unsigned char tub_ebcgraf[64];
-extern int tubdebug;
-extern int fs3270_major;
-extern int tty3270_major;
-extern int tty3270_proc_misc;
-extern enum tubwhat tty3270_proc_what;
-extern struct tty_driver *tty3270_driver;
-
-#ifndef spin_trylock_irqsave
-#define spin_trylock_irqsave(lock, flags) \
-({ \
-	int success; \
-	local_irq_save(flags); \
-	success = spin_trylock(lock); \
-	if (success == 0) \
-		local_irq_restore(flags); \
-	success; \
-})
-#endif /* if not spin_trylock_irqsave */
-
-#ifndef s390irq_spin_trylock_irqsave
-#define s390irq_spin_trylock_irqsave(irq, flags) \
-	spin_trylock_irqsave(&(ioinfo[irq]->irq_lock), flags)
-#endif /* if not s390irq_spin_trylock_irqsave */
-
-#define TUBLOCK(irq, flags) \
-	s390irq_spin_lock_irqsave(irq, flags)
-
-#define TUBTRYLOCK(irq, flags) \
-	s390irq_spin_trylock_irqsave(irq, flags)
-
-#define TUBUNLOCK(irq, flags) \
-	s390irq_spin_unlock_irqrestore(irq, flags)
-
-/*
- * Find tub_t * given fullscreen device's irq (subchannel number)
- */
-#if 0
-extern tub_t *tubfindbyirq(int);
-#endif
-#define IRQ2TUB(irq) tubfindbyirq(irq)
-/*
- * Find tub_t * given fullscreen device's inode pointer
- * This algorithm takes into account /dev/3270/tub.
- */
-extern inline tub_t *INODE2TUB(struct inode *ip)
-{
-	unsigned int minor = iminor(ip);
-	tub_t *tubp = NULL;
-	if (minor == 0 && current->tty) {
-		if (current->tty->driver == tty3270_driver)
-			minor = current->tty->index;
-	}
-	if (minor <= tubnummins && minor > 0)
-		tubp = (*tubminors)[minor];
-	return tubp;
-}
-
-/*
- * Find tub_t * given non-fullscreen (tty) device's tty_struct pointer
- */
-extern inline tub_t *TTY2TUB(struct tty_struct *tty)
-{
-	unsigned index = tty->index;
-	tub_t *tubp = NULL;
-
-	if (index <= tubnummins && index > 0)
-		tubp = (*tubminors)[index];
-	return tubp;
-}
-
-extern int tub3270_movedata(bcb_t *, bcb_t *, int);
-#if 0
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
-extern int tubmakemin(int, dev_info_t *);
-#else
-extern int tubmakemin(int, s390_dev_info_t *);
-#endif
-#endif
-extern int tub3270_con_copy(tub_t *);
-extern int tty3270_rcl_init(tub_t *);
-extern int tty3270_rcl_set(tub_t *, char *, int);
-extern void tty3270_rcl_fini(tub_t *);
-extern int tty3270_rcl_get(tub_t *, char *, int, int);
-extern void tty3270_rcl_put(tub_t *, char *, int);
-extern void tty3270_rcl_purge(tub_t *);
-#if 0
-/* these appear to be unused outside of tubttyrcl */
-extern void tty3270_rcl_sync(tub_t *);
-extern int tty3270_rcl_resize(tub_t *, int);
-#endif
-extern int tty3270_size(tub_t *, long *);
-extern int tty3270_aid_init(tub_t *);
-extern void tty3270_aid_fini(tub_t *);
-extern void tty3270_aid_reinit(tub_t *);
-extern int tty3270_aid_get(tub_t *, int, int *, char **);
-extern int tty3270_aid_set(tub_t *, char *, int);
-extern int tty3270_build(tub_t *);
-extern void tty3270_scl_settimer(tub_t *);
-extern void tty3270_scl_resettimer(tub_t *);
-extern int tty3270_scl_set(tub_t *, char *, int);
-extern int tty3270_scl_init(tub_t *tubp);
-extern void tty3270_scl_fini(tub_t *tubp);
diff -puN -L drivers/s390/char/tubttyaid.c drivers/s390/char/tubttyaid.c~s390-08-new-3270-driver /dev/null
--- 25/drivers/s390/char/tubttyaid.c
+++ /dev/null	Thu Apr 11 07:25:15 2002
@@ -1,205 +0,0 @@
-/*
- *  IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC
- *
- *  tubttyaid.c -- Linemode Attention-ID functionality
- *
- *
- *
- *
- *
- *  Author:  Richard Hitt
- */
-#include "tubio.h"
-
-#define PA1_STR "^C"
-#define PF3_STR "^D"
-#define PF9_STR "\033j"
-#define PF10_STR "\033k"
-#define PF11_STR "\033j"
-/* other AID-key default strings */
-
-static aid_t aidtab[64] = {
-/* 00         */	{ 0, 0 },
-/* C1 = PF13  */	{ TA_DOSTRING, 0 },
-/* C2 = PF14  */	{ TA_DOSTRING, 0 },
-/* C3 = PF15  */	{ TA_DOSTRING, 0 },
-/* C4 = PF16  */	{ TA_DOSTRING, 0 },
-/* C5 = PF17  */	{ TA_DOSTRING, 0 },
-/* C6 = PF18  */	{ TA_DOSTRING, 0 },
-/* C7 = PF19  */	{ TA_DOSTRING, 0 },
-/* C8 = PF20  */	{ TA_DOSTRING, 0 },
-/* C9 = PF21  */	{ TA_DOSTRING, 0 },
-/* 4A = PF22  */	{ TA_DOSTRING, 0 },
-/* 4B = PF23  */	{ TA_DOSTRING, 0 },
-/* 4C = PF24  */	{ TA_DOSTRING, 0 },
-/* 0D         */	{ 0, 0 },
-/* 0E         */	{ 0, 0 },
-/* 0F         */	{ 0, 0 },
-/* 10         */	{ 0, 0 },
-/* 11         */	{ 0, 0 },
-/* 12         */	{ 0, 0 },
-/* 13         */	{ 0, 0 },
-/* 14         */	{ 0, 0 },
-/* 15         */	{ 0, 0 },
-/* 16         */	{ 0, 0 },
-/* 17         */	{ 0, 0 },
-/* 18         */	{ 0, 0 },
-/* 19         */	{ 0, 0 },
-/* 1A         */	{ 0, 0 },
-/* 1B         */	{ 0, 0 },
-/* 1C         */	{ 0, 0 },
-/* 1D         */	{ 0, 0 },
-/* 1E         */	{ 0, 0 },
-/* 1F         */	{ 0, 0 },
-/* 60 = NoAID */	{ 0, 0 },
-/* 21         */	{ 0, 0 },
-/* 22         */	{ 0, 0 },
-/* 23         */	{ 0, 0 },
-/* 24         */	{ 0, 0 },
-/* 25         */	{ 0, 0 },
-/* E6 = OpRdr */	{ 0, 0 },
-/* E7 = MSRdr */	{ 0, 0 },
-/* E8 = NoAID */	{ 0, 0 },
-/* 29         */	{ 0, 0 },
-/* 2A         */	{ 0, 0 },
-/* 6B =  PA3  */        { TA_SHORTREAD, 0 },
-/* 6C =  PA1  */        { TA_SHORTREAD | TA_DOSTRING, PA1_STR },
-/* 6D = CLEAR */        { TA_SHORTREAD | TA_CLEARKEY, 0 },
-/* 6E =  PA2  */        { TA_SHORTREAD | TA_CLEARLOG, 0 },
-/* 2F         */	{ 0, 0 },
-/* F0 = TstRq */        { 0, 0 },
-/* F1 =  PF1  */	{ TA_DOSTRING, 0 },
-/* F2 =  PF2  */	{ TA_DOSTRING, 0 },
-/* F3 =  PF3  */        { TA_DOSTRING, PF3_STR },
-/* F4 =  PF4  */	{ TA_DOSTRING, 0 },
-/* F5 =  PF5  */	{ TA_DOSTRING, 0 },
-/* F6 =  PF6  */	{ TA_DOSTRING, 0 },
-/* F7 =  PF7  */	{ TA_DOSTRING, 0 },
-/* F8 =  PF8  */	{ TA_DOSTRING, 0 },
-/* F9 =  PF9  */        { TA_DOSTRING, PF9_STR },
-/* 7A = PF10  */        { TA_DOSTRING, PF10_STR },
-/* 7B = PF11  */        { TA_DOSTRING, PF11_STR },
-/* 7C = PF12  */	{ TA_DOSTRING, 0 },
-/* 7D = ENTER */        { TA_DOENTER, 0 },
-/* 7E = Pen   */        { 0, 0 },
-/* 3F         */	{ 0, 0 },
-};
-
-int
-tty3270_aid_init(tub_t *tubp)
-{
-	memcpy(tubp->tty_aid, aidtab, sizeof aidtab);
-	tubp->tty_aidinit = 1;
-	return 0;
-}
-
-void
-tty3270_aid_fini(tub_t *tubp)
-{
-	int i;
-	char *sp;
-
-	if (tubp->tty_aidinit == 0)
-		return;
-	for (i = 0; i < 64; i++) {
-		if ((sp = tubp->tty_aid[i].string) == NULL)
-			continue;
-		if (sp == aidtab[i].string)
-			continue;
-		kfree(sp);
-	}
-	tubp->tty_aidinit = 0;
-}
-
-void
-tty3270_aid_reinit(tub_t *tubp)
-{
-	tty3270_aid_fini(tubp);
-	tty3270_aid_init(tubp);
-}
-
-int
-tty3270_aid_get(tub_t *tubp, int aid, int *aidflags, char **aidstring)
-{
-	aid_t *ap;
-
-	ap = AIDENTRY(aid, tubp);
-	*aidflags = ap->aid;
-	*aidstring = ap->string;
-	return 0;
-}
-
-/*
- * tty3270_aid_set() -- write_proc extension
- * Parse written string as an AID name.  Return 0 if it's not.
- * Otherwise absorb the string and return count or -error.
- */
-int
-tty3270_aid_set(tub_t *tubp, char *buf, int count)
-{
-	char name[8];
-	char *sp;
-	int aidn, aidx;
-	aid_t *ap;
-	int len;
-	char *pfp;
-
-	if (tubp->tty_aidinit == 0)
-		return 0;
-	if (count < 3)          /* If AID-key name too short */
-		return 0;
-	name[0] = buf[0] < 0x60? buf[0]: (buf[0] & 0x5f);
-	name[1] = buf[1] < 0x60? buf[1]: (buf[1] & 0x5f);
-	if (name[0] == 'P' && name[1] == 'F') {
-		aidn = simple_strtoul(buf+2, &sp, 10);
-		if (aidn < 1 || aidn > 24)
-			return 0;
-		aidx = aidn > 12? aidn - 12: aidn + 0x30;
-		ap = &tubp->tty_aid[aidx];
-	} else if (name[0] == 'P' && name[1] == 'A') {
-		aidn = simple_strtoul(buf+2, &sp, 10);
-		if (aidn < 1 || aidn > 3)
-			return 0;
-		switch(aidn) {
-		case 1:  aidx = 0x2c; break;
-		case 2:  aidx = 0x2e; break;
-		case 3:  aidx = 0x2b; break;
-		default:  aidx = 0; break;
-		}
-		ap = &tubp->tty_aid[aidx];
-	} else {
-		return 0;
-	}
-
-	if (*sp == '\0') {
-		tubp->tty_showaidx = ap - tubp->tty_aid;
-		return count;
-	} else if (*sp == '=') {
-		len = strlen(++sp);
-		if (len == 0) {
-			if (ap->string != NULL &&
-			    ap->string != aidtab[aidx].string)
-				kfree(ap->string);
-			ap->string = aidtab[aidx].string;
-			ap->aid = aidtab[aidx].aid;
-			return count;
-		}
-		if ((pfp = kmalloc(len + 1, GFP_KERNEL)) == NULL)
-			return -ENOMEM;
-		if (ap->string != NULL &&
-		    ap->string != aidtab[aidx].string)
-			kfree(ap->string);
-		if (sp[len - 1] == '\n') {
-			ap->aid = TA_DOSTRING;
-			sp[len - 1] = '\0';
-			len--;
-		} else {
-			ap->aid = TA_DOSTRINGD;
-		}
-		memcpy(pfp, sp, len + 1);
-		ap->string = pfp;
-		return count;
-	} else {
-		return -EINVAL;
-	}
-}
diff -puN -L drivers/s390/char/tubttybld.c drivers/s390/char/tubttybld.c~s390-08-new-3270-driver /dev/null
--- 25/drivers/s390/char/tubttybld.c
+++ /dev/null	Thu Apr 11 07:25:15 2002
@@ -1,587 +0,0 @@
-/*
- *  IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC
- *
- *  tubttybld.c -- Linemode tty driver screen-building functions
- *
- *
- *
- *
- *
- *  Author:  Richard Hitt
- */
-
-#include "tubio.h"
-
-extern int tty3270_io(tub_t *);
-static void tty3270_set_status_area(tub_t *, char **);
-static int tty3270_next_char(tub_t *);
-static void tty3270_unnext_char(tub_t *, char);
-static void tty3270_update_log_area(tub_t *, char **);
-static int tty3270_update_log_area_esc(tub_t *, char **, int *);
-static void tty3270_clear_log_area(tub_t *, char **);
-static void tty3270_tub_bufadr(tub_t *, int, char **);
-static void tty3270_set_bufadr(tub_t *, char **, int *);
-
-/*
- * tty3270_clear_log_area(tub_t *tubp, char **cpp)
- */
-static void
-tty3270_clear_log_area(tub_t *tubp, char **cpp)
-{
-	*(*cpp)++ = TO_SBA;
-	TUB_BUFADR(GEOM_LOG, cpp);
-	*(*cpp)++ = TO_SF;
-	*(*cpp)++ = TF_LOG;
-	*(*cpp)++ = TO_RA;
-	TUB_BUFADR(GEOM_INPUT, cpp);
-	*(*cpp)++ = '\0';
-	tubp->tty_oucol = tubp->tty_nextlogx = 0;
-	*(*cpp)++ = TO_SBA;
-	TUB_BUFADR(tubp->tty_nextlogx, cpp);
-}
-
-static void
-tty3270_update_log_area(tub_t *tubp, char **cpp)
-{
-	int lastx = GEOM_INPUT;
-	int c;
-	int next, fill, i;
-	int sba_needed = 1;
-	char *overrun = &(*tubp->ttyscreen)[tubp->ttyscreenl - TS_LENGTH];
-
-	/* Check for possible ESC sequence work to do */
-	if (tubp->tty_escx != 0) {
-		/* If compiling new escape sequence */
-		if (tubp->tty_esca[0] == 0x1b) {
-			if (tty3270_update_log_area_esc(tubp, cpp, &sba_needed))
-				return;
-		/* If esc seq needs refreshing after a write */
-		} else if (tubp->tty_esca[0] == TO_SA) {
-			tty3270_set_bufadr(tubp, cpp, &sba_needed);
-			for (i = 0; i < tubp->tty_escx; i++)
-				*(*cpp)++ = tubp->tty_esca[i];
-		} else {
-			printk(KERN_WARNING "tty3270_update_log_area esca "
-			"character surprising:  %.2x\n", tubp->tty_esca[0]);
-		}
-	}
-
-	/* Place characters */
-	while (tubp->tty_bcb.bc_cnt != 0) {
-		/* Check for room.  TAB could take up to 4 chars. */
-		if (&(*cpp)[4] >= overrun)
-			break;
-
-		/* Fetch a character */
-		if ((c = tty3270_next_char(tubp)) == -1)
-			break;
-
-		switch(c) {
-		default:
-			if (tubp->tty_nextlogx >= lastx) {
-				if (sba_needed == 0 ||
-				    tubp->stat == TBS_RUNNING) {
-					tty3270_unnext_char(tubp, c);
-					tubp->stat = TBS_MORE;
-					tty3270_set_status_area(tubp, cpp);
-					tty3270_scl_settimer(tubp);
-				}
-				goto do_return;
-			}
-			tty3270_set_bufadr(tubp, cpp, &sba_needed);
-			/* Use blank if we don't know the character */
-			*(*cpp)++ = tub_ascebc[(int)(c < ' '? ' ': c)];
-			tubp->tty_nextlogx++;
-			tubp->tty_oucol++;
-			break;
-		case 0x1b:              /* ESC */
-			tubp->tty_escx = 0;
-			if (tty3270_update_log_area_esc(tubp, cpp, &sba_needed))
-				return;
-			break;
-		case '\r':		/* 0x0d -- Carriage Return */
-			tubp->tty_nextlogx -=
-				tubp->tty_nextlogx % GEOM_COLS;
-			sba_needed = 1;
-			break;
-		case '\n':		/* 0x0a -- New Line */
-			if (tubp->tty_oucol == GEOM_COLS) {
-				tubp->tty_oucol = 0;
-				break;
-			}
-			next = (tubp->tty_nextlogx + GEOM_COLS) /
-				GEOM_COLS * GEOM_COLS;
-			tubp->tty_nextlogx = next;
-			tubp->tty_oucol = 0;
-			sba_needed = 1;
-			break;
-		case '\t':		/* 0x09 -- Tabulate */
-			fill = (tubp->tty_nextlogx % GEOM_COLS) % 8;
-			for (; fill < 8; fill++) {
-				if (tubp->tty_nextlogx >= lastx)
-					break;
-				*(*cpp)++ = tub_ascebc[' '];
-				tubp->tty_nextlogx++;
-				tubp->tty_oucol++;
-			}
-			break;
-		case '\a':		/* 0x07 -- Alarm */
-			tubp->flags |= TUB_ALARM;
-			break;
-		case '\f':		/* 0x0c -- Form Feed */
-			tty3270_clear_log_area(tubp, cpp);
-			break;
-		case 0xf:	/* SuSE "exit alternate mode" */
-			break;
-		}
-	}
-do_return:
-}
-
-#define NUMQUANT 8
-static int
-tty3270_update_log_area_esc(tub_t *tubp, char **cpp, int *sba_needed)
-{
-	int c;
-	int i, j;
-	int start, end, next;
-	int quant[NUMQUANT];
-	char *overrun = &(*tubp->ttyscreen)[tubp->ttyscreenl - TS_LENGTH];
-	char sabuf[NUMQUANT*3], *sap = sabuf, *cp;
-
-	/* If starting a sequence, stuff ESC at [0] */
-	if (tubp->tty_escx == 0)
-		tubp->tty_esca[tubp->tty_escx++] = 0x1b;
-
-	/* Now that sequence is started, see if room in buffer */
-	if (&(*cpp)[NUMQUANT * 3] >= overrun)
-		return tubp->tty_escx;
-
-	/* Gather the rest of the sequence's characters */
-	while (tubp->tty_escx < sizeof tubp->tty_esca) {
-		if ((c = tty3270_next_char(tubp)) == -1)
-			return tubp->tty_escx;
-		if (tubp->tty_escx == 1) {
-			switch(c) {
-			case '[':
-				tubp->tty_esca[tubp->tty_escx++] = c;
-				continue;
-			case '7':
-				tubp->tty_savecursor = tubp->tty_nextlogx;
-				goto done_return;
-			case '8':
-				next = tubp->tty_savecursor;
-				goto do_setcur;
-			default:
-				goto error_return;
-			}
-		}
-		tubp->tty_esca[tubp->tty_escx++] = c;
-		if (c != ';' && (c < '0' || c > '9'))
-			break;
-	}
-
-	/* Check for overrun */
-	if (tubp->tty_escx == sizeof tubp->tty_esca)
-		goto error_return;
-
-	/* Parse potentially empty string "nn;nn;nn..." */
-	i = -1;
-	j = 2;		/* skip ESC, [ */
-	c = ';';
-	do {
-		if (c == ';') {
-			if (++i == NUMQUANT)
-				goto error_return;
-			quant[i] = 0;
-		} else if (c < '0' || c > '9') {
-			break;
-		} else {
-			quant[i] = quant[i] * 10 + c - '0';
-		}
-		c = tubp->tty_esca[j];
-	} while (j++ < tubp->tty_escx);
-
-	/* Add 3270 data stream output to execute the sequence */
-	switch(c) {
-	case 'm':		/* Set Attribute */
-		for (next = 0; next <= i; next++) {
-			int type = -1, value = 0;
-
-			switch(quant[next]) {
-			case 0:		/* Reset */
-				next = tubp->tty_nextlogx;
-				tty3270_set_bufadr(tubp, cpp, sba_needed);
-				*(*cpp)++ = TO_SA;
-				*(*cpp)++ = TAT_EXTHI;
-				*(*cpp)++ = TAX_RESET;
-				*(*cpp)++ = TO_SA;
-				*(*cpp)++ = TAT_COLOR;
-				*(*cpp)++ = TAC_RESET;
-				tubp->tty_nextlogx = next;
-				*sba_needed = 1;
-				sap = sabuf;
-				break;
-			case 1:		/* Bright */
-				break;
-			case 2:		/* Dim */
-				break;
-			case 4:		/* Underscore */
-				type = TAT_EXTHI; value = TAX_UNDER;
-				break;
-			case 5:		/* Blink */
-				type = TAT_EXTHI; value = TAX_BLINK;
-				break;
-			case 7:		/* Reverse */
-				type = TAT_EXTHI; value = TAX_REVER;
-				break;
-			case 8:		/* Hidden */
-				break;		/* For now ... */
-			/* Foreground Colors */
-			case 30:	/* Black */
-				type = TAT_COLOR; value = TAC_DEFAULT;
-				break;
-			case 31:	/* Red */
-				type = TAT_COLOR; value = TAC_RED;
-				break;
-			case 32:	/* Green */
-				type = TAT_COLOR; value = TAC_GREEN;
-				break;
-			case 33:	/* Yellow */
-				type = TAT_COLOR; value = TAC_YELLOW;
-				break;
-			case 34:	/* Blue */
-				type = TAT_COLOR; value = TAC_BLUE;
-				break;
-			case 35:	/* Magenta */
-				type = TAT_COLOR; value = TAC_PINK;
-				break;
-			case 36:	/* Cyan */
-				type = TAT_COLOR; value = TAC_TURQ;
-				break;
-			case 37:	/* White */
-				type = TAT_COLOR; value = TAC_WHITE;
-				break;
-			case 39:	/* Black */
-				type = TAT_COLOR; value = TAC_DEFAULT;
-				break;
-			/* Background Colors */
-			case 40:	/* Black */
-			case 41:	/* Red */
-			case 42:	/* Green */
-			case 43:	/* Yellow */
-			case 44:	/* Blue */
-			case 45:	/* Magenta */
-			case 46:	/* Cyan */
-			case 47:	/* White */
-				break;		/* For now ... */
-			/* Oops */
-			default:
-				break;
-			}
-			if (type != -1) {
-				tty3270_set_bufadr(tubp, cpp, sba_needed);
-				*(*cpp)++ = TO_SA;
-				*(*cpp)++ = type;
-				*(*cpp)++ = value;
-				*sap++ = TO_SA;
-				*sap++ = type;
-				*sap++ = value;
-			}
-		}
-		break;
-
-	case 'H':		/* Cursor Home */
-	case 'f':		/* Force Cursor Position */
-		if (quant[0]) quant[0]--;
-		if (quant[1]) quant[1]--;
-		next = quant[0] * GEOM_COLS + quant[1];
-		goto do_setcur;
-	case 'A':		/* Cursor Up */
-		if (quant[i] == 0) quant[i] = 1;
-		next = tubp->tty_nextlogx - GEOM_COLS * quant[i];
-		goto do_setcur;
-	case 'B':		/* Cursor Down */
-		if (quant[i] == 0) quant[i] = 1;
-		next = tubp->tty_nextlogx + GEOM_COLS * quant[i];
-		goto do_setcur;
-	case 'C':		/* Cursor Forward */
-		if (quant[i] == 0) quant[i] = 1;
-		next = tubp->tty_nextlogx % GEOM_COLS;
-		start = tubp->tty_nextlogx - next;
-		next = start + MIN(next + quant[i], GEOM_COLS - 1);
-		goto do_setcur;
-	case 'D':		/* Cursor Backward */
-		if (quant[i] == 0) quant[i] = 1;
-		next = MIN(quant[i], tubp->tty_nextlogx % GEOM_COLS);
-		next = tubp->tty_nextlogx - next;
-		goto do_setcur;
-	case 'G':
-		if (quant[0]) quant[0]--;
-		next = tubp->tty_nextlogx / GEOM_COLS * GEOM_COLS + quant[0];
-do_setcur:
-		if (next < 0)
-			break;
-		tubp->tty_nextlogx = next;
-		tubp->tty_oucol = tubp->tty_nextlogx % GEOM_COLS;
-		*sba_needed = 1;
-		break;
-
-	case 'r':		/* Define scroll area */
-		start = quant[0];
-		if (start <= 0) start = 1;
-		if (start > GEOM_ROWS - 2) start = GEOM_ROWS - 2;
-		tubp->tty_nextlogx = (start - 1) * GEOM_COLS;
-		tubp->tty_oucol = 0;
-		*sba_needed = 1;
-		break;
-
-	case 'X':		/* Erase for n chars from cursor */
-		start = tubp->tty_nextlogx;
-		end = start + (quant[0]?: 1);
-		goto do_fill;
-	case 'J':		/* Erase to screen end from cursor */
-		*(*cpp)++ = TO_SBA;
-		TUB_BUFADR(tubp->tty_nextlogx, cpp);
-		*(*cpp)++ = TO_RA;
-		TUB_BUFADR(GEOM_INPUT, cpp);
-		*(*cpp)++ = tub_ascebc[' '];
-		*(*cpp)++ = TO_SBA;
-		TUB_BUFADR(tubp->tty_nextlogx, cpp);
-		break;
-	case 'K':
-		start = tubp->tty_nextlogx;
-		end = (start + GEOM_COLS) / GEOM_COLS * GEOM_COLS;
-do_fill:
-		if (start >= GEOM_INPUT)
-			break;
-		if (end > GEOM_INPUT)
-			end = GEOM_INPUT;
-		if (end <= start)
-			break;
-		*(*cpp)++ = TO_SBA;
-		TUB_BUFADR(start, cpp);
-		if (end - start > 4) {
-			*(*cpp)++ = TO_RA;
-			TUB_BUFADR(end, cpp);
-			*(*cpp)++ = tub_ascebc[' '];
-		} else while (start++ < end) {
-			*(*cpp)++ = tub_ascebc[' '];
-		}
-		tubp->tty_nextlogx = end;
-		tubp->tty_oucol = tubp->tty_nextlogx % GEOM_COLS;
-		*sba_needed = 1;
-		break;
-	}
-done_return:
-	tubp->tty_escx = 0;
-	cp = sabuf;
-	while (cp != sap)
-		tubp->tty_esca[tubp->tty_escx++] = *cp++;
-	return 0;
-error_return:
-	tubp->tty_escx = 0;
-	return 0;
-}
-
-static int
-tty3270_next_char(tub_t *tubp)
-{
-	int c;
-	bcb_t *ib;
-
-	ib = &tubp->tty_bcb;
-	if (ib->bc_cnt == 0)
-		return -1;
-	c = ib->bc_buf[ib->bc_rd++];
-	if (ib->bc_rd == ib->bc_len)
-		ib->bc_rd = 0;
-	ib->bc_cnt--;
-	return c;
-}
-
-static void
-tty3270_unnext_char(tub_t *tubp, char c)
-{
-	bcb_t *ib;
-
-	ib = &tubp->tty_bcb;
-	if (ib->bc_rd == 0)
-		ib->bc_rd = ib->bc_len;
-	ib->bc_buf[--ib->bc_rd] = c;
-	ib->bc_cnt++;
-}
-
-
-static void
-tty3270_clear_input_area(tub_t *tubp, char **cpp)
-{
-	*(*cpp)++ = TO_SBA;
-	TUB_BUFADR(GEOM_INPUT, cpp);
-	*(*cpp)++ = TO_SF;
-	*(*cpp)++ = tubp->tty_inattr;
-	*(*cpp)++ = TO_IC;
-	*(*cpp)++ = TO_RA;
-	TUB_BUFADR(GEOM_STAT, cpp);
-	*(*cpp)++ = '\0';
-}
-
-static void
-tty3270_update_input_area(tub_t *tubp, char **cpp)
-{
-	int len;
-
-	*(*cpp)++ = TO_SBA;
-	TUB_BUFADR(GEOM_INPUT, cpp);
-	*(*cpp)++ = TO_SF;
-	*(*cpp)++ = TF_INMDT;
-	len = strlen(tubp->tty_input);
-	memcpy(*cpp, tubp->tty_input, len);
-	*cpp += len;
-	*(*cpp)++ = TO_IC;
-	len = GEOM_INPLEN - len;
-	if (len > 4) {
-		*(*cpp)++ = TO_RA;
-		TUB_BUFADR(GEOM_STAT, cpp);
-		*(*cpp)++ = '\0';
-	} else {
-		for (; len > 0; len--)
-			*(*cpp)++ = '\0';
-	}
-}
-
-/*
- * tty3270_set_status_area(tub_t *tubp, char **cpp)
- */
-static void
-tty3270_set_status_area(tub_t *tubp, char **cpp)
-{
-	char *sp;
-
-	if (tubp->stat == TBS_RUNNING)
-		sp = TS_RUNNING;
-	else if (tubp->stat == TBS_MORE)
-		sp = TS_MORE;
-	else if (tubp->stat == TBS_HOLD)
-		sp = TS_HOLD;
-	else
-		sp = "Linux Whatstat";
-
-	*(*cpp)++ = TO_SBA;
-	TUB_BUFADR(GEOM_STAT, cpp);
-	*(*cpp)++ = TO_SF;
-	*(*cpp)++ = TF_STAT;
-	memcpy(*cpp, sp, sizeof TS_RUNNING);
-	TUB_ASCEBC(*cpp, sizeof TS_RUNNING);
-	*cpp += sizeof TS_RUNNING;
-}
-
-/*
- * tty3270_build() -- build an output stream
- */
-int
-tty3270_build(tub_t *tubp)
-{
-	char *cp, *startcp;
-	int chancmd;
-	int writecc = TW_KR;
-	int force = 0;
-
-	if (tubp->mode == TBM_FS)
-		return 0;
-
-	cp = startcp = *tubp->ttyscreen + 1;
-
-	switch(tubp->cmd) {
-	default:
-		printk(KERN_WARNING "tty3270_build unknown command %d\n", tubp->cmd);
-		return 0;
-	case TBC_OPEN:
-tbc_open:
-		tubp->flags &= ~TUB_INPUT_HACK;
-		chancmd = TC_EWRITEA;
-		tty3270_clear_input_area(tubp, &cp);
-		tty3270_set_status_area(tubp, &cp);
-		tty3270_clear_log_area(tubp, &cp);
-		break;
-	case TBC_UPDLOG:
-		if (tubp->flags & TUB_INPUT_HACK)
-			goto tbc_open;
-		chancmd = TC_WRITE;
-		writecc = TW_NONE;
-		tty3270_update_log_area(tubp, &cp);
-		break;
-	case TBC_KRUPDLOG:
-		chancmd = TC_WRITE;
-		force = 1;
-		tty3270_update_log_area(tubp, &cp);
-		break;
-	case TBC_CLRUPDLOG:
-		chancmd = TC_WRITE;
-		tty3270_set_status_area(tubp, &cp);
-		tty3270_clear_log_area(tubp, &cp);
-		tty3270_update_log_area(tubp, &cp);
-		break;
-	case TBC_UPDATE:
-		chancmd = TC_EWRITEA;
-		tubp->tty_oucol = tubp->tty_nextlogx = 0;
-		tty3270_clear_input_area(tubp, &cp);
-		tty3270_set_status_area(tubp, &cp);
-		tty3270_update_log_area(tubp, &cp);
-		break;
-	case TBC_UPDSTAT:
-		chancmd = TC_WRITE;
-		tty3270_set_status_area(tubp, &cp);
-		break;
-	case TBC_CLRINPUT:
-		chancmd = TC_WRITE;
-		tty3270_clear_input_area(tubp, &cp);
-		break;
-	case TBC_UPDINPUT:
-		chancmd = TC_WRITE;
-		tty3270_update_input_area(tubp, &cp);
-		break;
-	}
-
-	/* Set Write Control Character and start I/O */
-	if (force == 0 && cp == startcp &&
-	    (tubp->flags & TUB_ALARM) == 0)
-		return 0;
-	if (tubp->flags & TUB_ALARM) {
-		tubp->flags &= ~TUB_ALARM;
-		writecc |= TW_PLUSALARM;
-	}
-	**tubp->ttyscreen = writecc;
-	tubp->ttyccw.cmd_code = chancmd;
-	tubp->ttyccw.flags = CCW_FLAG_SLI;
-	tubp->ttyccw.cda = virt_to_phys(*tubp->ttyscreen);
-	tubp->ttyccw.count = cp - *tubp->ttyscreen;
-	tty3270_io(tubp);
-	return 1;
-}
-
-static void
-tty3270_tub_bufadr(tub_t *tubp, int adr, char **cpp)
-{
-	if (tubp->tty_14bitadr) {
-		*(*cpp)++ = (adr >> 8) & 0x3f;
-		*(*cpp)++ = adr & 0xff;
-	} else {
-		*(*cpp)++ = tub_ebcgraf[(adr >> 6) & 0x3f];
-		*(*cpp)++ = tub_ebcgraf[adr & 0x3f];
-	}
-}
-
-static void
-tty3270_set_bufadr(tub_t *tubp, char **cpp, int *sba_needed)
-{
-	if (!*sba_needed)
-		return;
-	if (tubp->tty_nextlogx >= GEOM_INPUT) {
-		tubp->tty_nextlogx = GEOM_INPUT - 1;
-		tubp->tty_oucol = tubp->tty_nextlogx % GEOM_COLS;
-	}
-	*(*cpp)++ = TO_SBA;
-	TUB_BUFADR(tubp->tty_nextlogx, cpp);
-	*sba_needed = 0;
-}
diff -puN -L drivers/s390/char/tubtty.c drivers/s390/char/tubtty.c~s390-08-new-3270-driver /dev/null
--- 25/drivers/s390/char/tubtty.c
+++ /dev/null	Thu Apr 11 07:25:15 2002
@@ -1,955 +0,0 @@
-/*
- *  IBM/3270 Driver -- Copyright (C) 2000, 2001 UTS Global LLC
- *
- *  tubtty.c -- Linemode tty driver
- *
- *
- *
- *
- *
- *  Author:  Richard Hitt
- */
-#include <linux/config.h>
-#include "tubio.h"
-
-/* Initialization & uninitialization for tubtty */
-int tty3270_init(void);
-void tty3270_fini(void);
-
-/* Interface routines from the upper tty layer to the tty driver */
-static int tty3270_open(struct tty_struct *, struct file *);
-static void tty3270_close(struct tty_struct *, struct file *);
-static int tty3270_write(struct tty_struct *, int,
-        const unsigned char *, int);
-static void tty3270_put_char(struct tty_struct *, unsigned char);
-static void tty3270_flush_chars(struct tty_struct *);
-static int tty3270_write_room(struct tty_struct *);
-static int tty3270_chars_in_buffer(struct tty_struct *);
-static int tty3270_ioctl(struct tty_struct *, struct file *,
-	unsigned int cmd, unsigned long arg);
-static void tty3270_set_termios(struct tty_struct *, struct termios *);
-static void tty3270_hangup(struct tty_struct *);
-static void tty3270_flush_buffer(struct tty_struct *);
-static int tty3270_read_proc(char *, char **, off_t, int, int *, void *);
-static int tty3270_write_proc(struct file *, const char *,
-	unsigned long, void *);
-
-/* tty3270 utility functions */
-static void tty3270_tasklet(unsigned long);
-       void tty3270_sched_bh(tub_t *);
-static int tty3270_wait(tub_t *, long *);
-       void tty3270_int(tub_t *, devstat_t *);
-static int tty3270_try_logging(tub_t *);
-static void tty3270_start_input(tub_t *);
-static void tty3270_do_input(tub_t *);
-static void tty3270_do_enter(tub_t *, char *, int);
-static void tty3270_do_showi(tub_t *, char *, int);
-       int tty3270_io(tub_t *);
-static int tty3270_show_tube(int, char *, int);
-
-static int tty3270_major = -1;
-struct tty_driver *tty3270_driver;
-
-static int tty3270_proc_index;
-static int tty3270_proc_data;
-static int tty3270_proc_misc;
-static enum tubwhat tty3270_proc_what;
-
-static struct tty_operations tty3270_ops = {
-	.open = tty3270_open,
-	.close = tty3270_close,
-	.write = tty3270_write,
-	.put_char = tty3270_put_char,
-	.flush_chars = tty3270_flush_chars,
-	.write_room = tty3270_write_room,
-	.chars_in_buffer = tty3270_chars_in_buffer,
-#if 0
-	.ioctl = tty3270_ioctl,
-#endif
-	.set_termios = tty3270_set_termios,
-	.hangup = tty3270_hangup,
-	.flush_buffer = tty3270_flush_buffer,
-	.read_proc = tty3270_read_proc,
-	.write_proc = tty3270_write_proc,
-};
-
-/*
- * tty3270_init() -- Register the tty3270 driver
- */
-int
-tty3270_init(void)
-{
-	struct tty_driver *td = alloc_tty_driver(TUBMAXMINS);
-	int rc;
-
-	if (!td)
-		return -ENOMEM;
-
-	/* Initialize for tty driver */
-	td->owner = THIS_MODULE;
-	td->driver_name = "tty3270";
-	td->name = "tty3270";
-	td->major = IBM_TTY3270_MAJOR;
-	td->minor_start = 0;
-	td->type = TTY_DRIVER_TYPE_SYSTEM;
-	td->subtype = SYSTEM_TYPE_TTY;
-	td->init_termios = tty_std_termios;
-	td->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_NO_DEVFS;
-	tty_set_operations(td, &tty3270_ops);
-	rc = tty_register_driver(td);
-	if (rc) {
-		put_tty_driver(td);
-		printk(KERN_ERR "tty3270 registration failed with %d\n", rc);
-	} else {
-		tty3270_major = IBM_TTY3270_MAJOR;
-		if (td->proc_entry != NULL)
-			td->proc_entry->mode = S_IRUGO | S_IWUGO;
-		tty3270_driver = td;
-	}
-	return rc;
-}
-
-/*
- * tty3270_fini() -- Uninitialize linemode tubes
- */
-void
-tty3270_fini(void)
-{
-	if (tty3270_major != -1) {
-		tty_unregister_driver(tty3270_driver);
-		put_tty_driver(tty3270_driver);
-		tty3270_driver = NULL;
-		tty3270_major = -1;
-	}
-}
-
-static int 
-tty3270_open(struct tty_struct *tty, struct file *filp)
-{
-	tub_t *tubp;
-	long flags;
-	int rc;
-	int cmd;
-
-	if ((tubp = TTY2TUB(tty)) == NULL) {
-		return -ENODEV;
-	}
-
-	if ((rc = tty3270_wait(tubp, &flags)) != 0)
-		goto do_fail;
-	if (tubp->lnopen > 0) {
-		tubp->lnopen++;
-		TUBUNLOCK(tubp->irq, flags);
-		return 0;
-	}
-	if (tubp->flags & TUB_OPEN_STET) {
-		cmd = TBC_UPDLOG;
-	} else {
-		cmd = TBC_OPEN;
-		tubp->flags &= ~TUB_SIZED;
-	}
-	if ((rc = tty3270_size(tubp, &flags)) != 0)
-		goto do_fail;
-	if ((rc = tty3270_rcl_init(tubp)) != 0)
-		goto do_fail;
-	if ((rc = tty3270_aid_init(tubp)) != 0)
-		goto do_fail;
-	if ((rc = tty3270_scl_init(tubp)) != 0)
-		goto do_fail;
-	tubp->mode = TBM_LN;
-	tubp->intv = tty3270_int;
-	tubp->tty = tty;
-	tubp->lnopen = 1;
-	tty->driver_data = tubp;
-	tty->winsize.ws_row = tubp->geom_rows - 2;
-	tty->winsize.ws_col = tubp->geom_cols;
-	if (tubp->tty_input == NULL)
-		tubp->tty_input = kmalloc(GEOM_INPLEN, GFP_KERNEL|GFP_DMA);
-	tubp->tty_inattr = TF_INPUT;
-	tubp->cmd = cmd;
-	tty3270_build(tubp);
-	TUBUNLOCK(tubp->irq, flags);
-	return 0;
-
-do_fail:
-	tty3270_scl_fini(tubp);
-	tty3270_aid_fini(tubp);
-	tty3270_rcl_fini(tubp);
-	TUBUNLOCK(tubp->irq, flags);
-	return rc;
-}
-
-static void
-tty3270_close(struct tty_struct *tty, struct file *filp)
-{
-	tub_t *tubp;
-	long flags;
-
-	if ((tubp = tty->driver_data) == NULL)
-		return;
-
-	tty3270_wait(tubp, &flags);
-	if (--tubp->lnopen > 0)
-		goto do_return;
-	tubp->tty = NULL;
-	tty->driver_data = NULL;
-	tty3270_aid_fini(tubp);
-	tty3270_rcl_fini(tubp);
-	tty3270_scl_fini(tubp);
-do_return:
-	TUBUNLOCK(tubp->irq, flags);
-}
-
-static int 
-tty3270_write(struct tty_struct *tty, int fromuser,
-		const unsigned char *buf, int count)
-{
-	tub_t *tubp;
-	long flags;
-	bcb_t obcb;
-	int rc = 0;
-
-	if ((tubp = tty->driver_data) == NULL)
-		return -1;
-
-#ifdef CONFIG_TN3270_CONSOLE
-	if (CONSOLE_IS_3270 && tub3270_con_tubp == tubp)
-		tub3270_con_copy(tubp);
-#endif /* CONFIG_TN3270_CONSOLE */
-
-	obcb.bc_buf = (char *)buf;
-	obcb.bc_len = obcb.bc_cnt = obcb.bc_wr = count;
-	obcb.bc_rd = 0;
-
-	TUBLOCK(tubp->irq, flags);
-	rc = tub3270_movedata(&obcb, &tubp->tty_bcb, fromuser);
-	tty3270_try_logging(tubp);
-	TUBUNLOCK(tubp->irq, flags);
-	return rc;
-} 
-
-static void
-tty3270_put_char(struct tty_struct *tty, unsigned char ch)
-{
-	long flags;
-	tub_t *tubp;
-	bcb_t *ob;
-
-	if ((tubp = tty->driver_data) == NULL)
-		return;
-
-	TUBLOCK(tubp->irq, flags);
-	ob = &tubp->tty_bcb;
-	if (ob->bc_cnt < ob->bc_len) {
-		ob->bc_buf[ob->bc_wr++] = ch;
-		if (ob->bc_wr == ob->bc_len)
-			ob->bc_wr = 0;
-		ob->bc_cnt++;
-	}
-	tty3270_try_logging(tubp);
-	TUBUNLOCK(tubp->irq, flags);
-}
-
-static void
-tty3270_flush_chars(struct tty_struct *tty)
-{
-	tub_t *tubp;
-	long flags;
-
-	if ((tubp = tty->driver_data) == NULL)
-		return;
-
-	TUBLOCK(tubp->irq, flags);
-	tty3270_try_logging(tubp);
-	TUBUNLOCK(tubp->irq, flags);
-}
-
-static int 
-tty3270_write_room(struct tty_struct *tty)
-{
-	tub_t *tubp;
-	bcb_t *ob;
-
-	if ((tubp = tty->driver_data) == NULL)
-		return -1;
-
-	ob = &tubp->tty_bcb;
-	return ob->bc_len - ob->bc_cnt;
-}
-
-static int
-tty3270_chars_in_buffer(struct tty_struct *tty)
-{
-	tub_t *tubp;
-	bcb_t *ob;
-
-	if ((tubp = tty->driver_data) == NULL)
-		return -1;
-
-	ob = &tubp->tty_bcb;
-	return ob->bc_cnt;
-}
-
-static int
-tty3270_ioctl(struct tty_struct *tty, struct file *file,
-		unsigned int cmd, unsigned long arg)
-{
-	tub_t *tubp;
-	long flags;
-	int ret = 0;
-	struct termios termios;
-
-	if ((tubp = tty->driver_data) == NULL)
-		return -ENODEV;
-
-	TUBLOCK(tubp->irq, flags);
-	if (tty->flags * (1 << TTY_IO_ERROR)) {
-		ret = -EIO;
-		goto do_return;
-	}
-	switch(cmd) {
-	case TCGETS:
-		ret = -ENOIOCTLCMD;
-		goto do_return;
-	case TCFLSH:            /* arg:  2 or 0 */
-		ret = -ENOIOCTLCMD;
-		goto do_return;
-	case TCSETSF:
-		if (user_termios_to_kernel_termios(&termios,
-		    (struct termios *)arg)) {
-			ret = -EFAULT;
-			goto do_return;
-		}
-		ret = -ENOIOCTLCMD;
-		goto do_return;
-	case TCGETA:
-		ret = -ENOIOCTLCMD;
-		goto do_return;
-	case TCSETA:
-		if (user_termio_to_kernel_termios(&termios,
-		    (struct termio *)arg)) {
-			ret = -EFAULT;
-			goto do_return;
-		}
-		ret = -ENOIOCTLCMD;
-		goto do_return;
-	default:
-		ret = -ENOIOCTLCMD;
-		break;
-	}
-
-do_return:
-	TUBUNLOCK(tubp->irq, flags);
-	return ret;
-}
-
-static void
-tty3270_set_termios(struct tty_struct *tty, struct termios *old)
-{
-	tub_t *tubp;
-	long flags;
-	int new;
-
-	if ((tubp = tty->driver_data) == NULL)
-		return;
-
-	if (tty3270_wait(tubp, &flags) != 0) {
-		TUBUNLOCK(tubp->irq, flags);
-		return;
-	}
-	new = L_ICANON(tty)? L_ECHO(tty)? TF_INPUT: TF_INPUTN:
-		tubp->tty_inattr;
-	if (new != tubp->tty_inattr) {
-		tubp->tty_inattr = new;
-		tubp->cmd = TBC_CLRINPUT;
-		tty3270_build(tubp);
-	}
-
-	TUBUNLOCK(tubp->irq, flags);
-}
-
-static void
-tty3270_flush_buffer(struct tty_struct *tty)
-{
-	tub_t *tubp;
-	bcb_t *ob;
-	long flags;
-
-	if ((tubp = tty->driver_data) == NULL)
-		return;
-
-	if (tubp->mode == TBM_FS && tubp->fs_pid != 0) {
-		kill_proc(tubp->fs_pid, SIGHUP, 1);
-	}
-
-	if ((tubp->flags & TUB_OPEN_STET) == 0) {
-		ob = &tubp->tty_bcb;
-		TUBLOCK(tubp->irq, flags);
-		ob->bc_rd = 0;
-		ob->bc_wr = 0;
-		ob->bc_cnt = 0;
-		TUBUNLOCK(tubp->irq, flags);
-	}
-	wake_up_interruptible(&tty->write_wait);
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-	    tty->ldisc.write_wakeup)
-		(tty->ldisc.write_wakeup)(tty);
-}
-
-static int
-tty3270_read_proc(char *buf, char **start, off_t off, int count,
-		int *eof, void *data)
-{
-	tub_t *tubp;
-	int begin = 0;
-	int i;
-	int rc;
-	int len = 0;
-
-	if (tty3270_proc_what == TW_CONFIG) {
-		/*
-		 * Describe the 3270 configuration in ascii lines.
-		 * Line 1:		0 <fsmajor> 0
-		 * Console line:	<devnum> CONSOLE <minor>
-		 * Other lines:		<devnum> <ttymajor> <minor>
-		 */
-		len += sprintf(buf + len, "0 %d 0\n", fs3270_major);
-		for (i = 1; i <= tubnummins; i++) {
-			tubp = (*tubminors)[i];
-#ifdef CONFIG_TN3270_CONSOLE
-			if (CONSOLE_IS_3270 && tubp == tub3270_con_tubp)
-				len += sprintf(buf + len, "%.4x CONSOLE %d\n",
-					       tubp->devno, i);
-			else
-#endif
-				len += sprintf(buf + len, "%.4x %d %d\n",
-					       tubp->devno, tty3270_major, i);
-			if (begin + len > off + count)
-				break;
-			if (begin + len < off) {
-				begin += len;
-				len = 0;
-			}
-		}
-		if (i > tubnummins)
-			*eof = 1;
-		if (off >= begin + len) {
-			rc = 0;
-		} else {
-			*start = buf + off - begin;
-			rc = MIN(count, begin + len - off);
-		}
-		if (*eof && rc == 0)
-			tty3270_proc_what = TW_BOGUS;
-		return rc;
-	}
-
-	len += sprintf(buf, "There are %d devices.  fs major is %d, "
-		"tty major is %d.\n", tubnummins, fs3270_major,
-		tty3270_major);
-	len += sprintf(buf+len, "        index=%d data=%d misc=%d\n",
-		tty3270_proc_index,
-		tty3270_proc_data,
-		tty3270_proc_misc);
-
-	/*
-	 * Display info for the tube with minor nr in index
-	 */
-	len += tty3270_show_tube(tty3270_proc_index, buf+len, count-len);
-
-	*eof = 1;
-	if (off >= begin + len)
-		return 0;
-	*start = buf + off - begin;
-	return MIN(count, begin + len - off);
-}
-
-static int
-tty3270_write_proc(struct file *file, const char *buffer,
-		unsigned long count, void *data)
-{
-	char mybuf[GEOM_MAXINPLEN];
-	int mycount;
-	tub_t *tubp;
-	struct tty_struct *tty;
-	int rc;
-
-	mycount = MIN(count, sizeof mybuf - 1);
-	if (copy_from_user(mybuf, buffer, mycount) != 0)
-		return -EFAULT;
-	mybuf[mycount] = '\0';
-
-	/*
-	 * User-mode settings affect only the current tty ---
-	 */
-	tubp = NULL;
-	tty = current->tty;
-	if (tty && tty->driver == tty3270_driver)
-		tubp = (*tubminors)[tty->index];
-	if (tubp) {
-		if ((rc = tty3270_aid_set(tubp, mybuf, mycount + 1)))
-			return rc > 0? count: rc;
-		if ((rc = tty3270_rcl_set(tubp, mybuf, mycount + 1)))
-			return rc > 0? count: rc;
-		if ((rc = tty3270_scl_set(tubp, mybuf, mycount + 1)))
-			return rc > 0? count: rc;
-	}
-
-	/*
-	 * Superuser-mode settings affect the driver overall ---
-	 */
-	if (!capable(CAP_SYS_TTY_CONFIG)) {
-		return -EPERM;
-	} else if (strncmp(mybuf, "index=", 6) == 0) {
-		tty3270_proc_index = simple_strtoul(mybuf + 6, 0,0);
-		return count;
-	} else if (strncmp(mybuf, "data=", 5) == 0) {
-		tty3270_proc_data = simple_strtoul(mybuf + 5, 0, 0);
-		return count;
-	} else if (strncmp(mybuf, "misc=", 5) == 0) {
-		tty3270_proc_misc = simple_strtoul(mybuf + 5, 0, 0);
-		return count;
-	} else if (strncmp(mybuf, "what=", 5) == 0) {
-		if (strcmp(mybuf+5, "bogus") == 0)
-			tty3270_proc_what = 0;
-		else if (strncmp(mybuf+5, "config", 6) == 0)
-			tty3270_proc_what = TW_CONFIG;
-		return count;
-	} else {
-		return -EINVAL;
-	}
-}
-
-static void
-tty3270_hangup(struct tty_struct *tty)
-{
-	tub_t *tubp;
-	extern void fs3270_release(tub_t *);
-
-	if ((tubp = tty->driver_data) == NULL)
-		return;
-	tty3270_rcl_purge(tubp);
-	tty3270_aid_reinit(tubp);
-	fs3270_release(tubp);
-}
-
-
-/*
- * tty3270_tasklet(tubp) -- Perform back-half processing
- */
-static void
-tty3270_tasklet(unsigned long data)
-{
-	tub_t *tubp;
-	ioinfo_t *ioinfop;
-	long flags;
-	struct tty_struct *tty;
-
-	tubp = (tub_t *) data;
-	ioinfop = ioinfo[tubp->irq];
-	while (TUBTRYLOCK(tubp->irq, flags) == 0) {
-		if (ioinfop->ui.flags.unready == 1)
-			return;
-	}
-	if (ioinfop->ui.flags.unready == 1 ||
-	    ioinfop->ui.flags.ready == 0)
-		goto do_unlock;
-
-	tubp->flags &= ~TUB_BHPENDING;
-	tty = tubp->tty;
-
-	if (tubp->flags & TUB_UNSOL_DE) {
-		tubp->flags &= ~TUB_UNSOL_DE;
-		if (tty != NULL) {
-			tty_hangup(tty);
-			wake_up_interruptible(&tubp->waitq);
-			goto do_unlock;
-		}
-	}
-
-	if (tubp->flags & TUB_IACTIVE) {        /* If read ended, */
-		tty3270_do_input(tubp);
-		tubp->flags &= ~TUB_IACTIVE;
-	}
-
-	if ((tubp->flags & TUB_WORKING) == 0) {
-		if (tubp->flags & TUB_ATTN) {
-			tty3270_start_input(tubp);
-			tubp->flags &= ~TUB_ATTN;
-		} else if (tty3270_try_logging(tubp) == 0) {
-			wake_up_interruptible(&tubp->waitq);
-		}
-	}
-
-	if (tty != NULL) {
-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-		    tty->ldisc.write_wakeup != NULL)
-			(tty->ldisc.write_wakeup)(tty);
-		wake_up_interruptible(&tty->write_wait);
-	}
-do_unlock:
-	TUBUNLOCK(tubp->irq, flags);
-}
-
-/*
- * tty3270_sched_bh(tubp) -- Schedule the back half
- * Irq lock must be held on entry and remains held on exit.
- */
-void
-tty3270_sched_bh(tub_t *tubp)
-{
-	if (tubp->flags & TUB_BHPENDING)
-		return;
-	tubp->flags |= TUB_BHPENDING;
-	tasklet_init(&tubp->tasklet, tty3270_tasklet,
-		     (unsigned long) tubp);
-	tasklet_schedule(&tubp->tasklet);
-}
-
-/*
- * tty3270_io() -- Perform line-mode reads and writes here
- */
-int 
-tty3270_io(tub_t *tubp)
-{
-	int rc;
-	ccw1_t *ccwp;
-
-	tubp->flags |= TUB_WORKING;
-	tubp->dstat = 0;
-	ccwp = &tubp->ttyccw;
-
-	rc = do_IO(tubp->irq, ccwp, tubp->irq, 0, 0);
-	return rc;
-}
-
-/*
- * tty3270_wait(tubp) -- Wait until TUB_WORKING is off
- * On entry the lock must not be held; on exit it is held.
- */
-static int
-tty3270_wait(tub_t *tubp, long *flags)
-{
-	DECLARE_WAITQUEUE(wait, current);
-
-	TUBLOCK(tubp->irq, *flags);
-	add_wait_queue(&tubp->waitq, &wait);
-	while (!signal_pending(current) &&
-	    (tubp->flags & TUB_WORKING) != 0) {
-#warning FIXME: [kj] use set_current_state instead of current->state=
-		current->state = TASK_INTERRUPTIBLE;
-		TUBUNLOCK(tubp->irq, *flags);
-		schedule();
-#warning FIXME: [kj] use set_current_state instead of current->state=
-		current->state = TASK_RUNNING;
-		TUBLOCK(tubp->irq, *flags);
-	}
-	remove_wait_queue(&tubp->waitq, &wait);
-	return signal_pending(current)? -ERESTARTSYS: 0;
-}
-
-void
-tty3270_int(tub_t *tubp, devstat_t *dsp)
-{
-#define	DEV_UE_BUSY \
-	(DEV_STAT_CHN_END | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP)
-#define DEV_NOT_WORKING \
-	(DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_CHECK)
-
-	tubp->dstat = dsp->dstat;
-
-	/* Handle CE-DE-UE and subsequent UDE */
-	if (dsp->dstat == DEV_UE_BUSY) {
-		tubp->flags |= TUB_UE_BUSY;
-		return;
-	} else if (tubp->flags & TUB_UE_BUSY) {
-		tubp->flags &= ~TUB_UE_BUSY;
-		if (dsp->dstat == DEV_STAT_DEV_END &&
-		    (tubp->flags & TUB_WORKING) != 0) {
-			tty3270_io(tubp);
-			return;
-		}
-	}
-
-	/* Handle ATTN */
-	if (dsp->dstat & DEV_STAT_ATTENTION)
-		tubp->flags |= TUB_ATTN;
-
-	if (dsp->dstat & DEV_STAT_CHN_END) {
-		tubp->cswl = dsp->rescnt;
-		if ((dsp->dstat & DEV_STAT_DEV_END) == 0)
-			tubp->flags |= TUB_EXPECT_DE;
-		else
-			tubp->flags &= ~TUB_EXPECT_DE;
-	} else if (dsp->dstat & DEV_STAT_DEV_END) {
-		if ((tubp->flags & TUB_EXPECT_DE) == 0)
-			tubp->flags |= TUB_UNSOL_DE;
-		tubp->flags &= ~TUB_EXPECT_DE;
-	}
-	if (dsp->dstat & DEV_NOT_WORKING)
-		tubp->flags &= ~TUB_WORKING;
-	if (dsp->dstat & DEV_STAT_UNIT_CHECK)
-		tubp->sense = dsp->ii.sense;
-	if ((tubp->flags & TUB_WORKING) == 0)
-		tty3270_sched_bh(tubp);
-}
-
-/*
- * tty3270_refresh(), called by fs3270_close() when tubp->fsopen == 0.
- * On entry, lock is held.
- */
-void
-tty3270_refresh(tub_t *tubp)
-{
-	if (tubp->lnopen) {
-		tubp->mode = TBM_LN;
-		tubp->intv = tty3270_int;
-		tty3270_scl_resettimer(tubp);
-		tubp->cmd = TBC_UPDATE;
-		tty3270_build(tubp);
-	}
-}
-
-static int
-tty3270_try_logging(tub_t *tubp)
-{
-	if (tubp->flags & TUB_WORKING)
-		return 0;
-	if (tubp->mode == TBM_FS)
-		return 0;
-	if (tubp->stat == TBS_HOLD)
-		return 0;
-	if (tubp->stat == TBS_MORE)
-		return 0;
-#ifdef CONFIG_TN3270_CONSOLE
-	if (CONSOLE_IS_3270 && tub3270_con_tubp == tubp)
-		tub3270_con_copy(tubp);
-#endif /* CONFIG_TN3270_CONSOLE */
-	if (tubp->tty_bcb.bc_cnt == 0)
-		return 0;
-	if (tubp->intv != tty3270_int)
-		return 0;
-	tubp->cmd = TBC_UPDLOG;
-	return tty3270_build(tubp);
-}
-
-/* tty3270 utility functions */
-
-static void
-tty3270_start_input(tub_t *tubp)
-{
-	if (tubp->tty_input == NULL)
-		return;
-	tubp->ttyccw.cda = virt_to_phys(tubp->tty_input);
-	tubp->ttyccw.cmd_code = TC_READMOD;
-	tubp->ttyccw.count = GEOM_INPLEN;
-	tubp->ttyccw.flags = CCW_FLAG_SLI;
-	tty3270_io(tubp);
-	tubp->flags |= TUB_IACTIVE;
-}
-
-static void
-tty3270_do_input(tub_t *tubp)
-{
-	int count;
-	char *in;
-	int aidflags;
-	char *aidstring;
-
-	count = GEOM_INPLEN - tubp->cswl;
-	if ((in = tubp->tty_input) == NULL)
-		goto do_build;
-	tty3270_aid_get(tubp, in[0], &aidflags, &aidstring);
-
-	if (aidflags & TA_CLEARKEY) {
-		tubp->stat = TBS_RUNNING;
-		tty3270_scl_resettimer(tubp);
-		tubp->cmd = TBC_UPDATE;
-	} else if (aidflags & TA_CLEARLOG) {
-		tubp->stat = TBS_RUNNING;
-		tty3270_scl_resettimer(tubp);
-		tubp->cmd = TBC_CLRUPDLOG;
-	} else if (aidflags & TA_DOENTER) {
-		if (count <= 6) {
-			switch(tubp->stat) {
-			case TBS_MORE:
-				tubp->stat = TBS_HOLD;
-				tty3270_scl_resettimer(tubp);
-				break;
-			case TBS_HOLD:
-				tubp->stat = TBS_MORE;
-				tty3270_scl_settimer(tubp);
-				break;
-			case TBS_RUNNING:
-                                tty3270_do_enter(tubp, in + 6, 0);
-				break;
-			}
-			tubp->cmd = TBC_UPDSTAT;
-			goto do_build;
-		}
-		in += 6;
-		count -= 6;
-		TUB_EBCASC(in, count);
-		tubp->cmd = TBC_CLRINPUT;
-		tty3270_do_enter(tubp, in, count);
-	} else if ((aidflags & TA_DOSTRING) != 0 && aidstring != NULL) {
-		tubp->cmd = TBC_KRUPDLOG;
-		tty3270_do_enter(tubp, aidstring, strlen(aidstring));
-	} else if ((aidflags & TA_DOSTRINGD) != 0 && aidstring != NULL) {
-		tty3270_do_showi(tubp, aidstring, strlen(aidstring));
-		tubp->cmd = TBC_UPDINPUT;
-	} else {
-		if (in[0] != 0x60)
-			tubp->flags |= TUB_ALARM;
-		tubp->cmd = TBC_KRUPDLOG;
-	}
-do_build:
-	tty3270_build(tubp);
-}
-
-static void
-tty3270_do_enter(tub_t *tubp, char *cp, int count)
-{
-	struct tty_struct *tty;
-	int func = -1;
-
-	if ((tty = tubp->tty) == NULL)
-		return;
-	if (count < 0)
-		return;
-	if (count == 2 && (cp[0] == '^' || cp[0] == '\252')) {
-		switch(cp[1]) {
-		case 'c':  case 'C':
-			func = INTR_CHAR(tty);
-			break;
-		case 'd':  case 'D':
-			func = EOF_CHAR(tty);
-			break;
-		case 'z':  case 'Z':
-			func = SUSP_CHAR(tty);
-			break;
-		}
-	} else if (count == 2 && cp[0] == 0x1b) {        /* if ESC */
-		int inc = 0;
-		char buf[GEOM_INPLEN + 1];
-		int len;
-
-		switch(cp[1]) {
-		case 'k':  case 'K':
-			inc = -1;
-			break;
-		case 'j':  case 'J':
-			inc = 1;
-			break;
-		}
-		if (inc == 0)
-			goto not_rcl;
-		len = tty3270_rcl_get(tubp, buf, sizeof buf, inc);
-		if (len == 0) {
-			tubp->flags |= TUB_ALARM;
-			return;
-		}
-		tty3270_do_showi(tubp, buf, len);
-		tubp->cmd = TBC_UPDINPUT;
-		return;
-	}
-not_rcl:
-	if (func != -1) {
-		*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
-		*tty->flip.char_buf_ptr++ = func;
-		tty->flip.count++;
-	} else {
-		tty3270_rcl_put(tubp, cp, count);
-		memcpy(tty->flip.char_buf_ptr, cp, count);
-		/* Add newline unless line ends with "^n" */
-		if (count < 2 || cp[count - 1] != 'n' ||
-		    (cp[count - 2] != '^' && cp[count - 2] != '\252')) {
-			tty->flip.char_buf_ptr[count] = '\n';
-			count++;
-		} else {
-			count -= 2;     /* Lop trailing "^n" from text */
-		}
-		memset(tty->flip.flag_buf_ptr, TTY_NORMAL, count);
-		tty->flip.char_buf_ptr += count;
-		tty->flip.flag_buf_ptr += count;
-		tty->flip.count += count;
-	}
-	tty_flip_buffer_push(tty);
-}
-
-static void
-tty3270_do_showi(tub_t *tubp, char *cp, int cl)
-{
-	if (cl > GEOM_INPLEN)
-		cl = GEOM_INPLEN;
-	memset(tubp->tty_input, 0, GEOM_INPLEN);
-	memcpy(tubp->tty_input, cp, cl);
-	TUB_ASCEBC(tubp->tty_input, cl);
-}
-
-
-
-/* Debugging routine */
-static int
-tty3270_show_tube(int minor, char *buf, int count)
-{
-	tub_t *tubp;
-	struct tty_struct *tty;
-	struct termios *mp;
-	int len;
-
-/*012345678901234567890123456789012345678901234567890123456789       */
-/*Info for tub_t[dd] at xxxxxxxx:                                    */
-/*    geom:  rows=dd cols=dd model=d                                 */
-/*    lnopen=dd     fsopen=dd   waitq=xxxxxxxx                       */
-/*    dstat=xx      mode=dd     stat=dd     flags=xxxx               */
-/*    oucount=dddd  ourd=ddddd  ouwr=ddddd  nextlogx=ddddd           */
-/*    tty=xxxxxxxx                                                   */
-/*    write_wait=xxxxxxxx read_wait=xxxxxxxx                         */
-/*    iflag=xxxxxxxx oflag=xxxxxxxx cflag=xxxxxxxx lflag=xxxxxxxx    */
-
-	if (minor < 0 || minor > tubnummins ||
-	    (tubp = (*tubminors)[minor]) == NULL)
-		return sprintf(buf, "No tube at index=%d\n", minor);
-	
-	tty = tubp->tty;
-	len = 0;
-
-	len += sprintf(buf+len, "Info for tub_t[%d] at %p:\n", minor, tubp);
-
-	len += sprintf(buf+len, "inattr is at %p\n", &tubp->tty_inattr);
-
-
-	len += sprintf(buf+len, "    geom:  rows=%.2d cols=%.2d model=%.1d\n",
-		       tubp->geom_rows, tubp->geom_cols, tubp->tubiocb.model);
-
-	len += sprintf(buf+len,
-		       "    lnopen=%-2d     fsopen=%-2d   waitq=%p\n",
-		       tubp->lnopen, tubp->fsopen, &tubp->waitq);
-
-	len += sprintf(buf+len, "    dstat=%.2x      mode=%-2d     "
-		       "stat=%-2d     flags=%-4x\n", tubp->dstat,
-		       tubp->mode, tubp->stat, tubp->flags);
-
-#ifdef RBH_FIXTHIS
-	len += sprintf(buf+len,
-		       "    oucount=%-4d  ourd=%-5d  ouwr=%-5d"
-		       "  nextlogx=%-5d\n", tubp->tty_oucount,
-		       tubp->tty_ourd, tubp->tty_ouwr, tubp->tty_nextlogx);
-#endif
-
-	len += sprintf(buf+len, "    tty=%p\n",tubp->tty);
-
-	if (tty)
-		len += sprintf(buf+len,
-				"    write_wait=%p read_wait=%p\n",
-				&tty->write_wait, &tty->read_wait);
-
-	if (tty && ((mp = tty->termios)))
-		len += sprintf(buf+len,"    iflag=%.8x oflag=%.8x "
-			       "cflag=%.8x lflag=%.8x\n", mp->c_iflag,
-			       mp->c_oflag, mp->c_cflag, mp->c_lflag);
-
-
-	return len;
-}
diff -puN -L drivers/s390/char/tubttyrcl.c drivers/s390/char/tubttyrcl.c~s390-08-new-3270-driver /dev/null
--- 25/drivers/s390/char/tubttyrcl.c
+++ /dev/null	Thu Apr 11 07:25:15 2002
@@ -1,202 +0,0 @@
-/*
- *  IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC
- *
- *  tubttyrcl.c -- Linemode Command-recall functionality
- *
- *
- *
- *
- *
- *  Author:  Richard Hitt
- */
-#include "tubio.h"
-
-static void tty3270_rcl_sync(tub_t *);
-static int tty3270_rcl_resize(tub_t *, int);
-
-int
-tty3270_rcl_init(tub_t *tubp)
-{
-	return tty3270_rcl_resize(tubp, 20);
-}
-
-static int
-tty3270_rcl_resize(tub_t *tubp, int newrclk)
-{
-	char *(*newrclb)[];
-
-	if (newrclk > 1000)
-		return -EINVAL;
-	if (newrclk <= 0) {
-		tty3270_rcl_purge(tubp),
-		kfree(tubp->tty_rclbufs);
-		tubp->tty_rclbufs = NULL;
-		return 0;
-	}
-	if ((newrclb = (char *(*)[])kmalloc(
-	    newrclk * sizeof (char *), GFP_KERNEL)) == NULL)
-		return -ENOMEM;
-	memset(newrclb, 0, newrclk * sizeof (char *));
-	if (tubp->tty_rclbufs != NULL) {
-		int i, j, k;
-		char *data;
-
-		i = tubp->tty_rclp;
-		j = newrclk;
-		k = tubp->tty_rclk;
-		while (j-- && k--) {
-			if ((data = (*tubp->tty_rclbufs)[i]) == NULL)
-				break;
-			(*newrclb)[j] = data;
-			(*tubp->tty_rclbufs)[i] = NULL;
-			if (--i < 0)
-				i = tubp->tty_rclk - 1;
-		}
-		tty3270_rcl_purge(tubp);
-		kfree(tubp->tty_rclbufs);
-	}
-	tubp->tty_rclbufs = newrclb;
-	tubp->tty_rclk = newrclk;
-	tubp->tty_rclp = newrclk - 1;
-	tty3270_rcl_sync(tubp);
-	return 0;
-}
-
-int
-tty3270_rcl_set(tub_t *tubp, char *buf, int count)
-{
-#define RCL_SIZ "recallsize="
-#define L_RCL_SIZ (strlen(RCL_SIZ))
-	int newsize;
-	int len;
-	int rc;
-	char *rcl_siz = RCL_SIZ;
-	int l_rcl_siz = L_RCL_SIZ;
-
-	if (count < l_rcl_siz || strncmp(buf, rcl_siz, l_rcl_siz) != 0)
-		return 0;
-	if ((len = count - l_rcl_siz) == 0)
-		return count;
-	newsize = simple_strtoul(buf + l_rcl_siz, 0, 0);
-	rc = tty3270_rcl_resize(tubp, newsize);
-	return rc < 0? rc: count;
-}
-
-void
-tty3270_rcl_fini(tub_t *tubp)
-{
-	if (tubp->tty_rclbufs != NULL) {
-		tty3270_rcl_purge(tubp);
-		kfree(tubp->tty_rclbufs);
-		tubp->tty_rclbufs = NULL;
-	}
-}
-
-void
-tty3270_rcl_purge(tub_t *tubp)
-{
-	int i;
-	char *buf;
-
-	if (tubp->tty_rclbufs == NULL)
-		return;
-	for (i = 0; i < tubp->tty_rclk; i++) {
-		if ((buf = (*tubp->tty_rclbufs)[i]) == NULL)
-			continue;
-		kfree(buf);
-		(*tubp->tty_rclbufs)[i] = NULL;
-	}
-}
-
-int
-tty3270_rcl_get(tub_t *tubp, char *buf, int len, int inc)
-{
-	int iter;
-	int i;
-	char *data;
-
-	if (tubp->tty_rclbufs == NULL)
-		return 0;
-	if (tubp->tty_rclk <= 0)	/* overcautious */
-		return 0;
-	if (inc != 1 && inc != -1)	/* overcautious */
-		return 0;
-
-	if ((i = tubp->tty_rclb) == -1) {
-		i = tubp->tty_rclp;
-		if (inc == 1)
-			i++;
-	} else {
-		i += inc;
-	}
-	for (iter = tubp->tty_rclk; iter; iter--, i += inc) {
-		if (i < 0)
-			i = tubp->tty_rclk - 1;
-		else if (i >= tubp->tty_rclk)
-			i = 0;
-		if ((*tubp->tty_rclbufs)[i] != NULL)
-			break;
-	}
-	if (iter < 0 || (data = (*tubp->tty_rclbufs)[i]) == NULL)
-		return 0;
-	tubp->tty_rclb = i;
-	if ((len = MIN(len - 1, strlen(data))) <= 0)
-		return 0;
-	memcpy(buf, data, len);
-	buf[len] = '\0';
-	return len;
-}
-
-void
-tty3270_rcl_put(tub_t *tubp, char *data, int len)
-{
-	char *buf, **bufp;
-	int i;
-
-	if (tubp->tty_rclbufs == NULL)
-		return;
-
-	if (tubp->tty_rclk <= 0)        /* overcautious */
-		return;
-
-	/* If input area is invisible, don't log */
-	if (tubp->tty_inattr == TF_INPUTN)
-		return;
-
-	/* If this & most recent cmd text match, don't log */
-	if ((buf = (*tubp->tty_rclbufs)[tubp->tty_rclp]) != NULL &&
-	    strlen(buf) == len && memcmp(buf, data, len) == 0) {
-		tty3270_rcl_sync(tubp);
-		return;
-	}
-
-	/* Don't stack zero-length commands */
-	if (len == 0) {
-		tty3270_rcl_sync(tubp);
-		return;
-	}
-
-	i = tubp->tty_rclp;
-	if (++i == tubp->tty_rclk)
-		i = 0;
-	bufp = &(*tubp->tty_rclbufs)[i];
-	if (*bufp == NULL || strlen(*bufp) < len + 1) {
-		if (*bufp) {
-			kfree(*bufp);
-			*bufp = NULL;
-		}
-		if ((*bufp = kmalloc(len + 1, GFP_ATOMIC)) == NULL)
-			return;
-	}
-	memcpy(*bufp, data, len);
-	(*bufp)[len] = '\0';
-	tubp->tty_rclp = i;
-	tty3270_rcl_sync(tubp);
-}
-
-static void
-tty3270_rcl_sync(tub_t *tubp)
-{
-	tubp->tty_rclb = -1;
-}
-
diff -puN -L drivers/s390/char/tubttyscl.c drivers/s390/char/tubttyscl.c~s390-08-new-3270-driver /dev/null
--- 25/drivers/s390/char/tubttyscl.c
+++ /dev/null	Thu Apr 11 07:25:15 2002
@@ -1,88 +0,0 @@
-/*
- *  IBM/3270 Driver -- Copyright (C) 2000, 2001 UTS Global LLC
- *
- *  tubttyscl.c -- Linemode tty driver scroll-timing functions
- *
- *
- *
- *
- *
- *  Author:  Richard Hitt
- */
-#include "tubio.h"
-       void tty3270_scl_settimer(tub_t *);
-       void tty3270_scl_resettimer(tub_t *);
-static void tty3270_scl_timeout(unsigned long);
-
-void
-tty3270_scl_settimer(tub_t *tubp)
-{
-	struct timer_list *tp = &tubp->tty_stimer;
-
-	if (tubp->flags & TUB_SCROLLTIMING)
-		return;
-	if (tubp->tty_scrolltime == 0)
-		return;
-
-	init_timer(tp);
-	tp->expires = jiffies + HZ * tubp->tty_scrolltime;
-	tp->data = (unsigned long)tubp;
-	tp->function = tty3270_scl_timeout;
-	add_timer(tp);
-	tubp->flags |= TUB_SCROLLTIMING;
-}
-
-void
-tty3270_scl_resettimer(tub_t *tubp)
-{
-	struct timer_list *tp = &tubp->tty_stimer;
-
-	if ((tubp->flags & TUB_SCROLLTIMING) == 0)
-		return;
-
-	del_timer(tp);
-	tubp->flags &= ~TUB_SCROLLTIMING;
-}
-
-static void
-tty3270_scl_timeout(unsigned long data)
-{
-	tub_t *tubp = (void *)data;
-	long flags;
-
-	TUBLOCK(tubp->irq, flags);
-	tubp->stat = TBS_RUNNING;
-	tty3270_scl_resettimer(tubp);
-	tubp->cmd = TBC_CLRUPDLOG;
-	tty3270_build(tubp);
-	TUBUNLOCK(tubp->irq, flags);
-}
-
-int
-tty3270_scl_set(tub_t *tubp, char *buf, int count)
-{
-	if (strncmp(buf, "scrolltime=", 11) == 0) {
-		tubp->tty_scrolltime =
-			simple_strtoul(buf + 11, 0, 0);
-		return count;
-	}
-	return 0;
-}
-
-int
-tty3270_scl_init(tub_t *tubp)
-{
-	extern int tubscrolltime;
-
-	tubp->tty_scrolltime = tubscrolltime;
-	if (tubp->tty_scrolltime < 0)
-		tubp->tty_scrolltime = DEFAULT_SCROLLTIME;
-	return 0;
-}
-
-void
-tty3270_scl_fini(tub_t *tubp)
-{
-	if ((tubp->flags & TUB_OPEN_STET) == 0)
-		tty3270_scl_resettimer(tubp);
-}
diff -puN -L drivers/s390/char/tubttysiz.c drivers/s390/char/tubttysiz.c~s390-08-new-3270-driver /dev/null
--- 25/drivers/s390/char/tubttysiz.c
+++ /dev/null	Thu Apr 11 07:25:15 2002
@@ -1,306 +0,0 @@
-/*
- *  IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC
- *
- *  tubttysiz.c -- Linemode screen-size determiner
- *
- *
- *
- *
- *
- *  Author:  Richard Hitt
- */
-#include "tubio.h"
-static int tty3270_size_io(tub_t *tubp);
-static void tty3270_size_int(tub_t *tubp, devstat_t *dsp);
-static int tty3270_size_wait(tub_t *tubp, long *flags, int stat);
-
-/*
- * Structure representing Usable Area Query Reply Base
- */
-typedef struct {
-	short l;                /* Length of this structured field */
-	char sfid;              /* 0x81 if Query Reply */
-	char qcode;             /* 0x81 if Usable Area */
-#define QCODE_UA 0x81
-	char flags0;
-#define FLAGS0_ADDR 0x0f
-#define FLAGS0_ADDR_12_14       1       /* 12/14-bit adrs ok */
-#define FLAGS0_ADDR_12_14_16    3       /* 12/14/16-bit adrs ok */
-	char flags1;
-	short w;                /* Width of usable area */
-	short h;                /* Heigth of usavle area */
-	char units;             /* 0x00:in; 0x01:mm */
-	int xr;
-	int yr;
-	char aw;
-	char ah;
-	short buffsz;           /* Character buffer size, bytes */
-	char xmin;
-	char ymin;
-	char xmax;
-	char ymax;
-} __attribute__ ((packed)) uab_t;
-
-/*
- * Structure representing Alternate Usable Area Self-Defining Parameter
- */
-typedef struct {
-	char l;                 /* Length of this Self-Defining Parm */
-	char sdpid;             /* 0x02 if Alternate Usable Area */
-#define SDPID_AUA 0x02
-	char res;
-	char auaid;             /* 0x01 is Id for the A U A */
-	short wauai;            /* Width of AUAi */
-	short hauai;            /* Height of AUAi */
-	char auaunits;          /* 0x00:in, 0x01:mm */
-	int auaxr;
-	int auayr;
-	char awauai;
-	char ahauai;
-} __attribute__ ((packed)) aua_t;
-
-/*
- * Structure representing one followed by the other
- */
-typedef struct {
-	uab_t uab;
-	aua_t aua;
-} __attribute__ ((packed)) ua_t;
-
-/*
- * Try to determine screen size using Read Partition (Query)
- */
-int
-tty3270_size(tub_t *tubp, long *flags)
-{
-	char wbuf[7] = { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 };
-	int     rc = 0;
-	int     count;
-	unsigned char *cp;
-	ua_t *uap;
-	char miniscreen[256];
-	char (*screen)[];
-	int screenl;
-	int geom_rows, geom_cols, fourteenbitadr;
-	void (*oldint)(struct tub_s *, devstat_t *);
-
-	if (tubp->flags & TUB_SIZED)
-		return 0;
-	fourteenbitadr = 0;
-	geom_rows = tubp->geom_rows;
-	geom_cols = tubp->geom_cols;
-
-	oldint = tubp->intv;
-	tubp->intv = tty3270_size_int;
-
-	if (tubp->cmd == TBC_CONOPEN) {
-		tubp->ttyccw.cmd_code = TC_EWRITEA;
-		cp = miniscreen;
-		*cp++ = TW_KR;
-		/* more? */
-		tubp->ttyccw.flags = CCW_FLAG_SLI;
-		tubp->ttyccw.cda = virt_to_phys(miniscreen);
-		tubp->ttyccw.count = (char *)cp - miniscreen;
-		rc = tty3270_size_io(tubp);
-		rc = tty3270_size_wait(tubp, flags, 0);
-	}
-
-	tubp->ttyccw.cmd_code = TC_WRITESF;
-	tubp->ttyccw.flags = CCW_FLAG_SLI;
-	tubp->ttyccw.cda = virt_to_phys(wbuf);
-	tubp->ttyccw.count = sizeof wbuf;
-
-try_again:
-	rc = tty3270_size_io(tubp);
-	if (rc)
-		printk("tty3270_size_io returned %d\n", rc);
-
-	rc = tty3270_size_wait(tubp, flags, 0);
-	if (rc != 0) {
-		goto do_return;
-	}
-
-	/*
-	 * Unit-Check Processing:
-	 * Expect Command Reject or Intervention Required.
-	 * For Command Reject assume old hdwe/software and
-	 * set a default size of 80x24.
-	 * For Intervention Required, wait for signal pending
-	 * or Unsolicited Device End; if the latter, retry.
-	 */
-	if (tubp->dstat & DEV_STAT_UNIT_CHECK) {
-		if (tubp->sense.data[0] & SNS0_CMD_REJECT) {
-			goto use_diag210; /* perhaps it's tn3270 */
-		} else if (tubp->sense.data[0] & SNS0_INTERVENTION_REQ) {
-			if ((rc = tty3270_size_wait(tubp, flags,
-			    DEV_STAT_DEV_END)))
-				goto do_return;
-			goto try_again;
-		} else {
-			printk("tty3270_size(): unkn sense %.2x\n",
-				tubp->sense.data[0]);
-			goto do_return;
-		}
-	}
-	if ((rc = tty3270_size_wait(tubp, flags, DEV_STAT_ATTENTION)))
-		goto do_return;
-
-	/* Set up a read ccw and issue it */
-	tubp->ttyccw.cmd_code = TC_READMOD;
-	tubp->ttyccw.flags = CCW_FLAG_SLI;
-	tubp->ttyccw.cda = virt_to_phys(miniscreen);
-	tubp->ttyccw.count = sizeof miniscreen;
-	tty3270_size_io(tubp);
-	rc = tty3270_size_wait(tubp, flags, 0);
-	if (rc != 0)
-		goto do_return;
-
-	count = sizeof miniscreen - tubp->cswl;
-	cp = miniscreen;
-	if (*cp++ != 0x88)
-		goto do_return;
-	uap = (void *)cp;
-	if (uap->uab.qcode != QCODE_UA)
-		goto do_return;
-	geom_rows = uap->uab.h;
-	geom_cols = uap->uab.w;
-	if ((uap->uab.flags0 & FLAGS0_ADDR) == FLAGS0_ADDR_12_14 ||
-	    (uap->uab.flags0 & FLAGS0_ADDR) == FLAGS0_ADDR_12_14_16)
-		fourteenbitadr = 1;
-	if (uap->uab.l <= sizeof uap->uab)
-		goto do_return;
-	if (uap->aua.sdpid != SDPID_AUA) {
-		printk("AUA sdpid was 0x%.2x, expecting 0x%.2x\n",
-			uap->aua.sdpid, SDPID_AUA);
-		goto do_return;
-	}
-	geom_rows = uap->aua.hauai;
-	geom_cols = uap->aua.wauai;
-	goto do_return;
-
-use_diag210:
-	if (MACHINE_IS_VM) {
-		diag210_t d210;
-
-		d210.vrdcdvno = tubp->devno;
-		d210.vrdclen = sizeof d210;
-		rc = diag210(&d210);
-		if (rc) {
-			printk("tty3270_size: diag210 for 0x%.4x "
-				"returned %d\n", tubp->devno, rc);
-			goto do_return;
-		}
-		switch(d210.vrdccrmd) {
-		case 2:
-			geom_rows = 24;
-			geom_cols = 80;
-			goto do_return;
-		case 3:
-			geom_rows = 32;
-			geom_cols = 80;
-			goto do_return;
-		case 4:
-			geom_rows = 43;
-			geom_cols = 80;
-			goto do_return;
-		case 5:
-			geom_rows = 27;
-			geom_cols = 132;
-			goto do_return;
-		default:
-			printk("vrdccrmd is 0x%.8x\n", d210.vrdccrmd);
-		}
-	}
-
-do_return:
-	if (geom_rows == 0) {
-		geom_rows = _GEOM_ROWS;
-		geom_cols = _GEOM_COLS;
-	}
-	tubp->tubiocb.pf_cnt = 24;
-	tubp->tubiocb.re_cnt = 20;
-	tubp->tubiocb.map = 0;
-
-	screenl = geom_rows * geom_cols + 100;
-	screen = (char (*)[])kmalloc(screenl, GFP_KERNEL);
-	if (screen == NULL) {
-		printk("ttyscreen size %d unavailable\n", screenl);
-	} else {
-		if (tubp->ttyscreen)
-			kfree(tubp->ttyscreen);
-		tubp->tubiocb.line_cnt = tubp->geom_rows = geom_rows;
-		tubp->tubiocb.col_cnt = tubp->geom_cols = geom_cols;
-		tubp->tty_14bitadr = fourteenbitadr;
-		tubp->ttyscreen = screen;
-		tubp->ttyscreenl = screenl;
-		if (geom_rows == 24 && geom_cols == 80)
-			tubp->tubiocb.model = 2;
-		else if (geom_rows == 32 && geom_cols == 80)
-			tubp->tubiocb.model = 3;
-		else if (geom_rows == 43 && geom_cols == 80)
-			tubp->tubiocb.model = 4;
-		else if (geom_rows == 27 && geom_cols == 132)
-			tubp->tubiocb.model = 5;
-		else
-			tubp->tubiocb.model = 0;
-		tubp->flags |= TUB_SIZED;
-	}
-	if (rc == 0 && tubp->ttyscreen == NULL)
-		rc = -ENOMEM;
-	tubp->intv = oldint;
-	return rc;
-}
-
-static int
-tty3270_size_io(tub_t *tubp)
-{
-	tubp->flags |= TUB_WORKING;
-	tubp->dstat = 0;
-
-	return do_IO(tubp->irq, &tubp->ttyccw, tubp->irq, 0, 0);
-}
-
-static void
-tty3270_size_int(tub_t *tubp, devstat_t *dsp)
-{
-#define DEV_NOT_WORKING \
-  (DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_CHECK)
-
-	tubp->dstat = dsp->dstat;
-	if (dsp->dstat & DEV_STAT_CHN_END)
-		tubp->cswl = dsp->rescnt;
-	if (dsp->dstat & DEV_NOT_WORKING)
-		tubp->flags &= ~TUB_WORKING;
-	if (dsp->dstat & DEV_STAT_UNIT_CHECK)
-		tubp->sense = dsp->ii.sense;
-
-	wake_up_interruptible(&tubp->waitq);
-}
-
-/*
- * Wait for something.  If the third arg is zero, wait until
- * tty3270_size_int() turns off TUB_WORKING.  If the third arg
- * is not zero, it is a device-status bit; wait until dstat
- * has the bit turned on.  Never wait if signal is pending.
- * Return 0 unless signal pending, in which case -ERESTARTSYS.
- */
-static int
-tty3270_size_wait(tub_t *tubp, long *flags, int stat)
-{
-	DECLARE_WAITQUEUE(wait, current);
-
-	add_wait_queue(&tubp->waitq, &wait);
-	while (!signal_pending(current) &&
-	    (stat? (tubp->dstat & stat) == 0:
-	     (tubp->flags & TUB_WORKING) != 0)) {
-#warning FIXME: [kj] use set_current_state instead of current->state=
-		current->state = TASK_INTERRUPTIBLE;
-		TUBUNLOCK(tubp->irq, *flags);
-		schedule();
-#warning FIXME: [kj] use set_current_state instead of current->state=
-		current->state = TASK_RUNNING;
-		TUBLOCK(tubp->irq, *flags);
-	}
-	remove_wait_queue(&tubp->waitq, &wait);
-	return signal_pending(current)? -ERESTARTSYS: 0;
-}

_