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