Inet-Admins mailing list archive (inet-admins@info.east.ru)
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[inet-admins] Re: digi FEP/OS start failed
- To: Sergey Samoyloff <gonza@techline.ru>
- Subject: [inet-admins] Re: digi FEP/OS start failed
- From: Slawa Olhovchenkov <slw@convey.ru>
- Date: Sun, 5 Mar 2000 03:12:38 +0300
- In-Reply-To: <200003041728.UAA29528@hq.techline.ru>
- References: <20000304201216.A13792@as.convey.ru> <200003041728.UAA29528@hq.techline.ru>
On Sat, Mar 04, 2000 at 08:28:13PM +0300, Sergey Samoyloff wrote:
> Hello!
>
> >> Слав сразу не заметил, оно на fepos стартануть не может, ругается что
> >> digi0: FEP/OS start failed: got 0000 wait 534f
> >Попробуй в строке 1413 заменить 2000 на 2000000
>
> взвисла
Ничего не понимаю. А это вариант?
--
Slawa Olhovchenkov
/*-
* Copyright (c) 2000 Slawa Olhovchenkov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: digi.c,v 1.5 2000/03/04 21:13:35 slw Exp $
*/
#include "pci.h"
#include "digi.h"
#include "opt_devfs.h"
#include "opt_compat.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/reboot.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/dkstat.h>
#include <sys/file.h>
#include <sys/uio.h>
#if __FreeBSD__ > 3
#include <sys/interrupt.h>
#endif
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/tty.h>
#include <sys/syslog.h>
#if defined(DEVFS) && __FreeBSD__ <= 3
#include <sys/devfsext.h>
#endif
#include <sys/types.h>
#include <sys/fcntl.h>
#include <machine/clock.h>
#if __FreeBSD__ <= 3
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
#include <i386/isa/icu.h>
#else
#include <sys/bus.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <sys/timepps.h>
#include <isa/isareg.h>
#include <isa/isavar.h>
#include <machine/resource.h>
#endif
#include <vm/vm.h>
#include <vm/pmap.h>
#include <i386/isa/digireg.h>
#include <machine/ipl.h>
#ifndef SMP
#include <machine/lock.h>
#endif
static const char rcsid[] = "$Id: digi.c,v 1.5 2000/03/04 21:13:35 slw Exp $";
#define DOWNLOAD_MASK 0x800000
#define CALLOUT_MASK 0x400000
#define CONTROL_INIT_STATE 0x100000
#define CONTROL_LOCK_STATE 0x200000
#define CONTROL_MASK (DOWNLOAD_MASK|CONTROL_INIT_STATE|CONTROL_LOCK_STATE)
#define UNIT_MASK 0x030000
#define PORT_MASK 0x0000FF
#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev)))
#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK)
#define MINOR_TO_UNIT(mynor) (((mynor) & UNIT_MASK)>>16)
#define MINOR_TO_PORT(mynor) ((mynor) & PORT_MASK)
#ifdef SMP
#define disable_intr() COM_DISABLE_INTR()
#define enable_intr() COM_ENABLE_INTR()
#endif /* SMP */
static char const * const error_desc[] = {
#define CE_OVERRUN 0
"silo overflow",
#define CE_INTERRUPT_BUF_OVERFLOW 1
"interrupt-level buffer overflow",
#define CE_TTY_BUF_OVERFLOW 2
"tty-level buffer overflow",
};
#define CE_NTYPES 3
#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum])
/* digiboard port structure */
struct digi_p {
int status;
#define DIGI_DTR_OFF 2
#define PAUSE_TX 8
#define PAUSE_RX 16
int opencnt;
ushort txbufsize;
ushort rxbufsize;
struct board_chan *bc;
struct tty *tp;
#if __FreeBSD__ <= 3 && defined(DEVFS)
void *devfs_token_ttyd,*devfs_token_ttyl,*devfs_token_ttyi,
*devfs_token_cuaa,*devfs_token_cual,*devfs_token_cuai;
#endif
u_char *txbuf;
u_char *rxbuf;
u_char txwin;
u_char rxwin;
u_char unit; /* board unit number */
u_char pnum; /* port number */
u_char modemfake; /* Modem values to be forced */
u_char mstat;
u_char modem; /* Force values */
u_char dsr;
u_char dcd;
int active_out; /* nonzero if the callout device is open */
int dtr_wait; /* time to hold DTR down on close (* 1/hz) */
u_int wopeners; /* # processes waiting for DCD in open() */
/*
* The high level of the driver never reads status registers directly
* because there would be too many side effects to handle conveniently.
* Instead, it reads copies of the registers stored here by the
* interrupt handler.
*/
u_char last_modem_status; /* last MSR read by intr handler */
u_char prev_modem_status; /* last MSR handled by high level */
/* Initial state. */
struct termios it_in; /* should be in struct tty */
struct termios it_out;
/* Lock state. */
struct termios lt_in; /* should be in struct tty */
struct termios lt_out;
u_int do_timestamp;
u_int do_dcd_timestamp;
struct timeval dcd_timestamp;
u_long bytes_in, bytes_out;
u_int delta_error_counts[CE_NTYPES];
u_long error_counts;
tcflag_t c_iflag; /* hold true IXON/IXOFF/IXANY */
int lcc,lostcc,lbuf;
u_char send_ring;
};
typedef struct {
unsigned int from, to;
} Bitmap;
Bitmap DigiConc[]={{0x01, TIOCM_DTR}, {0x02, TIOCM_RTS}, {0x10, TIOCM_CTS},
{0x20, TIOCM_DSR}, {0x40, TIOCM_RI}, {0x80, TIOCM_CD},
{0, 0}};
Bitmap DigiXi[]={{0x02, TIOCM_RTS}, {0x08, TIOCM_CD}, {0x10, TIOCM_DSR},
{0x20, TIOCM_CTS}, {0x40, TIOCM_RI}, {0x80, TIOCM_DTR},
{0, 0}};
/* Digiboard per-board structure */
struct digi_softc {
/* struct board_info */
u_char status; /* status: DISABLED/ENABLED */
#define NOTINIT 0
#define ENABLED 1
#define DISABLED 2
ushort numports,maxport; /* number of ports on card */
u_long port; /* I/O port */
ushort wport; /* window select I/O port */
u_char *vmem, *memcmd, *memevent; /* virtual memory address */
long pmem, psize; /* physical memory address */
u_char *bios,*fep,*link;
int s_bios,s_fep,s_link;
struct timeval intr_timestamp;
void (*intr_handler)(int unit);
struct digi_p *ports; /* pointer to array of port descriptors */
struct global_data *gdata;
u_char window; /* saved window */
int win_size, mem_size, mem_seg;
#define NONE 0
#define PCXE 1
#define PCXI 2
#define PCXR 3
#define PCXM 4
#define PCXEVE 5
#define PCXEM 6
#define PCCX 7
#define PCEPC 8
#define PCI_MODEL 0x80
int model;
Bitmap *bitmap;
int opencnt;
int unit;
#if __FreeBSD__ <= 3
#ifdef DEVFS
void *devfs_token_ctl;
#endif
struct tty *ttys; /* pointer to array of TTY structures */
#else
struct resource *port_res, *mem_res, *irq_res;
int prid, mrid, irid;
void *ih;
#endif
};
struct con_bios {
struct con_bios *next;
u_char *bios;
size_t size;
};
struct con_bios *con_bios_list;
/* Device switch entry points. */
static d_open_t digiopen;
static d_close_t digiclose;
static d_read_t digiread;
static d_write_t digiwrite;
static d_ioctl_t digiioctl;
#if __FreeBSD__ <= 3
static d_stop_t digistop;
static d_devtotty_t digidevtotty;
static int digi_isa_attach __P((struct isa_device *dev));
static int digi_isa_probe __P((struct isa_device *dev));
#else
static int digi_isa_attach __P((device_t dev));
static int digi_isa_probe __P((device_t dev));
static void digistop __P((struct tty *tp, int rw));
#endif
static int digimctl __P((struct digi_p *port, int bits, int how));
static void fepcmd(struct digi_p *port, int cmd, int op, int ncmds);
#define fepcmd_b(p, cmd, o1, o2, ncmds) fepcmd(p, cmd, (o2<<8)|o1, ncmds)
#define fepcmd_w fepcmd
#define W(p)(*(ushort *)(p))
static void digistart __P((struct tty *tp));
static int digiparam __P((struct tty *tp, struct termios *t));
static void digihardclose __P((struct digi_p *port));
static char driver_name[] = "digi";
#define CDEV_MAJOR 200
#if __FreeBSD__ <= 3
static struct cdevsw digi_cdevsw = {
digiopen, digiclose, digiread, digiwrite, digiioctl, digistop, noreset,
digidevtotty,
#if __FreeBSD__ == 2
ttselect,
#else
ttpoll,
#endif
nommap, NULL, driver_name, NULL, -1,
#if __FreeBSD__ > 2
nodump, nopsize, D_TTY,
#endif
};
struct isa_driver digidriver = {
digi_isa_probe, digi_isa_attach, driver_name
};
static u_long digi_unit; /* For EISA and PCI */
static struct digi_softc digi_softc[NDIGI];
static digi_devsw_installed = 0;
static void digi_drvinit(void *unused)
{
dev_t dev;
if( ! digi_devsw_installed ) {
dev = makedev(CDEV_MAJOR, 0);
cdevsw_add(&dev,&digi_cdevsw, NULL);
digi_devsw_installed = 1;
}
}
SYSINIT(digidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,digi_drvinit,NULL)
#else
static int digi_numunits;
static devclass_t digi_devclass;
static device_method_t digi_isa_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, digi_isa_probe),
DEVMETHOD(device_attach, digi_isa_attach),
{ 0, 0 }
};
static driver_t digi_isa_driver = {
driver_name,
digi_isa_methods,
sizeof(struct digi_softc),
};
static struct cdevsw digi_cdevsw = {
/* open */ digiopen,
/* close */ digiclose,
/* read */ digiread,
/* write */ digiwrite,
/* ioctl */ digiioctl,
/* poll */ ttypoll,
/* mmap */ nommap,
/* strategy */ nostrategy,
/* name */ driver_name,
/* maj */ CDEV_MAJOR,
/* dump */ nodump,
/* psize */ nopsize,
/* flags */ D_TTY,
/* bmaj */ -1
};
DRIVER_MODULE(digi, isa, digi_isa_driver, digi_devclass, 0, 0);
#endif
static speed_t digidefaultrate = TTYDEF_SPEED;
static struct speedtab digispeedtab[] = {
{ 0, 0 }, /* old (sysV-like) Bx codes */
{ 50, 1 },
{ 75, 2 },
{ 110, 3 },
{ 134, 4 },
{ 150, 5 },
{ 200, 6 },
{ 300, 7 },
{ 600, 8 },
{ 1200, 9 },
{ 1800, 10 },
{ 2400, 11 },
{ 4800, 12 },
{ 9600, 13 },
{ 19200, 14 },
{ 38400, 15 },
{ 57600, (02000 | 1) },
{ 76800, (02000 | 2) },
{ 115200, (02000 | 3) },
{ 230400, (02000 | 6) },
{ -1, -1 }
};
#define DEBUG_INIT 1
#define DEBUG_OPEN 2
#define DEBUG_CLOSE 4
#define DEBUG_SET 8
#define DEBUG_INT 16
#define DEBUG_READ 32
#define DEBUG_WRITE 64
#define DEBUG_RX 128
#define DEBUG_TX 256
#define DEBUG_IRQ 512
#define DEBUG_MODEM 0x400
#define DEBUG_RI 0x800
static int debug=0;
#define PCIPORT sc->vmem[0x200000]
static int setwin(struct digi_softc *sc, unsigned addr);
static void hidewin(struct digi_softc *sc);
static __inline int
setwin(sc,addr)
struct digi_softc *sc;
unsigned int addr;
{
if(sc->wport == 0) return addr;
outb(sc->wport, sc->window=FEPWIN|(addr/sc->win_size));
return (addr % sc->win_size);
}
static __inline void
hidewin(sc)
struct digi_softc *sc;
{
if(sc->wport == 0) return ;
outb(sc->wport, sc->window=0);
outb(sc->port, 0);
}
static __inline void
towin(struct digi_softc *sc, int win)
{
if(sc->wport == 0) return ;
outb(sc->wport, sc->window=win);
}
#if __FreeBSD__ >= 3
void digiintr(int);
#else
inthand2_t digiintr;
#endif
static int digi_attach(int unit);
#include "eisa.h"
#if NEISA > 0
#define EISA_DEVICE_ID_DIGI_CX 0x10490102
#define EISA_DEVICE_ID_DIGI_PCXem 0x10490201
#define EISA_DEVICE_ID_DIGI_EPCX 0x10490301
#if __FreeBSD__ <= 3
#include <i386/eisa/eisaconf.h>
static int digi_eisa_probe(void);
static int digi_eisa_attach(struct eisa_device *e_dev);
static struct eisa_driver digi_eisa_driver = {
"digi",
digi_eisa_probe,
digi_eisa_attach,
NULL, /*shutdown*/
&digi_unit
};
DATA_SET (eisadriver_set, digi_eisa_driver);
#else
#include <dev/eisa/eisaconf.h>
static device_method_t digi_eisa_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, digi_eisa_probe),
DEVMETHOD(device_attach, digi_eisa_attach),
{ 0, 0 }
};
static driver_t digi_eisa_driver = {
driver_name,
digi_eisa_methods,
sizeof(struct digi_softc),
};
DRIVER_MODULE(digi, isa, digi_eisa_driver, digi_devclass, 0, 0);
#endif
static const char *digi_eisa_match(eisa_id_t type);
static const char*
digi_eisa_match(eisa_id_t type)
{
switch(type) {
case EISA_DEVICE_ID_DIGI_CX:
return ("Digi EISA C/X host adapter");
break;
case EISA_DEVICE_ID_DIGI_PCXem:
return ("Digi EISA PC/Xem host adapter");
break;
case EISA_DEVICE_ID_DIGI_EPCX:
return ("Digi EISA EPC/X host adapter");
break;
default:
break;
}
return (NULL);
}
#if __FreeBSD__ <= 3
static int
digi_eisa_probe(void)
{
u_long iobase;
struct eisa_device *e_dev = NULL;
int count;
static int irq_map[]={0,3,5,7,10,11,12,15};
count = 0;
while ((e_dev = eisa_match_dev(e_dev, digi_eisa_match))) {
u_long port,maddr;
u_int irq;
iobase = (e_dev->ioconf.slot * EISA_SLOT_SIZE); port= iobase+5;
eisa_add_iospace(e_dev, port, 10, RESVADDR_NONE);
irq = inb(port+2) & 0x0F;
if (irq) eisa_add_intr(e_dev, irq_map[irq]);
maddr = ((inb(port+2) & 0x80) << 8) | ((inb(port+3) & 0xFF) << 16) | ((inb(port+4) & 0xFF) << 24);
eisa_add_mspace(e_dev, maddr, 32768, RESVADDR_NONE); /* EISA C/X, EISA PC/Xem and EISA EPC/X have 32K window */
eisa_registerdev(e_dev, &digi_eisa_driver);
count++;
}
return count;
}
static int
digi_eisa_attach(struct eisa_device *e_dev)
{
int unit = e_dev->unit;
int irq;
resvaddr_t *ioport,*maddr;
struct digi_softc *sc;
sc= &digi_softc[unit];
bzero(sc, sizeof(struct digi_softc));
sc->status= DISABLED;
if (TAILQ_FIRST(&e_dev->ioconf.irqs) == NULL)
return (-1);
irq = TAILQ_FIRST(&e_dev->ioconf.irqs)->irq_no;
ioport = e_dev->ioconf.ioaddrs.lh_first;
maddr = e_dev->ioconf.maddrs.lh_first;
if (ioport == NULL)
return -1;
eisa_reg_start(e_dev);
if (eisa_reg_iospace(e_dev, ioport))
return -1;
if (eisa_reg_iospace(e_dev, maddr))
return -1;
if (eisa_reg_intr(e_dev, irq, (void *)digiintr, (void *)unit, &tty_imask, 1))
return -1;
eisa_reg_end(e_dev);
if (eisa_enable_intr(e_dev, irq)) {
eisa_release_intr(e_dev, irq, (void *)digiintr);
return -1;
}
sc->status=NOTINIT;
switch(e_dev->id){
case EISA_DEVICE_ID_DIGI_CX:
sc->maxport=64;
sc->model = PCCX;
break;
case EISA_DEVICE_ID_DIGI_PCXem:
sc->maxport=128;
sc->model = PCXEM;
break;
case EISA_DEVICE_ID_DIGI_EPCX:
sc->maxport=224;
sc->model = PCEPC;
break;
}
sc->port=(e_dev->ioconf.slot * EISA_SLOT_SIZE) + 5;
sc->wport=sc->port+1;
sc->win_size=32768;
sc->mem_size=0;
sc->mem_seg=0;
sc->pmem= maddr->addr;
sc->psize= 32768;
sc->vmem= (caddr_t) pmap_mapdev(maddr->addr, 32768);
return digi_attach(unit);
}
#else /* FreeBSD 4 */
static int
digi_eisa_probe(device_t dev)
{
u_long iobase, port, maddr;
u_int irq;
static int irq_map[]={0,3,5,7,10,11,12,15};
const char *desc;
struct digi_softc *sc= device_get_softc(dev);
bzero(sc, sizeof(*sc));
sc->status=DISABLED;
desc = digi_eisa_match(eisa_get_id(dev));
if(!desc) return ENXIO;
sc->status=NOTINIT;
device_set_desc(dev, desc);
iobase = eisa_get_slot(dev) * EISA_SLOT_SIZE; port= iobase+5;
eisa_add_iospace(dev, port, 10, RESVADDR_NONE);
irq = inb(port+2) & 0x0F;
if (irq) eisa_add_intr(dev, irq_map[irq], EISA_TRIGGER_LEVEL); /* XXX */
maddr = ((inb(port+2) & 0x80) << 8) | ((inb(port+3) & 0xFF) << 16) | ((inb(port+4) & 0xFF) << 24);
eisa_add_mspace(dev, maddr, 32768, RESVADDR_NONE); /* EISA C/X, EISA PC/Xem and EISA EPC/X have 32K window */
log(LOG_INFO,"digi%d: irq %d pmem 0x%lx\n", device_get_unit(dev), irq_map[irq], maddr);
switch(eisa_get_id(dev)){
case EISA_DEVICE_ID_DIGI_CX:
sc->maxport=64;
sc->model = PCCX;
break;
case EISA_DEVICE_ID_DIGI_PCXem:
sc->maxport=128;
sc->model = PCXEM;
break;
case EISA_DEVICE_ID_DIGI_EPCX:
sc->maxport=224;
sc->model = PCEPC;
break;
}
sc->port=port;
sc->wport=sc->port+1;
sc->win_size=32768;
sc->mem_size=0;
sc->mem_seg=0;
sc->pmem= maddr;
sc->psize= 32768;
return 0;
}
static int
digi_eisa_attach(device_t dev)
{
int irq;
void *ih;
int unit=device_get_unit(dev);
struct digi_softc *sc= device_get_softc(dev);
sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->prid, 0, ~0, 5, RF_ACTIVE);
if (!sc->port_res) return ENXIO;
sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mrid, 0, ~0, 1, RF_ACTIVE);
if (!sc->mem_res) return ENXIO;
#if 0 /* Memory detected by eisa_probe */
sc->pmem = (caddr_t)rman_get_start(sc->sc_mem_res);
sc->psize = rman_get_end(sc->sc_mem_res) - sc->pmem + 1;
#endif
sc->vmem = rman_get_virtual(sc->mem_res);
return digi_attach(unit);
}
static device_method_t digi_eisa_methods[] = {
DEVMETHOD(device_probe, digi_eisa_probe),
DEVMETHOD(device_attach, digi_eisa_attach),
{ 0, 0 }
};
static driver_t digi_eisa_driver = {
driver_name,
digi_eisa_driver,
sizeof(struct digi_softc),
};
DRIVER_MODULE(digi, eisa, digi_eisa_driver, digi_devclass, 0, 0);
#endif
#endif /* NEISA > 0 */
#if NPCI > 0
#include <pci/pcivar.h>
#include <pci/pcireg.h>
#if __FreeBSD__ <= 3
static const char *digi_probe_pci __P((pcici_t, pcidi_t));
static void digi_attach_pci __P((pcici_t, int));
static struct pci_device digi_driver_pci = {
"digi", digi_probe_pci, digi_attach_pci, &digi_unit, NULL
};
DATA_SET(pcidevice_set, digi_driver_pci);
static const char *
digi_probe_pci(config_id, device_id)
pcici_t config_id;
pcidi_t device_id;
{
if ((device_id & 0xffff) == 0x114f){
switch(device_id >> 16){
case 0x0002:
return("Digi PCI EPC/X ASIC Version Serial Adapter");
case 0x0004:
return("Digi PCI PC/Xem ASIC Version Serial Adapter");
case 0x0005:
return("Digi PCI PC/Xr ASIC Version Serial Adapter");
case 0x0006:
return("Digi PCI C/X ASIC Version Serial Adapter");
case 0x000a:
return("Digi PCI EPC/X PLX Version Serial Adapter");
case 0x0009:
return("Digi PCI PC/Xr PLX Version Serial Adapter");
}
}
return NULL;
}
static void
digi_attach_pci(config_id, unit)
pcici_t config_id;
int unit;
{
struct digi_softc *sc;
pcidi_t device_id;
int memreg, irq;
sc= &digi_softc[unit];
bzero(sc, sizeof(*sc));
sc->status=DISABLED;
pci_conf_write(config_id, 0x40,0);
pci_conf_write(config_id, 0x46,0);
pci_conf_write(config_id, 0x42,1);
device_id = pci_conf_read(config_id, 0); /* Conf. ID */
irq = pci_conf_read(config_id, PCI_INTERRUPT_REG) & 0xff;
switch (device_id>>16) {
case 0x0002:
sc->maxport=224;
memreg = 0x10;
sc->model = PCEPC|PCI_MODEL;
sc->win_size=2*1024*1024;
sc->mem_size=sc->win_size; /* XXX */
break;
case 0x0004:
sc->maxport=128;
memreg = 0x10;
sc->model = PCXEM|PCI_MODEL;
sc->win_size=256*1024;
sc->mem_size=sc->win_size; /* XXX */
break;
case 0x0005:
sc->maxport=8;
memreg = 0x10;
sc->model = PCXR|PCI_MODEL;
sc->win_size=128*1024;
sc->mem_size=sc->win_size; /* XXX */
break;
case 0x0006:
sc->maxport=64;
memreg = 0x10;
sc->model = PCCX|PCI_MODEL;
sc->win_size=1024*1024;
sc->mem_size=sc->win_size; /* XXX */
break;
case 0x000a:
sc->maxport=224;
memreg = 0x18;
sc->model = PCEPC|PCI_MODEL;
sc->win_size=2*1024*1024;
sc->mem_size=sc->win_size; /* XXX */
break;
case 0x0009:
sc->maxport=8;
memreg = 0x18;
sc->model = PCXR|PCI_MODEL;
sc->win_size=128*1024;
sc->mem_size=sc->win_size; /* XXX */
break;
default:
sc->status=DISABLED;
return;
}
#if 0
sc->pmem = pci_conf_read(config_id, memreg);
sc->vmem = pmap_mapdev(sc->pmem, 4*1024*1024);
#else
pci_map_mem(config_id, memreg, (vm_offset_t *)(&sc->vmem), (vm_offset_t *)(&sc->pmem));
#endif
log(LOG_INFO,"digi%d: irq %d pmem 0x%lx vmem 0x%p\n",
(int)unit,irq,sc->pmem,sc->vmem);
sc->port=0;
sc->wport=0;
sc->win_size=4*1024*1024;
sc->mem_size=0; /* XXX */
sc->mem_seg=0;
sc->psize=0;
if (!pci_map_int(config_id, (pci_inthand_t *)digiintr, (void *)unit, &tty_imask)) {
printf("digi%d: couldn't map interrupt\n", unit);
}
digi_attach(unit);
}
#else /* FreeBSD 4 */
static int
digi_pci_probe(device_t dev)
{
struct digi_softc *sc= device_get_softc(dev);
int memreg;
bzero(sc, sizeof(*sc));
sc->status=NOTINIT;
switch (pci_get_devid(dev)>>16) {
case 0x0002:
device_set_desc(dev, "Digi PCI EPC/X ASIC Version Serial Adapter");
sc->maxport=224;
memreg = 0x10;
sc->model = PCEPC|PCI_MODEL;
break;
case 0x0004:
device_set_desc(dev, "Digi PCI PC/Xem ASIC Version Serial Adapter");
sc->maxport=128;
memreg = 0x10;
sc->model = PCXEM|PCI_MODEL;
break;
case 0x0005:
device_set_desc(dev, "Digi PCI PC/Xr ASIC Version Serial Adapter");
sc->maxport=8;
memreg = 0x10;
sc->model = PCXR|PCI_MODEL;
break;
case 0x0006:
device_set_desc(dev, "Digi PCI C/X ASIC Version Serial Adapter");
sc->maxport=64;
memreg = 0x10;
sc->model = PCCX|PCI_MODEL;
break;
case 0x000a:
device_set_desc(dev, "Digi PCI EPC/X PLX Version Serial Adapter");
sc->maxport=224;
memreg = 0x18;
sc->model = PCEPC|PCI_MODEL;
break;
case 0x0009:
device_set_desc(dev, "Digi PCI PC/Xr PLX Version Serial Adapter");
sc->maxport=8;
memreg = 0x18;
sc->model = PCXR|PCI_MODEL;
break;
default:
sc->status=DISABLED;
return (ENXIO);
}
sc->port=0;
sc->wport=0;
sc->win_size=4*1024*1024;
sc->pmem = pci_read_config(dev, memreg, 4);
sc->mem_size=0; /* XXX */
sc->mem_seg=0;
sc->psize=0;
return 0;
}
static int
digi_pci_attach(device_t dev)
{
struct digi_softc *sc= device_get_softc(dev);
int irq, unit = device_get_unit(dev);
pci_write_config(dev, 0x40, 0, 1);
pci_write_config(dev, 0x46, 0, 1);
pci_write_config(dev, 0x42, 1, 1);
irq = pci_read_config(dev, PCI_INTERRUPT_REG, 4) & 0xff;
sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->prid, 0, ~0, 5, RF_ACTIVE);
if (!sc->port_res) return ENXIO;
sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mrid, 0, ~0, 1, RF_ACTIVE);
if (!sc->mem_res) return ENXIO;
sc->pmem = rman_get_start(sc->mem_res);
sc->psize = rman_get_end(sc->mem_res) - sc->pmem + 1;
sc->vmem = rman_get_virtual(sc->mem_res);
log(LOG_INFO,"digi%d: irq %d pmem 0x%lx vmem 0x%p\n",
(int)unit,irq,sc->pmem,sc->vmem);
return digi_attach(unit);
}
static device_method_t digi_pci_methods[] = {
DEVMETHOD(device_probe, digi_pci_probe),
DEVMETHOD(device_attach, digi_pci_attach),
{ 0, 0 }
};
static driver_t digi_pci_driver = {
driver_name,
digi_pci_methods,
sizeof(struct digi_softc),
};
DRIVER_MODULE(digi, pci, digi_pci_driver, digi_devclass, 0, 0);
#endif
#endif /* NPCI > 0 */
#if __FreeBSD__ <= 3
static u_long irq_table[] = {IRQ3, IRQ5, IRQ7, IRQ10, IRQ11, IRQ12, IRQ15, 0};
#else
static u_long irq_table[] = {3, 5, 7, 10, 11, 12, 15, 0};
#endif
static int
digi_isa_probe(dev)
#if __FreeBSD__ <= 3
struct isa_device *dev;
#else
device_t dev;
#endif
{
int i, t;
#if __FreeBSD__ <= 3
int unit=dev->id_unit;
struct digi_softc *sc= &digi_softc[unit];
bzero(sc, sizeof(*sc));
sc->port=dev->id_iobase;
sc->pmem=vtophys(dev->id_maddr);
sc->mem_size=dev->id_msize;
#else
int unit=device_get_unit(dev);
struct digi_softc *sc= device_get_softc(dev);
bzero(sc, sizeof(*sc));
sc->status=NOTINIT;
bus_get_resource(dev, SYS_RES_IOPORT, 0, &sc->port, NULL);
bus_get_resource(dev, SYS_RES_MEMORY, 0, &sc->pmem, &sc->psize);
sc->mem_size = sc->psize;
#endif
log(LOG_INFO,"digi%d: port 0x%lx mem 0x%lx\n", unit,sc->port,sc->pmem);
if(inb(sc->port) == 0xff) {
sc->status=DISABLED;
log(LOG_ERR,"digi%d: port not found\n",unit);
goto fail;
}
outb(sc->port, FEPRST|0x2);
for(i=0;((t=inb(sc->port)) & FEPRST) != FEPRST;i++) {
if(i>10000) {
sc->status=DISABLED;
log(LOG_ERR,"digi%d: failed probe reset\n",unit);
goto fail;
}
DELAY(1);
}
sc->status=NOTINIT;
if(debug&DEBUG_INIT)
log(LOG_DEBUG,"digi%d: got probe reset after %d us\n",unit,i);
/* try detect card */
if(t & 0x2) { /* PC/X* (Xe,Xe VE, Xi, Xm, Xt, Xt VE) */
if(t & 0x1) { /* bit0 always 1 -- PC/Xi */
sc->model=PCXI;
switch(t&0x30) {
case 0x00:
sc->mem_size=0x10000; break;
case 0x10:
sc->mem_size=0x20000; break;
case 0x20:
sc->mem_size=0x40000; break;
case 0x30:
sc->mem_size=0x80000; break;
}
sc->win_size=sc->mem_size;
sc->mem_seg=(0x100000-sc->mem_size)>>4;
log(LOG_INFO,"digi%d: PC/Xi %dKb\n",unit,sc->mem_size/1024);
} else {
outb(sc->port, FEPRST|0x2|0x1);
t=inb(sc->port);
if(t & 0x1) { /* bit0 r/w -- PC/Xm */
sc->model=PCXM;
sc->win_size=sc->mem_size; /* Get memory size from conf */
/* XXX Get memory size after BIOS start from 0xc12 */
sc->mem_seg=0;
log(LOG_INFO,"digi%d: PC/Xm\n",unit);
} else { /* bit0 always 1 -- PC/Xe or Xe VE */
if(t&0x40) {
sc->model=PCXEVE;
sc->wport=sc->port+1;
sc->win_size=8192;
sc->mem_size=65536;
log(LOG_INFO,"digi%d: PC/Xe VE [%02x]\n",unit, t & 0xff);
} else {
sc->model=PCXE;
sc->win_size=sc->mem_size=65536;
log(LOG_INFO,"digi%d: PC/Xe [%02x]\n",unit, t & 0xff);
}
sc->mem_seg=(0x100000-sc->mem_size)>>4;
}
}
} else { /* PC/Xem, C/X, EPC/X */
/* Only PC/Xem, C/X, EPC/X, PC/Xe VE have window mode */
sc->wport=sc->port+1;
sc->win_size=32768;
sc->mem_size=0;
sc->mem_seg=0;
if(t&1) { /* PC/Xem or EPC/X -- try EPC/X */
sc->model=PCEPC;
log(LOG_INFO,"digi%d: PC/Xem or EPC/X\n",unit);
} else {
sc->model=PCCX;
log(LOG_INFO,"digi%d: C/X\n",unit);
}
}
#if __FreeBSD__ <= 3
return 5; /* we need I/O space of 5 ports */
fail:
return 0;
#else
return 0;
fail:
return ENXIO;
#endif
}
static int
#if __FreeBSD__ <= 3
digi_isa_attach(struct isa_device *isdp)
#else
digi_isa_attach(device_t dev)
#endif
{
int unit;
struct digi_softc *sc;
u_long irq, *irqptr;
#if __FreeBSD__ <= 3
unit=isdp->id_unit;
sc= &digi_softc[unit];
#else
unit=device_get_unit(dev);
sc= device_get_softc(dev);
#endif
if(sc->status!=NOTINIT) {
log(LOG_ERR,"digi%d: try to attach a disabled or already init card\n",unit);
goto fail;
}
#if __FreeBSD__ <= 3
sc->vmem=isdp->id_maddr;
#else
sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->prid, 0, ~0, 5, RF_ACTIVE);
if (!sc->port_res) return ENXIO;
sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mrid, 0, ~0, sc->psize, RF_ACTIVE);
if (!sc->mem_res) return ENXIO;
sc->vmem = rman_get_virtual(sc->mem_res);
#endif
#if 0
/* Already reseted. after probe */
outb(sc->port, FEPRST|0x2);
for(i=0; (inb(sc->port) & FEPRST) != FEPRST ; i++)
{
if(i>10000)
{
log(LOG_ERR,"digi%d: 1st reset failed\n",unit);
sc->status=DISABLED;
hidewin(sc);
return 0;
}
DELAY(1);
}
if(debug&DEBUG_INIT)
log(LOG_DEBUG,"digi%d: got reset after %d us\n",unit,i);
#endif
/* set up interrupt and base address */
#if __FreeBSD__ > 3
if(bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL)) irq = 0;
#else
irq = isdp->id_irq;
#endif
if(irq) {
for(irqptr=irq_table; *irqptr && *irqptr!=irq; irqptr++) ;
if(!*irqptr){
log(LOG_ERR,"digi%d: illegal IRQ\n",unit);
sc->status=DISABLED;
hidewin(sc);
return 0;
}
irq= irqptr - irq_table + 1;
}
irq|= 0x10; /* window mode for PC/Xe VE*/
outb(sc->port+2,((u_long)sc->pmem>>8)|irq);
outb(sc->port+3,(u_long)sc->pmem>>16);
outb(sc->port+4,(u_long)sc->pmem>>24);
return digi_attach(unit);
fail:
#if __FreeBSD__ <= 3
return 0;
#else
/* XXX Free resources */
return ENXIO;
#endif
}
/* Common attach */
static int
digi_attach(int unit)
{
struct digi_softc *sc;
int i;
u_char *mem;
u_long volatile *lptr;
#if __FreeBSD__ <= 3
sc= &digi_softc[unit];
#else
device_t dev;
sc = (struct digi_softc *) devclass_get_softc(digi_devclass, unit);
dev = devclass_get_device(digi_devclass, unit);
if (unit >= digi_numunits) digi_numunits = unit + 1;
sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
if(sc->irq_res)
bus_setup_intr(dev, sc->irq_res, INTR_TYPE_TTY, (void (*)(void *))digiintr, (void *)unit, &sc->ih);
#endif
sc->unit = unit;
mem=sc->vmem;
/* detect memory size & test memory*/
if(sc->wport) {
for(i=0; i<sc->win_size*128; i+=sc->win_size){
lptr=(u_long *)(mem+setwin(sc,i));
*lptr = 0xA55A3CC3;
if(*lptr != 0xA55A3CC3){
if(i) break;
log(LOG_ERR,"digi%d: memory test failed [write] at %d: 0x%lx\n",unit,i,*lptr);
sc->status=DISABLED;
hidewin(sc);
goto fail;;
}
*lptr = ~0xA55A3CC3;
if(*lptr != ~0xA55A3CC3){
if(i) break;
log(LOG_ERR,"digi%d: memory test failed [inverse] at %d: 0x%lx\n",unit,i,*lptr);
sc->status=DISABLED;
hidewin(sc);
goto fail;
}
if(i == 0){ /* Set first word to test value */
*lptr = 0x87654321;
} else { /* Test modification of first word */
lptr=(u_long *)(mem+setwin(sc,0));
if(*lptr != 0x87654321) break;
}
}
sc->mem_size=i;
} else { /* Not windowed card */
lptr=(u_long *)mem;
*lptr = 0xA55A3CC3;
if(*lptr != 0xA55A3CC3){
log(LOG_ERR,"digi%d: memory test failed [write]: %lx\n",unit,*lptr);
sc->status=DISABLED;
hidewin(sc);
goto fail;
}
*lptr = ~0xA55A3CC3;
if(*lptr != ~0xA55A3CC3){
log(LOG_ERR,"digi%d: memory test failed [inverse]: %lx\n",unit,*lptr);
sc->status=DISABLED;
hidewin(sc);
goto fail;
}
lptr=(u_long *)(mem+sc->win_size-sizeof(u_long));
*lptr = 0xA55A3CC3;
if(*lptr != 0xA55A3CC3){
log(LOG_ERR,"digi%d: memory test failed [write end]: %lx\n",unit,*lptr);
sc->status=DISABLED;
hidewin(sc);
goto fail;
}
*lptr = ~0xA55A3CC3;
if(*lptr != ~0xA55A3CC3){
log(LOG_ERR,"digi%d: memory test failed [inverse end]: %lx\n",unit,*lptr);
sc->status=DISABLED;
hidewin(sc);
goto fail;
}
}
log(LOG_INFO,"digi%d: memory size %dKB\n",unit,sc->mem_size/1024);
#if __FreeBSD__ <= 3
#if defined(DEVFS)
sc->devfs_token_ctl = devfs_add_devswf(&digi_cdevsw,
(unit<<16) | DOWNLOAD_MASK, DV_CHR,
UID_ROOT, GID_WHEEL, 0600, "ttyD%r.ctl", unit);
sc->ttys = NULL;
#endif
#else
make_dev(&digi_cdevsw,(unit<<16) | DOWNLOAD_MASK,
UID_ROOT, GID_WHEEL, 0600, "ttyD%r.ctl", unit);
#endif
switch(sc->model){
case PCXE: case PCXEVE: case PCXI: case PCXM:
sc->bitmap=DigiXi;
break;
default:
sc->bitmap=DigiConc;
}
sc->gdata=(struct global_data *)(sc->vmem + 0xd10);
sc->bios= NULL; sc->fep = NULL; sc->link= NULL;
sc->ports= NULL;
sc->status= NOTINIT;
#if __FreeBSD__ <= 3
return 1;
#else
return 0;
#endif
fail:
sc->status= DISABLED;
#if __FreeBSD__ <= 3
return 0;
#else
/* XXX Free resources */
return ENXIO;
#endif
}
static void
digidelay(void *p)
{
wakeup(p);
}
static void
digipoll(void *p)
{
int i;
/* s=spltty(); */
timeout(digipoll, NULL, (hz>=200) ? hz / 100 : 1 );
#if __FreeBSD__ <= 3
for(i=0; i < NDIGI; i++)
if(digi_softc[i].intr_handler) digi_softc[i].intr_handler(i);
#else
for(i=0; i < digi_numunits; i++) {
struct digi_softc *sc=
(struct digi_softc *) devclass_get_softc(digi_devclass, i);
if(sc && sc->intr_handler) sc->intr_handler(i);
}
#endif
/* splx(s); */
}
static void
digi_int_test(void *ptr)
{
struct digi_softc *sc=(struct digi_softc *)ptr;
if(sc->intr_timestamp.tv_sec || sc->intr_timestamp.tv_usec){
/* interrupt OK! */
sc->intr_handler= NULL;
return;
}
log(LOG_ERR,"digi%d: interrupt not worked, use poll mode\n", sc->unit);
sc->intr_handler= digiintr ;
timeout(digipoll, NULL, (hz>=200) ? hz / 100 : 1 );
}
static void
digi_copy(struct digi_softc *sc, int addr, u_char *ptr, int size)
{
int cnt, t_addr;
if(sc->wport == 0)
bcopy(ptr, sc->vmem + addr, size);
else
while(size){
t_addr=setwin(sc, addr);
cnt= (size > sc->win_size-t_addr) ? sc->win_size-t_addr : size;
bcopy(ptr, sc->vmem + t_addr, cnt);
addr+= cnt; size-= cnt; ptr+= cnt;
}
}
static int
digiinit(int unit)
{
struct digi_softc *sc;
int i;
u_char *mem;
u_char *ptr;
int addr;
struct digi_p *port;
struct board_chan *bc;
#if __FreeBSD__ <= 3
sc=&digi_softc[unit];
#else
sc = (struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif
if(sc->status==DISABLED) {
log(LOG_ERR,"digi%d: try init a disabled card\n",unit);
return EIO;
}
if(!sc->bios && sc->model != PCXM) {
log(LOG_ERR,"digi%d: try init w/bios code\n",unit);
return EIO;
}
#if 0
if(!sc->link && sc->model >= PCCX) {
log(LOG_ERR,"digi%d: try init w/link info\n",unit);
return EIO;
}
#endif
if(!sc->fep) {
log(LOG_ERR,"digi%d: try init w/fep code\n",unit);
return EIO;
}
sc->status= NOTINIT;
mem=sc->vmem;
if(sc->model & PCI_MODEL) PCIPORT = FEPRST; else outb(sc->port, FEPRST|0x2);
for(i=0; ((sc->model & PCI_MODEL ? PCIPORT : inb(sc->port)) & FEPRST) != FEPRST ; i++)
{
if(i>10)
{
log(LOG_ERR,"digi%d: 1st reset failed\n",unit);
/* hidewin(sc); */
return EIO;
}
DELAY(1000);
}
if(debug&DEBUG_INIT)
log(LOG_DEBUG,"digi%d: got 1st reset after %d ms\n",unit,i);
switch(sc->model){
case PCXE: case PCXEVE: case PCXI: case PCCX:
addr=setwin(sc,sc->mem_size-2048);
bcopy(sc->bios, mem+addr, sc->s_bios);
break;
case PCXEM: case PCEPC: case PCXEM|PCI_MODEL: case PCEPC|PCI_MODEL: case PCCX|PCI_MODEL: case PCXR|PCI_MODEL:
addr=setwin(sc, 0);
*(unsigned int *)(mem+addr) =0x0bf00401;
*(unsigned int *)(mem+addr+4)=0;
digi_copy(sc, 0x1000, sc->bios, sc->s_bios);
case PCXM: case NONE: /* PC/Xm have resident bios */
break;
}
addr=setwin(sc,0xc00);
W(mem+addr)=0;
if(sc->model & PCI_MODEL) PCIPORT = FEPCLR; else outb(sc->port, FEPCLR|0x2);
for(i=0; ((sc->model & PCI_MODEL? PCIPORT: inb(sc->port)) & FEPRST)== FEPRST ; i++) {
if(i>10) {
log(LOG_ERR,"digi%d: BIOS clear reset failed\n",unit);
/* hidewin(sc); */
return EIO;
}
DELAY(1000);
}
if(debug&DEBUG_INIT) log(LOG_DEBUG,"digi%d: clear reset after %d us\n",unit,i);
for(i=0; W(mem+addr) != 'DG'; i++) {
int timeout_flag;
if(i> hz * 5) {
log(LOG_ERR,"digi%d: BIOS download failed code=0x%x must be 0x%x\n",
unit,W(mem+addr), *((ushort *)"GD"));
/* hidewin(sc); */
return EIO;
}
timeout(digidelay, &timeout_flag, 1 );
tsleep(&timeout_flag, TTIPRI | PCATCH, "digidelay", 0);
}
if(debug&DEBUG_INIT) log(LOG_DEBUG,"digi%d: BIOS loaded after %dms\n",unit,i*1000/hz);
if(sc->link){
addr=setwin(sc,0xcd0);
bcopy(sc->link, mem+addr,21);
}
/* load FEP/OS */
switch(sc->model){
case PCXE: case PCXEVE: case PCXI: case PCXM:
digi_copy(sc, 0x2000, sc->fep, sc->s_fep);
if(sc->model!=PCXM){
ptr=mem+setwin(sc,0xc40);
W(ptr)=2; W(ptr+2)= (0x100000-sc->mem_size+0x2000) >> 4;
W(ptr+4)=0; W(ptr+6)= 0x200; W(ptr+8)=0; W(ptr+10)=0x2000;
outb(sc->port,FEPREQ|0x2);
outb(sc->port,FEPCLR|0x2);
for(i=0; W(ptr); i++) {
int timeout_flag;
if(i>hz) {
log(LOG_ERR,"digi%d: FEP/OS move failed\n",unit);
hidewin(sc);
return EIO;
}
timeout(digidelay, &timeout_flag, 1 );
tsleep(&timeout_flag, TTIPRI | PCATCH, "digidelay", 0);
}
if(debug&DEBUG_INIT)
log(LOG_DEBUG,"digi%d: FEP/OS moved after %dms\n",unit,i*1000/hz);
}
ptr=mem+setwin(sc,0xc40);
W(ptr)=1;
W(ptr+2)=0x200;
W(ptr+4)=4;
ptr=mem+setwin(sc,FEPSTAT);
W(ptr)=0;
outb(sc->port,FEPREQ|0x2); /* send interrupt to BIOS */
outb(sc->port,FEPCLR|0x2);
break;
case PCXEM: case PCEPC: case PCXEM|PCI_MODEL: case PCEPC|PCI_MODEL: case PCCX|PCI_MODEL: case PCXR|PCI_MODEL:
digi_copy(sc, 0x1000, sc->fep, sc->s_fep);
ptr=mem+setwin(sc,FEPSTAT);
W(ptr)=0;
ptr=mem+setwin(sc,0xc34);
W(ptr)=0x1004; W(ptr+2)=0xbfc0; *(ptr-4)=3;
ptr=mem+setwin(sc,FEPSTAT);
break;
case PCCX:
digi_copy(sc, 0xd000, sc->fep, sc->s_fep);
ptr=mem+setwin(sc,0xc40);
W(ptr)=1;
W(ptr+2)=FEPCODE>>4;
W(ptr+4)=4;
ptr=mem+setwin(sc,FEPSTAT);
W(ptr)=0;
outb(sc->port,FEPREQ); /* send interrupt to BIOS */
outb(sc->port,FEPCLR);
break;
}
for(i=0; W(ptr)!= 'SO'; i++) {
int timeout_flag;
if(i>5 * hz) {
log(LOG_ERR,"digi%d: FEP/OS start failed: got %04x wait %04x\n",unit, W(ptr), 'SO');
hidewin(sc);
return EIO;
}
timeout(digidelay, &timeout_flag, 1 );
tsleep(&timeout_flag, TTIPRI | PCATCH, "digidelay", 0);
}
if(debug&DEBUG_INIT) log(LOG_DEBUG,"digi%d: FEP/OS started after %d ms\n",unit,i*1000/hz);
W(mem+0xe04) = 2;
switch(sc->model){
case PCXE: case PCXEVE: case PCXI: case PCXM:
sc->numports= W(mem+0xc22);
default:
sc->numports= W(mem+0xc02);
}
log(LOG_INFO,"digi%d: %d ports\n",unit,sc->numports);
sc->memcmd= sc->vmem+sc->gdata->cstart;
sc->memevent = sc->vmem+sc->gdata->istart;
if(sc->numports == 0) {
log(LOG_ERR,"digi%d: no ports found\n",unit);
sc->status=NOTINIT;
hidewin(sc);
return EIO;
}
if(sc->ports) free(sc->ports, M_TTYS);
MALLOC(sc->ports, struct digi_p *, sizeof(struct digi_p)*sc->numports,
M_TTYS, M_NOWAIT);
if(sc->ports==0) {
log(LOG_ERR,"digi%d: unable to malloc the per port structures\n",unit);
sc->status=NOTINIT;
hidewin(sc);
return EIO;
}
bzero(sc->ports, sizeof(struct digi_p)*sc->numports);
#if __FreeBSD__ <=3
if(sc->ttys) free(sc->ttys, M_TTYS);
MALLOC(sc->ttys, struct tty *, sizeof(struct tty)*sc->numports,
M_TTYS, M_NOWAIT);
if(sc->ttys==0)
{
log(LOG_ERR,"digi%d: unable to malloc the tty structures\n",unit);
FREE(sc->ttys, M_TTYS);
sc->status=NOTINIT;
hidewin(sc);
return EIO;
}
bzero(sc->ttys, sizeof(struct tty)*sc->numports);
#endif
/* We should now init per-port structures */
bc=(struct board_chan *)(mem + 0x1000);
for(i=0; i<sc->numports; i++, bc++)
{
port= &sc->ports[i];
port->pnum=i;
port->unit=unit;
port->status=ENABLED;
#if __FreeBSD__ <=3
port->tp=&sc->ttys[i];
#endif
port->bc=bc;
switch(sc->model){
case PCXE: case PCXEVE: case PCXI: case PCXM:
port->dcd= 0x08;
port->dsr= 0x10;
default:
port->dcd= 0x80;
port->dsr= 0x20;
}
port->txbuf=mem+( ((bc->tseg-sc->mem_seg)<<4)%sc->win_size );
port->rxbuf=mem+( ((bc->rseg-sc->mem_seg)<<4)%sc->win_size );
port->txwin=FEPWIN | (((bc->tseg-sc->mem_seg)<<4)/sc->win_size);
port->rxwin=FEPWIN | (((bc->rseg-sc->mem_seg)<<4)/sc->win_size);
port->txbufsize=bc->tmax+1;
port->rxbufsize=bc->rmax+1;
fepcmd_w(port, STXLWATER, port->txbufsize/4, 10);
fepcmd_w(port, SRXLWATER, port->rxbufsize/4, 10);
fepcmd_w(port, SRXHWATER, 3*port->rxbufsize/4, 10);
bc->edelay=100;
port->dtr_wait= 3*hz;
/*
* We don't use all the flags from <sys/ttydefaults.h> since they
* are only relevant for logins. It's important to have echo off
* initially so that the line doesn't start blathering before the
* echo flag can be turned off.
*/
port->it_in.c_iflag = 0;
port->it_in.c_oflag = 0;
port->it_in.c_cflag = TTYDEF_CFLAG;
port->it_in.c_lflag = 0;
termioschars(&port->it_in);
port->it_in.c_ispeed = port->it_in.c_ospeed = digidefaultrate;
port->it_out = port->it_in;
port->send_ring = 1; /* Default action on signal RI */
#if __FreeBSD__ <= 3
#if defined(DEVFS)
port->devfs_token_ttyd = devfs_add_devswf(&digi_cdevsw,
(unit<<16)+i, DV_CHR,
UID_ROOT, GID_WHEEL, 0600, "ttyD%d.%d", unit, i);
port->devfs_token_ttyi = devfs_add_devswf(&digi_cdevsw,
(unit<<16)+i | CONTROL_INIT_STATE, DV_CHR,
UID_ROOT, GID_WHEEL, 0600, "ttyiD%d.%d", unit, i);
port->devfs_token_ttyl = devfs_add_devswf(&digi_cdevsw,
(unit<<16)+i | CONTROL_LOCK_STATE, DV_CHR,
UID_ROOT, GID_WHEEL, 0600, "ttylD%d.%d", unit, i);
port->devfs_token_cuaa = devfs_add_devswf(&digi_cdevsw,
(unit<<16)+i | CALLOUT_MASK, DV_CHR,
UID_UUCP, GID_DIALER, 0660, "cuaD%d.%d", unit, i);
port->devfs_token_cuai = devfs_add_devswf(&digi_cdevsw,
(unit<<16)+i | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR,
UID_UUCP, GID_DIALER, 0660, "cuaiD%d.%d", unit, i);
port->devfs_token_cual = devfs_add_devswf(&digi_cdevsw,
(unit<<16)+i | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR,
UID_UUCP, GID_DIALER, 0660, "cualD%d.%d", unit, i);
#endif
#else
make_dev(&digi_cdevsw, (unit<<16)+i,
UID_ROOT, GID_WHEEL, 0600, "ttyD%d.%d", unit, i);
make_dev(&digi_cdevsw, ((unit<<16)+i) | CONTROL_INIT_STATE,
UID_ROOT, GID_WHEEL, 0600, "ttyiD%d.%d", unit, i);
make_dev(&digi_cdevsw, ((unit<<16)+i) | CONTROL_LOCK_STATE,
UID_ROOT, GID_WHEEL, 0600, "ttylD%d.%d", unit, i);
make_dev(&digi_cdevsw, ((unit<<16)+i) | CALLOUT_MASK,
UID_UUCP, GID_DIALER, 0660, "cuaD%d.%d", unit, i);
make_dev(&digi_cdevsw, ((unit<<16)+i) | CALLOUT_MASK | CONTROL_INIT_STATE,
UID_UUCP, GID_DIALER, 0660, "cuaiD%d.%d", unit, i);
make_dev(&digi_cdevsw, ((unit<<16)+i) | CALLOUT_MASK | CONTROL_LOCK_STATE,
UID_UUCP, GID_DIALER, 0660, "cualD%d.%d", unit, i);
#endif
}
sc->status=ENABLED;
sc->opencnt++;
timeout(digi_int_test, sc, hz);
fepcmd_w(&sc->ports[0], 0xff, 0, 0);
return 0;
}
#if __FreeBSD__ <= 3
struct tty *
digidevtotty(dev)
dev_t dev;
{
int mynor;
int unit;
int pnum;
struct digi_softc *sc;
mynor = minor(dev);
if (mynor & CONTROL_MASK)
return (NULL);
unit = MINOR_TO_UNIT(mynor);
pnum=MINOR_TO_PORT(mynor);
if ((u_int) unit >= NDIGI)
return (NULL);
sc=&digi_softc[unit];
if(sc->status!=ENABLED)
{
log(LOG_ERR,"digi%d: try to open a disabled card\n",unit);
return (NULL);
}
if(pnum>=sc->numports)
{
log(LOG_ERR,"digi%d: try to open non-existing port %d\n",unit,pnum);
return (NULL);
}
return (sc->ports[pnum].tp);
}
#endif
static int
digimctl(port, bits, how)
struct digi_p *port;
int bits;
int how;
{
int mstat;
Bitmap *bp;
if(how == DMGET)
{
mstat=port->bc->mstat;
bits = TIOCM_LE;
#if __FreeBSD__ <= 3
for(bp=digi_softc[port->unit].bitmap;bp->from;bp++)
#else
for(bp=((struct digi_softc *) devclass_get_softc(digi_devclass, port->unit))->bitmap;bp->from;bp++)
#endif
if(mstat & bp->from) bits |= bp->to;
return bits;
}
mstat = 0;
bits &= TIOCM_DTR | TIOCM_RTS;
#if __FreeBSD__ <= 3
for(bp=digi_softc[port->unit].bitmap;bp->to;bp++)
#else
for(bp=((struct digi_softc *) devclass_get_softc(digi_devclass, port->unit))->bitmap;bp->to;bp++)
#endif
if(bits & bp->to) mstat |= bp->from;
switch (how)
{
case DMSET:
fepcmd_b(port, SETMODEM, mstat, ~mstat, 0);
break;
case DMBIS:
fepcmd_b(port, SETMODEM, mstat, 0, 0);
break;
case DMBIC:
fepcmd_b(port, SETMODEM, 0, mstat, 0);
break;
}
return 0;
}
static void
digi_disc_optim(struct tty *tp, struct termios *t, struct digi_p *port)
{
if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP))
&& (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
&& (!(t->c_iflag & PARMRK)
|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
&& !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
&& linesw[tp->t_line].l_rint == ttyinput)
tp->t_state |= TS_CAN_BYPASS_L_RINT;
else
tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
}
int
digiopen(dev, flag, mode, p)
dev_t dev;
int flag;
int mode;
struct proc *p;
{
struct digi_softc *sc;
struct tty *tp;
int unit;
int pnum;
struct digi_p *port;
int s;
int error, mynor;
struct board_chan *bc;
error=0;
mynor=minor(dev);
unit=MINOR_TO_UNIT(minor(dev));
pnum=MINOR_TO_PORT(minor(dev));
#if __FreeBSD__ <= 3
if(unit >= NDIGI)
{
log(LOG_ERR,"digi%d: try to open a nonexisting card\n",unit);
return ENXIO;
}
sc=&digi_softc[unit];
#else
if(unit >= digi_numunits)
{
log(LOG_ERR,"digi%d: try to open a nonexisting card\n",unit);
return ENXIO;
}
sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);;
#endif
if(mynor & DOWNLOAD_MASK) return 0;
if(sc->status!=ENABLED)
{
log(LOG_ERR,"digi%d: try to open a disabled card\n",unit);
return ENXIO;
}
if(pnum>=sc->numports)
{
log(LOG_ERR,"digi%d: try to open non-existing port %d\n",unit,pnum);
return ENXIO;
}
if(mynor & CONTROL_MASK) {
sc->opencnt++;
return 0;
}
port=&sc->ports[pnum];
#if __FreeBSD__ > 3
port->tp = dev->si_tty = ttymalloc(port->tp);
#endif
tp=port->tp;
bc=port->bc;
s=spltty();
open_top:
while(port->status & DIGI_DTR_OFF) {
error=tsleep(&port->dtr_wait, TTIPRI | PCATCH, "digidtr", 0);
if (error) goto out;
}
if (tp->t_state & TS_ISOPEN)
{
/*
* The device is open, so everything has been initialized.
* Handle conflicts.
*/
if (mynor & CALLOUT_MASK)
{
if (!port->active_out)
{
error = EBUSY;
if(debug&DEBUG_OPEN)
log(LOG_DEBUG,"digi%d: port %d: BUSY error=%d\n",unit,pnum,error);
goto out;
}
}
else
{
if (port->active_out)
{
if (flag & O_NONBLOCK)
{
error = EBUSY;
if(debug&DEBUG_OPEN)
log(LOG_DEBUG,"digi%d: port %d: BUSY error=%d\n",unit,pnum,error);
goto out;
}
error = tsleep(&port->active_out, TTIPRI | PCATCH, "digibi", 0);
if (error != 0)
{
if(debug&DEBUG_OPEN)
log(LOG_DEBUG,"digi%d: port %d: tsleep(digibi) error=%d\n",
unit,pnum,error);
goto out;
}
goto open_top;
}
}
if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0)
{
error = EBUSY;
goto out;
}
}
else
{
/*
* The device isn't open, so there are no conflicts.
* Initialize it. Initialization is done twice in many
* cases: to preempt sleeping callin opens if we are
* callout, and to complete a callin open after DCD rises.
*/
tp->t_oproc=digistart;
tp->t_param=digiparam;
tp->t_dev=dev;
#if __FreeBSD__ >= 4
tp->t_stop = digistop;
#endif
tp->t_termios= (mynor & CALLOUT_MASK) ? port->it_out : port->it_in;
setwin(sc,0);
digimctl(port, TIOCM_DTR | TIOCM_RTS, DMSET);
/* port->imodem=bc->mstat; */
bc->rout=bc->rin; /* clear input queue */
bc->idata=1; bc->iempty=1; bc->ilow=1; bc->mint=port->dcd|0x40;
bc->tin=bc->tout;
++port->wopeners;
error=digiparam(tp, &tp->t_termios);
--port->wopeners;
if(error!=0)
{
if(debug&DEBUG_OPEN)
log(LOG_DEBUG,"digi%d: port %d: digiparam error=%d\n",unit,pnum,error);
goto out;
}
ttsetwater(tp);
/* handle fake DCD for callout devices */
/* and initial DCD */
if( (bc->mstat & port->dcd) || mynor & CALLOUT_MASK )
linesw[tp->t_line].l_modem(tp,1);
}
/*
* Wait for DCD if necessary.
*/
if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
&& !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK))
{
++port->wopeners;
error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "digidcd", 0);
--port->wopeners;
if (error != 0)
{
if(debug&DEBUG_OPEN)
log(LOG_DEBUG,"digi%d: port %d: tsleep(digidcd) error=%d\n",unit,pnum,error);
goto out;
}
goto open_top;
}
error = linesw[tp->t_line].l_open(dev, tp);
if(debug&DEBUG_OPEN)
log(LOG_DEBUG,"digi%d: port %d: l_open error=%d\n",unit,pnum,error);
digi_disc_optim(tp,&tp->t_termios,port);
if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
port->active_out = TRUE;
if (tp->t_state & TS_ISOPEN){
sc->opencnt++;
}
out:
splx(s);
if( !(tp->t_state & TS_ISOPEN) && port->wopeners==0 )
digihardclose(port);
if(debug&DEBUG_OPEN)
log(LOG_DEBUG,"digi%d: port %d: open() returns %d\n",unit,pnum,error);
return error;
}
/*ARGSUSED*/
int
digiclose(dev, flag, mode, p)
dev_t dev;
int flag;
int mode;
struct proc *p;
{
int mynor;
struct tty *tp;
int unit, pnum;
struct digi_softc *sc;
struct digi_p *port;
int s;
mynor=minor(dev);
unit=MINOR_TO_UNIT(mynor);
pnum=MINOR_TO_PORT(mynor);
#if __FreeBSD__ <=3
sc=&digi_softc[unit];
#else
sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif
if(mynor & DOWNLOAD_MASK){
return 0;
}
port=sc->ports+pnum;
tp=port->tp;
if(mynor & CONTROL_MASK){
if( --sc->opencnt == 0)
return 0;
}
if(debug&DEBUG_CLOSE)
log(LOG_DEBUG,"digi%d: port %d: closing\n",unit,pnum);
s=spltty();
linesw[tp->t_line].l_close(tp, flag);
digi_disc_optim(tp,&tp->t_termios,port);
digistop(tp, FREAD | FWRITE);
digihardclose(port);
ttyclose(tp);
if( --sc->opencnt == 0)
splx(s);
return (0);
}
static void
digidtrwakeup(void *chan)
{
struct digi_p *port;
port = (struct digi_p *)chan;
port->status &= ~DIGI_DTR_OFF;
wakeup(&port->dtr_wait);
}
static void
digihardclose(port)
struct digi_p *port;
{
struct digi_softc *sc;
struct board_chan *bc;
int s;
#if __FreeBSD__ <= 3
sc=&digi_softc[port->unit];
#else
sc=(struct digi_softc *) devclass_get_softc(digi_devclass, port->unit);
#endif
bc=port->bc;
s=spltty();
setwin(sc,0);
bc->idata=0; bc->iempty=0; bc->ilow=0; bc->mint=0;
if (port->tp->t_cflag & HUPCL
|| (!port->active_out && !(bc->mstat & port->dcd) && !(port->it_in.c_cflag & CLOCAL))
|| (!(port->tp->t_state & TS_ISOPEN)))
{
digimctl(port, TIOCM_DTR, DMBIC);
if (port->dtr_wait != 0) {
timeout(&digidtrwakeup, port, port->dtr_wait);
port->status |= DIGI_DTR_OFF;
}
}
port->active_out = FALSE;
wakeup(&port->active_out);
wakeup(TSA_CARR_ON(port->tp));
splx(s);
}
int
digiread(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
int mynor;
struct tty *tp;
int error, unit, pnum;
mynor=minor(dev);
if (mynor & CONTROL_MASK)
return (ENODEV);
unit=MINOR_TO_UNIT(mynor);
pnum=MINOR_TO_PORT(mynor);
#if __FreeBSD__ <= 3
tp=&digi_softc[unit].ttys[pnum];
#else
tp=((struct digi_softc *) devclass_get_softc(digi_devclass, unit))->ports[pnum].tp;
#endif
error=linesw[tp->t_line].l_read(tp, uio, flag);
if(debug&DEBUG_READ)
log(LOG_DEBUG,"digi%d: port %d: read() returns %d\n",unit,pnum,error);
return error;
}
int
digiwrite(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
int mynor;
struct tty *tp;
int error, unit, pnum;
mynor=minor(dev);
if (mynor & CONTROL_MASK)
return (ENODEV);
unit=MINOR_TO_UNIT(mynor);
pnum=MINOR_TO_PORT(mynor);
#if __FreeBSD__ <= 3
tp=&digi_softc[unit].ttys[pnum];
#else
tp=((struct digi_softc *) devclass_get_softc(digi_devclass, unit))->ports[pnum].tp;
#endif
error=linesw[tp->t_line].l_write(tp, uio, flag);
if(debug&DEBUG_WRITE)
log(LOG_DEBUG,"digi%d: port %d: write() returns %d\n",unit,pnum,error);
return error;
}
static int
digiioctl(dev, cmd, data, flag, p)
dev_t dev;
#if __FreeBSD__ == 2
int cmd;
#else
u_long cmd;
#endif
caddr_t data;
int flag;
struct proc *p;
{
struct digi_softc *sc;
int unit, pnum;
struct digi_p *port;
int mynor;
struct tty *tp;
struct board_chan *bc;
int error;
int s;
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
int oldcmd;
struct termios term;
#endif
mynor=minor(dev);
unit=MINOR_TO_UNIT(mynor);
pnum=MINOR_TO_PORT(mynor);
#if __FreeBSD__ <= 3
if(unit >= NDIGI) return ENXIO;
sc=&digi_softc[unit];
#else
if(unit >= digi_numunits) return ENXIO;
sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif
if(sc->status == DISABLED) return ENXIO;
#define DIGI_LFEP _IO('e','F')
#define DIGI_LBIOS _IO('e','B')
#define DIGI_LCON _IO('e','C')
#define DIGI_LINK _IO('e','L')
#define DIGI_INIT _IO('e','I')
#define DIGI_DEBUG _IO('e','D')
#define DIGI_RING _IO('e','R')
if(mynor & DOWNLOAD_MASK)
switch (cmd)
{
u_char *in,**pptr;
int *psize;
struct con_bios *con;
case DIGI_LFEP:
pptr=&sc->fep;
psize=&sc->s_fep;
goto load;
case DIGI_LCON:
if((con = con_bios_list) == NULL){
con = con_bios_list = malloc(sizeof(*con),M_TTYS,M_NOWAIT);
bzero(con, sizeof(*con));
} else {
while(con->next) con = con->next;
con= con->next = malloc(sizeof(*con),M_TTYS,M_NOWAIT);
}
bzero(con, sizeof(*con));
pptr=&con->bios;
psize=&con->size;
goto load;
case DIGI_LBIOS:
pptr=&sc->bios;
psize=&sc->s_bios;
goto load;
case DIGI_LINK:
pptr=&sc->link;
psize=&sc->s_link;
load:
in=(u_char *)(*(caddr_t *)data);
error= copyin(in,psize,sizeof(*psize));
if(error)return error;
in+=sizeof(*psize);
if(*pptr) free(*pptr,M_TTYS);
*pptr=malloc(*psize,M_TTYS,M_NOWAIT);
if(!*pptr)return ENOBUFS;
error = copyin(in,*pptr,*psize);
return error;
case DIGI_INIT:
return digiinit(unit);
case DIGI_DEBUG:
debug = *(int *) data;
log(LOG_DEBUG, "digi%d: setting debug %x\n",unit, debug);
return 0;
}
port=&sc->ports[pnum];
if(! port->status & ENABLED) return ENXIO;
tp=port->tp;
bc=port->bc;
if (mynor & CONTROL_MASK)
{
struct termios *ct;
switch (mynor & CONTROL_MASK)
{
case CONTROL_INIT_STATE:
ct = mynor & CALLOUT_MASK ? &port->it_out : &port->it_in;
break;
case CONTROL_LOCK_STATE:
ct = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in;
break;
default:
return (ENODEV); /* /dev/nodev */
}
switch (cmd)
{
case TIOCSETA:
#if __FreeBSD__ <= 3
error = suser(p->p_ucred, &p->p_acflag);
#else
error = suser(p);
#endif
if (error != 0)
return (error);
*ct = *(struct termios *)data;
return (0);
case TIOCGETA:
*(struct termios *)data = *ct;
return (0);
case TIOCGETD:
*(int *)data = TTYDISC;
return (0);
case TIOCGWINSZ:
bzero(data, sizeof(struct winsize));
return (0);
default:
return (ENOTTY);
}
}
tp = port->tp;
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
term = tp->t_termios;
oldcmd = cmd;
error = ttsetcompat(tp, &cmd, data, &term);
if(error != 0) return error;
if(cmd != oldcmd) data = (caddr_t)&term;
#endif
if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF)
{
int cc;
struct termios *dt = (struct termios *)data;
struct termios *lt = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in;
dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag);
dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag);
dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag);
dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag);
port->c_iflag = dt->c_iflag & (IXOFF|IXON|IXANY);
dt->c_iflag &= ~(IXOFF|IXON|IXANY);
for (cc = 0; cc < NCCS; ++cc)
if (lt->c_cc[cc] != 0)
dt->c_cc[cc] = tp->t_cc[cc];
if (lt->c_ispeed != 0)
dt->c_ispeed = tp->t_ispeed;
if (lt->c_ospeed != 0)
dt->c_ospeed = tp->t_ospeed;
}
error = linesw[tp->t_line].l_ioctl(tp, cmd, data, flag, p);
if (error == 0 && cmd == TIOCGETA)
((struct termios *)data)->c_iflag |= port->c_iflag;
#if __FreeBSD__ == 2
if (error >= 0 ) return error;
#else
if (error >= 0 && error != ENOIOCTL) return error;
#endif
s = spltty();
error = ttioctl(tp, cmd, data, flag);
if (error == 0 && cmd == TIOCGETA)
((struct termios *)data)->c_iflag |= port->c_iflag;
digi_disc_optim(tp, &tp->t_termios, port);
#if __FreeBSD__ == 2
if (error >= 0 )
#else
if (error >= 0 && error != ENOIOCTL)
#endif
{
splx(s);
return error;
}
setwin(sc,0);
switch (cmd)
{
case DIGI_RING:
port->send_ring = *(u_char *)data;
break;
case TIOCSBRK:
/* now it sends 250 millisecond break because I don't know */
/* how to send an infinite break */
fepcmd_w(port, SENDBREAK, 250, 10);
break;
case TIOCCBRK:
/* now it's empty */
break;
case TIOCSDTR:
digimctl(port, TIOCM_DTR, DMBIS);
break;
case TIOCCDTR:
digimctl(port, TIOCM_DTR, DMBIC);
break;
case TIOCMSET:
digimctl(port, *(int *)data, DMSET);
break;
case TIOCMBIS:
digimctl(port, *(int *)data, DMBIS);
break;
case TIOCMBIC:
digimctl(port, *(int *)data, DMBIC);
break;
case TIOCMGET:
*(int *)data = digimctl(port, 0, DMGET);
break;
case TIOCMSDTRWAIT:
#if __FreeBSD__ <= 3
error = suser(p->p_ucred, &p->p_acflag);
#else
error = suser(p);
#endif
if (error != 0) {
splx(s);
return error;
}
port->dtr_wait = *(int *)data * hz / 100;
break;
case TIOCMGDTRWAIT:
*(int *)data = port->dtr_wait * 100 / hz;
break;
case TIOCTIMESTAMP:
*(struct timeval *)data = sc->intr_timestamp;
break;
default:
splx(s);
return ENOTTY;
}
splx(s);
return 0;
}
static int
digiparam(tp, t)
struct tty *tp;
struct termios *t;
{
int mynor;
int unit;
int pnum;
struct digi_softc *sc;
struct digi_p *port;
struct board_chan *bc;
int cflag;
int iflag;
int hflow;
int s;
mynor=minor(tp->t_dev);
unit=MINOR_TO_UNIT(mynor);
pnum=MINOR_TO_PORT(mynor);
#if __FreeBSD__ <= 3
sc=&digi_softc[unit];
#else
sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif
port=&sc->ports[pnum];
bc=port->bc;
if(debug&DEBUG_SET)
log(LOG_DEBUG,"digi%d: port%d: setting parameters\n",unit,pnum);
if (t->c_ispeed == 0)
t->c_ispeed = t->c_ospeed;
cflag=ttspeedtab(t->c_ospeed, digispeedtab);
if (cflag < 0 || (cflag > 0 && t->c_ispeed != t->c_ospeed))
return (EINVAL);
s=spltty();
setwin(sc,0);
if(cflag==0)
{ /* hangup */
if(debug&DEBUG_SET)
log(LOG_DEBUG,"digi%d: port%d: hangup\n",unit,pnum);
digimctl(port, TIOCM_DTR, DMBIC);
}
else
digimctl(port, TIOCM_DTR, DMBIS);
if(debug&DEBUG_SET)
log(LOG_DEBUG,"digi%d: port%d: CBAUD=%d\n",unit,pnum,cflag);
/* convert flags to sysV-style values */
if(t->c_cflag & PARODD) cflag|=0x0200;
if(t->c_cflag & PARENB) cflag|=0x0100;
if(t->c_cflag & CSTOPB) cflag|=0x0080;
cflag|= (t->c_cflag & CSIZE) >> 4;
if(debug&DEBUG_SET)
log(LOG_DEBUG,"digi%d: port%d: CFLAG=0x%x\n",unit,pnum,cflag);
fepcmd_w(port, SETCFLAGS, (unsigned)cflag, 0);
iflag=t->c_iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP);
if(port->c_iflag & IXON) iflag|= 0x400;
if(port->c_iflag & IXANY) iflag|= 0x800;
if(port->c_iflag & IXOFF) iflag|=0x1000;
if(debug&DEBUG_SET)
log(LOG_DEBUG,"digi%d: port%d: set iflag=0x%x\n",unit,pnum,iflag);
fepcmd_w(port, SETIFLAGS, (unsigned)iflag, 0);
/* bc->mint=port->dcd; */
hflow = 0;
switch(sc->model){
case PCXE: case PCXEVE: case PCXI: case PCXM:
if(t->c_cflag & CDTR_IFLOW) hflow|= 0x80;
if(t->c_cflag & CRTS_IFLOW) hflow|= 0x02;
if(t->c_cflag & CCTS_OFLOW) hflow|= 0x20;
if(t->c_cflag & CDSR_OFLOW) hflow|= 0x10;
if(t->c_cflag & CCAR_OFLOW) hflow|= 0x08;
default:
if(t->c_cflag & CDTR_IFLOW) hflow|= 0x01;
if(t->c_cflag & CRTS_IFLOW) hflow|= 0x02;
if(t->c_cflag & CCTS_OFLOW) hflow|= 0x10;
if(t->c_cflag & CDSR_OFLOW) hflow|= 0x20;
if(t->c_cflag & CCAR_OFLOW) hflow|= 0x80;
}
if(debug&DEBUG_SET)
log(LOG_DEBUG,"digi%d: port%d: set hflow=0x%x\n",unit,pnum,hflow);
fepcmd_w(port, SETHFLOW, 0xff00|(unsigned)hflow, 0);
if(debug&DEBUG_SET)
log(LOG_DEBUG,"digi%d: port%d: set startc(0x%x), stopc(0x%x)\n",
unit,pnum, t->c_cc[VSTART], t->c_cc[VSTOP]);
fepcmd_b(port, SONOFFC, t->c_cc[VSTART], t->c_cc[VSTOP], 0);
splx(s);
return 0;
}
void
digiintr(unit)
int unit;
{
struct digi_p *port;
char *cxcon;
struct digi_softc *sc;
int ehead, etail;
struct board_chan *bc;
struct tty *tp;
int head, tail;
int wrapmask;
int size,window;
struct event
{
u_char pnum,event,mstat,lstat;
} event;
#ifdef COM_LOCK
COM_LOCK();
#endif
#if __FreeBSD__ <=3
sc=&digi_softc[unit];
#else
sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif
if(sc->status!=ENABLED)
{
if(debug&DEBUG_IRQ)
log(LOG_DEBUG,"digi%d: interrupt of not enabled board!\n",unit);
return;
}
microtime(&sc->intr_timestamp);
if(sc->wport){ window=sc->window; setwin(sc,0); }
if(W(sc->vmem+0xd00))
{
struct con_bios *con=con_bios_list;
register u_char *ptr;
ptr=sc->vmem+W(sc->vmem+0xd00);
while(con){
if(ptr[1] && W(ptr+2) == W(con->bios+2)) /* Not first block -- exact match */
break;
if(W(ptr+4) >= W(con->bios+4) && W(ptr+4) <= W(con->bios+6))
break; /* Initial search concetrator BIOS */
}
if(con == NULL)
{
log(LOG_ERR,"digi%d: wanted bios LREV=0x%04x not found!\n",unit,W(ptr+4));
W(ptr+10)=0;
W(sc->vmem+0xd00)=0;
goto eoi;
}
cxcon=con->bios;
W(ptr+4)=W(cxcon+4);
W(ptr+6)=W(cxcon+6);
if(ptr[1]==0) W(ptr+2)=W(cxcon+2);
W(ptr+8)=(ptr[1]<<6)+W(cxcon+8);
size=W(cxcon+10)-(ptr[1]<<10);
if(size<=0)
{
W(ptr+8)=W(cxcon+8);
W(ptr+10)=0;
}
else
{
if(size>1024) size=1024;
W(ptr+10)=size;
bcopy(cxcon+(ptr[1]<<10),ptr+12,size);
}
W(sc->vmem+0xd00)=0;
goto eoi;
}
ehead=sc->gdata->ein;
etail=sc->gdata->eout;
if(ehead==etail)
{
if(debug&DEBUG_IRQ && sc->intr_handler == NULL)
log(LOG_DEBUG,"digi%d: DUMMY interrupt %x %x\n",unit,ehead,etail);
goto eoi;
}
while(ehead!=etail)
{
event= *(struct event *)&sc->memevent[etail];
if(debug&DEBUG_IRQ) log(LOG_DEBUG,"digi%d: EVENT %04x: %02x port %d [%02x/%02x]\n", unit,etail,event.event, event.pnum, event.mstat,event.lstat);
etail= (etail+4) & sc->gdata->imax;
if(event.pnum>=sc->numports)
{
log(LOG_ERR,"digi%d: port %d: got event on nonexisting port\n",
unit,event.pnum);
continue;
}
port=&sc->ports[event.pnum];
bc=port->bc;
tp=port->tp;
if( !(tp->t_state & TS_ISOPEN) )
{
if(debug&DEBUG_IRQ)
log(LOG_DEBUG,"digi%d: port %d: got event 0x%x on closed port\n",
unit,event.pnum,event.event);
bc->rout=bc->rin;
bc->idata=0; bc->iempty=0; bc->ilow=0; bc->mint=0;
continue;
}
if( event.event & ~ALL_IND )
log(LOG_ERR,"digi%d: port%d: ? event 0x%x mstat 0x%x lstat 0x%x\n",
unit, event.pnum, event.event, event.mstat, event.lstat);
if(event.event & DATA_IND)
{
if(debug&DEBUG_IRQ)
log(LOG_DEBUG,"digi%d: port %d: DATA_IND\n",unit,event.pnum);
wrapmask=port->rxbufsize-1;
head=bc->rin;
tail=bc->rout;
size=0;
if( !(tp->t_state & TS_ISOPEN) )
{
bc->rout=head;
goto end_of_data;
}
while(head!=tail)
{
int top;
if(debug&DEBUG_INT)
log(LOG_DEBUG,"digi%d: port %d: p rx head=%d tail=%d\n",
unit,event.pnum,head,tail);
top= (head>tail)?head:wrapmask+1;
towin(sc,port->rxwin);
size=top-tail;
if(tp->t_state & TS_CAN_BYPASS_L_RINT)
{
size =
b_to_q((char *)port->rxbuf+tail,size,&tp->t_rawq);
/* port->delta_error_counts[CE_TTY_BUF_OVERFLOW]+= size; */
tail = top-size;
ttwakeup(tp);
}
else
{
for(;tail<top;) {
linesw[tp->t_line].l_rint(port->rxbuf[tail],tp);
towin(sc,port->rxwin);
size--; tail++;
if(tp->t_state & TS_TBLOCK) break;
}
}
tail &= wrapmask;
setwin(sc,0);
bc->rout=tail;
head=bc->rin;
if(size) break;
}
if(bc->orun)
{
CE_RECORD(port, CE_OVERRUN);
log(LOG_ERR,"digi%d: port%d: %s\n",
unit, event.pnum, error_desc[CE_OVERRUN]);
bc->orun=0;
}
end_of_data:
if(size){
tp->t_state |= TS_TBLOCK;
port->status |= PAUSE_RX;
if(debug&DEBUG_RX)
log(LOG_DEBUG,"digi%d: port %d: pause RX\n",unit,event.pnum);
/* fepcmd_w(port,PAUSERX,0,10); */
} else {
bc->idata=1;
}
}
if(event.event & MODEMCHG_IND)
{
if(debug&DEBUG_MODEM)
log(LOG_DEBUG,"digi%d: port %d: MODEMCHG_IND\n",unit,event.pnum);
if((event.mstat^event.lstat)&port->dcd)
linesw[tp->t_line].l_modem(tp,event.mstat & port->dcd);
if(event.mstat&0x40)
{
if(debug&DEBUG_RI)
log(LOG_DEBUG,"digi%d: port %d: RING\n",unit,event.pnum);
if(port->send_ring){
linesw[tp->t_line].l_rint('R',tp);
linesw[tp->t_line].l_rint('I',tp);
linesw[tp->t_line].l_rint('N',tp);
linesw[tp->t_line].l_rint('G',tp);
linesw[tp->t_line].l_rint('\r',tp);
linesw[tp->t_line].l_rint('\n',tp);
}
}
}
if(event.event & BREAK_IND)
{
if(debug&DEBUG_MODEM)
log(LOG_DEBUG,"digi%d: port %d: BREAK_IND\n",unit,event.pnum);
linesw[tp->t_line].l_rint(TTY_BI, tp);
}
if(event.event & (LOWTX_IND | EMPTYTX_IND) )
{
if(debug&DEBUG_IRQ)
log(LOG_DEBUG,"digi%d: port %d:%s%s\n",unit,event.pnum,
event.event & LOWTX_IND ? " LOWTX":"",
event.event & EMPTYTX_IND ? " EMPTYTX":"");
(*linesw[tp->t_line].l_start)(tp);
}
}
sc->gdata->eout=etail;
eoi:
if(sc->wport){ towin(sc,window); }
#ifdef COM_LOCK
COM_UNLOCK();
#endif
}
static void
digistart(tp)
struct tty *tp;
{
int unit;
int pnum;
struct digi_p *port;
struct digi_softc *sc;
struct board_chan *bc;
int head, tail;
int size, ocount, totcnt=0;
int s;
int wmask;
unit=MINOR_TO_UNIT(minor(tp->t_dev));
pnum=MINOR_TO_PORT(minor(tp->t_dev));
#if __FreeBSD__ <=3
sc=&digi_softc[unit];
#else
sc=(struct digi_softc *) devclass_get_softc(digi_devclass, unit);
#endif
port=&sc->ports[pnum];
bc=port->bc;
wmask=port->txbufsize-1;
s=spltty();
port->lcc=tp->t_outq.c_cc;
setwin(sc,0);
if(!(tp->t_state&TS_TBLOCK))
{
if(port->status&PAUSE_RX)
if(debug&DEBUG_RX)
log(LOG_DEBUG,"digi%d: port %d: resume RX\n",unit,pnum);
port->status &= ~PAUSE_RX;
/* fepcmd_w(port, RESUMERX, 0, 10); */
bc->idata= 1;
}
if(!(tp->t_state&TS_TTSTOP))
{
if(port->status & PAUSE_TX) {
if(debug&DEBUG_TX)
log(LOG_DEBUG,"digi%d: port %d: resume TX\n",unit,pnum);
port->status &= ~PAUSE_TX;
fepcmd_w(port, RESUMETX, 0, 10);
}
}
if(tp->t_outq.c_cc == 0)
{
tp->t_state &= ~TS_BUSY;
/* printf("digi%d: port%d: (s) output queue empty\n",
unit,pnum); */
}
else
tp->t_state |= TS_BUSY;
head=bc->tin;
while( tp->t_outq.c_cc!=0 )
{
tail=bc->tout;
if(debug&DEBUG_INT)
log(LOG_DEBUG,"digi%d: port%d: s tx head=%d tail=%d\n",
unit,pnum,head,tail);
if(head<tail)
size=tail-head-1;
else
{
size=port->txbufsize-head;
if(tail==0) size--;
}
if(size==0) break;
towin(sc,port->txwin);
totcnt+=ocount=q_to_b(&tp->t_outq, port->txbuf+head, size);
head+=ocount;
head&=wmask;
setwin(sc,0);
bc->tin=head;
bc->iempty=1; bc->ilow=1;
}
port->lostcc=tp->t_outq.c_cc;
tail=bc->tout;
if(head<tail)
size=port->txbufsize-tail+head;
else
{
size=head-tail;
}
port->lbuf=size;
if(debug&DEBUG_INT)
log(LOG_DEBUG,"digi%d: port%d: s total cnt=%d\n",unit,pnum,totcnt);
ttwwakeup(tp);
splx(s);
}
void
digistop(tp, rw)
struct tty *tp;
int rw;
{
int unit;
int pnum;
struct digi_p *port;
unit=MINOR_TO_UNIT(minor(tp->t_dev));
pnum=MINOR_TO_PORT(minor(tp->t_dev));
#if __FreeBSD__ <=3
port=&digi_softc[unit].ports[pnum];
#else
port=&((struct digi_softc *) devclass_get_softc(digi_devclass, unit))->ports[pnum];
#endif
if(debug&DEBUG_TX)
log(LOG_DEBUG,"digi%d: port %d: pause TX\n",unit,pnum);
port->status |= PAUSE_TX;
fepcmd_w(port, PAUSETX, 0, 10);
}
static void
fepcmd(port, cmd, op1, ncmds)
struct digi_p *port;
int cmd, op1, ncmds;
{
#if __FreeBSD__ <=3
struct digi_softc *sc=&digi_softc[port->unit];
#else
struct digi_softc *sc=(struct digi_softc *) devclass_get_softc(digi_devclass, port->unit);
#endif
u_char *mem=sc->memcmd;
unsigned tail, head;
int count, n;
setwin(sc,0);
head=sc->gdata->cin;
mem[head+0]=cmd;
mem[head+1]=port->pnum;
*(ushort *)(mem+head+2)=op1;
head=(head+4) & sc->gdata->cmax;
sc->gdata->cin=head;
for(count=FEPTIMEOUT; count>0; count--)
{
head=sc->gdata->cin;
tail=sc->gdata->cout;
n=(head-tail) & sc->gdata->cmax;
if(n <= ncmds * 4)
return;
}
log(LOG_ERR,"digi%d(%d): timeout on FEP command\n",
port->unit, port->pnum);
}
|