9c62b3ee7c
Reviewed by: mike (mentor)
2234 lines
52 KiB
C
2234 lines
52 KiB
C
/*-
|
|
* dgb.c $FreeBSD$
|
|
*
|
|
* Digiboard driver.
|
|
*
|
|
* Stage 1. "Better than nothing".
|
|
* Stage 2. "Gee, it works!".
|
|
*
|
|
* 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,
|
|
* without modification, immediately at the beginning of the file.
|
|
* 2. Redistributions of binary code must retain the above copyright
|
|
* notice, this list of conditions, and the following disclaimer,
|
|
* without modification, in the accompanying documentation.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* 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.
|
|
*
|
|
* Written by Sergey Babkin,
|
|
* Joint Stock Commercial Bank "Chelindbank"
|
|
* (Chelyabinsk, Russia)
|
|
* babkin@freebsd.org
|
|
*
|
|
* Assorted hacks to make it more functional and working under 3.0-current.
|
|
* Fixed broken routines to prevent processes hanging on closed (thanks
|
|
* to Bruce for his patience and assistance). Thanks also to Maxim Bolotin
|
|
* <max@run.net> for his patches which did most of the work to get this
|
|
* running under 2.2/3.0-current.
|
|
* Implemented ioctls: TIOCMSDTRWAIT, TIOCMGDTRWAIT, TIOCTIMESTAMP &
|
|
* TIOCDCDTIMESTAMP.
|
|
* Sysctl debug flag is now a bitflag, to filter noise during debugging.
|
|
* David L. Nugent <davidn@blaze.net.au>
|
|
*
|
|
* There was a copyright confusion: I thought that having read the
|
|
* GLPed drivers makes me mentally contaminated but in fact it does
|
|
* not. Since the Linux driver by Troy De Jongh <troyd@digibd.com> or
|
|
* <troyd@skypoint.com> was used unly to learn the Digi's interface,
|
|
* I've returned this driver to a BSD-style license. I tried to contact
|
|
* all the contributors and those who replied agreed with license
|
|
* change. If you did any contribution when the driver was GPLed and do
|
|
* not agree with the BSD-style re-licensing please contact me.
|
|
* -SB
|
|
*/
|
|
|
|
#include "opt_compat.h"
|
|
#include "opt_dgb.h"
|
|
#include "opt_tty.h"
|
|
|
|
#include "dgb.h"
|
|
|
|
/* Helg: i.e.25 times per sec board will be polled */
|
|
#define POLLSPERSEC 25
|
|
/* How many charactes can we write to input tty rawq */
|
|
#define DGB_IBUFSIZE (TTYHOG-100)
|
|
|
|
/* the overall number of ports controlled by this driver */
|
|
|
|
#ifndef NDGBPORTS
|
|
# define NDGBPORTS (NDGB*16)
|
|
#endif
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/bus.h>
|
|
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
|
|
#include <i386/isa/isa_device.h>
|
|
|
|
#ifndef COMPAT_OLDISA
|
|
#error "The dgb device requires the old isa compatibility shims"
|
|
#endif
|
|
|
|
#include <dev/dgb/dgbios.h>
|
|
#include <dev/dgb/dgfep.h>
|
|
|
|
#define DGB_DEBUG /* Enable debugging info via sysctl */
|
|
#include <dev/dgb/dgreg.h>
|
|
|
|
#define CALLOUT_MASK 0x80
|
|
#define CONTROL_MASK 0x60
|
|
#define CONTROL_INIT_STATE 0x20
|
|
#define CONTROL_LOCK_STATE 0x40
|
|
#define UNIT_MASK 0x30000
|
|
#define PORT_MASK 0x1F
|
|
#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)
|
|
|
|
/* types. XXX - should be elsewhere */
|
|
typedef u_char bool_t; /* boolean */
|
|
|
|
/* digiboard port structure */
|
|
struct dgb_p {
|
|
bool_t status;
|
|
|
|
u_char unit; /* board unit number */
|
|
u_char pnum; /* port number */
|
|
u_char omodem; /* FEP output modem status */
|
|
u_char imodem; /* FEP input modem status */
|
|
u_char modemfake; /* Modem values to be forced */
|
|
u_char modem; /* Force values */
|
|
u_char hflow;
|
|
u_char dsr;
|
|
u_char dcd;
|
|
u_char stopc;
|
|
u_char startc;
|
|
u_char stopca;
|
|
u_char startca;
|
|
u_char fepstopc;
|
|
u_char fepstartc;
|
|
u_char fepstopca;
|
|
u_char fepstartca;
|
|
u_char txwin;
|
|
u_char rxwin;
|
|
ushort fepiflag;
|
|
ushort fepcflag;
|
|
ushort fepoflag;
|
|
ushort txbufhead;
|
|
ushort txbufsize;
|
|
ushort rxbufhead;
|
|
ushort rxbufsize;
|
|
int close_delay;
|
|
int count;
|
|
int blocked_open;
|
|
int event;
|
|
int asyncflags;
|
|
u_long statusflags;
|
|
u_char *txptr;
|
|
u_char *rxptr;
|
|
volatile struct board_chan *brdchan;
|
|
struct tty *tty;
|
|
|
|
bool_t active_out; /* nonzero if the callout device is open */
|
|
u_int wopeners; /* # processes waiting for DCD in open() */
|
|
|
|
/* 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;
|
|
|
|
bool_t do_timestamp;
|
|
bool_t do_dcd_timestamp;
|
|
struct timeval timestamp;
|
|
struct timeval dcd_timestamp;
|
|
|
|
/* flags of state, are used in sleep() too */
|
|
u_char closing; /* port is being closed now */
|
|
u_char draining; /* port is being drained now */
|
|
u_char used; /* port is being used now */
|
|
u_char mustdrain; /* data must be waited to drain in dgbparam() */
|
|
};
|
|
|
|
/* Digiboard per-board structure */
|
|
struct dgb_softc {
|
|
/* struct board_info */
|
|
u_char status; /* status: DISABLED/ENABLED */
|
|
u_char unit; /* unit number */
|
|
u_char type; /* type of card: PCXE, PCXI, PCXEVE */
|
|
u_char altpin; /* do we need alternate pin setting ? */
|
|
int numports; /* number of ports on card */
|
|
int port; /* I/O port */
|
|
u_char *vmem; /* virtual memory address */
|
|
long pmem; /* physical memory address */
|
|
int mem_seg; /* internal memory segment */
|
|
struct dgb_p *ports; /* pointer to array of port descriptors */
|
|
struct tty *ttys; /* pointer to array of TTY structures */
|
|
volatile struct global_data *mailbox;
|
|
};
|
|
|
|
|
|
static struct dgb_softc dgb_softc[NDGB];
|
|
static struct dgb_p dgb_ports[NDGBPORTS];
|
|
static struct tty dgb_tty[NDGBPORTS];
|
|
|
|
/*
|
|
* The public functions in the com module ought to be declared in a com-driver
|
|
* system header.
|
|
*/
|
|
|
|
/* Interrupt handling entry points. */
|
|
static void dgbpoll (void *unit_c);
|
|
|
|
/* Device switch entry points. */
|
|
#define dgbreset noreset
|
|
#define dgbmmap nommap
|
|
#define dgbstrategy nostrategy
|
|
|
|
static int dgbattach (struct isa_device *dev);
|
|
static int dgbprobe (struct isa_device *dev);
|
|
|
|
static void fepcmd(struct dgb_p *port, unsigned cmd, unsigned op1, unsigned op2,
|
|
unsigned ncmds, unsigned bytecmd);
|
|
|
|
static void dgbstart (struct tty *tp);
|
|
static void dgbstop (struct tty *tp, int rw);
|
|
static int dgbparam (struct tty *tp, struct termios *t);
|
|
static void dgbhardclose (struct dgb_p *port);
|
|
static void dgb_drain_or_flush (struct dgb_p *port);
|
|
static int dgbdrain (struct dgb_p *port);
|
|
static void dgb_pause (void *chan);
|
|
static void wakeflush (void *p);
|
|
static void disc_optim (struct tty *tp, struct termios *t);
|
|
|
|
|
|
struct isa_driver dgbdriver = {
|
|
INTR_TYPE_TTY,
|
|
dgbprobe,
|
|
dgbattach,
|
|
"dgb",
|
|
0
|
|
};
|
|
COMPAT_ISA_DRIVER(dgb, dgbdriver);
|
|
|
|
static d_open_t dgbopen;
|
|
static d_close_t dgbclose;
|
|
static d_ioctl_t dgbioctl;
|
|
|
|
#define CDEV_MAJOR 58
|
|
static struct cdevsw dgb_cdevsw = {
|
|
.d_open = dgbopen,
|
|
.d_close = dgbclose,
|
|
.d_read = ttyread,
|
|
.d_write = ttywrite,
|
|
.d_ioctl = dgbioctl,
|
|
.d_poll = ttypoll,
|
|
.d_name = "dgb",
|
|
.d_maj = CDEV_MAJOR,
|
|
.d_flags = D_TTY,
|
|
.d_kqfilter = ttykqfilter,
|
|
};
|
|
|
|
static speed_t dgbdefaultrate = TTYDEF_SPEED;
|
|
|
|
static struct speedtab dgbspeedtab[] = {
|
|
{ 0, FEP_B0 }, /* old (sysV-like) Bx codes */
|
|
{ 50, FEP_B50 },
|
|
{ 75, FEP_B75 },
|
|
{ 110, FEP_B110 },
|
|
{ 134, FEP_B134 },
|
|
{ 150, FEP_B150 },
|
|
{ 200, FEP_B200 },
|
|
{ 300, FEP_B300 },
|
|
{ 600, FEP_B600 },
|
|
{ 1200, FEP_B1200 },
|
|
{ 1800, FEP_B1800 },
|
|
{ 2400, FEP_B2400 },
|
|
{ 4800, FEP_B4800 },
|
|
{ 9600, FEP_B9600 },
|
|
{ 19200, FEP_B19200 },
|
|
{ 38400, FEP_B38400 },
|
|
{ 57600, (FEP_FASTBAUD|FEP_B50) }, /* B50 & fast baud table */
|
|
{ 115200, (FEP_FASTBAUD|FEP_B110) }, /* B100 & fast baud table */
|
|
{ -1, -1 }
|
|
};
|
|
|
|
static struct dbgflagtbl
|
|
{
|
|
tcflag_t in_mask;
|
|
tcflag_t in_val;
|
|
tcflag_t out_val;
|
|
} dgb_cflags[] =
|
|
{
|
|
{ PARODD, PARODD, FEP_PARODD },
|
|
{ PARENB, PARENB, FEP_PARENB },
|
|
{ CSTOPB, CSTOPB, FEP_CSTOPB },
|
|
{ CSIZE, CS5, FEP_CS6 },
|
|
{ CSIZE, CS6, FEP_CS6 },
|
|
{ CSIZE, CS7, FEP_CS7 },
|
|
{ CSIZE, CS8, FEP_CS8 },
|
|
{ CLOCAL, CLOCAL, FEP_CLOCAL },
|
|
{ (tcflag_t)-1 }
|
|
}, dgb_iflags[] =
|
|
{
|
|
{ IGNBRK, IGNBRK, FEP_IGNBRK },
|
|
{ BRKINT, BRKINT, FEP_BRKINT },
|
|
{ IGNPAR, IGNPAR, FEP_IGNPAR },
|
|
{ PARMRK, PARMRK, FEP_PARMRK },
|
|
{ INPCK, INPCK, FEP_INPCK },
|
|
{ ISTRIP, ISTRIP, FEP_ISTRIP },
|
|
{ IXON, IXON, FEP_IXON },
|
|
{ IXOFF, IXOFF, FEP_IXOFF },
|
|
{ IXANY, IXANY, FEP_IXANY },
|
|
{ (tcflag_t)-1 }
|
|
}, dgb_flow[] =
|
|
{
|
|
{ CRTSCTS, CRTSCTS, CTS|RTS },
|
|
{ CRTSCTS, CCTS_OFLOW, CTS },
|
|
{ CRTSCTS, CRTS_IFLOW, RTS },
|
|
{ (tcflag_t)-1 }
|
|
};
|
|
|
|
/* xlat bsd termios flags to dgb sys-v style */
|
|
static tcflag_t
|
|
dgbflags(struct dbgflagtbl *tbl, tcflag_t input)
|
|
{
|
|
tcflag_t output = 0;
|
|
int i;
|
|
|
|
for (i=0; tbl[i].in_mask != (tcflag_t)-1; i++)
|
|
{
|
|
if ((input & tbl[i].in_mask) == tbl[i].in_val)
|
|
output |= tbl[i].out_val;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
#ifdef DGB_DEBUG
|
|
static int dgbdebug=0;
|
|
SYSCTL_INT(_debug, OID_AUTO, dgb_debug, CTLFLAG_RW, &dgbdebug, 0, "");
|
|
#endif
|
|
|
|
static __inline int setwin(struct dgb_softc *sc, unsigned addr);
|
|
static __inline int setinitwin(struct dgb_softc *sc, unsigned addr);
|
|
static __inline void hidewin(struct dgb_softc *sc);
|
|
static __inline void towin(struct dgb_softc *sc, int win);
|
|
|
|
/*Helg: to allow recursive dgb...() calls */
|
|
typedef struct
|
|
{ /* If we were called and don't want to disturb we need: */
|
|
int port; /* write to this port */
|
|
u_char data; /* this data on exit */
|
|
/* or DATA_WINOFF to close memory window on entry */
|
|
} BoardMemWinState; /* so several channels and even boards can coexist */
|
|
#define DATA_WINOFF 0
|
|
static BoardMemWinState bmws;
|
|
|
|
/* return current memory window state and close window */
|
|
static BoardMemWinState
|
|
bmws_get(void)
|
|
{
|
|
BoardMemWinState bmwsRet=bmws;
|
|
if(bmws.data!=DATA_WINOFF)
|
|
outb(bmws.port, bmws.data=DATA_WINOFF);
|
|
return bmwsRet;
|
|
}
|
|
|
|
/* restore memory window state */
|
|
static void
|
|
bmws_set(BoardMemWinState ws)
|
|
{
|
|
if(ws.data != bmws.data || ws.port!=bmws.port ) {
|
|
if(bmws.data!=DATA_WINOFF)
|
|
outb(bmws.port,DATA_WINOFF);
|
|
if(ws.data!=DATA_WINOFF)
|
|
outb(ws.port, ws.data);
|
|
bmws=ws;
|
|
}
|
|
}
|
|
|
|
static __inline int
|
|
setwin(sc,addr)
|
|
struct dgb_softc *sc;
|
|
unsigned int addr;
|
|
{
|
|
if(sc->type==PCXEVE) {
|
|
outb(bmws.port=sc->port+1, bmws.data=FEPWIN|(addr>>13));
|
|
DPRINT3(DB_WIN,"dgb%d: switched to window 0x%x\n",sc->unit,addr>>13);
|
|
return (addr & 0x1FFF);
|
|
} else {
|
|
outb(bmws.port=sc->port,bmws.data=FEPMEM);
|
|
return addr;
|
|
}
|
|
}
|
|
|
|
static __inline int
|
|
setinitwin(sc,addr)
|
|
struct dgb_softc *sc;
|
|
unsigned int addr;
|
|
{
|
|
if(sc->type==PCXEVE) {
|
|
outb(bmws.port=sc->port+1, bmws.data=FEPWIN|(addr>>13));
|
|
DPRINT3(DB_WIN,"dgb%d: switched to window 0x%x\n",sc->unit,addr>>13);
|
|
return (addr & 0x1FFF);
|
|
} else {
|
|
outb(bmws.port=sc->port,bmws.data=inb(sc->port)|FEPMEM);
|
|
return addr;
|
|
}
|
|
}
|
|
|
|
static __inline void
|
|
hidewin(sc)
|
|
struct dgb_softc *sc;
|
|
{
|
|
bmws.data=0;
|
|
if(sc->type==PCXEVE)
|
|
outb(bmws.port=sc->port+1, bmws.data);
|
|
else
|
|
outb(bmws.port=sc->port, bmws.data);
|
|
}
|
|
|
|
static __inline void
|
|
towin(sc,win)
|
|
struct dgb_softc *sc;
|
|
int win;
|
|
{
|
|
if(sc->type==PCXEVE) {
|
|
outb(bmws.port=sc->port+1, bmws.data=win);
|
|
} else {
|
|
outb(bmws.port=sc->port,bmws.data=FEPMEM);
|
|
}
|
|
}
|
|
|
|
static int
|
|
dgbprobe(dev)
|
|
struct isa_device *dev;
|
|
{
|
|
struct dgb_softc *sc= &dgb_softc[dev->id_unit];
|
|
int i, v;
|
|
u_long win_size; /* size of vizible memory window */
|
|
int unit=dev->id_unit;
|
|
|
|
sc->unit=dev->id_unit;
|
|
sc->port=dev->id_iobase;
|
|
|
|
if(dev->id_flags & DGBFLAG_ALTPIN)
|
|
sc->altpin=1;
|
|
else
|
|
sc->altpin=0;
|
|
|
|
/* left 24 bits only (ISA address) */
|
|
sc->pmem=((intptr_t)(void *)dev->id_maddr & 0xFFFFFF);
|
|
|
|
DPRINT4(DB_INFO,"dgb%d: port 0x%x mem 0x%lx\n",unit,sc->port,sc->pmem);
|
|
|
|
outb(sc->port, FEPRST);
|
|
sc->status=DISABLED;
|
|
|
|
for(i=0; i< 1000; i++) {
|
|
DELAY(1);
|
|
if( (inb(sc->port) & FEPMASK) == FEPRST ) {
|
|
sc->status=ENABLED;
|
|
DPRINT3(DB_EXCEPT,"dgb%d: got reset after %d us\n",unit,i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(sc->status!=ENABLED) {
|
|
DPRINT2(DB_EXCEPT,"dgb%d: failed to respond\n",dev->id_unit);
|
|
return 0;
|
|
}
|
|
|
|
/* check type of card and get internal memory characteristics */
|
|
|
|
v=inb(sc->port);
|
|
|
|
if( v & 0x1 ) {
|
|
switch( v&0x30 ) {
|
|
case 0:
|
|
sc->mem_seg=0xF000;
|
|
win_size=0x10000;
|
|
printf("dgb%d: PC/Xi 64K\n",dev->id_unit);
|
|
break;
|
|
case 0x10:
|
|
sc->mem_seg=0xE000;
|
|
win_size=0x20000;
|
|
printf("dgb%d: PC/Xi 128K\n",dev->id_unit);
|
|
break;
|
|
case 0x20:
|
|
sc->mem_seg=0xC000;
|
|
win_size=0x40000;
|
|
printf("dgb%d: PC/Xi 256K\n",dev->id_unit);
|
|
break;
|
|
default: /* case 0x30: */
|
|
sc->mem_seg=0x8000;
|
|
win_size=0x80000;
|
|
printf("dgb%d: PC/Xi 512K\n",dev->id_unit);
|
|
break;
|
|
}
|
|
sc->type=PCXI;
|
|
} else {
|
|
outb(sc->port, 1);
|
|
v=inb(sc->port);
|
|
|
|
if( v & 0x1 ) {
|
|
printf("dgb%d: PC/Xm isn't supported\n",dev->id_unit);
|
|
sc->status=DISABLED;
|
|
return 0;
|
|
}
|
|
|
|
sc->mem_seg=0xF000;
|
|
|
|
if(dev->id_flags==DGBFLAG_NOWIN || ( v&0xC0 )==0) {
|
|
win_size=0x10000;
|
|
printf("dgb%d: PC/Xe 64K\n",dev->id_unit);
|
|
sc->type=PCXE;
|
|
} else {
|
|
win_size=0x2000;
|
|
printf("dgb%d: PC/Xe 64/8K (windowed)\n",dev->id_unit);
|
|
sc->type=PCXEVE;
|
|
if((u_long)sc->pmem & ~0xFFE000) {
|
|
printf("dgb%d: warning: address 0x%lx truncated to 0x%lx\n",
|
|
dev->id_unit, sc->pmem,
|
|
sc->pmem & 0xFFE000);
|
|
|
|
dev->id_maddr= (u_char *)(void *)(intptr_t)( sc->pmem & 0xFFE000 );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* save size of vizible memory segment */
|
|
dev->id_msize=win_size;
|
|
|
|
/* map memory */
|
|
dev->id_maddr=sc->vmem=pmap_mapdev(sc->pmem,dev->id_msize);
|
|
|
|
outb(sc->port, FEPCLR); /* drop RESET */
|
|
hidewin(sc); /* Helg: to set initial bmws state */
|
|
|
|
return 4; /* we need I/O space of 4 ports */
|
|
}
|
|
|
|
static int
|
|
dgbattach(dev)
|
|
struct isa_device *dev;
|
|
{
|
|
int unit=dev->id_unit;
|
|
struct dgb_softc *sc= &dgb_softc[dev->id_unit];
|
|
int i, t;
|
|
u_char volatile *mem;
|
|
u_char volatile *ptr;
|
|
int addr;
|
|
struct dgb_p *port;
|
|
volatile struct board_chan *bc;
|
|
int shrinkmem;
|
|
int nfails;
|
|
volatile ushort *pstat;
|
|
int lowwater;
|
|
static int nports=0;
|
|
char suffix;
|
|
|
|
if(sc->status!=ENABLED) {
|
|
DPRINT2(DB_EXCEPT,"dbg%d: try to attach a disabled card\n",unit);
|
|
return 0;
|
|
}
|
|
|
|
mem=sc->vmem;
|
|
|
|
DPRINT3(DB_INFO,"dgb%d: internal memory segment 0x%x\n",unit,sc->mem_seg);
|
|
|
|
outb(sc->port, FEPRST); DELAY(1);
|
|
|
|
for(i=0; (inb(sc->port) & FEPMASK) != FEPRST ; i++) {
|
|
if(i>10000) {
|
|
printf("dgb%d: 1st reset failed\n",dev->id_unit);
|
|
sc->status=DISABLED;
|
|
hidewin(sc);
|
|
return 0;
|
|
}
|
|
DELAY(1);
|
|
}
|
|
|
|
DPRINT3(DB_INFO,"dgb%d: got reset after %d us\n",unit,i);
|
|
|
|
/* for PCXEVE set up interrupt and base address */
|
|
|
|
if(sc->type==PCXEVE) {
|
|
t=(((u_long)sc->pmem>>8) & 0xFFE0) | 0x10 /* enable windowing */;
|
|
/* IRQ isn't used */
|
|
outb(sc->port+2,t & 0xFF);
|
|
outb(sc->port+3,t>>8);
|
|
} else if(sc->type==PCXE) {
|
|
t=(((u_long)sc->pmem>>8) & 0xFFE0) /* disable windowing */;
|
|
outb(sc->port+2,t & 0xFF);
|
|
outb(sc->port+3,t>>8);
|
|
}
|
|
|
|
|
|
if(sc->type==PCXI || sc->type==PCXE) {
|
|
outb(sc->port, FEPRST|FEPMEM); DELAY(1);
|
|
|
|
for(i=0; (inb(sc->port) & FEPMASK) != (FEPRST|FEPMEM) ; i++) {
|
|
if(i>10000) {
|
|
printf("dgb%d: 2nd reset failed\n",dev->id_unit);
|
|
sc->status=DISABLED;
|
|
hidewin(sc);
|
|
return 0;
|
|
}
|
|
DELAY(1);
|
|
}
|
|
|
|
DPRINT3(DB_INFO,"dgb%d: got memory after %d us\n",unit,i);
|
|
}
|
|
|
|
mem=sc->vmem;
|
|
|
|
/* very short memory test */
|
|
|
|
addr=setinitwin(sc,BOTWIN);
|
|
*(u_long volatile *)(mem+addr) = 0xA55A3CC3;
|
|
if(*(u_long volatile *)(mem+addr)!=0xA55A3CC3) {
|
|
printf("dgb%d: 1st memory test failed\n",dev->id_unit);
|
|
sc->status=DISABLED;
|
|
hidewin(sc);
|
|
return 0;
|
|
}
|
|
|
|
addr=setinitwin(sc,TOPWIN);
|
|
*(u_long volatile *)(mem+addr) = 0x5AA5C33C;
|
|
if(*(u_long volatile *)(mem+addr)!=0x5AA5C33C) {
|
|
printf("dgb%d: 2nd memory test failed\n",dev->id_unit);
|
|
sc->status=DISABLED;
|
|
hidewin(sc);
|
|
return 0;
|
|
}
|
|
|
|
addr=setinitwin(sc,BIOSCODE+((0xF000-sc->mem_seg)<<4));
|
|
*(u_long volatile *)(mem+addr) = 0x5AA5C33C;
|
|
if(*(u_long volatile *)(mem+addr)!=0x5AA5C33C) {
|
|
printf("dgb%d: 3rd (BIOS) memory test failed\n",dev->id_unit);
|
|
}
|
|
|
|
addr=setinitwin(sc,MISCGLOBAL);
|
|
for(i=0; i<16; i++) {
|
|
mem[addr+i]=0;
|
|
}
|
|
|
|
if(sc->type==PCXI || sc->type==PCXE) {
|
|
|
|
addr=BIOSCODE+((0xF000-sc->mem_seg)<<4);
|
|
|
|
DPRINT3(DB_INFO,"dgb%d: BIOS local address=0x%x\n",unit,addr);
|
|
|
|
ptr= mem+addr;
|
|
|
|
for(i=0; i<pcxx_nbios; i++, ptr++)
|
|
*ptr = pcxx_bios[i];
|
|
|
|
ptr= mem+addr;
|
|
|
|
nfails=0;
|
|
for(i=0; i<pcxx_nbios; i++, ptr++)
|
|
if( *ptr != pcxx_bios[i] ) {
|
|
DPRINT5(DB_EXCEPT,"dgb%d: wrong code in BIOS at addr 0x%x : \
|
|
0x%x instead of 0x%x\n", unit, ptr-(mem+addr), *ptr, pcxx_bios[i] );
|
|
|
|
if(++nfails>=5) {
|
|
printf("dgb%d: 4th memory test (BIOS load) fails\n",unit);
|
|
break;
|
|
}
|
|
}
|
|
|
|
outb(sc->port,FEPMEM);
|
|
|
|
for(i=0; (inb(sc->port) & FEPMASK) != FEPMEM ; i++) {
|
|
if(i>10000) {
|
|
printf("dgb%d: BIOS start failed\n",dev->id_unit);
|
|
sc->status=DISABLED;
|
|
hidewin(sc);
|
|
return 0;
|
|
}
|
|
DELAY(1);
|
|
}
|
|
|
|
DPRINT3(DB_INFO,"dgb%d: reset dropped after %d us\n",unit,i);
|
|
|
|
for(i=0; i<200000; i++) {
|
|
if( *((ushort volatile *)(mem+MISCGLOBAL)) == *((ushort *)"GD") )
|
|
goto load_fep;
|
|
DELAY(1);
|
|
}
|
|
printf("dgb%d: BIOS download failed\n",dev->id_unit);
|
|
DPRINT4(DB_EXCEPT,"dgb%d: code=0x%x must be 0x%x\n",
|
|
dev->id_unit,
|
|
*((ushort volatile *)(mem+MISCGLOBAL)),
|
|
*((ushort *)"GD"));
|
|
|
|
sc->status=DISABLED;
|
|
hidewin(sc);
|
|
return 0;
|
|
}
|
|
|
|
if(sc->type==PCXEVE) {
|
|
/* set window 7 */
|
|
outb(sc->port+1,0xFF);
|
|
|
|
ptr= mem+(BIOSCODE & 0x1FFF);
|
|
|
|
for(i=0; i<pcxx_nbios; i++)
|
|
*ptr++ = pcxx_bios[i];
|
|
|
|
ptr= mem+(BIOSCODE & 0x1FFF);
|
|
|
|
nfails=0;
|
|
for(i=0; i<pcxx_nbios; i++, ptr++)
|
|
if( *ptr != pcxx_bios[i] ) {
|
|
DPRINT5(DB_EXCEPT,"dgb%d: wrong code in BIOS at addr 0x%x : \
|
|
0x%x instead of 0x%x\n", unit, ptr-(mem+addr), *ptr, pcxx_bios[i] );
|
|
|
|
if(++nfails>=5) {
|
|
printf("dgb%d: 4th memory test (BIOS load) fails\n",unit);
|
|
break;
|
|
}
|
|
}
|
|
|
|
outb(sc->port,FEPCLR);
|
|
|
|
setwin(sc,0);
|
|
|
|
for(i=0; (inb(sc->port) & FEPMASK) != FEPCLR ; i++) {
|
|
if(i>10000) {
|
|
printf("dgb%d: BIOS start failed\n",dev->id_unit);
|
|
sc->status=DISABLED;
|
|
hidewin(sc);
|
|
return 0;
|
|
}
|
|
DELAY(1);
|
|
}
|
|
|
|
DPRINT3(DB_INFO,"dgb%d: reset dropped after %d us\n",unit,i);
|
|
|
|
addr=setwin(sc,MISCGLOBAL);
|
|
|
|
for(i=0; i<200000; i++) {
|
|
if(*(ushort volatile *)(mem+addr)== *(ushort *)"GD")
|
|
goto load_fep;
|
|
DELAY(1);
|
|
}
|
|
printf("dgb%d: BIOS download failed\n",dev->id_unit);
|
|
DPRINT5(DB_EXCEPT,"dgb%d: Error#(0x%x,0x%x) code=0x%x\n",
|
|
dev->id_unit,
|
|
*(ushort volatile *)(mem+0xC12),
|
|
*(ushort volatile *)(mem+0xC14),
|
|
*(ushort volatile *)(mem+MISCGLOBAL));
|
|
|
|
sc->status=DISABLED;
|
|
hidewin(sc);
|
|
return 0;
|
|
}
|
|
|
|
load_fep:
|
|
DPRINT2(DB_INFO,"dgb%d: BIOS loaded\n",dev->id_unit);
|
|
|
|
addr=setwin(sc,FEPCODE);
|
|
|
|
ptr= mem+addr;
|
|
|
|
for(i=0; i<pcxx_ncook; i++)
|
|
*ptr++ = pcxx_cook[i];
|
|
|
|
addr=setwin(sc,MBOX);
|
|
*(ushort volatile *)(mem+addr+ 0)=2;
|
|
*(ushort volatile *)(mem+addr+ 2)=sc->mem_seg+FEPCODESEG;
|
|
*(ushort volatile *)(mem+addr+ 4)=0;
|
|
*(ushort volatile *)(mem+addr+ 6)=FEPCODESEG;
|
|
*(ushort volatile *)(mem+addr+ 8)=0;
|
|
*(ushort volatile *)(mem+addr+10)=pcxx_ncook;
|
|
|
|
outb(sc->port,FEPMEM|FEPINT); /* send interrupt to BIOS */
|
|
outb(sc->port,FEPMEM);
|
|
|
|
for(i=0; *(ushort volatile *)(mem+addr)!=0; i++) {
|
|
if(i>200000) {
|
|
printf("dgb%d: FEP code download failed\n",unit);
|
|
DPRINT3(DB_EXCEPT,"dgb%d: code=0x%x must be 0\n", unit,
|
|
*(ushort volatile *)(mem+addr));
|
|
sc->status=DISABLED;
|
|
hidewin(sc);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
DPRINT2(DB_INFO,"dgb%d: FEP code loaded\n",unit);
|
|
|
|
*(ushort volatile *)(mem+setwin(sc,FEPSTAT))=0;
|
|
addr=setwin(sc,MBOX);
|
|
*(ushort volatile *)(mem+addr+0)=1;
|
|
*(ushort volatile *)(mem+addr+2)=FEPCODESEG;
|
|
*(ushort volatile *)(mem+addr+4)=0x4;
|
|
|
|
outb(sc->port,FEPINT); /* send interrupt to BIOS */
|
|
outb(sc->port,FEPCLR);
|
|
|
|
addr=setwin(sc,FEPSTAT);
|
|
for(i=0; *(ushort volatile *)(mem+addr)!= *(ushort *)"OS"; i++) {
|
|
if(i>200000) {
|
|
printf("dgb%d: FEP/OS start failed\n",dev->id_unit);
|
|
sc->status=DISABLED;
|
|
hidewin(sc);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
DPRINT2(DB_INFO,"dgb%d: FEP/OS started\n",dev->id_unit);
|
|
|
|
sc->numports= *(ushort volatile *)(mem+setwin(sc,NPORT));
|
|
|
|
printf("dgb%d: %d ports\n",unit,sc->numports);
|
|
|
|
if(sc->numports > MAX_DGB_PORTS) {
|
|
printf("dgb%d: too many ports\n",unit);
|
|
sc->status=DISABLED;
|
|
hidewin(sc);
|
|
return 0;
|
|
}
|
|
|
|
if(nports+sc->numports>NDGBPORTS) {
|
|
printf("dgb%d: only %d ports are usable\n", unit, NDGBPORTS-nports);
|
|
sc->numports=NDGBPORTS-nports;
|
|
}
|
|
|
|
/* allocate port and tty structures */
|
|
sc->ports=&dgb_ports[nports];
|
|
sc->ttys=&dgb_tty[nports];
|
|
nports+=sc->numports;
|
|
|
|
addr=setwin(sc,PORTBASE);
|
|
pstat=(ushort volatile *)(mem+addr);
|
|
|
|
for(i=0; i<sc->numports && pstat[i]; i++)
|
|
if(pstat[i])
|
|
sc->ports[i].status=ENABLED;
|
|
else {
|
|
sc->ports[i].status=DISABLED;
|
|
printf("dgb%d: port%d is broken\n", unit, i);
|
|
}
|
|
|
|
/* We should now init per-port structures */
|
|
bc=(volatile struct board_chan *)(mem + CHANSTRUCT);
|
|
sc->mailbox=(volatile struct global_data *)(mem + FEP_GLOBAL);
|
|
|
|
if(sc->numports<3)
|
|
shrinkmem=1;
|
|
else
|
|
shrinkmem=0;
|
|
|
|
for(i=0; i<sc->numports; i++, bc++) {
|
|
port= &sc->ports[i];
|
|
|
|
port->tty=&sc->ttys[i];
|
|
port->unit=unit;
|
|
|
|
port->brdchan=bc;
|
|
|
|
if(sc->altpin) {
|
|
port->dsr=CD;
|
|
port->dcd=DSR;
|
|
} else {
|
|
port->dcd=CD;
|
|
port->dsr=DSR;
|
|
}
|
|
|
|
port->pnum=i;
|
|
|
|
if(shrinkmem) {
|
|
DPRINT2(DB_INFO,"dgb%d: shrinking memory\n",unit);
|
|
fepcmd(port, SETBUFFER, 32, 0, 0, 0);
|
|
shrinkmem=0;
|
|
}
|
|
|
|
if(sc->type!=PCXEVE) {
|
|
port->txptr=(u_char *)(uintptr_t)(volatile void *)
|
|
(mem+((bc->tseg-sc->mem_seg)<<4));
|
|
port->rxptr=(u_char *)(uintptr_t)(volatile void *)
|
|
(mem+((bc->rseg-sc->mem_seg)<<4));
|
|
port->txwin=port->rxwin=0;
|
|
} else {
|
|
port->txptr=(u_char *)(uintptr_t)(volatile void *)
|
|
(mem+( ((bc->tseg-sc->mem_seg)<<4) & 0x1FFF ));
|
|
port->rxptr=(u_char *)(uintptr_t)(volatile void *)
|
|
(mem+( ((bc->rseg-sc->mem_seg)<<4) & 0x1FFF ));
|
|
port->txwin=FEPWIN | ((bc->tseg-sc->mem_seg)>>9);
|
|
port->rxwin=FEPWIN | ((bc->rseg-sc->mem_seg)>>9);
|
|
}
|
|
|
|
port->txbufhead=0;
|
|
port->rxbufhead=0;
|
|
port->txbufsize=bc->tmax+1;
|
|
port->rxbufsize=bc->rmax+1;
|
|
|
|
lowwater= (port->txbufsize>=2000) ? 1024 : (port->txbufsize/2);
|
|
setwin(sc,0);
|
|
fepcmd(port, STXLWATER, lowwater, 0, 10, 0);
|
|
fepcmd(port, SRXLWATER, port->rxbufsize/4, 0, 10, 0);
|
|
fepcmd(port, SRXHWATER, 3*port->rxbufsize/4, 0, 10, 0);
|
|
|
|
bc->edelay=100;
|
|
bc->idata=1;
|
|
|
|
port->startc=bc->startc;
|
|
port->startca=bc->startca;
|
|
port->stopc=bc->stopc;
|
|
port->stopca=bc->stopca;
|
|
|
|
/*port->close_delay=50;*/
|
|
port->close_delay=3 * hz;
|
|
port->do_timestamp=0;
|
|
port->do_dcd_timestamp=0;
|
|
|
|
/*
|
|
* 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 = TTYDEF_IFLAG;
|
|
port->it_in.c_oflag = TTYDEF_OFLAG;
|
|
port->it_in.c_cflag = TTYDEF_CFLAG;
|
|
port->it_in.c_lflag = TTYDEF_LFLAG;
|
|
termioschars(&port->it_in);
|
|
port->it_in.c_ispeed = port->it_in.c_ospeed = dgbdefaultrate;
|
|
port->it_out = port->it_in;
|
|
/* MAX_DGB_PORTS is 32 => [0-9a-v] */
|
|
suffix = i < 10 ? '0' + i : 'a' + i - 10;
|
|
make_dev(&dgb_cdevsw, (unit*32)+i,
|
|
UID_ROOT, GID_WHEEL, 0600, "ttyD%d%c", unit, suffix);
|
|
|
|
make_dev(&dgb_cdevsw, (unit*32)+i+32,
|
|
UID_ROOT, GID_WHEEL, 0600, "ttyiD%d%c", unit, suffix);
|
|
|
|
make_dev(&dgb_cdevsw, (unit*32)+i+64,
|
|
UID_ROOT, GID_WHEEL, 0600, "ttylD%d%c", unit, suffix);
|
|
|
|
make_dev(&dgb_cdevsw, (unit*32)+i+128,
|
|
UID_UUCP, GID_DIALER, 0660, "cuaD%d%c", unit, suffix);
|
|
|
|
make_dev(&dgb_cdevsw, (unit*32)+i+160,
|
|
UID_UUCP, GID_DIALER, 0660, "cuaiD%d%c", unit, suffix);
|
|
|
|
make_dev(&dgb_cdevsw, (unit*32)+i+192,
|
|
UID_UUCP, GID_DIALER, 0660, "cualD%d%c", unit, suffix);
|
|
}
|
|
|
|
hidewin(sc);
|
|
|
|
/* register the polling function */
|
|
timeout(dgbpoll, (void *)unit, hz/POLLSPERSEC);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static int
|
|
dgbopen(dev, flag, mode, td)
|
|
dev_t dev;
|
|
int flag;
|
|
int mode;
|
|
struct thread *td;
|
|
{
|
|
struct dgb_softc *sc;
|
|
struct tty *tp;
|
|
int unit;
|
|
int mynor;
|
|
int pnum;
|
|
struct dgb_p *port;
|
|
int s,cs;
|
|
int error;
|
|
volatile struct board_chan *bc;
|
|
|
|
error=0;
|
|
|
|
mynor=minor(dev);
|
|
unit=MINOR_TO_UNIT(mynor);
|
|
pnum=MINOR_TO_PORT(mynor);
|
|
|
|
if(unit >= NDGB) {
|
|
DPRINT2(DB_EXCEPT,"dgb%d: try to open a nonexisting card\n",unit);
|
|
return ENXIO;
|
|
}
|
|
|
|
sc=&dgb_softc[unit];
|
|
|
|
if(sc->status!=ENABLED) {
|
|
DPRINT2(DB_EXCEPT,"dgb%d: try to open a disabled card\n",unit);
|
|
return ENXIO;
|
|
}
|
|
|
|
if(pnum>=sc->numports) {
|
|
DPRINT3(DB_EXCEPT,"dgb%d: try to open non-existing port %d\n",unit,pnum);
|
|
return ENXIO;
|
|
}
|
|
|
|
if(mynor & CONTROL_MASK)
|
|
return 0;
|
|
|
|
tp=&sc->ttys[pnum];
|
|
dev->si_tty = tp;
|
|
port=&sc->ports[pnum];
|
|
bc=port->brdchan;
|
|
|
|
open_top:
|
|
|
|
s=spltty();
|
|
|
|
while(port->closing) {
|
|
error=tsleep(&port->closing, TTOPRI|PCATCH, "dgocl", 0);
|
|
|
|
if(error) {
|
|
DPRINT4(DB_OPEN,"dgb%d: port%d: tsleep(dgocl) error=%d\n",unit,pnum,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;
|
|
DPRINT4(DB_OPEN,"dgb%d: port%d: BUSY error=%d\n",unit,pnum,error);
|
|
goto out;
|
|
}
|
|
} else {
|
|
if (port->active_out) {
|
|
if (flag & O_NONBLOCK) {
|
|
error = EBUSY;
|
|
DPRINT4(DB_OPEN,"dgb%d: port%d: BUSY error=%d\n",unit,pnum,error);
|
|
goto out;
|
|
}
|
|
error = tsleep(&port->active_out,
|
|
TTIPRI | PCATCH, "dgbi", 0);
|
|
if (error != 0) {
|
|
DPRINT4(DB_OPEN,"dgb%d: port%d: tsleep(dgbi) error=%d\n",
|
|
unit,pnum,error);
|
|
goto out;
|
|
}
|
|
splx(s);
|
|
goto open_top;
|
|
}
|
|
}
|
|
if (tp->t_state & TS_XCLUDE &&
|
|
suser(td)) {
|
|
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=dgbstart;
|
|
tp->t_param=dgbparam;
|
|
tp->t_stop=dgbstop;
|
|
tp->t_dev=dev;
|
|
tp->t_termios= (mynor & CALLOUT_MASK) ?
|
|
port->it_out :
|
|
port->it_in;
|
|
|
|
cs=splclock();
|
|
setwin(sc,0);
|
|
port->imodem=bc->mstat;
|
|
bc->rout=bc->rin; /* clear input queue */
|
|
bc->idata=1;
|
|
#ifdef PRINT_BUFSIZE
|
|
printf("dgb buffers tx=%x:%x rx=%x:%x\n",bc->tseg,bc->tmax,bc->rseg,bc->rmax);
|
|
#endif
|
|
|
|
hidewin(sc);
|
|
splx(cs);
|
|
|
|
port->wopeners++;
|
|
error=dgbparam(tp, &tp->t_termios);
|
|
port->wopeners--;
|
|
|
|
if(error!=0) {
|
|
DPRINT4(DB_OPEN,"dgb%d: port%d: dgbparam error=%d\n",unit,pnum,error);
|
|
goto out;
|
|
}
|
|
|
|
/* handle fake DCD for callout devices */
|
|
/* and initial DCD */
|
|
|
|
if( (port->imodem & 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, "dgdcd", 0);
|
|
--port->wopeners;
|
|
if (error != 0) {
|
|
DPRINT4(DB_OPEN,"dgb%d: port%d: tsleep(dgdcd) error=%d\n",unit,pnum,error);
|
|
goto out;
|
|
}
|
|
splx(s);
|
|
goto open_top;
|
|
}
|
|
error = linesw[tp->t_line].l_open(dev, tp);
|
|
disc_optim(tp,&tp->t_termios);
|
|
DPRINT4(DB_OPEN,"dgb%d: port%d: l_open error=%d\n",unit,pnum,error);
|
|
|
|
if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
|
|
port->active_out = TRUE;
|
|
|
|
port->used=1;
|
|
|
|
/* If any port is open (i.e. the open() call is completed for it)
|
|
* the device is busy
|
|
*/
|
|
|
|
out:
|
|
disc_optim(tp,&tp->t_termios);
|
|
splx(s);
|
|
|
|
if( !(tp->t_state & TS_ISOPEN) && port->wopeners==0 )
|
|
dgbhardclose(port);
|
|
|
|
DPRINT4(DB_OPEN,"dgb%d: port%d: open() returns %d\n",unit,pnum,error);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static int
|
|
dgbclose(dev, flag, mode, td)
|
|
dev_t dev;
|
|
int flag;
|
|
int mode;
|
|
struct thread *td;
|
|
{
|
|
int mynor;
|
|
struct tty *tp;
|
|
int unit, pnum;
|
|
struct dgb_softc *sc;
|
|
struct dgb_p *port;
|
|
int s;
|
|
int i;
|
|
|
|
mynor=minor(dev);
|
|
if(mynor & CONTROL_MASK)
|
|
return 0;
|
|
unit=MINOR_TO_UNIT(mynor);
|
|
pnum=MINOR_TO_PORT(mynor);
|
|
|
|
sc=&dgb_softc[unit];
|
|
tp=&sc->ttys[pnum];
|
|
port=sc->ports+pnum;
|
|
|
|
DPRINT3(DB_CLOSE,"dgb%d: port%d: closing\n",unit,pnum);
|
|
|
|
DPRINT3(DB_CLOSE,"dgb%d: port%d: draining port\n",unit,pnum);
|
|
dgb_drain_or_flush(port);
|
|
|
|
s=spltty();
|
|
|
|
port->closing=1;
|
|
DPRINT3(DB_CLOSE,"dgb%d: port%d: closing line disc\n",unit,pnum);
|
|
linesw[tp->t_line].l_close(tp,flag);
|
|
disc_optim(tp,&tp->t_termios);
|
|
|
|
DPRINT3(DB_CLOSE,"dgb%d: port%d: hard closing\n",unit,pnum);
|
|
dgbhardclose(port);
|
|
DPRINT3(DB_CLOSE,"dgb%d: port%d: closing tty\n",unit,pnum);
|
|
ttyclose(tp);
|
|
port->closing=0;
|
|
wakeup(&port->closing);
|
|
port->used=0;
|
|
|
|
/* mark the card idle when all ports are closed */
|
|
|
|
for(i=0; i<sc->numports; i++)
|
|
if(sc->ports[i].used)
|
|
break;
|
|
|
|
splx(s);
|
|
|
|
DPRINT3(DB_CLOSE,"dgb%d: port%d: closed\n",unit,pnum);
|
|
|
|
wakeup(TSA_CARR_ON(tp));
|
|
wakeup(&port->active_out);
|
|
port->active_out=0;
|
|
|
|
DPRINT3(DB_CLOSE,"dgb%d: port%d: close exit\n",unit,pnum);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
dgbhardclose(port)
|
|
struct dgb_p *port;
|
|
{
|
|
struct dgb_softc *sc=&dgb_softc[port->unit];
|
|
volatile struct board_chan *bc=port->brdchan;
|
|
int cs;
|
|
|
|
cs=splclock();
|
|
port->do_timestamp = 0;
|
|
setwin(sc,0);
|
|
|
|
bc->idata=0; bc->iempty=0; bc->ilow=0;
|
|
if(port->tty->t_cflag & HUPCL) {
|
|
port->omodem &= ~(RTS|DTR);
|
|
fepcmd(port, SETMODEM, 0, DTR|RTS, 0, 1);
|
|
}
|
|
|
|
hidewin(sc);
|
|
splx(cs);
|
|
|
|
timeout(dgb_pause, &port->brdchan, hz/2);
|
|
tsleep(&port->brdchan, TTIPRI | PCATCH, "dgclo", 0);
|
|
}
|
|
|
|
static void
|
|
dgb_pause(chan)
|
|
void *chan;
|
|
{
|
|
wakeup(chan);
|
|
}
|
|
|
|
static void
|
|
dgbpoll(unit_c)
|
|
void *unit_c;
|
|
{
|
|
int unit=(int)unit_c;
|
|
int pnum;
|
|
struct dgb_p *port;
|
|
struct dgb_softc *sc=&dgb_softc[unit];
|
|
int head, tail;
|
|
u_char *eventbuf;
|
|
int event, mstat, lstat;
|
|
volatile struct board_chan *bc;
|
|
struct tty *tp;
|
|
int rhead, rtail;
|
|
int whead, wtail;
|
|
int size;
|
|
u_char *ptr;
|
|
int ocount;
|
|
int ibuf_full,obuf_full;
|
|
|
|
BoardMemWinState ws=bmws_get();
|
|
|
|
if(sc->status==DISABLED) {
|
|
printf("dgb%d: polling of disabled board stopped\n",unit);
|
|
return;
|
|
}
|
|
|
|
setwin(sc,0);
|
|
|
|
head=sc->mailbox->ein;
|
|
tail=sc->mailbox->eout;
|
|
|
|
while(head!=tail) {
|
|
if(head >= FEP_IMAX-FEP_ISTART
|
|
|| tail >= FEP_IMAX-FEP_ISTART
|
|
|| (head|tail) & 03 ) {
|
|
printf("dgb%d: event queue's head or tail is wrong! hd=%d,tl=%d\n", unit,head,tail);
|
|
break;
|
|
}
|
|
|
|
eventbuf=sc->vmem+tail+FEP_ISTART;
|
|
pnum=eventbuf[0];
|
|
event=eventbuf[1];
|
|
mstat=eventbuf[2];
|
|
lstat=eventbuf[3];
|
|
|
|
port=&sc->ports[pnum];
|
|
bc=port->brdchan;
|
|
tp=&sc->ttys[pnum];
|
|
|
|
if(pnum>=sc->numports || port->status==DISABLED) {
|
|
printf("dgb%d: port%d: got event on nonexisting port\n",unit,pnum);
|
|
} else if(port->used || port->wopeners>0 ) {
|
|
|
|
int wrapmask=port->rxbufsize-1;
|
|
|
|
if( !(event & ALL_IND) )
|
|
printf("dgb%d: port%d: ? event 0x%x mstat 0x%x lstat 0x%x\n",
|
|
unit, pnum, event, mstat, lstat);
|
|
|
|
if(event & DATA_IND) {
|
|
DPRINT3(DB_DATA,"dgb%d: port%d: DATA_IND\n",unit,pnum);
|
|
|
|
rhead=bc->rin & wrapmask;
|
|
rtail=bc->rout & wrapmask;
|
|
|
|
if( !(tp->t_cflag & CREAD) || !port->used ) {
|
|
bc->rout=rhead;
|
|
goto end_of_data;
|
|
}
|
|
|
|
if(bc->orun) {
|
|
printf("dgb%d: port%d: overrun\n", unit, pnum);
|
|
bc->orun=0;
|
|
}
|
|
|
|
if(!(tp->t_state & TS_ISOPEN))
|
|
goto end_of_data;
|
|
|
|
for(ibuf_full=FALSE;rhead!=rtail && !ibuf_full;) {
|
|
DPRINT5(DB_RXDATA,"dgb%d: port%d: p rx head=%d tail=%d\n",
|
|
unit,pnum,rhead,rtail);
|
|
|
|
if(rhead>rtail)
|
|
size=rhead-rtail;
|
|
else
|
|
size=port->rxbufsize-rtail;
|
|
|
|
ptr=port->rxptr+rtail;
|
|
|
|
/* Helg: */
|
|
if( tp->t_rawq.c_cc + size > DGB_IBUFSIZE ) {
|
|
size=DGB_IBUFSIZE-tp->t_rawq.c_cc;
|
|
DPRINT1(DB_RXDATA,"*");
|
|
ibuf_full=TRUE;
|
|
}
|
|
|
|
if(size) {
|
|
if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
|
|
DPRINT1(DB_RXDATA,"!");
|
|
towin(sc,port->rxwin);
|
|
tk_nin += size;
|
|
tk_rawcc += size;
|
|
tp->t_rawcc += size;
|
|
b_to_q(ptr,size,&tp->t_rawq);
|
|
setwin(sc,0);
|
|
} else {
|
|
int i=size;
|
|
unsigned char chr;
|
|
do {
|
|
towin(sc,port->rxwin);
|
|
chr= *ptr++;
|
|
hidewin(sc);
|
|
(*linesw[tp->t_line].l_rint)(chr, tp);
|
|
} while (--i > 0 );
|
|
setwin(sc,0);
|
|
}
|
|
}
|
|
rtail= (rtail + size) & wrapmask;
|
|
bc->rout=rtail;
|
|
rhead=bc->rin & wrapmask;
|
|
hidewin(sc);
|
|
ttwakeup(tp);
|
|
setwin(sc,0);
|
|
}
|
|
end_of_data: ;
|
|
}
|
|
|
|
if(event & MODEMCHG_IND) {
|
|
DPRINT3(DB_MODEM,"dgb%d: port%d: MODEMCHG_IND\n",unit,pnum);
|
|
port->imodem=mstat;
|
|
if(mstat & port->dcd) {
|
|
hidewin(sc);
|
|
linesw[tp->t_line].l_modem(tp,1);
|
|
setwin(sc,0);
|
|
wakeup(TSA_CARR_ON(tp));
|
|
} else {
|
|
hidewin(sc);
|
|
linesw[tp->t_line].l_modem(tp,0);
|
|
setwin(sc,0);
|
|
if( port->draining) {
|
|
port->draining=0;
|
|
wakeup(&port->draining);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(event & BREAK_IND) {
|
|
if((tp->t_state & TS_ISOPEN) && (tp->t_iflag & IGNBRK)) {
|
|
DPRINT3(DB_BREAK,"dgb%d: port%d: BREAK_IND\n",unit,pnum);
|
|
hidewin(sc);
|
|
linesw[tp->t_line].l_rint(TTY_BI, tp);
|
|
setwin(sc,0);
|
|
}
|
|
}
|
|
|
|
/* Helg: with output flow control */
|
|
|
|
if(event & (LOWTX_IND | EMPTYTX_IND) ) {
|
|
DPRINT3(DB_TXDATA,"dgb%d: port%d: LOWTX_IND or EMPTYTX_IND\n",unit,pnum);
|
|
|
|
if( (event & EMPTYTX_IND ) && tp->t_outq.c_cc==0
|
|
&& port->draining) {
|
|
port->draining=0;
|
|
wakeup(&port->draining);
|
|
bc->ilow=0; bc->iempty=0;
|
|
} else {
|
|
|
|
int wrapmask=port->txbufsize-1;
|
|
|
|
for(obuf_full=FALSE; tp->t_outq.c_cc!=0 && !obuf_full; ) {
|
|
int s;
|
|
/* add "last-minute" data to write buffer */
|
|
if(!(tp->t_state & TS_BUSY)) {
|
|
hidewin(sc);
|
|
#ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */
|
|
ttwwakeup(tp);
|
|
#else
|
|
if(tp->t_outq.c_cc <= tp->t_lowat) {
|
|
if(tp->t_state & TS_ASLEEP) {
|
|
tp->t_state &= ~TS_ASLEEP;
|
|
wakeup(TSA_OLOWAT(tp));
|
|
}
|
|
/* selwakeup(&tp->t_wsel); */
|
|
}
|
|
#endif
|
|
setwin(sc,0);
|
|
}
|
|
s=spltty();
|
|
|
|
whead=bc->tin & wrapmask;
|
|
wtail=bc->tout & wrapmask;
|
|
|
|
if(whead<wtail)
|
|
size=wtail-whead-1;
|
|
else {
|
|
size=port->txbufsize-whead;
|
|
if(wtail==0)
|
|
size--;
|
|
}
|
|
|
|
if(size==0) {
|
|
DPRINT5(DB_WR,"dgb: head=%d tail=%d size=%d full=%d\n",
|
|
whead,wtail,size,obuf_full);
|
|
bc->iempty=1; bc->ilow=1;
|
|
obuf_full=TRUE;
|
|
splx(s);
|
|
break;
|
|
}
|
|
|
|
towin(sc,port->txwin);
|
|
|
|
ocount=q_to_b(&tp->t_outq, port->txptr+whead, size);
|
|
whead+=ocount;
|
|
|
|
setwin(sc,0);
|
|
bc->tin=whead;
|
|
bc->tin=whead & wrapmask;
|
|
splx(s);
|
|
}
|
|
|
|
if(obuf_full) {
|
|
DPRINT1(DB_WR," +BUSY\n");
|
|
tp->t_state|=TS_BUSY;
|
|
} else {
|
|
DPRINT1(DB_WR," -BUSY\n");
|
|
hidewin(sc);
|
|
#ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */
|
|
/* should clear TS_BUSY before ttwwakeup */
|
|
if(tp->t_state & TS_BUSY) {
|
|
tp->t_state &= ~TS_BUSY;
|
|
linesw[tp->t_line].l_start(tp);
|
|
ttwwakeup(tp);
|
|
}
|
|
#else
|
|
if(tp->t_state & TS_ASLEEP) {
|
|
tp->t_state &= ~TS_ASLEEP;
|
|
wakeup(TSA_OLOWAT(tp));
|
|
}
|
|
tp->t_state &= ~TS_BUSY;
|
|
#endif
|
|
setwin(sc,0);
|
|
}
|
|
}
|
|
}
|
|
bc->idata=1; /* require event on incoming data */
|
|
|
|
} else {
|
|
bc=port->brdchan;
|
|
DPRINT4(DB_EXCEPT,"dgb%d: port%d: got event 0x%x on closed port\n",
|
|
unit,pnum,event);
|
|
bc->rout=bc->rin;
|
|
bc->idata=bc->iempty=bc->ilow=0;
|
|
}
|
|
|
|
tail= (tail+4) & (FEP_IMAX-FEP_ISTART-4);
|
|
}
|
|
|
|
sc->mailbox->eout=tail;
|
|
bmws_set(ws);
|
|
|
|
timeout(dgbpoll, unit_c, hz/POLLSPERSEC);
|
|
}
|
|
|
|
static int
|
|
dgbioctl(dev, cmd, data, flag, td)
|
|
dev_t dev;
|
|
u_long cmd;
|
|
caddr_t data;
|
|
int flag;
|
|
struct thread *td;
|
|
{
|
|
struct dgb_softc *sc;
|
|
int unit, pnum;
|
|
struct dgb_p *port;
|
|
int mynor;
|
|
struct tty *tp;
|
|
volatile struct board_chan *bc;
|
|
int error;
|
|
int s,cs;
|
|
int tiocm_xxx;
|
|
|
|
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
|
|
u_long oldcmd;
|
|
struct termios term;
|
|
#endif
|
|
|
|
BoardMemWinState ws=bmws_get();
|
|
|
|
mynor=minor(dev);
|
|
unit=MINOR_TO_UNIT(mynor);
|
|
pnum=MINOR_TO_PORT(mynor);
|
|
|
|
sc=&dgb_softc[unit];
|
|
port=&sc->ports[pnum];
|
|
tp=&sc->ttys[pnum];
|
|
bc=port->brdchan;
|
|
|
|
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:
|
|
error = suser(td);
|
|
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);
|
|
}
|
|
}
|
|
|
|
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
|
|
term = tp->t_termios;
|
|
if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
|
|
DPRINT6(DB_PARAM,"dgb%d: port%d: dgbioctl-ISNOW c=0x%x i=0x%x l=0x%x\n",unit,pnum,term.c_cflag,term.c_iflag,term.c_lflag);
|
|
}
|
|
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;
|
|
|
|
DPRINT6(DB_PARAM,"dgb%d: port%d: dgbioctl-TOSET c=0x%x i=0x%x l=0x%x\n",unit,pnum,dt->c_cflag,dt->c_iflag,dt->c_lflag);
|
|
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);
|
|
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;
|
|
}
|
|
|
|
if(cmd==TIOCSTOP) {
|
|
cs=splclock();
|
|
setwin(sc,0);
|
|
fepcmd(port, PAUSETX, 0, 0, 0, 0);
|
|
bmws_set(ws);
|
|
splx(cs);
|
|
return 0;
|
|
} else if(cmd==TIOCSTART) {
|
|
cs=splclock();
|
|
setwin(sc,0);
|
|
fepcmd(port, RESUMETX, 0, 0, 0, 0);
|
|
bmws_set(ws);
|
|
splx(cs);
|
|
return 0;
|
|
}
|
|
|
|
if(cmd==TIOCSETAW || cmd==TIOCSETAF)
|
|
port->mustdrain=1;
|
|
|
|
error = linesw[tp->t_line].l_ioctl(tp, cmd, data, flag, td);
|
|
if (error != ENOIOCTL)
|
|
return error;
|
|
s = spltty();
|
|
error = ttioctl(tp, cmd, data, flag);
|
|
disc_optim(tp,&tp->t_termios);
|
|
port->mustdrain=0;
|
|
if (error != ENOIOCTL) {
|
|
splx(s);
|
|
if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
|
|
DPRINT6(DB_PARAM,"dgb%d: port%d: dgbioctl-RES c=0x%x i=0x%x l=0x%x\n",unit,pnum,tp->t_cflag,tp->t_iflag,tp->t_lflag);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case TIOCSBRK:
|
|
/* Helg: commented */
|
|
/* error=dgbdrain(port);*/
|
|
|
|
if(error!=0) {
|
|
splx(s);
|
|
return error;
|
|
}
|
|
|
|
cs=splclock();
|
|
setwin(sc,0);
|
|
|
|
/* now it sends 250 millisecond break because I don't know */
|
|
/* how to send an infinite break */
|
|
|
|
fepcmd(port, SENDBREAK, 250, 0, 10, 0);
|
|
hidewin(sc);
|
|
splx(cs);
|
|
break;
|
|
case TIOCCBRK:
|
|
/* now it's empty */
|
|
break;
|
|
case TIOCSDTR:
|
|
DPRINT3(DB_MODEM,"dgb%d: port%d: set DTR\n",unit,pnum);
|
|
port->omodem |= DTR;
|
|
cs=splclock();
|
|
setwin(sc,0);
|
|
fepcmd(port, SETMODEM, port->omodem, RTS, 0, 1);
|
|
|
|
if( !(bc->mstat & DTR) ) {
|
|
DPRINT3(DB_MODEM,"dgb%d: port%d: DTR is off\n",unit,pnum);
|
|
}
|
|
|
|
hidewin(sc);
|
|
splx(cs);
|
|
break;
|
|
case TIOCCDTR:
|
|
DPRINT3(DB_MODEM,"dgb%d: port%d: reset DTR\n",unit,pnum);
|
|
port->omodem &= ~DTR;
|
|
cs=splclock();
|
|
setwin(sc,0);
|
|
fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
|
|
|
|
if( bc->mstat & DTR ) {
|
|
DPRINT3(DB_MODEM,"dgb%d: port%d: DTR is on\n",unit,pnum);
|
|
}
|
|
|
|
hidewin(sc);
|
|
splx(cs);
|
|
break;
|
|
case TIOCMSET:
|
|
if(*(int *)data & TIOCM_DTR)
|
|
port->omodem |=DTR;
|
|
else
|
|
port->omodem &=~DTR;
|
|
|
|
if(*(int *)data & TIOCM_RTS)
|
|
port->omodem |=RTS;
|
|
else
|
|
port->omodem &=~RTS;
|
|
|
|
cs=splclock();
|
|
setwin(sc,0);
|
|
fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
|
|
hidewin(sc);
|
|
splx(cs);
|
|
break;
|
|
case TIOCMBIS:
|
|
if(*(int *)data & TIOCM_DTR)
|
|
port->omodem |=DTR;
|
|
|
|
if(*(int *)data & TIOCM_RTS)
|
|
port->omodem |=RTS;
|
|
|
|
cs=splclock();
|
|
setwin(sc,0);
|
|
fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
|
|
hidewin(sc);
|
|
splx(cs);
|
|
break;
|
|
case TIOCMBIC:
|
|
if(*(int *)data & TIOCM_DTR)
|
|
port->omodem &=~DTR;
|
|
|
|
if(*(int *)data & TIOCM_RTS)
|
|
port->omodem &=~RTS;
|
|
|
|
cs=splclock();
|
|
setwin(sc,0);
|
|
fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
|
|
hidewin(sc);
|
|
splx(cs);
|
|
break;
|
|
case TIOCMGET:
|
|
setwin(sc,0);
|
|
port->imodem=bc->mstat;
|
|
hidewin(sc);
|
|
|
|
tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */
|
|
|
|
DPRINT3(DB_MODEM,"dgb%d: port%d: modem stat -- ",unit,pnum);
|
|
|
|
if (port->imodem & DTR) {
|
|
DPRINT1(DB_MODEM,"DTR ");
|
|
tiocm_xxx |= TIOCM_DTR;
|
|
}
|
|
if (port->imodem & RTS) {
|
|
DPRINT1(DB_MODEM,"RTS ");
|
|
tiocm_xxx |= TIOCM_RTS;
|
|
}
|
|
if (port->imodem & CTS) {
|
|
DPRINT1(DB_MODEM,"CTS ");
|
|
tiocm_xxx |= TIOCM_CTS;
|
|
}
|
|
if (port->imodem & port->dcd) {
|
|
DPRINT1(DB_MODEM,"DCD ");
|
|
tiocm_xxx |= TIOCM_CD;
|
|
}
|
|
if (port->imodem & port->dsr) {
|
|
DPRINT1(DB_MODEM,"DSR ");
|
|
tiocm_xxx |= TIOCM_DSR;
|
|
}
|
|
if (port->imodem & RI) {
|
|
DPRINT1(DB_MODEM,"RI ");
|
|
tiocm_xxx |= TIOCM_RI;
|
|
}
|
|
*(int *)data = tiocm_xxx;
|
|
DPRINT1(DB_MODEM,"--\n");
|
|
break;
|
|
case TIOCMSDTRWAIT:
|
|
/* must be root since the wait applies to following logins */
|
|
error = suser(td);
|
|
if (error != 0) {
|
|
splx(s);
|
|
return (error);
|
|
}
|
|
port->close_delay = *(int *)data * hz / 100;
|
|
break;
|
|
case TIOCMGDTRWAIT:
|
|
*(int *)data = port->close_delay * 100 / hz;
|
|
break;
|
|
case TIOCTIMESTAMP:
|
|
port->do_timestamp = TRUE;
|
|
*(struct timeval *)data = port->timestamp;
|
|
break;
|
|
case TIOCDCDTIMESTAMP:
|
|
port->do_dcd_timestamp = TRUE;
|
|
*(struct timeval *)data = port->dcd_timestamp;
|
|
break;
|
|
default:
|
|
bmws_set(ws);
|
|
splx(s);
|
|
return ENOTTY;
|
|
}
|
|
bmws_set(ws);
|
|
splx(s);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
wakeflush(p)
|
|
void *p;
|
|
{
|
|
struct dgb_p *port=p;
|
|
|
|
wakeup(&port->draining);
|
|
}
|
|
|
|
/* wait for the output to drain */
|
|
|
|
static int
|
|
dgbdrain(port)
|
|
struct dgb_p *port;
|
|
{
|
|
struct dgb_softc *sc=&dgb_softc[port->unit];
|
|
volatile struct board_chan *bc=port->brdchan;
|
|
int error;
|
|
int head, tail;
|
|
|
|
BoardMemWinState ws=bmws_get();
|
|
|
|
setwin(sc,0);
|
|
|
|
bc->iempty=1;
|
|
tail=bc->tout;
|
|
head=bc->tin;
|
|
|
|
while(tail!=head) {
|
|
DPRINT5(DB_WR,"dgb%d: port%d: drain: head=%d tail=%d\n",
|
|
port->unit, port->pnum, head, tail);
|
|
|
|
hidewin(sc);
|
|
port->draining=1;
|
|
timeout(wakeflush,port, hz);
|
|
error=tsleep(&port->draining, TTIPRI | PCATCH, "dgdrn", 0);
|
|
port->draining=0;
|
|
setwin(sc,0);
|
|
|
|
if (error != 0) {
|
|
DPRINT4(DB_WR,"dgb%d: port%d: tsleep(dgdrn) error=%d\n",
|
|
port->unit,port->pnum,error);
|
|
|
|
bc->iempty=0;
|
|
bmws_set(ws);
|
|
return error;
|
|
}
|
|
|
|
tail=bc->tout;
|
|
head=bc->tin;
|
|
}
|
|
DPRINT5(DB_WR,"dgb%d: port%d: drain: head=%d tail=%d\n",
|
|
port->unit, port->pnum, head, tail);
|
|
bmws_set(ws);
|
|
return 0;
|
|
}
|
|
|
|
/* wait for the output to drain */
|
|
/* or simply clear the buffer it it's stopped */
|
|
|
|
static void
|
|
dgb_drain_or_flush(port)
|
|
struct dgb_p *port;
|
|
{
|
|
struct tty *tp=port->tty;
|
|
struct dgb_softc *sc=&dgb_softc[port->unit];
|
|
volatile struct board_chan *bc=port->brdchan;
|
|
int error;
|
|
int lasttail;
|
|
int head, tail;
|
|
|
|
setwin(sc,0);
|
|
|
|
lasttail=-1;
|
|
bc->iempty=1;
|
|
tail=bc->tout;
|
|
head=bc->tin;
|
|
|
|
while(tail!=head /* && tail!=lasttail */ ) {
|
|
DPRINT5(DB_WR,"dgb%d: port%d: flush: head=%d tail=%d\n",
|
|
port->unit, port->pnum, head, tail);
|
|
|
|
/* if there is no carrier simply clean the buffer */
|
|
if( !(tp->t_state & TS_CARR_ON) ) {
|
|
bc->tout=bc->tin=0;
|
|
bc->iempty=0;
|
|
hidewin(sc);
|
|
return;
|
|
}
|
|
|
|
hidewin(sc);
|
|
port->draining=1;
|
|
timeout(wakeflush,port, hz);
|
|
error=tsleep(&port->draining, TTIPRI | PCATCH, "dgfls", 0);
|
|
port->draining=0;
|
|
setwin(sc,0);
|
|
|
|
if (error != 0) {
|
|
DPRINT4(DB_WR,"dgb%d: port%d: tsleep(dgfls) error=%d\n",
|
|
port->unit,port->pnum,error);
|
|
|
|
/* silently clean the buffer */
|
|
|
|
bc->tout=bc->tin=0;
|
|
bc->iempty=0;
|
|
hidewin(sc);
|
|
return;
|
|
}
|
|
|
|
lasttail=tail;
|
|
tail=bc->tout;
|
|
head=bc->tin;
|
|
}
|
|
hidewin(sc);
|
|
DPRINT5(DB_WR,"dgb%d: port%d: flush: head=%d tail=%d\n",
|
|
port->unit, port->pnum, head, tail);
|
|
}
|
|
|
|
static int
|
|
dgbparam(tp, t)
|
|
struct tty *tp;
|
|
struct termios *t;
|
|
{
|
|
int unit=MINOR_TO_UNIT(minor(tp->t_dev));
|
|
int pnum=MINOR_TO_PORT(minor(tp->t_dev));
|
|
struct dgb_softc *sc=&dgb_softc[unit];
|
|
struct dgb_p *port=&sc->ports[pnum];
|
|
volatile struct board_chan *bc=port->brdchan;
|
|
int cflag;
|
|
int head;
|
|
int mval;
|
|
int iflag;
|
|
int hflow;
|
|
int cs;
|
|
|
|
BoardMemWinState ws=bmws_get();
|
|
|
|
DPRINT6(DB_PARAM,"dgb%d: port%d: dgbparm c=0x%x i=0x%x l=0x%x\n",unit,pnum,t->c_cflag,t->c_iflag,t->c_lflag);
|
|
|
|
if(port->mustdrain) {
|
|
DPRINT3(DB_PARAM,"dgb%d: port%d: must call dgbdrain()\n",unit,pnum);
|
|
dgbdrain(port);
|
|
}
|
|
|
|
cflag=ttspeedtab(t->c_ospeed, dgbspeedtab);
|
|
|
|
if (t->c_ispeed == 0)
|
|
t->c_ispeed = t->c_ospeed;
|
|
|
|
if (cflag < 0 /* || cflag > 0 && t->c_ispeed != t->c_ospeed */) {
|
|
DPRINT4(DB_PARAM,"dgb%d: port%d: invalid cflag=0%o\n",unit,pnum,cflag);
|
|
return (EINVAL);
|
|
}
|
|
|
|
cs=splclock();
|
|
setwin(sc,0);
|
|
|
|
if(cflag==0) { /* hangup */
|
|
DPRINT3(DB_PARAM,"dgb%d: port%d: hangup\n",unit,pnum);
|
|
head=bc->rin;
|
|
bc->rout=head;
|
|
head=bc->tin;
|
|
fepcmd(port, STOUT, (unsigned)head, 0, 0, 0);
|
|
mval= port->omodem & ~(DTR|RTS);
|
|
} else {
|
|
cflag |= dgbflags(dgb_cflags, t->c_cflag);
|
|
|
|
if(cflag!=port->fepcflag) {
|
|
port->fepcflag=cflag;
|
|
DPRINT5(DB_PARAM,"dgb%d: port%d: set cflag=0x%x c=0x%x\n",
|
|
unit,pnum,cflag,t->c_cflag&~CRTSCTS);
|
|
fepcmd(port, SETCTRLFLAGS, (unsigned)cflag, 0, 0, 0);
|
|
}
|
|
mval= port->omodem | (DTR|RTS);
|
|
}
|
|
|
|
iflag=dgbflags(dgb_iflags, t->c_iflag);
|
|
if(iflag!=port->fepiflag) {
|
|
port->fepiflag=iflag;
|
|
DPRINT5(DB_PARAM,"dgb%d: port%d: set iflag=0x%x c=0x%x\n",unit,pnum,iflag,t->c_iflag);
|
|
fepcmd(port, SETIFLAGS, (unsigned)iflag, 0, 0, 0);
|
|
}
|
|
|
|
bc->mint=port->dcd;
|
|
|
|
hflow=dgbflags(dgb_flow, t->c_cflag);
|
|
if(hflow!=port->hflow) {
|
|
port->hflow=hflow;
|
|
DPRINT5(DB_PARAM,"dgb%d: port%d: set hflow=0x%x f=0x%x\n",unit,pnum,hflow,t->c_cflag&CRTSCTS);
|
|
fepcmd(port, SETHFLOW, (unsigned)hflow, 0xff, 0, 1);
|
|
}
|
|
|
|
if(port->omodem != mval) {
|
|
DPRINT5(DB_PARAM,"dgb%d: port%d: setting modem parameters 0x%x was 0x%x\n",
|
|
unit,pnum,mval,port->omodem);
|
|
port->omodem=mval;
|
|
fepcmd(port, SETMODEM, (unsigned)mval, RTS|DTR, 0, 1);
|
|
}
|
|
|
|
if(port->fepstartc!=t->c_cc[VSTART] || port->fepstopc!=t->c_cc[VSTOP]) {
|
|
DPRINT5(DB_PARAM,"dgb%d: port%d: set startc=%d, stopc=%d\n",unit,pnum,t->c_cc[VSTART],t->c_cc[VSTOP]);
|
|
port->fepstartc=t->c_cc[VSTART];
|
|
port->fepstopc=t->c_cc[VSTOP];
|
|
fepcmd(port, SONOFFC, port->fepstartc, port->fepstopc, 0, 1);
|
|
}
|
|
|
|
bmws_set(ws);
|
|
splx(cs);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static void
|
|
dgbstart(tp)
|
|
struct tty *tp;
|
|
{
|
|
int unit;
|
|
int pnum;
|
|
struct dgb_p *port;
|
|
struct dgb_softc *sc;
|
|
volatile struct board_chan *bc;
|
|
int head, tail;
|
|
int size, ocount;
|
|
int s;
|
|
int wmask;
|
|
|
|
BoardMemWinState ws=bmws_get();
|
|
|
|
unit=MINOR_TO_UNIT(minor(tp->t_dev));
|
|
pnum=MINOR_TO_PORT(minor(tp->t_dev));
|
|
sc=&dgb_softc[unit];
|
|
port=&sc->ports[pnum];
|
|
bc=port->brdchan;
|
|
|
|
wmask=port->txbufsize-1;
|
|
|
|
s=spltty();
|
|
|
|
while( tp->t_outq.c_cc!=0 ) {
|
|
int cs;
|
|
#ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */
|
|
ttwwakeup(tp);
|
|
#else
|
|
if(tp->t_outq.c_cc <= tp->t_lowat) {
|
|
if(tp->t_state & TS_ASLEEP) {
|
|
tp->t_state &= ~TS_ASLEEP;
|
|
wakeup(TSA_OLOWAT(tp));
|
|
}
|
|
/*selwakeup(&tp->t_wsel);*/
|
|
}
|
|
#endif
|
|
cs=splclock();
|
|
setwin(sc,0);
|
|
|
|
head=bc->tin & wmask;
|
|
|
|
do { tail=bc->tout; } while (tail != bc->tout);
|
|
tail=bc->tout & wmask;
|
|
|
|
DPRINT5(DB_WR,"dgb%d: port%d: s tx head=%d tail=%d\n",unit,pnum,head,tail);
|
|
|
|
#ifdef LEAVE_FREE_CHARS
|
|
if(tail>head) {
|
|
size=tail-head-LEAVE_FREE_CHARS;
|
|
if (size <0)
|
|
size=0;
|
|
else {
|
|
size=port->txbufsize-head;
|
|
if(tail+port->txbufsize < head)
|
|
size=0;
|
|
}
|
|
}
|
|
#else
|
|
if(tail>head)
|
|
size=tail-head-1;
|
|
else {
|
|
size=port->txbufsize-head/*-1*/;
|
|
if(tail==0)
|
|
size--;
|
|
}
|
|
#endif
|
|
|
|
if(size==0) {
|
|
bc->iempty=1; bc->ilow=1;
|
|
splx(cs);
|
|
bmws_set(ws);
|
|
tp->t_state|=TS_BUSY;
|
|
splx(s);
|
|
return;
|
|
}
|
|
|
|
towin(sc,port->txwin);
|
|
|
|
ocount=q_to_b(&tp->t_outq, port->txptr+head, size);
|
|
head+=ocount;
|
|
if(head>=port->txbufsize)
|
|
head-=port->txbufsize;
|
|
|
|
setwin(sc,0);
|
|
bc->tin=head;
|
|
|
|
DPRINT5(DB_WR,"dgb%d: port%d: tx avail=%d count=%d\n",unit,pnum,size,ocount);
|
|
hidewin(sc);
|
|
splx(cs);
|
|
}
|
|
|
|
bmws_set(ws);
|
|
splx(s);
|
|
|
|
#ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */
|
|
if(tp->t_state & TS_BUSY) {
|
|
tp->t_state&=~TS_BUSY;
|
|
linesw[tp->t_line].l_start(tp);
|
|
ttwwakeup(tp);
|
|
}
|
|
#else
|
|
if(tp->t_state & TS_ASLEEP) {
|
|
tp->t_state &= ~TS_ASLEEP;
|
|
wakeup(TSA_OLOWAT(tp));
|
|
}
|
|
tp->t_state&=~TS_BUSY;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
dgbstop(tp, rw)
|
|
struct tty *tp;
|
|
int rw;
|
|
{
|
|
int unit;
|
|
int pnum;
|
|
struct dgb_p *port;
|
|
struct dgb_softc *sc;
|
|
volatile struct board_chan *bc;
|
|
int s;
|
|
|
|
BoardMemWinState ws=bmws_get();
|
|
|
|
unit=MINOR_TO_UNIT(minor(tp->t_dev));
|
|
pnum=MINOR_TO_PORT(minor(tp->t_dev));
|
|
|
|
sc=&dgb_softc[unit];
|
|
port=&sc->ports[pnum];
|
|
bc=port->brdchan;
|
|
|
|
DPRINT3(DB_WR,"dgb%d: port%d: stop\n",port->unit, port->pnum);
|
|
|
|
s = spltty();
|
|
setwin(sc,0);
|
|
|
|
if (rw & FWRITE) {
|
|
/* clear output queue */
|
|
bc->tout=bc->tin=0;
|
|
bc->ilow=0;bc->iempty=0;
|
|
}
|
|
if (rw & FREAD) {
|
|
/* clear input queue */
|
|
bc->rout=bc->rin;
|
|
bc->idata=1;
|
|
}
|
|
hidewin(sc);
|
|
bmws_set(ws);
|
|
splx(s);
|
|
dgbstart(tp);
|
|
}
|
|
|
|
static void
|
|
fepcmd(port, cmd, op1, op2, ncmds, bytecmd)
|
|
struct dgb_p *port;
|
|
unsigned cmd, op1, op2, ncmds, bytecmd;
|
|
{
|
|
struct dgb_softc *sc=&dgb_softc[port->unit];
|
|
u_char *mem=sc->vmem;
|
|
unsigned tail, head;
|
|
int count, n;
|
|
|
|
if(port->status==DISABLED) {
|
|
printf("dgb%d: port%d: FEP command on disabled port\n",
|
|
port->unit, port->pnum);
|
|
return;
|
|
}
|
|
|
|
/* setwin(sc,0); Require this to be set by caller */
|
|
head=sc->mailbox->cin;
|
|
|
|
if(head>=(FEP_CMAX-FEP_CSTART) || (head & 3)) {
|
|
printf("dgb%d: port%d: wrong pointer head of command queue : 0x%x\n",
|
|
port->unit, port->pnum, head);
|
|
return;
|
|
}
|
|
|
|
mem[head+FEP_CSTART+0]=cmd;
|
|
mem[head+FEP_CSTART+1]=port->pnum;
|
|
if(bytecmd) {
|
|
mem[head+FEP_CSTART+2]=op1;
|
|
mem[head+FEP_CSTART+3]=op2;
|
|
} else {
|
|
mem[head+FEP_CSTART+2]=op1&0xff;
|
|
mem[head+FEP_CSTART+3]=(op1>>8)&0xff;
|
|
}
|
|
|
|
DPRINT7(DB_FEP,"dgb%d: port%d: %s cmd=0x%x op1=0x%x op2=0x%x\n", port->unit, port->pnum,
|
|
(bytecmd)?"byte":"word", cmd, mem[head+FEP_CSTART+2], mem[head+FEP_CSTART+3]);
|
|
|
|
head=(head+4) & (FEP_CMAX-FEP_CSTART-4);
|
|
sc->mailbox->cin=head;
|
|
|
|
count=FEPTIMEOUT;
|
|
|
|
while (count-- != 0) {
|
|
head=sc->mailbox->cin;
|
|
tail=sc->mailbox->cout;
|
|
|
|
n = (head-tail) & (FEP_CMAX-FEP_CSTART-4);
|
|
if(n <= ncmds * (sizeof(ushort)*4))
|
|
return;
|
|
}
|
|
printf("dgb%d(%d): timeout on FEP cmd=0x%x\n", port->unit, port->pnum, cmd);
|
|
}
|
|
|
|
static void
|
|
disc_optim(tp, t)
|
|
struct tty *tp;
|
|
struct termios *t;
|
|
{
|
|
if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
|
|
&& (!(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;
|
|
}
|