From Peter.Jeremy@alcatel-lucent.com Tue Jun 21 04:47:41 2011 Return-Path: Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 61C43106566C for ; Tue, 21 Jun 2011 04:47:41 +0000 (UTC) (envelope-from Peter.Jeremy@alcatel-lucent.com) Received: from ihemail3.lucent.com (ihemail3.lucent.com [135.245.0.37]) by mx1.freebsd.org (Postfix) with ESMTP id 1E3028FC0A for ; Tue, 21 Jun 2011 04:47:40 +0000 (UTC) Received: from usnavsmail1.ndc.alcatel-lucent.com (usnavsmail1.ndc.alcatel-lucent.com [135.3.39.9]) by ihemail3.lucent.com (8.13.8/IER-o) with ESMTP id p5L4XsTT008191 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 20 Jun 2011 23:33:54 -0500 (CDT) Received: from unixmail.au.alcatel-lucent.com (unixmail.au.alcatel-lucent.com [139.188.42.130]) by usnavsmail1.ndc.alcatel-lucent.com (8.14.3/8.14.3/GMO) with ESMTP id p5L4Xn2o022818 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NOT) for ; Mon, 20 Jun 2011 23:33:52 -0500 Received: from insmb.au.alcatel-lucent.com (insmb.au.alcatel-lucent.com [139.188.42.184]) by unixmail.au.alcatel-lucent.com (8.13.8+Sun/8.13.3) with ESMTP id p5L4Xm1U024950 for ; Tue, 21 Jun 2011 14:33:48 +1000 (EST) Received: from pjdesk.au.alcatel-lucent.com (pjdesk.au.alcatel-lucent.com [139.188.2.2]) by insmb.au.alcatel-lucent.com (8.13.8+Sun/8.13.8) with ESMTP id p5L4QIJj015499 for ; Tue, 21 Jun 2011 14:26:18 +1000 (EST) Received: from pjdesk.au.alcatel-lucent.com (localhost [127.0.0.1]) by pjdesk.au.alcatel-lucent.com (8.14.4/8.14.4) with ESMTP id p5L4Q7dr089341; Tue, 21 Jun 2011 14:26:08 +1000 (EST) (envelope-from pjeremy@pjdesk.au.alcatel-lucent.com) Received: (from pjeremy@localhost) by pjdesk.au.alcatel-lucent.com (8.14.4/8.14.4/Submit) id p5L4Q7Qk089340; Tue, 21 Jun 2011 14:26:07 +1000 (EST) (envelope-from pjeremy) Message-Id: <201106210426.p5L4Q7Qk089340@pjdesk.au.alcatel-lucent.com> Date: Tue, 21 Jun 2011 14:26:07 +1000 (EST) From: Peter Jeremy Reply-To: Peter Jeremy To: FreeBSD-gnats-submit@freebsd.org Subject: [patch] Update digi(4) to work with TTYng X-Send-Pr-Version: 3.113 X-GNATS-Notify: >Number: 158086 >Category: kern >Synopsis: [digi] [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: Mon Jul 18 05:00:23 UTC 2011 >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@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 #include #include +#include #include #include #include #include -#include +#include +#include #include +#include #include #include #include @@ -59,25 +62,39 @@ #include #include -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_.ko" and look for a symbol called digi_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 #include +#include #include +#include #include #include #include @@ -54,6 +56,10 @@ #include #include +#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 #include +#include #include +#include #include #include #include @@ -49,7 +51,8 @@ #include #include -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: From: Peter Jeremy To: bug-followup@FreeBSD.org Cc: Subject: Re: kern/158086: [digi] [patch] Update digi(4) to work with TTYng Date: Fri, 1 Jul 2011 15:06:31 +1000 --lrvsYIebpInmECXG Content-Type: multipart/mixed; boundary="jt0yj30bxbg11sci" Content-Disposition: inline --jt0yj30bxbg11sci Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable I've discovered that my initial patch was still susceptable to interrupt storms (which has been an issue with digi(4) for a long time) and offer the attached patch. Note that this patch should be applied on top of the patch in the initial PR. I am happy to supply a single patch if anyone wants it but, given the size of the complete patch, this is more efficient. Note that I've also successfully tested multiple Ports/16em units on a single PCI/Xem card (which didn't work with the previous driver) as well as multiple PCI/Xem cards in one system. --=20 Peter Jeremy --jt0yj30bxbg11sci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="intr.fix" Content-Transfer-Encoding: quoted-printable --- sys/dev/digi/digi.c.158086 2011-06-21 08:43:51.000000000 +1000 +++ sys/dev/digi/digi.c 2011-07-01 14:48:04.000000000 +1000 @@ -1143,11 +1143,7 @@ =20 if (sc->status !=3D 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; + goto eoi2; } =20 #ifdef DIGI_INTERRUPT @@ -1330,16 +1326,18 @@ ehead =3D sc->gdata->ein; etail =3D sc->gdata->eout; } + eoi: - /* Ack any interrupt */ #ifdef DIGI_ISA - if (sc->pcibus) + digi_towin(sc, window); #endif - (void)sc->vmem[0x200002]; =20 +eoi2: + /* Ack any interrupt */ #ifdef DIGI_ISA - digi_towin(sc, window); + if (sc->pcibus) #endif + (void)sc->vmem[0x200002]; #ifdef DIGI_INTERRUPT if (!islocked) mtx_unlock(&sc->dg_mutex); --jt0yj30bxbg11sci-- --lrvsYIebpInmECXG Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.17 (FreeBSD) iEYEARECAAYFAk4NVdcACgkQ/opHv/APuIe9qwCeI3ZbMAMEE8f7niJnWWE0C/m2 P5IAnArb4nh2tK+kMJXNCImI4Vjaodle =+lSG -----END PGP SIGNATURE----- --lrvsYIebpInmECXG-- From: "David Boyd" To: , Cc: Subject: Re: kern/158086: [digi] [patch] Update digi(4) to work with TTYng Date: Sat, 9 Jul 2011 15:33:53 -0400 Per communications with Peter Jeremy and Gavin Atkinson: Applied patches kern/152254 and kern/158086 to 8.2-RELEASE(-p2) and 9.0-CURRENT. Testing of Digi Acceleport Xr (8-port) PCI went as follows: Modem (dial-out) no issues. Modem (dial-in) still waiting for digi hardware at remote end. (This may not happen). Serial console: 8.2-RELEASE(-p2) no issues. 9.0-CURRENT strange behavior with extended keypad keys PAGE-UP, PAGE-DOWN, INSERT and DELETE echos "~" character to display and exits dialog application. This behavior was reproducible on COM1 (uart) port, on SIIG (puc) ports and on USB (uplcom) port and is probably not a digi driver issue. The dialog application to reproduce this problem is simply: dialog --inputbox "Enter date?" 9 40 "$( date )" 2> /dev/null This is dialog on a 7.4-RELEASE system, not the new dialog in 9.0-CURRENT. David Boyd From: Peter Jeremy To: bug-followup@FreeBSD.org Cc: Subject: Re: kern/158086: [digi] [patch] Update digi(4) to work with TTYng Date: Mon, 18 Jul 2011 14:42:55 +1000 --brEuL7wsLY8+TuWz Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable I have been doing some testing, bidirectionally transferring all binary codes except xon and xoff between a pair of Digi ports and a pair of motherboard com ports at 115,200bps. (I'd like to test more ports but I could only liberate 2 suitable null modem cables). After transferring 10.8GB in each direction across each link (43GB total), I have a total of 14 corrupt bytes reported. All were received on the motherboard com ports and the 9 errors I've captured appear random. --=20 Peter Jeremy --brEuL7wsLY8+TuWz Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.17 (FreeBSD) iEYEARECAAYFAk4juc8ACgkQ/opHv/APuIep9gCfYDnbbjSzGoEDGhXu8bniJz9e ZbIAnjEVejDdWc0Hfjruu0Y/QEVa4SeL =fPkr -----END PGP SIGNATURE----- --brEuL7wsLY8+TuWz-- >Unformatted: