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