digi.c revision 132806
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 132806 2004-07-28 21:06:13Z 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 6276195Sbrian 6376195Sbrian#define CTRL_DEV 0x800000 6476195Sbrian#define CALLOUT_MASK 0x400000 6576195Sbrian#define CONTROL_INIT_STATE 0x100000 6676195Sbrian#define CONTROL_LOCK_STATE 0x200000 6776195Sbrian#define CONTROL_MASK (CTRL_DEV|CONTROL_INIT_STATE|CONTROL_LOCK_STATE) 6876195Sbrian#define UNIT_MASK 0x030000 6976195Sbrian#define PORT_MASK 0x0000FF 7076195Sbrian#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) 7176195Sbrian#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) 7276195Sbrian#define MINOR_TO_UNIT(mynor) (((mynor) & UNIT_MASK)>>16) 7376195Sbrian#define MINOR_TO_PORT(mynor) ((mynor) & PORT_MASK) 7476195Sbrian 7576195Sbrianstatic d_open_t digiopen; 7676195Sbrianstatic d_close_t digiclose; 7776195Sbrianstatic d_read_t digiread; 7876195Sbrianstatic d_write_t digiwrite; 7976195Sbrianstatic d_ioctl_t digiioctl; 8076195Sbrian 8176195Sbrianstatic void digistop(struct tty *tp, int rw); 82131373Sphkstatic void digibreak(struct tty *tp, int brk); 83131095Sphkstatic int digimodem(struct tty *tp, int sigon, int sigoff); 8476195Sbrianstatic void digi_poll(void *ptr); 8576195Sbrianstatic void digi_freemoduledata(struct digi_softc *); 8676195Sbrianstatic void fepcmd(struct digi_p *port, int cmd, int op, int ncmds); 8776195Sbrianstatic void digistart(struct tty *tp); 8876195Sbrianstatic int digiparam(struct tty *tp, struct termios *t); 8976195Sbrianstatic void digihardclose(struct digi_p *port); 9076195Sbrianstatic void digi_intr(void *); 9176195Sbrianstatic int digi_init(struct digi_softc *_sc); 9276195Sbrianstatic int digi_loadmoduledata(struct digi_softc *); 9376195Sbrianstatic int digi_inuse(struct digi_softc *); 9476195Sbrianstatic void digi_free_state(struct digi_softc *); 9576195Sbrian 9676195Sbrian#define fepcmd_b(port, cmd, op1, op2, ncmds) \ 9776195Sbrian fepcmd(port, cmd, (op2 << 8) | op1, ncmds) 9876195Sbrian#define fepcmd_w fepcmd 9976195Sbrian 10076195Sbrian 10176195Sbrianstatic speed_t digidefaultrate = TTYDEF_SPEED; 10276195Sbrian 10376195Sbrianstruct con_bios { 10476195Sbrian struct con_bios *next; 10576195Sbrian u_char *bios; 10676195Sbrian size_t size; 10776195Sbrian}; 10876195Sbrian 10989062Smsmithstatic struct con_bios *con_bios_list; 11091445Speterdevclass_t digi_devclass; 11176195Sbrianstatic char driver_name[] = "digi"; 11276195Sbrianunsigned digi_debug = 0; 11376195Sbrian 11476195Sbrianstatic struct speedtab digispeedtab[] = { 11576195Sbrian { 0, 0}, /* old (sysV-like) Bx codes */ 11676195Sbrian { 50, 1}, 11776195Sbrian { 75, 2}, 11876195Sbrian { 110, 3}, 11976195Sbrian { 134, 4}, 12076195Sbrian { 150, 5}, 12176195Sbrian { 200, 6}, 12276195Sbrian { 300, 7}, 12376195Sbrian { 600, 8}, 12476195Sbrian { 1200, 9}, 12576195Sbrian { 1800, 10}, 12676195Sbrian { 2400, 11}, 12776195Sbrian { 4800, 12}, 12876195Sbrian { 9600, 13}, 12976195Sbrian { 19200, 14}, 13076195Sbrian { 38400, 15}, 13176195Sbrian { 57600, (02000 | 1)}, 13276195Sbrian { 76800, (02000 | 2)}, 13376195Sbrian { 115200, (02000 | 3)}, 13476195Sbrian { 230400, (02000 | 6)}, 13576195Sbrian { -1, -1} 13676195Sbrian}; 13776195Sbrian 13876195Sbrianconst struct digi_control_signals digi_xixe_signals = { 13976195Sbrian 0x02, 0x08, 0x10, 0x20, 0x40, 0x80 14076195Sbrian}; 14176195Sbrian 14276195Sbrianconst struct digi_control_signals digi_normal_signals = { 14376195Sbrian 0x02, 0x80, 0x20, 0x10, 0x40, 0x01 14476195Sbrian}; 14576195Sbrian 14676195Sbrianstatic struct cdevsw digi_sw = { 147126080Sphk .d_version = D_VERSION, 148111815Sphk .d_open = digiopen, 149111815Sphk .d_close = digiclose, 150111815Sphk .d_read = digiread, 151111815Sphk .d_write = digiwrite, 152111815Sphk .d_ioctl = digiioctl, 153111815Sphk .d_name = driver_name, 154126080Sphk .d_flags = D_TTY | D_NEEDGIANT, 15576195Sbrian}; 15676195Sbrian 15776195Sbrianstatic void 15876195Sbriandigi_poll(void *ptr) 15976195Sbrian{ 16076195Sbrian struct digi_softc *sc; 16176195Sbrian 16276195Sbrian sc = (struct digi_softc *)ptr; 16376195Sbrian callout_handle_init(&sc->callout); 16476195Sbrian digi_intr(sc); 16576195Sbrian sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1); 16676195Sbrian} 16776195Sbrian 16876195Sbrianstatic void 16976195Sbriandigi_int_test(void *v) 17076195Sbrian{ 17176195Sbrian struct digi_softc *sc = v; 17276195Sbrian 17376195Sbrian callout_handle_init(&sc->inttest); 17476195Sbrian#ifdef DIGI_INTERRUPT 17576195Sbrian if (sc->intr_timestamp.tv_sec || sc->intr_timestamp.tv_usec) { 17676195Sbrian /* interrupt OK! */ 17776195Sbrian return; 17876195Sbrian } 17976195Sbrian log(LOG_ERR, "digi%d: Interrupt didn't work, use polled mode\n", unit); 18076195Sbrian#endif 18176195Sbrian sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1); 18276195Sbrian} 18376195Sbrian 18476195Sbrianstatic void 18576195Sbriandigi_freemoduledata(struct digi_softc *sc) 18676195Sbrian{ 18776195Sbrian if (sc->fep.data != NULL) { 18876195Sbrian free(sc->fep.data, M_TTYS); 18976195Sbrian sc->fep.data = NULL; 19076195Sbrian } 19176195Sbrian if (sc->link.data != NULL) { 19276195Sbrian free(sc->link.data, M_TTYS); 19376195Sbrian sc->link.data = NULL; 19476195Sbrian } 19576195Sbrian if (sc->bios.data != NULL) { 19676195Sbrian free(sc->bios.data, M_TTYS); 19776195Sbrian sc->bios.data = NULL; 19876195Sbrian } 19976195Sbrian} 20076195Sbrian 20176195Sbrianstatic int 20276195Sbriandigi_bcopy(const void *vfrom, void *vto, size_t sz) 20376195Sbrian{ 20476195Sbrian volatile const char *from = (volatile const char *)vfrom; 20576195Sbrian volatile char *to = (volatile char *)vto; 20676195Sbrian size_t i; 20776195Sbrian 20876195Sbrian for (i = 0; i < sz; i++) 20976195Sbrian *to++ = *from++; 21076195Sbrian 21176195Sbrian from = (const volatile char *)vfrom; 21276195Sbrian to = (volatile char *)vto; 21376195Sbrian for (i = 0; i < sz; i++) 21476195Sbrian if (*to++ != *from++) 21576195Sbrian return (0); 21676195Sbrian return (1); 21776195Sbrian} 21876195Sbrian 21994358Sbrianvoid 22094361Sbriandigi_delay(struct digi_softc *sc, const char *txt, u_long timo) 22194340Sbrian{ 22294340Sbrian if (cold) 22394361Sbrian DELAY(timo * 1000000 / hz); 22494340Sbrian else 22594361Sbrian tsleep(sc, PUSER | PCATCH, txt, timo); 22694340Sbrian} 22794340Sbrian 22876195Sbrianstatic int 22976195Sbriandigi_init(struct digi_softc *sc) 23076195Sbrian{ 23176195Sbrian int i, cnt, resp; 23276195Sbrian u_char *ptr; 23376195Sbrian int lowwater; 23476195Sbrian struct digi_p *port; 23576195Sbrian volatile struct board_chan *bc; 23676195Sbrian 23776195Sbrian ptr = NULL; 23876195Sbrian 23976195Sbrian if (sc->status == DIGI_STATUS_DISABLED) { 24076195Sbrian log(LOG_ERR, "digi%d: Cannot init a disabled card\n", 24176195Sbrian sc->res.unit); 24276195Sbrian return (EIO); 24376195Sbrian } 24476195Sbrian if (sc->bios.data == NULL) { 24576195Sbrian log(LOG_ERR, "digi%d: Cannot init without BIOS\n", 24676195Sbrian sc->res.unit); 24776195Sbrian return (EIO); 24876195Sbrian } 24976195Sbrian#if 0 25076195Sbrian if (sc->link.data == NULL && sc->model >= PCCX) { 25176195Sbrian log(LOG_ERR, "digi%d: Cannot init without link info\n", 25276195Sbrian sc->res.unit); 25376195Sbrian return (EIO); 25476195Sbrian } 25576195Sbrian#endif 25676195Sbrian if (sc->fep.data == NULL) { 25776195Sbrian log(LOG_ERR, "digi%d: Cannot init without fep code\n", 25876195Sbrian sc->res.unit); 25976195Sbrian return (EIO); 26076195Sbrian } 26176195Sbrian sc->status = DIGI_STATUS_NOTINIT; 26276195Sbrian 26376195Sbrian if (sc->numports) { 26476195Sbrian /* 26576195Sbrian * We're re-initialising - maybe because someone's attached 26676195Sbrian * another port module. For now, we just re-initialise 26776195Sbrian * everything. 26876195Sbrian */ 26976195Sbrian if (digi_inuse(sc)) 27076195Sbrian return (EBUSY); 27176195Sbrian 27276195Sbrian digi_free_state(sc); 27376195Sbrian } 27476195Sbrian 27576195Sbrian ptr = sc->setwin(sc, MISCGLOBAL); 27676195Sbrian for (i = 0; i < 16; i += 2) 27776195Sbrian vW(ptr + i) = 0; 27876195Sbrian 27976195Sbrian switch (sc->model) { 28076195Sbrian case PCXEVE: 28176195Sbrian outb(sc->wport, 0xff); /* window 7 */ 28276195Sbrian ptr = sc->vmem + (BIOSCODE & 0x1fff); 28376195Sbrian 28476195Sbrian if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) { 28576195Sbrian device_printf(sc->dev, "BIOS upload failed\n"); 28676195Sbrian return (EIO); 28776195Sbrian } 28876195Sbrian 28976195Sbrian outb(sc->port, FEPCLR); 29076195Sbrian break; 29176195Sbrian 29276195Sbrian case PCXE: 29376195Sbrian case PCXI: 29476195Sbrian case PCCX: 29576195Sbrian ptr = sc->setwin(sc, BIOSCODE + ((0xf000 - sc->mem_seg) << 4)); 29676195Sbrian if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) { 29776195Sbrian device_printf(sc->dev, "BIOS upload failed\n"); 29876195Sbrian return (EIO); 29976195Sbrian } 30076195Sbrian break; 30176195Sbrian 30276195Sbrian case PCXEM: 30376195Sbrian case PCIEPCX: 30476195Sbrian case PCIXR: 30594323Sbrian if (sc->pcibus) 30676195Sbrian PCIPORT = FEPRST; 30776195Sbrian else 30876195Sbrian outb(sc->port, FEPRST | FEPMEM); 30976195Sbrian 31094323Sbrian for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) & 31176195Sbrian FEPMASK) != FEPRST; i++) { 31294949Sbrian if (i > hz) { 31394323Sbrian log(LOG_ERR, "digi%d: %s init reset failed\n", 31494323Sbrian sc->res.unit, sc->name); 31576195Sbrian return (EIO); 31676195Sbrian } 31794361Sbrian digi_delay(sc, "digiinit0", 5); 31876195Sbrian } 31976195Sbrian DLOG(DIGIDB_INIT, (sc->dev, "Got init reset after %d us\n", i)); 32076195Sbrian 32176195Sbrian /* Now upload the BIOS */ 32276195Sbrian cnt = (sc->bios.size < sc->win_size - BIOSOFFSET) ? 32376195Sbrian sc->bios.size : sc->win_size - BIOSOFFSET; 32476195Sbrian 32576195Sbrian ptr = sc->setwin(sc, BIOSOFFSET); 32676195Sbrian if (!digi_bcopy(sc->bios.data, ptr, cnt)) { 32776195Sbrian device_printf(sc->dev, "BIOS upload (1) failed\n"); 32876195Sbrian return (EIO); 32976195Sbrian } 33076195Sbrian 33176195Sbrian if (cnt != sc->bios.size) { 33276195Sbrian /* and the second part */ 33376195Sbrian ptr = sc->setwin(sc, sc->win_size); 33476195Sbrian if (!digi_bcopy(sc->bios.data + cnt, ptr, 33576195Sbrian sc->bios.size - cnt)) { 33676195Sbrian device_printf(sc->dev, "BIOS upload failed\n"); 33776195Sbrian return (EIO); 33876195Sbrian } 33976195Sbrian } 34076195Sbrian 34176195Sbrian ptr = sc->setwin(sc, 0); 34276195Sbrian vW(ptr + 0) = 0x0401; 34376195Sbrian vW(ptr + 2) = 0x0bf0; 34476195Sbrian vW(ptr + 4) = 0x0000; 34576195Sbrian vW(ptr + 6) = 0x0000; 34676195Sbrian 34776195Sbrian break; 34876195Sbrian } 34976195Sbrian 35076195Sbrian DLOG(DIGIDB_INIT, (sc->dev, "BIOS uploaded\n")); 35176195Sbrian 35276195Sbrian ptr = sc->setwin(sc, MISCGLOBAL); 35376195Sbrian W(ptr) = 0; 35476195Sbrian 35594323Sbrian if (sc->pcibus) { 35676195Sbrian PCIPORT = FEPCLR; 35776195Sbrian resp = FEPRST; 35876195Sbrian } else if (sc->model == PCXEVE) { 35976195Sbrian outb(sc->port, FEPCLR); 36076195Sbrian resp = FEPRST; 36176195Sbrian } else { 36276195Sbrian outb(sc->port, FEPCLR | FEPMEM); 36376195Sbrian resp = FEPRST | FEPMEM; 36476195Sbrian } 36576195Sbrian 36694323Sbrian for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) & FEPMASK) 36776195Sbrian == resp; i++) { 36894949Sbrian if (i > hz) { 36976195Sbrian log(LOG_ERR, "digi%d: BIOS start failed\n", 37076195Sbrian sc->res.unit); 37176195Sbrian return (EIO); 37276195Sbrian } 37394361Sbrian digi_delay(sc, "digibios0", 5); 37476195Sbrian } 37576195Sbrian 37676195Sbrian DLOG(DIGIDB_INIT, (sc->dev, "BIOS started after %d us\n", i)); 37776195Sbrian 37876195Sbrian for (i = 0; vW(ptr) != *(u_short *)"GD"; i++) { 37994949Sbrian if (i > 2*hz) { 38076195Sbrian log(LOG_ERR, "digi%d: BIOS boot failed " 38176195Sbrian "(0x%02x != 0x%02x)\n", 38276195Sbrian sc->res.unit, vW(ptr), *(u_short *)"GD"); 38376195Sbrian return (EIO); 38476195Sbrian } 38594361Sbrian digi_delay(sc, "digibios1", 5); 38676195Sbrian } 38776195Sbrian 38876195Sbrian DLOG(DIGIDB_INIT, (sc->dev, "BIOS booted after %d iterations\n", i)); 38976195Sbrian 39076195Sbrian if (sc->link.data != NULL) { 39176195Sbrian DLOG(DIGIDB_INIT, (sc->dev, "Loading link data\n")); 39276195Sbrian ptr = sc->setwin(sc, 0xcd0); 39376195Sbrian digi_bcopy(sc->link.data, ptr, 21); /* XXX 21 ? */ 39476195Sbrian } 39576195Sbrian 39676195Sbrian /* load FEP/OS */ 39776195Sbrian 39876195Sbrian switch (sc->model) { 39976195Sbrian case PCXE: 40076195Sbrian case PCXEVE: 40176195Sbrian case PCXI: 40276195Sbrian ptr = sc->setwin(sc, sc->model == PCXI ? 0x2000 : 0x0); 40376195Sbrian digi_bcopy(sc->fep.data, ptr, sc->fep.size); 40476195Sbrian 40576195Sbrian /* A BIOS request to move our data to 0x2000 */ 40676195Sbrian ptr = sc->setwin(sc, MBOX); 40776195Sbrian vW(ptr + 0) = 2; 40876195Sbrian vW(ptr + 2) = sc->mem_seg + FEPCODESEG; 40976195Sbrian vW(ptr + 4) = 0; 41076195Sbrian vW(ptr + 6) = FEPCODESEG; 41176195Sbrian vW(ptr + 8) = 0; 41276195Sbrian vW(ptr + 10) = sc->fep.size; 41376195Sbrian 41476195Sbrian /* Run the BIOS request */ 41576195Sbrian outb(sc->port, FEPREQ | FEPMEM); 41676195Sbrian outb(sc->port, FEPCLR | FEPMEM); 41776195Sbrian 41876195Sbrian for (i = 0; W(ptr); i++) { 41994949Sbrian if (i > hz) { 42076195Sbrian log(LOG_ERR, "digi%d: FEP/OS move failed\n", 42176195Sbrian sc->res.unit); 42276195Sbrian sc->hidewin(sc); 42376195Sbrian return (EIO); 42476195Sbrian } 42594361Sbrian digi_delay(sc, "digifep0", 5); 42676195Sbrian } 42776195Sbrian DLOG(DIGIDB_INIT, 42876195Sbrian (sc->dev, "FEP/OS moved after %d iterations\n", i)); 42976195Sbrian 43076195Sbrian /* Clear the confirm word */ 43176195Sbrian ptr = sc->setwin(sc, FEPSTAT); 43276195Sbrian vW(ptr + 0) = 0; 43376195Sbrian 43476195Sbrian /* A BIOS request to execute the FEP/OS */ 43576195Sbrian ptr = sc->setwin(sc, MBOX); 43676195Sbrian vW(ptr + 0) = 0x01; 43776195Sbrian vW(ptr + 2) = FEPCODESEG; 43876195Sbrian vW(ptr + 4) = 0x04; 43976195Sbrian 44076195Sbrian /* Run the BIOS request */ 44176195Sbrian outb(sc->port, FEPREQ); 44276195Sbrian outb(sc->port, FEPCLR); 44376195Sbrian 44476195Sbrian ptr = sc->setwin(sc, FEPSTAT); 44576195Sbrian 44676195Sbrian break; 44776195Sbrian 44876195Sbrian case PCXEM: 44976195Sbrian case PCIEPCX: 45076195Sbrian case PCIXR: 45176195Sbrian DLOG(DIGIDB_INIT, (sc->dev, "Loading FEP/OS\n")); 45276195Sbrian 45376195Sbrian cnt = (sc->fep.size < sc->win_size - BIOSOFFSET) ? 45476195Sbrian sc->fep.size : sc->win_size - BIOSOFFSET; 45576195Sbrian 45676195Sbrian ptr = sc->setwin(sc, BIOSOFFSET); 45776195Sbrian digi_bcopy(sc->fep.data, ptr, cnt); 45876195Sbrian 45976195Sbrian if (cnt != sc->fep.size) { 46076195Sbrian ptr = sc->setwin(sc, BIOSOFFSET + cnt); 46176195Sbrian digi_bcopy(sc->fep.data + cnt, ptr, 46276195Sbrian sc->fep.size - cnt); 46376195Sbrian } 46476195Sbrian 46576195Sbrian DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS loaded\n")); 46676195Sbrian 46776195Sbrian ptr = sc->setwin(sc, 0xc30); 46876195Sbrian W(ptr + 4) = 0x1004; 46976195Sbrian W(ptr + 6) = 0xbfc0; 47076195Sbrian W(ptr + 0) = 0x03; 47176195Sbrian W(ptr + 2) = 0x00; 47276195Sbrian 47376195Sbrian /* Clear the confirm word */ 47476195Sbrian ptr = sc->setwin(sc, FEPSTAT); 47576195Sbrian W(ptr + 0) = 0; 47676195Sbrian 47776195Sbrian if (sc->port) 47876195Sbrian outb(sc->port, 0); /* XXX necessary ? */ 47976195Sbrian 48076195Sbrian break; 48176195Sbrian 48276195Sbrian case PCCX: 48376195Sbrian ptr = sc->setwin(sc, 0xd000); 48476195Sbrian digi_bcopy(sc->fep.data, ptr, sc->fep.size); 48576195Sbrian 48676195Sbrian /* A BIOS request to execute the FEP/OS */ 48776195Sbrian ptr = sc->setwin(sc, 0xc40); 48876195Sbrian W(ptr + 0) = 1; 48976195Sbrian W(ptr + 2) = FEPCODE >> 4; 49076195Sbrian W(ptr + 4) = 4; 49176195Sbrian 49276195Sbrian /* Clear the confirm word */ 49376195Sbrian ptr = sc->setwin(sc, FEPSTAT); 49476195Sbrian W(ptr + 0) = 0; 49576195Sbrian 49676195Sbrian /* Run the BIOS request */ 49776195Sbrian outb(sc->port, FEPREQ | FEPMEM); /* send interrupt to BIOS */ 49876195Sbrian outb(sc->port, FEPCLR | FEPMEM); 49976195Sbrian break; 50076195Sbrian } 50176195Sbrian 50276195Sbrian /* Now wait 'till the FEP/OS has booted */ 50376195Sbrian for (i = 0; vW(ptr) != *(u_short *)"OS"; i++) { 50494949Sbrian if (i > 2*hz) { 50576195Sbrian log(LOG_ERR, "digi%d: FEP/OS start failed " 50676195Sbrian "(0x%02x != 0x%02x)\n", 50776195Sbrian sc->res.unit, vW(ptr), *(u_short *)"OS"); 50876195Sbrian sc->hidewin(sc); 50976195Sbrian return (EIO); 51076195Sbrian } 51194361Sbrian digi_delay(sc, "digifep1", 5); 51276195Sbrian } 51376195Sbrian 51476195Sbrian DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS started after %d iterations\n", i)); 51576195Sbrian 51676195Sbrian if (sc->model >= PCXEM) { 51776195Sbrian ptr = sc->setwin(sc, 0xe04); 51876195Sbrian vW(ptr) = 2; 51976195Sbrian ptr = sc->setwin(sc, 0xc02); 52076195Sbrian sc->numports = vW(ptr); 52176195Sbrian } else { 52276195Sbrian ptr = sc->setwin(sc, 0xc22); 52376195Sbrian sc->numports = vW(ptr); 52476195Sbrian } 52576195Sbrian 52676195Sbrian if (sc->numports == 0) { 52776195Sbrian device_printf(sc->dev, "%s, 0 ports found\n", sc->name); 52876195Sbrian sc->hidewin(sc); 52976195Sbrian return (0); 53076195Sbrian } 53176195Sbrian 53276195Sbrian if (sc->numports > 256) { 53376195Sbrian /* Our minor numbering scheme is broken for more than 256 */ 53476195Sbrian device_printf(sc->dev, "%s, 256 ports (%d ports found)\n", 53576195Sbrian sc->name, sc->numports); 53676195Sbrian sc->numports = 256; 53776195Sbrian } else 53876195Sbrian device_printf(sc->dev, "%s, %d ports found\n", sc->name, 53976195Sbrian sc->numports); 54076195Sbrian 54176195Sbrian if (sc->ports) 54276195Sbrian free(sc->ports, M_TTYS); 54377004Sbrian sc->ports = malloc(sizeof(struct digi_p) * sc->numports, 544111119Simp M_TTYS, M_WAITOK | M_ZERO); 54576195Sbrian 54676195Sbrian if (sc->ttys) 54776195Sbrian free(sc->ttys, M_TTYS); 54877004Sbrian sc->ttys = malloc(sizeof(struct tty) * sc->numports, 549111119Simp M_TTYS, M_WAITOK | M_ZERO); 55076195Sbrian 55176195Sbrian /* 55276195Sbrian * XXX Should read port 0xc90 for an array of 2byte values, 1 per 55376195Sbrian * port. If the value is 0, the port is broken.... 55476195Sbrian */ 55576195Sbrian 55676195Sbrian ptr = sc->setwin(sc, 0); 55776195Sbrian 55876195Sbrian /* We should now init per-port structures */ 55976195Sbrian bc = (volatile struct board_chan *)(ptr + CHANSTRUCT); 56076195Sbrian sc->gdata = (volatile struct global_data *)(ptr + FEP_GLOBAL); 56176195Sbrian 56276195Sbrian sc->memcmd = ptr + sc->gdata->cstart; 56376195Sbrian sc->memevent = ptr + sc->gdata->istart; 56476195Sbrian 56576195Sbrian for (i = 0; i < sc->numports; i++, bc++) { 56676195Sbrian port = sc->ports + i; 56776195Sbrian port->pnum = i; 56876195Sbrian port->sc = sc; 56976195Sbrian port->status = ENABLED; 57076195Sbrian port->tp = sc->ttys + i; 57176195Sbrian port->bc = bc; 57276195Sbrian 57376195Sbrian if (sc->model == PCXEVE) { 57476195Sbrian port->txbuf = ptr + 57576195Sbrian (((bc->tseg - sc->mem_seg) << 4) & 0x1fff); 57676195Sbrian port->rxbuf = ptr + 57776195Sbrian (((bc->rseg - sc->mem_seg) << 4) & 0x1fff); 57876195Sbrian port->txwin = FEPWIN | ((bc->tseg - sc->mem_seg) >> 9); 57976195Sbrian port->rxwin = FEPWIN | ((bc->rseg - sc->mem_seg) >> 9); 58076195Sbrian } else if (sc->model == PCXI || sc->model == PCXE) { 58176195Sbrian port->txbuf = ptr + ((bc->tseg - sc->mem_seg) << 4); 58276195Sbrian port->rxbuf = ptr + ((bc->rseg - sc->mem_seg) << 4); 58376195Sbrian port->txwin = port->rxwin = 0; 58476195Sbrian } else { 58576195Sbrian port->txbuf = ptr + 58676195Sbrian (((bc->tseg - sc->mem_seg) << 4) % sc->win_size); 58776195Sbrian port->rxbuf = ptr + 58876195Sbrian (((bc->rseg - sc->mem_seg) << 4) % sc->win_size); 58976195Sbrian port->txwin = FEPWIN | 59076195Sbrian (((bc->tseg - sc->mem_seg) << 4) / sc->win_size); 59176195Sbrian port->rxwin = FEPWIN | 59276195Sbrian (((bc->rseg - sc->mem_seg) << 4) / sc->win_size); 59376195Sbrian } 59476195Sbrian port->txbufsize = bc->tmax + 1; 59576195Sbrian port->rxbufsize = bc->rmax + 1; 59676195Sbrian 59776195Sbrian lowwater = port->txbufsize >> 2; 59876195Sbrian if (lowwater > 1024) 59976195Sbrian lowwater = 1024; 60076195Sbrian sc->setwin(sc, 0); 60176195Sbrian fepcmd_w(port, STXLWATER, lowwater, 10); 60276195Sbrian fepcmd_w(port, SRXLWATER, port->rxbufsize >> 2, 10); 60376195Sbrian fepcmd_w(port, SRXHWATER, (3 * port->rxbufsize) >> 2, 10); 60476195Sbrian 60576195Sbrian bc->edelay = 100; 60676195Sbrian 60776195Sbrian /* 60876195Sbrian * We don't use all the flags from <sys/ttydefaults.h> since 60976195Sbrian * they are only relevant for logins. It's important to have 61076195Sbrian * echo off initially so that the line doesn't start blathering 61176195Sbrian * before the echo flag can be turned off. 61276195Sbrian */ 61376195Sbrian port->it_in.c_iflag = 0; 61476195Sbrian port->it_in.c_oflag = 0; 61576195Sbrian port->it_in.c_cflag = TTYDEF_CFLAG; 61676195Sbrian port->it_in.c_lflag = 0; 61776195Sbrian termioschars(&port->it_in); 61876195Sbrian port->it_in.c_ispeed = port->it_in.c_ospeed = digidefaultrate; 61976195Sbrian port->it_out = port->it_in; 62076195Sbrian port->send_ring = 1; /* Default action on signal RI */ 62176195Sbrian 62276195Sbrian port->dev[0] = make_dev(&digi_sw, (sc->res.unit << 16) + i, 62376195Sbrian UID_ROOT, GID_WHEEL, 0600, "ttyD%d.%d", sc->res.unit, i); 62476195Sbrian port->dev[1] = make_dev(&digi_sw, ((sc->res.unit << 16) + i) | 62576195Sbrian CONTROL_INIT_STATE, UID_ROOT, GID_WHEEL, 62676195Sbrian 0600, "ttyiD%d.%d", sc->res.unit, i); 62776195Sbrian port->dev[2] = make_dev(&digi_sw, ((sc->res.unit << 16) + i) | 62876195Sbrian CONTROL_LOCK_STATE, UID_ROOT, GID_WHEEL, 62976195Sbrian 0600, "ttylD%d.%d", sc->res.unit, i); 63076195Sbrian port->dev[3] = make_dev(&digi_sw, ((sc->res.unit << 16) + i) | 63176195Sbrian CALLOUT_MASK, UID_UUCP, GID_DIALER, 63276195Sbrian 0660, "cuaD%d.%d", sc->res.unit, i); 63376195Sbrian port->dev[4] = make_dev(&digi_sw, ((sc->res.unit << 16) + i) | 63476195Sbrian CALLOUT_MASK | CONTROL_INIT_STATE, UID_UUCP, GID_DIALER, 63576195Sbrian 0660, "cuaiD%d.%d", sc->res.unit, i); 63676195Sbrian port->dev[5] = make_dev(&digi_sw, ((sc->res.unit << 16) + i) | 63776195Sbrian CALLOUT_MASK | CONTROL_LOCK_STATE, UID_UUCP, GID_DIALER, 63876195Sbrian 0660, "cualD%d.%d", sc->res.unit, i); 63976195Sbrian } 64076195Sbrian 64176195Sbrian sc->hidewin(sc); 64276195Sbrian sc->inttest = timeout(digi_int_test, sc, hz); 64376195Sbrian /* fepcmd_w(&sc->ports[0], 0xff, 0, 0); */ 64476195Sbrian sc->status = DIGI_STATUS_ENABLED; 64576195Sbrian 64676195Sbrian return (0); 64776195Sbrian} 64876195Sbrian 64976195Sbrianstatic int 650131095Sphkdigimodem(struct tty *tp, int sigon, int sigoff) 65176195Sbrian{ 652131095Sphk struct digi_softc *sc; 653131095Sphk struct digi_p *port; 654131095Sphk int mynor, unit, pnum; 655131095Sphk int bitand, bitor, mstat; 65676195Sbrian 657131095Sphk mynor = minor(tp->t_dev); 658131095Sphk unit = MINOR_TO_UNIT(mynor); 659131095Sphk pnum = MINOR_TO_PORT(mynor); 660131095Sphk 661131095Sphk sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); 662131095Sphk port = &sc->ports[pnum]; 663131095Sphk 664131095Sphk if (sigon == 0 && sigoff == 0) { 66576195Sbrian port->sc->setwin(port->sc, 0); 66676195Sbrian mstat = port->bc->mstat; 66776195Sbrian port->sc->hidewin(port->sc); 66876195Sbrian if (mstat & port->sc->csigs->rts) 669131095Sphk sigon |= SER_RTS; 67078496Sbrian if (mstat & port->cd) 671131095Sphk sigon |= SER_DCD; 67278496Sbrian if (mstat & port->dsr) 673131095Sphk sigon |= SER_DSR; 67476195Sbrian if (mstat & port->sc->csigs->cts) 675131095Sphk sigon |= SER_CTS; 67676195Sbrian if (mstat & port->sc->csigs->ri) 677131095Sphk sigon |= SER_RI; 67876195Sbrian if (mstat & port->sc->csigs->dtr) 679131095Sphk sigon |= SER_DTR; 680131095Sphk return (sigon); 68176195Sbrian } 68276195Sbrian 683131095Sphk bitand = 0; 684131095Sphk bitor = 0; 68576195Sbrian 686131095Sphk if (sigoff & SER_DTR) 687131095Sphk bitand |= port->sc->csigs->dtr; 688131095Sphk if (sigoff & SER_RTS) 689131095Sphk bitand |= port->sc->csigs->rts; 690131095Sphk if (sigon & SER_DTR) 691131095Sphk bitor |= port->sc->csigs->dtr; 692131095Sphk if (sigon & SER_RTS) 693131095Sphk bitor |= port->sc->csigs->rts; 694131095Sphk fepcmd_b(port, SETMODEM, bitor, ~bitand, 0); 69576195Sbrian return (0); 69676195Sbrian} 69776195Sbrian 698104094Sphkstatic int 699130585Sphkdigiopen(struct cdev *dev, int flag, int mode, struct thread *td) 70076195Sbrian{ 70176195Sbrian struct digi_softc *sc; 70276195Sbrian struct tty *tp; 70376195Sbrian int unit; 70476195Sbrian int pnum; 70576195Sbrian struct digi_p *port; 70676195Sbrian int s; 70776195Sbrian int error, mynor; 70876195Sbrian volatile struct board_chan *bc; 70976195Sbrian 71076195Sbrian error = 0; 71176195Sbrian mynor = minor(dev); 71276195Sbrian unit = MINOR_TO_UNIT(minor(dev)); 71376195Sbrian pnum = MINOR_TO_PORT(minor(dev)); 71476195Sbrian 71576195Sbrian sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); 71676195Sbrian if (!sc) 71776195Sbrian return (ENXIO); 71876195Sbrian 71976195Sbrian if (sc->status != DIGI_STATUS_ENABLED) { 72076195Sbrian DLOG(DIGIDB_OPEN, (sc->dev, "Cannot open a disabled card\n")); 72176195Sbrian return (ENXIO); 72276195Sbrian } 72376195Sbrian if (pnum >= sc->numports) { 72476195Sbrian DLOG(DIGIDB_OPEN, (sc->dev, "port%d: Doesn't exist\n", pnum)); 72576195Sbrian return (ENXIO); 72676195Sbrian } 72776195Sbrian if (mynor & (CTRL_DEV | CONTROL_MASK)) { 72876195Sbrian sc->opencnt++; 72976195Sbrian return (0); 73076195Sbrian } 73176195Sbrian port = &sc->ports[pnum]; 73276195Sbrian tp = dev->si_tty = port->tp; 73376195Sbrian bc = port->bc; 73476195Sbrian 73576195Sbrian s = spltty(); 73676195Sbrian 73776195Sbrianopen_top: 73876195Sbrian while (port->status & DIGI_DTR_OFF) { 73976195Sbrian port->wopeners++; 740131981Sphk error = tsleep(&tp->t_dtr_wait, TTIPRI | PCATCH, "digidtr", 0); 74176195Sbrian port->wopeners--; 74276195Sbrian if (error) 74376195Sbrian goto out; 74476195Sbrian } 74576195Sbrian 74676195Sbrian if (tp->t_state & TS_ISOPEN) { 74776195Sbrian /* 74876195Sbrian * The device is open, so everything has been initialized. 74976195Sbrian * Handle conflicts. 75076195Sbrian */ 75176195Sbrian if (mynor & CALLOUT_MASK) { 75276195Sbrian if (!port->active_out) { 75376195Sbrian error = EBUSY; 75476195Sbrian DLOG(DIGIDB_OPEN, (sc->dev, "port %d:" 75576195Sbrian " BUSY error = %d\n", pnum, error)); 75676195Sbrian goto out; 75776195Sbrian } 75876195Sbrian } else if (port->active_out) { 75976195Sbrian if (flag & O_NONBLOCK) { 76076195Sbrian error = EBUSY; 76176195Sbrian DLOG(DIGIDB_OPEN, (sc->dev, 76276195Sbrian "port %d: BUSY error = %d\n", pnum, error)); 76376195Sbrian goto out; 76476195Sbrian } 76576195Sbrian port->wopeners++; 76676195Sbrian error = tsleep(&port->active_out, TTIPRI | PCATCH, 76776195Sbrian "digibi", 0); 76876195Sbrian port->wopeners--; 76976195Sbrian if (error != 0) { 77076195Sbrian DLOG(DIGIDB_OPEN, (sc->dev, 77176195Sbrian "port %d: tsleep(digibi) error = %d\n", 77276195Sbrian pnum, error)); 77376195Sbrian goto out; 77476195Sbrian } 77576195Sbrian goto open_top; 77676195Sbrian } 77793593Sjhb if (tp->t_state & TS_XCLUDE && suser(td) != 0) { 77876195Sbrian error = EBUSY; 77976195Sbrian goto out; 78076195Sbrian } 78176195Sbrian } else { 78276195Sbrian /* 78376195Sbrian * The device isn't open, so there are no conflicts. 78476195Sbrian * Initialize it. Initialization is done twice in many 78576195Sbrian * cases: to preempt sleeping callin opens if we are callout, 78676195Sbrian * and to complete a callin open after DCD rises. 78776195Sbrian */ 78876195Sbrian tp->t_oproc = digistart; 78976195Sbrian tp->t_param = digiparam; 790131095Sphk tp->t_modem = digimodem; 791131095Sphk tp->t_break = digibreak; 79276195Sbrian tp->t_stop = digistop; 79376195Sbrian tp->t_dev = dev; 79476195Sbrian tp->t_termios = (mynor & CALLOUT_MASK) ? 79576195Sbrian port->it_out : port->it_in; 79676195Sbrian sc->setwin(sc, 0); 79776195Sbrian 79876195Sbrian bc->rout = bc->rin; /* clear input queue */ 79976195Sbrian bc->idata = 1; 80076195Sbrian bc->iempty = 1; 80176195Sbrian bc->ilow = 1; 80278496Sbrian bc->mint = port->cd | port->sc->csigs->ri; 80376195Sbrian bc->tin = bc->tout; 80478496Sbrian if (port->ialtpin) { 80578496Sbrian port->cd = sc->csigs->dsr; 80678496Sbrian port->dsr = sc->csigs->cd; 80778496Sbrian } else { 80878496Sbrian port->cd = sc->csigs->cd; 80978496Sbrian port->dsr = sc->csigs->dsr; 81078496Sbrian } 81176195Sbrian port->wopeners++; /* XXX required ? */ 81276195Sbrian error = digiparam(tp, &tp->t_termios); 81376195Sbrian port->wopeners--; 81476195Sbrian 81576195Sbrian if (error != 0) { 81676195Sbrian DLOG(DIGIDB_OPEN, (sc->dev, 81776195Sbrian "port %d: cxpparam error = %d\n", pnum, error)); 81876195Sbrian goto out; 81976195Sbrian } 82076195Sbrian ttsetwater(tp); 82176195Sbrian 82276195Sbrian /* handle fake and initial DCD for callout devices */ 82376195Sbrian 82478496Sbrian if (bc->mstat & port->cd || mynor & CALLOUT_MASK) 825130077Sphk ttyld_modem(tp, 1); 82676195Sbrian } 82776195Sbrian 82876195Sbrian /* Wait for DCD if necessary */ 82976195Sbrian if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && 83076195Sbrian !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { 83176195Sbrian port->wopeners++; 83276195Sbrian error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "digidcd", 0); 83376195Sbrian port->wopeners--; 83476195Sbrian if (error != 0) { 83576195Sbrian DLOG(DIGIDB_OPEN, (sc->dev, 83676195Sbrian "port %d: tsleep(digidcd) error = %d\n", 83776195Sbrian pnum, error)); 83876195Sbrian goto out; 83976195Sbrian } 84076195Sbrian goto open_top; 84176195Sbrian } 842130077Sphk error = ttyld_open(tp, dev); 84376195Sbrian DLOG(DIGIDB_OPEN, (sc->dev, "port %d: l_open error = %d\n", 84476195Sbrian pnum, error)); 84576195Sbrian 846130096Sphk ttyldoptim(tp); 84776195Sbrian 84876195Sbrian if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) 84976195Sbrian port->active_out = TRUE; 85076195Sbrian 85176195Sbrian if (tp->t_state & TS_ISOPEN) 85276195Sbrian sc->opencnt++; 85376195Sbrianout: 85476195Sbrian splx(s); 85576195Sbrian 85676195Sbrian if (!(tp->t_state & TS_ISOPEN)) 85776195Sbrian digihardclose(port); 85876195Sbrian 85976195Sbrian DLOG(DIGIDB_OPEN, (sc->dev, "port %d: open() returns %d\n", 86076195Sbrian pnum, error)); 86176195Sbrian 86276195Sbrian return (error); 86376195Sbrian} 86476195Sbrian 865104094Sphkstatic int 866130585Sphkdigiclose(struct cdev *dev, int flag, int mode, struct thread *td) 86776195Sbrian{ 86876195Sbrian int mynor; 86976195Sbrian struct tty *tp; 87076195Sbrian int unit, pnum; 87176195Sbrian struct digi_softc *sc; 87276195Sbrian struct digi_p *port; 87376195Sbrian int s; 87476195Sbrian 87576195Sbrian mynor = minor(dev); 87676195Sbrian unit = MINOR_TO_UNIT(mynor); 87776195Sbrian pnum = MINOR_TO_PORT(mynor); 87876195Sbrian 87976195Sbrian sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); 88076195Sbrian KASSERT(sc, ("digi%d: softc not allocated in digiclose\n", unit)); 88176195Sbrian 88276195Sbrian if (mynor & (CTRL_DEV | CONTROL_MASK)) { 88376195Sbrian sc->opencnt--; 88476195Sbrian return (0); 88576195Sbrian } 88676195Sbrian 88776195Sbrian port = sc->ports + pnum; 88876195Sbrian tp = port->tp; 88976195Sbrian 89076195Sbrian DLOG(DIGIDB_CLOSE, (sc->dev, "port %d: closing\n", pnum)); 89176195Sbrian 89276195Sbrian s = spltty(); 893130077Sphk ttyld_close(tp, flag); 894130096Sphk ttyldoptim(tp); 89576195Sbrian digihardclose(port); 896132226Sphk tty_close(tp); 89776195Sbrian if (--sc->opencnt == 0) 89876195Sbrian splx(s); 89976195Sbrian return (0); 90076195Sbrian} 90176195Sbrian 90276195Sbrianstatic void 90376195Sbriandigidtrwakeup(void *chan) 90476195Sbrian{ 90576195Sbrian struct digi_p *port = chan; 90676195Sbrian 90776195Sbrian port->status &= ~DIGI_DTR_OFF; 908131981Sphk wakeup(&port->tp->t_dtr_wait); 90976195Sbrian port->wopeners--; 91076195Sbrian} 91176195Sbrian 91276195Sbrianstatic void 91376195Sbriandigihardclose(struct digi_p *port) 91476195Sbrian{ 91576195Sbrian volatile struct board_chan *bc; 91676195Sbrian int s; 91776195Sbrian 91876195Sbrian bc = port->bc; 91976195Sbrian 92076195Sbrian s = spltty(); 92176195Sbrian port->sc->setwin(port->sc, 0); 92276195Sbrian bc->idata = 0; 92376195Sbrian bc->iempty = 0; 92476195Sbrian bc->ilow = 0; 92576195Sbrian bc->mint = 0; 92676195Sbrian if ((port->tp->t_cflag & HUPCL) || 92778496Sbrian (!port->active_out && !(bc->mstat & port->cd) && 92876195Sbrian !(port->it_in.c_cflag & CLOCAL)) || 92976195Sbrian !(port->tp->t_state & TS_ISOPEN)) { 930131095Sphk digimodem(port->tp, 0, SER_DTR | SER_RTS); 931131981Sphk if (port->tp->t_dtr_wait != 0) { 93276195Sbrian /* Schedule a wakeup of any callin devices */ 93376195Sbrian port->wopeners++; 934131981Sphk timeout(&digidtrwakeup, port, port->tp->t_dtr_wait); 93576195Sbrian port->status |= DIGI_DTR_OFF; 93676195Sbrian } 93776195Sbrian } 93876195Sbrian port->active_out = FALSE; 93976195Sbrian wakeup(&port->active_out); 94076195Sbrian wakeup(TSA_CARR_ON(port->tp)); 94176195Sbrian splx(s); 94276195Sbrian} 94376195Sbrian 944104094Sphkstatic int 945130585Sphkdigiread(struct cdev *dev, struct uio *uio, int flag) 94676195Sbrian{ 94776195Sbrian int mynor; 94876195Sbrian struct tty *tp; 94976195Sbrian int error, unit, pnum; 95076195Sbrian struct digi_softc *sc; 95176195Sbrian 95276195Sbrian mynor = minor(dev); 95376195Sbrian if (mynor & CONTROL_MASK) 95476195Sbrian return (ENODEV); 95576195Sbrian 95676195Sbrian unit = MINOR_TO_UNIT(mynor); 95776195Sbrian pnum = MINOR_TO_PORT(mynor); 95876195Sbrian 95976195Sbrian sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); 96076195Sbrian KASSERT(sc, ("digi%d: softc not allocated in digiclose\n", unit)); 96176195Sbrian tp = &sc->ttys[pnum]; 96276195Sbrian 963130077Sphk error = ttyld_read(tp, uio, flag); 96476195Sbrian DLOG(DIGIDB_READ, (sc->dev, "port %d: read() returns %d\n", 96576195Sbrian pnum, error)); 96676195Sbrian 96776195Sbrian return (error); 96876195Sbrian} 96976195Sbrian 970104094Sphkstatic int 971130585Sphkdigiwrite(struct cdev *dev, struct uio *uio, int flag) 97276195Sbrian{ 97376195Sbrian int mynor; 97476195Sbrian struct tty *tp; 97576195Sbrian int error, unit, pnum; 97676195Sbrian struct digi_softc *sc; 97776195Sbrian 97876195Sbrian mynor = minor(dev); 97976195Sbrian if (mynor & CONTROL_MASK) 98076195Sbrian return (ENODEV); 98176195Sbrian 98276195Sbrian unit = MINOR_TO_UNIT(mynor); 98376195Sbrian pnum = MINOR_TO_PORT(mynor); 98476195Sbrian 98576195Sbrian sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); 98676195Sbrian KASSERT(sc, ("digi%d: softc not allocated in digiclose\n", unit)); 98776195Sbrian tp = &sc->ttys[pnum]; 98876195Sbrian 989130077Sphk error = ttyld_write(tp, uio, flag); 99076195Sbrian DLOG(DIGIDB_WRITE, (sc->dev, "port %d: write() returns %d\n", 99176195Sbrian pnum, error)); 99276195Sbrian 99376195Sbrian return (error); 99476195Sbrian} 99576195Sbrian 99676195Sbrian/* 99776195Sbrian * Load module "digi_<mod>.ko" and look for a symbol called digi_mod_<mod>. 99876195Sbrian * 99976195Sbrian * Populate sc->bios, sc->fep, and sc->link from this data. 100076195Sbrian * 100176195Sbrian * sc->fep.data, sc->bios.data and sc->link.data are malloc()d according 100276195Sbrian * to their respective sizes. 100376195Sbrian * 100476195Sbrian * The module is unloaded when we're done. 100576195Sbrian */ 100676195Sbrianstatic int 100776195Sbriandigi_loadmoduledata(struct digi_softc *sc) 100876195Sbrian{ 100976195Sbrian struct digi_mod *digi_mod; 101076195Sbrian linker_file_t lf; 101176195Sbrian char *modfile, *sym; 101276195Sbrian caddr_t symptr; 101378414Sbrian int modlen, res; 101476195Sbrian 101576195Sbrian KASSERT(sc->bios.data == NULL, ("Uninitialised BIOS variable")); 101676195Sbrian KASSERT(sc->fep.data == NULL, ("Uninitialised FEP variable")); 101776195Sbrian KASSERT(sc->link.data == NULL, ("Uninitialised LINK variable")); 101876195Sbrian KASSERT(sc->module != NULL, ("Uninitialised module name")); 101976195Sbrian 102078414Sbrian modlen = strlen(sc->module); 1021111119Simp modfile = malloc(modlen + 6, M_TEMP, M_WAITOK); 102278414Sbrian snprintf(modfile, modlen + 6, "digi_%s", sc->module); 1023132806Sphk res = linker_reference_module(modfile, NULL, &lf); 1024132806Sphk if ((res = linker_reference_module(modfile, NULL, &lf)) != 0) 1025132806Sphk printf("%s: Failed %d to autoload module\n", modfile, res); 102676195Sbrian free(modfile, M_TEMP); 102776195Sbrian if (res != 0) 102876195Sbrian return (res); 102976195Sbrian 1030111119Simp sym = malloc(modlen + 10, M_TEMP, M_WAITOK); 103178414Sbrian snprintf(sym, modlen + 10, "digi_mod_%s", sc->module); 103276195Sbrian if ((symptr = linker_file_lookup_symbol(lf, sym, 0)) == NULL) 103376195Sbrian printf("digi_%s.ko: Symbol `%s' not found\n", sc->module, sym); 103476195Sbrian free(sym, M_TEMP); 103576195Sbrian 103676195Sbrian digi_mod = (struct digi_mod *)symptr; 103776195Sbrian if (digi_mod->dm_version != DIGI_MOD_VERSION) { 103876195Sbrian printf("digi_%s.ko: Invalid version %d (need %d)\n", 103976195Sbrian sc->module, digi_mod->dm_version, DIGI_MOD_VERSION); 1040132117Sphk linker_file_unload(lf, LINKER_UNLOAD_FORCE); 104176195Sbrian return (EINVAL); 104276195Sbrian } 104376195Sbrian 104476195Sbrian sc->bios.size = digi_mod->dm_bios.size; 104576195Sbrian if (sc->bios.size != 0 && digi_mod->dm_bios.data != NULL) { 1046111119Simp sc->bios.data = malloc(sc->bios.size, M_TTYS, M_WAITOK); 104776195Sbrian bcopy(digi_mod->dm_bios.data, sc->bios.data, sc->bios.size); 104876195Sbrian } 104976195Sbrian 105076195Sbrian sc->fep.size = digi_mod->dm_fep.size; 105176195Sbrian if (sc->fep.size != 0 && digi_mod->dm_fep.data != NULL) { 1052111119Simp sc->fep.data = malloc(sc->fep.size, M_TTYS, M_WAITOK); 105376195Sbrian bcopy(digi_mod->dm_fep.data, sc->fep.data, sc->fep.size); 105476195Sbrian } 105576195Sbrian 105676195Sbrian sc->link.size = digi_mod->dm_link.size; 105776195Sbrian if (sc->link.size != 0 && digi_mod->dm_link.data != NULL) { 1058111119Simp sc->link.data = malloc(sc->link.size, M_TTYS, M_WAITOK); 105976195Sbrian bcopy(digi_mod->dm_link.data, sc->link.data, sc->link.size); 106076195Sbrian } 106176195Sbrian 1062132117Sphk linker_file_unload(lf, LINKER_UNLOAD_FORCE); 106376195Sbrian 106476195Sbrian return (0); 106576195Sbrian} 106676195Sbrian 106776195Sbrianstatic int 1068130585Sphkdigiioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 106976195Sbrian{ 107076195Sbrian int unit, pnum, mynor, error, s; 107176195Sbrian struct digi_softc *sc; 107276195Sbrian struct digi_p *port; 107376195Sbrian struct tty *tp; 1074130892Sphk#ifndef BURN_BRIDGES 1075130344Sphk#if defined(COMPAT_43) 107676195Sbrian int oldcmd; 107776195Sbrian struct termios term; 107876195Sbrian#endif 1079130892Sphk#endif 108076195Sbrian 108176195Sbrian mynor = minor(dev); 108276195Sbrian unit = MINOR_TO_UNIT(mynor); 108376195Sbrian pnum = MINOR_TO_PORT(mynor); 108476195Sbrian 108576195Sbrian sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); 108676195Sbrian KASSERT(sc, ("digi%d: softc not allocated in digiioctl\n", unit)); 108776195Sbrian 108876195Sbrian if (sc->status == DIGI_STATUS_DISABLED) 108976195Sbrian return (ENXIO); 109076195Sbrian 109176195Sbrian if (mynor & CTRL_DEV) { 109276195Sbrian switch (cmd) { 109376195Sbrian case DIGIIO_DEBUG: 109476195Sbrian#ifdef DEBUG 109576195Sbrian digi_debug = *(int *)data; 109676195Sbrian return (0); 109776195Sbrian#else 109876195Sbrian device_printf(sc->dev, "DEBUG not defined\n"); 109976195Sbrian return (ENXIO); 110076195Sbrian#endif 110176195Sbrian case DIGIIO_REINIT: 110276195Sbrian digi_loadmoduledata(sc); 110376195Sbrian error = digi_init(sc); 110476195Sbrian digi_freemoduledata(sc); 110576195Sbrian return (error); 110676195Sbrian 110776195Sbrian case DIGIIO_MODEL: 110876705Sbrian *(enum digi_model *)data = sc->model; 110976195Sbrian return (0); 111076195Sbrian 111176195Sbrian case DIGIIO_IDENT: 111276195Sbrian return (copyout(sc->name, *(char **)data, 111376195Sbrian strlen(sc->name) + 1)); 111476195Sbrian } 111576195Sbrian } 111676195Sbrian 111776195Sbrian if (pnum >= sc->numports) 111876195Sbrian return (ENXIO); 111976195Sbrian 112076195Sbrian port = sc->ports + pnum; 112176195Sbrian if (!(port->status & ENABLED)) 112276195Sbrian return (ENXIO); 112376195Sbrian 112476195Sbrian tp = port->tp; 112576195Sbrian 112676195Sbrian if (mynor & CONTROL_MASK) { 112776195Sbrian struct termios *ct; 112876195Sbrian 112976195Sbrian switch (mynor & CONTROL_MASK) { 113076195Sbrian case CONTROL_INIT_STATE: 113176195Sbrian ct = (mynor & CALLOUT_MASK) ? 113276195Sbrian &port->it_out : &port->it_in; 113376195Sbrian break; 113476195Sbrian case CONTROL_LOCK_STATE: 113576195Sbrian ct = (mynor & CALLOUT_MASK) ? 113676195Sbrian &port->lt_out : &port->lt_in; 113776195Sbrian break; 113876195Sbrian default: 113976195Sbrian return (ENODEV); /* /dev/nodev */ 114076195Sbrian } 114176195Sbrian 114276195Sbrian switch (cmd) { 114376195Sbrian case TIOCSETA: 114493593Sjhb error = suser(td); 114576195Sbrian if (error != 0) 114676195Sbrian return (error); 114776195Sbrian *ct = *(struct termios *)data; 114878496Sbrian return (0); 114976195Sbrian 115076195Sbrian case TIOCGETA: 115176195Sbrian *(struct termios *)data = *ct; 115278496Sbrian return (0); 115376195Sbrian 115476195Sbrian case TIOCGETD: 115576195Sbrian *(int *)data = TTYDISC; 115676195Sbrian return (0); 115778496Sbrian 115876195Sbrian case TIOCGWINSZ: 115976195Sbrian bzero(data, sizeof(struct winsize)); 116076195Sbrian return (0); 116178496Sbrian 116278496Sbrian case DIGIIO_GETALTPIN: 116378496Sbrian switch (mynor & CONTROL_MASK) { 116478496Sbrian case CONTROL_INIT_STATE: 116578496Sbrian *(int *)data = port->ialtpin; 116678496Sbrian break; 116778496Sbrian 116878496Sbrian case CONTROL_LOCK_STATE: 116978496Sbrian *(int *)data = port->laltpin; 117078496Sbrian break; 117178496Sbrian 117278496Sbrian default: 117378496Sbrian panic("Confusion when re-testing minor"); 117478496Sbrian return (ENODEV); 117578496Sbrian } 117678496Sbrian return (0); 117778496Sbrian 117878496Sbrian case DIGIIO_SETALTPIN: 117978496Sbrian switch (mynor & CONTROL_MASK) { 118078496Sbrian case CONTROL_INIT_STATE: 118178496Sbrian if (!port->laltpin) { 118278496Sbrian port->ialtpin = !!*(int *)data; 118378496Sbrian DLOG(DIGIDB_SET, (sc->dev, 118478496Sbrian "port%d: initial ALTPIN %s\n", pnum, 118578496Sbrian port->ialtpin ? "set" : "cleared")); 118678496Sbrian } 118778496Sbrian break; 118878496Sbrian 118978496Sbrian case CONTROL_LOCK_STATE: 119078496Sbrian port->laltpin = !!*(int *)data; 119178496Sbrian DLOG(DIGIDB_SET, (sc->dev, 119278496Sbrian "port%d: ALTPIN %slocked\n", 119378496Sbrian pnum, port->laltpin ? "" : "un")); 119478496Sbrian break; 119578496Sbrian 119678496Sbrian default: 119778496Sbrian panic("Confusion when re-testing minor"); 119878496Sbrian return (ENODEV); 119978496Sbrian } 120078496Sbrian return (0); 120178496Sbrian 120276195Sbrian default: 120376195Sbrian return (ENOTTY); 120476195Sbrian } 120576195Sbrian } 120678496Sbrian 120778496Sbrian switch (cmd) { 120878496Sbrian case DIGIIO_GETALTPIN: 120978496Sbrian *(int *)data = !!(port->dsr == sc->csigs->cd); 121078496Sbrian return (0); 121178496Sbrian 121278496Sbrian case DIGIIO_SETALTPIN: 121378496Sbrian if (!port->laltpin) { 121478496Sbrian if (*(int *)data) { 121578496Sbrian DLOG(DIGIDB_SET, (sc->dev, 121678496Sbrian "port%d: ALTPIN set\n", pnum)); 121778496Sbrian port->cd = sc->csigs->dsr; 121878496Sbrian port->dsr = sc->csigs->cd; 121978496Sbrian } else { 122078496Sbrian DLOG(DIGIDB_SET, (sc->dev, 122178496Sbrian "port%d: ALTPIN cleared\n", pnum)); 122278496Sbrian port->cd = sc->csigs->cd; 122378496Sbrian port->dsr = sc->csigs->dsr; 122478496Sbrian } 122578496Sbrian } 122678496Sbrian return (0); 122778496Sbrian } 122878496Sbrian 122976195Sbrian tp = port->tp; 1230130892Sphk#ifndef BURN_BRIDGES 1231130344Sphk#if defined(COMPAT_43) 123276195Sbrian term = tp->t_termios; 123376195Sbrian oldcmd = cmd; 123476195Sbrian error = ttsetcompat(tp, &cmd, data, &term); 123576195Sbrian if (error != 0) 123676195Sbrian return (error); 123776195Sbrian if (cmd != oldcmd) 123876195Sbrian data = (caddr_t) & term; 123976195Sbrian#endif 1240130892Sphk#endif 124176195Sbrian if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { 124276195Sbrian int cc; 124376195Sbrian struct termios *dt; 124476195Sbrian struct termios *lt; 124576195Sbrian 124676195Sbrian dt = (struct termios *)data; 124776195Sbrian lt = (mynor & CALLOUT_MASK) ? &port->lt_out : &port->lt_in; 124876195Sbrian 124976195Sbrian dt->c_iflag = 125076195Sbrian (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); 125176195Sbrian dt->c_oflag = 125276195Sbrian (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); 125376195Sbrian dt->c_cflag = 125476195Sbrian (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); 125576195Sbrian dt->c_lflag = 125676195Sbrian (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); 125776195Sbrian port->c_iflag = dt->c_iflag & (IXOFF | IXON | IXANY); 125876195Sbrian dt->c_iflag &= ~(IXOFF | IXON | IXANY); 125976195Sbrian for (cc = 0; cc < NCCS; ++cc) 126076195Sbrian if (lt->c_cc[cc] != 0) 126176195Sbrian dt->c_cc[cc] = tp->t_cc[cc]; 126276195Sbrian if (lt->c_ispeed != 0) 126376195Sbrian dt->c_ispeed = tp->t_ispeed; 126476195Sbrian if (lt->c_ospeed != 0) 126576195Sbrian dt->c_ospeed = tp->t_ospeed; 126676195Sbrian } 1267130057Sphk error = ttyioctl(dev, cmd, data, flag, td); 126876195Sbrian if (error == 0 && cmd == TIOCGETA) 126976195Sbrian ((struct termios *)data)->c_iflag |= port->c_iflag; 1270130096Sphk ttyldoptim(tp); 1271130057Sphk if (error >= 0 && error != ENOTTY) 127276195Sbrian return (error); 127376195Sbrian s = spltty(); 127476195Sbrian sc->setwin(sc, 0); 127576195Sbrian switch (cmd) { 127676195Sbrian case DIGIIO_RING: 127776195Sbrian port->send_ring = *(u_char *)data; 127876195Sbrian break; 127976195Sbrian#ifdef DIGI_INTERRUPT 128076195Sbrian case TIOCTIMESTAMP: 128176195Sbrian *(struct timeval *)data = sc->intr_timestamp; 128276195Sbrian 128376195Sbrian break; 128476195Sbrian#endif 128576195Sbrian default: 128676195Sbrian splx(s); 128776195Sbrian return (ENOTTY); 128876195Sbrian } 128976195Sbrian splx(s); 129076195Sbrian return (0); 129176195Sbrian} 129276195Sbrian 1293131373Sphkstatic void 1294131095Sphkdigibreak(struct tty *tp, int brk) 1295131095Sphk{ 1296131095Sphk int mynor; 1297131095Sphk int unit; 1298131095Sphk int pnum; 1299131095Sphk struct digi_softc *sc; 1300131095Sphk struct digi_p *port; 1301131095Sphk 1302131095Sphk mynor = minor(tp->t_dev); 1303131095Sphk unit = MINOR_TO_UNIT(mynor); 1304131095Sphk pnum = MINOR_TO_PORT(mynor); 1305131095Sphk 1306131095Sphk sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); 1307131095Sphk KASSERT(sc, ("digi%d: softc not allocated in digiparam\n", unit)); 1308131095Sphk 1309131095Sphk port = &sc->ports[pnum]; 1310131095Sphk 1311131095Sphk /* 1312131095Sphk * now it sends 400 millisecond break because I don't know 1313131095Sphk * how to send an infinite break 1314131095Sphk */ 1315131095Sphk if (brk) 1316131095Sphk fepcmd_w(port, SENDBREAK, 400, 10); 1317131095Sphk} 1318131095Sphk 1319131095Sphkstatic int 132076195Sbriandigiparam(struct tty *tp, struct termios *t) 132176195Sbrian{ 132276195Sbrian int mynor; 132376195Sbrian int unit; 132476195Sbrian int pnum; 132576195Sbrian struct digi_softc *sc; 132676195Sbrian struct digi_p *port; 132776195Sbrian int cflag; 132876195Sbrian int iflag; 132976195Sbrian int hflow; 133076195Sbrian int s; 133176195Sbrian int window; 133276195Sbrian 133376195Sbrian mynor = minor(tp->t_dev); 133476195Sbrian unit = MINOR_TO_UNIT(mynor); 133576195Sbrian pnum = MINOR_TO_PORT(mynor); 133676195Sbrian 133776195Sbrian sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); 133876195Sbrian KASSERT(sc, ("digi%d: softc not allocated in digiparam\n", unit)); 133976195Sbrian 134076195Sbrian port = &sc->ports[pnum]; 134176195Sbrian 134276195Sbrian DLOG(DIGIDB_SET, (sc->dev, "port%d: setting parameters\n", pnum)); 134376195Sbrian 134476195Sbrian if (t->c_ispeed == 0) 134576195Sbrian t->c_ispeed = t->c_ospeed; 134676195Sbrian 134776195Sbrian cflag = ttspeedtab(t->c_ospeed, digispeedtab); 134876195Sbrian 134976195Sbrian if (cflag < 0 || (cflag > 0 && t->c_ispeed != t->c_ospeed)) 135076195Sbrian return (EINVAL); 135176195Sbrian 135276195Sbrian s = splclock(); 135376195Sbrian 135476195Sbrian window = sc->window; 135576195Sbrian sc->setwin(sc, 0); 135676195Sbrian 135776195Sbrian if (cflag == 0) { /* hangup */ 135876195Sbrian DLOG(DIGIDB_SET, (sc->dev, "port%d: hangup\n", pnum)); 1359131095Sphk digimodem(port->tp, 0, SER_DTR | SER_RTS); 136076195Sbrian } else { 1361131095Sphk digimodem(port->tp, SER_DTR | SER_RTS, 0); 136276195Sbrian 136376195Sbrian DLOG(DIGIDB_SET, (sc->dev, "port%d: CBAUD = %d\n", pnum, 136476195Sbrian cflag)); 136576195Sbrian 136676195Sbrian#if 0 136776195Sbrian /* convert flags to sysV-style values */ 136876195Sbrian if (t->c_cflag & PARODD) 136976195Sbrian cflag |= 0x0200; 137076195Sbrian if (t->c_cflag & PARENB) 137176195Sbrian cflag |= 0x0100; 137276195Sbrian if (t->c_cflag & CSTOPB) 137376195Sbrian cflag |= 0x0080; 137476195Sbrian#else 137576195Sbrian /* convert flags to sysV-style values */ 137676195Sbrian if (t->c_cflag & PARODD) 137776195Sbrian cflag |= FEP_PARODD; 137876195Sbrian if (t->c_cflag & PARENB) 137976195Sbrian cflag |= FEP_PARENB; 138076195Sbrian if (t->c_cflag & CSTOPB) 138176195Sbrian cflag |= FEP_CSTOPB; 138276195Sbrian if (t->c_cflag & CLOCAL) 138376195Sbrian cflag |= FEP_CLOCAL; 138476195Sbrian#endif 138576195Sbrian 138676195Sbrian cflag |= (t->c_cflag & CSIZE) >> 4; 138776195Sbrian DLOG(DIGIDB_SET, (sc->dev, "port%d: CFLAG = 0x%x\n", pnum, 138876195Sbrian cflag)); 138976195Sbrian fepcmd_w(port, SETCFLAGS, (unsigned)cflag, 0); 139076195Sbrian } 139176195Sbrian 139276195Sbrian iflag = 139376195Sbrian t->c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP); 139476195Sbrian if (port->c_iflag & IXON) 139576195Sbrian iflag |= 0x400; 139676195Sbrian if (port->c_iflag & IXANY) 139776195Sbrian iflag |= 0x800; 139876195Sbrian if (port->c_iflag & IXOFF) 139976195Sbrian iflag |= 0x1000; 140076195Sbrian 140176195Sbrian DLOG(DIGIDB_SET, (sc->dev, "port%d: set iflag = 0x%x\n", pnum, iflag)); 140276195Sbrian fepcmd_w(port, SETIFLAGS, (unsigned)iflag, 0); 140376195Sbrian 140476195Sbrian hflow = 0; 140576195Sbrian if (t->c_cflag & CDTR_IFLOW) 140676195Sbrian hflow |= sc->csigs->dtr; 140776195Sbrian if (t->c_cflag & CRTS_IFLOW) 140876195Sbrian hflow |= sc->csigs->rts; 140976195Sbrian if (t->c_cflag & CCTS_OFLOW) 141076195Sbrian hflow |= sc->csigs->cts; 141176195Sbrian if (t->c_cflag & CDSR_OFLOW) 141278496Sbrian hflow |= port->dsr; 141376195Sbrian if (t->c_cflag & CCAR_OFLOW) 141478496Sbrian hflow |= port->cd; 141576195Sbrian 141676195Sbrian DLOG(DIGIDB_SET, (sc->dev, "port%d: set hflow = 0x%x\n", pnum, hflow)); 141776195Sbrian fepcmd_w(port, SETHFLOW, 0xff00 | (unsigned)hflow, 0); 141876195Sbrian 141976195Sbrian DLOG(DIGIDB_SET, (sc->dev, "port%d: set startc(0x%x), stopc(0x%x)\n", 142076195Sbrian pnum, t->c_cc[VSTART], t->c_cc[VSTOP])); 142176195Sbrian fepcmd_b(port, SONOFFC, t->c_cc[VSTART], t->c_cc[VSTOP], 0); 142276195Sbrian 142376195Sbrian if (sc->window != 0) 142476195Sbrian sc->towin(sc, 0); 142576195Sbrian if (window != 0) 142676195Sbrian sc->towin(sc, window); 142776195Sbrian splx(s); 142876195Sbrian 142976195Sbrian return (0); 143076195Sbrian} 143176195Sbrian 143276195Sbrianstatic void 143376195Sbriandigi_intr(void *vp) 143476195Sbrian{ 143576195Sbrian struct digi_p *port; 143676195Sbrian char *cxcon; 143776195Sbrian struct digi_softc *sc; 143876195Sbrian int ehead, etail; 143976195Sbrian volatile struct board_chan *bc; 144076195Sbrian struct tty *tp; 144176195Sbrian int head, tail; 144276195Sbrian int wrapmask; 144376195Sbrian int size, window; 144476195Sbrian struct event { 144576195Sbrian u_char pnum; 144676195Sbrian u_char event; 144776195Sbrian u_char mstat; 144876195Sbrian u_char lstat; 144976195Sbrian } event; 145076195Sbrian 145176195Sbrian sc = vp; 145276195Sbrian 145376195Sbrian if (sc->status != DIGI_STATUS_ENABLED) { 145476195Sbrian DLOG(DIGIDB_IRQ, (sc->dev, "interrupt on disabled board !\n")); 145576195Sbrian return; 145676195Sbrian } 145776327Sbrian 145876195Sbrian#ifdef DIGI_INTERRUPT 145976195Sbrian microtime(&sc->intr_timestamp); 146076195Sbrian#endif 146176195Sbrian 146276195Sbrian window = sc->window; 146376195Sbrian sc->setwin(sc, 0); 146476195Sbrian 146576195Sbrian if (sc->model >= PCXEM && W(sc->vmem + 0xd00)) { 146676195Sbrian struct con_bios *con = con_bios_list; 146776195Sbrian register u_char *ptr; 146876195Sbrian 146976195Sbrian ptr = sc->vmem + W(sc->vmem + 0xd00); 147076195Sbrian while (con) { 147176195Sbrian if (ptr[1] && W(ptr + 2) == W(con->bios + 2)) 147276195Sbrian /* Not first block -- exact match */ 147376195Sbrian break; 147476195Sbrian 147576195Sbrian if (W(ptr + 4) >= W(con->bios + 4) && 147676195Sbrian W(ptr + 4) <= W(con->bios + 6)) 147776195Sbrian /* Initial search concetrator BIOS */ 147876195Sbrian break; 147976195Sbrian } 148076195Sbrian 148176195Sbrian if (con == NULL) { 148276195Sbrian log(LOG_ERR, "digi%d: wanted bios LREV = 0x%04x" 148376195Sbrian " not found!\n", sc->res.unit, W(ptr + 4)); 148476195Sbrian W(ptr + 10) = 0; 148576195Sbrian W(sc->vmem + 0xd00) = 0; 148676195Sbrian goto eoi; 148776195Sbrian } 148876195Sbrian cxcon = con->bios; 148976195Sbrian W(ptr + 4) = W(cxcon + 4); 149076195Sbrian W(ptr + 6) = W(cxcon + 6); 149176195Sbrian if (ptr[1] == 0) 149276195Sbrian W(ptr + 2) = W(cxcon + 2); 149376195Sbrian W(ptr + 8) = (ptr[1] << 6) + W(cxcon + 8); 149476195Sbrian size = W(cxcon + 10) - (ptr[1] << 10); 149576195Sbrian if (size <= 0) { 149676195Sbrian W(ptr + 8) = W(cxcon + 8); 149776195Sbrian W(ptr + 10) = 0; 149876195Sbrian } else { 149976195Sbrian if (size > 1024) 150076195Sbrian size = 1024; 150176195Sbrian W(ptr + 10) = size; 150276195Sbrian bcopy(cxcon + (ptr[1] << 10), ptr + 12, size); 150376195Sbrian } 150476195Sbrian W(sc->vmem + 0xd00) = 0; 150576195Sbrian goto eoi; 150676195Sbrian } 150776195Sbrian 150876195Sbrian ehead = sc->gdata->ein; 150976195Sbrian etail = sc->gdata->eout; 151076195Sbrian if (ehead == etail) { 151176195Sbrian#ifdef DEBUG 151276195Sbrian sc->intr_count++; 151376195Sbrian if (sc->intr_count % 6000 == 0) { 151476195Sbrian DLOG(DIGIDB_IRQ, (sc->dev, 151576195Sbrian "6000 useless polls %x %x\n", ehead, etail)); 151676195Sbrian sc->intr_count = 0; 151776195Sbrian } 151876195Sbrian#endif 151976195Sbrian goto eoi; 152076195Sbrian } 152176195Sbrian while (ehead != etail) { 152276195Sbrian event = *(volatile struct event *)(sc->memevent + etail); 152376195Sbrian 152476195Sbrian etail = (etail + 4) & sc->gdata->imax; 152576195Sbrian 152676195Sbrian if (event.pnum >= sc->numports) { 152776195Sbrian log(LOG_ERR, "digi%d: port %d: got event" 152876195Sbrian " on nonexisting port\n", sc->res.unit, 152976195Sbrian event.pnum); 153076195Sbrian continue; 153176195Sbrian } 153276195Sbrian port = &sc->ports[event.pnum]; 153376195Sbrian bc = port->bc; 153476195Sbrian tp = port->tp; 153576195Sbrian 153676195Sbrian if (!(tp->t_state & TS_ISOPEN) && !port->wopeners) { 153776195Sbrian DLOG(DIGIDB_IRQ, (sc->dev, 153876195Sbrian "port %d: event 0x%x on closed port\n", 153976195Sbrian event.pnum, event.event)); 154076195Sbrian bc->rout = bc->rin; 154176195Sbrian bc->idata = 0; 154276195Sbrian bc->iempty = 0; 154376195Sbrian bc->ilow = 0; 154476195Sbrian bc->mint = 0; 154576195Sbrian continue; 154676195Sbrian } 154776195Sbrian if (event.event & ~ALL_IND) 154876195Sbrian log(LOG_ERR, "digi%d: port%d: ? event 0x%x mstat 0x%x" 154976195Sbrian " lstat 0x%x\n", sc->res.unit, event.pnum, 155076195Sbrian event.event, event.mstat, event.lstat); 155176195Sbrian 155276195Sbrian if (event.event & DATA_IND) { 155376195Sbrian DLOG(DIGIDB_IRQ, (sc->dev, "port %d: DATA_IND\n", 155476195Sbrian event.pnum)); 155576195Sbrian wrapmask = port->rxbufsize - 1; 155676195Sbrian head = bc->rin; 155776195Sbrian tail = bc->rout; 155876195Sbrian 155976195Sbrian size = 0; 156076195Sbrian if (!(tp->t_state & TS_ISOPEN)) { 156176195Sbrian bc->rout = head; 156276195Sbrian goto end_of_data; 156376195Sbrian } 156476195Sbrian while (head != tail) { 156576195Sbrian int top; 156676195Sbrian 156776195Sbrian DLOG(DIGIDB_INT, (sc->dev, 156876195Sbrian "port %d: p rx head = %d tail = %d\n", 156976195Sbrian event.pnum, head, tail)); 157076195Sbrian top = (head > tail) ? head : wrapmask + 1; 157176195Sbrian sc->towin(sc, port->rxwin); 157276195Sbrian size = top - tail; 157376195Sbrian if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 157476195Sbrian size = b_to_q((char *)port->rxbuf + 157576195Sbrian tail, size, &tp->t_rawq); 157676195Sbrian tail = top - size; 157776195Sbrian ttwakeup(tp); 157876195Sbrian } else for (; tail < top;) { 1579130095Sphk ttyld_rint(tp, port->rxbuf[tail]); 158076195Sbrian sc->towin(sc, port->rxwin); 158176195Sbrian size--; 158276195Sbrian tail++; 158376195Sbrian if (tp->t_state & TS_TBLOCK) 158476195Sbrian break; 158576195Sbrian } 158676195Sbrian tail &= wrapmask; 158776195Sbrian sc->setwin(sc, 0); 158876195Sbrian bc->rout = tail; 158976195Sbrian head = bc->rin; 159076195Sbrian if (size) 159176195Sbrian break; 159276195Sbrian } 159376195Sbrian 159476195Sbrian if (bc->orun) { 159576195Sbrian CE_RECORD(port, CE_OVERRUN); 159676195Sbrian log(LOG_ERR, "digi%d: port%d: %s\n", 159776195Sbrian sc->res.unit, event.pnum, 159876195Sbrian digi_errortxt(CE_OVERRUN)); 159976195Sbrian bc->orun = 0; 160076195Sbrian } 160176195Sbrianend_of_data: 160276195Sbrian if (size) { 160376195Sbrian tp->t_state |= TS_TBLOCK; 160476195Sbrian port->status |= PAUSE_RX; 160576195Sbrian DLOG(DIGIDB_RX, (sc->dev, "port %d: pause RX\n", 160676195Sbrian event.pnum)); 160776195Sbrian } else { 160876195Sbrian bc->idata = 1; 160976195Sbrian } 161076195Sbrian } 161176195Sbrian 161276195Sbrian if (event.event & MODEMCHG_IND) { 161376195Sbrian DLOG(DIGIDB_MODEM, (sc->dev, "port %d: MODEMCHG_IND\n", 161476195Sbrian event.pnum)); 161576195Sbrian 161678496Sbrian if ((event.mstat ^ event.lstat) & port->cd) { 161776195Sbrian sc->hidewin(sc); 1618130095Sphk ttyld_modem(tp, event.mstat & port->cd); 161976195Sbrian sc->setwin(sc, 0); 162076195Sbrian wakeup(TSA_CARR_ON(tp)); 162176195Sbrian } 162276195Sbrian 162376195Sbrian if (event.mstat & sc->csigs->ri) { 162476195Sbrian DLOG(DIGIDB_RI, (sc->dev, "port %d: RING\n", 162576195Sbrian event.pnum)); 162676195Sbrian if (port->send_ring) { 1627130077Sphk ttyld_rint(tp, 'R'); 1628130077Sphk ttyld_rint(tp, 'I'); 1629130077Sphk ttyld_rint(tp, 'N'); 1630130077Sphk ttyld_rint(tp, 'G'); 1631130077Sphk ttyld_rint(tp, '\r'); 1632130077Sphk ttyld_rint(tp, '\n'); 163376195Sbrian } 163476195Sbrian } 163576195Sbrian } 163676195Sbrian if (event.event & BREAK_IND) { 163776195Sbrian DLOG(DIGIDB_MODEM, (sc->dev, "port %d: BREAK_IND\n", 163876195Sbrian event.pnum)); 1639130077Sphk ttyld_rint(tp, TTY_BI); 164076195Sbrian } 164176195Sbrian if (event.event & (LOWTX_IND | EMPTYTX_IND)) { 164276195Sbrian DLOG(DIGIDB_IRQ, (sc->dev, "port %d:%s%s\n", 164376195Sbrian event.pnum, 164476195Sbrian event.event & LOWTX_IND ? " LOWTX" : "", 164576195Sbrian event.event & EMPTYTX_IND ? " EMPTYTX" : "")); 1646130077Sphk ttyld_start(tp); 164776195Sbrian } 164876195Sbrian } 164976195Sbrian sc->gdata->eout = etail; 165076195Sbrianeoi: 165176195Sbrian if (sc->window != 0) 165276195Sbrian sc->towin(sc, 0); 165376195Sbrian if (window != 0) 165476195Sbrian sc->towin(sc, window); 165576195Sbrian} 165676195Sbrian 165776195Sbrianstatic void 165876195Sbriandigistart(struct tty *tp) 165976195Sbrian{ 166076195Sbrian int unit; 166176195Sbrian int pnum; 166276195Sbrian struct digi_p *port; 166376195Sbrian struct digi_softc *sc; 166476195Sbrian volatile struct board_chan *bc; 166576195Sbrian int head, tail; 166676195Sbrian int size, ocount, totcnt = 0; 166776195Sbrian int s; 166876195Sbrian int wmask; 166976195Sbrian 167076195Sbrian unit = MINOR_TO_UNIT(minor(tp->t_dev)); 167176195Sbrian pnum = MINOR_TO_PORT(minor(tp->t_dev)); 167276195Sbrian 167376195Sbrian sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); 167476195Sbrian KASSERT(sc, ("digi%d: softc not allocated in digistart\n", unit)); 167576195Sbrian 167676195Sbrian port = &sc->ports[pnum]; 167776195Sbrian bc = port->bc; 167876195Sbrian 167976195Sbrian wmask = port->txbufsize - 1; 168076195Sbrian 168176195Sbrian s = spltty(); 168276195Sbrian port->lcc = tp->t_outq.c_cc; 168376195Sbrian sc->setwin(sc, 0); 168476195Sbrian if (!(tp->t_state & TS_TBLOCK)) { 168576195Sbrian if (port->status & PAUSE_RX) { 168676195Sbrian DLOG(DIGIDB_RX, (sc->dev, "port %d: resume RX\n", 168776195Sbrian pnum)); 168876195Sbrian /* 168976195Sbrian * CAREFUL - braces are needed here if the DLOG is 169076195Sbrian * optimised out! 169176195Sbrian */ 169276195Sbrian } 169376195Sbrian port->status &= ~PAUSE_RX; 169476195Sbrian bc->idata = 1; 169576195Sbrian } 169676195Sbrian if (!(tp->t_state & TS_TTSTOP) && port->status & PAUSE_TX) { 169776195Sbrian DLOG(DIGIDB_TX, (sc->dev, "port %d: resume TX\n", pnum)); 169876195Sbrian port->status &= ~PAUSE_TX; 169976195Sbrian fepcmd_w(port, RESUMETX, 0, 10); 170076195Sbrian } 170176195Sbrian if (tp->t_outq.c_cc == 0) 170276195Sbrian tp->t_state &= ~TS_BUSY; 170376195Sbrian else 170476195Sbrian tp->t_state |= TS_BUSY; 170576195Sbrian 170676195Sbrian head = bc->tin; 170776195Sbrian while (tp->t_outq.c_cc != 0) { 170876195Sbrian tail = bc->tout; 170976195Sbrian DLOG(DIGIDB_INT, (sc->dev, "port%d: s tx head = %d tail = %d\n", 171076195Sbrian pnum, head, tail)); 171176195Sbrian 171276195Sbrian if (head < tail) 171376195Sbrian size = tail - head - 1; 171476195Sbrian else { 171576195Sbrian size = port->txbufsize - head; 171676195Sbrian if (tail == 0) 171776195Sbrian size--; 171876195Sbrian } 171976195Sbrian 172076195Sbrian if (size == 0) 172176195Sbrian break; 172276195Sbrian sc->towin(sc, port->txwin); 172376195Sbrian ocount = q_to_b(&tp->t_outq, port->txbuf + head, size); 172476195Sbrian totcnt += ocount; 172576195Sbrian head += ocount; 172676195Sbrian head &= wmask; 172776195Sbrian sc->setwin(sc, 0); 172876195Sbrian bc->tin = head; 172976195Sbrian bc->iempty = 1; 173076195Sbrian bc->ilow = 1; 173176195Sbrian } 173276195Sbrian port->lostcc = tp->t_outq.c_cc; 173376195Sbrian tail = bc->tout; 173476195Sbrian if (head < tail) 173576195Sbrian size = port->txbufsize - tail + head; 173676195Sbrian else 173776195Sbrian size = head - tail; 173876195Sbrian 173976195Sbrian port->lbuf = size; 174076195Sbrian DLOG(DIGIDB_INT, (sc->dev, "port%d: s total cnt = %d\n", pnum, totcnt)); 174176195Sbrian ttwwakeup(tp); 174276195Sbrian splx(s); 174376195Sbrian} 174476195Sbrian 174576195Sbrianstatic void 174676195Sbriandigistop(struct tty *tp, int rw) 174776195Sbrian{ 174876195Sbrian struct digi_softc *sc; 174976195Sbrian int unit; 175076195Sbrian int pnum; 175176195Sbrian struct digi_p *port; 175276195Sbrian 175376195Sbrian unit = MINOR_TO_UNIT(minor(tp->t_dev)); 175476195Sbrian pnum = MINOR_TO_PORT(minor(tp->t_dev)); 175576195Sbrian 175676195Sbrian sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); 175776195Sbrian KASSERT(sc, ("digi%d: softc not allocated in digistop\n", unit)); 175876195Sbrian port = sc->ports + pnum; 175976195Sbrian 176076195Sbrian DLOG(DIGIDB_TX, (sc->dev, "port %d: pause TX\n", pnum)); 176176195Sbrian port->status |= PAUSE_TX; 176276195Sbrian fepcmd_w(port, PAUSETX, 0, 10); 176376195Sbrian} 176476195Sbrian 176576195Sbrianstatic void 176676195Sbrianfepcmd(struct digi_p *port, int cmd, int op1, int ncmds) 176776195Sbrian{ 176876195Sbrian u_char *mem; 176976195Sbrian unsigned tail, head; 177076195Sbrian int count, n; 177176195Sbrian 177276195Sbrian mem = port->sc->memcmd; 177376195Sbrian 177476195Sbrian port->sc->setwin(port->sc, 0); 177576195Sbrian 177676195Sbrian head = port->sc->gdata->cin; 177776195Sbrian mem[head + 0] = cmd; 177876195Sbrian mem[head + 1] = port->pnum; 1779118607Sjhb *(u_short *)(mem + head + 2) = op1; 178076195Sbrian 178176195Sbrian head = (head + 4) & port->sc->gdata->cmax; 178276195Sbrian port->sc->gdata->cin = head; 178376195Sbrian 178476195Sbrian for (count = FEPTIMEOUT; count > 0; count--) { 178576195Sbrian head = port->sc->gdata->cin; 178676195Sbrian tail = port->sc->gdata->cout; 178776195Sbrian n = (head - tail) & port->sc->gdata->cmax; 178876195Sbrian 178976195Sbrian if (n <= ncmds * sizeof(short) * 4) 179076195Sbrian break; 179176195Sbrian } 179276195Sbrian if (count == 0) 179376195Sbrian log(LOG_ERR, "digi%d: port%d: timeout on FEP command\n", 179476195Sbrian port->sc->res.unit, port->pnum); 179576195Sbrian} 179676195Sbrian 179776195Sbrianconst char * 179876195Sbriandigi_errortxt(int id) 179976195Sbrian{ 180076195Sbrian static const char *error_desc[] = { 180176195Sbrian "silo overflow", 180276195Sbrian "interrupt-level buffer overflow", 180376195Sbrian "tty-level buffer overflow", 180476195Sbrian }; 180576195Sbrian 180676195Sbrian KASSERT(id >= 0 && id < sizeof(error_desc) / sizeof(error_desc[0]), 180776195Sbrian ("Unexpected digi error id %d\n", id)); 180876195Sbrian 180976195Sbrian return (error_desc[id]); 181076195Sbrian} 181176195Sbrian 181276195Sbrianint 181376195Sbriandigi_attach(struct digi_softc *sc) 181476195Sbrian{ 181576195Sbrian sc->res.ctldev = make_dev(&digi_sw, 181676195Sbrian (sc->res.unit << 16) | CTRL_DEV, UID_ROOT, GID_WHEEL, 181776195Sbrian 0600, "digi%r.ctl", sc->res.unit); 181876195Sbrian 181976195Sbrian digi_loadmoduledata(sc); 182076195Sbrian digi_init(sc); 182176195Sbrian digi_freemoduledata(sc); 182276195Sbrian 182376195Sbrian return (0); 182476195Sbrian} 182576195Sbrian 182676195Sbrianstatic int 182776195Sbriandigi_inuse(struct digi_softc *sc) 182876195Sbrian{ 182976195Sbrian int i; 183076195Sbrian 183176195Sbrian for (i = 0; i < sc->numports; i++) 183276195Sbrian if (sc->ttys[i].t_state & TS_ISOPEN) { 183376195Sbrian DLOG(DIGIDB_INIT, (sc->dev, "port%d: busy\n", i)); 183476195Sbrian return (1); 183576195Sbrian } else if (sc->ports[i].wopeners || sc->ports[i].opencnt) { 183676195Sbrian DLOG(DIGIDB_INIT, (sc->dev, "port%d: blocked in open\n", 183776195Sbrian i)); 183876195Sbrian return (1); 183976195Sbrian } 184076195Sbrian return (0); 184176195Sbrian} 184276195Sbrian 184376195Sbrianstatic void 184476195Sbriandigi_free_state(struct digi_softc *sc) 184576195Sbrian{ 184676195Sbrian int d, i; 184776195Sbrian 184876195Sbrian /* Blow it all away */ 184976195Sbrian 185076195Sbrian for (i = 0; i < sc->numports; i++) 185176195Sbrian for (d = 0; d < 6; d++) 185276195Sbrian destroy_dev(sc->ports[i].dev[d]); 185376195Sbrian 185476195Sbrian untimeout(digi_poll, sc, sc->callout); 185576195Sbrian callout_handle_init(&sc->callout); 185676195Sbrian untimeout(digi_int_test, sc, sc->inttest); 185776195Sbrian callout_handle_init(&sc->inttest); 185876195Sbrian 185976195Sbrian bus_teardown_intr(sc->dev, sc->res.irq, sc->res.irqHandler); 186076195Sbrian#ifdef DIGI_INTERRUPT 186176195Sbrian if (sc->res.irq != NULL) { 186276195Sbrian bus_release_resource(dev, SYS_RES_IRQ, sc->res.irqrid, 186376195Sbrian sc->res.irq); 186476195Sbrian sc->res.irq = NULL; 186576195Sbrian } 186676195Sbrian#endif 186776195Sbrian if (sc->numports) { 186876195Sbrian KASSERT(sc->ports, ("digi%d: Lost my ports ?", sc->res.unit)); 186976195Sbrian KASSERT(sc->ttys, ("digi%d: Lost my ttys ?", sc->res.unit)); 187077004Sbrian free(sc->ports, M_TTYS); 187176195Sbrian sc->ports = NULL; 187277004Sbrian free(sc->ttys, M_TTYS); 187376195Sbrian sc->ttys = NULL; 187476195Sbrian sc->numports = 0; 187576195Sbrian } 187676195Sbrian 187776195Sbrian sc->status = DIGI_STATUS_NOTINIT; 187876195Sbrian} 187976195Sbrian 188076195Sbrianint 188176195Sbriandigi_detach(device_t dev) 188276195Sbrian{ 188376195Sbrian struct digi_softc *sc = device_get_softc(dev); 188476195Sbrian 188576195Sbrian DLOG(DIGIDB_INIT, (sc->dev, "detaching\n")); 188676195Sbrian 188776195Sbrian /* If we're INIT'd, numports must be 0 */ 188876195Sbrian KASSERT(sc->numports == 0 || sc->status != DIGI_STATUS_NOTINIT, 188976195Sbrian ("digi%d: numports(%d) & status(%d) are out of sync", 189076195Sbrian sc->res.unit, sc->numports, (int)sc->status)); 189176195Sbrian 189276195Sbrian if (digi_inuse(sc)) 189376195Sbrian return (EBUSY); 189476195Sbrian 189576195Sbrian digi_free_state(sc); 189676195Sbrian 1897120461Sphk destroy_dev(sc->res.ctldev); 189876195Sbrian 189976195Sbrian if (sc->res.mem != NULL) { 190076195Sbrian bus_release_resource(dev, SYS_RES_MEMORY, sc->res.mrid, 190176195Sbrian sc->res.mem); 190276195Sbrian sc->res.mem = NULL; 190376195Sbrian } 190476195Sbrian if (sc->res.io != NULL) { 190576195Sbrian bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid, 190676195Sbrian sc->res.io); 190776195Sbrian sc->res.io = NULL; 190876195Sbrian } 190976195Sbrian 191076195Sbrian return (0); 191176195Sbrian} 191276195Sbrian 191376195Sbrianint 191476195Sbriandigi_shutdown(device_t dev) 191576195Sbrian{ 191676195Sbrian return (0); 191776195Sbrian} 191894320Sbrian 191994320SbrianMODULE_VERSION(digi, 1); 1920