cy_isa.c revision 6261
16261Sjkh/*
26261Sjkh * cyclades cyclom-y serial driver
36261Sjkh *	Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993
46261Sjkh *
56261Sjkh * Copyright (c) 1993 Andrew Herbert.
66261Sjkh * All rights reserved.
76261Sjkh *
86261Sjkh * Redistribution and use in source and binary forms, with or without
96261Sjkh * modification, are permitted provided that the following conditions
106261Sjkh * are met:
116261Sjkh * 1. Redistributions of source code must retain the above copyright
126261Sjkh *    notice, this list of conditions and the following disclaimer.
136261Sjkh * 2. Redistributions in binary form must reproduce the above copyright
146261Sjkh *    notice, this list of conditions and the following disclaimer in the
156261Sjkh *    documentation and/or other materials provided with the distribution.
166261Sjkh * 3. The name Andrew Herbert may not be used to endorse or promote products
176261Sjkh *    derived from this software without specific prior written permission.
186261Sjkh *
196261Sjkh * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
206261Sjkh * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
216261Sjkh * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
226261Sjkh * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
236261Sjkh * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
246261Sjkh * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
256261Sjkh * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
266261Sjkh * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
276261Sjkh * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
286261Sjkh * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
296261Sjkh *
306261Sjkh *	$Id: cy.c,v 1.7 1994/05/24 07:31:12 mycroft Exp $
316261Sjkh */
326261Sjkh
336261Sjkh/*
346261Sjkh * Device minor number encoding:
356261Sjkh *
366261Sjkh *	c c x x u u u u		- bits in the minor device number
376261Sjkh *
386261Sjkh *	bits	meaning
396261Sjkh *	----	-------
406261Sjkh *	uuuu	physical serial line (i.e. unit) to use
416261Sjkh *			0-7 on a cyclom-8Y, 0-15 on a cyclom-16Y
426261Sjkh *	xx	unused
436261Sjkh *	cc	carrier control mode
446261Sjkh *			00	complete hardware carrier control of the tty.
456261Sjkh *				DCD must be high for the open(2) to complete.
466261Sjkh *			01	dialin pseudo-device (not yet implemented)
476261Sjkh *			10	carrier ignored until a high->low transition
486261Sjkh *			11	carrier completed ignored
496261Sjkh */
506261Sjkh
516261Sjkh/*
526261Sjkh * Known deficiencies:
536261Sjkh *
546261Sjkh *	* no BREAK handling - breaks are ignored, and can't be sent either
556261Sjkh *	* no support for bad-char reporting, except via PARMRK
566261Sjkh *	* no support for dialin + dialout devices
576261Sjkh */
586261Sjkh
596261Sjkh#include "cy.h"
606261Sjkh#if NCY > 0
616261Sjkh
626261Sjkh/* This disgusing hack because we actually have 16 units on one controller */
636261Sjkh#if NCY < 2
646261Sjkh#undef NCY
656261Sjkh#define NCY (16)
666261Sjkh#endif
676261Sjkh
686261Sjkh#include <sys/param.h>
696261Sjkh#include <sys/systm.h>
706261Sjkh#include <sys/kernel.h>
716261Sjkh#include <sys/malloc.h>
726261Sjkh#include <sys/ioctl.h>
736261Sjkh#include <sys/tty.h>
746261Sjkh#include <sys/proc.h>
756261Sjkh#include <sys/user.h>
766261Sjkh#include <sys/conf.h>
776261Sjkh#include <sys/file.h>
786261Sjkh#include <sys/uio.h>
796261Sjkh#include <sys/kernel.h>
806261Sjkh#include <sys/syslog.h>
816261Sjkh
826261Sjkh#include <machine/cpu.h>
836261Sjkh#ifdef NetBSD
846261Sjkh#include <machine/pio.h>
856261Sjkh#endif
866261Sjkh#include <machine/cpufunc.h>
876261Sjkh
886261Sjkh#include <i386/isa/isa_device.h>
896261Sjkh#include <i386/isa/ic/cd1400.h>
906261Sjkh
916261Sjkh#define RxFifoThreshold	3	/* 3 characters (out of 12) in the receive
926261Sjkh				 * FIFO before an interrupt is generated
936261Sjkh				 */
946261Sjkh#define	FastRawInput	/* bypass the regular char-by-char canonical input
956261Sjkh			 * processing whenever possible
966261Sjkh			 */
976261Sjkh#define	PollMode	/* use polling-based irq service routine, not the
986261Sjkh			 * hardware svcack lines.  Must be defined for
996261Sjkh			 * cyclom-16y boards.
1006261Sjkh			 *
1016261Sjkh			 * XXX cyclom-8y doesn't work without this defined
1026261Sjkh			 * either (!)
1036261Sjkh			 */
1046261Sjkh#define	LogOverruns	/* log receive fifo overruns */
1056261Sjkh#undef	TxBuffer	/* buffer driver output, to be slightly more
1066261Sjkh			 * efficient
1076261Sjkh			 *
1086261Sjkh			 * XXX presently buggy
1096261Sjkh			 */
1106261Sjkh#undef	Smarts		/* enable slightly more CD1400 intelligence.  Mainly
1116261Sjkh			 * the output CR/LF processing, plus we can avoid a
1126261Sjkh			 * few checks usually done in ttyinput().
1136261Sjkh			 *
1146261Sjkh			 * XXX not yet implemented, and not particularly
1156261Sjkh			 * worthwhile either.
1166261Sjkh			 */
1176261Sjkh#define CyDebug		/* include debugging code (minimal effect on
1186261Sjkh			 * performance)
1196261Sjkh			 */
1206261Sjkh
1216261Sjkh#define CY_RX_BUFS		2	/* two receive buffers per port */
1226261Sjkh#define	CY_RX_BUF_SIZE		256	/* bytes per receive buffer */
1236261Sjkh#define	CY_TX_BUF_SIZE		512	/* bytes per transmit buffer */
1246261Sjkh
1256261Sjkh/* #define CD1400s_PER_CYCLOM	1 */	/* cyclom-4y */
1266261Sjkh/* #define CD1400s_PER_CYCLOM	2 */	/* cyclom-8y */
1276261Sjkh#define CD1400s_PER_CYCLOM	4	/* cyclom-16y */
1286261Sjkh
1296261Sjkh/* FreeBSD's getty doesn't know option for setting RTS/CTS handshake.  Its
1306261Sjkh   getty, like a lot of other old cruft, should be replaced with something
1316261Sjkh   which used POSIX tty interfaces which at least allow enabling it.  In the
1326261Sjkh   meantime, use the force. */
1336261Sjkh#define ALWAYS_RTS_CTS	1
1346261Sjkh
1356261Sjkh#if CD1400s_PER_CYCLOM < 4
1366261Sjkh#define CD1400_MEMSIZE		0x400	/* 4*256 bytes per chip: cyclom-[48]y */
1376261Sjkh#else
1386261Sjkh#define CD1400_MEMSIZE		0x100	/* 256 bytes per chip: cyclom-16y */
1396261Sjkh					/* XXX or is it 0x400 like the rest? */
1406261Sjkh#define CYCLOM_16	1		/* This is a cyclom-16Y */
1416261Sjkh#endif
1426261Sjkh
1436261Sjkh#define PORTS_PER_CYCLOM	(CD1400_NO_OF_CHANNELS * CD1400s_PER_CYCLOM)
1446261Sjkh#define CYCLOM_RESET_16		0x1400			/* cyclom-16y reset */
1456261Sjkh#define CYCLOM_CLEAR_INTR	0x1800			/* intr ack address */
1466261Sjkh#define CYCLOM_CLOCK		25000000		/* baud rate clock */
1476261Sjkh
1486261Sjkh#define	CY_UNITMASK		0x0f
1496261Sjkh#define	CY_CARRIERMASK		0xC0
1506261Sjkh#define CY_CARRIERSHIFT		6
1516261Sjkh
1526261Sjkh#define	UNIT(x)		(minor(x) & CY_UNITMASK)
1536261Sjkh#define	CARRIER_MODE(x)	((minor(x) & CY_CARRIERMASK) >> CY_CARRIERSHIFT)
1546261Sjkh
1556261Sjkhtypedef u_char * volatile cy_addr;
1566261Sjkh
1576261Sjkhint		cyprobe(struct isa_device *dev);
1586261Sjkhint		cyattach(struct isa_device *isdp);
1596261Sjkhvoid		cystart(struct tty *tp);
1606261Sjkhint		cyparam(struct tty *tp, struct termios *t);
1616261Sjkhint		cyspeed(int speed, int *prescaler_io);
1626261Sjkhstatic void	cy_channel_init(dev_t dev, int reset);
1636261Sjkhstatic void	cd1400_channel_cmd(cy_addr base, u_char cmd);
1646261Sjkh
1656261Sjkh/* hsu@clinet.fi: sigh */
1666261Sjkh#ifdef __NetBSD__
1676261Sjkh#define DELAY(foo) delay(foo)
1686261Sjkhvoid		delay(int delay);
1696261Sjkh#endif
1706261Sjkh
1716261Sjkh/* Better get rid of this until the core people agree on kernel interfaces.
1726261Sjkh   At least it will then compile on both WhichBSDs.
1736261Sjkh */
1746261Sjkh#if 0
1756261Sjkhextern unsigned int	delaycount;	/* calibrated 1 ms cpu-spin delay */
1766261Sjkh#endif
1776261Sjkh
1786261Sjkhstruct	isa_driver cydriver = {
1796261Sjkh	cyprobe, cyattach, "cy"
1806261Sjkh};
1816261Sjkh
1826261Sjkh/* low-level ping-pong buffer structure */
1836261Sjkh
1846261Sjkhstruct cy_buf {
1856261Sjkh	u_char		*next_char;	/* location of next char to write */
1866261Sjkh	u_int		free;		/* free chars remaining in buffer */
1876261Sjkh	struct cy_buf	*next_buf;	/* circular, you know */
1886261Sjkh	u_char		buf[CY_RX_BUF_SIZE];	/* start of the buffer */
1896261Sjkh};
1906261Sjkh
1916261Sjkh/* low-level ring buffer */
1926261Sjkh
1936261Sjkh#ifdef TxBuffer
1946261Sjkhstruct cy_ring {
1956261Sjkh	u_char		buf[CY_TX_BUF_SIZE];
1966261Sjkh	u_char		*head;
1976261Sjkh	u_char		*tail;		/* next pos. to insert char */
1986261Sjkh	u_char		*endish;	/* physical end of buf */
1996261Sjkh	u_int		used;		/* no. of chars in queue */
2006261Sjkh};
2016261Sjkh#endif
2026261Sjkh
2036261Sjkh
2046261Sjkh/*
2056261Sjkh * define a structure to keep track of each serial line
2066261Sjkh */
2076261Sjkh
2086261Sjkhstruct cy {
2096261Sjkh	cy_addr		base_addr;	/* base address of this port's cd1400 */
2106261Sjkh	struct tty	*tty;
2116261Sjkh	u_int		dtrwait;	/* time (in ticks) to hold dtr low after close */
2126261Sjkh	u_int		recv_exception;	/* exception chars received */
2136261Sjkh	u_int		recv_normal;	/* normal chars received */
2146261Sjkh	u_int		xmit;		/* chars transmitted */
2156261Sjkh	u_int		mdm;		/* modem signal changes */
2166261Sjkh#ifdef CyDebug
2176261Sjkh	u_int		start_count;	/* no. of calls to cystart() */
2186261Sjkh	u_int		start_real;	/* no. of calls that did something */
2196261Sjkh#endif
2206261Sjkh	u_char		carrier_mode;	/* hardware carrier handling mode */
2216261Sjkh					/*
2226261Sjkh					 * 0 = always use
2236261Sjkh					 * 1 = always use (dialin port)
2246261Sjkh					 * 2 = ignore during open, then use it
2256261Sjkh					 * 3 = ignore completely
2266261Sjkh					 */
2276261Sjkh	u_char		carrier_delta;	/* true if carrier has changed state */
2286261Sjkh	u_char		fifo_overrun;	/* true if cd1400 receive fifo has... */
2296261Sjkh	u_char		rx_buf_overrun;	/* true if low-level buf overflow */
2306261Sjkh	u_char		intr_enable;	/* CD1400 SRER shadow */
2316261Sjkh	u_char		modem_sig;	/* CD1400 modem signal shadow */
2326261Sjkh	u_char		channel_control;/* CD1400 CCR control command shadow */
2336261Sjkh	u_char		cor[3];		/* CD1400 COR1-3 shadows */
2346261Sjkh#ifdef Smarts
2356261Sjkh	u_char		spec_char[4];	/* CD1400 SCHR1-4 shadows */
2366261Sjkh#endif
2376261Sjkh	struct cy_buf	*rx_buf;		/* current receive buffer */
2386261Sjkh	struct cy_buf	rx_buf_pool[CY_RX_BUFS];/* receive ping-pong buffers */
2396261Sjkh#ifdef TxBuffer
2406261Sjkh	struct cy_ring	tx_buf;		/* transmit buffer */
2416261Sjkh#endif
2426261Sjkh};
2436261Sjkh
2446261Sjkhint	cydefaultrate = TTYDEF_SPEED;
2456261Sjkhcy_addr	cyclom_base;			/* base address of the card */
2466261Sjkhstatic	struct cy *info[NCY*PORTS_PER_CYCLOM];
2476261Sjkhstruct	tty *cy_tty[NCY*PORTS_PER_CYCLOM];
2486261Sjkhstatic	volatile u_char timeout_scheduled = 0;	/* true if a timeout has been scheduled */
2496261Sjkh
2506261Sjkh#ifdef CyDebug
2516261Sjkhu_int	cy_svrr_probes = 0;		/* debugging */
2526261Sjkhu_int	cy_timeouts = 0;
2536261Sjkhu_int	cy_timeout_req = 0;
2546261Sjkh#endif
2556261Sjkh
2566261Sjkh/**********************************************************************/
2576261Sjkh
2586261Sjkhint
2596261Sjkhcyprobe(struct isa_device *dev)
2606261Sjkh{
2616261Sjkh    int		i, j;
2626261Sjkh    u_char	version = 0;	/* firmware version */
2636261Sjkh
2646261Sjkh    /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */
2656261Sjkh    i = *(cy_addr)(dev->id_maddr + CYCLOM_RESET_16);
2666261Sjkh
2676261Sjkh    DELAY(500);	/* wait for the board to get its act together (500 us) */
2686261Sjkh
2696261Sjkh    for (i = 0; i < CD1400s_PER_CYCLOM; i++) {
2706261Sjkh	cy_addr	base = dev->id_maddr + i * CD1400_MEMSIZE;
2716261Sjkh
2726261Sjkh	/* wait for chip to become ready for new command */
2736261Sjkh	for (j = 0; j < 100; j += 50) {
2746261Sjkh	    DELAY(50);	/* wait 50 us */
2756261Sjkh
2766261Sjkh	    if (!*(base + CD1400_CCR))
2776261Sjkh	    	break;
2786261Sjkh	}
2796261Sjkh
2806261Sjkh	/* clear the GFRCR register */
2816261Sjkh	*(base + CD1400_GFRCR) = 0;
2826261Sjkh
2836261Sjkh	/* issue a reset command */
2846261Sjkh	*(base + CD1400_CCR) = CD1400_CMD_RESET;
2856261Sjkh
2866261Sjkh	/* wait for the CD1400 to initialise itself */
2876261Sjkh	for (j = 0; j < 1000; j += 50) {
2886261Sjkh	    DELAY(50);	/* wait 50 us */
2896261Sjkh
2906261Sjkh	    /* retrieve firmware version */
2916261Sjkh	    version = *(base + CD1400_GFRCR);
2926261Sjkh	    if (version)
2936261Sjkh		break;
2946261Sjkh	}
2956261Sjkh
2966261Sjkh	/* anything in the 40-4f range is fine */
2976261Sjkh	if ((version & 0xf0) != 0x40) {
2986261Sjkh	    return 0;
2996261Sjkh	}
3006261Sjkh    }
3016261Sjkh
3026261Sjkh    return 1;	/* found */
3036261Sjkh}
3046261Sjkh
3056261Sjkh
3066261Sjkhint
3076261Sjkhcyattach(struct isa_device *isdp)
3086261Sjkh{
3096261Sjkh/*    u_char	unit = UNIT(isdp->id_unit); */
3106261Sjkh    int		i, j, k;
3116261Sjkh
3126261Sjkh    /* global variable used various routines */
3136261Sjkh    cyclom_base = (cy_addr)isdp->id_maddr;
3146261Sjkh
3156261Sjkh    for (i = 0, k = 0; i < CD1400s_PER_CYCLOM; i++) {
3166261Sjkh	cy_addr	base = cyclom_base + i * CD1400_MEMSIZE;
3176261Sjkh
3186261Sjkh	/* setup a 1ms clock tick */
3196261Sjkh	*(base + CD1400_PPR) = CD1400_CLOCK_25_1MS;
3206261Sjkh
3216261Sjkh	for (j = 0; j < CD1400_NO_OF_CHANNELS; j++, k++) {
3226261Sjkh	    struct cy	*ip;
3236261Sjkh
3246261Sjkh	    /*
3256261Sjkh	     * grab some space.  it'd be more polite to do this in cyopen(),
3266261Sjkh	     * but hey.
3276261Sjkh	     */
3286261Sjkh	    info[k] = ip = malloc(sizeof(struct cy), M_DEVBUF, M_WAITOK);
3296261Sjkh
3306261Sjkh	    /* clear all sorts of junk */
3316261Sjkh	    bzero(ip, sizeof(struct cy));
3326261Sjkh
3336261Sjkh	    ip->base_addr = base;
3346261Sjkh
3356261Sjkh	    /* initialise the channel, without resetting it first */
3366261Sjkh	    cy_channel_init(k, 0);
3376261Sjkh	}
3386261Sjkh    }
3396261Sjkh
3406261Sjkh    /* clear interrupts */
3416261Sjkh    *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0;
3426261Sjkh
3436261Sjkh    return 1;
3446261Sjkh}
3456261Sjkh
3466261Sjkh
3476261Sjkhint
3486261Sjkhcyopen(dev_t dev, int flag, int mode, struct proc *p)
3496261Sjkh{
3506261Sjkh	u_int		unit = UNIT(dev);
3516261Sjkh	struct cy	*infop;
3526261Sjkh	cy_addr		base;
3536261Sjkh	struct tty	*tp;
3546261Sjkh	int		error = 0;
3556261Sjkh	u_char		carrier;
3566261Sjkh
3576261Sjkh	if (unit >= PORTS_PER_CYCLOM)
3586261Sjkh		return (ENXIO);
3596261Sjkh
3606261Sjkh	infop = info[unit];
3616261Sjkh	base = infop->base_addr;
3626261Sjkh	if (!cy_tty[unit])
3636261Sjkh	    infop->tty = cy_tty[unit] = ttymalloc();
3646261Sjkh	tp = infop->tty;
3656261Sjkh
3666261Sjkh	tp->t_oproc = cystart;
3676261Sjkh	tp->t_param = cyparam;
3686261Sjkh	tp->t_dev = dev;
3696261Sjkh	if (!(tp->t_state & TS_ISOPEN)) {
3706261Sjkh		tp->t_state |= TS_WOPEN;
3716261Sjkh		ttychars(tp);
3726261Sjkh		if (tp->t_ispeed == 0) {
3736261Sjkh			tp->t_iflag = TTYDEF_IFLAG;
3746261Sjkh			tp->t_oflag = TTYDEF_OFLAG;
3756261Sjkh			tp->t_cflag = TTYDEF_CFLAG;
3766261Sjkh			tp->t_lflag = TTYDEF_LFLAG;
3776261Sjkh			tp->t_ispeed = tp->t_ospeed = cydefaultrate;
3786261Sjkh		}
3796261Sjkh
3806261Sjkh		(void) spltty();
3816261Sjkh		cy_channel_init(unit, 1);	/* reset the hardware */
3826261Sjkh
3836261Sjkh		/*
3846261Sjkh		 * raise dtr and generally set things up correctly.  this
3856261Sjkh		 * has the side-effect of selecting the appropriate cd1400
3866261Sjkh		 * channel, to help us with subsequent channel control stuff
3876261Sjkh		 */
3886261Sjkh		cyparam(tp, &tp->t_termios);
3896261Sjkh
3906261Sjkh		/* check carrier, and set t_state's TS_CARR_ON flag accordingly */
3916261Sjkh		infop->modem_sig = *(base + CD1400_MSVR);
3926261Sjkh		carrier = infop->modem_sig & CD1400_MSVR_CD;
3936261Sjkh
3946261Sjkh		if (carrier || (infop->carrier_mode >= 2))
3956261Sjkh			tp->t_state |= TS_CARR_ON;
3966261Sjkh		else
3976261Sjkh			tp->t_state &=~ TS_CARR_ON;
3986261Sjkh
3996261Sjkh		/*
4006261Sjkh		 * enable modem & rx interrupts - relies on cyparam()
4016261Sjkh		 * having selected the appropriate cd1400 channel
4026261Sjkh		 */
4036261Sjkh		infop->intr_enable = (1 << 7) | (1 << 4);
4046261Sjkh		*(base + CD1400_SRER) = infop->intr_enable;
4056261Sjkh
4066261Sjkh		ttsetwater(tp);
4076261Sjkh	} else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0)
4086261Sjkh		return (EBUSY);
4096261Sjkh
4106261Sjkh	if (!(flag & O_NONBLOCK))
4116261Sjkh		while (!(tp->t_cflag & CLOCAL) &&
4126261Sjkh		       !(tp->t_state & TS_CARR_ON) && !error)
4136261Sjkh			error = ttysleep(tp, (caddr_t)&tp->t_rawq,
4146261Sjkh		       			TTIPRI|PCATCH, ttopen, 0);
4156261Sjkh	(void) spl0();
4166261Sjkh
4176261Sjkh	if (!error)
4186261Sjkh		error = (*linesw[(u_char)tp->t_line].l_open)(dev, tp);
4196261Sjkh	return (error);
4206261Sjkh} /* end of cyopen() */
4216261Sjkh
4226261Sjkh
4236261Sjkhvoid
4246261Sjkhcyclose_wakeup(caddr_t arg)
4256261Sjkh{
4266261Sjkh	wakeup(arg);
4276261Sjkh} /* end of cyclose_wakeup() */
4286261Sjkh
4296261Sjkh
4306261Sjkhint
4316261Sjkhcyclose(dev_t dev, int flag, int mode, struct proc *p)
4326261Sjkh{
4336261Sjkh	u_int		unit = UNIT(dev);
4346261Sjkh	struct cy	*infop = info[unit];
4356261Sjkh	struct tty	*tp = infop->tty;
4366261Sjkh	cy_addr		base = infop->base_addr;
4376261Sjkh	int		s;
4386261Sjkh
4396261Sjkh	(*linesw[(u_char)tp->t_line].l_close)(tp, flag);
4406261Sjkh
4416261Sjkh	s = spltty();
4426261Sjkh	/* select the appropriate channel on the CD1400 */
4436261Sjkh	*(base + CD1400_CAR) = (u_char)(unit & 0x03);
4446261Sjkh
4456261Sjkh	/* disable this channel and lower DTR */
4466261Sjkh	infop->intr_enable = 0;
4476261Sjkh	*(base + CD1400_SRER) = (u_char)0;			/* no intrs */
4486261Sjkh	*(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR;	/* no DTR */
4496261Sjkh	infop->modem_sig &= ~CD1400_MSVR_DTR;
4506261Sjkh
4516261Sjkh	/* disable receiver (leave transmitter enabled) */
4526261Sjkh	infop->channel_control = (1 << 4) | (1 << 3) | 1;
4536261Sjkh	cd1400_channel_cmd(base, infop->channel_control);
4546261Sjkh	splx(s);
4556261Sjkh
4566261Sjkh	ttyclose(tp);
4576261Sjkh#ifdef broken /* session holds a ref to the tty; can't deallocate */
4586261Sjkh	ttyfree(tp);
4596261Sjkh	infop->tty = cy_tty[unit] = (struct tty *)NULL;
4606261Sjkh#endif
4616261Sjkh
4626261Sjkh	if (infop->dtrwait) {
4636261Sjkh		int error;
4646261Sjkh
4656261Sjkh		timeout(cyclose_wakeup, (caddr_t)&infop->dtrwait, infop->dtrwait);
4666261Sjkh		do {
4676261Sjkh			error = tsleep((caddr_t)&infop->dtrwait,
4686261Sjkh					TTIPRI|PCATCH, "cyclose", 0);
4696261Sjkh		} while (error == ERESTART);
4706261Sjkh	}
4716261Sjkh
4726261Sjkh	return 0;
4736261Sjkh} /* end of cyclose() */
4746261Sjkh
4756261Sjkh
4766261Sjkhint
4776261Sjkhcyread(dev_t dev, struct uio *uio, int flag)
4786261Sjkh{
4796261Sjkh	u_int		unit = UNIT(dev);
4806261Sjkh	struct tty	*tp = info[unit]->tty;
4816261Sjkh
4826261Sjkh	return (*linesw[(u_char)tp->t_line].l_read)(tp, uio, flag);
4836261Sjkh} /* end of cyread() */
4846261Sjkh
4856261Sjkh
4866261Sjkhint
4876261Sjkhcywrite(dev_t dev, struct uio *uio, int flag)
4886261Sjkh{
4896261Sjkh	u_int		unit = UNIT(dev);
4906261Sjkh	struct tty	*tp = info[unit]->tty;
4916261Sjkh
4926261Sjkh#ifdef Smarts
4936261Sjkh	/* XXX duplicate ttwrite(), but without so much output processing on
4946261Sjkh	 * CR & LF chars.  Hardly worth the effort, given that high-throughput
4956261Sjkh	 * sessions are raw anyhow.
4966261Sjkh	 */
4976261Sjkh#else
4986261Sjkh	return (*linesw[(u_char)tp->t_line].l_write)(tp, uio, flag);
4996261Sjkh#endif
5006261Sjkh} /* end of cywrite() */
5016261Sjkh
5026261Sjkh
5036261Sjkh#ifdef Smarts
5046261Sjkh/* standard line discipline input routine */
5056261Sjkhint
5066261Sjkhcyinput(int c, struct tty *tp)
5076261Sjkh{
5086261Sjkh	/* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK
5096261Sjkh	 * bits, as they are done by the CD1400.  Hardly worth the effort,
5106261Sjkh	 * given that high-throughput sessions are raw anyhow.
5116261Sjkh	 */
5126261Sjkh} /* end of cyinput() */
5136261Sjkh#endif /* Smarts */
5146261Sjkh
5156261Sjkh
5166261Sjkhinline static void
5176261Sjkhservice_upper_rx(int unit)
5186261Sjkh{
5196261Sjkh	struct	cy *ip = info[unit];
5206261Sjkh	struct	tty *tp = ip->tty;
5216261Sjkh	struct	cy_buf *buf;
5226261Sjkh	int		i;
5236261Sjkh	u_char	*ch;
5246261Sjkh
5256261Sjkh	buf = ip->rx_buf;
5266261Sjkh
5276261Sjkh	/* give service_rx() a new one */
5286261Sjkh	disable_intr();		/* faster than spltty() */
5296261Sjkh	ip->rx_buf = buf->next_buf;
5306261Sjkh	enable_intr();
5316261Sjkh
5326261Sjkh	if (tp->t_state & TS_ISOPEN) {
5336261Sjkh	    ch = buf->buf;
5346261Sjkh	    i = buf->next_char - buf->buf;
5356261Sjkh
5366261Sjkh#ifdef FastRawInput
5376261Sjkh	    /* try to avoid calling the line discipline stuff if we can */
5386261Sjkh	    if ((tp->t_line == 0) &&
5396261Sjkh		    !(tp->t_iflag & (ICRNL | IMAXBEL | INLCR)) &&
5406261Sjkh		    !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN |
5416261Sjkh			ISIG | PENDIN)) &&
5426261Sjkh		    !(tp->t_state & (TS_CNTTB | TS_LNCH))) {
5436261Sjkh
5446261Sjkh		i = b_to_q(ch, i, &tp->t_rawq);
5456261Sjkh		if (i) {
5466261Sjkh			/*
5476261Sjkh			 * we have no RTS flow control support on cy-8
5486261Sjkh			 * boards, so this is really just tough luck
5496261Sjkh			 */
5506261Sjkh
5516261Sjkh			log(LOG_WARNING, "cy%d: tty input queue overflow\n",
5526261Sjkh			    unit);
5536261Sjkh		}
5546261Sjkh
5556261Sjkh		ttwakeup(tp);	/* notify any readers */
5566261Sjkh	    }
5576261Sjkh	    else
5586261Sjkh#endif /* FastRawInput */
5596261Sjkh	    {
5606261Sjkh		while (i--)
5616261Sjkh		    (*linesw[(u_char)tp->t_line].l_rint)((int)*ch++, tp);
5626261Sjkh	    }
5636261Sjkh	}
5646261Sjkh
5656261Sjkh	/* clear the buffer we've just processed */
5666261Sjkh	buf->next_char = buf->buf;
5676261Sjkh	buf->free = CY_RX_BUF_SIZE;
5686261Sjkh} /* end of service_upper_rx() */
5696261Sjkh
5706261Sjkh
5716261Sjkh#ifdef TxBuffer
5726261Sjkhstatic void
5736261Sjkhservice_upper_tx(int unit)
5746261Sjkh{
5756261Sjkh	struct	cy *ip = info[unit];
5766261Sjkh	struct	tty *tp = ip->tty;
5776261Sjkh
5786261Sjkh	tp->t_state &=~ (TS_BUSY|TS_FLUSH);
5796261Sjkh
5806261Sjkh	if (tp->t_outq.c_cc <= tp->t_lowat) {
5816261Sjkh		if (tp->t_state&TS_ASLEEP) {
5826261Sjkh			tp->t_state &= ~TS_ASLEEP;
5836261Sjkh			wakeup((caddr_t)&tp->t_outq);
5846261Sjkh		}
5856261Sjkh		selwakeup(&tp->t_wsel);
5866261Sjkh	}
5876261Sjkh
5886261Sjkh	if (tp->t_outq.c_cc > 0) {
5896261Sjkh		struct	cy_ring *txq = &ip->tx_buf;
5906261Sjkh		int	free_count = CY_TX_BUF_SIZE - ip->tx_buf.used;
5916261Sjkh		u_char	*cp = txq->tail;
5926261Sjkh		int	count;
5936261Sjkh		int	chars_done;
5946261Sjkh
5956261Sjkh		tp->t_state |= TS_BUSY;
5966261Sjkh
5976261Sjkh		/* find the largest contig. copy we can do */
5986261Sjkh		count = ((txq->endish - cp) > free_count) ?
5996261Sjkh			    free_count : txq->endish - cp;
6006261Sjkh
6016261Sjkh		count = ((cp + free_count) > txq->endish) ?
6026261Sjkh			    txq->endish - cp : free_count;
6036261Sjkh
6046261Sjkh		/* copy the first slab */
6056261Sjkh		chars_done = q_to_b(&tp->t_outq, cp, count);
6066261Sjkh
6076261Sjkh		/* check for wrap-around time */
6086261Sjkh		cp += chars_done;
6096261Sjkh		if (cp == txq->endish)
6106261Sjkh			cp = txq->buf;		/* back to the start */
6116261Sjkh
6126261Sjkh		/* copy anything else, after we've wrapped around */
6136261Sjkh		if ((chars_done == count) && (count != free_count)) {
6146261Sjkh			/* copy the second slab */
6156261Sjkh			count = q_to_b(&tp->t_outq, cp, free_count - count);
6166261Sjkh			cp += count;
6176261Sjkh			chars_done += count;
6186261Sjkh		}
6196261Sjkh
6206261Sjkh		/*
6216261Sjkh		 * update queue, protecting ourselves from any rampant
6226261Sjkh		 * lower-layers
6236261Sjkh		 */
6246261Sjkh		disable_intr();
6256261Sjkh		txq->tail = cp;
6266261Sjkh		txq->used += chars_done;
6276261Sjkh		enable_intr();
6286261Sjkh	}
6296261Sjkh
6306261Sjkh	if (!tp->t_outq.c_cc)
6316261Sjkh		tp->t_state &=~ TS_BUSY;
6326261Sjkh} /* end of service_upper_tx() */
6336261Sjkh#endif /* TxBuffer */
6346261Sjkh
6356261Sjkh
6366261Sjkhinline static void
6376261Sjkhservice_upper_mdm(int unit)
6386261Sjkh{
6396261Sjkh	struct	cy *ip = info[unit];
6406261Sjkh
6416261Sjkh	if (ip->carrier_delta) {
6426261Sjkh		int	carrier = ip->modem_sig & CD1400_MSVR_CD;
6436261Sjkh		struct	tty *tp = ip->tty;
6446261Sjkh
6456261Sjkh		if (!(*linesw[(u_char)tp->t_line].l_modem)(tp, carrier)) {
6466261Sjkh			cy_addr	base = ip->base_addr;
6476261Sjkh
6486261Sjkh			/* clear DTR */
6496261Sjkh			disable_intr();
6506261Sjkh			*(base + CD1400_CAR) = (u_char)(unit & 0x03);
6516261Sjkh			*(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR;
6526261Sjkh			ip->modem_sig &= ~CD1400_MSVR_DTR;
6536261Sjkh			ip->carrier_delta = 0;
6546261Sjkh			enable_intr();
6556261Sjkh		}
6566261Sjkh		else {
6576261Sjkh			disable_intr();
6586261Sjkh			ip->carrier_delta = 0;
6596261Sjkh			enable_intr();
6606261Sjkh		}
6616261Sjkh	}
6626261Sjkh} /* end of service_upper_mdm() */
6636261Sjkh
6646261Sjkh
6656261Sjkh/* upper level character processing routine */
6666261Sjkhvoid
6676261Sjkhcytimeout(caddr_t ptr)
6686261Sjkh{
6696261Sjkh	int	unit;
6706261Sjkh
6716261Sjkh	timeout_scheduled = 0;
6726261Sjkh
6736261Sjkh#ifdef CyDebug
6746261Sjkh	cy_timeouts++;
6756261Sjkh#endif
6766261Sjkh
6776261Sjkh	/* check each port in turn */
6786261Sjkh	for (unit = 0; unit < NCY*PORTS_PER_CYCLOM; unit++) {
6796261Sjkh		struct	cy *ip = info[unit];
6806261Sjkh#ifndef TxBuffer
6816261Sjkh		struct	tty *tp = ip->tty;
6826261Sjkh#endif
6836261Sjkh
6846261Sjkh		/* ignore anything that is not open */
6856261Sjkh		if (!ip->tty)
6866261Sjkh			continue;
6876261Sjkh
6886261Sjkh		/*
6896261Sjkh		 * any received chars to handle? (doesn't matter if intr routine
6906261Sjkh		 * kicks in while we're testing this)
6916261Sjkh		 */
6926261Sjkh		if (ip->rx_buf->free != CY_RX_BUF_SIZE)
6936261Sjkh			service_upper_rx(unit);
6946261Sjkh
6956261Sjkh#ifdef TxBuffer
6966261Sjkh		/* anything to add to the transmit buffer (low-water mark)? */
6976261Sjkh		if (ip->tx_buf.used < CY_TX_BUF_SIZE/2)
6986261Sjkh			service_upper_tx(unit);
6996261Sjkh#else
7006261Sjkh		if (tp->t_outq.c_cc <= tp->t_lowat) {
7016261Sjkh			if (tp->t_state&TS_ASLEEP) {
7026261Sjkh				tp->t_state &= ~TS_ASLEEP;
7036261Sjkh				wakeup((caddr_t)&tp->t_outq);
7046261Sjkh			}
7056261Sjkh			selwakeup(&tp->t_wsel);
7066261Sjkh		}
7076261Sjkh#endif
7086261Sjkh
7096261Sjkh		/* anything modem signals altered? */
7106261Sjkh		service_upper_mdm(unit);
7116261Sjkh
7126261Sjkh		/* any overruns to log? */
7136261Sjkh#ifdef LogOverruns
7146261Sjkh		if (ip->fifo_overrun) {
7156261Sjkh			/*
7166261Sjkh			 * turn off the alarm - not important enough to bother
7176261Sjkh			 * with interrupt protection.
7186261Sjkh			 */
7196261Sjkh			ip->fifo_overrun = 0;
7206261Sjkh
7216261Sjkh			log(LOG_WARNING, "cy%d: receive fifo overrun\n", unit);
7226261Sjkh		}
7236261Sjkh#endif
7246261Sjkh		if (ip->rx_buf_overrun) {
7256261Sjkh			/*
7266261Sjkh			 * turn off the alarm - not important enough to bother
7276261Sjkh			 * with interrupt protection.
7286261Sjkh			 */
7296261Sjkh			ip->rx_buf_overrun = 0;
7306261Sjkh
7316261Sjkh			log(LOG_WARNING, "cy%d: receive buffer full\n", unit);
7326261Sjkh		}
7336261Sjkh	}
7346261Sjkh} /* cytimeout() */
7356261Sjkh
7366261Sjkh
7376261Sjkhinline static void
7386261Sjkhschedule_upper_service(void)
7396261Sjkh{
7406261Sjkh#ifdef CyDebug
7416261Sjkh    cy_timeout_req++;
7426261Sjkh#endif
7436261Sjkh
7446261Sjkh    if (!timeout_scheduled) {
7456261Sjkh	timeout(cytimeout, (caddr_t)0, 1);	/* call next tick */
7466261Sjkh	timeout_scheduled = 1;
7476261Sjkh    }
7486261Sjkh} /* end of schedule_upper_service() */
7496261Sjkh
7506261Sjkh
7516261Sjkh/* initialise a channel on the cyclom board */
7526261Sjkh
7536261Sjkhstatic void
7546261Sjkhcy_channel_init(dev_t dev, int reset)
7556261Sjkh{
7566261Sjkh	u_int	unit = UNIT(dev);
7576261Sjkh	int	carrier_mode = CARRIER_MODE(dev);
7586261Sjkh	struct	cy *ip = info[unit];
7596261Sjkh	cy_addr	base = ip->base_addr;
7606261Sjkh	struct	tty *tp = ip->tty;
7616261Sjkh	struct	cy_buf *buf, *next_buf;
7626261Sjkh	int	i;
7636261Sjkh#ifndef PollMode
7646261Sjkh	u_char	cd1400_unit;
7656261Sjkh#endif
7666261Sjkh
7676261Sjkh	/* clear the structure and refill it */
7686261Sjkh	bzero(ip, sizeof(struct cy));
7696261Sjkh	ip->base_addr = base;
7706261Sjkh	ip->tty = tp;
7716261Sjkh	ip->carrier_mode = carrier_mode;
7726261Sjkh
7736261Sjkh	/* select channel of the CD1400 */
7746261Sjkh	*(base + CD1400_CAR) = (u_char)(unit & 0x03);
7756261Sjkh
7766261Sjkh	if (reset)
7776261Sjkh		cd1400_channel_cmd(base, 0x80);	/* reset the channel */
7786261Sjkh
7796261Sjkh	/* set LIVR to 0 - intr routines depend on this */
7806261Sjkh	*(base + CD1400_LIVR) = 0;
7816261Sjkh
7826261Sjkh#ifndef PollMode
7836261Sjkh	/* set top four bits of {R,T,M}ICR to the cd1400
7846261Sjkh	 * number, cd1400_unit
7856261Sjkh	 */
7866261Sjkh	cd1400_unit = unit / CD1400_NO_OF_CHANNELS;
7876261Sjkh	*(base + CD1400_RICR) = (u_char)(cd1400_unit << 4);
7886261Sjkh	*(base + CD1400_TICR) = (u_char)(cd1400_unit << 4);
7896261Sjkh	*(base + CD1400_MICR) = (u_char)(cd1400_unit << 4);
7906261Sjkh#endif
7916261Sjkh
7926261Sjkh	ip->dtrwait = hz/4;	/* quarter of a second */
7936261Sjkh
7946261Sjkh	/* setup low-level buffers */
7956261Sjkh	i = CY_RX_BUFS;
7966261Sjkh	ip->rx_buf = next_buf = &ip->rx_buf_pool[0];
7976261Sjkh	while (i--) {
7986261Sjkh		buf = &ip->rx_buf_pool[i];
7996261Sjkh
8006261Sjkh		buf->next_char = buf->buf;	/* first char to use */
8016261Sjkh		buf->free = CY_RX_BUF_SIZE;	/* i.e. empty */
8026261Sjkh		buf->next_buf = next_buf;	/* where to go next */
8036261Sjkh		next_buf = buf;
8046261Sjkh	}
8056261Sjkh
8066261Sjkh#ifdef TxBuffer
8076261Sjkh	ip->tx_buf.endish = ip->tx_buf.buf + CY_TX_BUF_SIZE;
8086261Sjkh
8096261Sjkh	/* clear the low-level tx buffer */
8106261Sjkh	ip->tx_buf.head = ip->tx_buf.tail = ip->tx_buf.buf;
8116261Sjkh	ip->tx_buf.used = 0;
8126261Sjkh#endif
8136261Sjkh
8146261Sjkh	/* clear the low-level rx buffer */
8156261Sjkh	ip->rx_buf->next_char = ip->rx_buf->buf;	/* first char to use */
8166261Sjkh	ip->rx_buf->free = CY_RX_BUF_SIZE;		/* completely empty */
8176261Sjkh} /* end of cy_channel_init() */
8186261Sjkh
8196261Sjkh
8206261Sjkh/* service a receive interrupt */
8216261Sjkhinline static void
8226261Sjkhservice_rx(int cd, caddr_t base)
8236261Sjkh{
8246261Sjkh	struct cy	*infop;
8256261Sjkh	unsigned	count;
8266261Sjkh	int		ch;
8276261Sjkh	u_char		serv_type, channel;
8286261Sjkh#ifdef PollMode
8296261Sjkh	u_char		save_rir, save_car;
8306261Sjkh#endif
8316261Sjkh
8326261Sjkh	/* setup */
8336261Sjkh#ifdef PollMode
8346261Sjkh	save_rir = *(base + CD1400_RIR);
8356261Sjkh	channel = cd * CD1400_NO_OF_CHANNELS + (save_rir & 0x3);
8366261Sjkh	save_car = *(base + CD1400_CAR);
8376261Sjkh	*(base + CD1400_CAR) = save_rir;	/* enter modem service */
8386261Sjkh	serv_type = *(base + CD1400_RIVR);
8396261Sjkh#else
8406261Sjkh	serv_type = *(base + CD1400_SVCACKR);	/* ack receive service */
8416261Sjkh	channel = ((u_char)*(base + CD1400_RICR)) >> 2;	/* get cyclom channel # */
8426261Sjkh
8436261Sjkh#ifdef CyDebug
8446261Sjkh	if (channel >= PORTS_PER_CYCLOM) {
8456261Sjkh	    printf("cy: service_rx - channel %02x\n", channel);
8466261Sjkh	    panic("cy: service_rx - bad channel");
8476261Sjkh	}
8486261Sjkh#endif
8496261Sjkh#endif
8506261Sjkh
8516261Sjkh	infop = info[channel];
8526261Sjkh
8536261Sjkh	/* read those chars */
8546261Sjkh	if (serv_type & CD1400_RIVR_EXCEPTION) {
8556261Sjkh		/* read the exception status */
8566261Sjkh		u_char status = *(base + CD1400_RDSR);
8576261Sjkh
8586261Sjkh		/* XXX is it a break?  Do something if it is! */
8596261Sjkh
8606261Sjkh		/* XXX is IGNPAR not set?  Store a null in the buffer. */
8616261Sjkh
8626261Sjkh#ifdef LogOverruns
8636261Sjkh		if (status & CD1400_RDSR_OVERRUN) {
8646261Sjkh#if 0
8656261Sjkh			ch |= TTY_PE;		/* for SLIP */
8666261Sjkh#endif
8676261Sjkh			infop->fifo_overrun++;
8686261Sjkh		}
8696261Sjkh#endif
8706261Sjkh		infop->recv_exception++;
8716261Sjkh	}
8726261Sjkh	else {
8736261Sjkh		struct	cy_buf *buf = infop->rx_buf;
8746261Sjkh
8756261Sjkh		count = (u_char)*(base + CD1400_RDCR);	/* how many to read? */
8766261Sjkh		infop->recv_normal += count;
8776261Sjkh		if (buf->free < count) {
8786261Sjkh			infop->rx_buf_overrun += count;
8796261Sjkh
8806261Sjkh			/* read & discard everything */
8816261Sjkh			while (count--)
8826261Sjkh				ch = (u_char)*(base + CD1400_RDSR);
8836261Sjkh		}
8846261Sjkh		else {
8856261Sjkh			/* slurp it into our low-level buffer */
8866261Sjkh			buf->free -= count;
8876261Sjkh			while (count--) {
8886261Sjkh				ch = (u_char)*(base + CD1400_RDSR);	/* read the char */
8896261Sjkh				*(buf->next_char++) = ch;
8906261Sjkh			}
8916261Sjkh		}
8926261Sjkh	}
8936261Sjkh
8946261Sjkh#ifdef PollMode
8956261Sjkh	*(base + CD1400_RIR) = (u_char)(save_rir & 0x3f);	/* terminate service context */
8966261Sjkh#else
8976261Sjkh	*(base + CD1400_EOSRR) = (u_char)0;	/* terminate service context */
8986261Sjkh#endif
8996261Sjkh} /* end of service_rx */
9006261Sjkh
9016261Sjkh
9026261Sjkh/* service a transmit interrupt */
9036261Sjkhinline static void
9046261Sjkhservice_tx(int cd, caddr_t base)
9056261Sjkh{
9066261Sjkh	struct cy	*ip;
9076261Sjkh#ifdef TxBuffer
9086261Sjkh	struct cy_ring	*txq;
9096261Sjkh#else
9106261Sjkh	struct tty	*tp;
9116261Sjkh#endif
9126261Sjkh	u_char		channel;
9136261Sjkh#ifdef PollMode
9146261Sjkh	u_char		save_tir, save_car;
9156261Sjkh#else
9166261Sjkh	u_char		vector;
9176261Sjkh#endif
9186261Sjkh
9196261Sjkh	/* setup */
9206261Sjkh#ifdef PollMode
9216261Sjkh	save_tir = *(base + CD1400_TIR);
9226261Sjkh	channel = cd * CD1400_NO_OF_CHANNELS + (save_tir & 0x3);
9236261Sjkh	save_car = *(base + CD1400_CAR);
9246261Sjkh	*(base + CD1400_CAR) = save_tir;	/* enter tx service */
9256261Sjkh#else
9266261Sjkh	vector = *(base + CD1400_SVCACKT);	/* ack transmit service */
9276261Sjkh	channel = ((u_char)*(base + CD1400_TICR)) >> 2;	/* get cyclom channel # */
9286261Sjkh
9296261Sjkh#ifdef CyDebug
9306261Sjkh	if (channel >= PORTS_PER_CYCLOM) {
9316261Sjkh	    printf("cy: service_tx - channel %02x\n", channel);
9326261Sjkh	    panic("cy: service_tx - bad channel");
9336261Sjkh	}
9346261Sjkh#endif
9356261Sjkh#endif
9366261Sjkh
9376261Sjkh	ip = info[channel];
9386261Sjkh#ifdef TxBuffer
9396261Sjkh	txq = &ip->tx_buf;
9406261Sjkh
9416261Sjkh	if (txq->used > 0) {
9426261Sjkh		cy_addr	base = ip->base_addr;
9436261Sjkh		int	count = min(CD1400_FIFOSIZE, txq->used);
9446261Sjkh		int	chars_done = count;
9456261Sjkh		u_char	*cp = txq->head;
9466261Sjkh		u_char	*buf_end = txq->endish;
9476261Sjkh
9486261Sjkh		/* ip->state |= CY_BUSY; */
9496261Sjkh		while (count--) {
9506261Sjkh			*(base + CD1400_TDR) = *cp++;
9516261Sjkh			if (cp >= buf_end)
9526261Sjkh				cp = txq->buf;
9536261Sjkh		};
9546261Sjkh		txq->head = cp;
9556261Sjkh		txq->used -= chars_done; /* important that this is atomic */
9566261Sjkh		ip->xmit += chars_done;
9576261Sjkh	}
9586261Sjkh
9596261Sjkh	/*
9606261Sjkh	 * disable tx intrs if no more chars to send.  we re-enable
9616261Sjkh	 * them in cystart()
9626261Sjkh	 */
9636261Sjkh	if (!txq->used) {
9646261Sjkh		ip->intr_enable &=~ (1 << 2);
9656261Sjkh		*(base + CD1400_SRER) = ip->intr_enable;
9666261Sjkh		/* ip->state &= ~CY_BUSY; */
9676261Sjkh	}
9686261Sjkh#else
9696261Sjkh	tp = ip->tty;
9706261Sjkh
9716261Sjkh	if (!(tp->t_state & TS_TTSTOP) && (tp->t_outq.c_cc > 0)) {
9726261Sjkh		cy_addr	base = ip->base_addr;
9736261Sjkh		int	count = min(CD1400_FIFOSIZE, tp->t_outq.c_cc);
9746261Sjkh
9756261Sjkh		ip->xmit += count;
9766261Sjkh		tp->t_state |= TS_BUSY;
9776261Sjkh		while (count--)
9786261Sjkh			*(base + CD1400_TDR) = getc(&tp->t_outq);
9796261Sjkh	}
9806261Sjkh
9816261Sjkh	/*
9826261Sjkh	 * disable tx intrs if no more chars to send.  we re-enable them
9836261Sjkh	 * in cystart()
9846261Sjkh	 */
9856261Sjkh	if (!tp->t_outq.c_cc) {
9866261Sjkh		ip->intr_enable &=~ (1 << 2);
9876261Sjkh		*(base + CD1400_SRER) = ip->intr_enable;
9886261Sjkh		tp->t_state &= ~TS_BUSY;
9896261Sjkh	}
9906261Sjkh#endif
9916261Sjkh
9926261Sjkh#ifdef PollMode
9936261Sjkh	*(base + CD1400_TIR) = (u_char)(save_tir & 0x3f);	/* terminate service context */
9946261Sjkh#else
9956261Sjkh	*(base + CD1400_EOSRR) = (u_char)0;	/* terminate service context */
9966261Sjkh#endif
9976261Sjkh} /* end of service_tx */
9986261Sjkh
9996261Sjkh
10006261Sjkh/* service a modem status interrupt */
10016261Sjkhinline static void
10026261Sjkhservice_mdm(int cd, caddr_t base)
10036261Sjkh{
10046261Sjkh	struct cy	*infop;
10056261Sjkh	u_char		channel, deltas;
10066261Sjkh#ifdef PollMode
10076261Sjkh	u_char		save_mir, save_car;
10086261Sjkh#else
10096261Sjkh	u_char		vector;
10106261Sjkh#endif
10116261Sjkh
10126261Sjkh	/* setup */
10136261Sjkh#ifdef PollMode
10146261Sjkh	save_mir = *(base + CD1400_MIR);
10156261Sjkh	channel = cd * CD1400_NO_OF_CHANNELS + (save_mir & 0x3);
10166261Sjkh	save_car = *(base + CD1400_CAR);
10176261Sjkh	*(base + CD1400_CAR) = save_mir;	/* enter modem service */
10186261Sjkh#else
10196261Sjkh	vector = *(base + CD1400_SVCACKM);	/* ack modem service */
10206261Sjkh	channel = ((u_char)*(base + CD1400_MICR)) >> 2;	/* get cyclom channel # */
10216261Sjkh
10226261Sjkh#ifdef CyDebug
10236261Sjkh	if (channel >= PORTS_PER_CYCLOM) {
10246261Sjkh	    printf("cy: service_mdm - channel %02x\n", channel);
10256261Sjkh	    panic("cy: service_mdm - bad channel");
10266261Sjkh	}
10276261Sjkh#endif
10286261Sjkh#endif
10296261Sjkh
10306261Sjkh	infop = info[channel];
10316261Sjkh
10326261Sjkh	/* read the siggies and see what's changed */
10336261Sjkh	infop->modem_sig = (u_char)*(base + CD1400_MSVR);
10346261Sjkh	deltas = (u_char)*(base + CD1400_MISR);
10356261Sjkh
10366261Sjkh	if ((infop->carrier_mode <= 2) && (deltas & CD1400_MISR_CDd))
10376261Sjkh		/* something for the upper layer to deal with */
10386261Sjkh		infop->carrier_delta = 1;
10396261Sjkh
10406261Sjkh	infop->mdm++;
10416261Sjkh
10426261Sjkh	/* terminate service context */
10436261Sjkh#ifdef PollMode
10446261Sjkh	*(base + CD1400_MIR) = (u_char)(save_mir & 0x3f);
10456261Sjkh#else
10466261Sjkh	*(base + CD1400_EOSRR) = (u_char)0;
10476261Sjkh#endif
10486261Sjkh} /* end of service_mdm */
10496261Sjkh
10506261Sjkh
10516261Sjkhint
10526261Sjkhcyintr(int unit)
10536261Sjkh{
10546261Sjkh    int		cd;
10556261Sjkh    u_char	status;
10566261Sjkh
10576261Sjkh    /* check each CD1400 in turn */
10586261Sjkh    for (cd = 0; cd < CD1400s_PER_CYCLOM; cd++) {
10596261Sjkh	cy_addr	base = cyclom_base + cd*CD1400_MEMSIZE;
10606261Sjkh
10616261Sjkh	/* poll to see if it has any work */
10626261Sjkh	while (status = (u_char)*(base + CD1400_SVRR)) {
10636261Sjkh#ifdef CyDebug
10646261Sjkh	    cy_svrr_probes++;
10656261Sjkh#endif
10666261Sjkh	    /* service requests as appropriate, giving priority to RX */
10676261Sjkh	    if (status & CD1400_SVRR_RX)
10686261Sjkh		    service_rx(cd, base);
10696261Sjkh	    if (status & CD1400_SVRR_TX)
10706261Sjkh		    service_tx(cd, base);
10716261Sjkh	    if (status & CD1400_SVRR_MDM)
10726261Sjkh		    service_mdm(cd, base);
10736261Sjkh	}
10746261Sjkh    }
10756261Sjkh
10766261Sjkh    /* request upper level service to deal with whatever happened */
10776261Sjkh    schedule_upper_service();
10786261Sjkh
10796261Sjkh    /* re-enable interrupts on the cyclom */
10806261Sjkh    *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0;
10816261Sjkh
10826261Sjkh    return 1;
10836261Sjkh}
10846261Sjkh
10856261Sjkh
10866261Sjkhint
10876261Sjkhcyioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
10886261Sjkh{
10896261Sjkh	int		unit = UNIT(dev);
10906261Sjkh	struct cy	*infop = info[unit];
10916261Sjkh	struct tty	*tp = infop->tty;
10926261Sjkh	int		error;
10936261Sjkh
10946261Sjkh	error = (*linesw[(u_char)tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
10956261Sjkh	if (error >= 0)
10966261Sjkh		return (error);
10976261Sjkh	error = ttioctl(tp, cmd, data, flag
10986261Sjkh#ifdef NetBSD
10996261Sjkh			, p
11006261Sjkh#endif
11016261Sjkh			);
11026261Sjkh	if (error >= 0)
11036261Sjkh		return (error);
11046261Sjkh
11056261Sjkh	switch (cmd) {
11066261Sjkh#ifdef notyet	/* sigh - more junk to do XXX */
11076261Sjkh	case TIOCSBRK:
11086261Sjkh		break;
11096261Sjkh	case TIOCCBRK:
11106261Sjkh		break;
11116261Sjkh	case TIOCSDTR:
11126261Sjkh		break;
11136261Sjkh	case TIOCCDTR:
11146261Sjkh		break;
11156261Sjkh
11166261Sjkh	case TIOCMSET:
11176261Sjkh		break;
11186261Sjkh	case TIOCMBIS:
11196261Sjkh		break;
11206261Sjkh	case TIOCMBIC:
11216261Sjkh		break;
11226261Sjkh#endif /* notyet */
11236261Sjkh
11246261Sjkh	case TIOCMGET: {
11256261Sjkh		int	bits = 0;
11266261Sjkh		u_char	status = infop->modem_sig;
11276261Sjkh
11286261Sjkh		if (status & CD1400_MSVR_DTR) bits |= TIOCM_DTR | TIOCM_RTS;
11296261Sjkh		if (status & CD1400_MSVR_CD) bits |= TIOCM_CD;
11306261Sjkh		if (status & CD1400_MSVR_CTS) bits |= TIOCM_CTS;
11316261Sjkh		if (status & CD1400_MSVR_DSR) bits |= TIOCM_DSR;
11326261Sjkh#ifdef CYCLOM_16
11336261Sjkh		if (status & CD1400_MSVR_RI) bits |= TIOCM_RI;
11346261Sjkh#endif
11356261Sjkh		if (infop->channel_control & 0x02) bits |= TIOCM_LE;
11366261Sjkh		*(int *)data = bits;
11376261Sjkh		break;
11386261Sjkh	}
11396261Sjkh
11406261Sjkh#ifdef TIOCMSBIDIR
11416261Sjkh	case TIOCMSBIDIR:
11426261Sjkh		return (ENOTTY);
11436261Sjkh#endif /* TIOCMSBIDIR */
11446261Sjkh
11456261Sjkh#ifdef TIOCMGBIDIR
11466261Sjkh	case TIOCMGBIDIR:
11476261Sjkh		return (ENOTTY);
11486261Sjkh#endif /* TIOCMGBIDIR */
11496261Sjkh
11506261Sjkh#ifdef TIOCMSDTRWAIT
11516261Sjkh	case TIOCMSDTRWAIT:
11526261Sjkh		/* must be root to set dtr delay */
11536261Sjkh			if (p->p_ucred->cr_uid != 0)
11546261Sjkh				return(EPERM);
11556261Sjkh
11566261Sjkh			infop->dtrwait = *(u_int *)data;
11576261Sjkh			break;
11586261Sjkh#endif /* TIOCMSDTRWAIT */
11596261Sjkh
11606261Sjkh#ifdef TIOCMGDTRWAIT
11616261Sjkh	case TIOCMGDTRWAIT:
11626261Sjkh		*(u_int *)data = infop->dtrwait;
11636261Sjkh		break;
11646261Sjkh#endif /* TIOCMGDTRWAIT */
11656261Sjkh
11666261Sjkh	default:
11676261Sjkh		return (ENOTTY);
11686261Sjkh	}
11696261Sjkh
11706261Sjkh	return 0;
11716261Sjkh} /* end of cyioctl() */
11726261Sjkh
11736261Sjkh
11746261Sjkhint
11756261Sjkhcyparam(struct tty *tp, struct termios *t)
11766261Sjkh{
11776261Sjkh	u_char		unit = UNIT(tp->t_dev);
11786261Sjkh	struct cy	*infop = info[unit];
11796261Sjkh	cy_addr		base = infop->base_addr;
11806261Sjkh	int		cflag = t->c_cflag;
11816261Sjkh	int		iflag = t->c_iflag;
11826261Sjkh	int		ispeed, ospeed;
11836261Sjkh	int		itimeout;
11846261Sjkh	int		iprescaler, oprescaler;
11856261Sjkh	int		s;
11866261Sjkh	u_char		cor_change = 0;
11876261Sjkh	u_char		opt;
11886261Sjkh
11896261Sjkh	if (!t->c_ispeed)
11906261Sjkh	    t->c_ispeed = t->c_ospeed;
11916261Sjkh
11926261Sjkh	s = spltty();
11936261Sjkh
11946261Sjkh	/* select the appropriate channel on the CD1400 */
11956261Sjkh	*(base + CD1400_CAR) = unit & 0x03;
11966261Sjkh
11976261Sjkh	/* handle DTR drop on speed == 0 trick */
11986261Sjkh	if (t->c_ospeed == 0) {
11996261Sjkh	    *(base + CD1400_DTR) = CD1400_DTR_CLEAR;
12006261Sjkh	    infop->modem_sig &= ~CD1400_MSVR_DTR;
12016261Sjkh	}
12026261Sjkh	else {
12036261Sjkh	    *(base + CD1400_DTR) = CD1400_DTR_SET;
12046261Sjkh	    infop->modem_sig |= CD1400_MSVR_DTR;
12056261Sjkh	}
12066261Sjkh
12076261Sjkh	/* set baud rates if they've changed from last time */
12086261Sjkh
12096261Sjkh	if ((ospeed = cyspeed(t->c_ospeed, &oprescaler)) < 0)
12106261Sjkh	    return EINVAL;
12116261Sjkh	*(base + CD1400_TBPR) = (u_char)ospeed;
12126261Sjkh	*(base + CD1400_TCOR) = (u_char)oprescaler;
12136261Sjkh
12146261Sjkh	if ((ispeed = cyspeed(t->c_ispeed, &iprescaler)) < 0)
12156261Sjkh	    return EINVAL;
12166261Sjkh	*(base + CD1400_RBPR) = (u_char)ispeed;
12176261Sjkh	*(base + CD1400_RCOR) = (u_char)iprescaler;
12186261Sjkh
12196261Sjkh	/*
12206261Sjkh	 * set receive time-out period
12216261Sjkh	 *	generate a rx interrupt if no new chars are received in
12226261Sjkh	 *	this many ticks
12236261Sjkh	 * don't bother comparing old & new VMIN, VTIME and ispeed - it
12246261Sjkh	 * can't be much worse just to calculate and set it each time!
12256261Sjkh	 * certainly less hassle. :-)
12266261Sjkh	 */
12276261Sjkh
12286261Sjkh	/*
12296261Sjkh	 * calculate minimum timeout period:
12306261Sjkh	 *     5 ms or the time it takes to receive 1 char, rounded up to the
12316261Sjkh	 *     next ms, whichever is greater
12326261Sjkh	 */
12336261Sjkh	if (t->c_ispeed > 0) {
12346261Sjkh	    itimeout = (t->c_ispeed > 2200) ? 5 : (10000/t->c_ispeed + 1);
12356261Sjkh
12366261Sjkh	    /* if we're using VTIME as an inter-char timeout, and it is set to
12376261Sjkh	     * be longer than the minimum calculated above, go for it
12386261Sjkh	     */
12396261Sjkh	    if (t->c_cc[VMIN] && t->c_cc[VTIME] && t->c_cc[VTIME]*10 > itimeout)
12406261Sjkh		itimeout = t->c_cc[VTIME]*10;
12416261Sjkh
12426261Sjkh	    /* store it, taking care not to overflow the byte-sized register */
12436261Sjkh	    *(base + CD1400_RTPR) = (u_char)((itimeout <= 255) ? itimeout : 255);
12446261Sjkh	}
12456261Sjkh
12466261Sjkh
12476261Sjkh	/*
12486261Sjkh	 * channel control
12496261Sjkh	 *	receiver enable
12506261Sjkh	 *	transmitter enable (always set)
12516261Sjkh	 */
12526261Sjkh	opt = (1 << 4) | (1 << 3) | ((cflag & CREAD) ? (1 << 1) : 1);
12536261Sjkh	if (opt != infop->channel_control) {
12546261Sjkh	    infop->channel_control = opt;
12556261Sjkh	    cd1400_channel_cmd(base, opt);
12566261Sjkh	}
12576261Sjkh
12586261Sjkh#ifdef Smarts
12596261Sjkh	/* set special chars */
12606261Sjkh	if (t->c_cc[VSTOP] != _POSIX_VDISABLE &&
12616261Sjkh		(t->c_cc[VSTOP] != infop->spec_char[0])) {
12626261Sjkh	    *(base + CD1400_SCHR1) = infop->spec_char[0] = t->c_cc[VSTOP];
12636261Sjkh	}
12646261Sjkh	if (t->c_cc[VSTART] != _POSIX_VDISABLE &&
12656261Sjkh		(t->c_cc[VSTART] != infop->spec_char[1])) {
12666261Sjkh	    *(base + CD1400_SCHR2) = infop->spec_char[0] = t->c_cc[VSTART];
12676261Sjkh	}
12686261Sjkh	if (t->c_cc[VINTR] != _POSIX_VDISABLE &&
12696261Sjkh		(t->c_cc[VINTR] != infop->spec_char[2])) {
12706261Sjkh	    *(base + CD1400_SCHR3) = infop->spec_char[0] = t->c_cc[VINTR];
12716261Sjkh	}
12726261Sjkh	if (t->c_cc[VSUSP] != _POSIX_VDISABLE &&
12736261Sjkh		(t->c_cc[VSUSP] != infop->spec_char[3])) {
12746261Sjkh	    *(base + CD1400_SCHR4) = infop->spec_char[0] = t->c_cc[VSUSP];
12756261Sjkh	}
12766261Sjkh#endif
12776261Sjkh
12786261Sjkh	/*
12796261Sjkh	 * set channel option register 1 -
12806261Sjkh	 *	parity mode
12816261Sjkh	 *	stop bits
12826261Sjkh	 *	char length
12836261Sjkh	 */
12846261Sjkh	opt = 0;
12856261Sjkh	/* parity */
12866261Sjkh	if (cflag & PARENB) {
12876261Sjkh	    if (cflag & PARODD)
12886261Sjkh		opt |= 1 << 7;
12896261Sjkh	    opt |= 2 << 5;		/* normal parity mode */
12906261Sjkh	}
12916261Sjkh	if (!(iflag & INPCK))
12926261Sjkh	    opt |= 1 << 4;		/* ignore parity */
12936261Sjkh	/* stop bits */
12946261Sjkh	if (cflag & CSTOPB)
12956261Sjkh	    opt |= 2 << 2;
12966261Sjkh	/* char length */
12976261Sjkh	opt |= (cflag & CSIZE) >> 8;	/* nasty, but fast */
12986261Sjkh	if (opt != infop->cor[0]) {
12996261Sjkh	    cor_change |= 1 << 1;
13006261Sjkh	    *(base + CD1400_COR1) = opt;
13016261Sjkh	}
13026261Sjkh
13036261Sjkh	/*
13046261Sjkh	 * set channel option register 2 -
13056261Sjkh	 *	flow control
13066261Sjkh	 */
13076261Sjkh	opt = 0;
13086261Sjkh#ifdef Smarts
13096261Sjkh	if (iflag & IXANY)
13106261Sjkh	    opt |= 1 << 7;		/* auto output restart on any char after XOFF */
13116261Sjkh	if (iflag & IXOFF)
13126261Sjkh	    opt |= 1 << 6;		/* auto XOFF output flow-control */
13136261Sjkh#endif
13146261Sjkh#ifndef ALWAYS_RTS_CTS
13156261Sjkh	if (cflag & CCTS_OFLOW)
13166261Sjkh#endif
13176261Sjkh	    opt |= 1 << 1;		/* auto CTS flow-control */
13186261Sjkh
13196261Sjkh	if (opt != infop->cor[1]) {
13206261Sjkh	    cor_change |= 1 << 2;
13216261Sjkh	    *(base + CD1400_COR2) = opt;
13226261Sjkh	}
13236261Sjkh
13246261Sjkh	/*
13256261Sjkh	 * set channel option register 3 -
13266261Sjkh	 *	receiver FIFO interrupt threshold
13276261Sjkh	 *	flow control
13286261Sjkh	 */
13296261Sjkh	opt = RxFifoThreshold;	/* rx fifo threshold */
13306261Sjkh#ifdef Smarts
13316261Sjkh	if (t->c_lflag & ICANON)
13326261Sjkh	    opt |= 1 << 6;		/* detect INTR & SUSP chars */
13336261Sjkh	if (iflag & IXOFF)
13346261Sjkh	    opt |= (1 << 5) | (1 << 4);	/* transparent in-band flow control */
13356261Sjkh#endif
13366261Sjkh	if (opt != infop->cor[2]) {
13376261Sjkh	    cor_change |= 1 << 3;
13386261Sjkh	    *(base + CD1400_COR3) = opt;
13396261Sjkh	}
13406261Sjkh
13416261Sjkh
13426261Sjkh	/* notify the CD1400 if COR1-3 have changed */
13436261Sjkh	if (cor_change) {
13446261Sjkh	    cor_change |= 1 << 6;	/* COR change flag */
13456261Sjkh	    cd1400_channel_cmd(base, cor_change);
13466261Sjkh	}
13476261Sjkh
13486261Sjkh	/*
13496261Sjkh	 * set channel option register 4 -
13506261Sjkh	 *	CR/NL processing
13516261Sjkh	 *	break processing
13526261Sjkh	 *	received exception processing
13536261Sjkh	 */
13546261Sjkh	opt = 0;
13556261Sjkh	if (iflag & IGNCR)
13566261Sjkh	    opt |= 1 << 7;
13576261Sjkh#ifdef Smarts
13586261Sjkh	/*
13596261Sjkh	 * we need a new ttyinput() for this, as we don't want to
13606261Sjkh	 * have ICRNL && INLCR being done in both layers, or to have
13616261Sjkh	 * synchronisation problems
13626261Sjkh	 */
13636261Sjkh	if (iflag & ICRNL)
13646261Sjkh	    opt |= 1 << 6;
13656261Sjkh	if (iflag & INLCR)
13666261Sjkh	    opt |= 1 << 5;
13676261Sjkh#endif
13686261Sjkh	if (iflag & IGNBRK)
13696261Sjkh	    opt |= 1 << 4;
13706261Sjkh	if (!(iflag & BRKINT))
13716261Sjkh	    opt |= 1 << 3;
13726261Sjkh	if (iflag & IGNPAR)
13736261Sjkh#ifdef LogOverruns
13746261Sjkh	    opt |= 0;		/* broken chars cause receive exceptions */
13756261Sjkh#else
13766261Sjkh	    opt |= 2;		/* discard broken chars */
13776261Sjkh#endif
13786261Sjkh	else {
13796261Sjkh	    if (iflag & PARMRK)
13806261Sjkh		opt |= 4;	/* precede broken chars with 0xff 0x0 */
13816261Sjkh	    else
13826261Sjkh#ifdef LogOverruns
13836261Sjkh		opt |= 0;	/* broken chars cause receive exceptions */
13846261Sjkh#else
13856261Sjkh		opt |= 3;	/* convert framing/parity errs to nulls */
13866261Sjkh#endif
13876261Sjkh	}
13886261Sjkh	*(base + CD1400_COR4) = opt;
13896261Sjkh
13906261Sjkh	/*
13916261Sjkh	 * set channel option register 5 -
13926261Sjkh	 */
13936261Sjkh	opt = 0;
13946261Sjkh	if (iflag & ISTRIP)
13956261Sjkh	    opt |= 1 << 7;
13966261Sjkh	if (t->c_iflag & IEXTEN) {
13976261Sjkh	    opt |= 1 << 6;	/* enable LNEXT (e.g. ctrl-v quoting) handling */
13986261Sjkh	}
13996261Sjkh#ifdef Smarts
14006261Sjkh	if (t->c_oflag & ONLCR)
14016261Sjkh	    opt |= 1 << 1;
14026261Sjkh	if (t->c_oflag & OCRNL)
14036261Sjkh	    opt |= 1;
14046261Sjkh#endif
14056261Sjkh	*(base + CD1400_COR5) = opt;
14066261Sjkh
14076261Sjkh	/*
14086261Sjkh	 * set modem change option register 1
14096261Sjkh	 *	generate modem interrupts on which 1 -> 0 input transitions
14106261Sjkh	 *	also controls auto-DTR output flow-control, which we don't use
14116261Sjkh	 */
14126261Sjkh	opt = (cflag & CLOCAL) ? 0 : 1 << 4;	/* CD */
14136261Sjkh	*(base + CD1400_MCOR1) = opt;
14146261Sjkh
14156261Sjkh	/*
14166261Sjkh	 * set modem change option register 2
14176261Sjkh	 *	generate modem interrupts on specific 0 -> 1 input transitions
14186261Sjkh	 */
14196261Sjkh	opt = (cflag & CLOCAL) ? 0 : 1 << 4;	/* CD */
14206261Sjkh	*(base + CD1400_MCOR2) = opt;
14216261Sjkh
14226261Sjkh	splx(s);
14236261Sjkh
14246261Sjkh	return 0;
14256261Sjkh} /* end of cyparam */
14266261Sjkh
14276261Sjkh
14286261Sjkhvoid
14296261Sjkhcystart(struct tty *tp)
14306261Sjkh{
14316261Sjkh	u_char		unit = UNIT(tp->t_dev);
14326261Sjkh	struct cy	*infop = info[unit];
14336261Sjkh	cy_addr		base = infop->base_addr;
14346261Sjkh	int		s;
14356261Sjkh
14366261Sjkh#ifdef CyDebug
14376261Sjkh	infop->start_count++;
14386261Sjkh#endif
14396261Sjkh
14406261Sjkh	/* check the flow-control situation */
14416261Sjkh	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
14426261Sjkh		return;
14436261Sjkh
14446261Sjkh	if (tp->t_outq.c_cc <= tp->t_lowat) {
14456261Sjkh		if (tp->t_state&TS_ASLEEP) {
14466261Sjkh			tp->t_state &= ~TS_ASLEEP;
14476261Sjkh			wakeup((caddr_t)&tp->t_outq);
14486261Sjkh		}
14496261Sjkh		selwakeup(&tp->t_wsel);
14506261Sjkh	}
14516261Sjkh
14526261Sjkh#ifdef TxBuffer
14536261Sjkh	service_upper_tx(unit);		/* feed the monster */
14546261Sjkh#endif
14556261Sjkh
14566261Sjkh	s = spltty();
14576261Sjkh
14586261Sjkh	if (!(infop->intr_enable & (1 << 2))) {
14596261Sjkh	    /* select the channel */
14606261Sjkh	    *(base + CD1400_CAR) = unit & (u_char)3;
14616261Sjkh
14626261Sjkh	    /* (re)enable interrupts to set things in motion */
14636261Sjkh	    infop->intr_enable |= (1 << 2);
14646261Sjkh	    *(base + CD1400_SRER) = infop->intr_enable;
14656261Sjkh
14666261Sjkh	    infop->start_real++;
14676261Sjkh	}
14686261Sjkh
14696261Sjkh	splx(s);
14706261Sjkh} /* end of cystart() */
14716261Sjkh
14726261Sjkh
14736261Sjkhint
14746261Sjkhcystop(struct tty *tp, int flag)
14756261Sjkh{
14766261Sjkh	u_char		unit = UNIT(tp->t_dev);
14776261Sjkh	struct cy	*ip = info[unit];
14786261Sjkh	cy_addr		base = ip->base_addr;
14796261Sjkh	int		s;
14806261Sjkh
14816261Sjkh	s = spltty();
14826261Sjkh
14836261Sjkh	/* select the channel */
14846261Sjkh	*(base + CD1400_CAR) = unit & 3;
14856261Sjkh
14866261Sjkh	/* halt output by disabling transmit interrupts */
14876261Sjkh	ip->intr_enable &=~ (1 << 2);
14886261Sjkh	*(base + CD1400_SRER) = ip->intr_enable;
14896261Sjkh
14906261Sjkh	splx(s);
14916261Sjkh
14926261Sjkh	return 0;
14936261Sjkh}
14946261Sjkh
14956261Sjkh
14966261Sjkhint
14976261Sjkhcyselect(dev_t dev, int rw, struct proc *p)
14986261Sjkh{
14996261Sjkh	struct tty	*tp = info[UNIT(dev)]->tty;
15006261Sjkh	int		s = spltty();
15016261Sjkh	int		nread;
15026261Sjkh
15036261Sjkh	switch (rw) {
15046261Sjkh
15056261Sjkh	case FREAD:
15066261Sjkh		nread = ttnread(tp);
15076261Sjkh		if (nread > 0 ||
15086261Sjkh		   ((tp->t_cflag&CLOCAL) == 0 && (tp->t_state&TS_CARR_ON) == 0))
15096261Sjkh			goto win;
15106261Sjkh		selrecord(p, &tp->t_rsel);
15116261Sjkh		break;
15126261Sjkh
15136261Sjkh	case FWRITE:
15146261Sjkh		if (tp->t_outq.c_cc <= tp->t_lowat)
15156261Sjkh			goto win;
15166261Sjkh		selrecord(p, &tp->t_wsel);
15176261Sjkh		break;
15186261Sjkh	}
15196261Sjkh	splx(s);
15206261Sjkh	return (0);
15216261Sjkh  win:
15226261Sjkh	splx(s);
15236261Sjkh	return (1);
15246261Sjkh} /* end of cyselect() */
15256261Sjkh
15266261Sjkh
15276261Sjkhint
15286261Sjkhcyspeed(int speed, int *prescaler_io)
15296261Sjkh{
15306261Sjkh    int		actual;
15316261Sjkh    int		error;
15326261Sjkh    int		divider;
15336261Sjkh    int		prescaler;
15346261Sjkh    int		prescaler_unit;
15356261Sjkh
15366261Sjkh    if (speed == 0)
15376261Sjkh	return 0;
15386261Sjkh
15396261Sjkh    if (speed < 0 || speed > 150000)
15406261Sjkh	return -1;
15416261Sjkh
15426261Sjkh    /* determine which prescaler to use */
15436261Sjkh    for (prescaler_unit = 4, prescaler = 2048; prescaler_unit;
15446261Sjkh	    prescaler_unit--, prescaler >>= 2) {
15456261Sjkh	if (CYCLOM_CLOCK/prescaler/speed > 63)
15466261Sjkh	    break;
15476261Sjkh    }
15486261Sjkh
15496261Sjkh    divider = (CYCLOM_CLOCK/prescaler*2/speed + 1)/2;	/* round off */
15506261Sjkh    if (divider > 255)
15516261Sjkh	divider = 255;
15526261Sjkh    actual = CYCLOM_CLOCK/prescaler/divider;
15536261Sjkh    error = ((actual-speed)*2000/speed +1)/2;	/* percentage */
15546261Sjkh
15556261Sjkh    /* 3.0% max error tolerance */
15566261Sjkh    if (error < -30 || error > 30)
15576261Sjkh	return -1;
15586261Sjkh
15596261Sjkh#if 0
15606261Sjkh    printf("prescaler = %d (%d)\n", prescaler, prescaler_unit);
15616261Sjkh    printf("divider = %d (%x)\n", divider, divider);
15626261Sjkh    printf("actual = %d\n", actual);
15636261Sjkh    printf("error = %d\n", error);
15646261Sjkh#endif
15656261Sjkh
15666261Sjkh    *prescaler_io = prescaler_unit;
15676261Sjkh    return divider;
15686261Sjkh} /* end of cyspeed() */
15696261Sjkh
15706261Sjkh
15716261Sjkhstatic void
15726261Sjkhcd1400_channel_cmd(cy_addr base, u_char cmd)
15736261Sjkh{
15746261Sjkh 	/* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed,
15756261Sjkh	   as the card is probed every round?  Replaced delaycount with 8k.
15766261Sjkh	   Either delaycount has to be implemented in FreeBSD or more sensible
15776261Sjkh	   way of doing these should be implemented.  DELAY isn't enough here.
15786261Sjkh	   */
15796261Sjkh	unsigned maxwait = 5 * 8 * 1024;	/* approx. 5 ms */
15806261Sjkh
15816261Sjkh	/* wait for processing of previous command to complete */
15826261Sjkh	while (*(base + CD1400_CCR) && maxwait--)
15836261Sjkh	  	;
15846261Sjkh
15856261Sjkh	if (!maxwait)
15866261Sjkh		log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n",
15876261Sjkh		    5 * 8 * 1024);
15886261Sjkh
15896261Sjkh	*(base + CD1400_CCR) = cmd;
15906261Sjkh} /* end of cd1400_channel_cmd() */
15916261Sjkh
15926261Sjkh
15936261Sjkh#ifdef CyDebug
15946261Sjkh/* useful in ddb */
15956261Sjkhvoid
15966261Sjkhcyclear(void)
15976261Sjkh{
15986261Sjkh    /* clear the timeout request */
15996261Sjkh    disable_intr();
16006261Sjkh    timeout_scheduled = 0;
16016261Sjkh    enable_intr();
16026261Sjkh}
16036261Sjkh
16046261Sjkhvoid
16056261Sjkhcyclearintr(void)
16066261Sjkh{
16076261Sjkh    /* clear interrupts */
16086261Sjkh    *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0;
16096261Sjkh}
16106261Sjkh
16116261Sjkhint
16126261Sjkhcyparam_dummy(struct tty *tp, struct termios *t)
16136261Sjkh{
16146261Sjkh    return 0;
16156261Sjkh}
16166261Sjkh
16176261Sjkhvoid
16186261Sjkhcyset(int unit, int active)
16196261Sjkh{
16206261Sjkh    if (unit < 0 || unit > PORTS_PER_CYCLOM) {
16216261Sjkh	printf("bad unit number %d\n", unit);
16226261Sjkh	return;
16236261Sjkh    }
16246261Sjkh    cy_tty[unit]->t_param = active ? cyparam : cyparam_dummy;
16256261Sjkh}
16266261Sjkh
16276261Sjkh
16286261Sjkh/* useful in ddb */
16296261Sjkhvoid
16306261Sjkhcystatus(int unit)
16316261Sjkh{
16326261Sjkh	struct cy	*infop = info[unit];
16336261Sjkh	struct tty	*tp = infop->tty;
16346261Sjkh	cy_addr		base = infop->base_addr;
16356261Sjkh
16366261Sjkh	printf("info for channel %d\n", unit);
16376261Sjkh	printf("------------------\n");
16386261Sjkh
16396261Sjkh	printf("cd1400 base address:\t0x%x\n", (int)infop->base_addr);
16406261Sjkh
16416261Sjkh	/* select the port */
16426261Sjkh	*(base + CD1400_CAR) = (u_char)unit;
16436261Sjkh
16446261Sjkh	printf("saved channel_control:\t%02x\n", infop->channel_control);
16456261Sjkh	printf("saved cor1:\t\t%02x\n", infop->cor[0]);
16466261Sjkh	printf("service request enable reg:\t%02x (%02x cached)\n",
16476261Sjkh		(u_char)*(base + CD1400_SRER), infop->intr_enable);
16486261Sjkh	printf("service request register:\t%02x\n",
16496261Sjkh		(u_char)*(base + CD1400_SVRR));
16506261Sjkh	printf("\n");
16516261Sjkh	printf("modem status:\t\t\t%02x (%02x cached)\n",
16526261Sjkh		(u_char)*(base + CD1400_MSVR), infop->modem_sig);
16536261Sjkh	printf("rx/tx/mdm interrupt registers:\t%02x %02x %02x\n",
16546261Sjkh		(u_char)*(base + CD1400_RIR), (u_char)*(base + CD1400_TIR),
16556261Sjkh		(u_char)*(base + CD1400_MIR));
16566261Sjkh	printf("\n");
16576261Sjkh	if (tp) {
16586261Sjkh		printf("tty state:\t\t\t%04x\n", tp->t_state);
16596261Sjkh		printf("upper layer queue lengths:\t%d raw, %d canon, %d output\n",
16606261Sjkh		    tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc);
16616261Sjkh	}
16626261Sjkh	else
16636261Sjkh		printf("tty state:\t\t\tclosed\n");
16646261Sjkh	printf("\n");
16656261Sjkh
16666261Sjkh	printf("calls to cystart():\t\t%d (%d useful)\n",
16676261Sjkh		infop->start_count, infop->start_real);
16686261Sjkh	printf("\n");
16696261Sjkh	printf("total cyclom service probes:\t%d\n", cy_svrr_probes);
16706261Sjkh	printf("calls to upper layer:\t\t%d\n", cy_timeouts);
16716261Sjkh	printf("rx buffer chars free:\t\t%d\n", infop->rx_buf->free);
16726261Sjkh#ifdef TxBuffer
16736261Sjkh	printf("tx buffer chars used:\t\t%d\n", infop->tx_buf.used);
16746261Sjkh#endif
16756261Sjkh	printf("received chars:\t\t\t%d good, %d exception\n",
16766261Sjkh		infop->recv_normal, infop->recv_exception);
16776261Sjkh	printf("transmitted chars:\t\t%d\n", infop->xmit);
16786261Sjkh	printf("modem signal deltas:\t\t%d\n", infop->mdm);
16796261Sjkh	printf("\n");
16806261Sjkh} /* end of cystatus() */
16816261Sjkh#endif
16826261Sjkh#endif /* NCY > 0 */
1683