[RFC][PATCH 9/13] Equinox SST driver: tty interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Adds Equinox multi-port serial (SST) driver.

Part 9: new source file: drivers/char/eqnx/sst_tty.c.  Provides the
standard
TTY interface routines for the SST driver.

Signed-off-by: Mike Straub <[email protected]>

---
 sst_tty.c | 1961
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1961 insertions(+)

diff -Naurp -X dontdiff linux-2.6.17/drivers/char/eqnx/sst_tty.c
linux-2.6.17.eqnx/drivers/char/eqnx/sst_tty.c
--- linux-2.6.17/drivers/char/eqnx/sst_tty.c	1969-12-31
19:00:00.000000000 -0500
+++ linux-2.6.17.eqnx/drivers/char/eqnx/sst_tty.c	2006-06-20
09:50:09.000000000 -0400
@@ -0,0 +1,1961 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * This driver supports the PCI models of the Equinox / Avocent SST
boards
+ * using SSP-4 and SSP-64 ASIC technology
+ * Boards supported:
+ * SSP-4P
+ * SSP-8P
+ * SSP-16P
+ * SSP-64P
+ * SSP-128P
+ *
+ * Currently maintained by mike straub <[email protected]>
+ */
+
+/*
+ * standard tty interface routines
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#ifdef CONFIG_MODVERSIONS
+#define MODVERSIONS	1
+#endif
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <linux/device.h>
+
+#include "icp.h"
+#include "eqnx_def.h"
+#include "eqnx.h"
+
+/**********************************************************************
*****/
+/* module globals and defines
*/
+/**********************************************************************
*****/
+
+/* defer call to write_wakeup */
+int eqnx_write_wakeup_deferred = 0;
+
+/* local buffer for copying output characters. Used in eqnx_put_char */
+extern char *eqnx_txcookbuf;
+static int eqnx_txcooksize = 0;
+static int eqnx_txcookrealsize = 0;
+static struct tty_struct *eqnx_txcooktty = (struct tty_struct *)NULL;
+
+#define	MY_GROUP()	(process_group(current))
+
+static void chanon(struct mpchan *mpc);
+static void chanoff(struct mpchan *mpc);
+static void eqnx_write_SSP64(struct mpchan *mpc, unsigned char *buf,
int count);
+void eqnx_write_SSP4(struct mpchan *mpc, int func_type);
+void eqnx_flush_chars(struct tty_struct *tty);
+static void eqnx_flush_chars_locked(struct tty_struct *tty);
+void eqnx_flush_buffer_locked(struct tty_struct *tty);
+static void delay_jiffies(int);
+extern int SSTMINOR(unsigned int, unsigned int);
+
+/**********************************************************************
*****/
+/* external variable and routines
*/
+/**********************************************************************
*****/
+
+extern int eqnx_nssps;
+extern struct mpdev eqnx_dev[MAXSSP];
+extern struct mpchan *eqnx_chan;
+extern struct datascope eqnx_dscope[2];
+extern char *eqnx_tmpwritebuf;
+extern struct semaphore eqnx_tmpwritesem;
+
+extern void eqnx_megaparam(int);
+extern int eqnx_modem(int, int);
+extern void eqnx_frame_wait(struct mpchan *, int);
+extern void eqnx_chnl_sync(struct mpchan *);
+extern int SSTMINOR(unsigned int, unsigned int);
+extern u32 eqnx_get_modem_info(struct mpchan *);
+extern void eqnx_set_modem_info(struct mpchan *, unsigned int, unsigned
int,
+				struct tty_struct *);
+
+/*
+ * eqnx_open(tty, filp)
+ *
+ * Open of tty device associated with SST channel.
+ *
+ * tty	= pointer to tty structure being opened.
+ * filp	= file structure.
+ */
+int eqnx_open(struct tty_struct *tty, struct file *filp)
+{
+	struct mpchan *mpc;
+	struct tty_struct *tp;
+	struct mpdev *mpd;
+	int d, minor, major, nchan, rc = 0;
+	unsigned long flags;
+
+	major = tty->driver->major;
+	minor = tty->driver->minor_start + tty->index;
+
+	d = SSTMINOR(major, minor);
+
+	/* validate channel number */
+	if ((d > (eqnx_nssps * MAXCHNL_BRD)) || (eqnx_nssps == 0) || (d
< 0))
+		return (-ENODEV);
+
+	mpc = &eqnx_chan[d];
+	mpd = mpc->mpc_mpd;
+
+	/* validate that the board for this channel is valid and alive
*/
+	if ((mpd == (struct mpdev *)NULL) || (!(mpd->mpd_alive)))
+		return (-ENODEV);
+
+	dev_dbg(mpd->dev, "eqnx_open: device %d\n", d);
+
+	mpc->mpc_major = major;
+	mpc->mpc_minor = minor;
+
+	spin_lock_irqsave(&mpd->mpd_lock, flags);
+
+	/* verify that the channel is valid and alive */
+	nchan = MPCHAN(d);
+	if ((mpd >= &eqnx_dev[eqnx_nssps]) || (!(mpd->mpd_alive)) ||
+	    (nchan >= (int)mpd->mpd_nchan) ||
+	    (mpc->mpc_icp->icp_rng_state != RNG_GOOD) ||
+	    (mpc->mpc_icp->lmx[mpc->mpc_lmxno].lmx_active != DEV_GOOD))
{
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+		return (-ENODEV);
+	}
+
+	/* if a bridge, check the mux */
+	if (mpc->mpc_icp->lmx[mpc->mpc_lmxno].lmx_bridge)
+		if (mpc->mpc_icp->lmx[mpc->mpc_lmxno].lmx_rmt_active !=
+		    DEV_GOOD) {
+			spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+			return (-ENODEV);
+		}
+
+	/* Do not allow ports to open on SST-16 boards without a panel
*/
+	if (mpd->mpd_board_def->flags & NOPANEL) {
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+		return (-ENODEV);
+	}
+
+	/* check for channel number outside valid range, SSP64 only */
+	if (mpd->mpd_board_def->asic == SSP64) {
+		if ((MPCHAN(d) % 16) >=
+		    mpc->mpc_icp->lmx[mpc->mpc_lmxno].lmx_chan) {
+			dev_dbg(mpd->dev, "eqnx_open: mpchan(%d) "
+				"lmx_no(%d) lmx_chan(%d)\n",
+				MPCHAN(d), mpc->mpc_lmxno,
+
mpc->mpc_icp->lmx[mpc->mpc_lmxno].lmx_chan);
+			spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+			return (-ENODEV);
+		}
+	}
+
+	mpc->refcount++;
+
+	/* initialize termios struct if required */
+	mpc->mpc_tty = tty;
+	tp = tty;
+	if (tty->termios == NULL) {
+		*tty->termios = *mpc->normaltermios;
+	}
+
+	if (!(mpc->flags & ASYNC_INITIALIZED)) {
+		tty->driver_data = mpc;
+
+		/* force CLOCAL on for RS422 ports */
+		if ((mpc->mpc_icp->lmx[mpc->mpc_lmxno].lmx_id ==
LMX_8E_422) ||
+		    (mpc->mpc_icp->lmx[mpc->mpc_lmxno].lmx_id ==
LMX_PM16_422))
+			tty->termios->c_cflag |= CLOCAL;
+
+		if ((mpc->mpc_flags & MPC_OPEN) == 0) {
+			dev_dbg(mpd->dev, "eqnx_open: device %d "
+				"calling megaparam\n", d);
+			chanon(mpc);
+			eqnx_megaparam(d);
+		}
+
+		mpc->carr_state = eqnx_modem(d, TURNON);
+		mpc->flags |= ASYNC_INITIALIZED;
+		clear_bit(TTY_IO_ERROR, &tty->flags);
+	}
+
+	/*
+	 * If port is in the middle of closing, then wait. Get error
status
+	 * from flag settings.
+	 */
+	if (mpc->flags & ASYNC_CLOSING) {
+		dev_dbg(mpd->dev, "eqnx_open: sleeping on close_wait "
+			"for device %d\n", d);
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+		wait_event_interruptible(mpc->close_wait,
+					 (mpc->flags & ASYNC_CLOSING) ==
0);
+		if (mpc->flags & ASYNC_HUP_NOTIFY)
+			return (-EAGAIN);
+
+		return (-ERESTARTSYS);
+	}
+
+	/* callin device is only one available */
+	if (1) {
+		dev_dbg(mpd->dev, "eqnx_open: opening dialin device
%d\n", d);
+		if (filp->f_flags & O_NONBLOCK) {
+			dev_dbg(mpd->dev, "eqnx_open: opening dialin
device "
+				"%d non_blocking with c_flags = %o\n",
+				d, tp->termios->c_cflags);
+		} else {
+			mpc->openwaitcnt++;
+			if (mpc->refcount)
+				mpc->refcount--;
+
+			while (1) {
+				mpc->carr_state = eqnx_modem(d, TURNON);
+
+				if (tty_hung_up_p(filp) ||
+				    ((mpc->flags & ASYNC_INITIALIZED) ==
0)) {
+					if (mpc->flags &
ASYNC_HUP_NOTIFY)
+						rc = -EBUSY;
+					else
+						rc = -ERESTARTSYS;
+					break;
+				}
+
+				/* if CLOCAL set, allow the open */
+				if (((mpc->flags & ASYNC_CLOSING) == 0)
&&
+				    ((tp->termios->c_cflag & CLOCAL) ||
+				     (mpc->carr_state)))
+					break;
+
+				if (signal_pending(current)) {
+					rc = -ERESTARTSYS;
+					break;
+				}
+
+				dev_dbg(mpd->dev, "eqnx_open: device %d
"
+					"sleeping on open_wait\n", d);
+
+				/* wait while DCD is low */
+				mpc->open_wait_wait++;
+				spin_unlock_irqrestore(&mpd->mpd_lock,
flags);
+				wait_event_interruptible(mpc->open_wait,
+
mpc->open_wait_wait ==
+							 0);
+				spin_lock_irqsave(&mpd->mpd_lock,
flags);
+				if (signal_pending(current)) {
+					rc = -ERESTARTSYS;
+					break;
+				}
+				if (mpc->carr_state)
+					break;
+			}
+			if (!tty_hung_up_p(filp))
+				mpc->refcount++;
+			mpc->openwaitcnt--;
+			if (rc < 0) {
+				spin_unlock_irqrestore(&mpd->mpd_lock,
flags);
+				dev_dbg(mpd->dev, "eqnx_open: device %d
"
+					"open fail w/ c_cflag=%o
carr_stat=%d, "
+					"rc=%d\n",
+					d, tp->termios->c_cflag,
+					mpc->carr_state, rc);
+				return (rc);
+			}
+		}
+		dev_dbg(mpd->dev, "eqnx_open: device %d open fail w/ "
+			"c_cflag=%o, carr_stat=%d, rc=%d\n",
+			d, tp->termios->c_cflag, mpc->carr_state, rc);
+		mpc->flags |= ASYNC_NORMAL_ACTIVE;
+	}
+
+	if ((mpc->refcount == 1) && (mpc->flags & ASYNC_SPLIT_TERMIOS))
{
+		*tp->termios = *mpc->normaltermios;
+	}
+
+	/* SSP4 channel setup */
+	if ((mpd->mpd_board_def->asic != SSP64) && (!mpc->xmit_buf)) {
+		unsigned long page;
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+		page = get_zeroed_page(GFP_KERNEL);
+		if (!page)
+			return (-ENOMEM);
+
+		spin_lock_irqsave(&mpd->mpd_lock, flags);
+		mpc->xmit_buf = (unsigned char *)page;
+		mpc->xmit_head = 0;
+	}
+	mpc->pgrp = MY_GROUP();
+
+	spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+
+	return (0);
+}
+
+/*
+ * eqnx_close(tty, filp)
+ *
+ * Close of tty device associated with SST channel.
+ * Called on last close of any device.
+ * Release resources, reset device, and wakeup any sleepers
+ * waiting for channel to become idle.
+ * 
+ * tty	= pointer to tty structure.
+ * filp	= file structure.
+ */
+void eqnx_close(struct tty_struct *tty, struct file *filp)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	int nchan, d;
+	unsigned long flags;
+	u16 attn;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	if ((mpd = mpc->mpc_mpd) == (struct mpdev *)NULL)
+		return;
+	nchan = MPCHAN(d);
+	if ((mpd >= &eqnx_dev[eqnx_nssps]) || (!(mpd->mpd_alive)) ||
+	    (nchan >= (int)mpd->mpd_nchan))
+		return;
+
+	dev_dbg(mpd->dev, "eqnx_close: device %d being closed\n", d);
+
+	spin_lock_irqsave(&mpd->mpd_lock, flags);
+	if (tty_hung_up_p(filp)) {
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+		return;
+	}
+	dev_dbg(mpd->dev, "eqnx_close: device %d being closed with "
+		"refcount = %d\n", d, mpc->refcount);
+	/* do nothing if not final close */
+	if (mpc->refcount)
+		mpc->refcount -= 1;
+	if (mpc->refcount) {
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+		return;
+	}
+
+	mpc->flags |= ASYNC_CLOSING;
+	if (mpc->flags & ASYNC_NORMAL_ACTIVE)
+		*mpc->normaltermios = *tty->termios;
+	if (tty == eqnx_txcooktty)
+		eqnx_flush_chars_locked(tty);
+	tty->closing = true;
+
+	/* wait for all data to be sent if busy */
+	if (mpc->mpc_flags & MPC_BUSY) {
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+		tty_wait_until_sent(tty, (unsigned
long)mpc->closing_wait);
+		spin_lock_irqsave(&mpd->mpd_lock, flags);
+	}
+
+	mpc->flags &= ~ASYNC_INITIALIZED;
+	if (tty->termios->c_cflag & HUPCL) {
+		mpc->flags &= ~ASYNC_INITIALIZED;
+		eqnx_modem(d, TURNOFF);
+	}
+
+	mpc->mpc_flags &= ~(MPC_SOFTCAR | MPC_DIALOUT | MPC_MODEM |
+			    MPC_DIALIN | MPC_CTS);
+	attn = SSTRD16(mpc->mpc_icpi->cin_attn_ena);
+	attn &= ~ENA_DCD_CNG;
+	SSTWR16(mpc->mpc_icpi->cin_attn_ena, attn);
+	mpc->mpc_icpo->cout_cpu_req &= ~TX_SUSP;
+	set_bit(TTY_IO_ERROR, &tty->flags);
+	if (tty->ldisc.flush_buffer)
+		(tty->ldisc.flush_buffer) (tty);
+	chanoff(mpc);
+
+	eqnx_flush_buffer_locked(tty);
+	tty->closing = false;
+	tty->driver_data = (void *)NULL;
+	mpc->mpc_tty = (struct tty_struct *)NULL;
+
+	/* wakeup open waiters */
+	if (mpc->openwaitcnt) {
+		if (mpc->close_delay) {
+			spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+			delay_jiffies(mpc->close_delay);
+			spin_lock_irqsave(&mpd->mpd_lock, flags);
+		}
+		dev_dbg(mpd->dev, "eqnx_close: waking up open_waiters
for "
+			"device %d\n", d);
+		mpc->open_wait_wait = 0;
+		wake_up_interruptible(&mpc->open_wait);
+	}
+
+	/* SSP channel cleanup */
+	if ((mpd->mpd_board_def->asic != SSP64) && (mpc->xmit_buf)) {
+		free_page((unsigned long)mpc->xmit_buf);
+		mpc->xmit_buf = NULL;
+	}
+
+	mpc->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
+	dev_dbg(mpd->dev, "eqnx_close: close complete for device %d\n",
d);
+	wake_up_interruptible(&mpc->close_wait);
+	eqnx_write_wakeup_deferred = 0;
+	spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+	return;
+}
+
+/*
+ * eqnx_write(tty, buf, count)
+ *
+ * Write data bytes to the tty device.  In 2.6 kernel, buffer
+ * is always in kernel space.
+ *
+ * Return with the number of bytes accepted.
+ *
+ * tty	= pointer to tty structure.
+ * buf	= buffer of bytes to write.
+ * count = number of bytes to write.
+ */
+int eqnx_write(struct tty_struct *tty, const unsigned char *buf, int
count)
+{
+	struct mpchan *mpc;
+	int space, nx, d, qsize, written = 0, c = 0, datascope = 0;
+	volatile union global_regs_u *icpg;
+	volatile struct icp_out_struct *icpo;
+	volatile struct cout_que_struct *icpq;
+	unsigned char oldreg, *b;
+	struct mpdev *mpd;
+	unsigned long flags;
+	u16 qcnt, attn_enbl;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return (-ENODEV);
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return (-ENODEV);
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return (-ENODEV);
+
+	mpd = mpc->mpc_mpd;
+	dev_dbg(mpd->dev, "eqnx_write: tty=%d, count=%d\n",
+		(unsigned int)SSTMINOR(mpc->mpc_major, mpc->mpc_minor),
count);
+
+	if (tty == eqnx_txcooktty)
+		eqnx_flush_chars(tty);
+
+	spin_lock_irqsave(&mpd->mpd_lock, flags);
+	icpo = mpc->mpc_icpo;
+	icpg = (volatile union global_regs_u *)icpo;
+	icpq = &icpo->cout_q0;
+
+	qsize = mpc->mpc_txq.q_size;
+	space = qsize - (mpc->mpc_count + SSTRD16(icpq->q_data_count)) -
1;
+	if (!tty->stopped && !tty->hw_stopped && space >= (qsize >> 4))
{
+		if ((mpd->mpd_board_def->asic != SSP64) &&
(mpc->xmit_cnt))
+			goto ssp_end;
+		dev_dbg(mpd->dev, "eqnx_write: device = %d, q_size = %d,
"
+			"space = %d\n",
+			SSTMINOR(mpc->mpc_major, mpc->mpc_minor),
+			mpc->mpc_txq.q_size, space);
+		c = MIN(space, count);
+		b = (unsigned char *)buf;
+
+		mpc->mpc_flags |= MPC_BUSY;
+		if ((mpc->mpc_flags & MPC_DSCOPEW) &&
+		    (eqnx_dscope[1].chan == (mpc - eqnx_chan)))
+			datascope = 1;
+
+		while (c > 0) {
+			/* copy data directly from buffer to SSP queue
*/
+			nx = MIN(c, (mpc->mpc_txq.q_end -
+				     mpc->mpc_txq.q_ptr + 1));
+			memcpy((mpc->mpc_txq.q_addr +
mpc->mpc_txq.q_ptr),
+			       b, nx);
+			mpc->mpc_txq.q_ptr += nx;
+			if (mpc->mpc_txq.q_ptr > mpc->mpc_txq.q_end)
+				mpc->mpc_txq.q_ptr =
mpc->mpc_txq.q_begin;
+
+			/* if datascope enabled, copy to datascope
buffers */
+			if (datascope) {
+				int room, move_cnt, d = 1;
+				unsigned char *ubase = b;
+				room = DSQSIZE - ((eqnx_dscope[d].next -
+
eqnx_dscope[d].q.q_ptr) &
+						  (DSQMASK));
+				if (nx > room)
+					eqnx_dscope[d].status |=
DSQWRAP;
+				else
+					room = nx;
+				while (room > 0) {
+					move_cnt = MIN(room,
+
eqnx_dscope[d].q.q_end -
+
eqnx_dscope[d].next + 1);
+					memcpy(eqnx_dscope[d].q.q_addr +
+					       eqnx_dscope[d].next,
ubase,
+					       move_cnt);
+					eqnx_dscope[d].next += move_cnt;
+					if (eqnx_dscope[d].next >
+					    eqnx_dscope[d].q.q_end)
+						eqnx_dscope[d].next =
+
eqnx_dscope[d].q.q_begin;
+					ubase += move_cnt;
+					room -= move_cnt;
+				}
+				eqnx_dscope[d].scope_wait_wait = 0;
+				wake_up_interruptible(&eqnx_dscope[d].
+						      scope_wait);
+			}
+
+			c -= nx;
+			count -= nx;
+			written += nx;
+			buf += nx;
+			b += nx;
+		}
+
+		oldreg = icpo->cout_cpu_req;
+		icpo->cout_cpu_req |= TX_SUSP;
+		eqnx_chnl_sync(mpc);
+
+		/* force the send if required */
+		qcnt = SSTRD16(icpq->q_data_count);
+		if (qcnt < 9) {
+			if ((icpo->cout_intnl_svd_toggls & TOGGLS_LSEND)
==
+			    (icpo->cout_cpu_req & CPU_SND_REQ)) {
+				icpo->cout_cpu_req ^= (CPU_SND_REQ);
+				mpc->mpc_cout_events &= ~EV_CPU_REQ_DN;
+			}
+		}
+		qcnt += written;
+		SSTWR16(icpq->q_data_count, qcnt);
+		space -= written;
+		dev_dbg(mpd->dev, "eqnx_write: wrote %d chars for device
%d\n",
+			written, SSTMINOR(mpc->mpc_major,
mpc->mpc_minor));
+		dev_dbg(mpd->dev, "eqnx_write: q data count %d, space %d
"
+			"for device %d\n",
+			qcnt, space, SSTMINOR(mpc->mpc_major,
mpc->mpc_minor));
+		if (!(oldreg & TX_SUSP))
+			icpo->cout_cpu_req &= ~TX_SUSP;
+		mpc->mpc_output += written;
+		attn_enbl = SSTRD16(icpo->cout_attn_enbl);
+		attn_enbl |= (ENA_TX_EMPTY_Q0 | ENA_TX_LOW_Q0);
+		SSTWR16(icpo->cout_attn_enbl, attn_enbl);
+	}
+
+ssp_end:
+	/* all done if SSP64 */
+	if (mpd->mpd_board_def->asic == SSP64) {
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+		dev_dbg(mpd->dev, "eqnx_write: wrote %d bytes for device
%d\n",
+			written, d);
+		return (written);
+	}
+
+	/* now try to fill xmit_buf with the data */
+	while (count) {
+		dev_dbg(mpd->dev, "eqnx_write: fill xmit_buf with %d\n",
count);
+		c = MIN(count, MIN(XMIT_BUF_SIZE - mpc->xmit_cnt - 1,
+				   XMIT_BUF_SIZE - mpc->xmit_head));
+		if (c <= 0)
+			break;
+		b = (unsigned char *)buf;
+
+		if (mpc->xmit_buf == NULL)
+			break;
+		memcpy(mpc->xmit_buf + mpc->xmit_head, b, c);
+		mpc->xmit_head = (mpc->xmit_head + c) & (XMIT_BUF_SIZE -
1);
+		mpc->xmit_cnt += c;
+		buf += c;
+		count -= c;
+		written += c;
+		dev_dbg(mpd->dev, "eqnx_write: filled xmit_buf for
device "
+			"%d count %d\n", d, c);
+	}
+
+	spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+	dev_dbg(mpd->dev, "eqnx_write: wrote %d bytes for device %d\n",
+		written, d);
+
+	return (written);
+}
+
+/*
+ * eqnx_put_char(tty, ch)
+ *
+ * Write single data byte to the tty device.  Note data is queued and
+ * flush call must be done to force to the device.
+ *
+ * tty	= pointer to tty structure.
+ * ch	= byte to write.
+ */
+void eqnx_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	unsigned long flags;
+	int d;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	mpd = mpc->mpc_mpd;
+	if (mpd == (struct mpdev *)NULL)
+		return;
+
+	dev_dbg(mpd->dev, "eqnx_put_char: tty=%d, ch=%x\n", d, (int)ch);
+
+	/* allocate tkcooktty buffer for queueing put_char bytes */
+	if (tty != eqnx_txcooktty) {
+		if (eqnx_txcooktty != (struct tty_struct *)NULL)
+			eqnx_flush_chars(eqnx_txcooktty);
+		eqnx_txcooktty = tty;
+	}
+
+	if (mpd->mpd_board_def->asic != SSP64) {
+		/* SSP4 */
+		spin_lock_irqsave(&mpd->mpd_lock, flags);
+		if (mpc->xmit_head >= (XMIT_BUF_SIZE - 1)) {
+			eqnx_flush_chars_locked(tty);
+			eqnx_txcooktty = tty;
+		}
+		if (mpc->xmit_buf != NULL) {
+			mpc->xmit_buf[mpc->xmit_head++] = ch;
+			mpc->xmit_head &= (XMIT_BUF_SIZE - 1);
+			mpc->xmit_cnt++;
+		}
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+
+		if (eqnx_write_wakeup_deferred) {
+			eqnx_write_wakeup_deferred = 0;
+			tty->ldisc.write_wakeup(tty);
+		}
+	} else {
+		/* SSP64 */
+		if (eqnx_txcooksize >= (XMIT_BUF_SIZE - 1)) {
+			eqnx_flush_chars(eqnx_txcooktty);
+			eqnx_txcooktty = tty;
+		}
+		eqnx_txcookbuf[eqnx_txcooksize++] = ch;
+	}
+}
+
+/*
+ * eqnx_flush_chars_locked(tty)
+ *
+ * Flush (actually drain) data bytes queued by put_char routine.
+ * Helper routine called by eqnx_flush_chars.
+ *
+ * tty	= pointer to tty structure.
+ *
+ * mpdev (board-level) lock ** MUST ** be held.
+ */
+static void eqnx_flush_chars_locked(struct tty_struct *tty)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	unsigned int d;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	mpd = mpc->mpc_mpd;
+	dev_dbg(mpd->dev, "eqnx_flush_chars_locked: tty=%d\n", d);
+
+	if (mpd == (struct mpdev *)NULL)
+		return;
+
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	if (eqnx_txcooktty == NULL)
+		return;
+	else {
+
+#ifdef	DEBUG_LOCKS
+		if (!(spin_is_locked(&mpd->mpd_lock)))
+			dev_dbg(mpd->dev, "eqnx_flush_chars_locked: mpd
"
+				"lock !locked\n");
+#endif
+
+		if (mpd->mpd_board_def->asic == SSP64) {
+			/* SSP64 */
+			unsigned int cooksize = eqnx_txcooksize;
+			eqnx_txcooksize = 0;
+			eqnx_txcookrealsize = 0;
+			eqnx_txcooktty = (struct tty_struct *)NULL;
+			if (cooksize == 0)
+				return;
+			dev_dbg(mpd->dev, "eqnx_flush_chars_locked: "
+				"calling eqnx_write_SSP64\n");
+			eqnx_write_SSP64(mpc, eqnx_txcookbuf, cooksize);
+		} else {
+			/* SSP4 */
+			if (mpc->xmit_cnt == 0)
+				return;
+			dev_dbg(mpd->dev, "eqnx_flush_chars_locked: "
+				"calling eqnx_write_SSP4\n");
+			eqnx_write_SSP4(mpc, EQNX_COOKED);
+		}
+	}
+}
+
+/*
+ * eqnx_flush_chars(tty)
+ *
+ * Flush (actually drain) data bytes queued by put_char routine.
+ *
+ * tty	= pointer to tty structure.
+ *
+ * mpdev (board-level) lock ** MUST NOT ** be held.
+ */
+void eqnx_flush_chars(struct tty_struct *tty)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	unsigned int d;
+	unsigned long flags;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	mpd = mpc->mpc_mpd;
+	if (mpd == (struct mpdev *)NULL)
+		return;
+
+	if (eqnx_txcooktty == NULL)
+		return;
+	else {
+
+#ifdef	DEBUG_LOCKS
+		if (spin_is_locked(&mpd->mpd_lock))
+			dev_dbg(mpd->dev, "eqnx_flush_chars: mpd lock "
+				"already held\n");
+#endif
+
+		spin_lock_irqsave(&mpd->mpd_lock, flags);
+		eqnx_flush_chars_locked(tty);
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+
+		if (eqnx_write_wakeup_deferred) {
+			eqnx_write_wakeup_deferred = 0;
+			tty->ldisc.write_wakeup(tty);
+		}
+	}
+}
+
+/*
+ * eqnx_write_room(tty)
+ *
+ * Return amount of room available for a write.
+ *
+ * tty = pointer to tty structure.
+ */
+int eqnx_write_room(struct tty_struct *tty)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	volatile struct cout_que_struct *icpq;
+	volatile struct icp_out_struct *icpo;
+	unsigned int space;
+	int d;
+	unsigned long flags;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return (-ENODEV);
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return (-ENODEV);
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return (-ENODEV);
+
+	mpd = mpc->mpc_mpd;
+	if (mpd == (struct mpdev *)NULL)
+		return (-ENODEV);
+
+	dev_dbg(mpd->dev, "eqnx_write_room: device = %d\n", d);
+	spin_lock_irqsave(&mpd->mpd_lock, flags);
+
+	if (mpd->mpd_board_def->asic != SSP64) {
+		/* SSP4 */
+		int ret;
+		ret = (XMIT_BUF_SIZE - mpc->xmit_cnt - 1);
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+		if (ret < 0)
+			return (0);
+		return (ret);
+	}
+
+	if (tty == eqnx_txcooktty) {
+		if (eqnx_txcookrealsize != 0) {
+			space = (eqnx_txcookrealsize - eqnx_txcooksize);
+			spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+			return (space);
+		}
+	}
+
+	icpo = mpc->mpc_icpo;
+	icpq = &icpo->cout_q0;
+	space = mpc->mpc_txq.q_size - (mpc->mpc_count +
+				       SSTRD16(icpq->q_data_count)) - 1;
+	if (tty == eqnx_txcooktty) {
+		eqnx_txcookrealsize = space;
+		space -= eqnx_txcooksize;
+	}
+	spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+	return (space);
+}
+
+/*
+ * eqnx_chars_in_buffer(tty)
+ *
+ * Return number of data bytes buffered to be output
+ *
+ * tty	= pointer to tty structure.
+ */
+int eqnx_chars_in_buffer(struct tty_struct *tty)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	unsigned long flags;
+	volatile struct cout_que_struct *icpq;
+	volatile struct icp_out_struct *icpo;
+	unsigned int count;
+	int d;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return (-ENODEV);
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return (-ENODEV);
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return (-ENODEV);
+
+	mpd = mpc->mpc_mpd;
+	if (mpd == (struct mpdev *)NULL)
+		return (-ENODEV);
+
+	spin_lock_irqsave(&mpd->mpd_lock, flags);
+	icpo = mpc->mpc_icpo;
+	icpq = &icpo->cout_q0;
+	count = SSTRD16(icpq->q_data_count);
+
+	/*
+	 * for SSP4, include include internal buffer in addition
+	 * to number of data bytes queued to the SSP
+	 */
+	if (mpc->mpc_mpd->mpd_board_def->asic != SSP64)
+		count += mpc->xmit_cnt;
+	spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+	dev_dbg(mpd->dev, "eqnx_chars_in_buffer: count for device %d = "
+		"%d\n", d, count);
+	return (count);
+}
+
+/*
+ * eqnx_throttle(tty)
+ *
+ * Stop queuing input to the line discipline.
+ *
+ * tty	= pointer to tty structure.
+ */
+void eqnx_throttle(struct tty_struct *tty)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	int d;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	mpd = mpc->mpc_mpd;
+	dev_dbg(mpd->dev, "eqnx_throttle: device %d\n",
+		SSTMINOR(mpc->mpc_major, mpc->mpc_minor));
+	mpc->mpc_block = true;
+}
+
+/*
+ * eqnx_unthrottle(tty)
+ *
+ * Resume queuing input to the line discipline.
+ *
+ * tty	= pointer to tty structure.
+ */
+void eqnx_unthrottle(struct tty_struct *tty)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	int d;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	mpd = mpc->mpc_mpd;
+	dev_dbg(mpd->dev, "eqnx_unthrottle: device %d\n",
+		SSTMINOR(mpc->mpc_major, mpc->mpc_minor));
+	mpc->mpc_block = false;
+}
+
+/*
+ * eqnx_flush_buffer_locked(tty)
+ *
+ * Flush all input and output queued data.
+ * Helper routine called by eqnx_flush_buffer.
+ *
+ * tty	= pointer to tty structure.
+ *
+ * mpdev (board-level) lock ** MUST ** be held
+ */
+void eqnx_flush_buffer_locked(struct tty_struct *tty)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	volatile struct icp_in_struct *icpi;
+	volatile struct icp_out_struct *icpo;
+	volatile struct cout_que_struct *icpq;
+	volatile struct cin_bnk_struct *icpb;
+	u8 cin;
+	u16 nxt_dma;
+	int d;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	mpd = mpc->mpc_mpd;
+	if (mpd == (struct mpdev *)NULL)
+		return;
+
+#ifdef	DEBUG_LOCKS
+	if (!(spin_is_locked(&mpd->mpd_lock)))
+		dev_dbg(mpd->dev, "eqnx_flush_buffer_locked: mpd lock
not "
+			"locked\n");
+#endif
+
+	icpi = mpc->mpc_icpi;
+	icpo = mpc->mpc_icpo;
+	icpq = &icpo->cout_q0;
+
+	dev_dbg(mpd->dev, "eqnx_flush_buffer_locked: device = %d\n", d);
+	if (tty == eqnx_txcooktty) {
+		eqnx_txcooktty = (struct tty_struct *)NULL;
+		eqnx_txcooksize = 0;
+		eqnx_txcookrealsize = 0;
+	}
+
+	/* flush transmit buffers */
+	icpo->cout_lck_cntrl |= LCK_Q_ACT;
+	eqnx_chnl_sync(mpc);
+	icpo->cout_dma_stat_save = 0;
+	icpo->cout_intnl_fifo_ptrs = 0;
+	SSTWR16(icpq->q_data_count, 0);
+	mpc->mpc_count = 0;
+	SSTWR16(icpq->q_data_ptr_l, (mpc->mpc_txq.q_begin +
mpc->mpc_txbase));
+	mpc->mpc_txq.q_ptr = mpc->mpc_txq.q_begin;
+	mpc->xmit_cnt = mpc->xmit_head = mpc->xmit_tail = 0;
+	SSTWR16(icpo->cout_attn_enbl, EN_REG_UPDT_EV);
+	icpo->cout_lck_cntrl &= ~LCK_Q_ACT;
+	mpc->mpc_flags &= ~MPC_BUSY;
+
+	/* flush receiver queue */
+	/* set rx ptr = tail */
+	icpb = (icpi->cin_locks & LOCK_A) ? &icpi->cin_bank_b :
+	    &icpi->cin_bank_a;
+	/* wait for valid registers */
+	if (!(SSTRD16(icpb->bank_events) & EV_REG_UPDT))
+		eqnx_frame_wait(mpc, 2);
+	cin = icpi->cin_locks;
+	icpi->cin_locks = cin | (DIS_BANK_A | DIS_BANK_B);
+	eqnx_chnl_sync(mpc);
+	icpi->cin_bank_a.bank_fifo_lvl &= ~0x8f;
+	icpi->cin_bank_b.bank_fifo_lvl &= ~0x8f;
+	nxt_dma = SSTRD16(icpb->bank_nxt_dma);
+	SSTWR16(icpi->cin_tail_ptr_a, nxt_dma);
+	SSTWR16(icpi->cin_tail_ptr_b, nxt_dma);
+	mpc->mpc_rxq.q_ptr = nxt_dma;
+	icpi->cin_locks = cin;
+	mpc->mpc_flags |= MPC_RXFLUSH;
+	mpc->mpc_block = false;
+	wake_up_interruptible(&tty->write_wait);
+
+	/* signal write wakeup when safe to call ldisc */
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		eqnx_write_wakeup_deferred++;
+}
+
+/*
+ * eqnx_flush_buffer(tty)
+ *
+ * Flush all input and output queued data.
+ *
+ * tty	= pointer to tty structure.
+ *
+ * mpdev (board-level) lock ** MUST NOT ** be held
+ */
+void eqnx_flush_buffer(struct tty_struct *tty)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	unsigned long flags;
+	int d;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	mpd = mpc->mpc_mpd;
+	if (mpd == (struct mpdev *)NULL)
+		return;
+
+#ifdef	DEBUG_LOCKS
+	if (spin_is_locked(&mpd->mpd_lock))
+		dev_dbg(mpd->dev, "eqnx_flush_buffer: mpd lock already "
+			"held\n");
+#endif
+	spin_lock_irqsave(&mpd->mpd_lock, flags);
+	eqnx_flush_buffer_locked(tty);
+	spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+
+	if (eqnx_write_wakeup_deferred) {
+		eqnx_write_wakeup_deferred = 0;
+		tty->ldisc.write_wakeup(tty);
+	}
+}
+
+/*
+ * eqnx_stop(tty)
+ *
+ * Stop queueing data bytes to be output.
+ *
+ * tty	= pointer to tty structure.
+ */
+void eqnx_stop(struct tty_struct *tty)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	volatile struct icp_out_struct *icpo;
+	unsigned long flags;
+	int d;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	mpd = mpc->mpc_mpd;
+	if (mpd == (struct mpdev *)NULL)
+		return;
+
+	icpo = mpc->mpc_icpo;
+	dev_dbg(mpd->dev, "eqnx_stop: device %d\n", d);
+
+	spin_lock_irqsave(&mpd->mpd_lock, flags);
+
+	/* suspend output */
+	icpo->cout_cpu_req |= CPU_PAUSE;
+	spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+}
+
+/*
+ * eqnx_start(tty)
+ *
+ * Re-start queueing data bytes to be output.
+ *
+ * tty	= pointer to tty structure.
+ */
+void eqnx_start(struct tty_struct *tty)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	volatile struct icp_out_struct *icpo;
+	unsigned long flags;
+	int d;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	mpd = mpc->mpc_mpd;
+	if (mpd == (struct mpdev *)NULL)
+		return;
+
+	icpo = mpc->mpc_icpo;
+	dev_dbg(mpd->dev, "eqnx_start: device %d\n", d);
+
+	spin_lock_irqsave(&mpd->mpd_lock, flags);
+
+	/* resume output */
+	icpo->cout_cpu_req &= ~CPU_PAUSE;
+	spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+}
+
+/*
+ * eqnx_hangup(tty)
+ *
+ * Hangup tty device.
+ * Drop control signals, flush buffer, and close port.
+ *
+ * tty	= pointer to tty structure.
+ */
+void eqnx_hangup(struct tty_struct *tty)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	unsigned long flags;
+	int d;
+	u16 attn;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	mpd = mpc->mpc_mpd;
+	if (mpd == (struct mpdev *)NULL)
+		return;
+
+	dev_dbg(mpd->dev, "eqnx_hangup: device %d\n", d);
+
+	spin_lock_irqsave(&mpd->mpd_lock, flags);
+
+	mpc->flags &= ~ASYNC_INITIALIZED;
+	if (mpc->flags & ASYNC_NORMAL_ACTIVE)
+		*mpc->normaltermios = *tty->termios;
+	if (tty == eqnx_txcooktty)
+		eqnx_flush_chars_locked(tty);
+	if (tty->termios->c_cflag & HUPCL)
+		eqnx_modem(d, TURNOFF);
+
+	mpc->mpc_flags &= ~(MPC_SOFTCAR | MPC_DIALOUT | MPC_MODEM |
+			    MPC_DIALIN | MPC_CTS);
+	attn = SSTRD16(mpc->mpc_icpi->cin_attn_ena);
+	attn &= ~ENA_DCD_CNG;
+	SSTWR16(mpc->mpc_icpi->cin_attn_ena, attn);
+	chanoff(mpc);
+	eqnx_flush_buffer_locked(tty);
+	set_bit(TTY_IO_ERROR, &tty->flags);
+	mpc->flags &= ~ASYNC_NORMAL_ACTIVE;
+	mpc->mpc_tty = (struct tty_struct *)NULL;
+	mpc->refcount = 0;
+
+	/* reset SSP4 xmit buf variables */
+	if ((mpd->mpd_board_def->asic != SSP64) && (mpc->xmit_buf)) {
+		free_page((unsigned long)mpc->xmit_buf);
+		mpc->xmit_buf = NULL;
+		mpc->xmit_cnt = 0;
+		mpc->xmit_head = 0;
+		mpc->xmit_tail = 0;
+	}
+	mpc->open_wait_wait = 0;
+	wake_up_interruptible(&mpc->open_wait);
+	spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+
+	if (eqnx_write_wakeup_deferred) {
+		eqnx_write_wakeup_deferred = 0;
+		tty->ldisc.write_wakeup(tty);
+	}
+}
+
+/*
+ * eqnx_set_termios(tty, old)
+ *
+ * Device termios structure has been modified.
+ *
+ * tty	= pointer to tty structure.
+ * old	= previous termios settings.
+ */
+void eqnx_set_termios(struct tty_struct *tty, struct termios *old)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	int d;
+	struct termios *tiosp;
+	unsigned long flags;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return;
+
+	mpd = mpc->mpc_mpd;
+	if (mpd == (struct mpdev *)NULL)
+		return;
+
+	dev_dbg(mpd->dev, "eqnx_set_termios: device %d\n", d);
+
+	tiosp = tty->termios;
+	if ((tiosp->c_cflag == old->c_cflag) &&
+	    (tiosp->c_iflag == old->c_iflag) &&
+	    (tiosp->c_cc[VSTOP] == old->c_cc[VSTOP]) &&
+	    (tiosp->c_cc[VSTART] == old->c_cc[VSTART]))
+		return;
+
+	spin_lock_irqsave(&mpd->mpd_lock, flags);
+	dev_dbg(mpd->dev, "eqnx_set_termios: calling megaparam dev
%d\n", d);
+	eqnx_megaparam(d);
+	if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) ==
0))
+		tty->hw_stopped = 0;
+	if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL))
{
+		mpc->carr_state = true;
+		mpc->open_wait_wait = 0;
+		wake_up_interruptible(&mpc->open_wait);
+	}
+	spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+}
+
+/*
+ * eqnx_tiocmget(tty, file)
+ *
+ * return state of control signals.
+ *
+ * tty	= pointer to tty structure.
+ * file	= pointer to file structure.
+ */
+int eqnx_tiocmget(struct tty_struct *tty, struct file *file)
+{
+	u32 result = 0;
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	int d;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return -ENODEV;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return -ENODEV;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return -ENODEV;
+
+	mpd = mpc->mpc_mpd;
+	dev_dbg(mpd->dev, "eqnx_tiocmget: device %d\n", d);
+
+	result = eqnx_get_modem_info(mpc);
+	return result;
+}
+
+/*
+ * eqnx_tiocmset(tty, file, set, clear)
+ *
+ * set state of control signals.
+ *
+ * tty	= pointer to tty structure.
+ * file	= pointer to file structure.
+ * set	= control signals to set.
+ * clear = control signals to clear.
+ */
+int eqnx_tiocmset(struct tty_struct *tty, struct file *file,
+		  unsigned int set, unsigned int clear)
+{
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	int d;
+
+	/* channel validity checks */
+	if (tty == (struct tty_struct *)NULL)
+		return -ENODEV;
+	mpc = (struct mpchan *)tty->driver_data;
+	if (mpc == (struct mpchan *)NULL)
+		return -ENODEV;
+
+	d = SSTMINOR(mpc->mpc_major, mpc->mpc_minor);
+	if (d > (eqnx_nssps * MAXCHNL_BRD))
+		return -ENODEV;
+
+	mpd = mpc->mpc_mpd;
+	dev_dbg(mpd->dev, "eqnx_tiocmset: device %d, set = %x, clear =
%x\n",
+		d, set, clear);
+
+	eqnx_set_modem_info(mpc, TIOCMBIS, set, tty);
+	eqnx_set_modem_info(mpc, TIOCMBIC, clear, tty);
+	return 0;
+}
+
+/*
+ * chan_alive(index)
+ *
+ * return true if channel alive
+ */
+static inline int chan_alive(int index)
+{
+	int chan;
+	struct mpchan *mpc;
+	struct mpdev *mpd;
+	struct icp_struct *icp;
+	volatile struct icp_in_struct *icpi;
+	volatile struct cin_bnk_struct *icpb;
+
+	chan = index % MAXCHNL_BRD;
+	mpc = &eqnx_chan[index];
+	if (mpc == NULL)
+		return false;
+
+	mpd = mpc->mpc_mpd;
+	if ((mpd == NULL) || (chan > mpd->mpd_nchan))
+		return false;
+
+	if (!(mpd->mpd_alive))
+		return false;
+
+	icp = &mpd->icp[chan / mpd->mpd_sspchan];
+	if (icp->icp_rng_state != RNG_GOOD)
+		return false;
+
+	if (mpd->mpd_board_def->asic == SSP64) {
+		icpi = mpc->mpc_icpi;
+		icpb = (icpi->cin_locks & LOCK_A) ? &icpi->cin_bank_b :
+		    &icpi->cin_bank_a;
+		if ((!(SSTRD16(icpb->bank_signals) & LMX_PRESENT)) ||
+		    (icp->lmx[mpc->mpc_lmxno].lmx_active != DEV_GOOD))
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * chanon(mpc)
+ *
+ * Turn on a channel.
+ *
+ * mpc	= pointer to channel structure.
+ *
+ * mpdev (board-level) lock ** MUST ** be held.
+ */
+static void chanon(struct mpchan *mpc)
+{
+	volatile struct icp_in_struct *icpi = mpc->mpc_icpi;
+	volatile struct icp_out_struct *icpo = mpc->mpc_icpo;
+	volatile union global_regs_u *icpg;
+	volatile struct cout_que_struct *icpq = &icpo->cout_q0;
+	volatile struct cin_bnk_struct *icpb;
+	unsigned short cur;
+	u8 cin;
+	u16 nxt_dma, attn_ena, status, attn_enbl;
+#ifdef	DEBUG_LOCKS
+	struct mpdev *mpd = mpc->mpc_mpd;
+
+	if (!(spin_is_locked(&mpc->mpc_mpd->mpd_lock)))
+		dev_dbg(mpd->dev, "chanon: mpd lock !locked\n");
+#endif
+
+	if (mpc->mpc_mpd->mpd_board_def->asic == SSP64)
+		icpg = (volatile union global_regs_u *)icpo;
+	else
+		icpg = (volatile union global_regs_u *)
+		    ((unsigned long)(mpc->mpc_icpi) + 0x400);
+
+	icpb = (icpi->cin_locks & LOCK_A) ? &icpi->cin_bank_b :
+	    &icpi->cin_bank_a;
+	/* make sure registers are valid */
+	if (!(SSTRD16(icpb->bank_events) & EV_REG_UPDT))
+		eqnx_frame_wait(mpc, 2);
+
+	if ((SSTRD16(icpb->bank_signals) & (LMX_ONLN | LMX_PRESENT |
+					    AMI_CNFG)) ==
+	    (LMX_ONLN | LMX_PRESENT | AMI_CNFG)) {
+		/* if bridge, stop output on MUX disconnect */
+		icpi->cin_susp_output_lmx |= MUX_NOT_CONN;
+		icpo->cout_lmx_command |= TX_TRGT_MUX;
+	} else {
+		/* local panel */
+		icpi->cin_susp_output_lmx &= ~MUX_NOT_CONN;
+		icpo->cout_lmx_command &= ~TX_TRGT_MUX;
+	}
+
+	/* lock output */
+	icpo->cout_lck_cntrl |= LCK_Q_ACT;
+
+	/* lock input */
+	cin = icpi->cin_locks;
+	icpi->cin_locks = cin | DIS_BANK_A | DIS_BANK_B;
+	eqnx_chnl_sync(mpc);
+
+	icpi->cin_bank_a.bank_fifo_lvl &= ~0x8f;
+	icpi->cin_bank_b.bank_fifo_lvl &= ~0x8f;
+	nxt_dma = SSTRD16(icpb->bank_nxt_dma);
+	SSTWR16(icpi->cin_tail_ptr_a, nxt_dma);
+	SSTWR16(icpi->cin_tail_ptr_b, nxt_dma);
+	mpc->mpc_rxq.q_ptr = nxt_dma;
+	SSTWR16(icpi->cin_min_char_lvl, 1);
+
+	/* unlock input */
+	attn_ena = SSTRD16(icpi->cin_attn_ena);
+	attn_ena |= (ENA_DMA_FAIL | ENA_IXOFF_SVS | ENA_VMIN);
+	SSTWR16(icpi->cin_attn_ena, attn_ena);
+	icpi->cin_locks = cin;
+	icpo->cout_dma_stat_save = 0;
+	icpo->cout_intnl_fifo_ptrs = 0;
+	SSTWR16(icpq->q_data_count, 0);
+	SSTWR16(icpq->q_data_ptr_l, (mpc->mpc_txbase +
mpc->mpc_txq.q_begin));
+	icpi->cin_iband_flow_cntrl = 0;
+	mpc->mpc_txq.q_ptr = mpc->mpc_txq.q_begin;
+	mpc->mpc_count = 0;
+	status = SSTRD16(icpo->cout_status);
+	status &= ~TXSR_SND_BRK;
+	SSTWR16(icpo->cout_status, status);
+	GET_CTRL_SIGS(mpc, cur);
+	cur |= TX_HFC_DTR | TX_HFC_RTS;
+	SET_CTRL_SIGS(mpc, cur);
+
+	/* unlock output */
+	attn_enbl = SSTRD16(icpo->cout_attn_enbl);
+	attn_enbl |= EN_REG_UPDT_EV;
+	SSTWR16(icpo->cout_attn_enbl, attn_enbl);
+	icpo->cout_lck_cntrl &= ~LCK_Q_ACT;
+
+	/* calibrate invent char count */
+	mpc->mpc_tx_last_invent = SSTRD16(icpo->cout_ses_char_ctr_a);
+
+	/* Calibrate events and control signals in both banks */
+	FREEZ_BANK(mpc);
+	FREEZ_BANK(mpc);
+	mpc->mpc_cin_events = 0;
+	mpc->mpc_flags |= MPC_OPEN;
+}
+
+/*
+ * chanoff(mpc)
+ *
+ * Turn off a channel.
+ *
+ * mpc	= pointer to channel structure.
+ *
+ * mpdev (board-level) lock ** MUST ** be held.
+ */
+static void chanoff(struct mpchan *mpc)
+{
+	volatile struct icp_in_struct *icpi;
+	volatile struct icp_out_struct *icpo;
+	volatile struct cout_que_struct *icpq;
+	volatile struct cin_bnk_struct *icpb;
+	int loop, ok;
+	u8 cin;
+	u16 events, nxt_dma, attn_ena;
+
+#ifdef	DEBUG_LOCKS
+	if (!(spin_is_locked(&mpc->mpc_mpd->mpd_lock)))
+		dev_dbg(mpc->mpc_mpd->dev, "chanoff: mpd lock
!locked\n");
+#endif
+
+	icpi = mpc->mpc_icpi;
+	icpo = mpc->mpc_icpo;
+	icpq = &icpo->cout_q0;
+	icpb = (icpi->cin_locks & LOCK_A) ? &icpi->cin_bank_b :
+	    &icpi->cin_bank_a;
+	/* make sure registers are valid */
+	if (!(SSTRD16(icpb->bank_events) & EV_REG_UPDT))
+		eqnx_frame_wait(mpc, 2);
+
+	mpc->mpc_param &= (IOCTLLB | IOCTCTS | IOCTLCK | IOCTRTS);
+
+	/* disable events */
+	attn_ena = SSTRD16(icpi->cin_attn_ena);
+	attn_ena &= (ENA_DCD_CNG | ENA_CTS_CNG | ENA_DSR_CNG |
ENA_RI_CNG);
+	if (!(mpc->mpc_chan % 16))
+		attn_ena |= ENA_LMX_CNG;
+	SSTWR16(icpi->cin_attn_ena, attn_ena);
+
+	/* lock output */
+	icpo->cout_lck_cntrl |= LCK_Q_ACT;
+	eqnx_chnl_sync(mpc);
+	icpo->cout_intnl_fifo_ptrs = 0;
+	icpo->cout_dma_stat_save = 0;
+	SSTWR16(icpq->q_data_count, 0);
+	SSTWR16(icpq->q_data_ptr_l, (mpc->mpc_txbase +
mpc->mpc_txq.q_begin));
+	mpc->mpc_txq.q_ptr = mpc->mpc_txq.q_begin;
+	mpc->mpc_count = 0;
+	mpc->carr_state = false;
+
+	/* unlock output and update */
+	icpo->cout_lck_cntrl &= ~LCK_Q_ACT;
+	eqnx_chnl_sync(mpc);
+	if ((icpo->cout_intnl_svd_toggls & TOGGLS_LSEND) ==
+	    (icpo->cout_cpu_req & CPU_SND_REQ))
+		icpo->cout_cpu_req ^= CPU_SND_REQ;
+
+	/* wait for ack */
+	loop = 0;
+	ok = false;
+	while (++loop < 100000) {
+		if (SSTRD16(icpo->cout_status) & TXSR_EV_B_ACT)
+			events = SSTRD16(icpo->cout_events_b);
+		else
+			events = SSTRD16(icpo->cout_events_a);
+		if (events & EV_CPU_REQ_DN) {
+			ok = true;
+			break;
+		}
+	}
+
+	if (!ok)
+		dev_warn(mpc->mpc_mpd->dev, "chanoff: cpu_req ack
failed.\n");
+
+	/* lock input */
+	cin = icpi->cin_locks;
+	icpi->cin_locks = cin | DIS_BANK_A | DIS_BANK_B;
+	eqnx_chnl_sync(mpc);
+	icpi->cin_bank_a.bank_fifo_lvl &= ~0x8f;
+	icpi->cin_bank_b.bank_fifo_lvl &= ~0x8f;
+	nxt_dma = SSTRD16(icpb->bank_nxt_dma);
+	SSTWR16(icpi->cin_tail_ptr_a, nxt_dma);
+	SSTWR16(icpi->cin_tail_ptr_b, nxt_dma);
+	mpc->mpc_rxq.q_ptr = nxt_dma;
+	SSTWR16(icpi->cin_min_char_lvl, 1);
+
+	/* allow overrun check while closed */
+	attn_ena = SSTRD16(icpi->cin_attn_ena);
+	attn_ena |= (ENA_VMIN | ENA_DMA_FAIL);
+	SSTWR16(icpi->cin_attn_ena, attn_ena);
+
+	/* unlock input */
+	icpi->cin_locks = cin;
+	if (icpi->cin_iband_flow_cntrl & XOFF_RCV) {
+		icpi->cin_locks |= DIS_IBAND_FLW;
+		eqnx_chnl_sync(mpc);
+		icpi->cin_iband_flow_cntrl &= ~XOFF_RCV;
+		icpi->cin_locks &= ~DIS_IBAND_FLW;
+	}
+
+	SSTWR16(icpo->cout_attn_enbl, EN_REG_UPDT_EV);
+	mpc->mpc_flags &= ~MPC_OPEN;
+	mpc->mpc_cin_events = 0;
+	mpc->mpc_cout_events = 0;
+}
+
+/**********************************************************************
***
+ *
+ * Output support routines
+ *
+
************************************************************************
*/
+
+/*
+ * eqnx_write_SSP64(mpc, buf, count)
+ * write output data to channel.
+ *
+ * mpc	= pointer to channel structure.
+ * buf	= data buffer.
+ * count= count of bytes to write.
+ *
+ * mpdev (board-level) lock ** MUST ** be held.
+ */
+static void eqnx_write_SSP64(struct mpchan *mpc, unsigned char *buf,
int count)
+{
+	int c, nx, datascope = 0;
+	volatile struct icp_out_struct *icpo;
+	volatile union global_regs_u *icpg;
+	volatile struct cout_que_struct *icpq;
+	unsigned char oldreg;
+	u16 qcnt, attn_enbl;
+	struct mpdev *mpd = mpc->mpc_mpd;
+
+#ifdef	DEBUG_LOCKS
+	if (!(spin_is_locked(&mpc->mpc_mpd->mpd_lock)))
+		dev_dbg(mpd->dev, "eqnx_write_SSP64: mpd lock
locked\n");
+#endif
+
+	icpo = mpc->mpc_icpo;
+	icpg = (volatile union global_regs_u *)icpo;
+	icpq = &icpo->cout_q0;
+
+	c = count;
+	mpc->mpc_flags |= MPC_BUSY;
+	if ((mpc->mpc_flags & MPC_DSCOPEW) &&
+	    (eqnx_dscope[1].chan == mpc - eqnx_chan))
+		datascope = 1;
+
+	/* direct copy to on-board buffers. */
+	nx = MIN(c, (mpc->mpc_txq.q_end - mpc->mpc_txq.q_ptr + 1));
+	memcpy((mpc->mpc_txq.q_addr + mpc->mpc_txq.q_ptr), buf, nx);
+	mpc->mpc_txq.q_ptr += nx;
+	if (mpc->mpc_txq.q_ptr > mpc->mpc_txq.q_end)
+		mpc->mpc_txq.q_ptr = mpc->mpc_txq.q_begin;
+	if (datascope) {
+		int room, move_cnt;
+		int d = 1;
+		char *ubase = buf;
+		room = DSQSIZE - ((eqnx_dscope[d].next -
+				   eqnx_dscope[d].q.q_ptr) & (DSQMASK));
+		if (nx > room)
+			eqnx_dscope[d].status |= DSQWRAP;
+		else
+			room = nx;
+		while (room > 0) {
+			move_cnt = MIN(room,
+				       eqnx_dscope[d].q.q_end -
+				       eqnx_dscope[d].next + 1);
+			memcpy(eqnx_dscope[d].q.q_addr +
eqnx_dscope[d].next,
+			       ubase, move_cnt);
+			eqnx_dscope[d].next += move_cnt;
+			if (eqnx_dscope[d].next >
eqnx_dscope[d].q.q_end)
+				eqnx_dscope[d].next =
eqnx_dscope[d].q.q_begin;
+			ubase += move_cnt;
+			room -= move_cnt;
+		}
+		eqnx_dscope[d].scope_wait_wait = 0;
+		wake_up_interruptible(&eqnx_dscope[d].scope_wait);
+	}
+
+	buf += nx;
+	count -= nx;
+	c -= nx;
+	if (c) {
+		memcpy(mpc->mpc_txq.q_addr + mpc->mpc_txq.q_ptr, buf,
c);
+		mpc->mpc_txq.q_ptr += c;
+		if (mpc->mpc_txq.q_ptr > mpc->mpc_txq.q_end)
+			mpc->mpc_txq.q_ptr = mpc->mpc_txq.q_begin;
+		if (datascope) {
+			int room, move_cnt;
+			int d = 1;
+			char *ubase = buf;
+			room = DSQSIZE - ((eqnx_dscope[d].next -
+					   eqnx_dscope[d].q.q_ptr) &
(DSQMASK));
+			if (c > room)
+				eqnx_dscope[d].status |= DSQWRAP;
+			else
+				room = c;
+			while (room > 0) {
+				move_cnt = MIN(room,
eqnx_dscope[d].q.q_end -
+					       eqnx_dscope[d].next + 1);
+				memcpy(eqnx_dscope[d].q.q_addr +
+				       eqnx_dscope[d].next, ubase,
move_cnt);
+				eqnx_dscope[d].next += move_cnt;
+				if (eqnx_dscope[d].next >
+				    eqnx_dscope[d].q.q_end)
+					eqnx_dscope[d].next =
+					    eqnx_dscope[d].q.q_begin;
+				ubase += move_cnt;
+				room -= move_cnt;
+			}
+			eqnx_dscope[d].scope_wait_wait = 0;
+
wake_up_interruptible(&eqnx_dscope[d].scope_wait);
+		}
+		buf += c;
+		count -= c;
+	}
+	oldreg = icpo->cout_cpu_req;
+	icpo->cout_cpu_req |= TX_SUSP;
+	eqnx_chnl_sync(mpc);
+	qcnt = SSTRD16(icpq->q_data_count);
+	if (qcnt < 9) {
+		if ((icpo->cout_intnl_svd_toggls & 0x4) ==
+		    (icpo->cout_cpu_req & 0x4)) {
+			icpo->cout_cpu_req ^= (CPU_SND_REQ);
+			mpc->mpc_cout_events &= ~EV_CPU_REQ_DN;
+		}
+	}
+	qcnt += (c + nx);
+	SSTWR16(icpq->q_data_count, qcnt);
+	dev_dbg(mpd->dev, "eqnx_write_SSP64: wrote %d chars for device
%d\n",
+		(c + nx), SSTMINOR(mpc->mpc_major, mpc->mpc_minor));
+	if (!(oldreg & TX_SUSP))
+		icpo->cout_cpu_req &= ~TX_SUSP;
+	mpc->mpc_output += (c + nx);
+	attn_enbl = SSTRD16(icpo->cout_attn_enbl);
+	attn_enbl |= (ENA_TX_EMPTY_Q0 | ENA_TX_LOW_Q0);
+	SSTWR16(icpo->cout_attn_enbl, attn_enbl);
+}
+
+/*
+ * eqnx_write_SSP4(mpc, func_type)
+ * write output data to channel.
+ *
+ * mpc	= pointer to channel structure.
+ * func_type: one of EQNX_COOKED or EQNX_TXINT
+ *
+ * mpdev (board-level) lock ** MUST ** be held.
+ */
+void eqnx_write_SSP4(struct mpchan *mpc, int func_type)
+{
+	struct tty_struct *tty = mpc->mpc_tty;
+	int space, c, nx, datascope = 0, block_count;
+	volatile struct icp_out_struct *icpo;
+	volatile union global_regs_u *icpg;
+	volatile struct cout_que_struct *icpq;
+	unsigned char oldreg;
+	u16 qcnt, attn_enbl;
+	struct mpdev *mpd = mpc->mpc_mpd;
+
+#ifdef	DEBUG_LOCKS
+	if (!(spin_is_locked(&mpc->mpc_mpd->mpd_lock)))
+		dev_dbg(mpd->dev, "eqnx_write_SSP4: mpd lock
!locked\n");
+#endif
+
+	icpo = mpc->mpc_icpo;
+	icpg = (volatile union global_regs_u *)icpo;
+	icpq = &icpo->cout_q0;
+
+	block_count = icpq->q_block_count * 64;
+	space = mpc->mpc_txq.q_size - (mpc->mpc_count +
+				       SSTRD16(icpq->q_data_count)) - 1;
+	if ((func_type != EQNX_TXINT) &&
+	    ((space < block_count) && (mpc->xmit_cnt >= block_count)))
+		return;
+
+	while (1) {
+		c = MIN(space, MIN(mpc->xmit_cnt,
+				   XMIT_BUF_SIZE - mpc->xmit_tail));
+		if (c <= 0)
+			break;
+		mpc->mpc_flags |= MPC_BUSY;
+		if ((mpc->mpc_flags & MPC_DSCOPEW) &&
+		    (eqnx_dscope[1].chan == mpc - eqnx_chan))
+			datascope = 1;
+
+		/* direct copy to on-board buffers. */
+		if (mpc->xmit_buf == NULL)
+			break;
+
+		nx = MIN(c, (mpc->mpc_txq.q_end - mpc->mpc_txq.q_ptr +
1));
+		memcpy((mpc->mpc_txq.q_addr + mpc->mpc_txq.q_ptr),
+		       mpc->xmit_buf + mpc->xmit_tail, nx);
+		mpc->mpc_txq.q_ptr += nx;
+		if (mpc->mpc_txq.q_ptr > mpc->mpc_txq.q_end)
+			mpc->mpc_txq.q_ptr = mpc->mpc_txq.q_begin;
+		if (datascope) {
+			int room, move_cnt;
+			int d = 1;
+			char *ubase = mpc->xmit_buf + mpc->xmit_tail;
+			room = DSQSIZE - ((eqnx_dscope[d].next -
+					   eqnx_dscope[d].q.q_ptr)
+					  & (DSQMASK));
+			if (nx > room)
+				eqnx_dscope[d].status |= DSQWRAP;
+			else
+				room = nx;
+			while (room > 0) {
+				move_cnt = MIN(room,
+					       eqnx_dscope[d].q.q_end -
+					       eqnx_dscope[d].next + 1);
+				memcpy(eqnx_dscope[d].q.q_addr +
+				       eqnx_dscope[d].next, ubase,
move_cnt);
+				eqnx_dscope[d].next += move_cnt;
+				if (eqnx_dscope[d].next >
+				    eqnx_dscope[d].q.q_end)
+					eqnx_dscope[d].next =
+					    eqnx_dscope[d].q.q_begin;
+				ubase += move_cnt;
+				room -= move_cnt;
+			}
+			eqnx_dscope[d].scope_wait_wait = 0;
+
wake_up_interruptible(&eqnx_dscope[d].scope_wait);
+		}
+		mpc->xmit_tail += nx;
+		mpc->xmit_tail &= XMIT_BUF_SIZE - 1;
+		mpc->xmit_cnt -= nx;
+		c -= nx;
+		if (mpc->xmit_buf == NULL)
+			break;
+
+		if (c) {
+			memcpy(mpc->mpc_txq.q_addr + mpc->mpc_txq.q_ptr,
+			       mpc->xmit_buf + mpc->xmit_tail, c);
+			mpc->mpc_txq.q_ptr += c;
+			if (mpc->mpc_txq.q_ptr > mpc->mpc_txq.q_end)
+				mpc->mpc_txq.q_ptr =
mpc->mpc_txq.q_begin;
+			if (datascope) {
+				int room, move_cnt;
+				int d = 1;
+				char *ubase = mpc->xmit_buf +
mpc->xmit_tail;
+				room = DSQSIZE - ((eqnx_dscope[d].next -
+
eqnx_dscope[d].q.q_ptr) &
+						  (DSQMASK));
+				if (c > room)
+					eqnx_dscope[d].status |=
DSQWRAP;
+				else
+					room = c;
+				while (room > 0) {
+					move_cnt = MIN(room,
+
eqnx_dscope[d].q.q_end -
+
eqnx_dscope[d].next + 1);
+					memcpy(eqnx_dscope[d].q.q_addr +
+					       eqnx_dscope[d].next,
ubase,
+					       move_cnt);
+					eqnx_dscope[d].next += move_cnt;
+					if (eqnx_dscope[d].next >
+					    eqnx_dscope[d].q.q_end)
+						eqnx_dscope[d].next =
+
eqnx_dscope[d].q.q_begin;
+					ubase += move_cnt;
+					room -= move_cnt;
+				}
+				eqnx_dscope[d].scope_wait_wait = 0;
+				wake_up_interruptible(&eqnx_dscope[d].
+						      scope_wait);
+			}
+			mpc->xmit_tail += c;
+			mpc->xmit_tail &= XMIT_BUF_SIZE - 1;
+			mpc->xmit_cnt -= c;
+		}
+		oldreg = icpo->cout_cpu_req;
+		icpo->cout_cpu_req |= TX_SUSP;
+		eqnx_chnl_sync(mpc);
+		qcnt = SSTRD16(icpq->q_data_count);
+		if (qcnt < 9) {
+			if ((icpo->cout_intnl_svd_toggls & 0x4) ==
+			    (icpo->cout_cpu_req & 0x4)) {
+				icpo->cout_cpu_req ^= (CPU_SND_REQ);
+				mpc->mpc_cout_events &= ~EV_CPU_REQ_DN;
+			}
+		}
+		qcnt += (c + nx);
+		SSTWR16(icpq->q_data_count, qcnt);
+		space -= (c + nx);
+		dev_dbg(mpd->dev, "eqnx_write_SSP4: wrote %d chars for "
+			"device %d\n", (c + nx),
+			SSTMINOR(mpc->mpc_major, mpc->mpc_minor));
+		if (!(oldreg & TX_SUSP))
+			icpo->cout_cpu_req &= ~TX_SUSP;
+		mpc->mpc_output += (c + nx);
+		attn_enbl = SSTRD16(icpo->cout_attn_enbl);
+		attn_enbl |= (ENA_TX_EMPTY_Q0 | ENA_TX_LOW_Q0);
+		SSTWR16(icpo->cout_attn_enbl, attn_enbl);
+	}
+	if ((mpc->xmit_cnt < block_count) && (func_type == EQNX_TXINT))
{
+		dev_dbg(mpd->dev, "eqnx_write_SSP4: calling write_wakeup
"
+			"for device = %d\n",
+			SSTMINOR(mpc->mpc_major, mpc->mpc_minor));
+		/* signal write wakeup when safe to call ldisc */
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			eqnx_write_wakeup_deferred++;
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+/*
+ * delay_jiffies(len)
+ *
+ * Delay by the specified number of jiffies.
+ *
+ * len	= jiffies to delay.
+ */
+static void delay_jiffies(int len)
+{
+	if (len > 0) {
+		msleep(jiffies_to_msecs(len));
+	}
+}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux