digi.c revision 136200
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 136200 2004-10-06 20:01:49Z 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
8876195Sbrian
8976195Sbrianstatic speed_t digidefaultrate = TTYDEF_SPEED;
9076195Sbrian
9176195Sbrianstruct con_bios {
9276195Sbrian	struct con_bios *next;
9376195Sbrian	u_char *bios;
9476195Sbrian	size_t size;
9576195Sbrian};
9676195Sbrian
9789062Smsmithstatic struct con_bios *con_bios_list;
9891445Speterdevclass_t	 digi_devclass;
9976195Sbrianstatic char	 driver_name[] = "digi";
10076195Sbrianunsigned 	 digi_debug = 0;
10176195Sbrian
10276195Sbrianstatic struct speedtab digispeedtab[] = {
10376195Sbrian	{ 0,		0},			/* old (sysV-like) Bx codes */
10476195Sbrian	{ 50,		1},
10576195Sbrian	{ 75,		2},
10676195Sbrian	{ 110,		3},
10776195Sbrian	{ 134,		4},
10876195Sbrian	{ 150,		5},
10976195Sbrian	{ 200,		6},
11076195Sbrian	{ 300,		7},
11176195Sbrian	{ 600,		8},
11276195Sbrian	{ 1200,		9},
11376195Sbrian	{ 1800,		10},
11476195Sbrian	{ 2400,		11},
11576195Sbrian	{ 4800,		12},
11676195Sbrian	{ 9600,		13},
11776195Sbrian	{ 19200,	14},
11876195Sbrian	{ 38400,	15},
11976195Sbrian	{ 57600,	(02000 | 1)},
12076195Sbrian	{ 76800,	(02000 | 2)},
12176195Sbrian	{ 115200,	(02000 | 3)},
12276195Sbrian	{ 230400,	(02000 | 6)},
12376195Sbrian	{ -1,		-1}
12476195Sbrian};
12576195Sbrian
12676195Sbrianconst struct digi_control_signals digi_xixe_signals = {
12776195Sbrian	0x02, 0x08, 0x10, 0x20, 0x40, 0x80
12876195Sbrian};
12976195Sbrian
13076195Sbrianconst struct digi_control_signals digi_normal_signals = {
13176195Sbrian	0x02, 0x80, 0x20, 0x10, 0x40, 0x01
13276195Sbrian};
13376195Sbrian
134136200Sphkstatic struct cdevsw digi_csw = {
135126080Sphk	.d_version =	D_VERSION,
136136200Sphk	.d_open =	digicopen,
137136200Sphk	.d_close =	digicclose,
138136200Sphk	.d_ioctl =	digicioctl,
139111815Sphk	.d_name =	driver_name,
140126080Sphk	.d_flags =	D_TTY | D_NEEDGIANT,
14176195Sbrian};
14276195Sbrian
14376195Sbrianstatic void
14476195Sbriandigi_poll(void *ptr)
14576195Sbrian{
14676195Sbrian	struct digi_softc *sc;
14776195Sbrian
14876195Sbrian	sc = (struct digi_softc *)ptr;
14976195Sbrian	callout_handle_init(&sc->callout);
15076195Sbrian	digi_intr(sc);
15176195Sbrian	sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1);
15276195Sbrian}
15376195Sbrian
15476195Sbrianstatic void
15576195Sbriandigi_int_test(void *v)
15676195Sbrian{
15776195Sbrian	struct digi_softc *sc = v;
15876195Sbrian
15976195Sbrian	callout_handle_init(&sc->inttest);
16076195Sbrian#ifdef DIGI_INTERRUPT
16176195Sbrian	if (sc->intr_timestamp.tv_sec || sc->intr_timestamp.tv_usec) {
16276195Sbrian		/* interrupt OK! */
16376195Sbrian		return;
16476195Sbrian	}
16576195Sbrian	log(LOG_ERR, "digi%d: Interrupt didn't work, use polled mode\n", unit);
16676195Sbrian#endif
16776195Sbrian	sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1);
16876195Sbrian}
16976195Sbrian
17076195Sbrianstatic void
17176195Sbriandigi_freemoduledata(struct digi_softc *sc)
17276195Sbrian{
17376195Sbrian	if (sc->fep.data != NULL) {
17476195Sbrian		free(sc->fep.data, M_TTYS);
17576195Sbrian		sc->fep.data = NULL;
17676195Sbrian	}
17776195Sbrian	if (sc->link.data != NULL) {
17876195Sbrian		free(sc->link.data, M_TTYS);
17976195Sbrian		sc->link.data = NULL;
18076195Sbrian	}
18176195Sbrian	if (sc->bios.data != NULL) {
18276195Sbrian		free(sc->bios.data, M_TTYS);
18376195Sbrian		sc->bios.data = NULL;
18476195Sbrian	}
18576195Sbrian}
18676195Sbrian
18776195Sbrianstatic int
18876195Sbriandigi_bcopy(const void *vfrom, void *vto, size_t sz)
18976195Sbrian{
19076195Sbrian	volatile const char *from = (volatile const char *)vfrom;
19176195Sbrian	volatile char *to = (volatile char *)vto;
19276195Sbrian	size_t i;
19376195Sbrian
19476195Sbrian	for (i = 0; i < sz; i++)
19576195Sbrian		*to++ = *from++;
19676195Sbrian
19776195Sbrian	from = (const volatile char *)vfrom;
19876195Sbrian	to = (volatile char *)vto;
19976195Sbrian	for (i = 0; i < sz; i++)
20076195Sbrian		if (*to++ != *from++)
20176195Sbrian			return (0);
20276195Sbrian	return (1);
20376195Sbrian}
20476195Sbrian
20594358Sbrianvoid
20694361Sbriandigi_delay(struct digi_softc *sc, const char *txt, u_long timo)
20794340Sbrian{
20894340Sbrian	if (cold)
20994361Sbrian		DELAY(timo * 1000000 / hz);
21094340Sbrian	else
21194361Sbrian		tsleep(sc, PUSER | PCATCH, txt, timo);
21294340Sbrian}
21394340Sbrian
21476195Sbrianstatic int
21576195Sbriandigi_init(struct digi_softc *sc)
21676195Sbrian{
21776195Sbrian	int i, cnt, resp;
21876195Sbrian	u_char *ptr;
21976195Sbrian	int lowwater;
22076195Sbrian	struct digi_p *port;
22176195Sbrian	volatile struct board_chan *bc;
222136200Sphk	struct tty *tp;
22376195Sbrian
22476195Sbrian	ptr = NULL;
22576195Sbrian
22676195Sbrian	if (sc->status == DIGI_STATUS_DISABLED) {
22776195Sbrian		log(LOG_ERR, "digi%d: Cannot init a disabled card\n",
22876195Sbrian		    sc->res.unit);
22976195Sbrian		return (EIO);
23076195Sbrian	}
23176195Sbrian	if (sc->bios.data == NULL) {
23276195Sbrian		log(LOG_ERR, "digi%d: Cannot init without BIOS\n",
23376195Sbrian		    sc->res.unit);
23476195Sbrian		return (EIO);
23576195Sbrian	}
23676195Sbrian#if 0
23776195Sbrian	if (sc->link.data == NULL && sc->model >= PCCX) {
23876195Sbrian		log(LOG_ERR, "digi%d: Cannot init without link info\n",
23976195Sbrian		    sc->res.unit);
24076195Sbrian		return (EIO);
24176195Sbrian	}
24276195Sbrian#endif
24376195Sbrian	if (sc->fep.data == NULL) {
24476195Sbrian		log(LOG_ERR, "digi%d: Cannot init without fep code\n",
24576195Sbrian		    sc->res.unit);
24676195Sbrian		return (EIO);
24776195Sbrian	}
24876195Sbrian	sc->status = DIGI_STATUS_NOTINIT;
24976195Sbrian
25076195Sbrian	if (sc->numports) {
25176195Sbrian		/*
25276195Sbrian		 * We're re-initialising - maybe because someone's attached
25376195Sbrian		 * another port module.  For now, we just re-initialise
25476195Sbrian		 * everything.
25576195Sbrian		 */
25676195Sbrian		if (digi_inuse(sc))
25776195Sbrian			return (EBUSY);
25876195Sbrian
25976195Sbrian		digi_free_state(sc);
26076195Sbrian	}
26176195Sbrian
26276195Sbrian	ptr = sc->setwin(sc, MISCGLOBAL);
26376195Sbrian	for (i = 0; i < 16; i += 2)
26476195Sbrian		vW(ptr + i) = 0;
26576195Sbrian
26676195Sbrian	switch (sc->model) {
26776195Sbrian	case PCXEVE:
26876195Sbrian		outb(sc->wport, 0xff);		/* window 7 */
26976195Sbrian		ptr = sc->vmem + (BIOSCODE & 0x1fff);
27076195Sbrian
27176195Sbrian		if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) {
27276195Sbrian			device_printf(sc->dev, "BIOS upload failed\n");
27376195Sbrian			return (EIO);
27476195Sbrian		}
27576195Sbrian
27676195Sbrian		outb(sc->port, FEPCLR);
27776195Sbrian		break;
27876195Sbrian
27976195Sbrian	case PCXE:
28076195Sbrian	case PCXI:
28176195Sbrian	case PCCX:
28276195Sbrian		ptr = sc->setwin(sc, BIOSCODE + ((0xf000 - sc->mem_seg) << 4));
28376195Sbrian		if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) {
28476195Sbrian			device_printf(sc->dev, "BIOS upload failed\n");
28576195Sbrian			return (EIO);
28676195Sbrian		}
28776195Sbrian		break;
28876195Sbrian
28976195Sbrian	case PCXEM:
29076195Sbrian	case PCIEPCX:
29176195Sbrian	case PCIXR:
29294323Sbrian		if (sc->pcibus)
29376195Sbrian			PCIPORT = FEPRST;
29476195Sbrian		else
29576195Sbrian			outb(sc->port, FEPRST | FEPMEM);
29676195Sbrian
29794323Sbrian		for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) &
29876195Sbrian		    FEPMASK) != FEPRST; i++) {
29994949Sbrian			if (i > hz) {
30094323Sbrian				log(LOG_ERR, "digi%d: %s init reset failed\n",
30194323Sbrian				    sc->res.unit, sc->name);
30276195Sbrian				return (EIO);
30376195Sbrian			}
30494361Sbrian			digi_delay(sc, "digiinit0", 5);
30576195Sbrian		}
30676195Sbrian		DLOG(DIGIDB_INIT, (sc->dev, "Got init reset after %d us\n", i));
30776195Sbrian
30876195Sbrian		/* Now upload the BIOS */
30976195Sbrian		cnt = (sc->bios.size < sc->win_size - BIOSOFFSET) ?
31076195Sbrian		    sc->bios.size : sc->win_size - BIOSOFFSET;
31176195Sbrian
31276195Sbrian		ptr = sc->setwin(sc, BIOSOFFSET);
31376195Sbrian		if (!digi_bcopy(sc->bios.data, ptr, cnt)) {
31476195Sbrian			device_printf(sc->dev, "BIOS upload (1) failed\n");
31576195Sbrian			return (EIO);
31676195Sbrian		}
31776195Sbrian
31876195Sbrian		if (cnt != sc->bios.size) {
31976195Sbrian			/* and the second part */
32076195Sbrian			ptr = sc->setwin(sc, sc->win_size);
32176195Sbrian			if (!digi_bcopy(sc->bios.data + cnt, ptr,
32276195Sbrian			    sc->bios.size - cnt)) {
32376195Sbrian				device_printf(sc->dev, "BIOS upload failed\n");
32476195Sbrian				return (EIO);
32576195Sbrian			}
32676195Sbrian		}
32776195Sbrian
32876195Sbrian		ptr = sc->setwin(sc, 0);
32976195Sbrian		vW(ptr + 0) = 0x0401;
33076195Sbrian		vW(ptr + 2) = 0x0bf0;
33176195Sbrian		vW(ptr + 4) = 0x0000;
33276195Sbrian		vW(ptr + 6) = 0x0000;
33376195Sbrian
33476195Sbrian		break;
33576195Sbrian	}
33676195Sbrian
33776195Sbrian	DLOG(DIGIDB_INIT, (sc->dev, "BIOS uploaded\n"));
33876195Sbrian
33976195Sbrian	ptr = sc->setwin(sc, MISCGLOBAL);
34076195Sbrian	W(ptr) = 0;
34176195Sbrian
34294323Sbrian	if (sc->pcibus) {
34376195Sbrian		PCIPORT = FEPCLR;
34476195Sbrian		resp = FEPRST;
34576195Sbrian	} else if (sc->model == PCXEVE) {
34676195Sbrian		outb(sc->port, FEPCLR);
34776195Sbrian		resp = FEPRST;
34876195Sbrian	} else {
34976195Sbrian		outb(sc->port, FEPCLR | FEPMEM);
35076195Sbrian		resp = FEPRST | FEPMEM;
35176195Sbrian	}
35276195Sbrian
35394323Sbrian	for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) & FEPMASK)
35476195Sbrian	    == resp; i++) {
35594949Sbrian		if (i > hz) {
35676195Sbrian			log(LOG_ERR, "digi%d: BIOS start failed\n",
35776195Sbrian			    sc->res.unit);
35876195Sbrian			return (EIO);
35976195Sbrian		}
36094361Sbrian		digi_delay(sc, "digibios0", 5);
36176195Sbrian	}
36276195Sbrian
36376195Sbrian	DLOG(DIGIDB_INIT, (sc->dev, "BIOS started after %d us\n", i));
36476195Sbrian
36576195Sbrian	for (i = 0; vW(ptr) != *(u_short *)"GD"; i++) {
36694949Sbrian		if (i > 2*hz) {
36776195Sbrian			log(LOG_ERR, "digi%d: BIOS boot failed "
36876195Sbrian			    "(0x%02x != 0x%02x)\n",
36976195Sbrian			    sc->res.unit, vW(ptr), *(u_short *)"GD");
37076195Sbrian			return (EIO);
37176195Sbrian		}
37294361Sbrian		digi_delay(sc, "digibios1", 5);
37376195Sbrian	}
37476195Sbrian
37576195Sbrian	DLOG(DIGIDB_INIT, (sc->dev, "BIOS booted after %d iterations\n", i));
37676195Sbrian
37776195Sbrian	if (sc->link.data != NULL) {
37876195Sbrian		DLOG(DIGIDB_INIT, (sc->dev, "Loading link data\n"));
37976195Sbrian		ptr = sc->setwin(sc, 0xcd0);
38076195Sbrian		digi_bcopy(sc->link.data, ptr, 21);	/* XXX 21 ? */
38176195Sbrian	}
38276195Sbrian
38376195Sbrian	/* load FEP/OS */
38476195Sbrian
38576195Sbrian	switch (sc->model) {
38676195Sbrian	case PCXE:
38776195Sbrian	case PCXEVE:
38876195Sbrian	case PCXI:
38976195Sbrian		ptr = sc->setwin(sc, sc->model == PCXI ? 0x2000 : 0x0);
39076195Sbrian		digi_bcopy(sc->fep.data, ptr, sc->fep.size);
39176195Sbrian
39276195Sbrian		/* A BIOS request to move our data to 0x2000 */
39376195Sbrian		ptr = sc->setwin(sc, MBOX);
39476195Sbrian		vW(ptr + 0) = 2;
39576195Sbrian		vW(ptr + 2) = sc->mem_seg + FEPCODESEG;
39676195Sbrian		vW(ptr + 4) = 0;
39776195Sbrian		vW(ptr + 6) = FEPCODESEG;
39876195Sbrian		vW(ptr + 8) = 0;
39976195Sbrian		vW(ptr + 10) = sc->fep.size;
40076195Sbrian
40176195Sbrian		/* Run the BIOS request */
40276195Sbrian		outb(sc->port, FEPREQ | FEPMEM);
40376195Sbrian		outb(sc->port, FEPCLR | FEPMEM);
40476195Sbrian
40576195Sbrian		for (i = 0; W(ptr); i++) {
40694949Sbrian			if (i > hz) {
40776195Sbrian				log(LOG_ERR, "digi%d: FEP/OS move failed\n",
40876195Sbrian				    sc->res.unit);
40976195Sbrian				sc->hidewin(sc);
41076195Sbrian				return (EIO);
41176195Sbrian			}
41294361Sbrian			digi_delay(sc, "digifep0", 5);
41376195Sbrian		}
41476195Sbrian		DLOG(DIGIDB_INIT,
41576195Sbrian		    (sc->dev, "FEP/OS moved after %d iterations\n", i));
41676195Sbrian
41776195Sbrian		/* Clear the confirm word */
41876195Sbrian		ptr = sc->setwin(sc, FEPSTAT);
41976195Sbrian		vW(ptr + 0) = 0;
42076195Sbrian
42176195Sbrian		/* A BIOS request to execute the FEP/OS */
42276195Sbrian		ptr = sc->setwin(sc, MBOX);
42376195Sbrian		vW(ptr + 0) = 0x01;
42476195Sbrian		vW(ptr + 2) = FEPCODESEG;
42576195Sbrian		vW(ptr + 4) = 0x04;
42676195Sbrian
42776195Sbrian		/* Run the BIOS request */
42876195Sbrian		outb(sc->port, FEPREQ);
42976195Sbrian		outb(sc->port, FEPCLR);
43076195Sbrian
43176195Sbrian		ptr = sc->setwin(sc, FEPSTAT);
43276195Sbrian
43376195Sbrian		break;
43476195Sbrian
43576195Sbrian	case PCXEM:
43676195Sbrian	case PCIEPCX:
43776195Sbrian	case PCIXR:
43876195Sbrian		DLOG(DIGIDB_INIT, (sc->dev, "Loading FEP/OS\n"));
43976195Sbrian
44076195Sbrian		cnt = (sc->fep.size < sc->win_size - BIOSOFFSET) ?
44176195Sbrian		    sc->fep.size : sc->win_size - BIOSOFFSET;
44276195Sbrian
44376195Sbrian		ptr = sc->setwin(sc, BIOSOFFSET);
44476195Sbrian		digi_bcopy(sc->fep.data, ptr, cnt);
44576195Sbrian
44676195Sbrian		if (cnt != sc->fep.size) {
44776195Sbrian			ptr = sc->setwin(sc, BIOSOFFSET + cnt);
44876195Sbrian			digi_bcopy(sc->fep.data + cnt, ptr,
44976195Sbrian			    sc->fep.size - cnt);
45076195Sbrian		}
45176195Sbrian
45276195Sbrian		DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS loaded\n"));
45376195Sbrian
45476195Sbrian		ptr = sc->setwin(sc, 0xc30);
45576195Sbrian		W(ptr + 4) = 0x1004;
45676195Sbrian		W(ptr + 6) = 0xbfc0;
45776195Sbrian		W(ptr + 0) = 0x03;
45876195Sbrian		W(ptr + 2) = 0x00;
45976195Sbrian
46076195Sbrian		/* Clear the confirm word */
46176195Sbrian		ptr = sc->setwin(sc, FEPSTAT);
46276195Sbrian		W(ptr + 0) = 0;
46376195Sbrian
46476195Sbrian		if (sc->port)
46576195Sbrian			outb(sc->port, 0);		/* XXX necessary ? */
46676195Sbrian
46776195Sbrian		break;
46876195Sbrian
46976195Sbrian	case PCCX:
47076195Sbrian		ptr = sc->setwin(sc, 0xd000);
47176195Sbrian		digi_bcopy(sc->fep.data, ptr, sc->fep.size);
47276195Sbrian
47376195Sbrian		/* A BIOS request to execute the FEP/OS */
47476195Sbrian		ptr = sc->setwin(sc, 0xc40);
47576195Sbrian		W(ptr + 0) = 1;
47676195Sbrian		W(ptr + 2) = FEPCODE >> 4;
47776195Sbrian		W(ptr + 4) = 4;
47876195Sbrian
47976195Sbrian		/* Clear the confirm word */
48076195Sbrian		ptr = sc->setwin(sc, FEPSTAT);
48176195Sbrian		W(ptr + 0) = 0;
48276195Sbrian
48376195Sbrian		/* Run the BIOS request */
48476195Sbrian		outb(sc->port, FEPREQ | FEPMEM); /* send interrupt to BIOS */
48576195Sbrian		outb(sc->port, FEPCLR | FEPMEM);
48676195Sbrian		break;
48776195Sbrian	}
48876195Sbrian
48976195Sbrian	/* Now wait 'till the FEP/OS has booted */
49076195Sbrian	for (i = 0; vW(ptr) != *(u_short *)"OS"; i++) {
49194949Sbrian		if (i > 2*hz) {
49276195Sbrian			log(LOG_ERR, "digi%d: FEP/OS start failed "
49376195Sbrian			    "(0x%02x != 0x%02x)\n",
49476195Sbrian			    sc->res.unit, vW(ptr), *(u_short *)"OS");
49576195Sbrian			sc->hidewin(sc);
49676195Sbrian			return (EIO);
49776195Sbrian		}
49894361Sbrian		digi_delay(sc, "digifep1", 5);
49976195Sbrian	}
50076195Sbrian
50176195Sbrian	DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS started after %d iterations\n", i));
50276195Sbrian
50376195Sbrian	if (sc->model >= PCXEM) {
50476195Sbrian		ptr = sc->setwin(sc, 0xe04);
50576195Sbrian		vW(ptr) = 2;
50676195Sbrian		ptr = sc->setwin(sc, 0xc02);
50776195Sbrian		sc->numports = vW(ptr);
50876195Sbrian	} else {
50976195Sbrian		ptr = sc->setwin(sc, 0xc22);
51076195Sbrian		sc->numports = vW(ptr);
51176195Sbrian	}
51276195Sbrian
51376195Sbrian	if (sc->numports == 0) {
51476195Sbrian		device_printf(sc->dev, "%s, 0 ports found\n", sc->name);
51576195Sbrian		sc->hidewin(sc);
51676195Sbrian		return (0);
51776195Sbrian	}
51876195Sbrian
519136200Sphk	device_printf(sc->dev, "%s, %d ports found\n", sc->name, sc->numports);
52076195Sbrian
52176195Sbrian	if (sc->ports)
52276195Sbrian		free(sc->ports, M_TTYS);
52377004Sbrian	sc->ports = malloc(sizeof(struct digi_p) * sc->numports,
524111119Simp	    M_TTYS, M_WAITOK | M_ZERO);
52576195Sbrian
52676195Sbrian	/*
52776195Sbrian	 * XXX Should read port 0xc90 for an array of 2byte values, 1 per
52876195Sbrian	 * port.  If the value is 0, the port is broken....
52976195Sbrian	 */
53076195Sbrian
53176195Sbrian	ptr = sc->setwin(sc, 0);
53276195Sbrian
53376195Sbrian	/* We should now init per-port structures */
53476195Sbrian	bc = (volatile struct board_chan *)(ptr + CHANSTRUCT);
53576195Sbrian	sc->gdata = (volatile struct global_data *)(ptr + FEP_GLOBAL);
53676195Sbrian
53776195Sbrian	sc->memcmd = ptr + sc->gdata->cstart;
53876195Sbrian	sc->memevent = ptr + sc->gdata->istart;
53976195Sbrian
54076195Sbrian	for (i = 0; i < sc->numports; i++, bc++) {
54176195Sbrian		port = sc->ports + i;
54276195Sbrian		port->pnum = i;
54376195Sbrian		port->sc = sc;
54476195Sbrian		port->status = ENABLED;
54576195Sbrian		port->bc = bc;
546136200Sphk		tp = port->tp = ttyalloc();
547136200Sphk		tp->t_oproc = digistart;
548136200Sphk		tp->t_param = digiparam;
549136200Sphk		tp->t_modem = digimodem;
550136200Sphk		tp->t_break = digibreak;
551136200Sphk		tp->t_stop = digistop;
552136200Sphk		tp->t_cioctl = digisioctl;
553136200Sphk		tp->t_ioctl = digiioctl;
554136200Sphk		tp->t_open = digiopen;
555136200Sphk		tp->t_close = digiclose;
556136200Sphk		tp->t_sc = port;
55776195Sbrian
55876195Sbrian		if (sc->model == PCXEVE) {
55976195Sbrian			port->txbuf = ptr +
56076195Sbrian			    (((bc->tseg - sc->mem_seg) << 4) & 0x1fff);
56176195Sbrian			port->rxbuf = ptr +
56276195Sbrian			    (((bc->rseg - sc->mem_seg) << 4) & 0x1fff);
56376195Sbrian			port->txwin = FEPWIN | ((bc->tseg - sc->mem_seg) >> 9);
56476195Sbrian			port->rxwin = FEPWIN | ((bc->rseg - sc->mem_seg) >> 9);
56576195Sbrian		} else if (sc->model == PCXI || sc->model == PCXE) {
56676195Sbrian			port->txbuf = ptr + ((bc->tseg - sc->mem_seg) << 4);
56776195Sbrian			port->rxbuf = ptr + ((bc->rseg - sc->mem_seg) << 4);
56876195Sbrian			port->txwin = port->rxwin = 0;
56976195Sbrian		} else {
57076195Sbrian			port->txbuf = ptr +
57176195Sbrian			    (((bc->tseg - sc->mem_seg) << 4) % sc->win_size);
57276195Sbrian			port->rxbuf = ptr +
57376195Sbrian			    (((bc->rseg - sc->mem_seg) << 4) % sc->win_size);
57476195Sbrian			port->txwin = FEPWIN |
57576195Sbrian			    (((bc->tseg - sc->mem_seg) << 4) / sc->win_size);
57676195Sbrian			port->rxwin = FEPWIN |
57776195Sbrian			    (((bc->rseg - sc->mem_seg) << 4) / sc->win_size);
57876195Sbrian		}
57976195Sbrian		port->txbufsize = bc->tmax + 1;
58076195Sbrian		port->rxbufsize = bc->rmax + 1;
58176195Sbrian
58276195Sbrian		lowwater = port->txbufsize >> 2;
58376195Sbrian		if (lowwater > 1024)
58476195Sbrian			lowwater = 1024;
58576195Sbrian		sc->setwin(sc, 0);
58676195Sbrian		fepcmd_w(port, STXLWATER, lowwater, 10);
58776195Sbrian		fepcmd_w(port, SRXLWATER, port->rxbufsize >> 2, 10);
58876195Sbrian		fepcmd_w(port, SRXHWATER, (3 * port->rxbufsize) >> 2, 10);
58976195Sbrian
59076195Sbrian		bc->edelay = 100;
59176195Sbrian
59276195Sbrian		/*
59376195Sbrian		 * We don't use all the flags from <sys/ttydefaults.h> since
59476195Sbrian		 * they are only relevant for logins.  It's important to have
59576195Sbrian		 * echo off initially so that the line doesn't start blathering
59676195Sbrian		 * before the echo flag can be turned off.
59776195Sbrian		 */
598136200Sphk		tp->t_init_in.c_iflag = 0;
599136200Sphk		tp->t_init_in.c_oflag = 0;
600136200Sphk		tp->t_init_in.c_cflag = TTYDEF_CFLAG;
601136200Sphk		tp->t_init_in.c_lflag = 0;
602136200Sphk		termioschars(&tp->t_init_in);
603136200Sphk		tp->t_init_in.c_ispeed = tp->t_init_in.c_ospeed = digidefaultrate;
604136200Sphk		tp->t_init_out = tp->t_init_in;
60576195Sbrian		port->send_ring = 1;	/* Default action on signal RI */
606136200Sphk		ttycreate(tp, NULL, 0, MINOR_CALLOUT, "D%r%r", sc->res.unit, i);
60776195Sbrian	}
60876195Sbrian
60976195Sbrian	sc->hidewin(sc);
61076195Sbrian	sc->inttest = timeout(digi_int_test, sc, hz);
61176195Sbrian	/* fepcmd_w(&sc->ports[0], 0xff, 0, 0); */
61276195Sbrian	sc->status = DIGI_STATUS_ENABLED;
61376195Sbrian
61476195Sbrian	return (0);
61576195Sbrian}
61676195Sbrian
61776195Sbrianstatic int
618131095Sphkdigimodem(struct tty *tp, int sigon, int sigoff)
61976195Sbrian{
620131095Sphk	struct digi_softc *sc;
621131095Sphk	struct digi_p *port;
622131095Sphk	int bitand, bitor, mstat;
62376195Sbrian
624136200Sphk	port = tp->t_sc;
625136200Sphk	sc = port->sc;
626131095Sphk
627131095Sphk	if (sigon == 0 && sigoff == 0) {
62876195Sbrian		port->sc->setwin(port->sc, 0);
62976195Sbrian		mstat = port->bc->mstat;
63076195Sbrian		port->sc->hidewin(port->sc);
63176195Sbrian		if (mstat & port->sc->csigs->rts)
632131095Sphk			sigon |= SER_RTS;
63378496Sbrian		if (mstat & port->cd)
634131095Sphk			sigon |= SER_DCD;
63578496Sbrian		if (mstat & port->dsr)
636131095Sphk			sigon |= SER_DSR;
63776195Sbrian		if (mstat & port->sc->csigs->cts)
638131095Sphk			sigon |= SER_CTS;
63976195Sbrian		if (mstat & port->sc->csigs->ri)
640131095Sphk			sigon |= SER_RI;
64176195Sbrian		if (mstat & port->sc->csigs->dtr)
642131095Sphk			sigon |= SER_DTR;
643131095Sphk		return (sigon);
64476195Sbrian	}
64576195Sbrian
646131095Sphk	bitand = 0;
647131095Sphk	bitor = 0;
64876195Sbrian
649131095Sphk	if (sigoff & SER_DTR)
650131095Sphk		bitand |= port->sc->csigs->dtr;
651131095Sphk	if (sigoff & SER_RTS)
652131095Sphk		bitand |= port->sc->csigs->rts;
653131095Sphk	if (sigon & SER_DTR)
654131095Sphk		bitor |= port->sc->csigs->dtr;
655131095Sphk	if (sigon & SER_RTS)
656131095Sphk		bitor |= port->sc->csigs->rts;
657131095Sphk	fepcmd_b(port, SETMODEM, bitor, ~bitand, 0);
65876195Sbrian	return (0);
65976195Sbrian}
66076195Sbrian
661104094Sphkstatic int
662136200Sphkdigicopen(struct cdev *dev, int flag, int mode, struct thread *td)
66376195Sbrian{
66476195Sbrian	struct digi_softc *sc;
665136200Sphk
666136200Sphk	sc = dev->si_drv1;
667136200Sphk	if (sc->status != DIGI_STATUS_ENABLED) {
668136200Sphk		DLOG(DIGIDB_OPEN, (sc->dev, "Cannot open a disabled card\n"));
669136200Sphk		return (ENXIO);
670136200Sphk	}
671136200Sphk	sc->opencnt++;
672136200Sphk	return (0);
673136200Sphk}
674136200Sphk
675136200Sphkstatic int
676136200Sphkdigiopen(struct tty *tp, struct cdev *dev)
677136200Sphk{
678136200Sphk	int error;
679136200Sphk	struct digi_softc *sc;
68076195Sbrian	struct digi_p *port;
68176195Sbrian	volatile struct board_chan *bc;
68276195Sbrian
683136200Sphk	port = tp->t_sc;
684136200Sphk	sc = port->sc;
68576195Sbrian
68676195Sbrian	if (sc->status != DIGI_STATUS_ENABLED) {
68776195Sbrian		DLOG(DIGIDB_OPEN, (sc->dev, "Cannot open a disabled card\n"));
68876195Sbrian		return (ENXIO);
68976195Sbrian	}
69076195Sbrian	bc = port->bc;
69176195Sbrian
692136200Sphk	/*
693136200Sphk	 * The device isn't open, so there are no conflicts.
694136200Sphk	 * Initialize it.  Initialization is done twice in many
695136200Sphk	 * cases: to preempt sleeping callin opens if we are callout,
696136200Sphk	 * and to complete a callin open after DCD rises.
697136200Sphk	 */
698136200Sphk	sc->setwin(sc, 0);
69976195Sbrian
700136200Sphk	bc->rout = bc->rin;	/* clear input queue */
701136200Sphk	bc->idata = 1;
702136200Sphk	bc->iempty = 1;
703136200Sphk	bc->ilow = 1;
704136200Sphk	bc->mint = port->cd | port->sc->csigs->ri;
705136200Sphk	bc->tin = bc->tout;
706136200Sphk	if (port->ialtpin) {
707136200Sphk		port->cd = sc->csigs->dsr;
708136200Sphk		port->dsr = sc->csigs->cd;
70976195Sbrian	} else {
710136200Sphk		port->cd = sc->csigs->cd;
711136200Sphk		port->dsr = sc->csigs->dsr;
71276195Sbrian	}
713136200Sphk	tp->t_wopeners++;			/* XXX required ? */
714136200Sphk	error = digiparam(tp, &tp->t_termios);
715136200Sphk	tp->t_wopeners--;
71676195Sbrian
71776195Sbrian	return (error);
71876195Sbrian}
71976195Sbrian
720104094Sphkstatic int
721136200Sphkdigicclose(struct cdev *dev, int flag, int mode, struct thread *td)
72276195Sbrian{
72376195Sbrian	struct digi_softc *sc;
72476195Sbrian
725136200Sphk	sc = dev->si_drv1;
726136200Sphk	sc->opencnt--;
72776195Sbrian	return (0);
72876195Sbrian}
72976195Sbrian
73076195Sbrianstatic void
73176195Sbriandigidtrwakeup(void *chan)
73276195Sbrian{
73376195Sbrian	struct digi_p *port = chan;
73476195Sbrian
73576195Sbrian	port->status &= ~DIGI_DTR_OFF;
736131981Sphk	wakeup(&port->tp->t_dtr_wait);
737136200Sphk	port->tp->t_wopeners--;
73876195Sbrian}
73976195Sbrian
74076195Sbrianstatic void
741136200Sphkdigiclose(struct tty *tp)
74276195Sbrian{
74376195Sbrian	volatile struct board_chan *bc;
744136200Sphk	struct digi_p *port;
74576195Sbrian	int s;
74676195Sbrian
747136200Sphk	port = tp->t_sc;
74876195Sbrian	bc = port->bc;
74976195Sbrian
75076195Sbrian	s = spltty();
75176195Sbrian	port->sc->setwin(port->sc, 0);
75276195Sbrian	bc->idata = 0;
75376195Sbrian	bc->iempty = 0;
75476195Sbrian	bc->ilow = 0;
75576195Sbrian	bc->mint = 0;
756136200Sphk	if ((tp->t_cflag & HUPCL) ||
757136200Sphk	    (!tp->t_actout && !(bc->mstat & port->cd) &&
758136200Sphk	    !(tp->t_init_in.c_cflag & CLOCAL)) ||
759136200Sphk	    !(tp->t_state & TS_ISOPEN)) {
760136200Sphk		digimodem(tp, 0, SER_DTR | SER_RTS);
761136200Sphk		if (tp->t_dtr_wait != 0) {
76276195Sbrian			/* Schedule a wakeup of any callin devices */
763136200Sphk			tp->t_wopeners++;
764136200Sphk			timeout(&digidtrwakeup, port, tp->t_dtr_wait);
76576195Sbrian			port->status |= DIGI_DTR_OFF;
76676195Sbrian		}
76776195Sbrian	}
768136200Sphk	tp->t_actout = FALSE;
769136200Sphk	wakeup(&tp->t_actout);
770136200Sphk	wakeup(TSA_CARR_ON(tp));
77176195Sbrian	splx(s);
77276195Sbrian}
77376195Sbrian
77476195Sbrian/*
77576195Sbrian * Load module "digi_<mod>.ko" and look for a symbol called digi_mod_<mod>.
77676195Sbrian *
77776195Sbrian * Populate sc->bios, sc->fep, and sc->link from this data.
77876195Sbrian *
77976195Sbrian * sc->fep.data, sc->bios.data and sc->link.data are malloc()d according
78076195Sbrian * to their respective sizes.
78176195Sbrian *
78276195Sbrian * The module is unloaded when we're done.
78376195Sbrian */
78476195Sbrianstatic int
78576195Sbriandigi_loadmoduledata(struct digi_softc *sc)
78676195Sbrian{
78776195Sbrian	struct digi_mod *digi_mod;
78876195Sbrian	linker_file_t lf;
78976195Sbrian	char *modfile, *sym;
79076195Sbrian	caddr_t symptr;
79178414Sbrian	int modlen, res;
79276195Sbrian
79376195Sbrian	KASSERT(sc->bios.data == NULL, ("Uninitialised BIOS variable"));
79476195Sbrian	KASSERT(sc->fep.data == NULL, ("Uninitialised FEP variable"));
79576195Sbrian	KASSERT(sc->link.data == NULL, ("Uninitialised LINK variable"));
79676195Sbrian	KASSERT(sc->module != NULL, ("Uninitialised module name"));
79776195Sbrian
79878414Sbrian	modlen = strlen(sc->module);
799111119Simp	modfile = malloc(modlen + 6, M_TEMP, M_WAITOK);
80078414Sbrian	snprintf(modfile, modlen + 6, "digi_%s", sc->module);
801132806Sphk	res = linker_reference_module(modfile, NULL, &lf);
802132806Sphk	if ((res = linker_reference_module(modfile, NULL, &lf)) != 0)
803132806Sphk		printf("%s: Failed %d to autoload module\n", modfile, res);
80476195Sbrian	free(modfile, M_TEMP);
80576195Sbrian	if (res != 0)
80676195Sbrian		return (res);
80776195Sbrian
808111119Simp	sym = malloc(modlen + 10, M_TEMP, M_WAITOK);
80978414Sbrian	snprintf(sym, modlen + 10, "digi_mod_%s", sc->module);
81076195Sbrian	if ((symptr = linker_file_lookup_symbol(lf, sym, 0)) == NULL)
81176195Sbrian		printf("digi_%s.ko: Symbol `%s' not found\n", sc->module, sym);
81276195Sbrian	free(sym, M_TEMP);
81376195Sbrian
81476195Sbrian	digi_mod = (struct digi_mod *)symptr;
81576195Sbrian	if (digi_mod->dm_version != DIGI_MOD_VERSION) {
81676195Sbrian		printf("digi_%s.ko: Invalid version %d (need %d)\n",
81776195Sbrian		    sc->module, digi_mod->dm_version, DIGI_MOD_VERSION);
818132117Sphk		linker_file_unload(lf, LINKER_UNLOAD_FORCE);
81976195Sbrian		return (EINVAL);
82076195Sbrian	}
82176195Sbrian
82276195Sbrian	sc->bios.size = digi_mod->dm_bios.size;
82376195Sbrian	if (sc->bios.size != 0 && digi_mod->dm_bios.data != NULL) {
824111119Simp		sc->bios.data = malloc(sc->bios.size, M_TTYS, M_WAITOK);
82576195Sbrian		bcopy(digi_mod->dm_bios.data, sc->bios.data, sc->bios.size);
82676195Sbrian	}
82776195Sbrian
82876195Sbrian	sc->fep.size = digi_mod->dm_fep.size;
82976195Sbrian	if (sc->fep.size != 0 && digi_mod->dm_fep.data != NULL) {
830111119Simp		sc->fep.data = malloc(sc->fep.size, M_TTYS, M_WAITOK);
83176195Sbrian		bcopy(digi_mod->dm_fep.data, sc->fep.data, sc->fep.size);
83276195Sbrian	}
83376195Sbrian
83476195Sbrian	sc->link.size = digi_mod->dm_link.size;
83576195Sbrian	if (sc->link.size != 0 && digi_mod->dm_link.data != NULL) {
836111119Simp		sc->link.data = malloc(sc->link.size, M_TTYS, M_WAITOK);
83776195Sbrian		bcopy(digi_mod->dm_link.data, sc->link.data, sc->link.size);
83876195Sbrian	}
83976195Sbrian
840132117Sphk	linker_file_unload(lf, LINKER_UNLOAD_FORCE);
84176195Sbrian
84276195Sbrian	return (0);
84376195Sbrian}
84476195Sbrian
84576195Sbrianstatic int
846136200Sphkdigisioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
84776195Sbrian{
848136200Sphk	struct digi_p *port;
84976195Sbrian	struct digi_softc *sc;
85076195Sbrian
851136200Sphk	port = dev->si_drv1;
852136200Sphk	sc = port->sc;
85376195Sbrian
854136200Sphk	switch (cmd) {
855136200Sphk	case DIGIIO_GETALTPIN:
856136200Sphk		if (ISINIT(dev))
857136200Sphk			*(int *)data = port->ialtpin;
858136200Sphk		else if (ISLOCK(dev))
859136200Sphk			*(int *)data = port->laltpin;
860136200Sphk		else
861136200Sphk			return (ENOTTY);
862136200Sphk		break;
863136200Sphk	case DIGIIO_SETALTPIN:
864136200Sphk		if (ISINIT(dev)) {
865136200Sphk			if (!port->laltpin) {
866136200Sphk				port->ialtpin = !!*(int *)data;
867136200Sphk				DLOG(DIGIDB_SET, (sc->dev,
868136200Sphk				    "port%d: initial ALTPIN %s\n", port->pnum,
869136200Sphk				    port->ialtpin ? "set" : "cleared"));
870136200Sphk			}
871136200Sphk		} else if (ISLOCK(dev)) {
872136200Sphk			port->laltpin = !!*(int *)data;
873136200Sphk			DLOG(DIGIDB_SET, (sc->dev,
874136200Sphk			    "port%d: ALTPIN %slocked\n",
875136200Sphk			    port->pnum, port->laltpin ? "" : "un"));
876136200Sphk		} else
877136200Sphk			return (ENOTTY);
878136200Sphk		break;
879136200Sphk	default:
880136200Sphk		return (ENOTTY);
881136200Sphk	}
882136200Sphk	return (0);
883136200Sphk}
88476195Sbrian
885136200Sphkstatic int
886136200Sphkdigicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
887136200Sphk{
888136200Sphk	int error;
889136200Sphk	struct digi_softc *sc;
890136200Sphk
891136200Sphk	sc = dev->si_drv1;
892136200Sphk
89376195Sbrian	if (sc->status == DIGI_STATUS_DISABLED)
89476195Sbrian		return (ENXIO);
89576195Sbrian
896136200Sphk	switch (cmd) {
897136200Sphk	case DIGIIO_DEBUG:
89876195Sbrian#ifdef DEBUG
899136200Sphk		digi_debug = *(int *)data;
900136200Sphk		return (0);
90176195Sbrian#else
902136200Sphk		device_printf(sc->dev, "DEBUG not defined\n");
903136200Sphk		return (ENXIO);
90476195Sbrian#endif
905136200Sphk	case DIGIIO_REINIT:
906136200Sphk		digi_loadmoduledata(sc);
907136200Sphk		error = digi_init(sc);
908136200Sphk		digi_freemoduledata(sc);
909136200Sphk		return (error);
91076195Sbrian
911136200Sphk	case DIGIIO_MODEL:
912136200Sphk		*(enum digi_model *)data = sc->model;
913136200Sphk		return (0);
91476195Sbrian
915136200Sphk	case DIGIIO_IDENT:
916136200Sphk		return (copyout(sc->name, *(char **)data,
917136200Sphk		    strlen(sc->name) + 1));
918136200Sphk	default:
919136200Sphk		return (ENOIOCTL);
92076195Sbrian	}
921136200Sphk}
92276195Sbrian
923136200Sphkstatic int
924136200Sphkdigiioctl(struct tty *tp, u_long cmd, void *data, int flag, struct thread *td)
925136200Sphk{
926136200Sphk	struct digi_softc *sc;
927136200Sphk	struct digi_p *port;
928136200Sphk
929136200Sphk	port = tp->t_sc;
930136200Sphk	sc = port->sc;
931136200Sphk	if (sc->status == DIGI_STATUS_DISABLED)
93276195Sbrian		return (ENXIO);
93376195Sbrian
93476195Sbrian	if (!(port->status & ENABLED))
93576195Sbrian		return (ENXIO);
93676195Sbrian
93778496Sbrian	switch (cmd) {
93878496Sbrian	case DIGIIO_GETALTPIN:
93978496Sbrian		*(int *)data = !!(port->dsr == sc->csigs->cd);
94078496Sbrian		return (0);
94178496Sbrian
94278496Sbrian	case DIGIIO_SETALTPIN:
94378496Sbrian		if (!port->laltpin) {
94478496Sbrian			if (*(int *)data) {
94578496Sbrian				DLOG(DIGIDB_SET, (sc->dev,
946136200Sphk				    "port%d: ALTPIN set\n", port->pnum));
94778496Sbrian				port->cd = sc->csigs->dsr;
94878496Sbrian				port->dsr = sc->csigs->cd;
94978496Sbrian			} else {
95078496Sbrian				DLOG(DIGIDB_SET, (sc->dev,
951136200Sphk				    "port%d: ALTPIN cleared\n", port->pnum));
95278496Sbrian				port->cd = sc->csigs->cd;
95378496Sbrian				port->dsr = sc->csigs->dsr;
95478496Sbrian			}
95578496Sbrian		}
95678496Sbrian		return (0);
95776195Sbrian	case DIGIIO_RING:
95876195Sbrian		port->send_ring = *(u_char *)data;
95976195Sbrian		break;
96076195Sbrian	default:
96176195Sbrian		return (ENOTTY);
96276195Sbrian	}
96376195Sbrian	return (0);
96476195Sbrian}
96576195Sbrian
966131373Sphkstatic void
967131095Sphkdigibreak(struct tty *tp, int brk)
968131095Sphk{
969131095Sphk	struct digi_p *port;
970131095Sphk
971136200Sphk	port = tp->t_sc;
972131095Sphk
973131095Sphk	/*
974131095Sphk	 * now it sends 400 millisecond break because I don't know
975131095Sphk	 * how to send an infinite break
976131095Sphk	 */
977131095Sphk	if (brk)
978131095Sphk		fepcmd_w(port, SENDBREAK, 400, 10);
979131095Sphk}
980131095Sphk
981131095Sphkstatic int
98276195Sbriandigiparam(struct tty *tp, struct termios *t)
98376195Sbrian{
98476195Sbrian	struct digi_softc *sc;
98576195Sbrian	struct digi_p *port;
98676195Sbrian	int cflag;
98776195Sbrian	int iflag;
98876195Sbrian	int hflow;
98976195Sbrian	int s;
99076195Sbrian	int window;
99176195Sbrian
992136200Sphk	port = tp->t_sc;
993136200Sphk	sc = port->sc;
994136200Sphk	DLOG(DIGIDB_SET, (sc->dev, "port%d: setting parameters\n", port->pnum));
99576195Sbrian
99676195Sbrian	if (t->c_ispeed == 0)
99776195Sbrian		t->c_ispeed = t->c_ospeed;
99876195Sbrian
99976195Sbrian	cflag = ttspeedtab(t->c_ospeed, digispeedtab);
100076195Sbrian
100176195Sbrian	if (cflag < 0 || (cflag > 0 && t->c_ispeed != t->c_ospeed))
100276195Sbrian		return (EINVAL);
100376195Sbrian
100476195Sbrian	s = splclock();
100576195Sbrian
100676195Sbrian	window = sc->window;
100776195Sbrian	sc->setwin(sc, 0);
100876195Sbrian
100976195Sbrian	if (cflag == 0) {				/* hangup */
1010136200Sphk		DLOG(DIGIDB_SET, (sc->dev, "port%d: hangup\n", port->pnum));
1011131095Sphk		digimodem(port->tp, 0, SER_DTR | SER_RTS);
101276195Sbrian	} else {
1013131095Sphk		digimodem(port->tp, SER_DTR | SER_RTS, 0);
101476195Sbrian
1015136200Sphk		DLOG(DIGIDB_SET, (sc->dev, "port%d: CBAUD = %d\n", port->pnum,
101676195Sbrian		    cflag));
101776195Sbrian
101876195Sbrian#if 0
101976195Sbrian		/* convert flags to sysV-style values */
102076195Sbrian		if (t->c_cflag & PARODD)
102176195Sbrian			cflag |= 0x0200;
102276195Sbrian		if (t->c_cflag & PARENB)
102376195Sbrian			cflag |= 0x0100;
102476195Sbrian		if (t->c_cflag & CSTOPB)
102576195Sbrian			cflag |= 0x0080;
102676195Sbrian#else
102776195Sbrian		/* convert flags to sysV-style values */
102876195Sbrian		if (t->c_cflag & PARODD)
102976195Sbrian			cflag |= FEP_PARODD;
103076195Sbrian		if (t->c_cflag & PARENB)
103176195Sbrian			cflag |= FEP_PARENB;
103276195Sbrian		if (t->c_cflag & CSTOPB)
103376195Sbrian			cflag |= FEP_CSTOPB;
103476195Sbrian		if (t->c_cflag & CLOCAL)
103576195Sbrian			cflag |= FEP_CLOCAL;
103676195Sbrian#endif
103776195Sbrian
103876195Sbrian		cflag |= (t->c_cflag & CSIZE) >> 4;
1039136200Sphk		DLOG(DIGIDB_SET, (sc->dev, "port%d: CFLAG = 0x%x\n", port->pnum,
104076195Sbrian		    cflag));
104176195Sbrian		fepcmd_w(port, SETCFLAGS, (unsigned)cflag, 0);
104276195Sbrian	}
104376195Sbrian
104476195Sbrian	iflag =
104576195Sbrian	    t->c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP);
104676195Sbrian	if (port->c_iflag & IXON)
104776195Sbrian		iflag |= 0x400;
104876195Sbrian	if (port->c_iflag & IXANY)
104976195Sbrian		iflag |= 0x800;
105076195Sbrian	if (port->c_iflag & IXOFF)
105176195Sbrian		iflag |= 0x1000;
105276195Sbrian
1053136200Sphk	DLOG(DIGIDB_SET, (sc->dev, "port%d: set iflag = 0x%x\n", port->pnum, iflag));
105476195Sbrian	fepcmd_w(port, SETIFLAGS, (unsigned)iflag, 0);
105576195Sbrian
105676195Sbrian	hflow = 0;
105776195Sbrian	if (t->c_cflag & CDTR_IFLOW)
105876195Sbrian		hflow |= sc->csigs->dtr;
105976195Sbrian	if (t->c_cflag & CRTS_IFLOW)
106076195Sbrian		hflow |= sc->csigs->rts;
106176195Sbrian	if (t->c_cflag & CCTS_OFLOW)
106276195Sbrian		hflow |= sc->csigs->cts;
106376195Sbrian	if (t->c_cflag & CDSR_OFLOW)
106478496Sbrian		hflow |= port->dsr;
106576195Sbrian	if (t->c_cflag & CCAR_OFLOW)
106678496Sbrian		hflow |= port->cd;
106776195Sbrian
1068136200Sphk	DLOG(DIGIDB_SET, (sc->dev, "port%d: set hflow = 0x%x\n", port->pnum, hflow));
106976195Sbrian	fepcmd_w(port, SETHFLOW, 0xff00 | (unsigned)hflow, 0);
107076195Sbrian
107176195Sbrian	DLOG(DIGIDB_SET, (sc->dev, "port%d: set startc(0x%x), stopc(0x%x)\n",
1072136200Sphk	    port->pnum, t->c_cc[VSTART], t->c_cc[VSTOP]));
107376195Sbrian	fepcmd_b(port, SONOFFC, t->c_cc[VSTART], t->c_cc[VSTOP], 0);
107476195Sbrian
107576195Sbrian	if (sc->window != 0)
107676195Sbrian		sc->towin(sc, 0);
107776195Sbrian	if (window != 0)
107876195Sbrian		sc->towin(sc, window);
107976195Sbrian	splx(s);
108076195Sbrian
108176195Sbrian	return (0);
108276195Sbrian}
108376195Sbrian
108476195Sbrianstatic void
108576195Sbriandigi_intr(void *vp)
108676195Sbrian{
108776195Sbrian	struct digi_p *port;
108876195Sbrian	char *cxcon;
108976195Sbrian	struct digi_softc *sc;
109076195Sbrian	int ehead, etail;
109176195Sbrian	volatile struct board_chan *bc;
109276195Sbrian	struct tty *tp;
109376195Sbrian	int head, tail;
109476195Sbrian	int wrapmask;
109576195Sbrian	int size, window;
109676195Sbrian	struct event {
109776195Sbrian		u_char pnum;
109876195Sbrian		u_char event;
109976195Sbrian		u_char mstat;
110076195Sbrian		u_char lstat;
110176195Sbrian	} event;
110276195Sbrian
110376195Sbrian	sc = vp;
110476195Sbrian
110576195Sbrian	if (sc->status != DIGI_STATUS_ENABLED) {
110676195Sbrian		DLOG(DIGIDB_IRQ, (sc->dev, "interrupt on disabled board !\n"));
110776195Sbrian		return;
110876195Sbrian	}
110976327Sbrian
111076195Sbrian#ifdef DIGI_INTERRUPT
111176195Sbrian	microtime(&sc->intr_timestamp);
111276195Sbrian#endif
111376195Sbrian
111476195Sbrian	window = sc->window;
111576195Sbrian	sc->setwin(sc, 0);
111676195Sbrian
111776195Sbrian	if (sc->model >= PCXEM && W(sc->vmem + 0xd00)) {
111876195Sbrian		struct con_bios *con = con_bios_list;
111976195Sbrian		register u_char *ptr;
112076195Sbrian
112176195Sbrian		ptr = sc->vmem + W(sc->vmem + 0xd00);
112276195Sbrian		while (con) {
112376195Sbrian			if (ptr[1] && W(ptr + 2) == W(con->bios + 2))
112476195Sbrian				/* Not first block -- exact match */
112576195Sbrian				break;
112676195Sbrian
112776195Sbrian			if (W(ptr + 4) >= W(con->bios + 4) &&
112876195Sbrian			    W(ptr + 4) <= W(con->bios + 6))
112976195Sbrian				/* Initial search concetrator BIOS */
113076195Sbrian				break;
113176195Sbrian		}
113276195Sbrian
113376195Sbrian		if (con == NULL) {
113476195Sbrian			log(LOG_ERR, "digi%d: wanted bios LREV = 0x%04x"
113576195Sbrian			    " not found!\n", sc->res.unit, W(ptr + 4));
113676195Sbrian			W(ptr + 10) = 0;
113776195Sbrian			W(sc->vmem + 0xd00) = 0;
113876195Sbrian			goto eoi;
113976195Sbrian		}
114076195Sbrian		cxcon = con->bios;
114176195Sbrian		W(ptr + 4) = W(cxcon + 4);
114276195Sbrian		W(ptr + 6) = W(cxcon + 6);
114376195Sbrian		if (ptr[1] == 0)
114476195Sbrian			W(ptr + 2) = W(cxcon + 2);
114576195Sbrian		W(ptr + 8) = (ptr[1] << 6) + W(cxcon + 8);
114676195Sbrian		size = W(cxcon + 10) - (ptr[1] << 10);
114776195Sbrian		if (size <= 0) {
114876195Sbrian			W(ptr + 8) = W(cxcon + 8);
114976195Sbrian			W(ptr + 10) = 0;
115076195Sbrian		} else {
115176195Sbrian			if (size > 1024)
115276195Sbrian				size = 1024;
115376195Sbrian			W(ptr + 10) = size;
115476195Sbrian			bcopy(cxcon + (ptr[1] << 10), ptr + 12, size);
115576195Sbrian		}
115676195Sbrian		W(sc->vmem + 0xd00) = 0;
115776195Sbrian		goto eoi;
115876195Sbrian	}
115976195Sbrian
116076195Sbrian	ehead = sc->gdata->ein;
116176195Sbrian	etail = sc->gdata->eout;
116276195Sbrian	if (ehead == etail) {
116376195Sbrian#ifdef DEBUG
116476195Sbrian		sc->intr_count++;
116576195Sbrian		if (sc->intr_count % 6000 == 0) {
116676195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev,
116776195Sbrian			    "6000 useless polls %x %x\n", ehead, etail));
116876195Sbrian			sc->intr_count = 0;
116976195Sbrian		}
117076195Sbrian#endif
117176195Sbrian		goto eoi;
117276195Sbrian	}
117376195Sbrian	while (ehead != etail) {
117476195Sbrian		event = *(volatile struct event *)(sc->memevent + etail);
117576195Sbrian
117676195Sbrian		etail = (etail + 4) & sc->gdata->imax;
117776195Sbrian
117876195Sbrian		if (event.pnum >= sc->numports) {
117976195Sbrian			log(LOG_ERR, "digi%d: port %d: got event"
118076195Sbrian			    " on nonexisting port\n", sc->res.unit,
118176195Sbrian			    event.pnum);
118276195Sbrian			continue;
118376195Sbrian		}
118476195Sbrian		port = &sc->ports[event.pnum];
118576195Sbrian		bc = port->bc;
118676195Sbrian		tp = port->tp;
118776195Sbrian
1188136200Sphk		if (!(tp->t_state & TS_ISOPEN) && !tp->t_wopeners) {
118976195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev,
119076195Sbrian			    "port %d: event 0x%x on closed port\n",
119176195Sbrian			    event.pnum, event.event));
119276195Sbrian			bc->rout = bc->rin;
119376195Sbrian			bc->idata = 0;
119476195Sbrian			bc->iempty = 0;
119576195Sbrian			bc->ilow = 0;
119676195Sbrian			bc->mint = 0;
119776195Sbrian			continue;
119876195Sbrian		}
119976195Sbrian		if (event.event & ~ALL_IND)
120076195Sbrian			log(LOG_ERR, "digi%d: port%d: ? event 0x%x mstat 0x%x"
120176195Sbrian			    " lstat 0x%x\n", sc->res.unit, event.pnum,
120276195Sbrian			    event.event, event.mstat, event.lstat);
120376195Sbrian
120476195Sbrian		if (event.event & DATA_IND) {
120576195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev, "port %d: DATA_IND\n",
120676195Sbrian			    event.pnum));
120776195Sbrian			wrapmask = port->rxbufsize - 1;
120876195Sbrian			head = bc->rin;
120976195Sbrian			tail = bc->rout;
121076195Sbrian
121176195Sbrian			size = 0;
121276195Sbrian			if (!(tp->t_state & TS_ISOPEN)) {
121376195Sbrian				bc->rout = head;
121476195Sbrian				goto end_of_data;
121576195Sbrian			}
121676195Sbrian			while (head != tail) {
121776195Sbrian				int top;
121876195Sbrian
121976195Sbrian				DLOG(DIGIDB_INT, (sc->dev,
122076195Sbrian				    "port %d: p rx head = %d tail = %d\n",
122176195Sbrian				    event.pnum, head, tail));
122276195Sbrian				top = (head > tail) ? head : wrapmask + 1;
122376195Sbrian				sc->towin(sc, port->rxwin);
122476195Sbrian				size = top - tail;
122576195Sbrian				if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
122676195Sbrian					size = b_to_q((char *)port->rxbuf +
122776195Sbrian					    tail, size, &tp->t_rawq);
122876195Sbrian					tail = top - size;
122976195Sbrian					ttwakeup(tp);
123076195Sbrian				} else for (; tail < top;) {
1231130095Sphk					ttyld_rint(tp, port->rxbuf[tail]);
123276195Sbrian					sc->towin(sc, port->rxwin);
123376195Sbrian					size--;
123476195Sbrian					tail++;
123576195Sbrian					if (tp->t_state & TS_TBLOCK)
123676195Sbrian						break;
123776195Sbrian				}
123876195Sbrian				tail &= wrapmask;
123976195Sbrian				sc->setwin(sc, 0);
124076195Sbrian				bc->rout = tail;
124176195Sbrian				head = bc->rin;
124276195Sbrian				if (size)
124376195Sbrian					break;
124476195Sbrian			}
124576195Sbrian
124676195Sbrian			if (bc->orun) {
124776195Sbrian				CE_RECORD(port, CE_OVERRUN);
124876195Sbrian				log(LOG_ERR, "digi%d: port%d: %s\n",
124976195Sbrian				    sc->res.unit, event.pnum,
125076195Sbrian				    digi_errortxt(CE_OVERRUN));
125176195Sbrian				bc->orun = 0;
125276195Sbrian			}
125376195Sbrianend_of_data:
125476195Sbrian			if (size) {
125576195Sbrian				tp->t_state |= TS_TBLOCK;
125676195Sbrian				port->status |= PAUSE_RX;
125776195Sbrian				DLOG(DIGIDB_RX, (sc->dev, "port %d: pause RX\n",
125876195Sbrian				    event.pnum));
125976195Sbrian			} else {
126076195Sbrian				bc->idata = 1;
126176195Sbrian			}
126276195Sbrian		}
126376195Sbrian
126476195Sbrian		if (event.event & MODEMCHG_IND) {
126576195Sbrian			DLOG(DIGIDB_MODEM, (sc->dev, "port %d: MODEMCHG_IND\n",
126676195Sbrian			    event.pnum));
126776195Sbrian
126878496Sbrian			if ((event.mstat ^ event.lstat) & port->cd) {
126976195Sbrian				sc->hidewin(sc);
1270130095Sphk				ttyld_modem(tp, event.mstat & port->cd);
127176195Sbrian				sc->setwin(sc, 0);
127276195Sbrian				wakeup(TSA_CARR_ON(tp));
127376195Sbrian			}
127476195Sbrian
127576195Sbrian			if (event.mstat & sc->csigs->ri) {
127676195Sbrian				DLOG(DIGIDB_RI, (sc->dev, "port %d: RING\n",
127776195Sbrian				    event.pnum));
127876195Sbrian				if (port->send_ring) {
1279130077Sphk					ttyld_rint(tp, 'R');
1280130077Sphk					ttyld_rint(tp, 'I');
1281130077Sphk					ttyld_rint(tp, 'N');
1282130077Sphk					ttyld_rint(tp, 'G');
1283130077Sphk					ttyld_rint(tp, '\r');
1284130077Sphk					ttyld_rint(tp, '\n');
128576195Sbrian				}
128676195Sbrian			}
128776195Sbrian		}
128876195Sbrian		if (event.event & BREAK_IND) {
128976195Sbrian			DLOG(DIGIDB_MODEM, (sc->dev, "port %d: BREAK_IND\n",
129076195Sbrian			    event.pnum));
1291130077Sphk			ttyld_rint(tp, TTY_BI);
129276195Sbrian		}
129376195Sbrian		if (event.event & (LOWTX_IND | EMPTYTX_IND)) {
129476195Sbrian			DLOG(DIGIDB_IRQ, (sc->dev, "port %d:%s%s\n",
129576195Sbrian			    event.pnum,
129676195Sbrian			    event.event & LOWTX_IND ? " LOWTX" : "",
129776195Sbrian			    event.event & EMPTYTX_IND ?  " EMPTYTX" : ""));
1298130077Sphk			ttyld_start(tp);
129976195Sbrian		}
130076195Sbrian	}
130176195Sbrian	sc->gdata->eout = etail;
130276195Sbrianeoi:
130376195Sbrian	if (sc->window != 0)
130476195Sbrian		sc->towin(sc, 0);
130576195Sbrian	if (window != 0)
130676195Sbrian		sc->towin(sc, window);
130776195Sbrian}
130876195Sbrian
130976195Sbrianstatic void
131076195Sbriandigistart(struct tty *tp)
131176195Sbrian{
131276195Sbrian	struct digi_p *port;
131376195Sbrian	struct digi_softc *sc;
131476195Sbrian	volatile struct board_chan *bc;
131576195Sbrian	int head, tail;
131676195Sbrian	int size, ocount, totcnt = 0;
131776195Sbrian	int s;
131876195Sbrian	int wmask;
131976195Sbrian
1320136200Sphk	port = tp->t_sc;
1321136200Sphk	sc = port->sc;
132276195Sbrian	bc = port->bc;
132376195Sbrian
132476195Sbrian	wmask = port->txbufsize - 1;
132576195Sbrian
132676195Sbrian	s = spltty();
132776195Sbrian	port->lcc = tp->t_outq.c_cc;
132876195Sbrian	sc->setwin(sc, 0);
132976195Sbrian	if (!(tp->t_state & TS_TBLOCK)) {
133076195Sbrian		if (port->status & PAUSE_RX) {
133176195Sbrian			DLOG(DIGIDB_RX, (sc->dev, "port %d: resume RX\n",
1332136200Sphk			    port->pnum));
133376195Sbrian			/*
133476195Sbrian			 * CAREFUL - braces are needed here if the DLOG is
133576195Sbrian			 * optimised out!
133676195Sbrian			 */
133776195Sbrian		}
133876195Sbrian		port->status &= ~PAUSE_RX;
133976195Sbrian		bc->idata = 1;
134076195Sbrian	}
134176195Sbrian	if (!(tp->t_state & TS_TTSTOP) && port->status & PAUSE_TX) {
1342136200Sphk		DLOG(DIGIDB_TX, (sc->dev, "port %d: resume TX\n", port->pnum));
134376195Sbrian		port->status &= ~PAUSE_TX;
134476195Sbrian		fepcmd_w(port, RESUMETX, 0, 10);
134576195Sbrian	}
134676195Sbrian	if (tp->t_outq.c_cc == 0)
134776195Sbrian		tp->t_state &= ~TS_BUSY;
134876195Sbrian	else
134976195Sbrian		tp->t_state |= TS_BUSY;
135076195Sbrian
135176195Sbrian	head = bc->tin;
135276195Sbrian	while (tp->t_outq.c_cc != 0) {
135376195Sbrian		tail = bc->tout;
135476195Sbrian		DLOG(DIGIDB_INT, (sc->dev, "port%d: s tx head = %d tail = %d\n",
1355136200Sphk		    port->pnum, head, tail));
135676195Sbrian
135776195Sbrian		if (head < tail)
135876195Sbrian			size = tail - head - 1;
135976195Sbrian		else {
136076195Sbrian			size = port->txbufsize - head;
136176195Sbrian			if (tail == 0)
136276195Sbrian				size--;
136376195Sbrian		}
136476195Sbrian
136576195Sbrian		if (size == 0)
136676195Sbrian			break;
136776195Sbrian		sc->towin(sc, port->txwin);
136876195Sbrian		ocount = q_to_b(&tp->t_outq, port->txbuf + head, size);
136976195Sbrian		totcnt += ocount;
137076195Sbrian		head += ocount;
137176195Sbrian		head &= wmask;
137276195Sbrian		sc->setwin(sc, 0);
137376195Sbrian		bc->tin = head;
137476195Sbrian		bc->iempty = 1;
137576195Sbrian		bc->ilow = 1;
137676195Sbrian	}
137776195Sbrian	port->lostcc = tp->t_outq.c_cc;
137876195Sbrian	tail = bc->tout;
137976195Sbrian	if (head < tail)
138076195Sbrian		size = port->txbufsize - tail + head;
138176195Sbrian	else
138276195Sbrian		size = head - tail;
138376195Sbrian
138476195Sbrian	port->lbuf = size;
1385136200Sphk	DLOG(DIGIDB_INT, (sc->dev, "port%d: s total cnt = %d\n", port->pnum, totcnt));
138676195Sbrian	ttwwakeup(tp);
138776195Sbrian	splx(s);
138876195Sbrian}
138976195Sbrian
139076195Sbrianstatic void
139176195Sbriandigistop(struct tty *tp, int rw)
139276195Sbrian{
139376195Sbrian	struct digi_softc *sc;
139476195Sbrian	struct digi_p *port;
139576195Sbrian
1396136200Sphk	port = tp->t_sc;
1397136200Sphk	sc = port->sc;
139876195Sbrian
1399136200Sphk	DLOG(DIGIDB_TX, (sc->dev, "port %d: pause TX\n", port->pnum));
140076195Sbrian	port->status |= PAUSE_TX;
140176195Sbrian	fepcmd_w(port, PAUSETX, 0, 10);
140276195Sbrian}
140376195Sbrian
140476195Sbrianstatic void
140576195Sbrianfepcmd(struct digi_p *port, int cmd, int op1, int ncmds)
140676195Sbrian{
140776195Sbrian	u_char *mem;
140876195Sbrian	unsigned tail, head;
140976195Sbrian	int count, n;
141076195Sbrian
141176195Sbrian	mem = port->sc->memcmd;
141276195Sbrian
141376195Sbrian	port->sc->setwin(port->sc, 0);
141476195Sbrian
141576195Sbrian	head = port->sc->gdata->cin;
141676195Sbrian	mem[head + 0] = cmd;
141776195Sbrian	mem[head + 1] = port->pnum;
1418118607Sjhb	*(u_short *)(mem + head + 2) = op1;
141976195Sbrian
142076195Sbrian	head = (head + 4) & port->sc->gdata->cmax;
142176195Sbrian	port->sc->gdata->cin = head;
142276195Sbrian
142376195Sbrian	for (count = FEPTIMEOUT; count > 0; count--) {
142476195Sbrian		head = port->sc->gdata->cin;
142576195Sbrian		tail = port->sc->gdata->cout;
142676195Sbrian		n = (head - tail) & port->sc->gdata->cmax;
142776195Sbrian
142876195Sbrian		if (n <= ncmds * sizeof(short) * 4)
142976195Sbrian			break;
143076195Sbrian	}
143176195Sbrian	if (count == 0)
143276195Sbrian		log(LOG_ERR, "digi%d: port%d: timeout on FEP command\n",
143376195Sbrian		    port->sc->res.unit, port->pnum);
143476195Sbrian}
143576195Sbrian
143676195Sbrianconst char *
143776195Sbriandigi_errortxt(int id)
143876195Sbrian{
143976195Sbrian	static const char *error_desc[] = {
144076195Sbrian		"silo overflow",
144176195Sbrian		"interrupt-level buffer overflow",
144276195Sbrian		"tty-level buffer overflow",
144376195Sbrian	};
144476195Sbrian
144576195Sbrian	KASSERT(id >= 0 && id < sizeof(error_desc) / sizeof(error_desc[0]),
144676195Sbrian	    ("Unexpected digi error id %d\n", id));
144776195Sbrian
144876195Sbrian	return (error_desc[id]);
144976195Sbrian}
145076195Sbrian
145176195Sbrianint
145276195Sbriandigi_attach(struct digi_softc *sc)
145376195Sbrian{
1454136200Sphk	sc->res.ctldev = make_dev(&digi_csw,
1455136200Sphk	    sc->res.unit << 16, UID_ROOT, GID_WHEEL,
145676195Sbrian	    0600, "digi%r.ctl", sc->res.unit);
1457136200Sphk	sc->res.ctldev->si_drv1 = sc;
145876195Sbrian
145976195Sbrian	digi_loadmoduledata(sc);
146076195Sbrian	digi_init(sc);
146176195Sbrian	digi_freemoduledata(sc);
146276195Sbrian
146376195Sbrian	return (0);
146476195Sbrian}
146576195Sbrian
146676195Sbrianstatic int
146776195Sbriandigi_inuse(struct digi_softc *sc)
146876195Sbrian{
146976195Sbrian	int i;
1470136200Sphk	struct digi_p *port;
147176195Sbrian
1472136200Sphk	port = &sc->ports[0];
1473136200Sphk	for (i = 0; i < sc->numports; i++, port++)
1474136200Sphk		if (port->tp->t_state & TS_ISOPEN) {
147576195Sbrian			DLOG(DIGIDB_INIT, (sc->dev, "port%d: busy\n", i));
147676195Sbrian			return (1);
1477136200Sphk		} else if (port->tp->t_wopeners || port->opencnt) {
147876195Sbrian			DLOG(DIGIDB_INIT, (sc->dev, "port%d: blocked in open\n",
147976195Sbrian			    i));
148076195Sbrian			return (1);
148176195Sbrian		}
148276195Sbrian	return (0);
148376195Sbrian}
148476195Sbrian
148576195Sbrianstatic void
148676195Sbriandigi_free_state(struct digi_softc *sc)
148776195Sbrian{
1488136200Sphk	int i;
148976195Sbrian
149076195Sbrian	/* Blow it all away */
149176195Sbrian
149276195Sbrian	for (i = 0; i < sc->numports; i++)
1493136200Sphk		ttygone(sc->ports[i].tp);
149476195Sbrian
1495136200Sphk	/* XXX: this might be better done as a ttypurge method */
149676195Sbrian	untimeout(digi_poll, sc, sc->callout);
149776195Sbrian	callout_handle_init(&sc->callout);
149876195Sbrian	untimeout(digi_int_test, sc, sc->inttest);
149976195Sbrian	callout_handle_init(&sc->inttest);
150076195Sbrian
1501136200Sphk	for (i = 0; i < sc->numports; i++)
1502136200Sphk		ttyfree(sc->ports[i].tp);
1503136200Sphk
150476195Sbrian	bus_teardown_intr(sc->dev, sc->res.irq, sc->res.irqHandler);
150576195Sbrian#ifdef DIGI_INTERRUPT
150676195Sbrian	if (sc->res.irq != NULL) {
150776195Sbrian		bus_release_resource(dev, SYS_RES_IRQ, sc->res.irqrid,
150876195Sbrian		    sc->res.irq);
150976195Sbrian		sc->res.irq = NULL;
151076195Sbrian	}
151176195Sbrian#endif
151276195Sbrian	if (sc->numports) {
151376195Sbrian		KASSERT(sc->ports, ("digi%d: Lost my ports ?", sc->res.unit));
151477004Sbrian		free(sc->ports, M_TTYS);
151576195Sbrian		sc->ports = NULL;
151676195Sbrian		sc->numports = 0;
151776195Sbrian	}
151876195Sbrian
151976195Sbrian	sc->status = DIGI_STATUS_NOTINIT;
152076195Sbrian}
152176195Sbrian
152276195Sbrianint
152376195Sbriandigi_detach(device_t dev)
152476195Sbrian{
152576195Sbrian	struct digi_softc *sc = device_get_softc(dev);
152676195Sbrian
152776195Sbrian	DLOG(DIGIDB_INIT, (sc->dev, "detaching\n"));
152876195Sbrian
152976195Sbrian	/* If we're INIT'd, numports must be 0 */
153076195Sbrian	KASSERT(sc->numports == 0 || sc->status != DIGI_STATUS_NOTINIT,
153176195Sbrian	    ("digi%d: numports(%d) & status(%d) are out of sync",
153276195Sbrian	    sc->res.unit, sc->numports, (int)sc->status));
153376195Sbrian
153476195Sbrian	if (digi_inuse(sc))
153576195Sbrian		return (EBUSY);
153676195Sbrian
153776195Sbrian	digi_free_state(sc);
153876195Sbrian
1539120461Sphk	destroy_dev(sc->res.ctldev);
154076195Sbrian
154176195Sbrian	if (sc->res.mem != NULL) {
154276195Sbrian		bus_release_resource(dev, SYS_RES_MEMORY, sc->res.mrid,
154376195Sbrian		    sc->res.mem);
154476195Sbrian		sc->res.mem = NULL;
154576195Sbrian	}
154676195Sbrian	if (sc->res.io != NULL) {
154776195Sbrian		bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
154876195Sbrian		    sc->res.io);
154976195Sbrian		sc->res.io = NULL;
155076195Sbrian	}
155176195Sbrian
155276195Sbrian	return (0);
155376195Sbrian}
155476195Sbrian
155576195Sbrianint
155676195Sbriandigi_shutdown(device_t dev)
155776195Sbrian{
155876195Sbrian	return (0);
155976195Sbrian}
156094320Sbrian
156194320SbrianMODULE_VERSION(digi, 1);
1562