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: stable/11/sys/dev/digi/digi.c 320921 2017-07-12 20:10:53Z jhb $
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++) {
363150132Sandre		if (i > 5*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 */
591151383Sphk		ttycreate(tp, TS_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	if ((res = linker_reference_module(modfile, NULL, &lf)) != 0)
787132806Sphk		printf("%s: Failed %d to autoload module\n", modfile, res);
78876195Sbrian	free(modfile, M_TEMP);
78976195Sbrian	if (res != 0)
79076195Sbrian		return (res);
79176195Sbrian
792111119Simp	sym = malloc(modlen + 10, M_TEMP, M_WAITOK);
79378414Sbrian	snprintf(sym, modlen + 10, "digi_mod_%s", sc->module);
794142506Ssam	symptr = linker_file_lookup_symbol(lf, sym, 0);
795142506Ssam	free(sym, M_TEMP);
796142506Ssam	if (symptr == NULL) {
79776195Sbrian		printf("digi_%s.ko: Symbol `%s' not found\n", sc->module, sym);
798159804Sjhb		linker_release_module(NULL, NULL, lf);
799142506Ssam		return (EINVAL);
800142506Ssam	}
80176195Sbrian
80276195Sbrian	digi_mod = (struct digi_mod *)symptr;
80376195Sbrian	if (digi_mod->dm_version != DIGI_MOD_VERSION) {
80476195Sbrian		printf("digi_%s.ko: Invalid version %d (need %d)\n",
80576195Sbrian		    sc->module, digi_mod->dm_version, DIGI_MOD_VERSION);
806159804Sjhb		linker_release_module(NULL, NULL, lf);
80776195Sbrian		return (EINVAL);
80876195Sbrian	}
80976195Sbrian
81076195Sbrian	sc->bios.size = digi_mod->dm_bios.size;
81176195Sbrian	if (sc->bios.size != 0 && digi_mod->dm_bios.data != NULL) {
812111119Simp		sc->bios.data = malloc(sc->bios.size, M_TTYS, M_WAITOK);
81376195Sbrian		bcopy(digi_mod->dm_bios.data, sc->bios.data, sc->bios.size);
81476195Sbrian	}
81576195Sbrian
81676195Sbrian	sc->fep.size = digi_mod->dm_fep.size;
81776195Sbrian	if (sc->fep.size != 0 && digi_mod->dm_fep.data != NULL) {
818111119Simp		sc->fep.data = malloc(sc->fep.size, M_TTYS, M_WAITOK);
81976195Sbrian		bcopy(digi_mod->dm_fep.data, sc->fep.data, sc->fep.size);
82076195Sbrian	}
82176195Sbrian
82276195Sbrian	sc->link.size = digi_mod->dm_link.size;
82376195Sbrian	if (sc->link.size != 0 && digi_mod->dm_link.data != NULL) {
824111119Simp		sc->link.data = malloc(sc->link.size, M_TTYS, M_WAITOK);
82576195Sbrian		bcopy(digi_mod->dm_link.data, sc->link.data, sc->link.size);
82676195Sbrian	}
82776195Sbrian
828159804Sjhb	linker_release_module(NULL, NULL, lf);
82976195Sbrian
83076195Sbrian	return (0);
83176195Sbrian}
83276195Sbrian
83376195Sbrianstatic int
834136200Sphkdigisioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
83576195Sbrian{
836136200Sphk	struct digi_p *port;
83776195Sbrian	struct digi_softc *sc;
83876195Sbrian
839136200Sphk	port = dev->si_drv1;
840136200Sphk	sc = port->sc;
84176195Sbrian
842136200Sphk	switch (cmd) {
843136200Sphk	case DIGIIO_GETALTPIN:
844136200Sphk		if (ISINIT(dev))
845136200Sphk			*(int *)data = port->ialtpin;
846136200Sphk		else if (ISLOCK(dev))
847136200Sphk			*(int *)data = port->laltpin;
848136200Sphk		else
849136200Sphk			return (ENOTTY);
850136200Sphk		break;
851136200Sphk	case DIGIIO_SETALTPIN:
852136200Sphk		if (ISINIT(dev)) {
853136200Sphk			if (!port->laltpin) {
854136200Sphk				port->ialtpin = !!*(int *)data;
855136200Sphk				DLOG(DIGIDB_SET, (sc->dev,
856136200Sphk				    "port%d: initial ALTPIN %s\n", port->pnum,
857136200Sphk				    port->ialtpin ? "set" : "cleared"));
858136200Sphk			}
859136200Sphk		} else if (ISLOCK(dev)) {
860136200Sphk			port->laltpin = !!*(int *)data;
861136200Sphk			DLOG(DIGIDB_SET, (sc->dev,
862136200Sphk			    "port%d: ALTPIN %slocked\n",
863136200Sphk			    port->pnum, port->laltpin ? "" : "un"));
864136200Sphk		} else
865136200Sphk			return (ENOTTY);
866136200Sphk		break;
867136200Sphk	default:
868136200Sphk		return (ENOTTY);
869136200Sphk	}
870136200Sphk	return (0);
871136200Sphk}
87276195Sbrian
873136200Sphkstatic int
874136200Sphkdigicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
875136200Sphk{
876136200Sphk	int error;
877136200Sphk	struct digi_softc *sc;
878136200Sphk
879136200Sphk	sc = dev->si_drv1;
880136200Sphk
88176195Sbrian	if (sc->status == DIGI_STATUS_DISABLED)
88276195Sbrian		return (ENXIO);
88376195Sbrian
884136200Sphk	switch (cmd) {
885136200Sphk	case DIGIIO_DEBUG:
88676195Sbrian#ifdef DEBUG
887136200Sphk		digi_debug = *(int *)data;
888136200Sphk		return (0);
88976195Sbrian#else
890136200Sphk		device_printf(sc->dev, "DEBUG not defined\n");
891136200Sphk		return (ENXIO);
89276195Sbrian#endif
893136200Sphk	case DIGIIO_REINIT:
894136200Sphk		digi_loadmoduledata(sc);
895136200Sphk		error = digi_init(sc);
896136200Sphk		digi_freemoduledata(sc);
897136200Sphk		return (error);
89876195Sbrian
899136200Sphk	case DIGIIO_MODEL:
900136200Sphk		*(enum digi_model *)data = sc->model;
901136200Sphk		return (0);
90276195Sbrian
903136200Sphk	case DIGIIO_IDENT:
904136200Sphk		return (copyout(sc->name, *(char **)data,
905136200Sphk		    strlen(sc->name) + 1));
906136200Sphk	default:
907136200Sphk		return (ENOIOCTL);
90876195Sbrian	}
909136200Sphk}
91076195Sbrian
911136200Sphkstatic int
912136200Sphkdigiioctl(struct tty *tp, u_long cmd, void *data, int flag, struct thread *td)
913136200Sphk{
914136200Sphk	struct digi_softc *sc;
915136200Sphk	struct digi_p *port;
916162711Sru#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
917162711Sru    defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
918162711Sru	int ival;
919162711Sru#endif
920136200Sphk
921136200Sphk	port = tp->t_sc;
922136200Sphk	sc = port->sc;
923136200Sphk	if (sc->status == DIGI_STATUS_DISABLED)
92476195Sbrian		return (ENXIO);
92576195Sbrian
92676195Sbrian	if (!(port->status & ENABLED))
92776195Sbrian		return (ENXIO);
92876195Sbrian
92978496Sbrian	switch (cmd) {
93078496Sbrian	case DIGIIO_GETALTPIN:
93178496Sbrian		*(int *)data = !!(port->dsr == sc->csigs->cd);
93278496Sbrian		return (0);
93378496Sbrian
93478496Sbrian	case DIGIIO_SETALTPIN:
93578496Sbrian		if (!port->laltpin) {
93678496Sbrian			if (*(int *)data) {
93778496Sbrian				DLOG(DIGIDB_SET, (sc->dev,
938136200Sphk				    "port%d: ALTPIN set\n", port->pnum));
93978496Sbrian				port->cd = sc->csigs->dsr;
94078496Sbrian				port->dsr = sc->csigs->cd;
94178496Sbrian			} else {
94278496Sbrian				DLOG(DIGIDB_SET, (sc->dev,
943136200Sphk				    "port%d: ALTPIN cleared\n", port->pnum));
94478496Sbrian				port->cd = sc->csigs->cd;
94578496Sbrian				port->dsr = sc->csigs->dsr;
94678496Sbrian			}
94778496Sbrian		}
94878496Sbrian		return (0);
949162711Sru#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
950162711Sru    defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
951162711Sru	case _IO('e', 'C'):
952162711Sru		ival = IOCPARM_IVAL(data);
953162711Sru		data = &ival;
954162711Sru		/* FALLTHROUGH */
955162711Sru#endif
95676195Sbrian	case DIGIIO_RING:
957162711Sru		port->send_ring = (u_char)*(int *)data;
95876195Sbrian		break;
95976195Sbrian	default:
96076195Sbrian		return (ENOTTY);
96176195Sbrian	}
96276195Sbrian	return (0);
96376195Sbrian}
96476195Sbrian
965131373Sphkstatic void
966131095Sphkdigibreak(struct tty *tp, int brk)
967131095Sphk{
968131095Sphk	struct digi_p *port;
969131095Sphk
970136200Sphk	port = tp->t_sc;
971131095Sphk
972131095Sphk	/*
973131095Sphk	 * now it sends 400 millisecond break because I don't know
974131095Sphk	 * how to send an infinite break
975131095Sphk	 */
976131095Sphk	if (brk)
977131095Sphk		fepcmd_w(port, SENDBREAK, 400, 10);
978131095Sphk}
979131095Sphk
980131095Sphkstatic int
98176195Sbriandigiparam(struct tty *tp, struct termios *t)
98276195Sbrian{
98376195Sbrian	struct digi_softc *sc;
98476195Sbrian	struct digi_p *port;
98576195Sbrian	int cflag;
98676195Sbrian	int iflag;
98776195Sbrian	int hflow;
98876195Sbrian	int s;
98976195Sbrian	int window;
99076195Sbrian
991136200Sphk	port = tp->t_sc;
992136200Sphk	sc = port->sc;
993136200Sphk	DLOG(DIGIDB_SET, (sc->dev, "port%d: setting parameters\n", port->pnum));
99476195Sbrian
99576195Sbrian	if (t->c_ispeed == 0)
99676195Sbrian		t->c_ispeed = t->c_ospeed;
99776195Sbrian
99876195Sbrian	cflag = ttspeedtab(t->c_ospeed, digispeedtab);
99976195Sbrian
100076195Sbrian	if (cflag < 0 || (cflag > 0 && t->c_ispeed != t->c_ospeed))
100176195Sbrian		return (EINVAL);
100276195Sbrian
100376195Sbrian	s = splclock();
100476195Sbrian
100576195Sbrian	window = sc->window;
100676195Sbrian	sc->setwin(sc, 0);
100776195Sbrian
100876195Sbrian	if (cflag == 0) {				/* hangup */
1009136200Sphk		DLOG(DIGIDB_SET, (sc->dev, "port%d: hangup\n", port->pnum));
1010131095Sphk		digimodem(port->tp, 0, SER_DTR | SER_RTS);
101176195Sbrian	} else {
1012131095Sphk		digimodem(port->tp, SER_DTR | SER_RTS, 0);
101376195Sbrian
1014136200Sphk		DLOG(DIGIDB_SET, (sc->dev, "port%d: CBAUD = %d\n", port->pnum,
101576195Sbrian		    cflag));
101676195Sbrian
101776195Sbrian#if 0
101876195Sbrian		/* convert flags to sysV-style values */
101976195Sbrian		if (t->c_cflag & PARODD)
102076195Sbrian			cflag |= 0x0200;
102176195Sbrian		if (t->c_cflag & PARENB)
102276195Sbrian			cflag |= 0x0100;
102376195Sbrian		if (t->c_cflag & CSTOPB)
102476195Sbrian			cflag |= 0x0080;
102576195Sbrian#else
102676195Sbrian		/* convert flags to sysV-style values */
102776195Sbrian		if (t->c_cflag & PARODD)
102876195Sbrian			cflag |= FEP_PARODD;
102976195Sbrian		if (t->c_cflag & PARENB)
103076195Sbrian			cflag |= FEP_PARENB;
103176195Sbrian		if (t->c_cflag & CSTOPB)
103276195Sbrian			cflag |= FEP_CSTOPB;
103376195Sbrian		if (t->c_cflag & CLOCAL)
103476195Sbrian			cflag |= FEP_CLOCAL;
103576195Sbrian#endif
103676195Sbrian
103776195Sbrian		cflag |= (t->c_cflag & CSIZE) >> 4;
1038136200Sphk		DLOG(DIGIDB_SET, (sc->dev, "port%d: CFLAG = 0x%x\n", port->pnum,
103976195Sbrian		    cflag));
104076195Sbrian		fepcmd_w(port, SETCFLAGS, (unsigned)cflag, 0);
104176195Sbrian	}
104276195Sbrian
104376195Sbrian	iflag =
104476195Sbrian	    t->c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP);
104576195Sbrian	if (port->c_iflag & IXON)
104676195Sbrian		iflag |= 0x400;
104776195Sbrian	if (port->c_iflag & IXANY)
104876195Sbrian		iflag |= 0x800;
104976195Sbrian	if (port->c_iflag & IXOFF)
105076195Sbrian		iflag |= 0x1000;
105176195Sbrian
1052136200Sphk	DLOG(DIGIDB_SET, (sc->dev, "port%d: set iflag = 0x%x\n", port->pnum, iflag));
105376195Sbrian	fepcmd_w(port, SETIFLAGS, (unsigned)iflag, 0);
105476195Sbrian
105576195Sbrian	hflow = 0;
105676195Sbrian	if (t->c_cflag & CDTR_IFLOW)
105776195Sbrian		hflow |= sc->csigs->dtr;
105876195Sbrian	if (t->c_cflag & CRTS_IFLOW)
105976195Sbrian		hflow |= sc->csigs->rts;
106076195Sbrian	if (t->c_cflag & CCTS_OFLOW)
106176195Sbrian		hflow |= sc->csigs->cts;
106276195Sbrian	if (t->c_cflag & CDSR_OFLOW)
106378496Sbrian		hflow |= port->dsr;
106476195Sbrian	if (t->c_cflag & CCAR_OFLOW)
106578496Sbrian		hflow |= port->cd;
106676195Sbrian
1067136200Sphk	DLOG(DIGIDB_SET, (sc->dev, "port%d: set hflow = 0x%x\n", port->pnum, hflow));
106876195Sbrian	fepcmd_w(port, SETHFLOW, 0xff00 | (unsigned)hflow, 0);
106976195Sbrian
107076195Sbrian	DLOG(DIGIDB_SET, (sc->dev, "port%d: set startc(0x%x), stopc(0x%x)\n",
1071136200Sphk	    port->pnum, t->c_cc[VSTART], t->c_cc[VSTOP]));
107276195Sbrian	fepcmd_b(port, SONOFFC, t->c_cc[VSTART], t->c_cc[VSTOP], 0);
107376195Sbrian
107476195Sbrian	if (sc->window != 0)
107576195Sbrian		sc->towin(sc, 0);
107676195Sbrian	if (window != 0)
107776195Sbrian		sc->towin(sc, window);
107876195Sbrian	splx(s);
107976195Sbrian
108076195Sbrian	return (0);
108176195Sbrian}
108276195Sbrian
108376195Sbrianstatic void
108476195Sbriandigi_intr(void *vp)
108576195Sbrian{
108676195Sbrian	struct digi_p *port;
108776195Sbrian	char *cxcon;
108876195Sbrian	struct digi_softc *sc;
108976195Sbrian	int ehead, etail;
109076195Sbrian	volatile struct board_chan *bc;
109176195Sbrian	struct tty *tp;
109276195Sbrian	int head, tail;
109376195Sbrian	int wrapmask;
109476195Sbrian	int size, window;
109576195Sbrian	struct event {
109676195Sbrian		u_char pnum;
109776195Sbrian		u_char event;
109876195Sbrian		u_char mstat;
109976195Sbrian		u_char lstat;
110076195Sbrian	} event;
110176195Sbrian
110276195Sbrian	sc = vp;
110376195Sbrian
110476195Sbrian	if (sc->status != DIGI_STATUS_ENABLED) {
110576195Sbrian		DLOG(DIGIDB_IRQ, (sc->dev, "interrupt on disabled board !\n"));
110676195Sbrian		return;
110776195Sbrian	}
110876327Sbrian
110976195Sbrian#ifdef DIGI_INTERRUPT
111076195Sbrian	microtime(&sc->intr_timestamp);
111176195Sbrian#endif
111276195Sbrian
111376195Sbrian	window = sc->window;
111476195Sbrian	sc->setwin(sc, 0);
111576195Sbrian
111676195Sbrian	if (sc->model >= PCXEM && W(sc->vmem + 0xd00)) {
111776195Sbrian		struct con_bios *con = con_bios_list;
111876195Sbrian		register u_char *ptr;
111976195Sbrian
112076195Sbrian		ptr = sc->vmem + W(sc->vmem + 0xd00);
112176195Sbrian		while (con) {
112276195Sbrian			if (ptr[1] && W(ptr + 2) == W(con->bios + 2))
112376195Sbrian				/* Not first block -- exact match */
112476195Sbrian				break;
112576195Sbrian
112676195Sbrian			if (W(ptr + 4) >= W(con->bios + 4) &&
112776195Sbrian			    W(ptr + 4) <= W(con->bios + 6))
112876195Sbrian				/* Initial search concetrator BIOS */
112976195Sbrian				break;
113076195Sbrian		}
113176195Sbrian
113276195Sbrian		if (con == NULL) {
113376195Sbrian			log(LOG_ERR, "digi%d: wanted bios LREV = 0x%04x"
113476195Sbrian			    " not found!\n", sc->res.unit, W(ptr + 4));
113576195Sbrian			W(ptr + 10) = 0;
113676195Sbrian			W(sc->vmem + 0xd00) = 0;
113776195Sbrian			goto eoi;
113876195Sbrian		}
113976195Sbrian		cxcon = con->bios;
114076195Sbrian		W(ptr + 4) = W(cxcon + 4);
114176195Sbrian		W(ptr + 6) = W(cxcon + 6);
114276195Sbrian		if (ptr[1] == 0)
114376195Sbrian			W(ptr + 2) = W(cxcon + 2);
114476195Sbrian		W(ptr + 8) = (ptr[1] << 6) + W(cxcon + 8);
114576195Sbrian		size = W(cxcon + 10) - (ptr[1] << 10);
114676195Sbrian		if (size <= 0) {
114776195Sbrian			W(ptr + 8) = W(cxcon + 8);
114876195Sbrian			W(ptr + 10) = 0;
114976195Sbrian		} else {
115076195Sbrian			if (size > 1024)
115176195Sbrian				size = 1024;
115276195Sbrian			W(ptr + 10) = size;
115376195Sbrian			bcopy(cxcon + (ptr[1] << 10), ptr + 12, size);
115476195Sbrian		}
115576195Sbrian		W(sc->vmem + 0xd00) = 0;
115676195Sbrian		goto eoi;
115776195Sbrian	}
115876195Sbrian
115976195Sbrian	ehead = sc->gdata->ein;
116076195Sbrian	etail = sc->gdata->eout;
116176195Sbrian	if (ehead == etail) {
116276195Sbrian#ifdef DEBUG
116376195Sbrian		sc->intr_count++;
116476195Sbrian		if (sc->intr_count % 6000 == 0) {
116576195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev,
116676195Sbrian			    "6000 useless polls %x %x\n", ehead, etail));
116776195Sbrian			sc->intr_count = 0;
116876195Sbrian		}
116976195Sbrian#endif
117076195Sbrian		goto eoi;
117176195Sbrian	}
117276195Sbrian	while (ehead != etail) {
117376195Sbrian		event = *(volatile struct event *)(sc->memevent + etail);
117476195Sbrian
117576195Sbrian		etail = (etail + 4) & sc->gdata->imax;
117676195Sbrian
117776195Sbrian		if (event.pnum >= sc->numports) {
117876195Sbrian			log(LOG_ERR, "digi%d: port %d: got event"
117976195Sbrian			    " on nonexisting port\n", sc->res.unit,
118076195Sbrian			    event.pnum);
118176195Sbrian			continue;
118276195Sbrian		}
118376195Sbrian		port = &sc->ports[event.pnum];
118476195Sbrian		bc = port->bc;
118576195Sbrian		tp = port->tp;
118676195Sbrian
1187136200Sphk		if (!(tp->t_state & TS_ISOPEN) && !tp->t_wopeners) {
118876195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev,
118976195Sbrian			    "port %d: event 0x%x on closed port\n",
119076195Sbrian			    event.pnum, event.event));
119176195Sbrian			bc->rout = bc->rin;
119276195Sbrian			bc->idata = 0;
119376195Sbrian			bc->iempty = 0;
119476195Sbrian			bc->ilow = 0;
119576195Sbrian			bc->mint = 0;
119676195Sbrian			continue;
119776195Sbrian		}
119876195Sbrian		if (event.event & ~ALL_IND)
119976195Sbrian			log(LOG_ERR, "digi%d: port%d: ? event 0x%x mstat 0x%x"
120076195Sbrian			    " lstat 0x%x\n", sc->res.unit, event.pnum,
120176195Sbrian			    event.event, event.mstat, event.lstat);
120276195Sbrian
120376195Sbrian		if (event.event & DATA_IND) {
120476195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev, "port %d: DATA_IND\n",
120576195Sbrian			    event.pnum));
120676195Sbrian			wrapmask = port->rxbufsize - 1;
120776195Sbrian			head = bc->rin;
120876195Sbrian			tail = bc->rout;
120976195Sbrian
121076195Sbrian			size = 0;
121176195Sbrian			if (!(tp->t_state & TS_ISOPEN)) {
121276195Sbrian				bc->rout = head;
121376195Sbrian				goto end_of_data;
121476195Sbrian			}
121576195Sbrian			while (head != tail) {
121676195Sbrian				int top;
121776195Sbrian
121876195Sbrian				DLOG(DIGIDB_INT, (sc->dev,
121976195Sbrian				    "port %d: p rx head = %d tail = %d\n",
122076195Sbrian				    event.pnum, head, tail));
122176195Sbrian				top = (head > tail) ? head : wrapmask + 1;
122276195Sbrian				sc->towin(sc, port->rxwin);
122376195Sbrian				size = top - tail;
122476195Sbrian				if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
122576195Sbrian					size = b_to_q((char *)port->rxbuf +
122676195Sbrian					    tail, size, &tp->t_rawq);
122776195Sbrian					tail = top - size;
122876195Sbrian					ttwakeup(tp);
122976195Sbrian				} else for (; tail < top;) {
1230130095Sphk					ttyld_rint(tp, port->rxbuf[tail]);
123176195Sbrian					sc->towin(sc, port->rxwin);
123276195Sbrian					size--;
123376195Sbrian					tail++;
123476195Sbrian					if (tp->t_state & TS_TBLOCK)
123576195Sbrian						break;
123676195Sbrian				}
123776195Sbrian				tail &= wrapmask;
123876195Sbrian				sc->setwin(sc, 0);
123976195Sbrian				bc->rout = tail;
124076195Sbrian				head = bc->rin;
124176195Sbrian				if (size)
124276195Sbrian					break;
124376195Sbrian			}
124476195Sbrian
124576195Sbrian			if (bc->orun) {
124676195Sbrian				CE_RECORD(port, CE_OVERRUN);
124776195Sbrian				log(LOG_ERR, "digi%d: port%d: %s\n",
124876195Sbrian				    sc->res.unit, event.pnum,
124976195Sbrian				    digi_errortxt(CE_OVERRUN));
125076195Sbrian				bc->orun = 0;
125176195Sbrian			}
125276195Sbrianend_of_data:
125376195Sbrian			if (size) {
125476195Sbrian				tp->t_state |= TS_TBLOCK;
125576195Sbrian				port->status |= PAUSE_RX;
125676195Sbrian				DLOG(DIGIDB_RX, (sc->dev, "port %d: pause RX\n",
125776195Sbrian				    event.pnum));
125876195Sbrian			} else {
125976195Sbrian				bc->idata = 1;
126076195Sbrian			}
126176195Sbrian		}
126276195Sbrian
126376195Sbrian		if (event.event & MODEMCHG_IND) {
126476195Sbrian			DLOG(DIGIDB_MODEM, (sc->dev, "port %d: MODEMCHG_IND\n",
126576195Sbrian			    event.pnum));
126676195Sbrian
126778496Sbrian			if ((event.mstat ^ event.lstat) & port->cd) {
126876195Sbrian				sc->hidewin(sc);
1269130095Sphk				ttyld_modem(tp, event.mstat & port->cd);
127076195Sbrian				sc->setwin(sc, 0);
127176195Sbrian				wakeup(TSA_CARR_ON(tp));
127276195Sbrian			}
127376195Sbrian
127476195Sbrian			if (event.mstat & sc->csigs->ri) {
127576195Sbrian				DLOG(DIGIDB_RI, (sc->dev, "port %d: RING\n",
127676195Sbrian				    event.pnum));
127776195Sbrian				if (port->send_ring) {
1278130077Sphk					ttyld_rint(tp, 'R');
1279130077Sphk					ttyld_rint(tp, 'I');
1280130077Sphk					ttyld_rint(tp, 'N');
1281130077Sphk					ttyld_rint(tp, 'G');
1282130077Sphk					ttyld_rint(tp, '\r');
1283130077Sphk					ttyld_rint(tp, '\n');
128476195Sbrian				}
128576195Sbrian			}
128676195Sbrian		}
128776195Sbrian		if (event.event & BREAK_IND) {
128876195Sbrian			DLOG(DIGIDB_MODEM, (sc->dev, "port %d: BREAK_IND\n",
128976195Sbrian			    event.pnum));
1290130077Sphk			ttyld_rint(tp, TTY_BI);
129176195Sbrian		}
129276195Sbrian		if (event.event & (LOWTX_IND | EMPTYTX_IND)) {
129376195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev, "port %d:%s%s\n",
129476195Sbrian			    event.pnum,
129576195Sbrian			    event.event & LOWTX_IND ? " LOWTX" : "",
129676195Sbrian			    event.event & EMPTYTX_IND ?  " EMPTYTX" : ""));
1297130077Sphk			ttyld_start(tp);
129876195Sbrian		}
129976195Sbrian	}
130076195Sbrian	sc->gdata->eout = etail;
130176195Sbrianeoi:
130276195Sbrian	if (sc->window != 0)
130376195Sbrian		sc->towin(sc, 0);
130476195Sbrian	if (window != 0)
130576195Sbrian		sc->towin(sc, window);
130676195Sbrian}
130776195Sbrian
130876195Sbrianstatic void
130976195Sbriandigistart(struct tty *tp)
131076195Sbrian{
131176195Sbrian	struct digi_p *port;
131276195Sbrian	struct digi_softc *sc;
131376195Sbrian	volatile struct board_chan *bc;
131476195Sbrian	int head, tail;
131576195Sbrian	int size, ocount, totcnt = 0;
131676195Sbrian	int s;
131776195Sbrian	int wmask;
131876195Sbrian
1319136200Sphk	port = tp->t_sc;
1320136200Sphk	sc = port->sc;
132176195Sbrian	bc = port->bc;
132276195Sbrian
132376195Sbrian	wmask = port->txbufsize - 1;
132476195Sbrian
132576195Sbrian	s = spltty();
132676195Sbrian	port->lcc = tp->t_outq.c_cc;
132776195Sbrian	sc->setwin(sc, 0);
132876195Sbrian	if (!(tp->t_state & TS_TBLOCK)) {
132976195Sbrian		if (port->status & PAUSE_RX) {
133076195Sbrian			DLOG(DIGIDB_RX, (sc->dev, "port %d: resume RX\n",
1331136200Sphk			    port->pnum));
133276195Sbrian			/*
133376195Sbrian			 * CAREFUL - braces are needed here if the DLOG is
133476195Sbrian			 * optimised out!
133576195Sbrian			 */
133676195Sbrian		}
133776195Sbrian		port->status &= ~PAUSE_RX;
133876195Sbrian		bc->idata = 1;
133976195Sbrian	}
134076195Sbrian	if (!(tp->t_state & TS_TTSTOP) && port->status & PAUSE_TX) {
1341136200Sphk		DLOG(DIGIDB_TX, (sc->dev, "port %d: resume TX\n", port->pnum));
134276195Sbrian		port->status &= ~PAUSE_TX;
134376195Sbrian		fepcmd_w(port, RESUMETX, 0, 10);
134476195Sbrian	}
134576195Sbrian	if (tp->t_outq.c_cc == 0)
134676195Sbrian		tp->t_state &= ~TS_BUSY;
134776195Sbrian	else
134876195Sbrian		tp->t_state |= TS_BUSY;
134976195Sbrian
135076195Sbrian	head = bc->tin;
135176195Sbrian	while (tp->t_outq.c_cc != 0) {
135276195Sbrian		tail = bc->tout;
135376195Sbrian		DLOG(DIGIDB_INT, (sc->dev, "port%d: s tx head = %d tail = %d\n",
1354136200Sphk		    port->pnum, head, tail));
135576195Sbrian
135676195Sbrian		if (head < tail)
135776195Sbrian			size = tail - head - 1;
135876195Sbrian		else {
135976195Sbrian			size = port->txbufsize - head;
136076195Sbrian			if (tail == 0)
136176195Sbrian				size--;
136276195Sbrian		}
136376195Sbrian
136476195Sbrian		if (size == 0)
136576195Sbrian			break;
136676195Sbrian		sc->towin(sc, port->txwin);
136776195Sbrian		ocount = q_to_b(&tp->t_outq, port->txbuf + head, size);
136876195Sbrian		totcnt += ocount;
136976195Sbrian		head += ocount;
137076195Sbrian		head &= wmask;
137176195Sbrian		sc->setwin(sc, 0);
137276195Sbrian		bc->tin = head;
137376195Sbrian		bc->iempty = 1;
137476195Sbrian		bc->ilow = 1;
137576195Sbrian	}
137676195Sbrian	port->lostcc = tp->t_outq.c_cc;
137776195Sbrian	tail = bc->tout;
137876195Sbrian	if (head < tail)
137976195Sbrian		size = port->txbufsize - tail + head;
138076195Sbrian	else
138176195Sbrian		size = head - tail;
138276195Sbrian
138376195Sbrian	port->lbuf = size;
1384136200Sphk	DLOG(DIGIDB_INT, (sc->dev, "port%d: s total cnt = %d\n", port->pnum, totcnt));
138576195Sbrian	ttwwakeup(tp);
138676195Sbrian	splx(s);
138776195Sbrian}
138876195Sbrian
138976195Sbrianstatic void
139076195Sbriandigistop(struct tty *tp, int rw)
139176195Sbrian{
139276195Sbrian	struct digi_softc *sc;
139376195Sbrian	struct digi_p *port;
139476195Sbrian
1395136200Sphk	port = tp->t_sc;
1396136200Sphk	sc = port->sc;
139776195Sbrian
1398136200Sphk	DLOG(DIGIDB_TX, (sc->dev, "port %d: pause TX\n", port->pnum));
139976195Sbrian	port->status |= PAUSE_TX;
140076195Sbrian	fepcmd_w(port, PAUSETX, 0, 10);
140176195Sbrian}
140276195Sbrian
140376195Sbrianstatic void
140476195Sbrianfepcmd(struct digi_p *port, int cmd, int op1, int ncmds)
140576195Sbrian{
140676195Sbrian	u_char *mem;
140776195Sbrian	unsigned tail, head;
140876195Sbrian	int count, n;
140976195Sbrian
141076195Sbrian	mem = port->sc->memcmd;
141176195Sbrian
141276195Sbrian	port->sc->setwin(port->sc, 0);
141376195Sbrian
141476195Sbrian	head = port->sc->gdata->cin;
141576195Sbrian	mem[head + 0] = cmd;
141676195Sbrian	mem[head + 1] = port->pnum;
1417118607Sjhb	*(u_short *)(mem + head + 2) = op1;
141876195Sbrian
141976195Sbrian	head = (head + 4) & port->sc->gdata->cmax;
142076195Sbrian	port->sc->gdata->cin = head;
142176195Sbrian
142276195Sbrian	for (count = FEPTIMEOUT; count > 0; count--) {
142376195Sbrian		head = port->sc->gdata->cin;
142476195Sbrian		tail = port->sc->gdata->cout;
142576195Sbrian		n = (head - tail) & port->sc->gdata->cmax;
142676195Sbrian
142776195Sbrian		if (n <= ncmds * sizeof(short) * 4)
142876195Sbrian			break;
142976195Sbrian	}
143076195Sbrian	if (count == 0)
143176195Sbrian		log(LOG_ERR, "digi%d: port%d: timeout on FEP command\n",
143276195Sbrian		    port->sc->res.unit, port->pnum);
143376195Sbrian}
143476195Sbrian
143576195Sbrianconst char *
143676195Sbriandigi_errortxt(int id)
143776195Sbrian{
143876195Sbrian	static const char *error_desc[] = {
143976195Sbrian		"silo overflow",
144076195Sbrian		"interrupt-level buffer overflow",
144176195Sbrian		"tty-level buffer overflow",
144276195Sbrian	};
144376195Sbrian
1444298307Spfg	KASSERT(id >= 0 && id < nitems(error_desc),
1445298307Spfg		("Unexpected digi error id %d\n", id));
144676195Sbrian
144776195Sbrian	return (error_desc[id]);
144876195Sbrian}
144976195Sbrian
145076195Sbrianint
145176195Sbriandigi_attach(struct digi_softc *sc)
145276195Sbrian{
1453136200Sphk	sc->res.ctldev = make_dev(&digi_csw,
1454136200Sphk	    sc->res.unit << 16, UID_ROOT, GID_WHEEL,
145576195Sbrian	    0600, "digi%r.ctl", sc->res.unit);
1456136200Sphk	sc->res.ctldev->si_drv1 = sc;
145776195Sbrian
145876195Sbrian	digi_loadmoduledata(sc);
145976195Sbrian	digi_init(sc);
146076195Sbrian	digi_freemoduledata(sc);
1461320921Sjhb	device_printf(dev,
1462320921Sjhb	    "WARNING: This driver is deprecated and will be removed.\n");
146376195Sbrian
146476195Sbrian	return (0);
146576195Sbrian}
146676195Sbrian
146776195Sbrianstatic int
146876195Sbriandigi_inuse(struct digi_softc *sc)
146976195Sbrian{
147076195Sbrian	int i;
1471136200Sphk	struct digi_p *port;
147276195Sbrian
1473136200Sphk	port = &sc->ports[0];
1474136200Sphk	for (i = 0; i < sc->numports; i++, port++)
1475136200Sphk		if (port->tp->t_state & TS_ISOPEN) {
147676195Sbrian			DLOG(DIGIDB_INIT, (sc->dev, "port%d: busy\n", i));
147776195Sbrian			return (1);
1478136200Sphk		} else if (port->tp->t_wopeners || port->opencnt) {
147976195Sbrian			DLOG(DIGIDB_INIT, (sc->dev, "port%d: blocked in open\n",
148076195Sbrian			    i));
148176195Sbrian			return (1);
148276195Sbrian		}
148376195Sbrian	return (0);
148476195Sbrian}
148576195Sbrian
148676195Sbrianstatic void
148776195Sbriandigi_free_state(struct digi_softc *sc)
148876195Sbrian{
1489136200Sphk	int i;
149076195Sbrian
149176195Sbrian	/* Blow it all away */
149276195Sbrian
149376195Sbrian	for (i = 0; i < sc->numports; i++)
1494136200Sphk		ttygone(sc->ports[i].tp);
149576195Sbrian
1496136200Sphk	/* XXX: this might be better done as a ttypurge method */
149776195Sbrian	untimeout(digi_poll, sc, sc->callout);
149876195Sbrian	callout_handle_init(&sc->callout);
149976195Sbrian	untimeout(digi_int_test, sc, sc->inttest);
150076195Sbrian	callout_handle_init(&sc->inttest);
150176195Sbrian
1502136200Sphk	for (i = 0; i < sc->numports; i++)
1503136200Sphk		ttyfree(sc->ports[i].tp);
1504136200Sphk
150576195Sbrian	bus_teardown_intr(sc->dev, sc->res.irq, sc->res.irqHandler);
150676195Sbrian#ifdef DIGI_INTERRUPT
150776195Sbrian	if (sc->res.irq != NULL) {
150876195Sbrian		bus_release_resource(dev, SYS_RES_IRQ, sc->res.irqrid,
150976195Sbrian		    sc->res.irq);
151076195Sbrian		sc->res.irq = NULL;
151176195Sbrian	}
151276195Sbrian#endif
151376195Sbrian	if (sc->numports) {
151476195Sbrian		KASSERT(sc->ports, ("digi%d: Lost my ports ?", sc->res.unit));
151577004Sbrian		free(sc->ports, M_TTYS);
151676195Sbrian		sc->ports = NULL;
151776195Sbrian		sc->numports = 0;
151876195Sbrian	}
151976195Sbrian
152076195Sbrian	sc->status = DIGI_STATUS_NOTINIT;
152176195Sbrian}
152276195Sbrian
152376195Sbrianint
152476195Sbriandigi_detach(device_t dev)
152576195Sbrian{
152676195Sbrian	struct digi_softc *sc = device_get_softc(dev);
152776195Sbrian
152876195Sbrian	DLOG(DIGIDB_INIT, (sc->dev, "detaching\n"));
152976195Sbrian
153076195Sbrian	/* If we're INIT'd, numports must be 0 */
153176195Sbrian	KASSERT(sc->numports == 0 || sc->status != DIGI_STATUS_NOTINIT,
153276195Sbrian	    ("digi%d: numports(%d) & status(%d) are out of sync",
153376195Sbrian	    sc->res.unit, sc->numports, (int)sc->status));
153476195Sbrian
153576195Sbrian	if (digi_inuse(sc))
153676195Sbrian		return (EBUSY);
153776195Sbrian
153876195Sbrian	digi_free_state(sc);
153976195Sbrian
1540120461Sphk	destroy_dev(sc->res.ctldev);
154176195Sbrian
154276195Sbrian	if (sc->res.mem != NULL) {
154376195Sbrian		bus_release_resource(dev, SYS_RES_MEMORY, sc->res.mrid,
154476195Sbrian		    sc->res.mem);
154576195Sbrian		sc->res.mem = NULL;
154676195Sbrian	}
154776195Sbrian	if (sc->res.io != NULL) {
154876195Sbrian		bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
154976195Sbrian		    sc->res.io);
155076195Sbrian		sc->res.io = NULL;
155176195Sbrian	}
155276195Sbrian
155376195Sbrian	return (0);
155476195Sbrian}
155576195Sbrian
155676195Sbrianint
155776195Sbriandigi_shutdown(device_t dev)
155876195Sbrian{
155976195Sbrian	return (0);
156076195Sbrian}
156194320Sbrian
156294320SbrianMODULE_VERSION(digi, 1);
1563