kern/158086: [patch] Update digi(4) to work with TTYng
Peter Jeremy
Peter.Jeremy at alcatel-lucent.com
Tue Jun 21 04:50:08 UTC 2011
>Number: 158086
>Category: kern
>Synopsis: [patch] Update digi(4) to work with TTYng
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Tue Jun 21 04:50:08 UTC 2011
>Closed-Date:
>Last-Modified:
>Originator: Peter Jeremy
>Release: FreeBSD 9.0-CURRENT amd64
>Organization:
Alcatel-Lucent Australia Limited
>Environment:
System: FreeBSD C2B0004103.au.alcatel-lucent.com 9.0-CURRENT FreeBSD 9.0-CURRENT #0: Tue Jun 21 12:53:47 EST 2011 root at C2B0004103.au.alcatel-lucent.com:/var/obj/usr/src/sys/pjdesk amd64
>Description:
The digi(4) driver needs updating to work with TTYng. The patch
below does this. The code has also been cross-checked against
the Linux driver (dgap-1.3) available from Digi.
Notes:
- kern/152254 must be committed before this PR
- Only PCI DigiBoards are supported. Additional locking would
be required to support ISA cards and I do not have access to
any for testing.
- The code still handles firmware loading/unloading itself
rather than using firmware(9) because each card needs two
or three firmware images and the current approach allows
one kld per card, rather than one kld per image.
- Interrupt or polling mode can be selected at compile time.
- Giant is no longer used.
- The softc has been rototilled to reduce the amount of padding
- If compiled with debugging, a sysctl is available to control
verbosity.
- This patch still includes debugging code.
>How-To-Repeat:
Try to compile digi(4) on 8.0 or later.
>Fix:
Index: sys/conf/files
===================================================================
RCS file: /usr/ncvs/src/sys/conf/files,v
retrieving revision 1.1606
diff -u -r1.1606 files
--- sys/conf/files 10 Jun 2011 22:38:31 -0000 1.1606
+++ sys/conf/files 20 Jun 2011 22:43:01 -0000
@@ -942,7 +942,7 @@
dev/digi/Xem.c optional digi_Xem
dev/digi/Xr.c optional digi_Xr
dev/digi/digi.c optional digi
-dev/digi/digi_isa.c optional digi isa
+#dev/digi/digi_isa.c optional digi isa
dev/digi/digi_pci.c optional digi pci
dev/dpt/dpt_eisa.c optional dpt eisa
dev/dpt/dpt_pci.c optional dpt pci
Index: sys/dev/digi/CX.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/CX.c,v
retrieving revision 1.3
diff -u -r1.3 CX.c
--- sys/dev/digi/CX.c 24 Aug 2003 17:46:03 -0000 1.3
+++ sys/dev/digi/CX.c 20 Jun 2011 22:43:50 -0000
@@ -44,5 +44,18 @@
{ NULL, 0 }
};
+static int
+digi_fw_load(module_t mod, int cmd, void *arg)
+{
+
+ switch (cmd) {
+ case MOD_LOAD:
+ case MOD_UNLOAD:
+ return (0);
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
MODULE_VERSION(digi_CX, 1);
-DEV_MODULE(digi_CX, 0, 0);
+DEV_MODULE(digi_CX, digi_fw_load, 0);
Index: sys/dev/digi/CX_PCI.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/CX_PCI.c,v
retrieving revision 1.3
diff -u -r1.3 CX_PCI.c
--- sys/dev/digi/CX_PCI.c 24 Aug 2003 17:46:03 -0000 1.3
+++ sys/dev/digi/CX_PCI.c 20 Jun 2011 22:43:50 -0000
@@ -44,5 +44,18 @@
{ NULL, 0 }
};
+static int
+digi_fw_load(module_t mod, int cmd, void *arg)
+{
+
+ switch (cmd) {
+ case MOD_LOAD:
+ case MOD_UNLOAD:
+ return (0);
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
MODULE_VERSION(digi_CX_PCI, 1);
-DEV_MODULE(digi_CX_PCI, 0, 0);
+DEV_MODULE(digi_CX_PCI, digi_fw_load, 0);
Index: sys/dev/digi/EPCX.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/EPCX.c,v
retrieving revision 1.3
diff -u -r1.3 EPCX.c
--- sys/dev/digi/EPCX.c 24 Aug 2003 17:46:03 -0000 1.3
+++ sys/dev/digi/EPCX.c 20 Jun 2011 22:43:50 -0000
@@ -44,5 +44,18 @@
{ NULL, 0 }
};
+static int
+digi_fw_load(module_t mod, int cmd, void *arg)
+{
+
+ switch (cmd) {
+ case MOD_LOAD:
+ case MOD_UNLOAD:
+ return (0);
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
MODULE_VERSION(digi_EPCX, 1);
-DEV_MODULE(digi_EPCX, 0, 0);
+DEV_MODULE(digi_EPCX, digi_fw_load, 0);
Index: sys/dev/digi/EPCX_PCI.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/EPCX_PCI.c,v
retrieving revision 1.3
diff -u -r1.3 EPCX_PCI.c
--- sys/dev/digi/EPCX_PCI.c 24 Aug 2003 17:46:03 -0000 1.3
+++ sys/dev/digi/EPCX_PCI.c 20 Jun 2011 22:43:50 -0000
@@ -44,5 +44,18 @@
{ NULL, 0 }
};
+static int
+digi_fw_load(module_t mod, int cmd, void *arg)
+{
+
+ switch (cmd) {
+ case MOD_LOAD:
+ case MOD_UNLOAD:
+ return (0);
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
MODULE_VERSION(digi_EPCX_PCI, 1);
-DEV_MODULE(digi_EPCX_PCI, 0, 0);
+DEV_MODULE(digi_EPCX_PCI, digi_fw_load, 0);
Index: sys/dev/digi/Xe.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/Xe.c,v
retrieving revision 1.3
diff -u -r1.3 Xe.c
--- sys/dev/digi/Xe.c 24 Aug 2003 17:46:03 -0000 1.3
+++ sys/dev/digi/Xe.c 20 Jun 2011 22:43:50 -0000
@@ -44,5 +44,18 @@
{ NULL, 0 }
};
+static int
+digi_fw_load(module_t mod, int cmd, void *arg)
+{
+
+ switch (cmd) {
+ case MOD_LOAD:
+ case MOD_UNLOAD:
+ return (0);
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
MODULE_VERSION(digi_Xe, 1);
-DEV_MODULE(digi_Xe, 0, 0);
+DEV_MODULE(digi_Xe, digi_fw_load, 0);
Index: sys/dev/digi/Xem.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/Xem.c,v
retrieving revision 1.3
diff -u -r1.3 Xem.c
--- sys/dev/digi/Xem.c 24 Aug 2003 17:46:03 -0000 1.3
+++ sys/dev/digi/Xem.c 20 Jun 2011 22:43:50 -0000
@@ -44,5 +44,18 @@
{ NULL, 0 }
};
+static int
+digi_fw_load(module_t mod, int cmd, void *arg)
+{
+
+ switch (cmd) {
+ case MOD_LOAD:
+ case MOD_UNLOAD:
+ return (0);
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
MODULE_VERSION(digi_Xem, 1);
-DEV_MODULE(digi_Xem, 0, 0);
+DEV_MODULE(digi_Xem, digi_fw_load, 0);
Index: sys/dev/digi/Xr.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/Xr.c,v
retrieving revision 1.3
diff -u -r1.3 Xr.c
--- sys/dev/digi/Xr.c 24 Aug 2003 17:46:03 -0000 1.3
+++ sys/dev/digi/Xr.c 20 Jun 2011 22:43:50 -0000
@@ -44,5 +44,18 @@
{ NULL, 0 }
};
+static int
+digi_fw_load(module_t mod, int cmd, void *arg)
+{
+
+ switch (cmd) {
+ case MOD_LOAD:
+ case MOD_UNLOAD:
+ return (0);
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
MODULE_VERSION(digi_Xr, 1);
-DEV_MODULE(digi_Xr, 0, 0);
+DEV_MODULE(digi_Xr, digi_fw_load, 0);
Index: sys/dev/digi/digi.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/digi.c,v
retrieving revision 1.63
diff -u -r1.63 digi.c
--- sys/dev/digi/digi.c 27 Sep 2006 19:56:58 -0000 1.63
+++ sys/dev/digi/digi.c 20 Jun 2011 22:43:51 -0000
@@ -42,12 +42,15 @@
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/linker.h>
+#include <sys/lock.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/module.h>
-#include <sys/tty.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
#include <sys/syslog.h>
+#include <sys/tty.h>
#include <sys/fcntl.h>
#include <sys/serial.h>
#include <sys/bus.h>
@@ -59,25 +62,39 @@
#include <dev/digi/digi_mod.h>
#include <dev/digi/digi_pci.h>
-static t_open_t digiopen;
+static tsw_open_t digiopen;
+static tsw_close_t digiclose;
+static tsw_outwakeup_t digioutwakeup;
+static tsw_inwakeup_t digiinwakeup;
+static tsw_ioctl_t digiioctl;
+static tsw_param_t digiparam;
+static tsw_modem_t digimodem;
+static tsw_pktnotify_t diginotify;
+static tsw_free_t digifree;
+static tsw_cioctl_t digisioctl;
+
+static struct ttydevsw digi_class = {
+ .tsw_flags = TF_INITLOCK | TF_CALLOUT,
+ .tsw_open = digiopen,
+ .tsw_close = digiclose,
+ .tsw_outwakeup = digioutwakeup,
+ .tsw_inwakeup = digiinwakeup,
+ .tsw_ioctl = digiioctl,
+ .tsw_param = digiparam,
+ .tsw_modem = digimodem,
+ .tsw_pktnotify = diginotify,
+ .tsw_free = digifree,
+ .tsw_cioctl = digisioctl,
+};
+
static d_open_t digicopen;
static d_close_t digicclose;
-static t_ioctl_t digiioctl;
-static d_ioctl_t digisioctl;
static d_ioctl_t digicioctl;
-static void digistop(struct tty *tp, int rw);
-static void digibreak(struct tty *tp, int brk);
-static int digimodem(struct tty *tp, int sigon, int sigoff);
static void digi_poll(void *ptr);
-static void digi_freemoduledata(struct digi_softc *);
static void fepcmd(struct digi_p *port, int cmd, int op, int ncmds);
-static void digistart(struct tty *tp);
-static int digiparam(struct tty *tp, struct termios *t);
-static void digiclose(struct tty *tp);
-static void digi_intr(void *);
static int digi_init(struct digi_softc *_sc);
-static int digi_loadmoduledata(struct digi_softc *);
+static int digi_loadmoduledata(struct digi_softc *, struct digi_mod **, linker_file_t *);
static int digi_inuse(struct digi_softc *);
static void digi_free_state(struct digi_softc *);
@@ -85,18 +102,25 @@
fepcmd(port, cmd, (op2 << 8) | op1, ncmds)
#define fepcmd_w fepcmd
-struct con_bios {
- struct con_bios *next;
- u_char *bios;
- size_t size;
-};
-
-static struct con_bios *con_bios_list;
devclass_t digi_devclass;
static char driver_name[] = "digi";
-unsigned digi_debug = 0;
-static struct speedtab digispeedtab[] = {
+#ifdef DEBUG
+unsigned long digi_debug = 1;
+
+SYSCTL_ULONG(_debug, OID_AUTO, digi_debug, CTLFLAG_RW, &digi_debug, 0,
+ "digi(4) debug flags");
+TUNABLE_ULONG("debug.digi_debug", &digi_debug);
+#endif
+
+/* digi(4) uses the old SysV-style Bx codes to control line speed.
+ * (The Linux driver suggests some Digi variants can handle direct
+ * baud-rate programming but that would be a more invasive change)
+ */
+static const struct {
+ int sp_speed; /* Actual line rate in BPS */
+ int sp_code; /* Code for Digi */
+} digispeedtab[] = {
{ 0, 0}, /* old (sysV-like) Bx codes */
{ 50, 1},
{ 75, 2},
@@ -134,7 +158,7 @@
.d_close = digicclose,
.d_ioctl = digicioctl,
.d_name = driver_name,
- .d_flags = D_TTY | D_NEEDGIANT,
+ .d_flags = D_TTY,
};
static void
@@ -143,9 +167,12 @@
struct digi_softc *sc;
sc = (struct digi_softc *)ptr;
- callout_handle_init(&sc->callout);
+ mtx_assert(&sc->dg_mutex, MA_OWNED);
+
+ if (callout_pending(&sc->callout) || callout_active(&sc->callout) == 0)
+ return;
digi_intr(sc);
- sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1);
+ callout_schedule(&sc->callout, (hz >= 200) ? hz / 100 : 1);
}
static void
@@ -153,36 +180,22 @@
{
struct digi_softc *sc = v;
- callout_handle_init(&sc->inttest);
+ mtx_assert(&sc->dg_mutex, MA_OWNED);
+ if (callout_pending(&sc->callout) || !callout_active(&sc->callout))
+ return;
#ifdef DIGI_INTERRUPT
- if (sc->intr_timestamp.tv_sec || sc->intr_timestamp.tv_usec) {
+ if (sc->interrupt_seen) {
+ callout_deactivate(&sc->callout);
/* interrupt OK! */
return;
}
- log(LOG_ERR, "digi%d: Interrupt didn't work, use polled mode\n", unit);
+ log(LOG_ERR, "digi%d: Interrupt didn't work, use polled mode\n", sc->res.unit);
#endif
- sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1);
-}
-
-static void
-digi_freemoduledata(struct digi_softc *sc)
-{
- if (sc->fep.data != NULL) {
- free(sc->fep.data, M_TTYS);
- sc->fep.data = NULL;
- }
- if (sc->link.data != NULL) {
- free(sc->link.data, M_TTYS);
- sc->link.data = NULL;
- }
- if (sc->bios.data != NULL) {
- free(sc->bios.data, M_TTYS);
- sc->bios.data = NULL;
- }
+ callout_reset(&sc->callout, (hz >= 200) ? hz / 100 : 1, digi_poll, sc);
}
static int
-digi_bcopy(const void *vfrom, void *vto, size_t sz)
+digi_bcopy(const void *vfrom, void volatile *vto, size_t sz)
{
volatile const char *from = (volatile const char *)vfrom;
volatile char *to = (volatile char *)vto;
@@ -204,6 +217,8 @@
{
if (cold)
DELAY(timo * 1000000 / hz);
+ else if (mtx_owned(&sc->dg_mutex))
+ mtx_sleep(sc, &sc->dg_mutex, PUSER | PCATCH, txt, timo);
else
tsleep(sc, PUSER | PCATCH, txt, timo);
}
@@ -212,62 +227,78 @@
digi_init(struct digi_softc *sc)
{
int i, cnt, resp;
- u_char *ptr;
+ int error;
+ u_char volatile *ptr;
+ u_char *cptr;
int lowwater;
struct digi_p *port;
volatile struct board_chan *bc;
struct tty *tp;
+ struct digi_mod *dm;
+ linker_file_t lf;
+ enum digi_board_status old_state;
+ mtx_assert(&sc->dg_mutex, MA_OWNED);
ptr = NULL;
- if (sc->status == DIGI_STATUS_DISABLED) {
- log(LOG_ERR, "digi%d: Cannot init a disabled card\n",
+ if (sc->status == DIGI_STATUS_DISABLED ||
+ sc->status == DIGI_STATUS_STARTING) {
+ log(LOG_ERR, "digi%d: Card [de]initialisation in progress\n",
sc->res.unit);
return (EIO);
}
- if (sc->bios.data == NULL) {
- log(LOG_ERR, "digi%d: Cannot init without BIOS\n",
- sc->res.unit);
- return (EIO);
- }
-#if 0
- if (sc->link.data == NULL && sc->model >= PCCX) {
- log(LOG_ERR, "digi%d: Cannot init without link info\n",
- sc->res.unit);
- return (EIO);
- }
-#endif
- if (sc->fep.data == NULL) {
- log(LOG_ERR, "digi%d: Cannot init without fep code\n",
- sc->res.unit);
- return (EIO);
+ old_state = sc->status;
+ sc->status = DIGI_STATUS_STARTING;
+
+ /* Status now protects against unwanted re-entrancy
+ * release mutex to load firmware */
+ mtx_unlock(&sc->dg_mutex);
+
+ /* Load required firmware image */
+ error = digi_loadmoduledata(sc, &dm, &lf);
+ if (error) {
+ mtx_lock(&sc->dg_mutex);
+ sc->status = old_state;
+ return (error);
}
- sc->status = DIGI_STATUS_NOTINIT;
if (sc->numports) {
+ mtx_lock(&sc->dg_mutex);
/*
* We're re-initialising - maybe because someone's attached
* another port module. For now, we just re-initialise
* everything.
*/
- if (digi_inuse(sc))
+ if (digi_inuse(sc)) {
+ linker_release_module(NULL, NULL, lf);
+ sc->status = old_state;
return (EBUSY);
+ }
digi_free_state(sc);
+ sc->status = DIGI_STATUS_STARTING;
+ mtx_unlock(&sc->dg_mutex);
+
}
- ptr = sc->setwin(sc, MISCGLOBAL);
+ ptr = digi_setwin(sc, MISCGLOBAL);
for (i = 0; i < 16; i += 2)
vW(ptr + i) = 0;
+ error = EIO; /* default error if initialisation fails */
+
switch (sc->model) {
+ default:
+ goto init_failed;
+
+#ifdef DIGI_ISA
case PCXEVE:
outb(sc->wport, 0xff); /* window 7 */
ptr = sc->vmem + (BIOSCODE & 0x1fff);
- if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) {
+ if (!digi_bcopy(dm->dm_bios.data, ptr, dm->dm_bios.size)) {
device_printf(sc->dev, "BIOS upload failed\n");
- return (EIO);
+ goto init_failed;
}
outb(sc->port, FEPCLR);
@@ -275,136 +306,143 @@
case PCXE:
case PCXI:
+#endif
+
case PCCX:
- ptr = sc->setwin(sc, BIOSCODE + ((0xf000 - sc->mem_seg) << 4));
- if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) {
+ ptr = digi_setwin(sc, BIOSCODE + ((0xf000 - MEM_SEG(sc)) << 4));
+ if (!digi_bcopy(dm->dm_bios.data, ptr, dm->dm_bios.size)) {
device_printf(sc->dev, "BIOS upload failed\n");
- return (EIO);
+ goto init_failed;
}
break;
case PCXEM:
case PCIEPCX:
case PCIXR:
- if (sc->pcibus)
- PCIPORT = FEPRST;
- else
- outb(sc->port, FEPRST | FEPMEM);
+ digi_outportmem(sc, FEPRST);
- for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) &
- FEPMASK) != FEPRST; i++) {
+ for (i = 0; (digi_inport(sc) & FEPMASK) != FEPRST; i++) {
if (i > hz) {
log(LOG_ERR, "digi%d: %s init reset failed\n",
sc->res.unit, sc->name);
- return (EIO);
+ goto init_failed;
}
digi_delay(sc, "digiinit0", 5);
}
- DLOG(DIGIDB_INIT, (sc->dev, "Got init reset after %d us\n", i));
+ DLOG(DIGIDB_INIT, (sc->dev,
+ "Got init reset after %d iterations\n", i));
+
+ /* Clear POST area */
+ ptr = digi_setwin(sc, MISCGLOBAL);
+ for (i = 0; i < 16; i++)
+ *(uint8_t volatile *)(ptr + i) = 0;
/* Now upload the BIOS */
- cnt = (sc->bios.size < sc->win_size - BIOSOFFSET) ?
- sc->bios.size : sc->win_size - BIOSOFFSET;
+ cnt = (dm->dm_bios.size < sc->win_size - BIOSOFFSET) ?
+ dm->dm_bios.size : sc->win_size - BIOSOFFSET;
- ptr = sc->setwin(sc, BIOSOFFSET);
- if (!digi_bcopy(sc->bios.data, ptr, cnt)) {
+ ptr = digi_setwin(sc, BIOSOFFSET);
+ if (!digi_bcopy(dm->dm_bios.data, ptr, cnt)) {
device_printf(sc->dev, "BIOS upload (1) failed\n");
- return (EIO);
+ goto init_failed;
}
- if (cnt != sc->bios.size) {
+ if (cnt != dm->dm_bios.size) {
/* and the second part */
- ptr = sc->setwin(sc, sc->win_size);
- if (!digi_bcopy(sc->bios.data + cnt, ptr,
- sc->bios.size - cnt)) {
+ ptr = digi_setwin(sc, sc->win_size);
+ if (!digi_bcopy(dm->dm_bios.data + cnt, ptr,
+ dm->dm_bios.size - cnt)) {
device_printf(sc->dev, "BIOS upload failed\n");
- return (EIO);
+ goto init_failed;
}
}
- ptr = sc->setwin(sc, 0);
- vW(ptr + 0) = 0x0401;
- vW(ptr + 2) = 0x0bf0;
- vW(ptr + 4) = 0x0000;
- vW(ptr + 6) = 0x0000;
+ ptr = digi_setwin(sc, 0);
+ vD(ptr + 0) = 0x0bf00401;
+ vD(ptr + 4) = 0x00000000;
break;
}
DLOG(DIGIDB_INIT, (sc->dev, "BIOS uploaded\n"));
- ptr = sc->setwin(sc, MISCGLOBAL);
- W(ptr) = 0;
+ ptr = digi_setwin(sc, MISCGLOBAL);
+ vW(ptr) = 0;
- if (sc->pcibus) {
- PCIPORT = FEPCLR;
- resp = FEPRST;
- } else if (sc->model == PCXEVE) {
- outb(sc->port, FEPCLR);
+ digi_outportmem(sc, FEPRST);
+#ifdef DIGI_ISA
+ if (sc->pcibus || sc->model == PCXEVE) {
+ digi_outport(sc, FEPCLR);
resp = FEPRST;
} else {
outb(sc->port, FEPCLR | FEPMEM);
resp = FEPRST | FEPMEM;
}
-
- for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) & FEPMASK)
- == resp; i++) {
+#else
+ digi_outport(sc, FEPCLR);
+ resp = FEPRST;
+#endif
+ for (i = 0; (digi_inport(sc) & FEPMASK) == resp; i++) {
if (i > hz) {
log(LOG_ERR, "digi%d: BIOS start failed\n",
sc->res.unit);
- return (EIO);
+ goto init_failed;
}
digi_delay(sc, "digibios0", 5);
}
- DLOG(DIGIDB_INIT, (sc->dev, "BIOS started after %d us\n", i));
+ DLOG(DIGIDB_INIT, (sc->dev, "BIOS started after %d iterations\n", i));
for (i = 0; vW(ptr) != *(u_short *)"GD"; i++) {
if (i > 5*hz) {
log(LOG_ERR, "digi%d: BIOS boot failed "
"(0x%02x != 0x%02x)\n",
sc->res.unit, vW(ptr), *(u_short *)"GD");
- return (EIO);
+ goto init_failed;
}
digi_delay(sc, "digibios1", 5);
}
DLOG(DIGIDB_INIT, (sc->dev, "BIOS booted after %d iterations\n", i));
- if (sc->link.data != NULL) {
+ if (dm->dm_link.data != NULL) {
DLOG(DIGIDB_INIT, (sc->dev, "Loading link data\n"));
- ptr = sc->setwin(sc, 0xcd0);
- digi_bcopy(sc->link.data, ptr, 21); /* XXX 21 ? */
+ ptr = digi_setwin(sc, 0xcd0);
+ digi_bcopy(dm->dm_link.data, ptr, 21); /* XXX 21 ? */
}
/* load FEP/OS */
switch (sc->model) {
+ default:
+ goto init_failed;
+
+#ifdef DIGI_ISA
case PCXE:
case PCXEVE:
case PCXI:
- ptr = sc->setwin(sc, sc->model == PCXI ? 0x2000 : 0x0);
- digi_bcopy(sc->fep.data, ptr, sc->fep.size);
+ ptr = digi_setwin(sc, sc->model == PCXI ? 0x2000 : 0x0);
+ digi_bcopy(dm->dm_fep.data, ptr, dm->dm_fep.size);
/* A BIOS request to move our data to 0x2000 */
- ptr = sc->setwin(sc, MBOX);
+ ptr = digi_setwin(sc, MBOX);
vW(ptr + 0) = 2;
- vW(ptr + 2) = sc->mem_seg + FEPCODESEG;
+ vW(ptr + 2) = MEM_SEG(sc) + FEPCODESEG;
vW(ptr + 4) = 0;
vW(ptr + 6) = FEPCODESEG;
vW(ptr + 8) = 0;
- vW(ptr + 10) = sc->fep.size;
+ vW(ptr + 10) = dm->dm_fep.size;
/* Run the BIOS request */
outb(sc->port, FEPREQ | FEPMEM);
outb(sc->port, FEPCLR | FEPMEM);
- for (i = 0; W(ptr); i++) {
+ for (i = 0; vW(ptr); i++) {
if (i > hz) {
log(LOG_ERR, "digi%d: FEP/OS move failed\n",
sc->res.unit);
- sc->hidewin(sc);
- return (EIO);
+ digi_hidewin(sc);
+ goto init_failed;
}
digi_delay(sc, "digifep0", 5);
}
@@ -412,11 +450,11 @@
(sc->dev, "FEP/OS moved after %d iterations\n", i));
/* Clear the confirm word */
- ptr = sc->setwin(sc, FEPSTAT);
+ ptr = digi_setwin(sc, FEPSTAT);
vW(ptr + 0) = 0;
/* A BIOS request to execute the FEP/OS */
- ptr = sc->setwin(sc, MBOX);
+ ptr = digi_setwin(sc, MBOX);
vW(ptr + 0) = 0x01;
vW(ptr + 2) = FEPCODESEG;
vW(ptr + 4) = 0x04;
@@ -425,71 +463,76 @@
outb(sc->port, FEPREQ);
outb(sc->port, FEPCLR);
- ptr = sc->setwin(sc, FEPSTAT);
+ ptr = digi_setwin(sc, FEPSTAT);
break;
-
+#endif
case PCXEM:
case PCIEPCX:
case PCIXR:
DLOG(DIGIDB_INIT, (sc->dev, "Loading FEP/OS\n"));
- cnt = (sc->fep.size < sc->win_size - BIOSOFFSET) ?
- sc->fep.size : sc->win_size - BIOSOFFSET;
+ cnt = (dm->dm_fep.size < sc->win_size - BIOSOFFSET) ?
+ dm->dm_fep.size : sc->win_size - BIOSOFFSET;
- ptr = sc->setwin(sc, BIOSOFFSET);
- digi_bcopy(sc->fep.data, ptr, cnt);
+ ptr = digi_setwin(sc, BIOSOFFSET);
+ digi_bcopy(dm->dm_fep.data, ptr, cnt);
- if (cnt != sc->fep.size) {
- ptr = sc->setwin(sc, BIOSOFFSET + cnt);
- digi_bcopy(sc->fep.data + cnt, ptr,
- sc->fep.size - cnt);
+ if (cnt != dm->dm_fep.size) {
+ ptr = digi_setwin(sc, BIOSOFFSET + cnt);
+ digi_bcopy(dm->dm_fep.data + cnt, ptr,
+ dm->dm_fep.size - cnt);
}
DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS loaded\n"));
- ptr = sc->setwin(sc, 0xc30);
- W(ptr + 4) = 0x1004;
- W(ptr + 6) = 0xbfc0;
- W(ptr + 0) = 0x03;
- W(ptr + 2) = 0x00;
+ ptr = digi_setwin(sc, 0xc30);
+ vD(ptr + 4) = 0xbfc01004;
+ vD(ptr + 0) = 0x00000003;
/* Clear the confirm word */
- ptr = sc->setwin(sc, FEPSTAT);
- W(ptr + 0) = 0;
-
+ ptr = digi_setwin(sc, FEPSTAT);
+ vW(ptr + 0) = 0;
+#ifdef DIGI_ISA
if (sc->port)
outb(sc->port, 0); /* XXX necessary ? */
-
+#endif
break;
+#ifdef NEEDS_PORTING
case PCCX:
- ptr = sc->setwin(sc, 0xd000);
- digi_bcopy(sc->fep.data, ptr, sc->fep.size);
+ ptr = digi_setwin(sc, 0xd000);
+ digi_bcopy(dm->dm_fep.data, ptr, dm->dm_fep.size);
/* A BIOS request to execute the FEP/OS */
- ptr = sc->setwin(sc, 0xc40);
- W(ptr + 0) = 1;
- W(ptr + 2) = FEPCODE >> 4;
- W(ptr + 4) = 4;
+ ptr = digi_setwin(sc, 0xc40);
+ vW(ptr + 0) = 1;
+ vW(ptr + 2) = FEPCODE >> 4;
+ vW(ptr + 4) = 4;
/* Clear the confirm word */
- ptr = sc->setwin(sc, FEPSTAT);
- W(ptr + 0) = 0;
+ ptr = digi_setwin(sc, FEPSTAT);
+ vW(ptr + 0) = 0;
/* Run the BIOS request */
outb(sc->port, FEPREQ | FEPMEM); /* send interrupt to BIOS */
outb(sc->port, FEPCLR | FEPMEM);
break;
+#endif
}
+ /* Release the firmware image */
+ i = linker_release_module(NULL, NULL, lf);
+ DLOG(DIGIDB_INIT, (sc->dev, "linker_release_module(%p)=%d\n", lf, i));
+
/* Now wait 'till the FEP/OS has booted */
for (i = 0; vW(ptr) != *(u_short *)"OS"; i++) {
if (i > 2*hz) {
log(LOG_ERR, "digi%d: FEP/OS start failed "
"(0x%02x != 0x%02x)\n",
sc->res.unit, vW(ptr), *(u_short *)"OS");
- sc->hidewin(sc);
+ digi_hidewin(sc);
+ sc->status = DIGI_STATUS_NOTINIT;
return (EIO);
}
digi_delay(sc, "digifep1", 5);
@@ -498,34 +541,40 @@
DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS started after %d iterations\n", i));
if (sc->model >= PCXEM) {
- ptr = sc->setwin(sc, 0xe04);
- vW(ptr) = 2;
- ptr = sc->setwin(sc, 0xc02);
+ /* Interrupt configuration */
+ ptr = digi_setwin(sc, 0xe04);
+#ifdef DIGI_INTERRUPT
+ vW(ptr) = 1; /* From dgap driver */
+#else
+ vW(ptr) = 2; /* ???? */
+#endif
+ /* Read number of ports */
+ ptr = digi_setwin(sc, 0xc02);
sc->numports = vW(ptr);
} else {
- ptr = sc->setwin(sc, 0xc22);
+ ptr = digi_setwin(sc, 0xc22);
sc->numports = vW(ptr);
}
+ device_printf(sc->dev, "%s, %d ports found\n", sc->name, sc->numports);
+
if (sc->numports == 0) {
- device_printf(sc->dev, "%s, 0 ports found\n", sc->name);
- sc->hidewin(sc);
+ digi_hidewin(sc);
+ sc->status = DIGI_STATUS_NOTINIT;
return (0);
}
- device_printf(sc->dev, "%s, %d ports found\n", sc->name, sc->numports);
-
if (sc->ports)
- free(sc->ports, M_TTYS);
+ free(sc->ports, M_DEVBUF);
sc->ports = malloc(sizeof(struct digi_p) * sc->numports,
- M_TTYS, M_WAITOK | M_ZERO);
+ M_DEVBUF, M_WAITOK | M_ZERO);
/*
* XXX Should read port 0xc90 for an array of 2byte values, 1 per
* port. If the value is 0, the port is broken....
*/
- ptr = sc->setwin(sc, 0);
+ ptr = digi_setwin(sc, 0);
/* We should now init per-port structures */
bc = (volatile struct board_chan *)(ptr + CHANSTRUCT);
@@ -540,38 +589,31 @@
port->sc = sc;
port->status = ENABLED;
port->bc = bc;
- tp = port->tp = ttyalloc();
- tp->t_oproc = digistart;
- tp->t_param = digiparam;
- tp->t_modem = digimodem;
- tp->t_break = digibreak;
- tp->t_stop = digistop;
- tp->t_cioctl = digisioctl;
- tp->t_ioctl = digiioctl;
- tp->t_open = digiopen;
- tp->t_close = digiclose;
- tp->t_sc = port;
+ /* Normally tty_alloc() is passed the softc but for digi, we
+ * need to pass the digi_p associated with the current port */
+ tp = port->tp = tty_alloc(&digi_class, port);
+ cptr = (u_char *)(intptr_t)ptr;
if (sc->model == PCXEVE) {
- port->txbuf = ptr +
- (((bc->tseg - sc->mem_seg) << 4) & 0x1fff);
- port->rxbuf = ptr +
- (((bc->rseg - sc->mem_seg) << 4) & 0x1fff);
- port->txwin = FEPWIN | ((bc->tseg - sc->mem_seg) >> 9);
- port->rxwin = FEPWIN | ((bc->rseg - sc->mem_seg) >> 9);
+ port->txbuf = cptr +
+ (((bc->tseg - MEM_SEG(sc)) << 4) & 0x1fff);
+ port->rxbuf = cptr +
+ (((bc->rseg - MEM_SEG(sc)) << 4) & 0x1fff);
+ port->txwin = FEPWIN | ((bc->tseg - MEM_SEG(sc)) >> 9);
+ port->rxwin = FEPWIN | ((bc->rseg - MEM_SEG(sc)) >> 9);
} else if (sc->model == PCXI || sc->model == PCXE) {
- port->txbuf = ptr + ((bc->tseg - sc->mem_seg) << 4);
- port->rxbuf = ptr + ((bc->rseg - sc->mem_seg) << 4);
+ port->txbuf = cptr + ((bc->tseg - MEM_SEG(sc)) << 4);
+ port->rxbuf = cptr + ((bc->rseg - MEM_SEG(sc)) << 4);
port->txwin = port->rxwin = 0;
} else {
- port->txbuf = ptr +
- (((bc->tseg - sc->mem_seg) << 4) % sc->win_size);
- port->rxbuf = ptr +
- (((bc->rseg - sc->mem_seg) << 4) % sc->win_size);
+ port->txbuf = cptr +
+ (((bc->tseg - MEM_SEG(sc)) << 4) % sc->win_size);
+ port->rxbuf = cptr +
+ (((bc->rseg - MEM_SEG(sc)) << 4) % sc->win_size);
port->txwin = FEPWIN |
- (((bc->tseg - sc->mem_seg) << 4) / sc->win_size);
+ (((bc->tseg - MEM_SEG(sc)) << 4) / sc->win_size);
port->rxwin = FEPWIN |
- (((bc->rseg - sc->mem_seg) << 4) / sc->win_size);
+ (((bc->rseg - MEM_SEG(sc)) << 4) / sc->win_size);
}
port->txbufsize = bc->tmax + 1;
port->rxbufsize = bc->rmax + 1;
@@ -579,24 +621,31 @@
lowwater = port->txbufsize >> 2;
if (lowwater > 1024)
lowwater = 1024;
- sc->setwin(sc, 0);
fepcmd_w(port, STXLWATER, lowwater, 10);
fepcmd_w(port, SRXLWATER, port->rxbufsize >> 2, 10);
fepcmd_w(port, SRXHWATER, (3 * port->rxbufsize) >> 2, 10);
- bc->edelay = 100;
+ bc->edelay = 100; /*XXX Use 0 if interrupts enabled */
+ bc->idata = 1;
- ttyinitmode(tp, 0, 0);
port->send_ring = 1; /* Default action on signal RI */
- ttycreate(tp, TS_CALLOUT, "D%r%r", sc->res.unit, i);
+ tty_makedev(tp, NULL, "D%r%r", sc->res.unit, i);
}
- sc->hidewin(sc);
- sc->inttest = timeout(digi_int_test, sc, hz);
+ mtx_lock(&sc->dg_mutex);
+ digi_hidewin(sc);
+ callout_reset(&sc->callout, hz, digi_int_test, sc);
/* fepcmd_w(&sc->ports[0], 0xff, 0, 0); */
sc->status = DIGI_STATUS_ENABLED;
return (0);
+
+init_failed:
+ linker_release_module(NULL, NULL, lf);
+ mtx_lock(&sc->dg_mutex);
+ sc->status = DIGI_STATUS_NOTINIT;
+ return (error);
+
}
static int
@@ -606,24 +655,24 @@
struct digi_p *port;
int bitand, bitor, mstat;
- port = tp->t_sc;
+ port = tty_softc(tp);
sc = port->sc;
if (sigon == 0 && sigoff == 0) {
- port->sc->setwin(port->sc, 0);
+ digi_setwin(sc, 0);
mstat = port->bc->mstat;
- port->sc->hidewin(port->sc);
- if (mstat & port->sc->csigs->rts)
+ digi_hidewin(sc);
+ if (mstat & sc->csigs->rts)
sigon |= SER_RTS;
if (mstat & port->cd)
sigon |= SER_DCD;
if (mstat & port->dsr)
sigon |= SER_DSR;
- if (mstat & port->sc->csigs->cts)
+ if (mstat & sc->csigs->cts)
sigon |= SER_CTS;
- if (mstat & port->sc->csigs->ri)
+ if (mstat & sc->csigs->ri)
sigon |= SER_RI;
- if (mstat & port->sc->csigs->dtr)
+ if (mstat & sc->csigs->dtr)
sigon |= SER_DTR;
return (sigon);
}
@@ -632,13 +681,13 @@
bitor = 0;
if (sigoff & SER_DTR)
- bitand |= port->sc->csigs->dtr;
+ bitand |= sc->csigs->dtr;
if (sigoff & SER_RTS)
- bitand |= port->sc->csigs->rts;
+ bitand |= sc->csigs->rts;
if (sigon & SER_DTR)
- bitor |= port->sc->csigs->dtr;
+ bitor |= sc->csigs->dtr;
if (sigon & SER_RTS)
- bitor |= port->sc->csigs->rts;
+ bitor |= sc->csigs->rts;
fepcmd_b(port, SETMODEM, bitor, ~bitand, 0);
return (0);
}
@@ -649,26 +698,32 @@
struct digi_softc *sc;
sc = dev->si_drv1;
+ mtx_lock(&sc->dg_mutex);
if (sc->status != DIGI_STATUS_ENABLED) {
+ mtx_unlock(&sc->dg_mutex);
DLOG(DIGIDB_OPEN, (sc->dev, "Cannot open a disabled card\n"));
return (ENXIO);
}
sc->opencnt++;
+ mtx_unlock(&sc->dg_mutex);
return (0);
}
static int
-digiopen(struct tty *tp, struct cdev *dev)
+digiopen(struct tty *tp)
{
- int error;
struct digi_softc *sc;
struct digi_p *port;
volatile struct board_chan *bc;
- port = tp->t_sc;
+ port = tty_softc(tp);
sc = port->sc;
+ /* device lock needed to protect against detach or reninit during open */
+ mtx_lock(&sc->dg_mutex);
+
if (sc->status != DIGI_STATUS_ENABLED) {
+ mtx_unlock(&sc->dg_mutex);
DLOG(DIGIDB_OPEN, (sc->dev, "Cannot open a disabled card\n"));
return (ENXIO);
}
@@ -680,13 +735,13 @@
* cases: to preempt sleeping callin opens if we are callout,
* and to complete a callin open after DCD rises.
*/
- sc->setwin(sc, 0);
+ digi_setwin(sc, 0);
bc->rout = bc->rin; /* clear input queue */
bc->idata = 1;
bc->iempty = 1;
bc->ilow = 1;
- bc->mint = port->cd | port->sc->csigs->ri;
+ bc->mint = port->cd | sc->csigs->ri; /*XXX cf m_int on dgap */
bc->tin = bc->tout;
if (port->ialtpin) {
port->cd = sc->csigs->dsr;
@@ -695,11 +750,11 @@
port->cd = sc->csigs->cd;
port->dsr = sc->csigs->dsr;
}
- tp->t_wopeners++; /* XXX required ? */
- error = digiparam(tp, &tp->t_termios);
- tp->t_wopeners--;
- return (error);
+ port->status |= DG_OPENED;
+ mtx_unlock(&sc->dg_mutex);
+
+ return (0);
}
static int
@@ -708,93 +763,69 @@
struct digi_softc *sc;
sc = dev->si_drv1;
+ /*XXX Is the mutex really needed here */
+ mtx_lock(&sc->dg_mutex);
sc->opencnt--;
+ mtx_unlock(&sc->dg_mutex);
return (0);
}
static void
-digidtrwakeup(void *chan)
-{
- struct digi_p *port = chan;
-
- port->status &= ~DIGI_DTR_OFF;
- wakeup(&port->tp->t_dtr_wait);
- port->tp->t_wopeners--;
-}
-
-static void
digiclose(struct tty *tp)
{
volatile struct board_chan *bc;
struct digi_p *port;
- int s;
- port = tp->t_sc;
+ port = tty_softc(tp);
bc = port->bc;
- s = spltty();
- port->sc->setwin(port->sc, 0);
+ digi_setwin(port->sc, 0);
bc->idata = 0;
bc->iempty = 0;
bc->ilow = 0;
bc->mint = 0;
- if ((tp->t_cflag & HUPCL) ||
- (!tp->t_actout && !(bc->mstat & port->cd) &&
- !(tp->t_init_in.c_cflag & CLOCAL)) ||
- !(tp->t_state & TS_ISOPEN)) {
+ if (port->p_hupcl ||
+//XXXXX (!tp->t_actout && !(bc->mstat & port->cd) && !port->p_clocal) ||
+ !tty_opened(tp)) {
digimodem(tp, 0, SER_DTR | SER_RTS);
- if (tp->t_dtr_wait != 0) {
- /* Schedule a wakeup of any callin devices */
- tp->t_wopeners++;
- timeout(&digidtrwakeup, port, tp->t_dtr_wait);
- port->status |= DIGI_DTR_OFF;
- }
}
- tp->t_actout = FALSE;
- wakeup(&tp->t_actout);
- wakeup(TSA_CARR_ON(tp));
- splx(s);
+ port->status &= ~DG_OPENED;
}
/*
* Load module "digi_<mod>.ko" and look for a symbol called digi_mod_<mod>.
*
- * Populate sc->bios, sc->fep, and sc->link from this data.
- *
- * sc->fep.data, sc->bios.data and sc->link.data are malloc()d according
- * to their respective sizes.
- *
- * The module is unloaded when we're done.
+ * Returns 0 if no error - in which case, the pointer to the digi_mod
+ * structure has been initialised and the linker file must be freed by
+ * the caller.
*/
static int
-digi_loadmoduledata(struct digi_softc *sc)
+digi_loadmoduledata(struct digi_softc *sc, struct digi_mod **mod, linker_file_t *lfp)
{
struct digi_mod *digi_mod;
linker_file_t lf;
- char *modfile, *sym;
+ char *name;
caddr_t symptr;
int modlen, res;
- KASSERT(sc->bios.data == NULL, ("Uninitialised BIOS variable"));
- KASSERT(sc->fep.data == NULL, ("Uninitialised FEP variable"));
- KASSERT(sc->link.data == NULL, ("Uninitialised LINK variable"));
KASSERT(sc->module != NULL, ("Uninitialised module name"));
modlen = strlen(sc->module);
- modfile = malloc(modlen + 6, M_TEMP, M_WAITOK);
- snprintf(modfile, modlen + 6, "digi_%s", sc->module);
- if ((res = linker_reference_module(modfile, NULL, &lf)) != 0)
- printf("%s: Failed %d to autoload module\n", modfile, res);
- free(modfile, M_TEMP);
- if (res != 0)
+ name = malloc(modlen + 10, M_TEMP, M_WAITOK);
+
+ snprintf(name, modlen + 10, "digi_%s", sc->module);
+ if ((res = linker_reference_module(name, NULL, &lf)) != 0)
+ printf("%s: Failed %d to autoload module\n", name, res);
+ if (res != 0) {
+ free(name, M_TEMP);
return (res);
+ }
- sym = malloc(modlen + 10, M_TEMP, M_WAITOK);
- snprintf(sym, modlen + 10, "digi_mod_%s", sc->module);
- symptr = linker_file_lookup_symbol(lf, sym, 0);
- free(sym, M_TEMP);
+ snprintf(name, modlen + 10, "digi_mod_%s", sc->module);
+ symptr = linker_file_lookup_symbol(lf, name, 0);
+ free(name, M_TEMP);
if (symptr == NULL) {
- printf("digi_%s.ko: Symbol `%s' not found\n", sc->module, sym);
+ printf("digi_%s.ko: Symbol `%s' not found\n", sc->module, name);
linker_release_module(NULL, NULL, lf);
return (EINVAL);
}
@@ -807,56 +838,39 @@
return (EINVAL);
}
- sc->bios.size = digi_mod->dm_bios.size;
- if (sc->bios.size != 0 && digi_mod->dm_bios.data != NULL) {
- sc->bios.data = malloc(sc->bios.size, M_TTYS, M_WAITOK);
- bcopy(digi_mod->dm_bios.data, sc->bios.data, sc->bios.size);
- }
-
- sc->fep.size = digi_mod->dm_fep.size;
- if (sc->fep.size != 0 && digi_mod->dm_fep.data != NULL) {
- sc->fep.data = malloc(sc->fep.size, M_TTYS, M_WAITOK);
- bcopy(digi_mod->dm_fep.data, sc->fep.data, sc->fep.size);
- }
-
- sc->link.size = digi_mod->dm_link.size;
- if (sc->link.size != 0 && digi_mod->dm_link.data != NULL) {
- sc->link.data = malloc(sc->link.size, M_TTYS, M_WAITOK);
- bcopy(digi_mod->dm_link.data, sc->link.data, sc->link.size);
- }
-
- linker_release_module(NULL, NULL, lf);
+ *mod = digi_mod;
+ *lfp = lf;
return (0);
}
static int
-digisioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
+digisioctl(struct tty *tp, int unit, u_long cmd, caddr_t data, struct thread *td)
{
struct digi_p *port;
struct digi_softc *sc;
- port = dev->si_drv1;
+ port = tty_softc(tp);
sc = port->sc;
switch (cmd) {
case DIGIIO_GETALTPIN:
- if (ISINIT(dev))
+ if (unit & TTYUNIT_INIT)
*(int *)data = port->ialtpin;
- else if (ISLOCK(dev))
+ else if (unit & TTYUNIT_LOCK)
*(int *)data = port->laltpin;
else
return (ENOTTY);
break;
case DIGIIO_SETALTPIN:
- if (ISINIT(dev)) {
+ if (unit & TTYUNIT_INIT) {
if (!port->laltpin) {
port->ialtpin = !!*(int *)data;
DLOG(DIGIDB_SET, (sc->dev,
"port%d: initial ALTPIN %s\n", port->pnum,
port->ialtpin ? "set" : "cleared"));
}
- } else if (ISLOCK(dev)) {
+ } else if (unit & TTYUNIT_LOCK) {
port->laltpin = !!*(int *)data;
DLOG(DIGIDB_SET, (sc->dev,
"port%d: ALTPIN %slocked\n",
@@ -865,7 +879,7 @@
return (ENOTTY);
break;
default:
- return (ENOTTY);
+ return (ENOIOCTL);
}
return (0);
}
@@ -877,39 +891,52 @@
struct digi_softc *sc;
sc = dev->si_drv1;
+ mtx_lock(&sc->dg_mutex);
- if (sc->status == DIGI_STATUS_DISABLED)
+ if (sc->status == DIGI_STATUS_DISABLED ||
+ sc->status == DIGI_STATUS_STARTING) {
+ mtx_unlock(&sc->dg_mutex);
return (ENXIO);
+ }
+ error = 0;
switch (cmd) {
case DIGIIO_DEBUG:
#ifdef DEBUG
digi_debug = *(int *)data;
- return (0);
#else
device_printf(sc->dev, "DEBUG not defined\n");
- return (ENXIO);
+ error = ENXIO;
#endif
+ break;
+
case DIGIIO_REINIT:
- digi_loadmoduledata(sc);
+ /* don't count this open for "in-use" checks */
+ sc->opencnt--;
error = digi_init(sc);
- digi_freemoduledata(sc);
- return (error);
+ sc->opencnt++;
+ break;
case DIGIIO_MODEL:
*(enum digi_model *)data = sc->model;
- return (0);
+ break;
case DIGIIO_IDENT:
- return (copyout(sc->name, *(char **)data,
- strlen(sc->name) + 1));
+ error = copyout(sc->name, *(char **)data,
+ strlen(sc->name) + 1);
+ break;
+
default:
- return (ENOIOCTL);
+ error = ENOIOCTL;
+ break;
}
+
+ mtx_unlock(&sc->dg_mutex);
+ return (error);
}
static int
-digiioctl(struct tty *tp, u_long cmd, void *data, int flag, struct thread *td)
+digiioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
{
struct digi_softc *sc;
struct digi_p *port;
@@ -918,9 +945,9 @@
int ival;
#endif
- port = tp->t_sc;
+ port = tty_softc(tp);
sc = port->sc;
- if (sc->status == DIGI_STATUS_DISABLED)
+ if (sc->status != DIGI_STATUS_ENABLED)
return (ENXIO);
if (!(port->status & ENABLED))
@@ -950,31 +977,30 @@
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('e', 'C'):
ival = IOCPARM_IVAL(data);
- data = &ival;
+ data = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case DIGIIO_RING:
port->send_ring = (u_char)*(int *)data;
break;
- default:
- return (ENOTTY);
- }
- return (0);
-}
-
-static void
-digibreak(struct tty *tp, int brk)
-{
- struct digi_p *port;
- port = tp->t_sc;
+ case TIOCCBRK:
+ /* There's no support for turning break state on/off
+ * arbitrarily so TIOCCBRK is a no-op and TIOCSBRK sends
+ * a fixed 400msec break */
+ break;
- /*
- * now it sends 400 millisecond break because I don't know
- * how to send an infinite break
- */
- if (brk)
+ case TIOCSBRK:
+ /* There's no support for turning break state on/off
+ * arbitrarily so TIOCCBRK is a no-op and TIOCSBRK sends
+ * a fixed 400msec break */
fepcmd_w(port, SENDBREAK, 400, 10);
+ break;
+
+ default:
+ return (ENOIOCTL);
+ }
+ return (0);
}
static int
@@ -986,25 +1012,30 @@
int iflag;
int hflow;
int s;
+#ifdef DIGI_ISA
int window;
+#endif
- port = tp->t_sc;
+ port = tty_softc(tp);
sc = port->sc;
DLOG(DIGIDB_SET, (sc->dev, "port%d: setting parameters\n", port->pnum));
if (t->c_ispeed == 0)
t->c_ispeed = t->c_ospeed;
- cflag = ttspeedtab(t->c_ospeed, digispeedtab);
+ /* Map actual baudrate to baudrate code */
+ for (s = 0; digispeedtab[s].sp_speed >= 0; s++)
+ if (digispeedtab[s].sp_speed == t->c_ospeed)
+ break;
+ cflag = digispeedtab[s].sp_code;
if (cflag < 0 || (cflag > 0 && t->c_ispeed != t->c_ospeed))
return (EINVAL);
- s = splclock();
-
+#ifdef DIGI_ISA
window = sc->window;
- sc->setwin(sc, 0);
-
+ digi_setwin(sc, 0);
+#endif
if (cflag == 0) { /* hangup */
DLOG(DIGIDB_SET, (sc->dev, "port%d: hangup\n", port->pnum));
digimodem(port->tp, 0, SER_DTR | SER_RTS);
@@ -1014,15 +1045,6 @@
DLOG(DIGIDB_SET, (sc->dev, "port%d: CBAUD = %d\n", port->pnum,
cflag));
-#if 0
- /* convert flags to sysV-style values */
- if (t->c_cflag & PARODD)
- cflag |= 0x0200;
- if (t->c_cflag & PARENB)
- cflag |= 0x0100;
- if (t->c_cflag & CSTOPB)
- cflag |= 0x0080;
-#else
/* convert flags to sysV-style values */
if (t->c_cflag & PARODD)
cflag |= FEP_PARODD;
@@ -1032,7 +1054,6 @@
cflag |= FEP_CSTOPB;
if (t->c_cflag & CLOCAL)
cflag |= FEP_CLOCAL;
-#endif
cflag |= (t->c_cflag & CSIZE) >> 4;
DLOG(DIGIDB_SET, (sc->dev, "port%d: CFLAG = 0x%x\n", port->pnum,
@@ -1042,11 +1063,11 @@
iflag =
t->c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP);
- if (port->c_iflag & IXON)
+ if (t->c_iflag & IXON)
iflag |= 0x400;
- if (port->c_iflag & IXANY)
+ if (t->c_iflag & IXANY)
iflag |= 0x800;
- if (port->c_iflag & IXOFF)
+ if (t->c_iflag & IXOFF)
iflag |= 0x1000;
DLOG(DIGIDB_SET, (sc->dev, "port%d: set iflag = 0x%x\n", port->pnum, iflag));
@@ -1071,27 +1092,39 @@
port->pnum, t->c_cc[VSTART], t->c_cc[VSTOP]));
fepcmd_b(port, SONOFFC, t->c_cc[VSTART], t->c_cc[VSTOP], 0);
+#ifdef DIGI_ISA
if (sc->window != 0)
- sc->towin(sc, 0);
+ digi_towin(sc, 0);
if (window != 0)
- sc->towin(sc, window);
- splx(s);
+ digi_towin(sc, window);
+#endif
+
+ /* save relevant bits for later */
+ port->p_hupcl = !!(t->c_cflag & HUPCL);
+ /* Want 'initial' value for CLOCAL so only set if device closed */
+ if (!tty_opened(tp))
+ port->p_clocal = !!(t->c_cflag & CLOCAL);
return (0);
}
-static void
+void
digi_intr(void *vp)
{
struct digi_p *port;
- char *cxcon;
struct digi_softc *sc;
int ehead, etail;
volatile struct board_chan *bc;
struct tty *tp;
int head, tail;
int wrapmask;
- int size, window;
+#ifdef DIGI_INTERRUPT
+ int islocked;
+#endif
+#ifdef DIGI_ISA
+ int window;
+#endif
+ size_t size;
struct event {
u_char pnum;
u_char event;
@@ -1101,58 +1134,39 @@
sc = vp;
+#ifdef DIGI_INTERRUPT
+ /* When called from an interrupt, mutex isn't owned on entry */
+ islocked = mtx_owned(&sc->dg_mutex);
+ if (!islocked)
+ mtx_lock(&sc->dg_mutex);
+#endif
+
if (sc->status != DIGI_STATUS_ENABLED) {
DLOG(DIGIDB_IRQ, (sc->dev, "interrupt on disabled board !\n"));
+#ifdef DIGI_INTERRUPT
+ if (!islocked)
+ mtx_unlock(&sc->dg_mutex);
+#endif
return;
}
#ifdef DIGI_INTERRUPT
- microtime(&sc->intr_timestamp);
+ sc->interrupt_seen = 1;
#endif
+#ifdef DIGI_ISA
window = sc->window;
- sc->setwin(sc, 0);
+ digi_setwin(sc, 0);
+#endif
- if (sc->model >= PCXEM && W(sc->vmem + 0xd00)) {
- struct con_bios *con = con_bios_list;
- register u_char *ptr;
-
- ptr = sc->vmem + W(sc->vmem + 0xd00);
- while (con) {
- if (ptr[1] && W(ptr + 2) == W(con->bios + 2))
- /* Not first block -- exact match */
- break;
-
- if (W(ptr + 4) >= W(con->bios + 4) &&
- W(ptr + 4) <= W(con->bios + 6))
- /* Initial search concetrator BIOS */
- break;
- }
-
- if (con == NULL) {
- log(LOG_ERR, "digi%d: wanted bios LREV = 0x%04x"
- " not found!\n", sc->res.unit, W(ptr + 4));
- W(ptr + 10) = 0;
- W(sc->vmem + 0xd00) = 0;
- goto eoi;
- }
- cxcon = con->bios;
- W(ptr + 4) = W(cxcon + 4);
- W(ptr + 6) = W(cxcon + 6);
- if (ptr[1] == 0)
- W(ptr + 2) = W(cxcon + 2);
- W(ptr + 8) = (ptr[1] << 6) + W(cxcon + 8);
- size = W(cxcon + 10) - (ptr[1] << 10);
- if (size <= 0) {
- W(ptr + 8) = W(cxcon + 8);
- W(ptr + 10) = 0;
- } else {
- if (size > 1024)
- size = 1024;
- W(ptr + 10) = size;
- bcopy(cxcon + (ptr[1] << 10), ptr + 12, size);
- }
- W(sc->vmem + 0xd00) = 0;
+ if (sc->model >= PCXEM && vW(sc->vmem + 0xd00)) {
+ register u_char volatile *ptr;
+
+ ptr = sc->vmem + vW(sc->vmem + 0xd00);
+ log(LOG_ERR, "digi%d: wanted bios LREV = 0x%04x"
+ " not found!\n", sc->res.unit, vW(ptr + 4));
+ vW(ptr + 10) = 0;
+ vW(sc->vmem + 0xd00) = 0;
goto eoi;
}
@@ -1172,7 +1186,7 @@
while (ehead != etail) {
event = *(volatile struct event *)(sc->memevent + etail);
- etail = (etail + 4) & sc->gdata->imax;
+ sc->gdata->eout = (etail + 4) & sc->gdata->imax;
if (event.pnum >= sc->numports) {
log(LOG_ERR, "digi%d: port %d: got event"
@@ -1184,7 +1198,12 @@
bc = port->bc;
tp = port->tp;
- if (!(tp->t_state & TS_ISOPEN) && !tp->t_wopeners) {
+ /* Accessing the TTY subsystem requires the relevant port
+ * mutex to be owned. To prevent lock reversal, release
+ * the board lock */
+ mtx_unlock(&sc->dg_mutex);
+ tty_lock(tp);
+ if (!tty_opened(tp) && !(port->status & DG_OPENED)) {
DLOG(DIGIDB_IRQ, (sc->dev,
"port %d: event 0x%x on closed port\n",
event.pnum, event.event));
@@ -1193,7 +1212,7 @@
bc->iempty = 0;
bc->ilow = 0;
bc->mint = 0;
- continue;
+ goto end_of_event;
}
if (event.event & ~ALL_IND)
log(LOG_ERR, "digi%d: port%d: ? event 0x%x mstat 0x%x"
@@ -1208,7 +1227,7 @@
tail = bc->rout;
size = 0;
- if (!(tp->t_state & TS_ISOPEN)) {
+ if (!tty_opened(tp)) {
bc->rout = head;
goto end_of_data;
}
@@ -1219,23 +1238,21 @@
"port %d: p rx head = %d tail = %d\n",
event.pnum, head, tail));
top = (head > tail) ? head : wrapmask + 1;
- sc->towin(sc, port->rxwin);
size = top - tail;
- if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
- size = b_to_q((char *)port->rxbuf +
- tail, size, &tp->t_rawq);
+ if (ttydisc_can_bypass(tp)) {
+ size -= ttydisc_rint_bypass(tp,
+ port->rxbuf + tail, size);
tail = top - size;
- ttwakeup(tp);
} else for (; tail < top;) {
- ttyld_rint(tp, port->rxbuf[tail]);
- sc->towin(sc, port->rxwin);
+ digi_towin(sc, port->rxwin);
+ if (ttydisc_rint(tp, port->rxbuf[tail],
+ 0) != 0)
+ break;
size--;
tail++;
- if (tp->t_state & TS_TBLOCK)
- break;
}
tail &= wrapmask;
- sc->setwin(sc, 0);
+ digi_setwin(sc, 0);
bc->rout = tail;
head = bc->rin;
if (size)
@@ -1243,15 +1260,14 @@
}
if (bc->orun) {
- CE_RECORD(port, CE_OVERRUN);
log(LOG_ERR, "digi%d: port%d: %s\n",
sc->res.unit, event.pnum,
digi_errortxt(CE_OVERRUN));
+ ttydisc_rint(tp, 0, TRE_OVERRUN);
bc->orun = 0;
}
end_of_data:
if (size) {
- tp->t_state |= TS_TBLOCK;
port->status |= PAUSE_RX;
DLOG(DIGIDB_RX, (sc->dev, "port %d: pause RX\n",
event.pnum));
@@ -1265,94 +1281,99 @@
event.pnum));
if ((event.mstat ^ event.lstat) & port->cd) {
- sc->hidewin(sc);
- ttyld_modem(tp, event.mstat & port->cd);
- sc->setwin(sc, 0);
- wakeup(TSA_CARR_ON(tp));
+ digi_hidewin(sc);
+ ttydisc_modem(tp, event.mstat & port->cd);
+ digi_setwin(sc, 0);
}
if (event.mstat & sc->csigs->ri) {
DLOG(DIGIDB_RI, (sc->dev, "port %d: RING\n",
event.pnum));
if (port->send_ring) {
- ttyld_rint(tp, 'R');
- ttyld_rint(tp, 'I');
- ttyld_rint(tp, 'N');
- ttyld_rint(tp, 'G');
- ttyld_rint(tp, '\r');
- ttyld_rint(tp, '\n');
+ /* Cheat a bit here - there shouldn't
+ * be any data in the kernel buffers
+ * and correctly supporting overflow
+ * control would be painful */
+ ttydisc_rint(tp, 'R', 0);
+ ttydisc_rint(tp, 'I', 0);
+ ttydisc_rint(tp, 'N', 0);
+ ttydisc_rint(tp, 'G', 0);
+ ttydisc_rint(tp, '\r', 0);
+ ttydisc_rint(tp, '\n', 0);
}
}
}
if (event.event & BREAK_IND) {
DLOG(DIGIDB_MODEM, (sc->dev, "port %d: BREAK_IND\n",
event.pnum));
- ttyld_rint(tp, TTY_BI);
+ ttydisc_rint(tp, 0, TRE_BREAK);
}
if (event.event & (LOWTX_IND | EMPTYTX_IND)) {
DLOG(DIGIDB_IRQ, (sc->dev, "port %d:%s%s\n",
event.pnum,
event.event & LOWTX_IND ? " LOWTX" : "",
event.event & EMPTYTX_IND ? " EMPTYTX" : ""));
- ttyld_start(tp);
+ digioutwakeup(tp);
}
+ ttydisc_rint_done(tp);
+end_of_event:
+ tty_unlock(tp);
+ mtx_lock(&sc->dg_mutex);
+ /* Check the device is still there and reload ring pointers */
+ if (sc->status != DIGI_STATUS_ENABLED) {
+#ifdef DIGI_INTERRUPT
+ if (!islocked)
+ mtx_unlock(&sc->dg_mutex);
+#endif
+ return;
+ }
+ ehead = sc->gdata->ein;
+ etail = sc->gdata->eout;
}
- sc->gdata->eout = etail;
eoi:
- if (sc->window != 0)
- sc->towin(sc, 0);
- if (window != 0)
- sc->towin(sc, window);
+ /* Ack any interrupt */
+#ifdef DIGI_ISA
+ if (sc->pcibus)
+#endif
+ (void)sc->vmem[0x200002];
+
+#ifdef DIGI_ISA
+ digi_towin(sc, window);
+#endif
+#ifdef DIGI_INTERRUPT
+ if (!islocked)
+ mtx_unlock(&sc->dg_mutex);
+#endif
}
static void
-digistart(struct tty *tp)
+digioutwakeup(struct tty *tp)
{
struct digi_p *port;
struct digi_softc *sc;
volatile struct board_chan *bc;
int head, tail;
int size, ocount, totcnt = 0;
- int s;
int wmask;
- port = tp->t_sc;
+ port = tty_softc(tp);
sc = port->sc;
bc = port->bc;
wmask = port->txbufsize - 1;
- s = spltty();
- port->lcc = tp->t_outq.c_cc;
- sc->setwin(sc, 0);
- if (!(tp->t_state & TS_TBLOCK)) {
- if (port->status & PAUSE_RX) {
- DLOG(DIGIDB_RX, (sc->dev, "port %d: resume RX\n",
- port->pnum));
- /*
- * CAREFUL - braces are needed here if the DLOG is
- * optimised out!
- */
- }
- port->status &= ~PAUSE_RX;
- bc->idata = 1;
- }
- if (!(tp->t_state & TS_TTSTOP) && port->status & PAUSE_TX) {
+ digi_setwin(sc, 0);
+ if (port->status & PAUSE_TX) {
DLOG(DIGIDB_TX, (sc->dev, "port %d: resume TX\n", port->pnum));
port->status &= ~PAUSE_TX;
fepcmd_w(port, RESUMETX, 0, 10);
}
- if (tp->t_outq.c_cc == 0)
- tp->t_state &= ~TS_BUSY;
- else
- tp->t_state |= TS_BUSY;
head = bc->tin;
- while (tp->t_outq.c_cc != 0) {
+ do {
tail = bc->tout;
DLOG(DIGIDB_INT, (sc->dev, "port%d: s tx head = %d tail = %d\n",
port->pnum, head, tail));
-
if (head < tail)
size = tail - head - 1;
else {
@@ -1363,73 +1384,117 @@
if (size == 0)
break;
- sc->towin(sc, port->txwin);
- ocount = q_to_b(&tp->t_outq, port->txbuf + head, size);
+ digi_towin(sc, port->txwin);
+ ocount = ttydisc_getc(tp, port->txbuf + head, size);
+ if (ocount == 0)
+ break;
totcnt += ocount;
head += ocount;
head &= wmask;
- sc->setwin(sc, 0);
+ digi_setwin(sc, 0);
bc->tin = head;
bc->iempty = 1;
bc->ilow = 1;
- }
- port->lostcc = tp->t_outq.c_cc;
- tail = bc->tout;
- if (head < tail)
- size = port->txbufsize - tail + head;
- else
- size = head - tail;
+ } while (1);
- port->lbuf = size;
- DLOG(DIGIDB_INT, (sc->dev, "port%d: s total cnt = %d\n", port->pnum, totcnt));
- ttwwakeup(tp);
- splx(s);
+ DLOG(DIGIDB_INT, (sc->dev, "port%d: s total cnt = %d\n", port->pnum,
+ totcnt));
}
+/* Invoked when the TTY subsystem can accept more input from the device */
static void
-digistop(struct tty *tp, int rw)
+digiinwakeup(struct tty *tp)
+{
+ struct digi_p *port;
+ volatile struct board_chan *bc;
+#ifdef DEBUG
+ struct digi_softc *sc;
+#endif
+
+ port = tty_softc(tp);
+ bc = port->bc;
+#ifdef DEBUG
+ sc = port->sc;
+#endif
+
+ if (port->status & PAUSE_RX) {
+ DLOG(DIGIDB_RX, (sc->dev, "port %d: resume RX\n", port->pnum));
+ port->status &= ~PAUSE_RX;
+ }
+ bc->idata = 1;
+}
+
+static void
+diginotify(struct tty *tp, char event)
{
struct digi_softc *sc;
struct digi_p *port;
+ volatile struct board_chan *bc;
- port = tp->t_sc;
+ port = tty_softc(tp);
sc = port->sc;
+ bc = port->bc;
- DLOG(DIGIDB_TX, (sc->dev, "port %d: pause TX\n", port->pnum));
- port->status |= PAUSE_TX;
- fepcmd_w(port, PAUSETX, 0, 10);
+ DLOG(DIGIDB_TX, (sc->dev, "port %d: event %d\n", port->pnum, event));
+ switch (event) {
+ case TIOCPKT_FLUSHREAD: /* flush Rx queue */
+ bc->rout = bc->rin; /* clear input queue */
+ bc->orun = 0; /* clear overrun */
+ break;
+ case TIOCPKT_FLUSHWRITE: /* flush Tx queue */
+ fepcmd_w(port, STPTR, bc->tin, 10);
+ port->status &= ~PAUSE_TX;
+ fepcmd_w(port, RESUMETX, 0, 10);
+ break;
+ case TIOCPKT_STOP: /* stop output */
+ port->status |= PAUSE_TX;
+ fepcmd_w(port, PAUSETX, 0, 10);
+ break;
+ case TIOCPKT_START: /* start output */
+ port->status &= ~PAUSE_TX;
+ fepcmd_w(port, RESUMETX, 0, 10);
+ break;
+ }
}
static void
fepcmd(struct digi_p *port, int cmd, int op1, int ncmds)
{
- u_char *mem;
+ u_char volatile *mem;
+ struct digi_softc *sc;
unsigned tail, head;
int count, n;
+ int islocked;
- mem = port->sc->memcmd;
+ sc = port->sc;
+ islocked = mtx_owned(&sc->dg_mutex);
+ if (!islocked)
+ mtx_lock(&sc->dg_mutex);
+ mem = sc->memcmd;
- port->sc->setwin(port->sc, 0);
+ digi_setwin(sc, 0);
- head = port->sc->gdata->cin;
+ head = sc->gdata->cin;
mem[head + 0] = cmd;
mem[head + 1] = port->pnum;
- *(u_short *)(mem + head + 2) = op1;
+ *(u_short volatile *)(mem + head + 2) = op1;
- head = (head + 4) & port->sc->gdata->cmax;
- port->sc->gdata->cin = head;
+ head = (head + 4) & sc->gdata->cmax;
+ sc->gdata->cin = head;
for (count = FEPTIMEOUT; count > 0; count--) {
- head = port->sc->gdata->cin;
- tail = port->sc->gdata->cout;
- n = (head - tail) & port->sc->gdata->cmax;
+ head = sc->gdata->cin;
+ tail = sc->gdata->cout;
+ n = (head - tail) & sc->gdata->cmax;
if (n <= ncmds * sizeof(short) * 4)
break;
}
+ if (!islocked)
+ mtx_unlock(&sc->dg_mutex);
if (count == 0)
log(LOG_ERR, "digi%d: port%d: timeout on FEP command\n",
- port->sc->res.unit, port->pnum);
+ sc->res.unit, port->pnum);
}
const char *
@@ -1450,16 +1515,19 @@
int
digi_attach(struct digi_softc *sc)
{
+ int error;
+
sc->res.ctldev = make_dev(&digi_csw,
sc->res.unit << 16, UID_ROOT, GID_WHEEL,
0600, "digi%r.ctl", sc->res.unit);
sc->res.ctldev->si_drv1 = sc;
- digi_loadmoduledata(sc);
- digi_init(sc);
- digi_freemoduledata(sc);
+ printf("digi%d: softc at %p\n", sc->res.unit, sc);
+ mtx_lock(&sc->dg_mutex);
+ error = digi_init(sc);
+ mtx_unlock(&sc->dg_mutex);
- return (0);
+ return (error);
}
static int
@@ -1468,12 +1536,15 @@
int i;
struct digi_p *port;
+ if (sc->opencnt != 0)
+ return (1);
+
port = &sc->ports[0];
for (i = 0; i < sc->numports; i++, port++)
- if (port->tp->t_state & TS_ISOPEN) {
+ if (tty_opened(port->tp)) {
DLOG(DIGIDB_INIT, (sc->dev, "port%d: busy\n", i));
return (1);
- } else if (port->tp->t_wopeners || port->opencnt) {
+ } else if (port->status & DG_OPENED) {
DLOG(DIGIDB_INIT, (sc->dev, "port%d: blocked in open\n",
i));
return (1);
@@ -1482,35 +1553,66 @@
}
static void
+digifree(void *arg)
+{
+ struct digi_p *port = arg;
+
+ atomic_subtract_32(&port->sc->dg_free, 1);
+}
+
+/* Free all state associated with the given DigiBoard */
+static void
digi_free_state(struct digi_softc *sc)
{
+#ifdef DIGI_INTERRUPT
+ u_char volatile *ptr;
+#endif
int i;
- /* Blow it all away */
+ mtx_assert(&sc->dg_mutex, MA_OWNED);
+ sc->status = DIGI_STATUS_DISABLED; /* Keep others out */
- for (i = 0; i < sc->numports; i++)
- ttygone(sc->ports[i].tp);
+#ifdef DIGI_INTERRUPT
+ /* Interrupt configuration */
+ ptr = digi_setwin(sc, 0xe04);
+ vW(ptr) = 0; /* By experimentation */
+#endif
- /* XXX: this might be better done as a ttypurge method */
- untimeout(digi_poll, sc, sc->callout);
- callout_handle_init(&sc->callout);
- untimeout(digi_int_test, sc, sc->inttest);
- callout_handle_init(&sc->inttest);
+ /* Need to drop the device lock to prevent a lock reversal during
+ * the TTY cleanup, callout drain and interrupt cleanup */
+ mtx_unlock(&sc->dg_mutex);
+
+ /* Stop more data arriving */
+ callout_drain(&sc->callout);
+
+ /* Free each TTY */
+ sc->dg_free = 0;
+ for (i = 0; i < sc->numports; i++) {
+ atomic_add_32(&sc->dg_free, 1);
+ tty_lock(sc->ports[i].tp);
+ tty_rel_gone(sc->ports[i].tp);
+ }
- for (i = 0; i < sc->numports; i++)
- ttyfree(sc->ports[i].tp);
+ while (sc->dg_free != 0)
+ digi_delay(sc, "dgdie", hz / 10);
- bus_teardown_intr(sc->dev, sc->res.irq, sc->res.irqHandler);
#ifdef DIGI_INTERRUPT
+ /* Whilst it's not explicitly mentioned, holding dg_mutex whilst
+ * calling bus_teardown_intr() seems liable to deadlock if the
+ * interrupt handler (digi_intr()) is executing */
+ bus_teardown_intr(sc->dev, sc->res.irq, sc->res.irqHandler);
if (sc->res.irq != NULL) {
- bus_release_resource(dev, SYS_RES_IRQ, sc->res.irqrid,
+ bus_release_resource(sc->dev, SYS_RES_IRQ, sc->res.irqrid,
sc->res.irq);
sc->res.irq = NULL;
}
#endif
+ /* Re-acquire device lock for remaining cleanup processing */
+ mtx_lock(&sc->dg_mutex);
+
if (sc->numports) {
KASSERT(sc->ports, ("digi%d: Lost my ports ?", sc->res.unit));
- free(sc->ports, M_TTYS);
+ free(sc->ports, M_DEVBUF);
sc->ports = NULL;
sc->numports = 0;
}
@@ -1524,16 +1626,19 @@
struct digi_softc *sc = device_get_softc(dev);
DLOG(DIGIDB_INIT, (sc->dev, "detaching\n"));
+ mtx_lock(&sc->dg_mutex);
- /* If we're INIT'd, numports must be 0 */
- KASSERT(sc->numports == 0 || sc->status != DIGI_STATUS_NOTINIT,
- ("digi%d: numports(%d) & status(%d) are out of sync",
- sc->res.unit, sc->numports, (int)sc->status));
-
- if (digi_inuse(sc))
+ if (digi_inuse(sc) ||
+ sc->status == DIGI_STATUS_DISABLED ||
+ sc->status == DIGI_STATUS_STARTING) {
+ mtx_unlock(&sc->dg_mutex);
return (EBUSY);
+ }
digi_free_state(sc);
+ /* Note that we're cleaning up & destroy mutex */
+ sc->status = DIGI_STATUS_DISABLED;
+ mtx_destroy(&sc->dg_mutex);
destroy_dev(sc->res.ctldev);
@@ -1542,11 +1647,13 @@
sc->res.mem);
sc->res.mem = NULL;
}
+#ifdef DIGI_ISA
if (sc->res.io != NULL) {
bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
sc->res.io);
sc->res.io = NULL;
}
+#endif
return (0);
}
@@ -1554,6 +1661,20 @@
int
digi_shutdown(device_t dev)
{
+ struct digi_softc *sc = device_get_softc(dev);
+
+ DLOG(DIGIDB_INIT, (sc->dev, "shutdown\n"));
+ mtx_lock(&sc->dg_mutex);
+
+ if (digi_inuse(sc) ||
+ sc->status == DIGI_STATUS_DISABLED ||
+ sc->status == DIGI_STATUS_STARTING) {
+ mtx_unlock(&sc->dg_mutex);
+ return (EBUSY);
+ }
+
+ digi_free_state(sc);
+ mtx_unlock(&sc->dg_mutex);
return (0);
}
Index: sys/dev/digi/digi.h
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/digi.h,v
retrieving revision 1.19
diff -u -r1.19 digi.h
--- sys/dev/digi/digi.h 14 Oct 2004 18:37:59 -0000 1.19
+++ sys/dev/digi/digi.h 20 Jun 2011 22:43:51 -0000
@@ -28,7 +28,11 @@
*
* $FreeBSD: src/sys/dev/digi/digi.h,v 1.19 2004/10/14 18:37:59 phk Exp $
*/
+/* TODO: Someone needs to work through digi.c and digi_isa.c and add all
+ * the necessary locking for ISA-based cards to work with TTYng */
+#undef DIGI_ISA /* ISA-based cards aren't supported */
+//#define const
#define W(p) (*(u_int16_t *)(p))
#define vW(p) (*(u_int16_t volatile *)(p))
#define D(p) (*(u_int32_t *)(p))
@@ -38,16 +42,15 @@
#define CE_INTERRUPT_BUF_OVERFLOW 1
#define CE_TTY_BUF_OVERFLOW 2
#define CE_NTYPES 3
-#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum])
-/*#define DIGI_INTERRUPT*/
+#define DIGI_INTERRUPT /* Use interrupts instead of polling if possible */
#ifndef DEBUG
#define DEBUG
#endif
#ifdef DEBUG
-extern unsigned digi_debug;
+extern unsigned long digi_debug;
#define DLOG(level, args) if (digi_debug & (level)) device_printf args
#else
#define DLOG(level, args)
@@ -62,11 +65,11 @@
int status;
#define ENABLED 1
-#define DIGI_DTR_OFF 2
+//#define DIGI_DTR_OFF 2
#define PAUSE_TX 8
#define PAUSE_RX 16
+#define DG_OPENED 0x20
- int opencnt;
u_short txbufsize;
u_short rxbufsize;
volatile struct board_chan *bc;
@@ -79,29 +82,13 @@
u_char pnum; /* port number */
- u_char modemfake; /* Modem values to be forced */
- u_char mstat;
- u_char modem; /* Force values */
-
- /*
- * The high level of the driver never reads status registers directly
- * because there would be too many side effects to handle conveniently.
- * Instead, it reads copies of the registers stored here by the
- * interrupt handler.
- */
- u_char last_modem_status; /* last MSR read by intr handler */
- u_char prev_modem_status; /* last MSR handled by high level */
-
- u_long bytes_in, bytes_out;
- u_int delta_error_counts[CE_NTYPES];
- u_long error_counts;
-
- tcflag_t c_iflag; /* hold true IXON/IXOFF/IXANY */
- int lcc, lostcc, lbuf;
u_char send_ring;
unsigned laltpin : 1; /* Alternate pin settings locked */
unsigned ialtpin : 1; /* Initial alternate pin settings */
+ unsigned p_hupcl : 1; /* Hangup on any close */
+ unsigned p_clocal : 1; /* termios 'clocal' handling */
+
int cd; /* Depends on the altpin setting */
int dsr;
@@ -120,72 +107,84 @@
};
enum digi_board_status {
- DIGI_STATUS_NOTINIT,
- DIGI_STATUS_ENABLED,
- DIGI_STATUS_DISABLED
+ DIGI_STATUS_NOTINIT, /* Board free */
+ DIGI_STATUS_ENABLED, /* Board ready for normal use */
+ DIGI_STATUS_DISABLED, /* Board being uninitialised */
+ DIGI_STATUS_STARTING /* Board being initialised */
};
/* Digiboard per-board structure */
struct digi_softc {
/* struct board_info */
device_t dev;
+ struct mtx dg_mutex; /* Device and TTY mutex */
const char *name;
+ const char *module;
enum digi_board_status status;
- u_short numports; /* number of ports on card */
+ u_int numports; /* number of ports on card */
+#ifdef DIGI_ISA
u_int port; /* I/O port */
u_int wport; /* window select I/O port */
+#endif
struct {
- struct resource *mem;
+ struct cdev *ctldev;
+ int unit;
int mrid;
+ struct resource *mem;
+#ifdef DIGI_INTERRUPT
struct resource *irq;
+ void *irqHandler;
int irqrid;
- struct resource *io;
+#endif
+#ifdef DIGI_ISA
int iorid;
- void *irqHandler;
- int unit;
- struct cdev *ctldev;
+ struct resource *io;
+#endif
} res;
- u_char *vmem; /* virtual memory address */
- u_char *memcmd;
+ u_char volatile *vmem; /* virtual memory address */
+ u_char volatile *memcmd;
volatile u_char *memevent;
+#ifdef DIGI_ISA
long pmem; /* physical memory address */
-
- struct {
- u_char *data;
- size_t size;
- } bios, fep, link;
-
-#ifdef DIGI_INTERRUPT
- struct timeval intr_timestamp;
#endif
struct digi_p *ports; /* pointer to array of port descriptors */
volatile struct global_data *gdata;
- u_char window; /* saved window */
+ const struct digi_control_signals *csigs;
+ struct callout callout; /* poll timeout handle */
int win_size;
int win_bits;
- int mem_size;
- int mem_seg;
enum digi_model model;
- const struct digi_control_signals *csigs;
+ int dg_free;
int opencnt;
+#ifdef DIGI_ISA
unsigned pcibus : 1; /* On a PCI bus ? */
+#endif
+#ifdef DIGI_INTERRUPT
+ unsigned interrupt_seen : 1; /* Seen an interrupt */
+#endif
- struct callout_handle callout; /* poll timeout handle */
- struct callout_handle inttest; /* int test timeout handle */
- const char *module;
-
- u_char *(*setwin)(struct digi_softc *_sc, unsigned _addr);
+#ifdef DIGI_ISA
+ u_char volatile *(*setwin)(struct digi_softc *_sc, unsigned _addr);
void (*hidewin)(struct digi_softc *_sc);
void (*towin)(struct digi_softc *_sc, int _win);
+ u_char window; /* saved window */
+ int mem_seg;
+#endif
#ifdef DEBUG
int intr_count;
#endif
};
+#ifdef DIGI_ISA
+#define MEM_SEG(sc) ((sc)->mem_seg)
+#else
+#define MEM_SEG(sc) (0)
+#endif
+
extern devclass_t digi_devclass;
extern const struct digi_control_signals digi_xixe_signals;
@@ -197,3 +196,66 @@
int digi_shutdown(device_t _dev);
void digi_delay(struct digi_softc *_sc, const char *_txt,
u_long _timo);
+void digi_intr(void *);
+
+#ifdef DIGI_ISA
+static __inline u_char volatile *
+digi_setwin(struct digi_softc *sc, unsigned addr)
+{
+ return (sc->setwin(sc, addr));
+}
+
+static __inline void
+digi_hidewin(struct digi_softc *sc)
+{
+ sc->hidewin(sc);
+}
+
+static __inline void
+digi_towin(struct digi_softc *sc, int win)
+{
+ sc->towin(sc, win);
+}
+#else
+static __inline u_char volatile *
+digi_setwin(struct digi_softc *sc, unsigned addr)
+{
+ return (sc->vmem + addr);
+}
+
+#define digi_hidewin(_sc)
+#define digi_towin(_sc, _win)
+#endif
+
+static __inline u_char
+digi_inport(struct digi_softc *sc)
+{
+#ifdef DIGI_ISA
+ if (!sc->pcibus)
+ return (inb(sc->port));
+ else
+#endif
+ return (((u_char volatile *)(sc->vmem))[0x200000]);
+}
+
+static __inline void
+digi_outport(struct digi_softc *sc, u_char val)
+{
+#ifdef DIGI_ISA
+ if (!sc->pcibus)
+ return (outb(sc->port, val));
+ else
+#endif
+ (((u_char volatile *)(sc->vmem))[0x200000]) = val;
+}
+
+static __inline void
+digi_outportmem(struct digi_softc *sc, u_char val)
+{
+#ifdef DIGI_ISA
+ if (!sc->pcibus)
+ return (outb(sc->port, val | FEPMEM));
+ else
+#endif
+ (((u_char volatile *)(sc->vmem))[0x200000]) = val;
+}
Index: sys/dev/digi/digi_isa.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/digi_isa.c,v
retrieving revision 1.13
diff -u -r1.13 digi_isa.c
--- sys/dev/digi/digi_isa.c 30 May 2004 20:08:30 -0000 1.13
+++ sys/dev/digi/digi_isa.c 20 Jun 2011 22:43:51 -0000
@@ -41,7 +41,9 @@
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/lock.h>
#include <sys/module.h>
+#include <sys/mutex.h>
#include <sys/tty.h>
#include <sys/bus.h>
#include <machine/bus.h>
@@ -54,6 +56,10 @@
#include <dev/digi/digireg.h>
#include <dev/digi/digi.h>
+#ifndef DIGI_ISA
+#error No support for ISA Digi cards
+#endif
+
/* Valid i/o addresses are any of these with either 0 or 4 added */
static u_long digi_validio[] = {
0x100, 0x110, 0x120, 0x200, 0x220, 0x300, 0x320
@@ -70,14 +76,14 @@
};
#define DIGI_NVALIDMEM (sizeof(digi_validmem) / sizeof(digi_validmem[0]))
-static u_char *
+static u_char volatile *
digi_isa_setwin(struct digi_softc *sc, unsigned int addr)
{
outb(sc->wport, sc->window = FEPWIN | (addr >> sc->win_bits));
return (sc->vmem + (addr % sc->win_size));
}
-static u_char *
+static u_char volatile *
digi_xi_setwin(struct digi_softc *sc, unsigned int addr)
{
outb(sc->wport, sc->window = FEPMEM);
@@ -320,7 +326,7 @@
{
struct digi_softc *sc = device_get_softc(dev);
int i, t, res;
- u_char *ptr;
+ u_char volatile *ptr;
int reset;
u_long msize, iosize;
long scport;
@@ -333,6 +339,7 @@
sc->status = DIGI_STATUS_NOTINIT;
sc->dev = dev;
sc->res.unit = device_get_unit(dev);
+ mtx_init(&sc->dg_mutex, "digimtx", NULL, MTX_DEF);
DLOG(DIGIDB_INIT, (sc->dev, "attaching\n"));
bus_get_resource(dev, SYS_RES_IOPORT, 0, &scport, &iosize);
Index: sys/dev/digi/digi_pci.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/digi_pci.c,v
retrieving revision 1.12
diff -u -r1.12 digi_pci.c
--- sys/dev/digi/digi_pci.c 5 Mar 2005 18:30:10 -0000 1.12
+++ sys/dev/digi/digi_pci.c 20 Jun 2011 22:43:51 -0000
@@ -34,7 +34,9 @@
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/lock.h>
#include <sys/module.h>
+#include <sys/mutex.h>
#include <sys/tty.h>
#include <sys/bus.h>
#include <machine/bus.h>
@@ -49,7 +51,8 @@
#include <dev/digi/digi.h>
#include <dev/digi/digi_pci.h>
-static u_char *
+#ifdef DIGI_ISA
+static volatile u_char *
digi_pci_setwin(struct digi_softc *sc, unsigned int addr)
{
return (sc->vmem + addr);
@@ -66,6 +69,7 @@
{
return;
}
+#endif
static int
digi_pci_probe(device_t dev)
@@ -107,12 +111,15 @@
#endif
sc = device_get_softc(dev);
+ DLOG(DIGIDB_INIT, (dev, "dev@%p, softc@%p\n", dev, sc));
+
KASSERT(sc, ("digi%d: softc not allocated in digi_pci_attach\n",
device_get_unit(dev)));
bzero(sc, sizeof(*sc));
sc->dev = dev;
sc->res.unit = device_get_unit(dev);
+ mtx_init(&sc->dg_mutex, "digimtx", NULL, MTX_DEF);
device_id = pci_get_devid(dev);
switch (device_id >> 16) {
@@ -175,9 +182,11 @@
return (ENXIO);
}
- pci_write_config(dev, 0x40, 0, 4);
- pci_write_config(dev, 0x46, 0, 4);
+ pci_write_config(dev, 0x40, 0, 1);
+ pci_write_config(dev, 0x46, 0, 1);
+ /* Limit burst length to 2 double-words */
+ pci_write_config(dev, 0x42, 1, 1);
sc->res.mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->res.mrid,
RF_ACTIVE);
@@ -189,26 +198,25 @@
device_printf(dev, "couldn't map interrupt\n");
return (ENXIO);
}
- retVal = bus_setup_intr(dev, sc->res.irq, INTR_TYPE_TTY,
- digiintr, sc, &sc->res.irqHandler);
+ retVal = bus_setup_intr(dev, sc->res.irq, INTR_TYPE_TTY | INTR_MPSAFE,
+ NULL, digi_intr, sc, &sc->res.irqHandler);
#else
DLOG(DIGIDB_IRQ, (sc->dev, "Interrupt support compiled out\n"));
#endif
sc->vmem = rman_get_virtual(sc->res.mem);
- sc->pmem = vtophys(sc->vmem);
- sc->pcibus = 1;
sc->win_size = 0x200000;
sc->win_bits = 21;
sc->csigs = &digi_normal_signals;
sc->status = DIGI_STATUS_NOTINIT;
- callout_handle_init(&sc->callout);
- callout_handle_init(&sc->inttest);
+ callout_init_mtx(&sc->callout, &sc->dg_mutex, 0);
+#ifdef DIGI_ISA
+ sc->pcibus = 1;
sc->setwin = digi_pci_setwin;
sc->hidewin = digi_pci_hidewin;
sc->towin = digi_pci_towin;
-
- PCIPORT = FEPRST;
+#endif
+ digi_outport(sc, FEPRST);
return (digi_attach(sc));
}
Index: sys/dev/digi/digi_pci.h
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/digi_pci.h,v
retrieving revision 1.1
diff -u -r1.1 digi_pci.h
--- sys/dev/digi/digi_pci.h 2 May 2001 01:08:04 -0000 1.1
+++ sys/dev/digi/digi_pci.h 20 Jun 2011 22:43:51 -0000
@@ -38,5 +38,3 @@
#define PCI_DEVICE_920_4 0x0026 /* XR-Plus 920 K, 4 port */
#define PCI_DEVICE_920_8 0x0027 /* XR-Plus 920 K, 8 port */
#define PCI_DEVICE_920_2 0x0034 /* XR-Plus 920 K, 2 port */
-
-#define PCIPORT sc->vmem[0x200000]
Index: sys/dev/digi/digireg.h
===================================================================
RCS file: /usr/ncvs/src/sys/dev/digi/digireg.h,v
retrieving revision 1.3
diff -u -r1.3 digireg.h
--- sys/dev/digi/digireg.h 7 Aug 2003 15:04:24 -0000 1.3
+++ sys/dev/digi/digireg.h 20 Jun 2011 22:43:51 -0000
@@ -46,39 +46,39 @@
volatile u_short tcjmp;
volatile u_short fil1;
volatile u_short rpjmp;
-
+
volatile u_short tseg;
volatile u_short tin;
volatile u_short tout;
volatile u_short tmax;
-
+
volatile u_short rseg;
volatile u_short rin;
volatile u_short rout;
volatile u_short rmax;
-
+
volatile u_short tlow;
volatile u_short rlow;
volatile u_short rhigh;
volatile u_short incr;
-
+
volatile u_short dev;
volatile u_short edelay;
volatile u_short blen;
volatile u_short btime;
-
+
volatile u_short iflag;
volatile u_short oflag;
volatile u_short cflag;
volatile u_short gmask;
-
+
volatile u_short col;
volatile u_short delay;
volatile u_short imask;
volatile u_short tflush;
volatile u_char _1[16];
-
+
volatile u_char num;
volatile u_char ract;
volatile u_char bstat;
@@ -87,7 +87,7 @@
volatile u_char ilow;
volatile u_char idata;
volatile u_char eflag;
-
+
volatile u_char tflag;
volatile u_char rflag;
volatile u_char xmask;
@@ -112,7 +112,7 @@
volatile u_char _2;
volatile u_char _3[28];
-};
+};
#define SRXLWATER 0xe0
#define SRXHWATER 0xe1
Index: sys/modules/Makefile
===================================================================
RCS file: /usr/ncvs/src/sys/modules/Makefile,v
retrieving revision 1.681
diff -u -r1.681 Makefile
--- sys/modules/Makefile 19 Jun 2011 22:08:55 -0000 1.681
+++ sys/modules/Makefile 20 Jun 2011 22:45:29 -0000
@@ -78,6 +78,7 @@
dcons \
dcons_crom \
de \
+ digi \
${_dpms} \
${_dpt} \
${_drm} \
Index: sys/modules/digi/digi/Makefile
===================================================================
RCS file: /usr/ncvs/src/sys/modules/digi/digi/Makefile,v
retrieving revision 1.6
diff -u -r1.6 Makefile
--- sys/modules/digi/digi/Makefile 1 Sep 2008 23:59:00 -0000 1.6
+++ sys/modules/digi/digi/Makefile 20 Jun 2011 22:45:45 -0000
@@ -2,7 +2,8 @@
.PATH: ${.CURDIR}/../../../dev/digi
KMOD= digi
-SRCS= digi.c digi_pci.c digi_isa.c
+SRCS= digi.c digi_pci.c
+#SRCS+= digi_isa.c
SRCS+= digi.h digi_pci.h digireg.h digi_mod.h
SRCS+= bus_if.h pci_if.h device_if.h
SRCS+= opt_compat.h
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list