patch-2.4.4 linux/drivers/s390/char/tubtty.c

Next file: linux/drivers/s390/char/tubttyaid.c
Previous file: linux/drivers/s390/char/tubio.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/drivers/s390/char/tubtty.c linux/drivers/s390/char/tubtty.c
@@ -0,0 +1,1001 @@
+/*
+ *  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_bh(void *);
+       void tty3270_sched_bh(tub_t *);
+static int tty3270_wait(tub_t *, int *);
+       void tty3270_int(tub_t *, devstat_t *);
+       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);
+
+int tty3270_major = -1;
+char tty3270_major_string[16];
+struct tty_driver tty3270_driver;
+int tty3270_refcount;
+struct tty_struct *tty3270_table[TUBMAXMINS];
+struct termios *tty3270_termios[TUBMAXMINS];
+struct termios *tty3270_termios_locked[TUBMAXMINS];
+#ifdef CONFIG_3270_CONSOLE
+int con3270_major = -1;
+struct tty_driver con3270_driver;
+int con3270_refcount;
+struct tty_struct *con3270_table[1];
+struct termios *con3270_termios[1];
+struct termios *con3270_termios_locked[1];
+#endif /* CONFIG_3270_CONSOLE */
+
+int tty3270_proc_index;
+int tty3270_proc_data;
+int tty3270_proc_misc;
+enum tubwhat tty3270_proc_what;
+
+/*
+ * tty3270_init() -- Register the tty3270 driver
+ */
+int
+tty3270_init(void)
+{
+	struct tty_driver *td = &tty3270_driver;
+	int rc;
+
+	/* Initialize for tty driver */
+	td->magic = TTY_DRIVER_MAGIC;
+	td->driver_name = "tty3270";
+	td->name = "tty3270";
+	td->major = IBM_TTY3270_MAJOR;
+	td->minor_start = 0;
+	td->num = TUBMAXMINS;
+	td->type = TTY_DRIVER_TYPE_SYSTEM;
+	td->subtype = SYSTEM_TYPE_TTY;
+	td->init_termios = tty_std_termios;
+	td->flags = TTY_DRIVER_RESET_TERMIOS;
+	td->refcount = &tty3270_refcount;
+	td->table = tty3270_table;
+	td->termios = tty3270_termios;
+	td->termios_locked = tty3270_termios_locked;
+
+	td->open = tty3270_open;
+	td->close = tty3270_close;
+	td->write = tty3270_write;
+	td->put_char = tty3270_put_char;
+	td->flush_chars = tty3270_flush_chars;
+	td->write_room = tty3270_write_room;
+	td->chars_in_buffer = tty3270_chars_in_buffer;
+	td->ioctl = tty3270_ioctl;
+	td->ioctl = NULL;
+	td->set_termios = tty3270_set_termios;
+	td->throttle = NULL;
+	td->unthrottle = NULL;
+	td->stop = NULL;
+	td->start = NULL;
+	td->hangup = tty3270_hangup;
+	td->break_ctl = NULL;
+	td->flush_buffer = tty3270_flush_buffer;
+	td->set_ldisc = NULL;
+	td->wait_until_sent = NULL;
+	td->send_xchar = NULL;
+	td->read_proc = tty3270_read_proc;
+	td->write_proc = tty3270_write_proc;
+
+	rc = tty_register_driver(td);
+	if (rc) {
+		printk(KERN_ERR "tty3270 registration failed with %d\n", rc);
+	} else {
+		tty3270_major = IBM_TTY3270_MAJOR;
+		sprintf(tty3270_major_string, "%d", tty3270_major);
+		if (td->proc_entry != NULL)
+			td->proc_entry->mode = S_IRUGO | S_IWUGO;
+	}
+#ifdef CONFIG_3270_CONSOLE
+	tty3270_con_driver = *td;
+	td = &tty3270_con_driver;
+	td->driver_name = "con3270";
+	td->name = "con3270";
+	td->major = MAJOR(S390_CONSOLE_DEV);
+	td->minor_start = MINOR(S390_CONSOLE_DEV);
+	td->num = 1;
+	td->refcount = &con3270_refcount;
+	td->table = con3270_table;
+	td->termios = con3270_termios;
+	td->termios_locked = con3270_termios_locked;
+
+	rc = tty_register_driver(td);
+	if (rc) {
+		printk(KERN_ERR "con3270 registration failed with %d\n", rc);
+	} else {
+		con3270_major = MAJOR(S390_CONSOLE_DEV);
+		if (td->proc_entry != NULL)
+			td->proc_entry->mode = S_IRUGO | S_IWUGO;
+	}
+#endif /* if CONFIG_3270_CONSOLE */
+
+	return rc;
+}
+
+/*
+ * tty3270_fini() -- Uninitialize linemode tubes
+ */
+void
+tty3270_fini(void)
+{
+	if (tty3270_major != -1) {
+		tty_unregister_driver(&tty3270_driver);
+		tty3270_major = -1;
+	}
+#ifdef CONFIG_3270_CONSOLE
+	if (con3270_major != -1) {
+		tty_unregister_driver(&con3270_driver);
+		con3270_major = -1;
+	}
+#endif
+}
+
+static int 
+tty3270_open(struct tty_struct *tty, struct file *filp)
+{
+	tub_t *tubp;
+	int flags;
+	int rc;
+	int cmd;
+
+	if ((tubp = TTY2TUB(tty)) == NULL) {
+		return -ENODEV;
+	}
+
+	tub_inc_use_count();
+	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;
+	tty->winsize.ws_col = tubp->geom_cols;
+	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);
+	tub_dec_use_count();
+	return rc;
+}
+
+static void
+tty3270_close(struct tty_struct *tty, struct file *filp)
+{
+	tub_t *tubp;
+	int 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:
+	tub_dec_use_count();
+	TUBUNLOCK(tubp->irq, flags);
+}
+
+static int 
+tty3270_write(struct tty_struct *tty, int fromuser,
+		const unsigned char *buf, int count)
+{
+	tub_t *tubp;
+	int flags;
+	bcb_t obcb;
+	int rc = 0;
+
+	if ((tubp = tty->driver_data) == NULL)
+		return -1;
+
+#ifdef CONFIG_3270_CONSOLE
+	if (tub3270_con_tubp == tubp)
+		tub3270_con_copy(tubp);
+#endif /* CONFIG_3270_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);
+	tty3270_try_logging(tubp);
+	TUBUNLOCK(tubp->irq, flags);
+	return rc;
+} 
+
+static void
+tty3270_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	int 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;
+	int 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;
+	int 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;
+	int 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;
+	int 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;
+	char *majstr;
+
+	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];
+			majstr = tty3270_major_string;
+#ifdef CONFIG_3270_CONSOLE
+			if (tubp == tub3270_con_tubp)
+				majstr = "CONSOLE";
+#endif /* CONFIG_3270_CONSOLE */
+			len += sprintf(buf + len, "%.3x %s %d\n",
+				tubp->devno, majstr, 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;
+	kdev_t device;
+	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;
+	device = tty? tty->device: 0;
+	if (device) {
+		if (MAJOR(device) == IBM_TTY3270_MAJOR)
+			tubp = (*tubminors)[MINOR(device)];
+#ifdef CONFIG_3270_CONSOLE
+		if (device == S390_CONSOLE_DEV)
+			tubp = tub3270_con_tubp;
+#endif /* CONFIG_3270_CONSOLE */
+	}
+	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 (!suser()) {
+		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_bh(tubp) -- Perform back-half processing
+ */
+static void
+tty3270_bh(void *data)
+{
+	int flags;
+	tub_t *tubp;
+	struct tty_struct *tty;
+
+	tubp = data;
+	TUBLOCK(tubp->irq, flags);
+	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;
+	tubp->tqueue.routine = tty3270_bh;
+	tubp->tqueue.data = tubp;
+	queue_task(&tubp->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+/*
+ * 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, int *flags)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	TUBLOCK(tubp->irq, *flags);
+	add_wait_queue(&tubp->waitq, &wait);
+	while (!signal_pending(current) &&
+	    (tubp->flags & TUB_WORKING) != 0) {
+		current->state = TASK_INTERRUPTIBLE;
+		TUBUNLOCK(tubp->irq, *flags);
+		schedule();
+		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);
+	}
+}
+
+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_3270_CONSOLE
+	if (tub3270_con_tubp == tubp)
+		tub3270_con_copy(tubp);
+#endif /* CONFIG_3270_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)
+{
+	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;
+	in = tubp->tty_input;
+	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 %.8x:\n",
+	minor, (int)tubp);
+
+	len += sprintf(buf+len, "inattr is at %.8x\n",
+		(int)&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=%.8x\n",
+	tubp->lnopen, tubp->fsopen, (int)&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=%.8x\n",
+	(int)tubp->tty);
+
+	if (tty) len += sprintf(buf+len,
+"    write_wait=%.8x read_wait=%.8x\n",
+	(int)&tty->write_wait, (int)&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;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)