digi.c revision 136680
176195Sbrian/*-
276195Sbrian * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
376358Sbrian *   based on work by Slawa Olhovchenkov
476358Sbrian *                    John Prince <johnp@knight-trosoft.com>
576358Sbrian *                    Eric Hernes
676195Sbrian * All rights reserved.
776195Sbrian *
876195Sbrian * Redistribution and use in source and binary forms, with or without
976195Sbrian * modification, are permitted provided that the following conditions
1076195Sbrian * are met:
1176195Sbrian * 1. Redistributions of source code must retain the above copyright
1276195Sbrian *    notice, this list of conditions and the following disclaimer.
1376195Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1476195Sbrian *    notice, this list of conditions and the following disclaimer in the
1576195Sbrian *    documentation and/or other materials provided with the distribution.
1676195Sbrian *
1776195Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1876195Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1976195Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2076195Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2176195Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2276195Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2376195Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2476195Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2576195Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2676195Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2776195Sbrian * SUCH DAMAGE.
2876195Sbrian *
2976195Sbrian * $FreeBSD: head/sys/dev/digi/digi.c 136680 2004-10-18 21:51:27Z phk $
3076195Sbrian */
3176195Sbrian
3276195Sbrian/*-
3376195Sbrian * TODO:
3476195Sbrian *	Figure out what the con bios stuff is supposed to do
3576195Sbrian *	Test with *LOTS* more cards - I only have a PCI8r and an ISA Xem.
3676195Sbrian */
3776195Sbrian
3890684Sbde#include "opt_compat.h"
3990684Sbde
4076195Sbrian#include <sys/param.h>
4176195Sbrian#include <sys/systm.h>
4276195Sbrian#include <sys/proc.h>
4376195Sbrian#include <sys/conf.h>
4476195Sbrian#include <sys/linker.h>
4576195Sbrian#include <sys/kernel.h>
4676195Sbrian#include <sys/mbuf.h>
4776195Sbrian#include <sys/malloc.h>
48129879Sphk#include <sys/module.h>
4976195Sbrian#include <sys/tty.h>
5076195Sbrian#include <sys/syslog.h>
5176195Sbrian#include <sys/fcntl.h>
52131095Sphk#include <sys/serial.h>
5376195Sbrian#include <sys/bus.h>
5476195Sbrian#include <machine/resource.h>
5576195Sbrian
5676848Sbrian#include <sys/digiio.h>
5776853Sbrian#include <dev/digi/digireg.h>
5876853Sbrian#include <dev/digi/digi.h>
5976853Sbrian#include <dev/digi/digi_mod.h>
6076853Sbrian#include <dev/digi/digi_pci.h>
6176195Sbrian
62136200Sphkstatic t_open_t		digiopen;
63136200Sphkstatic d_open_t		digicopen;
64136200Sphkstatic d_close_t	digicclose;
65136200Sphkstatic t_ioctl_t	digiioctl;
66136200Sphkstatic d_ioctl_t	digisioctl;
67136200Sphkstatic d_ioctl_t	digicioctl;
6876195Sbrian
6976195Sbrianstatic void	digistop(struct tty *tp, int rw);
70131373Sphkstatic void	digibreak(struct tty *tp, int brk);
71131095Sphkstatic int	digimodem(struct tty *tp, int sigon, int sigoff);
7276195Sbrianstatic void	digi_poll(void *ptr);
7376195Sbrianstatic void	digi_freemoduledata(struct digi_softc *);
7476195Sbrianstatic void	fepcmd(struct digi_p *port, int cmd, int op, int ncmds);
7576195Sbrianstatic void	digistart(struct tty *tp);
7676195Sbrianstatic int	digiparam(struct tty *tp, struct termios *t);
77136200Sphkstatic void	digiclose(struct tty *tp);
7876195Sbrianstatic void	digi_intr(void *);
7976195Sbrianstatic int	digi_init(struct digi_softc *_sc);
8076195Sbrianstatic int	digi_loadmoduledata(struct digi_softc *);
8176195Sbrianstatic int	digi_inuse(struct digi_softc *);
8276195Sbrianstatic void	digi_free_state(struct digi_softc *);
8376195Sbrian
8476195Sbrian#define	fepcmd_b(port, cmd, op1, op2, ncmds) \
8576195Sbrian	fepcmd(port, cmd, (op2 << 8) | op1, ncmds)
8676195Sbrian#define	fepcmd_w	fepcmd
8776195Sbrian
8876195Sbrianstruct con_bios {
8976195Sbrian	struct con_bios *next;
9076195Sbrian	u_char *bios;
9176195Sbrian	size_t size;
9276195Sbrian};
9376195Sbrian
9489062Smsmithstatic struct con_bios *con_bios_list;
9591445Speterdevclass_t	 digi_devclass;
9676195Sbrianstatic char	 driver_name[] = "digi";
9776195Sbrianunsigned 	 digi_debug = 0;
9876195Sbrian
9976195Sbrianstatic struct speedtab digispeedtab[] = {
10076195Sbrian	{ 0,		0},			/* old (sysV-like) Bx codes */
10176195Sbrian	{ 50,		1},
10276195Sbrian	{ 75,		2},
10376195Sbrian	{ 110,		3},
10476195Sbrian	{ 134,		4},
10576195Sbrian	{ 150,		5},
10676195Sbrian	{ 200,		6},
10776195Sbrian	{ 300,		7},
10876195Sbrian	{ 600,		8},
10976195Sbrian	{ 1200,		9},
11076195Sbrian	{ 1800,		10},
11176195Sbrian	{ 2400,		11},
11276195Sbrian	{ 4800,		12},
11376195Sbrian	{ 9600,		13},
11476195Sbrian	{ 19200,	14},
11576195Sbrian	{ 38400,	15},
11676195Sbrian	{ 57600,	(02000 | 1)},
11776195Sbrian	{ 76800,	(02000 | 2)},
11876195Sbrian	{ 115200,	(02000 | 3)},
11976195Sbrian	{ 230400,	(02000 | 6)},
12076195Sbrian	{ -1,		-1}
12176195Sbrian};
12276195Sbrian
12376195Sbrianconst struct digi_control_signals digi_xixe_signals = {
12476195Sbrian	0x02, 0x08, 0x10, 0x20, 0x40, 0x80
12576195Sbrian};
12676195Sbrian
12776195Sbrianconst struct digi_control_signals digi_normal_signals = {
12876195Sbrian	0x02, 0x80, 0x20, 0x10, 0x40, 0x01
12976195Sbrian};
13076195Sbrian
131136200Sphkstatic struct cdevsw digi_csw = {
132126080Sphk	.d_version =	D_VERSION,
133136200Sphk	.d_open =	digicopen,
134136200Sphk	.d_close =	digicclose,
135136200Sphk	.d_ioctl =	digicioctl,
136111815Sphk	.d_name =	driver_name,
137126080Sphk	.d_flags =	D_TTY | D_NEEDGIANT,
13876195Sbrian};
13976195Sbrian
14076195Sbrianstatic void
14176195Sbriandigi_poll(void *ptr)
14276195Sbrian{
14376195Sbrian	struct digi_softc *sc;
14476195Sbrian
14576195Sbrian	sc = (struct digi_softc *)ptr;
14676195Sbrian	callout_handle_init(&sc->callout);
14776195Sbrian	digi_intr(sc);
14876195Sbrian	sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1);
14976195Sbrian}
15076195Sbrian
15176195Sbrianstatic void
15276195Sbriandigi_int_test(void *v)
15376195Sbrian{
15476195Sbrian	struct digi_softc *sc = v;
15576195Sbrian
15676195Sbrian	callout_handle_init(&sc->inttest);
15776195Sbrian#ifdef DIGI_INTERRUPT
15876195Sbrian	if (sc->intr_timestamp.tv_sec || sc->intr_timestamp.tv_usec) {
15976195Sbrian		/* interrupt OK! */
16076195Sbrian		return;
16176195Sbrian	}
16276195Sbrian	log(LOG_ERR, "digi%d: Interrupt didn't work, use polled mode\n", unit);
16376195Sbrian#endif
16476195Sbrian	sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1);
16576195Sbrian}
16676195Sbrian
16776195Sbrianstatic void
16876195Sbriandigi_freemoduledata(struct digi_softc *sc)
16976195Sbrian{
17076195Sbrian	if (sc->fep.data != NULL) {
17176195Sbrian		free(sc->fep.data, M_TTYS);
17276195Sbrian		sc->fep.data = NULL;
17376195Sbrian	}
17476195Sbrian	if (sc->link.data != NULL) {
17576195Sbrian		free(sc->link.data, M_TTYS);
17676195Sbrian		sc->link.data = NULL;
17776195Sbrian	}
17876195Sbrian	if (sc->bios.data != NULL) {
17976195Sbrian		free(sc->bios.data, M_TTYS);
18076195Sbrian		sc->bios.data = NULL;
18176195Sbrian	}
18276195Sbrian}
18376195Sbrian
18476195Sbrianstatic int
18576195Sbriandigi_bcopy(const void *vfrom, void *vto, size_t sz)
18676195Sbrian{
18776195Sbrian	volatile const char *from = (volatile const char *)vfrom;
18876195Sbrian	volatile char *to = (volatile char *)vto;
18976195Sbrian	size_t i;
19076195Sbrian
19176195Sbrian	for (i = 0; i < sz; i++)
19276195Sbrian		*to++ = *from++;
19376195Sbrian
19476195Sbrian	from = (const volatile char *)vfrom;
19576195Sbrian	to = (volatile char *)vto;
19676195Sbrian	for (i = 0; i < sz; i++)
19776195Sbrian		if (*to++ != *from++)
19876195Sbrian			return (0);
19976195Sbrian	return (1);
20076195Sbrian}
20176195Sbrian
20294358Sbrianvoid
20394361Sbriandigi_delay(struct digi_softc *sc, const char *txt, u_long timo)
20494340Sbrian{
20594340Sbrian	if (cold)
20694361Sbrian		DELAY(timo * 1000000 / hz);
20794340Sbrian	else
20894361Sbrian		tsleep(sc, PUSER | PCATCH, txt, timo);
20994340Sbrian}
21094340Sbrian
21176195Sbrianstatic int
21276195Sbriandigi_init(struct digi_softc *sc)
21376195Sbrian{
21476195Sbrian	int i, cnt, resp;
21576195Sbrian	u_char *ptr;
21676195Sbrian	int lowwater;
21776195Sbrian	struct digi_p *port;
21876195Sbrian	volatile struct board_chan *bc;
219136200Sphk	struct tty *tp;
22076195Sbrian
22176195Sbrian	ptr = NULL;
22276195Sbrian
22376195Sbrian	if (sc->status == DIGI_STATUS_DISABLED) {
22476195Sbrian		log(LOG_ERR, "digi%d: Cannot init a disabled card\n",
22576195Sbrian		    sc->res.unit);
22676195Sbrian		return (EIO);
22776195Sbrian	}
22876195Sbrian	if (sc->bios.data == NULL) {
22976195Sbrian		log(LOG_ERR, "digi%d: Cannot init without BIOS\n",
23076195Sbrian		    sc->res.unit);
23176195Sbrian		return (EIO);
23276195Sbrian	}
23376195Sbrian#if 0
23476195Sbrian	if (sc->link.data == NULL && sc->model >= PCCX) {
23576195Sbrian		log(LOG_ERR, "digi%d: Cannot init without link info\n",
23676195Sbrian		    sc->res.unit);
23776195Sbrian		return (EIO);
23876195Sbrian	}
23976195Sbrian#endif
24076195Sbrian	if (sc->fep.data == NULL) {
24176195Sbrian		log(LOG_ERR, "digi%d: Cannot init without fep code\n",
24276195Sbrian		    sc->res.unit);
24376195Sbrian		return (EIO);
24476195Sbrian	}
24576195Sbrian	sc->status = DIGI_STATUS_NOTINIT;
24676195Sbrian
24776195Sbrian	if (sc->numports) {
24876195Sbrian		/*
24976195Sbrian		 * We're re-initialising - maybe because someone's attached
25076195Sbrian		 * another port module.  For now, we just re-initialise
25176195Sbrian		 * everything.
25276195Sbrian		 */
25376195Sbrian		if (digi_inuse(sc))
25476195Sbrian			return (EBUSY);
25576195Sbrian
25676195Sbrian		digi_free_state(sc);
25776195Sbrian	}
25876195Sbrian
25976195Sbrian	ptr = sc->setwin(sc, MISCGLOBAL);
26076195Sbrian	for (i = 0; i < 16; i += 2)
26176195Sbrian		vW(ptr + i) = 0;
26276195Sbrian
26376195Sbrian	switch (sc->model) {
26476195Sbrian	case PCXEVE:
26576195Sbrian		outb(sc->wport, 0xff);		/* window 7 */
26676195Sbrian		ptr = sc->vmem + (BIOSCODE & 0x1fff);
26776195Sbrian
26876195Sbrian		if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) {
26976195Sbrian			device_printf(sc->dev, "BIOS upload failed\n");
27076195Sbrian			return (EIO);
27176195Sbrian		}
27276195Sbrian
27376195Sbrian		outb(sc->port, FEPCLR);
27476195Sbrian		break;
27576195Sbrian
27676195Sbrian	case PCXE:
27776195Sbrian	case PCXI:
27876195Sbrian	case PCCX:
27976195Sbrian		ptr = sc->setwin(sc, BIOSCODE + ((0xf000 - sc->mem_seg) << 4));
28076195Sbrian		if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) {
28176195Sbrian			device_printf(sc->dev, "BIOS upload failed\n");
28276195Sbrian			return (EIO);
28376195Sbrian		}
28476195Sbrian		break;
28576195Sbrian
28676195Sbrian	case PCXEM:
28776195Sbrian	case PCIEPCX:
28876195Sbrian	case PCIXR:
28994323Sbrian		if (sc->pcibus)
29076195Sbrian			PCIPORT = FEPRST;
29176195Sbrian		else
29276195Sbrian			outb(sc->port, FEPRST | FEPMEM);
29376195Sbrian
29494323Sbrian		for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) &
29576195Sbrian		    FEPMASK) != FEPRST; i++) {
29694949Sbrian			if (i > hz) {
29794323Sbrian				log(LOG_ERR, "digi%d: %s init reset failed\n",
29894323Sbrian				    sc->res.unit, sc->name);
29976195Sbrian				return (EIO);
30076195Sbrian			}
30194361Sbrian			digi_delay(sc, "digiinit0", 5);
30276195Sbrian		}
30376195Sbrian		DLOG(DIGIDB_INIT, (sc->dev, "Got init reset after %d us\n", i));
30476195Sbrian
30576195Sbrian		/* Now upload the BIOS */
30676195Sbrian		cnt = (sc->bios.size < sc->win_size - BIOSOFFSET) ?
30776195Sbrian		    sc->bios.size : sc->win_size - BIOSOFFSET;
30876195Sbrian
30976195Sbrian		ptr = sc->setwin(sc, BIOSOFFSET);
31076195Sbrian		if (!digi_bcopy(sc->bios.data, ptr, cnt)) {
31176195Sbrian			device_printf(sc->dev, "BIOS upload (1) failed\n");
31276195Sbrian			return (EIO);
31376195Sbrian		}
31476195Sbrian
31576195Sbrian		if (cnt != sc->bios.size) {
31676195Sbrian			/* and the second part */
31776195Sbrian			ptr = sc->setwin(sc, sc->win_size);
31876195Sbrian			if (!digi_bcopy(sc->bios.data + cnt, ptr,
31976195Sbrian			    sc->bios.size - cnt)) {
32076195Sbrian				device_printf(sc->dev, "BIOS upload failed\n");
32176195Sbrian				return (EIO);
32276195Sbrian			}
32376195Sbrian		}
32476195Sbrian
32576195Sbrian		ptr = sc->setwin(sc, 0);
32676195Sbrian		vW(ptr + 0) = 0x0401;
32776195Sbrian		vW(ptr + 2) = 0x0bf0;
32876195Sbrian		vW(ptr + 4) = 0x0000;
32976195Sbrian		vW(ptr + 6) = 0x0000;
33076195Sbrian
33176195Sbrian		break;
33276195Sbrian	}
33376195Sbrian
33476195Sbrian	DLOG(DIGIDB_INIT, (sc->dev, "BIOS uploaded\n"));
33576195Sbrian
33676195Sbrian	ptr = sc->setwin(sc, MISCGLOBAL);
33776195Sbrian	W(ptr) = 0;
33876195Sbrian
33994323Sbrian	if (sc->pcibus) {
34076195Sbrian		PCIPORT = FEPCLR;
34176195Sbrian		resp = FEPRST;
34276195Sbrian	} else if (sc->model == PCXEVE) {
34376195Sbrian		outb(sc->port, FEPCLR);
34476195Sbrian		resp = FEPRST;
34576195Sbrian	} else {
34676195Sbrian		outb(sc->port, FEPCLR | FEPMEM);
34776195Sbrian		resp = FEPRST | FEPMEM;
34876195Sbrian	}
34976195Sbrian
35094323Sbrian	for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) & FEPMASK)
35176195Sbrian	    == resp; i++) {
35294949Sbrian		if (i > hz) {
35376195Sbrian			log(LOG_ERR, "digi%d: BIOS start failed\n",
35476195Sbrian			    sc->res.unit);
35576195Sbrian			return (EIO);
35676195Sbrian		}
35794361Sbrian		digi_delay(sc, "digibios0", 5);
35876195Sbrian	}
35976195Sbrian
36076195Sbrian	DLOG(DIGIDB_INIT, (sc->dev, "BIOS started after %d us\n", i));
36176195Sbrian
36276195Sbrian	for (i = 0; vW(ptr) != *(u_short *)"GD"; i++) {
36394949Sbrian		if (i > 2*hz) {
36476195Sbrian			log(LOG_ERR, "digi%d: BIOS boot failed "
36576195Sbrian			    "(0x%02x != 0x%02x)\n",
36676195Sbrian			    sc->res.unit, vW(ptr), *(u_short *)"GD");
36776195Sbrian			return (EIO);
36876195Sbrian		}
36994361Sbrian		digi_delay(sc, "digibios1", 5);
37076195Sbrian	}
37176195Sbrian
37276195Sbrian	DLOG(DIGIDB_INIT, (sc->dev, "BIOS booted after %d iterations\n", i));
37376195Sbrian
37476195Sbrian	if (sc->link.data != NULL) {
37576195Sbrian		DLOG(DIGIDB_INIT, (sc->dev, "Loading link data\n"));
37676195Sbrian		ptr = sc->setwin(sc, 0xcd0);
37776195Sbrian		digi_bcopy(sc->link.data, ptr, 21);	/* XXX 21 ? */
37876195Sbrian	}
37976195Sbrian
38076195Sbrian	/* load FEP/OS */
38176195Sbrian
38276195Sbrian	switch (sc->model) {
38376195Sbrian	case PCXE:
38476195Sbrian	case PCXEVE:
38576195Sbrian	case PCXI:
38676195Sbrian		ptr = sc->setwin(sc, sc->model == PCXI ? 0x2000 : 0x0);
38776195Sbrian		digi_bcopy(sc->fep.data, ptr, sc->fep.size);
38876195Sbrian
38976195Sbrian		/* A BIOS request to move our data to 0x2000 */
39076195Sbrian		ptr = sc->setwin(sc, MBOX);
39176195Sbrian		vW(ptr + 0) = 2;
39276195Sbrian		vW(ptr + 2) = sc->mem_seg + FEPCODESEG;
39376195Sbrian		vW(ptr + 4) = 0;
39476195Sbrian		vW(ptr + 6) = FEPCODESEG;
39576195Sbrian		vW(ptr + 8) = 0;
39676195Sbrian		vW(ptr + 10) = sc->fep.size;
39776195Sbrian
39876195Sbrian		/* Run the BIOS request */
39976195Sbrian		outb(sc->port, FEPREQ | FEPMEM);
40076195Sbrian		outb(sc->port, FEPCLR | FEPMEM);
40176195Sbrian
40276195Sbrian		for (i = 0; W(ptr); i++) {
40394949Sbrian			if (i > hz) {
40476195Sbrian				log(LOG_ERR, "digi%d: FEP/OS move failed\n",
40576195Sbrian				    sc->res.unit);
40676195Sbrian				sc->hidewin(sc);
40776195Sbrian				return (EIO);
40876195Sbrian			}
40994361Sbrian			digi_delay(sc, "digifep0", 5);
41076195Sbrian		}
41176195Sbrian		DLOG(DIGIDB_INIT,
41276195Sbrian		    (sc->dev, "FEP/OS moved after %d iterations\n", i));
41376195Sbrian
41476195Sbrian		/* Clear the confirm word */
41576195Sbrian		ptr = sc->setwin(sc, FEPSTAT);
41676195Sbrian		vW(ptr + 0) = 0;
41776195Sbrian
41876195Sbrian		/* A BIOS request to execute the FEP/OS */
41976195Sbrian		ptr = sc->setwin(sc, MBOX);
42076195Sbrian		vW(ptr + 0) = 0x01;
42176195Sbrian		vW(ptr + 2) = FEPCODESEG;
42276195Sbrian		vW(ptr + 4) = 0x04;
42376195Sbrian
42476195Sbrian		/* Run the BIOS request */
42576195Sbrian		outb(sc->port, FEPREQ);
42676195Sbrian		outb(sc->port, FEPCLR);
42776195Sbrian
42876195Sbrian		ptr = sc->setwin(sc, FEPSTAT);
42976195Sbrian
43076195Sbrian		break;
43176195Sbrian
43276195Sbrian	case PCXEM:
43376195Sbrian	case PCIEPCX:
43476195Sbrian	case PCIXR:
43576195Sbrian		DLOG(DIGIDB_INIT, (sc->dev, "Loading FEP/OS\n"));
43676195Sbrian
43776195Sbrian		cnt = (sc->fep.size < sc->win_size - BIOSOFFSET) ?
43876195Sbrian		    sc->fep.size : sc->win_size - BIOSOFFSET;
43976195Sbrian
44076195Sbrian		ptr = sc->setwin(sc, BIOSOFFSET);
44176195Sbrian		digi_bcopy(sc->fep.data, ptr, cnt);
44276195Sbrian
44376195Sbrian		if (cnt != sc->fep.size) {
44476195Sbrian			ptr = sc->setwin(sc, BIOSOFFSET + cnt);
44576195Sbrian			digi_bcopy(sc->fep.data + cnt, ptr,
44676195Sbrian			    sc->fep.size - cnt);
44776195Sbrian		}
44876195Sbrian
44976195Sbrian		DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS loaded\n"));
45076195Sbrian
45176195Sbrian		ptr = sc->setwin(sc, 0xc30);
45276195Sbrian		W(ptr + 4) = 0x1004;
45376195Sbrian		W(ptr + 6) = 0xbfc0;
45476195Sbrian		W(ptr + 0) = 0x03;
45576195Sbrian		W(ptr + 2) = 0x00;
45676195Sbrian
45776195Sbrian		/* Clear the confirm word */
45876195Sbrian		ptr = sc->setwin(sc, FEPSTAT);
45976195Sbrian		W(ptr + 0) = 0;
46076195Sbrian
46176195Sbrian		if (sc->port)
46276195Sbrian			outb(sc->port, 0);		/* XXX necessary ? */
46376195Sbrian
46476195Sbrian		break;
46576195Sbrian
46676195Sbrian	case PCCX:
46776195Sbrian		ptr = sc->setwin(sc, 0xd000);
46876195Sbrian		digi_bcopy(sc->fep.data, ptr, sc->fep.size);
46976195Sbrian
47076195Sbrian		/* A BIOS request to execute the FEP/OS */
47176195Sbrian		ptr = sc->setwin(sc, 0xc40);
47276195Sbrian		W(ptr + 0) = 1;
47376195Sbrian		W(ptr + 2) = FEPCODE >> 4;
47476195Sbrian		W(ptr + 4) = 4;
47576195Sbrian
47676195Sbrian		/* Clear the confirm word */
47776195Sbrian		ptr = sc->setwin(sc, FEPSTAT);
47876195Sbrian		W(ptr + 0) = 0;
47976195Sbrian
48076195Sbrian		/* Run the BIOS request */
48176195Sbrian		outb(sc->port, FEPREQ | FEPMEM); /* send interrupt to BIOS */
48276195Sbrian		outb(sc->port, FEPCLR | FEPMEM);
48376195Sbrian		break;
48476195Sbrian	}
48576195Sbrian
48676195Sbrian	/* Now wait 'till the FEP/OS has booted */
48776195Sbrian	for (i = 0; vW(ptr) != *(u_short *)"OS"; i++) {
48894949Sbrian		if (i > 2*hz) {
48976195Sbrian			log(LOG_ERR, "digi%d: FEP/OS start failed "
49076195Sbrian			    "(0x%02x != 0x%02x)\n",
49176195Sbrian			    sc->res.unit, vW(ptr), *(u_short *)"OS");
49276195Sbrian			sc->hidewin(sc);
49376195Sbrian			return (EIO);
49476195Sbrian		}
49594361Sbrian		digi_delay(sc, "digifep1", 5);
49676195Sbrian	}
49776195Sbrian
49876195Sbrian	DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS started after %d iterations\n", i));
49976195Sbrian
50076195Sbrian	if (sc->model >= PCXEM) {
50176195Sbrian		ptr = sc->setwin(sc, 0xe04);
50276195Sbrian		vW(ptr) = 2;
50376195Sbrian		ptr = sc->setwin(sc, 0xc02);
50476195Sbrian		sc->numports = vW(ptr);
50576195Sbrian	} else {
50676195Sbrian		ptr = sc->setwin(sc, 0xc22);
50776195Sbrian		sc->numports = vW(ptr);
50876195Sbrian	}
50976195Sbrian
51076195Sbrian	if (sc->numports == 0) {
51176195Sbrian		device_printf(sc->dev, "%s, 0 ports found\n", sc->name);
51276195Sbrian		sc->hidewin(sc);
51376195Sbrian		return (0);
51476195Sbrian	}
51576195Sbrian
516136200Sphk	device_printf(sc->dev, "%s, %d ports found\n", sc->name, sc->numports);
51776195Sbrian
51876195Sbrian	if (sc->ports)
51976195Sbrian		free(sc->ports, M_TTYS);
52077004Sbrian	sc->ports = malloc(sizeof(struct digi_p) * sc->numports,
521111119Simp	    M_TTYS, M_WAITOK | M_ZERO);
52276195Sbrian
52376195Sbrian	/*
52476195Sbrian	 * XXX Should read port 0xc90 for an array of 2byte values, 1 per
52576195Sbrian	 * port.  If the value is 0, the port is broken....
52676195Sbrian	 */
52776195Sbrian
52876195Sbrian	ptr = sc->setwin(sc, 0);
52976195Sbrian
53076195Sbrian	/* We should now init per-port structures */
53176195Sbrian	bc = (volatile struct board_chan *)(ptr + CHANSTRUCT);
53276195Sbrian	sc->gdata = (volatile struct global_data *)(ptr + FEP_GLOBAL);
53376195Sbrian
53476195Sbrian	sc->memcmd = ptr + sc->gdata->cstart;
53576195Sbrian	sc->memevent = ptr + sc->gdata->istart;
53676195Sbrian
53776195Sbrian	for (i = 0; i < sc->numports; i++, bc++) {
53876195Sbrian		port = sc->ports + i;
53976195Sbrian		port->pnum = i;
54076195Sbrian		port->sc = sc;
54176195Sbrian		port->status = ENABLED;
54276195Sbrian		port->bc = bc;
543136200Sphk		tp = port->tp = ttyalloc();
544136200Sphk		tp->t_oproc = digistart;
545136200Sphk		tp->t_param = digiparam;
546136200Sphk		tp->t_modem = digimodem;
547136200Sphk		tp->t_break = digibreak;
548136200Sphk		tp->t_stop = digistop;
549136200Sphk		tp->t_cioctl = digisioctl;
550136200Sphk		tp->t_ioctl = digiioctl;
551136200Sphk		tp->t_open = digiopen;
552136200Sphk		tp->t_close = digiclose;
553136200Sphk		tp->t_sc = port;
55476195Sbrian
55576195Sbrian		if (sc->model == PCXEVE) {
55676195Sbrian			port->txbuf = ptr +
55776195Sbrian			    (((bc->tseg - sc->mem_seg) << 4) & 0x1fff);
55876195Sbrian			port->rxbuf = ptr +
55976195Sbrian			    (((bc->rseg - sc->mem_seg) << 4) & 0x1fff);
56076195Sbrian			port->txwin = FEPWIN | ((bc->tseg - sc->mem_seg) >> 9);
56176195Sbrian			port->rxwin = FEPWIN | ((bc->rseg - sc->mem_seg) >> 9);
56276195Sbrian		} else if (sc->model == PCXI || sc->model == PCXE) {
56376195Sbrian			port->txbuf = ptr + ((bc->tseg - sc->mem_seg) << 4);
56476195Sbrian			port->rxbuf = ptr + ((bc->rseg - sc->mem_seg) << 4);
56576195Sbrian			port->txwin = port->rxwin = 0;
56676195Sbrian		} else {
56776195Sbrian			port->txbuf = ptr +
56876195Sbrian			    (((bc->tseg - sc->mem_seg) << 4) % sc->win_size);
56976195Sbrian			port->rxbuf = ptr +
57076195Sbrian			    (((bc->rseg - sc->mem_seg) << 4) % sc->win_size);
57176195Sbrian			port->txwin = FEPWIN |
57276195Sbrian			    (((bc->tseg - sc->mem_seg) << 4) / sc->win_size);
57376195Sbrian			port->rxwin = FEPWIN |
57476195Sbrian			    (((bc->rseg - sc->mem_seg) << 4) / sc->win_size);
57576195Sbrian		}
57676195Sbrian		port->txbufsize = bc->tmax + 1;
57776195Sbrian		port->rxbufsize = bc->rmax + 1;
57876195Sbrian
57976195Sbrian		lowwater = port->txbufsize >> 2;
58076195Sbrian		if (lowwater > 1024)
58176195Sbrian			lowwater = 1024;
58276195Sbrian		sc->setwin(sc, 0);
58376195Sbrian		fepcmd_w(port, STXLWATER, lowwater, 10);
58476195Sbrian		fepcmd_w(port, SRXLWATER, port->rxbufsize >> 2, 10);
58576195Sbrian		fepcmd_w(port, SRXHWATER, (3 * port->rxbufsize) >> 2, 10);
58676195Sbrian
58776195Sbrian		bc->edelay = 100;
58876195Sbrian
589136680Sphk		ttyinitmode(tp, 0, 0);
59076195Sbrian		port->send_ring = 1;	/* Default action on signal RI */
591136200Sphk		ttycreate(tp, NULL, 0, MINOR_CALLOUT, "D%r%r", sc->res.unit, i);
59276195Sbrian	}
59376195Sbrian
59476195Sbrian	sc->hidewin(sc);
59576195Sbrian	sc->inttest = timeout(digi_int_test, sc, hz);
59676195Sbrian	/* fepcmd_w(&sc->ports[0], 0xff, 0, 0); */
59776195Sbrian	sc->status = DIGI_STATUS_ENABLED;
59876195Sbrian
59976195Sbrian	return (0);
60076195Sbrian}
60176195Sbrian
60276195Sbrianstatic int
603131095Sphkdigimodem(struct tty *tp, int sigon, int sigoff)
60476195Sbrian{
605131095Sphk	struct digi_softc *sc;
606131095Sphk	struct digi_p *port;
607131095Sphk	int bitand, bitor, mstat;
60876195Sbrian
609136200Sphk	port = tp->t_sc;
610136200Sphk	sc = port->sc;
611131095Sphk
612131095Sphk	if (sigon == 0 && sigoff == 0) {
61376195Sbrian		port->sc->setwin(port->sc, 0);
61476195Sbrian		mstat = port->bc->mstat;
61576195Sbrian		port->sc->hidewin(port->sc);
61676195Sbrian		if (mstat & port->sc->csigs->rts)
617131095Sphk			sigon |= SER_RTS;
61878496Sbrian		if (mstat & port->cd)
619131095Sphk			sigon |= SER_DCD;
62078496Sbrian		if (mstat & port->dsr)
621131095Sphk			sigon |= SER_DSR;
62276195Sbrian		if (mstat & port->sc->csigs->cts)
623131095Sphk			sigon |= SER_CTS;
62476195Sbrian		if (mstat & port->sc->csigs->ri)
625131095Sphk			sigon |= SER_RI;
62676195Sbrian		if (mstat & port->sc->csigs->dtr)
627131095Sphk			sigon |= SER_DTR;
628131095Sphk		return (sigon);
62976195Sbrian	}
63076195Sbrian
631131095Sphk	bitand = 0;
632131095Sphk	bitor = 0;
63376195Sbrian
634131095Sphk	if (sigoff & SER_DTR)
635131095Sphk		bitand |= port->sc->csigs->dtr;
636131095Sphk	if (sigoff & SER_RTS)
637131095Sphk		bitand |= port->sc->csigs->rts;
638131095Sphk	if (sigon & SER_DTR)
639131095Sphk		bitor |= port->sc->csigs->dtr;
640131095Sphk	if (sigon & SER_RTS)
641131095Sphk		bitor |= port->sc->csigs->rts;
642131095Sphk	fepcmd_b(port, SETMODEM, bitor, ~bitand, 0);
64376195Sbrian	return (0);
64476195Sbrian}
64576195Sbrian
646104094Sphkstatic int
647136200Sphkdigicopen(struct cdev *dev, int flag, int mode, struct thread *td)
64876195Sbrian{
64976195Sbrian	struct digi_softc *sc;
650136200Sphk
651136200Sphk	sc = dev->si_drv1;
652136200Sphk	if (sc->status != DIGI_STATUS_ENABLED) {
653136200Sphk		DLOG(DIGIDB_OPEN, (sc->dev, "Cannot open a disabled card\n"));
654136200Sphk		return (ENXIO);
655136200Sphk	}
656136200Sphk	sc->opencnt++;
657136200Sphk	return (0);
658136200Sphk}
659136200Sphk
660136200Sphkstatic int
661136200Sphkdigiopen(struct tty *tp, struct cdev *dev)
662136200Sphk{
663136200Sphk	int error;
664136200Sphk	struct digi_softc *sc;
66576195Sbrian	struct digi_p *port;
66676195Sbrian	volatile struct board_chan *bc;
66776195Sbrian
668136200Sphk	port = tp->t_sc;
669136200Sphk	sc = port->sc;
67076195Sbrian
67176195Sbrian	if (sc->status != DIGI_STATUS_ENABLED) {
67276195Sbrian		DLOG(DIGIDB_OPEN, (sc->dev, "Cannot open a disabled card\n"));
67376195Sbrian		return (ENXIO);
67476195Sbrian	}
67576195Sbrian	bc = port->bc;
67676195Sbrian
677136200Sphk	/*
678136200Sphk	 * The device isn't open, so there are no conflicts.
679136200Sphk	 * Initialize it.  Initialization is done twice in many
680136200Sphk	 * cases: to preempt sleeping callin opens if we are callout,
681136200Sphk	 * and to complete a callin open after DCD rises.
682136200Sphk	 */
683136200Sphk	sc->setwin(sc, 0);
68476195Sbrian
685136200Sphk	bc->rout = bc->rin;	/* clear input queue */
686136200Sphk	bc->idata = 1;
687136200Sphk	bc->iempty = 1;
688136200Sphk	bc->ilow = 1;
689136200Sphk	bc->mint = port->cd | port->sc->csigs->ri;
690136200Sphk	bc->tin = bc->tout;
691136200Sphk	if (port->ialtpin) {
692136200Sphk		port->cd = sc->csigs->dsr;
693136200Sphk		port->dsr = sc->csigs->cd;
69476195Sbrian	} else {
695136200Sphk		port->cd = sc->csigs->cd;
696136200Sphk		port->dsr = sc->csigs->dsr;
69776195Sbrian	}
698136200Sphk	tp->t_wopeners++;			/* XXX required ? */
699136200Sphk	error = digiparam(tp, &tp->t_termios);
700136200Sphk	tp->t_wopeners--;
70176195Sbrian
70276195Sbrian	return (error);
70376195Sbrian}
70476195Sbrian
705104094Sphkstatic int
706136200Sphkdigicclose(struct cdev *dev, int flag, int mode, struct thread *td)
70776195Sbrian{
70876195Sbrian	struct digi_softc *sc;
70976195Sbrian
710136200Sphk	sc = dev->si_drv1;
711136200Sphk	sc->opencnt--;
71276195Sbrian	return (0);
71376195Sbrian}
71476195Sbrian
71576195Sbrianstatic void
71676195Sbriandigidtrwakeup(void *chan)
71776195Sbrian{
71876195Sbrian	struct digi_p *port = chan;
71976195Sbrian
72076195Sbrian	port->status &= ~DIGI_DTR_OFF;
721131981Sphk	wakeup(&port->tp->t_dtr_wait);
722136200Sphk	port->tp->t_wopeners--;
72376195Sbrian}
72476195Sbrian
72576195Sbrianstatic void
726136200Sphkdigiclose(struct tty *tp)
72776195Sbrian{
72876195Sbrian	volatile struct board_chan *bc;
729136200Sphk	struct digi_p *port;
73076195Sbrian	int s;
73176195Sbrian
732136200Sphk	port = tp->t_sc;
73376195Sbrian	bc = port->bc;
73476195Sbrian
73576195Sbrian	s = spltty();
73676195Sbrian	port->sc->setwin(port->sc, 0);
73776195Sbrian	bc->idata = 0;
73876195Sbrian	bc->iempty = 0;
73976195Sbrian	bc->ilow = 0;
74076195Sbrian	bc->mint = 0;
741136200Sphk	if ((tp->t_cflag & HUPCL) ||
742136200Sphk	    (!tp->t_actout && !(bc->mstat & port->cd) &&
743136200Sphk	    !(tp->t_init_in.c_cflag & CLOCAL)) ||
744136200Sphk	    !(tp->t_state & TS_ISOPEN)) {
745136200Sphk		digimodem(tp, 0, SER_DTR | SER_RTS);
746136200Sphk		if (tp->t_dtr_wait != 0) {
74776195Sbrian			/* Schedule a wakeup of any callin devices */
748136200Sphk			tp->t_wopeners++;
749136200Sphk			timeout(&digidtrwakeup, port, tp->t_dtr_wait);
75076195Sbrian			port->status |= DIGI_DTR_OFF;
75176195Sbrian		}
75276195Sbrian	}
753136200Sphk	tp->t_actout = FALSE;
754136200Sphk	wakeup(&tp->t_actout);
755136200Sphk	wakeup(TSA_CARR_ON(tp));
75676195Sbrian	splx(s);
75776195Sbrian}
75876195Sbrian
75976195Sbrian/*
76076195Sbrian * Load module "digi_<mod>.ko" and look for a symbol called digi_mod_<mod>.
76176195Sbrian *
76276195Sbrian * Populate sc->bios, sc->fep, and sc->link from this data.
76376195Sbrian *
76476195Sbrian * sc->fep.data, sc->bios.data and sc->link.data are malloc()d according
76576195Sbrian * to their respective sizes.
76676195Sbrian *
76776195Sbrian * The module is unloaded when we're done.
76876195Sbrian */
76976195Sbrianstatic int
77076195Sbriandigi_loadmoduledata(struct digi_softc *sc)
77176195Sbrian{
77276195Sbrian	struct digi_mod *digi_mod;
77376195Sbrian	linker_file_t lf;
77476195Sbrian	char *modfile, *sym;
77576195Sbrian	caddr_t symptr;
77678414Sbrian	int modlen, res;
77776195Sbrian
77876195Sbrian	KASSERT(sc->bios.data == NULL, ("Uninitialised BIOS variable"));
77976195Sbrian	KASSERT(sc->fep.data == NULL, ("Uninitialised FEP variable"));
78076195Sbrian	KASSERT(sc->link.data == NULL, ("Uninitialised LINK variable"));
78176195Sbrian	KASSERT(sc->module != NULL, ("Uninitialised module name"));
78276195Sbrian
78378414Sbrian	modlen = strlen(sc->module);
784111119Simp	modfile = malloc(modlen + 6, M_TEMP, M_WAITOK);
78578414Sbrian	snprintf(modfile, modlen + 6, "digi_%s", sc->module);
786132806Sphk	res = linker_reference_module(modfile, NULL, &lf);
787132806Sphk	if ((res = linker_reference_module(modfile, NULL, &lf)) != 0)
788132806Sphk		printf("%s: Failed %d to autoload module\n", modfile, res);
78976195Sbrian	free(modfile, M_TEMP);
79076195Sbrian	if (res != 0)
79176195Sbrian		return (res);
79276195Sbrian
793111119Simp	sym = malloc(modlen + 10, M_TEMP, M_WAITOK);
79478414Sbrian	snprintf(sym, modlen + 10, "digi_mod_%s", sc->module);
79576195Sbrian	if ((symptr = linker_file_lookup_symbol(lf, sym, 0)) == NULL)
79676195Sbrian		printf("digi_%s.ko: Symbol `%s' not found\n", sc->module, sym);
79776195Sbrian	free(sym, M_TEMP);
79876195Sbrian
79976195Sbrian	digi_mod = (struct digi_mod *)symptr;
80076195Sbrian	if (digi_mod->dm_version != DIGI_MOD_VERSION) {
80176195Sbrian		printf("digi_%s.ko: Invalid version %d (need %d)\n",
80276195Sbrian		    sc->module, digi_mod->dm_version, DIGI_MOD_VERSION);
803132117Sphk		linker_file_unload(lf, LINKER_UNLOAD_FORCE);
80476195Sbrian		return (EINVAL);
80576195Sbrian	}
80676195Sbrian
80776195Sbrian	sc->bios.size = digi_mod->dm_bios.size;
80876195Sbrian	if (sc->bios.size != 0 && digi_mod->dm_bios.data != NULL) {
809111119Simp		sc->bios.data = malloc(sc->bios.size, M_TTYS, M_WAITOK);
81076195Sbrian		bcopy(digi_mod->dm_bios.data, sc->bios.data, sc->bios.size);
81176195Sbrian	}
81276195Sbrian
81376195Sbrian	sc->fep.size = digi_mod->dm_fep.size;
81476195Sbrian	if (sc->fep.size != 0 && digi_mod->dm_fep.data != NULL) {
815111119Simp		sc->fep.data = malloc(sc->fep.size, M_TTYS, M_WAITOK);
81676195Sbrian		bcopy(digi_mod->dm_fep.data, sc->fep.data, sc->fep.size);
81776195Sbrian	}
81876195Sbrian
81976195Sbrian	sc->link.size = digi_mod->dm_link.size;
82076195Sbrian	if (sc->link.size != 0 && digi_mod->dm_link.data != NULL) {
821111119Simp		sc->link.data = malloc(sc->link.size, M_TTYS, M_WAITOK);
82276195Sbrian		bcopy(digi_mod->dm_link.data, sc->link.data, sc->link.size);
82376195Sbrian	}
82476195Sbrian
825132117Sphk	linker_file_unload(lf, LINKER_UNLOAD_FORCE);
82676195Sbrian
82776195Sbrian	return (0);
82876195Sbrian}
82976195Sbrian
83076195Sbrianstatic int
831136200Sphkdigisioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
83276195Sbrian{
833136200Sphk	struct digi_p *port;
83476195Sbrian	struct digi_softc *sc;
83576195Sbrian
836136200Sphk	port = dev->si_drv1;
837136200Sphk	sc = port->sc;
83876195Sbrian
839136200Sphk	switch (cmd) {
840136200Sphk	case DIGIIO_GETALTPIN:
841136200Sphk		if (ISINIT(dev))
842136200Sphk			*(int *)data = port->ialtpin;
843136200Sphk		else if (ISLOCK(dev))
844136200Sphk			*(int *)data = port->laltpin;
845136200Sphk		else
846136200Sphk			return (ENOTTY);
847136200Sphk		break;
848136200Sphk	case DIGIIO_SETALTPIN:
849136200Sphk		if (ISINIT(dev)) {
850136200Sphk			if (!port->laltpin) {
851136200Sphk				port->ialtpin = !!*(int *)data;
852136200Sphk				DLOG(DIGIDB_SET, (sc->dev,
853136200Sphk				    "port%d: initial ALTPIN %s\n", port->pnum,
854136200Sphk				    port->ialtpin ? "set" : "cleared"));
855136200Sphk			}
856136200Sphk		} else if (ISLOCK(dev)) {
857136200Sphk			port->laltpin = !!*(int *)data;
858136200Sphk			DLOG(DIGIDB_SET, (sc->dev,
859136200Sphk			    "port%d: ALTPIN %slocked\n",
860136200Sphk			    port->pnum, port->laltpin ? "" : "un"));
861136200Sphk		} else
862136200Sphk			return (ENOTTY);
863136200Sphk		break;
864136200Sphk	default:
865136200Sphk		return (ENOTTY);
866136200Sphk	}
867136200Sphk	return (0);
868136200Sphk}
86976195Sbrian
870136200Sphkstatic int
871136200Sphkdigicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
872136200Sphk{
873136200Sphk	int error;
874136200Sphk	struct digi_softc *sc;
875136200Sphk
876136200Sphk	sc = dev->si_drv1;
877136200Sphk
87876195Sbrian	if (sc->status == DIGI_STATUS_DISABLED)
87976195Sbrian		return (ENXIO);
88076195Sbrian
881136200Sphk	switch (cmd) {
882136200Sphk	case DIGIIO_DEBUG:
88376195Sbrian#ifdef DEBUG
884136200Sphk		digi_debug = *(int *)data;
885136200Sphk		return (0);
88676195Sbrian#else
887136200Sphk		device_printf(sc->dev, "DEBUG not defined\n");
888136200Sphk		return (ENXIO);
88976195Sbrian#endif
890136200Sphk	case DIGIIO_REINIT:
891136200Sphk		digi_loadmoduledata(sc);
892136200Sphk		error = digi_init(sc);
893136200Sphk		digi_freemoduledata(sc);
894136200Sphk		return (error);
89576195Sbrian
896136200Sphk	case DIGIIO_MODEL:
897136200Sphk		*(enum digi_model *)data = sc->model;
898136200Sphk		return (0);
89976195Sbrian
900136200Sphk	case DIGIIO_IDENT:
901136200Sphk		return (copyout(sc->name, *(char **)data,
902136200Sphk		    strlen(sc->name) + 1));
903136200Sphk	default:
904136200Sphk		return (ENOIOCTL);
90576195Sbrian	}
906136200Sphk}
90776195Sbrian
908136200Sphkstatic int
909136200Sphkdigiioctl(struct tty *tp, u_long cmd, void *data, int flag, struct thread *td)
910136200Sphk{
911136200Sphk	struct digi_softc *sc;
912136200Sphk	struct digi_p *port;
913136200Sphk
914136200Sphk	port = tp->t_sc;
915136200Sphk	sc = port->sc;
916136200Sphk	if (sc->status == DIGI_STATUS_DISABLED)
91776195Sbrian		return (ENXIO);
91876195Sbrian
91976195Sbrian	if (!(port->status & ENABLED))
92076195Sbrian		return (ENXIO);
92176195Sbrian
92278496Sbrian	switch (cmd) {
92378496Sbrian	case DIGIIO_GETALTPIN:
92478496Sbrian		*(int *)data = !!(port->dsr == sc->csigs->cd);
92578496Sbrian		return (0);
92678496Sbrian
92778496Sbrian	case DIGIIO_SETALTPIN:
92878496Sbrian		if (!port->laltpin) {
92978496Sbrian			if (*(int *)data) {
93078496Sbrian				DLOG(DIGIDB_SET, (sc->dev,
931136200Sphk				    "port%d: ALTPIN set\n", port->pnum));
93278496Sbrian				port->cd = sc->csigs->dsr;
93378496Sbrian				port->dsr = sc->csigs->cd;
93478496Sbrian			} else {
93578496Sbrian				DLOG(DIGIDB_SET, (sc->dev,
936136200Sphk				    "port%d: ALTPIN cleared\n", port->pnum));
93778496Sbrian				port->cd = sc->csigs->cd;
93878496Sbrian				port->dsr = sc->csigs->dsr;
93978496Sbrian			}
94078496Sbrian		}
94178496Sbrian		return (0);
94276195Sbrian	case DIGIIO_RING:
94376195Sbrian		port->send_ring = *(u_char *)data;
94476195Sbrian		break;
94576195Sbrian	default:
94676195Sbrian		return (ENOTTY);
94776195Sbrian	}
94876195Sbrian	return (0);
94976195Sbrian}
95076195Sbrian
951131373Sphkstatic void
952131095Sphkdigibreak(struct tty *tp, int brk)
953131095Sphk{
954131095Sphk	struct digi_p *port;
955131095Sphk
956136200Sphk	port = tp->t_sc;
957131095Sphk
958131095Sphk	/*
959131095Sphk	 * now it sends 400 millisecond break because I don't know
960131095Sphk	 * how to send an infinite break
961131095Sphk	 */
962131095Sphk	if (brk)
963131095Sphk		fepcmd_w(port, SENDBREAK, 400, 10);
964131095Sphk}
965131095Sphk
966131095Sphkstatic int
96776195Sbriandigiparam(struct tty *tp, struct termios *t)
96876195Sbrian{
96976195Sbrian	struct digi_softc *sc;
97076195Sbrian	struct digi_p *port;
97176195Sbrian	int cflag;
97276195Sbrian	int iflag;
97376195Sbrian	int hflow;
97476195Sbrian	int s;
97576195Sbrian	int window;
97676195Sbrian
977136200Sphk	port = tp->t_sc;
978136200Sphk	sc = port->sc;
979136200Sphk	DLOG(DIGIDB_SET, (sc->dev, "port%d: setting parameters\n", port->pnum));
98076195Sbrian
98176195Sbrian	if (t->c_ispeed == 0)
98276195Sbrian		t->c_ispeed = t->c_ospeed;
98376195Sbrian
98476195Sbrian	cflag = ttspeedtab(t->c_ospeed, digispeedtab);
98576195Sbrian
98676195Sbrian	if (cflag < 0 || (cflag > 0 && t->c_ispeed != t->c_ospeed))
98776195Sbrian		return (EINVAL);
98876195Sbrian
98976195Sbrian	s = splclock();
99076195Sbrian
99176195Sbrian	window = sc->window;
99276195Sbrian	sc->setwin(sc, 0);
99376195Sbrian
99476195Sbrian	if (cflag == 0) {				/* hangup */
995136200Sphk		DLOG(DIGIDB_SET, (sc->dev, "port%d: hangup\n", port->pnum));
996131095Sphk		digimodem(port->tp, 0, SER_DTR | SER_RTS);
99776195Sbrian	} else {
998131095Sphk		digimodem(port->tp, SER_DTR | SER_RTS, 0);
99976195Sbrian
1000136200Sphk		DLOG(DIGIDB_SET, (sc->dev, "port%d: CBAUD = %d\n", port->pnum,
100176195Sbrian		    cflag));
100276195Sbrian
100376195Sbrian#if 0
100476195Sbrian		/* convert flags to sysV-style values */
100576195Sbrian		if (t->c_cflag & PARODD)
100676195Sbrian			cflag |= 0x0200;
100776195Sbrian		if (t->c_cflag & PARENB)
100876195Sbrian			cflag |= 0x0100;
100976195Sbrian		if (t->c_cflag & CSTOPB)
101076195Sbrian			cflag |= 0x0080;
101176195Sbrian#else
101276195Sbrian		/* convert flags to sysV-style values */
101376195Sbrian		if (t->c_cflag & PARODD)
101476195Sbrian			cflag |= FEP_PARODD;
101576195Sbrian		if (t->c_cflag & PARENB)
101676195Sbrian			cflag |= FEP_PARENB;
101776195Sbrian		if (t->c_cflag & CSTOPB)
101876195Sbrian			cflag |= FEP_CSTOPB;
101976195Sbrian		if (t->c_cflag & CLOCAL)
102076195Sbrian			cflag |= FEP_CLOCAL;
102176195Sbrian#endif
102276195Sbrian
102376195Sbrian		cflag |= (t->c_cflag & CSIZE) >> 4;
1024136200Sphk		DLOG(DIGIDB_SET, (sc->dev, "port%d: CFLAG = 0x%x\n", port->pnum,
102576195Sbrian		    cflag));
102676195Sbrian		fepcmd_w(port, SETCFLAGS, (unsigned)cflag, 0);
102776195Sbrian	}
102876195Sbrian
102976195Sbrian	iflag =
103076195Sbrian	    t->c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP);
103176195Sbrian	if (port->c_iflag & IXON)
103276195Sbrian		iflag |= 0x400;
103376195Sbrian	if (port->c_iflag & IXANY)
103476195Sbrian		iflag |= 0x800;
103576195Sbrian	if (port->c_iflag & IXOFF)
103676195Sbrian		iflag |= 0x1000;
103776195Sbrian
1038136200Sphk	DLOG(DIGIDB_SET, (sc->dev, "port%d: set iflag = 0x%x\n", port->pnum, iflag));
103976195Sbrian	fepcmd_w(port, SETIFLAGS, (unsigned)iflag, 0);
104076195Sbrian
104176195Sbrian	hflow = 0;
104276195Sbrian	if (t->c_cflag & CDTR_IFLOW)
104376195Sbrian		hflow |= sc->csigs->dtr;
104476195Sbrian	if (t->c_cflag & CRTS_IFLOW)
104576195Sbrian		hflow |= sc->csigs->rts;
104676195Sbrian	if (t->c_cflag & CCTS_OFLOW)
104776195Sbrian		hflow |= sc->csigs->cts;
104876195Sbrian	if (t->c_cflag & CDSR_OFLOW)
104978496Sbrian		hflow |= port->dsr;
105076195Sbrian	if (t->c_cflag & CCAR_OFLOW)
105178496Sbrian		hflow |= port->cd;
105276195Sbrian
1053136200Sphk	DLOG(DIGIDB_SET, (sc->dev, "port%d: set hflow = 0x%x\n", port->pnum, hflow));
105476195Sbrian	fepcmd_w(port, SETHFLOW, 0xff00 | (unsigned)hflow, 0);
105576195Sbrian
105676195Sbrian	DLOG(DIGIDB_SET, (sc->dev, "port%d: set startc(0x%x), stopc(0x%x)\n",
1057136200Sphk	    port->pnum, t->c_cc[VSTART], t->c_cc[VSTOP]));
105876195Sbrian	fepcmd_b(port, SONOFFC, t->c_cc[VSTART], t->c_cc[VSTOP], 0);
105976195Sbrian
106076195Sbrian	if (sc->window != 0)
106176195Sbrian		sc->towin(sc, 0);
106276195Sbrian	if (window != 0)
106376195Sbrian		sc->towin(sc, window);
106476195Sbrian	splx(s);
106576195Sbrian
106676195Sbrian	return (0);
106776195Sbrian}
106876195Sbrian
106976195Sbrianstatic void
107076195Sbriandigi_intr(void *vp)
107176195Sbrian{
107276195Sbrian	struct digi_p *port;
107376195Sbrian	char *cxcon;
107476195Sbrian	struct digi_softc *sc;
107576195Sbrian	int ehead, etail;
107676195Sbrian	volatile struct board_chan *bc;
107776195Sbrian	struct tty *tp;
107876195Sbrian	int head, tail;
107976195Sbrian	int wrapmask;
108076195Sbrian	int size, window;
108176195Sbrian	struct event {
108276195Sbrian		u_char pnum;
108376195Sbrian		u_char event;
108476195Sbrian		u_char mstat;
108576195Sbrian		u_char lstat;
108676195Sbrian	} event;
108776195Sbrian
108876195Sbrian	sc = vp;
108976195Sbrian
109076195Sbrian	if (sc->status != DIGI_STATUS_ENABLED) {
109176195Sbrian		DLOG(DIGIDB_IRQ, (sc->dev, "interrupt on disabled board !\n"));
109276195Sbrian		return;
109376195Sbrian	}
109476327Sbrian
109576195Sbrian#ifdef DIGI_INTERRUPT
109676195Sbrian	microtime(&sc->intr_timestamp);
109776195Sbrian#endif
109876195Sbrian
109976195Sbrian	window = sc->window;
110076195Sbrian	sc->setwin(sc, 0);
110176195Sbrian
110276195Sbrian	if (sc->model >= PCXEM && W(sc->vmem + 0xd00)) {
110376195Sbrian		struct con_bios *con = con_bios_list;
110476195Sbrian		register u_char *ptr;
110576195Sbrian
110676195Sbrian		ptr = sc->vmem + W(sc->vmem + 0xd00);
110776195Sbrian		while (con) {
110876195Sbrian			if (ptr[1] && W(ptr + 2) == W(con->bios + 2))
110976195Sbrian				/* Not first block -- exact match */
111076195Sbrian				break;
111176195Sbrian
111276195Sbrian			if (W(ptr + 4) >= W(con->bios + 4) &&
111376195Sbrian			    W(ptr + 4) <= W(con->bios + 6))
111476195Sbrian				/* Initial search concetrator BIOS */
111576195Sbrian				break;
111676195Sbrian		}
111776195Sbrian
111876195Sbrian		if (con == NULL) {
111976195Sbrian			log(LOG_ERR, "digi%d: wanted bios LREV = 0x%04x"
112076195Sbrian			    " not found!\n", sc->res.unit, W(ptr + 4));
112176195Sbrian			W(ptr + 10) = 0;
112276195Sbrian			W(sc->vmem + 0xd00) = 0;
112376195Sbrian			goto eoi;
112476195Sbrian		}
112576195Sbrian		cxcon = con->bios;
112676195Sbrian		W(ptr + 4) = W(cxcon + 4);
112776195Sbrian		W(ptr + 6) = W(cxcon + 6);
112876195Sbrian		if (ptr[1] == 0)
112976195Sbrian			W(ptr + 2) = W(cxcon + 2);
113076195Sbrian		W(ptr + 8) = (ptr[1] << 6) + W(cxcon + 8);
113176195Sbrian		size = W(cxcon + 10) - (ptr[1] << 10);
113276195Sbrian		if (size <= 0) {
113376195Sbrian			W(ptr + 8) = W(cxcon + 8);
113476195Sbrian			W(ptr + 10) = 0;
113576195Sbrian		} else {
113676195Sbrian			if (size > 1024)
113776195Sbrian				size = 1024;
113876195Sbrian			W(ptr + 10) = size;
113976195Sbrian			bcopy(cxcon + (ptr[1] << 10), ptr + 12, size);
114076195Sbrian		}
114176195Sbrian		W(sc->vmem + 0xd00) = 0;
114276195Sbrian		goto eoi;
114376195Sbrian	}
114476195Sbrian
114576195Sbrian	ehead = sc->gdata->ein;
114676195Sbrian	etail = sc->gdata->eout;
114776195Sbrian	if (ehead == etail) {
114876195Sbrian#ifdef DEBUG
114976195Sbrian		sc->intr_count++;
115076195Sbrian		if (sc->intr_count % 6000 == 0) {
115176195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev,
115276195Sbrian			    "6000 useless polls %x %x\n", ehead, etail));
115376195Sbrian			sc->intr_count = 0;
115476195Sbrian		}
115576195Sbrian#endif
115676195Sbrian		goto eoi;
115776195Sbrian	}
115876195Sbrian	while (ehead != etail) {
115976195Sbrian		event = *(volatile struct event *)(sc->memevent + etail);
116076195Sbrian
116176195Sbrian		etail = (etail + 4) & sc->gdata->imax;
116276195Sbrian
116376195Sbrian		if (event.pnum >= sc->numports) {
116476195Sbrian			log(LOG_ERR, "digi%d: port %d: got event"
116576195Sbrian			    " on nonexisting port\n", sc->res.unit,
116676195Sbrian			    event.pnum);
116776195Sbrian			continue;
116876195Sbrian		}
116976195Sbrian		port = &sc->ports[event.pnum];
117076195Sbrian		bc = port->bc;
117176195Sbrian		tp = port->tp;
117276195Sbrian
1173136200Sphk		if (!(tp->t_state & TS_ISOPEN) && !tp->t_wopeners) {
117476195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev,
117576195Sbrian			    "port %d: event 0x%x on closed port\n",
117676195Sbrian			    event.pnum, event.event));
117776195Sbrian			bc->rout = bc->rin;
117876195Sbrian			bc->idata = 0;
117976195Sbrian			bc->iempty = 0;
118076195Sbrian			bc->ilow = 0;
118176195Sbrian			bc->mint = 0;
118276195Sbrian			continue;
118376195Sbrian		}
118476195Sbrian		if (event.event & ~ALL_IND)
118576195Sbrian			log(LOG_ERR, "digi%d: port%d: ? event 0x%x mstat 0x%x"
118676195Sbrian			    " lstat 0x%x\n", sc->res.unit, event.pnum,
118776195Sbrian			    event.event, event.mstat, event.lstat);
118876195Sbrian
118976195Sbrian		if (event.event & DATA_IND) {
119076195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev, "port %d: DATA_IND\n",
119176195Sbrian			    event.pnum));
119276195Sbrian			wrapmask = port->rxbufsize - 1;
119376195Sbrian			head = bc->rin;
119476195Sbrian			tail = bc->rout;
119576195Sbrian
119676195Sbrian			size = 0;
119776195Sbrian			if (!(tp->t_state & TS_ISOPEN)) {
119876195Sbrian				bc->rout = head;
119976195Sbrian				goto end_of_data;
120076195Sbrian			}
120176195Sbrian			while (head != tail) {
120276195Sbrian				int top;
120376195Sbrian
120476195Sbrian				DLOG(DIGIDB_INT, (sc->dev,
120576195Sbrian				    "port %d: p rx head = %d tail = %d\n",
120676195Sbrian				    event.pnum, head, tail));
120776195Sbrian				top = (head > tail) ? head : wrapmask + 1;
120876195Sbrian				sc->towin(sc, port->rxwin);
120976195Sbrian				size = top - tail;
121076195Sbrian				if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
121176195Sbrian					size = b_to_q((char *)port->rxbuf +
121276195Sbrian					    tail, size, &tp->t_rawq);
121376195Sbrian					tail = top - size;
121476195Sbrian					ttwakeup(tp);
121576195Sbrian				} else for (; tail < top;) {
1216130095Sphk					ttyld_rint(tp, port->rxbuf[tail]);
121776195Sbrian					sc->towin(sc, port->rxwin);
121876195Sbrian					size--;
121976195Sbrian					tail++;
122076195Sbrian					if (tp->t_state & TS_TBLOCK)
122176195Sbrian						break;
122276195Sbrian				}
122376195Sbrian				tail &= wrapmask;
122476195Sbrian				sc->setwin(sc, 0);
122576195Sbrian				bc->rout = tail;
122676195Sbrian				head = bc->rin;
122776195Sbrian				if (size)
122876195Sbrian					break;
122976195Sbrian			}
123076195Sbrian
123176195Sbrian			if (bc->orun) {
123276195Sbrian				CE_RECORD(port, CE_OVERRUN);
123376195Sbrian				log(LOG_ERR, "digi%d: port%d: %s\n",
123476195Sbrian				    sc->res.unit, event.pnum,
123576195Sbrian				    digi_errortxt(CE_OVERRUN));
123676195Sbrian				bc->orun = 0;
123776195Sbrian			}
123876195Sbrianend_of_data:
123976195Sbrian			if (size) {
124076195Sbrian				tp->t_state |= TS_TBLOCK;
124176195Sbrian				port->status |= PAUSE_RX;
124276195Sbrian				DLOG(DIGIDB_RX, (sc->dev, "port %d: pause RX\n",
124376195Sbrian				    event.pnum));
124476195Sbrian			} else {
124576195Sbrian				bc->idata = 1;
124676195Sbrian			}
124776195Sbrian		}
124876195Sbrian
124976195Sbrian		if (event.event & MODEMCHG_IND) {
125076195Sbrian			DLOG(DIGIDB_MODEM, (sc->dev, "port %d: MODEMCHG_IND\n",
125176195Sbrian			    event.pnum));
125276195Sbrian
125378496Sbrian			if ((event.mstat ^ event.lstat) & port->cd) {
125476195Sbrian				sc->hidewin(sc);
1255130095Sphk				ttyld_modem(tp, event.mstat & port->cd);
125676195Sbrian				sc->setwin(sc, 0);
125776195Sbrian				wakeup(TSA_CARR_ON(tp));
125876195Sbrian			}
125976195Sbrian
126076195Sbrian			if (event.mstat & sc->csigs->ri) {
126176195Sbrian				DLOG(DIGIDB_RI, (sc->dev, "port %d: RING\n",
126276195Sbrian				    event.pnum));
126376195Sbrian				if (port->send_ring) {
1264130077Sphk					ttyld_rint(tp, 'R');
1265130077Sphk					ttyld_rint(tp, 'I');
1266130077Sphk					ttyld_rint(tp, 'N');
1267130077Sphk					ttyld_rint(tp, 'G');
1268130077Sphk					ttyld_rint(tp, '\r');
1269130077Sphk					ttyld_rint(tp, '\n');
127076195Sbrian				}
127176195Sbrian			}
127276195Sbrian		}
127376195Sbrian		if (event.event & BREAK_IND) {
127476195Sbrian			DLOG(DIGIDB_MODEM, (sc->dev, "port %d: BREAK_IND\n",
127576195Sbrian			    event.pnum));
1276130077Sphk			ttyld_rint(tp, TTY_BI);
127776195Sbrian		}
127876195Sbrian		if (event.event & (LOWTX_IND | EMPTYTX_IND)) {
127976195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev, "port %d:%s%s\n",
128076195Sbrian			    event.pnum,
128176195Sbrian			    event.event & LOWTX_IND ? " LOWTX" : "",
128276195Sbrian			    event.event & EMPTYTX_IND ?  " EMPTYTX" : ""));
1283130077Sphk			ttyld_start(tp);
128476195Sbrian		}
128576195Sbrian	}
128676195Sbrian	sc->gdata->eout = etail;
128776195Sbrianeoi:
128876195Sbrian	if (sc->window != 0)
128976195Sbrian		sc->towin(sc, 0);
129076195Sbrian	if (window != 0)
129176195Sbrian		sc->towin(sc, window);
129276195Sbrian}
129376195Sbrian
129476195Sbrianstatic void
129576195Sbriandigistart(struct tty *tp)
129676195Sbrian{
129776195Sbrian	struct digi_p *port;
129876195Sbrian	struct digi_softc *sc;
129976195Sbrian	volatile struct board_chan *bc;
130076195Sbrian	int head, tail;
130176195Sbrian	int size, ocount, totcnt = 0;
130276195Sbrian	int s;
130376195Sbrian	int wmask;
130476195Sbrian
1305136200Sphk	port = tp->t_sc;
1306136200Sphk	sc = port->sc;
130776195Sbrian	bc = port->bc;
130876195Sbrian
130976195Sbrian	wmask = port->txbufsize - 1;
131076195Sbrian
131176195Sbrian	s = spltty();
131276195Sbrian	port->lcc = tp->t_outq.c_cc;
131376195Sbrian	sc->setwin(sc, 0);
131476195Sbrian	if (!(tp->t_state & TS_TBLOCK)) {
131576195Sbrian		if (port->status & PAUSE_RX) {
131676195Sbrian			DLOG(DIGIDB_RX, (sc->dev, "port %d: resume RX\n",
1317136200Sphk			    port->pnum));
131876195Sbrian			/*
131976195Sbrian			 * CAREFUL - braces are needed here if the DLOG is
132076195Sbrian			 * optimised out!
132176195Sbrian			 */
132276195Sbrian		}
132376195Sbrian		port->status &= ~PAUSE_RX;
132476195Sbrian		bc->idata = 1;
132576195Sbrian	}
132676195Sbrian	if (!(tp->t_state & TS_TTSTOP) && port->status & PAUSE_TX) {
1327136200Sphk		DLOG(DIGIDB_TX, (sc->dev, "port %d: resume TX\n", port->pnum));
132876195Sbrian		port->status &= ~PAUSE_TX;
132976195Sbrian		fepcmd_w(port, RESUMETX, 0, 10);
133076195Sbrian	}
133176195Sbrian	if (tp->t_outq.c_cc == 0)
133276195Sbrian		tp->t_state &= ~TS_BUSY;
133376195Sbrian	else
133476195Sbrian		tp->t_state |= TS_BUSY;
133576195Sbrian
133676195Sbrian	head = bc->tin;
133776195Sbrian	while (tp->t_outq.c_cc != 0) {
133876195Sbrian		tail = bc->tout;
133976195Sbrian		DLOG(DIGIDB_INT, (sc->dev, "port%d: s tx head = %d tail = %d\n",
1340136200Sphk		    port->pnum, head, tail));
134176195Sbrian
134276195Sbrian		if (head < tail)
134376195Sbrian			size = tail - head - 1;
134476195Sbrian		else {
134576195Sbrian			size = port->txbufsize - head;
134676195Sbrian			if (tail == 0)
134776195Sbrian				size--;
134876195Sbrian		}
134976195Sbrian
135076195Sbrian		if (size == 0)
135176195Sbrian			break;
135276195Sbrian		sc->towin(sc, port->txwin);
135376195Sbrian		ocount = q_to_b(&tp->t_outq, port->txbuf + head, size);
135476195Sbrian		totcnt += ocount;
135576195Sbrian		head += ocount;
135676195Sbrian		head &= wmask;
135776195Sbrian		sc->setwin(sc, 0);
135876195Sbrian		bc->tin = head;
135976195Sbrian		bc->iempty = 1;
136076195Sbrian		bc->ilow = 1;
136176195Sbrian	}
136276195Sbrian	port->lostcc = tp->t_outq.c_cc;
136376195Sbrian	tail = bc->tout;
136476195Sbrian	if (head < tail)
136576195Sbrian		size = port->txbufsize - tail + head;
136676195Sbrian	else
136776195Sbrian		size = head - tail;
136876195Sbrian
136976195Sbrian	port->lbuf = size;
1370136200Sphk	DLOG(DIGIDB_INT, (sc->dev, "port%d: s total cnt = %d\n", port->pnum, totcnt));
137176195Sbrian	ttwwakeup(tp);
137276195Sbrian	splx(s);
137376195Sbrian}
137476195Sbrian
137576195Sbrianstatic void
137676195Sbriandigistop(struct tty *tp, int rw)
137776195Sbrian{
137876195Sbrian	struct digi_softc *sc;
137976195Sbrian	struct digi_p *port;
138076195Sbrian
1381136200Sphk	port = tp->t_sc;
1382136200Sphk	sc = port->sc;
138376195Sbrian
1384136200Sphk	DLOG(DIGIDB_TX, (sc->dev, "port %d: pause TX\n", port->pnum));
138576195Sbrian	port->status |= PAUSE_TX;
138676195Sbrian	fepcmd_w(port, PAUSETX, 0, 10);
138776195Sbrian}
138876195Sbrian
138976195Sbrianstatic void
139076195Sbrianfepcmd(struct digi_p *port, int cmd, int op1, int ncmds)
139176195Sbrian{
139276195Sbrian	u_char *mem;
139376195Sbrian	unsigned tail, head;
139476195Sbrian	int count, n;
139576195Sbrian
139676195Sbrian	mem = port->sc->memcmd;
139776195Sbrian
139876195Sbrian	port->sc->setwin(port->sc, 0);
139976195Sbrian
140076195Sbrian	head = port->sc->gdata->cin;
140176195Sbrian	mem[head + 0] = cmd;
140276195Sbrian	mem[head + 1] = port->pnum;
1403118607Sjhb	*(u_short *)(mem + head + 2) = op1;
140476195Sbrian
140576195Sbrian	head = (head + 4) & port->sc->gdata->cmax;
140676195Sbrian	port->sc->gdata->cin = head;
140776195Sbrian
140876195Sbrian	for (count = FEPTIMEOUT; count > 0; count--) {
140976195Sbrian		head = port->sc->gdata->cin;
141076195Sbrian		tail = port->sc->gdata->cout;
141176195Sbrian		n = (head - tail) & port->sc->gdata->cmax;
141276195Sbrian
141376195Sbrian		if (n <= ncmds * sizeof(short) * 4)
141476195Sbrian			break;
141576195Sbrian	}
141676195Sbrian	if (count == 0)
141776195Sbrian		log(LOG_ERR, "digi%d: port%d: timeout on FEP command\n",
141876195Sbrian		    port->sc->res.unit, port->pnum);
141976195Sbrian}
142076195Sbrian
142176195Sbrianconst char *
142276195Sbriandigi_errortxt(int id)
142376195Sbrian{
142476195Sbrian	static const char *error_desc[] = {
142576195Sbrian		"silo overflow",
142676195Sbrian		"interrupt-level buffer overflow",
142776195Sbrian		"tty-level buffer overflow",
142876195Sbrian	};
142976195Sbrian
143076195Sbrian	KASSERT(id >= 0 && id < sizeof(error_desc) / sizeof(error_desc[0]),
143176195Sbrian	    ("Unexpected digi error id %d\n", id));
143276195Sbrian
143376195Sbrian	return (error_desc[id]);
143476195Sbrian}
143576195Sbrian
143676195Sbrianint
143776195Sbriandigi_attach(struct digi_softc *sc)
143876195Sbrian{
1439136200Sphk	sc->res.ctldev = make_dev(&digi_csw,
1440136200Sphk	    sc->res.unit << 16, UID_ROOT, GID_WHEEL,
144176195Sbrian	    0600, "digi%r.ctl", sc->res.unit);
1442136200Sphk	sc->res.ctldev->si_drv1 = sc;
144376195Sbrian
144476195Sbrian	digi_loadmoduledata(sc);
144576195Sbrian	digi_init(sc);
144676195Sbrian	digi_freemoduledata(sc);
144776195Sbrian
144876195Sbrian	return (0);
144976195Sbrian}
145076195Sbrian
145176195Sbrianstatic int
145276195Sbriandigi_inuse(struct digi_softc *sc)
145376195Sbrian{
145476195Sbrian	int i;
1455136200Sphk	struct digi_p *port;
145676195Sbrian
1457136200Sphk	port = &sc->ports[0];
1458136200Sphk	for (i = 0; i < sc->numports; i++, port++)
1459136200Sphk		if (port->tp->t_state & TS_ISOPEN) {
146076195Sbrian			DLOG(DIGIDB_INIT, (sc->dev, "port%d: busy\n", i));
146176195Sbrian			return (1);
1462136200Sphk		} else if (port->tp->t_wopeners || port->opencnt) {
146376195Sbrian			DLOG(DIGIDB_INIT, (sc->dev, "port%d: blocked in open\n",
146476195Sbrian			    i));
146576195Sbrian			return (1);
146676195Sbrian		}
146776195Sbrian	return (0);
146876195Sbrian}
146976195Sbrian
147076195Sbrianstatic void
147176195Sbriandigi_free_state(struct digi_softc *sc)
147276195Sbrian{
1473136200Sphk	int i;
147476195Sbrian
147576195Sbrian	/* Blow it all away */
147676195Sbrian
147776195Sbrian	for (i = 0; i < sc->numports; i++)
1478136200Sphk		ttygone(sc->ports[i].tp);
147976195Sbrian
1480136200Sphk	/* XXX: this might be better done as a ttypurge method */
148176195Sbrian	untimeout(digi_poll, sc, sc->callout);
148276195Sbrian	callout_handle_init(&sc->callout);
148376195Sbrian	untimeout(digi_int_test, sc, sc->inttest);
148476195Sbrian	callout_handle_init(&sc->inttest);
148576195Sbrian
1486136200Sphk	for (i = 0; i < sc->numports; i++)
1487136200Sphk		ttyfree(sc->ports[i].tp);
1488136200Sphk
148976195Sbrian	bus_teardown_intr(sc->dev, sc->res.irq, sc->res.irqHandler);
149076195Sbrian#ifdef DIGI_INTERRUPT
149176195Sbrian	if (sc->res.irq != NULL) {
149276195Sbrian		bus_release_resource(dev, SYS_RES_IRQ, sc->res.irqrid,
149376195Sbrian		    sc->res.irq);
149476195Sbrian		sc->res.irq = NULL;
149576195Sbrian	}
149676195Sbrian#endif
149776195Sbrian	if (sc->numports) {
149876195Sbrian		KASSERT(sc->ports, ("digi%d: Lost my ports ?", sc->res.unit));
149977004Sbrian		free(sc->ports, M_TTYS);
150076195Sbrian		sc->ports = NULL;
150176195Sbrian		sc->numports = 0;
150276195Sbrian	}
150376195Sbrian
150476195Sbrian	sc->status = DIGI_STATUS_NOTINIT;
150576195Sbrian}
150676195Sbrian
150776195Sbrianint
150876195Sbriandigi_detach(device_t dev)
150976195Sbrian{
151076195Sbrian	struct digi_softc *sc = device_get_softc(dev);
151176195Sbrian
151276195Sbrian	DLOG(DIGIDB_INIT, (sc->dev, "detaching\n"));
151376195Sbrian
151476195Sbrian	/* If we're INIT'd, numports must be 0 */
151576195Sbrian	KASSERT(sc->numports == 0 || sc->status != DIGI_STATUS_NOTINIT,
151676195Sbrian	    ("digi%d: numports(%d) & status(%d) are out of sync",
151776195Sbrian	    sc->res.unit, sc->numports, (int)sc->status));
151876195Sbrian
151976195Sbrian	if (digi_inuse(sc))
152076195Sbrian		return (EBUSY);
152176195Sbrian
152276195Sbrian	digi_free_state(sc);
152376195Sbrian
1524120461Sphk	destroy_dev(sc->res.ctldev);
152576195Sbrian
152676195Sbrian	if (sc->res.mem != NULL) {
152776195Sbrian		bus_release_resource(dev, SYS_RES_MEMORY, sc->res.mrid,
152876195Sbrian		    sc->res.mem);
152976195Sbrian		sc->res.mem = NULL;
153076195Sbrian	}
153176195Sbrian	if (sc->res.io != NULL) {
153276195Sbrian		bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
153376195Sbrian		    sc->res.io);
153476195Sbrian		sc->res.io = NULL;
153576195Sbrian	}
153676195Sbrian
153776195Sbrian	return (0);
153876195Sbrian}
153976195Sbrian
154076195Sbrianint
154176195Sbriandigi_shutdown(device_t dev)
154276195Sbrian{
154376195Sbrian	return (0);
154476195Sbrian}
154594320Sbrian
154694320SbrianMODULE_VERSION(digi, 1);
1547