66fc150697
Reviewed by: mike (mentor)
3853 lines
97 KiB
C
3853 lines
97 KiB
C
/*****************************************************************************/
|
|
|
|
/*
|
|
* istallion.c -- stallion intelligent multiport serial driver.
|
|
*
|
|
* Copyright (c) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
|
|
* 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.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Greg Ungerer.
|
|
* 4. Neither the name of the author nor the names of any co-contributors
|
|
* may 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.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
|
|
#include "opt_compat.h"
|
|
#include "opt_tty.h"
|
|
|
|
#define TTYDEFCHARS 1
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/bus.h>
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
#include <i386/isa/isa_device.h>
|
|
#include <machine/cdk.h>
|
|
#include <machine/comstats.h>
|
|
|
|
#ifndef COMPAT_OLDISA
|
|
#error "The stli device requires the old isa compatibility shims"
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Define the version level of the kernel - so we can compile in the
|
|
* appropriate bits of code. By default this will compile for a 2.1
|
|
* level kernel.
|
|
*/
|
|
#define VFREEBSD 220
|
|
|
|
#if VFREEBSD >= 220
|
|
#define STATIC static
|
|
#else
|
|
#define STATIC
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Define different board types. Not all of the following board types
|
|
* are supported by this driver. But I will use the standard "assigned"
|
|
* board numbers. Currently supported boards are abbreviated as:
|
|
* ECP = EasyConnection 8/64, ONB = ONboard, BBY = Brumby and
|
|
* STAL = Stallion.
|
|
*/
|
|
#define BRD_UNKNOWN 0
|
|
#define BRD_STALLION 1
|
|
#define BRD_BRUMBY4 2
|
|
#define BRD_ONBOARD2 3
|
|
#define BRD_ONBOARD 4
|
|
#define BRD_BRUMBY8 5
|
|
#define BRD_BRUMBY16 6
|
|
#define BRD_ONBOARDE 7
|
|
#define BRD_ONBOARD32 9
|
|
#define BRD_ONBOARD2_32 10
|
|
#define BRD_ONBOARDRS 11
|
|
#define BRD_EASYIO 20
|
|
#define BRD_ECH 21
|
|
#define BRD_ECHMC 22
|
|
#define BRD_ECP 23
|
|
#define BRD_ECPE 24
|
|
#define BRD_ECPMC 25
|
|
#define BRD_ECHPCI 26
|
|
|
|
#define BRD_BRUMBY BRD_BRUMBY4
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Define important driver limitations.
|
|
*/
|
|
#define STL_MAXBRDS 8
|
|
#define STL_MAXPANELS 4
|
|
#define STL_PORTSPERPANEL 16
|
|
#define STL_PORTSPERBRD 64
|
|
|
|
#define STL_MAXCHANS STL_PORTSPERBRD
|
|
|
|
|
|
/*
|
|
* Define the important minor number break down bits. These have been
|
|
* chosen to be "compatible" with the standard sio driver minor numbers.
|
|
* Extra high bits are used to distinguish between boards and also for
|
|
* really high port numbers (> 32).
|
|
*/
|
|
#define STL_CALLOUTDEV 0x80
|
|
#define STL_CTRLLOCK 0x40
|
|
#define STL_CTRLINIT 0x20
|
|
#define STL_CTRLDEV (STL_CTRLLOCK | STL_CTRLINIT)
|
|
|
|
#define STL_MEMDEV 0x07000000
|
|
|
|
#define STL_DEFSPEED TTYDEF_SPEED
|
|
#define STL_DEFCFLAG (CS8 | CREAD | HUPCL)
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Define our local driver identity first. Set up stuff to deal with
|
|
* all the local structures required by a serial tty driver.
|
|
*/
|
|
static char stli_drvname[] = "stli";
|
|
static char const stli_longdrvname[] = "Stallion Multiport Serial Driver";
|
|
static char const stli_drvversion[] = "1.0.0";
|
|
|
|
static int stli_nrbrds = 0;
|
|
static int stli_doingtimeout = 0;
|
|
|
|
static char *__file__ = /*__FILE__*/ "istallion.c";
|
|
|
|
/*
|
|
* Define some macros to use to class define boards.
|
|
*/
|
|
#define BRD_ISA 0x1
|
|
#define BRD_EISA 0x2
|
|
#define BRD_MCA 0x4
|
|
#define BRD_PCI 0x8
|
|
|
|
static unsigned char stli_stliprobed[STL_MAXBRDS];
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Define a set of structures to hold all the board/panel/port info
|
|
* for our ports. These will be dynamically allocated as required at
|
|
* driver initialization time.
|
|
*/
|
|
|
|
/*
|
|
* Port and board structures to hold status info about each object.
|
|
* The board structure contains pointers to structures for each port
|
|
* connected to it. Panels are not distinguished here, since
|
|
* communication with the slave board will always be on a per port
|
|
* basis.
|
|
*/
|
|
typedef struct {
|
|
struct tty tty;
|
|
int portnr;
|
|
int panelnr;
|
|
int brdnr;
|
|
int ioaddr;
|
|
int callout;
|
|
int devnr;
|
|
int dtrwait;
|
|
int dotimestamp;
|
|
int waitopens;
|
|
int hotchar;
|
|
int rc;
|
|
int argsize;
|
|
void *argp;
|
|
unsigned int state;
|
|
unsigned int sigs;
|
|
struct termios initintios;
|
|
struct termios initouttios;
|
|
struct termios lockintios;
|
|
struct termios lockouttios;
|
|
struct timeval timestamp;
|
|
asysigs_t asig;
|
|
unsigned long addr;
|
|
unsigned long rxlost;
|
|
unsigned long rxoffset;
|
|
unsigned long txoffset;
|
|
unsigned long pflag;
|
|
unsigned int rxsize;
|
|
unsigned int txsize;
|
|
unsigned char reqidx;
|
|
unsigned char reqbit;
|
|
unsigned char portidx;
|
|
unsigned char portbit;
|
|
} stliport_t;
|
|
|
|
/*
|
|
* Use a structure of function pointers to do board level operations.
|
|
* These include, enable/disable, paging shared memory, interrupting, etc.
|
|
*/
|
|
typedef struct stlibrd {
|
|
int brdnr;
|
|
int brdtype;
|
|
int unitid;
|
|
int state;
|
|
int nrpanels;
|
|
int nrports;
|
|
int nrdevs;
|
|
unsigned int iobase;
|
|
unsigned long paddr;
|
|
void *vaddr;
|
|
int memsize;
|
|
int pagesize;
|
|
int hostoffset;
|
|
int slaveoffset;
|
|
int bitsize;
|
|
int confbits;
|
|
void (*init)(struct stlibrd *brdp);
|
|
void (*enable)(struct stlibrd *brdp);
|
|
void (*reenable)(struct stlibrd *brdp);
|
|
void (*disable)(struct stlibrd *brdp);
|
|
void (*intr)(struct stlibrd *brdp);
|
|
void (*reset)(struct stlibrd *brdp);
|
|
char *(*getmemptr)(struct stlibrd *brdp,
|
|
unsigned long offset, int line);
|
|
int panels[STL_MAXPANELS];
|
|
int panelids[STL_MAXPANELS];
|
|
stliport_t *ports[STL_PORTSPERBRD];
|
|
} stlibrd_t;
|
|
|
|
static stlibrd_t *stli_brds[STL_MAXBRDS];
|
|
|
|
static int stli_shared = 0;
|
|
|
|
/*
|
|
* Keep a local char buffer for processing chars into the LD. We
|
|
* do this to avoid copying from the boards shared memory one char
|
|
* at a time.
|
|
*/
|
|
static int stli_rxtmplen;
|
|
static stliport_t *stli_rxtmpport;
|
|
static char stli_rxtmpbuf[TTYHOG];
|
|
|
|
/*
|
|
* Define global stats structures. Not used often, and can be re-used
|
|
* for each stats call.
|
|
*/
|
|
static comstats_t stli_comstats;
|
|
static combrd_t stli_brdstats;
|
|
static asystats_t stli_cdkstats;
|
|
|
|
/*
|
|
* Per board state flags. Used with the state field of the board struct.
|
|
* Not really much here... All we need to do is keep track of whether
|
|
* the board has been detected, and whether it is actully running a slave
|
|
* or not.
|
|
*/
|
|
#define BST_FOUND 0x1
|
|
#define BST_STARTED 0x2
|
|
|
|
/*
|
|
* Define the set of port state flags. These are marked for internal
|
|
* state purposes only, usually to do with the state of communications
|
|
* with the slave. They need to be updated atomically.
|
|
*/
|
|
#define ST_INITIALIZING 0x1
|
|
#define ST_INITIALIZED 0x2
|
|
#define ST_OPENING 0x4
|
|
#define ST_CLOSING 0x8
|
|
#define ST_CMDING 0x10
|
|
#define ST_RXING 0x20
|
|
#define ST_TXBUSY 0x40
|
|
#define ST_DOFLUSHRX 0x80
|
|
#define ST_DOFLUSHTX 0x100
|
|
#define ST_DOSIGS 0x200
|
|
#define ST_GETSIGS 0x400
|
|
#define ST_DTRWAIT 0x800
|
|
|
|
/*
|
|
* Define an array of board names as printable strings. Handy for
|
|
* referencing boards when printing trace and stuff.
|
|
*/
|
|
static char *stli_brdnames[] = {
|
|
"Unknown",
|
|
"Stallion",
|
|
"Brumby",
|
|
"ONboard-MC",
|
|
"ONboard",
|
|
"Brumby",
|
|
"Brumby",
|
|
"ONboard-EI",
|
|
(char *) NULL,
|
|
"ONboard",
|
|
"ONboard-MC",
|
|
"ONboard-MC",
|
|
(char *) NULL,
|
|
(char *) NULL,
|
|
(char *) NULL,
|
|
(char *) NULL,
|
|
(char *) NULL,
|
|
(char *) NULL,
|
|
(char *) NULL,
|
|
(char *) NULL,
|
|
"EasyIO",
|
|
"EC8/32-AT",
|
|
"EC8/32-MC",
|
|
"EC8/64-AT",
|
|
"EC8/64-EI",
|
|
"EC8/64-MC",
|
|
"EC8/32-PCI",
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Hardware configuration info for ECP boards. These defines apply
|
|
* to the directly accessible io ports of the ECP. There is a set of
|
|
* defines for each ECP board type, ISA, EISA and MCA.
|
|
*/
|
|
#define ECP_IOSIZE 4
|
|
#define ECP_MEMSIZE (128 * 1024)
|
|
#define ECP_ATPAGESIZE (4 * 1024)
|
|
#define ECP_EIPAGESIZE (64 * 1024)
|
|
#define ECP_MCPAGESIZE (4 * 1024)
|
|
|
|
#define STL_EISAID 0x8c4e
|
|
|
|
/*
|
|
* Important defines for the ISA class of ECP board.
|
|
*/
|
|
#define ECP_ATIREG 0
|
|
#define ECP_ATCONFR 1
|
|
#define ECP_ATMEMAR 2
|
|
#define ECP_ATMEMPR 3
|
|
#define ECP_ATSTOP 0x1
|
|
#define ECP_ATINTENAB 0x10
|
|
#define ECP_ATENABLE 0x20
|
|
#define ECP_ATDISABLE 0x00
|
|
#define ECP_ATADDRMASK 0x3f000
|
|
#define ECP_ATADDRSHFT 12
|
|
|
|
/*
|
|
* Important defines for the EISA class of ECP board.
|
|
*/
|
|
#define ECP_EIIREG 0
|
|
#define ECP_EIMEMARL 1
|
|
#define ECP_EICONFR 2
|
|
#define ECP_EIMEMARH 3
|
|
#define ECP_EIENABLE 0x1
|
|
#define ECP_EIDISABLE 0x0
|
|
#define ECP_EISTOP 0x4
|
|
#define ECP_EIEDGE 0x00
|
|
#define ECP_EILEVEL 0x80
|
|
#define ECP_EIADDRMASKL 0x00ff0000
|
|
#define ECP_EIADDRSHFTL 16
|
|
#define ECP_EIADDRMASKH 0xff000000
|
|
#define ECP_EIADDRSHFTH 24
|
|
#define ECP_EIBRDENAB 0xc84
|
|
|
|
#define ECP_EISAID 0x4
|
|
|
|
/*
|
|
* Important defines for the Micro-channel class of ECP board.
|
|
* (It has a lot in common with the ISA boards.)
|
|
*/
|
|
#define ECP_MCIREG 0
|
|
#define ECP_MCCONFR 1
|
|
#define ECP_MCSTOP 0x20
|
|
#define ECP_MCENABLE 0x80
|
|
#define ECP_MCDISABLE 0x00
|
|
|
|
/*
|
|
* Hardware configuration info for ONboard and Brumby boards. These
|
|
* defines apply to the directly accessible io ports of these boards.
|
|
*/
|
|
#define ONB_IOSIZE 16
|
|
#define ONB_MEMSIZE (64 * 1024)
|
|
#define ONB_ATPAGESIZE (64 * 1024)
|
|
#define ONB_MCPAGESIZE (64 * 1024)
|
|
#define ONB_EIMEMSIZE (128 * 1024)
|
|
#define ONB_EIPAGESIZE (64 * 1024)
|
|
|
|
/*
|
|
* Important defines for the ISA class of ONboard board.
|
|
*/
|
|
#define ONB_ATIREG 0
|
|
#define ONB_ATMEMAR 1
|
|
#define ONB_ATCONFR 2
|
|
#define ONB_ATSTOP 0x4
|
|
#define ONB_ATENABLE 0x01
|
|
#define ONB_ATDISABLE 0x00
|
|
#define ONB_ATADDRMASK 0xff0000
|
|
#define ONB_ATADDRSHFT 16
|
|
|
|
#define ONB_HIMEMENAB 0x02
|
|
|
|
/*
|
|
* Important defines for the EISA class of ONboard board.
|
|
*/
|
|
#define ONB_EIIREG 0
|
|
#define ONB_EIMEMARL 1
|
|
#define ONB_EICONFR 2
|
|
#define ONB_EIMEMARH 3
|
|
#define ONB_EIENABLE 0x1
|
|
#define ONB_EIDISABLE 0x0
|
|
#define ONB_EISTOP 0x4
|
|
#define ONB_EIEDGE 0x00
|
|
#define ONB_EILEVEL 0x80
|
|
#define ONB_EIADDRMASKL 0x00ff0000
|
|
#define ONB_EIADDRSHFTL 16
|
|
#define ONB_EIADDRMASKH 0xff000000
|
|
#define ONB_EIADDRSHFTH 24
|
|
#define ONB_EIBRDENAB 0xc84
|
|
|
|
#define ONB_EISAID 0x1
|
|
|
|
/*
|
|
* Important defines for the Brumby boards. They are pretty simple,
|
|
* there is not much that is programmably configurable.
|
|
*/
|
|
#define BBY_IOSIZE 16
|
|
#define BBY_MEMSIZE (64 * 1024)
|
|
#define BBY_PAGESIZE (16 * 1024)
|
|
|
|
#define BBY_ATIREG 0
|
|
#define BBY_ATCONFR 1
|
|
#define BBY_ATSTOP 0x4
|
|
|
|
/*
|
|
* Important defines for the Stallion boards. They are pretty simple,
|
|
* there is not much that is programmably configurable.
|
|
*/
|
|
#define STAL_IOSIZE 16
|
|
#define STAL_MEMSIZE (64 * 1024)
|
|
#define STAL_PAGESIZE (64 * 1024)
|
|
|
|
/*
|
|
* Define the set of status register values for EasyConnection panels.
|
|
* The signature will return with the status value for each panel. From
|
|
* this we can determine what is attached to the board - before we have
|
|
* actually down loaded any code to it.
|
|
*/
|
|
#define ECH_PNLSTATUS 2
|
|
#define ECH_PNL16PORT 0x20
|
|
#define ECH_PNLIDMASK 0x07
|
|
#define ECH_PNLINTRPEND 0x80
|
|
|
|
/*
|
|
* Define some macros to do things to the board. Even those these boards
|
|
* are somewhat related there is often significantly different ways of
|
|
* doing some operation on it (like enable, paging, reset, etc). So each
|
|
* board class has a set of functions which do the commonly required
|
|
* operations. The macros below basically just call these functions,
|
|
* generally checking for a NULL function - which means that the board
|
|
* needs nothing done to it to achieve this operation!
|
|
*/
|
|
#define EBRDINIT(brdp) \
|
|
if (brdp->init != NULL) \
|
|
(* brdp->init)(brdp)
|
|
|
|
#define EBRDENABLE(brdp) \
|
|
if (brdp->enable != NULL) \
|
|
(* brdp->enable)(brdp);
|
|
|
|
#define EBRDDISABLE(brdp) \
|
|
if (brdp->disable != NULL) \
|
|
(* brdp->disable)(brdp);
|
|
|
|
#define EBRDINTR(brdp) \
|
|
if (brdp->intr != NULL) \
|
|
(* brdp->intr)(brdp);
|
|
|
|
#define EBRDRESET(brdp) \
|
|
if (brdp->reset != NULL) \
|
|
(* brdp->reset)(brdp);
|
|
|
|
#define EBRDGETMEMPTR(brdp,offset) \
|
|
(* brdp->getmemptr)(brdp, offset, __LINE__)
|
|
|
|
/*
|
|
* Define the maximal baud rate.
|
|
*/
|
|
#define STL_MAXBAUD 230400
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Define macros to extract a brd and port number from a minor number.
|
|
* This uses the extended minor number range in the upper 2 bytes of
|
|
* the device number. This gives us plenty of minor numbers to play
|
|
* with...
|
|
*/
|
|
#define MKDEV2BRD(m) ((minor(m) & 0x00700000) >> 20)
|
|
#define MKDEV2PORT(m) ((minor(m) & 0x1f) | ((minor(m) & 0x00010000) >> 11))
|
|
|
|
/*
|
|
* Define some handy local macros...
|
|
*/
|
|
#ifndef MIN
|
|
#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Declare all those functions in this driver! First up is the set of
|
|
* externally visible functions.
|
|
*/
|
|
static int stliprobe(struct isa_device *idp);
|
|
static int stliattach(struct isa_device *idp);
|
|
|
|
STATIC d_open_t stliopen;
|
|
STATIC d_close_t stliclose;
|
|
STATIC d_read_t stliread;
|
|
STATIC d_write_t stliwrite;
|
|
STATIC d_ioctl_t stliioctl;
|
|
|
|
/*
|
|
* Internal function prototypes.
|
|
*/
|
|
static stliport_t *stli_dev2port(dev_t dev);
|
|
static int stli_isaprobe(struct isa_device *idp);
|
|
static int stli_eisaprobe(struct isa_device *idp);
|
|
static int stli_mcaprobe(struct isa_device *idp);
|
|
static int stli_brdinit(stlibrd_t *brdp);
|
|
static int stli_brdattach(stlibrd_t *brdp);
|
|
static int stli_initecp(stlibrd_t *brdp);
|
|
static int stli_initonb(stlibrd_t *brdp);
|
|
static int stli_initports(stlibrd_t *brdp);
|
|
static int stli_startbrd(stlibrd_t *brdp);
|
|
static void stli_poll(void *arg);
|
|
static __inline void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp);
|
|
static __inline int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp);
|
|
static __inline void stli_dodelaycmd(stliport_t *portp,
|
|
volatile cdkctrl_t *cp);
|
|
static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts);
|
|
static long stli_mktiocm(unsigned long sigvalue);
|
|
static void stli_rxprocess(stlibrd_t *brdp, stliport_t *portp);
|
|
static void stli_flush(stliport_t *portp, int flag);
|
|
static void stli_start(struct tty *tp);
|
|
static void stli_stop(struct tty *tp, int rw);
|
|
static int stli_param(struct tty *tp, struct termios *tiosp);
|
|
static void stli_ttyoptim(stliport_t *portp, struct termios *tiosp);
|
|
static void stli_dtrwakeup(void *arg);
|
|
static int stli_initopen(stliport_t *portp);
|
|
static int stli_shutdownclose(stliport_t *portp);
|
|
static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp,
|
|
unsigned long arg, int wait);
|
|
static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp,
|
|
unsigned long arg, int wait);
|
|
static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp,
|
|
unsigned long cmd, void *arg, int size, int copyback);
|
|
static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp,
|
|
unsigned long cmd, void *arg, int size, int copyback);
|
|
static void stli_mkasyport(stliport_t *portp, asyport_t *pp,
|
|
struct termios *tiosp);
|
|
static int stli_memrw(dev_t dev, struct uio *uiop, int flag);
|
|
static int stli_memioctl(dev_t dev, unsigned long cmd, caddr_t data,
|
|
int flag, struct thread *td);
|
|
static int stli_getbrdstats(caddr_t data);
|
|
static int stli_getportstats(stliport_t *portp, caddr_t data);
|
|
static int stli_clrportstats(stliport_t *portp, caddr_t data);
|
|
static stliport_t *stli_getport(int brdnr, int panelnr, int portnr);
|
|
|
|
static void stli_ecpinit(stlibrd_t *brdp);
|
|
static void stli_ecpenable(stlibrd_t *brdp);
|
|
static void stli_ecpdisable(stlibrd_t *brdp);
|
|
static void stli_ecpreset(stlibrd_t *brdp);
|
|
static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset,
|
|
int line);
|
|
static void stli_ecpintr(stlibrd_t *brdp);
|
|
static void stli_ecpeiinit(stlibrd_t *brdp);
|
|
static void stli_ecpeienable(stlibrd_t *brdp);
|
|
static void stli_ecpeidisable(stlibrd_t *brdp);
|
|
static void stli_ecpeireset(stlibrd_t *brdp);
|
|
static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset,
|
|
int line);
|
|
static void stli_ecpmcenable(stlibrd_t *brdp);
|
|
static void stli_ecpmcdisable(stlibrd_t *brdp);
|
|
static void stli_ecpmcreset(stlibrd_t *brdp);
|
|
static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset,
|
|
int line);
|
|
|
|
static void stli_onbinit(stlibrd_t *brdp);
|
|
static void stli_onbenable(stlibrd_t *brdp);
|
|
static void stli_onbdisable(stlibrd_t *brdp);
|
|
static void stli_onbreset(stlibrd_t *brdp);
|
|
static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset,
|
|
int line);
|
|
static void stli_onbeinit(stlibrd_t *brdp);
|
|
static void stli_onbeenable(stlibrd_t *brdp);
|
|
static void stli_onbedisable(stlibrd_t *brdp);
|
|
static void stli_onbereset(stlibrd_t *brdp);
|
|
static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset,
|
|
int line);
|
|
static void stli_bbyinit(stlibrd_t *brdp);
|
|
static void stli_bbyreset(stlibrd_t *brdp);
|
|
static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset,
|
|
int line);
|
|
static void stli_stalinit(stlibrd_t *brdp);
|
|
static void stli_stalreset(stlibrd_t *brdp);
|
|
static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset,
|
|
int line);
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Declare the driver isa structure.
|
|
*/
|
|
struct isa_driver stlidriver = {
|
|
INTR_TYPE_TTY,
|
|
stliprobe,
|
|
stliattach,
|
|
stli_drvname
|
|
};
|
|
COMPAT_ISA_DRIVER(stli, stlidriver);
|
|
|
|
/*****************************************************************************/
|
|
|
|
#if VFREEBSD >= 220
|
|
|
|
/*
|
|
* FreeBSD-2.2+ kernel linkage.
|
|
*/
|
|
|
|
#define CDEV_MAJOR 75
|
|
static struct cdevsw stli_cdevsw = {
|
|
.d_open = stliopen,
|
|
.d_close = stliclose,
|
|
.d_read = stliread,
|
|
.d_write = stliwrite,
|
|
.d_ioctl = stliioctl,
|
|
.d_poll = ttypoll,
|
|
.d_name = stli_drvname,
|
|
.d_maj = CDEV_MAJOR,
|
|
.d_flags = D_TTY,
|
|
.d_kqfilter = ttykqfilter,
|
|
};
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
static stlibrd_t *stli_brdalloc(void)
|
|
{
|
|
stlibrd_t *brdp;
|
|
|
|
brdp = (stlibrd_t *) malloc(sizeof(stlibrd_t), M_TTYS, M_NOWAIT|M_ZERO);
|
|
if (brdp == (stlibrd_t *) NULL) {
|
|
printf("STALLION: failed to allocate memory (size=%d)\n",
|
|
sizeof(stlibrd_t));
|
|
return((stlibrd_t *) NULL);
|
|
}
|
|
return(brdp);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Find an available internal board number (unit number). The problem
|
|
* is that the same unit numbers can be assigned to different class
|
|
* boards - but we only want to maintain one setup board structures.
|
|
*/
|
|
|
|
static int stli_findfreeunit(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; (i < STL_MAXBRDS); i++)
|
|
if (stli_brds[i] == (stlibrd_t *) NULL)
|
|
break;
|
|
return((i >= STL_MAXBRDS) ? -1 : i);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Try and determine the ISA board type. Hopefully the board
|
|
* configuration entry will help us out, using the flags field.
|
|
* If not, we may ne be able to determine the board type...
|
|
*/
|
|
|
|
static int stli_isaprobe(struct isa_device *idp)
|
|
{
|
|
int btype;
|
|
|
|
#if DEBUG
|
|
printf("stli_isaprobe(idp=%x): unit=%d iobase=%x flags=%x\n",
|
|
(int) idp, idp->id_unit, idp->id_iobase, idp->id_flags);
|
|
#endif
|
|
|
|
switch (idp->id_flags) {
|
|
case BRD_STALLION:
|
|
case BRD_BRUMBY4:
|
|
case BRD_BRUMBY8:
|
|
case BRD_BRUMBY16:
|
|
case BRD_ONBOARD:
|
|
case BRD_ONBOARD32:
|
|
case BRD_ECP:
|
|
btype = idp->id_flags;
|
|
break;
|
|
default:
|
|
btype = 0;
|
|
break;
|
|
}
|
|
return(btype);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Probe for an EISA board type. We should be able to read the EISA ID,
|
|
* that will tell us if a board is present or not...
|
|
*/
|
|
|
|
static int stli_eisaprobe(struct isa_device *idp)
|
|
{
|
|
int btype, eid;
|
|
|
|
#if DEBUG
|
|
printf("stli_eisaprobe(idp=%x): unit=%d iobase=%x flags=%x\n",
|
|
(int) idp, idp->id_unit, idp->id_iobase, idp->id_flags);
|
|
#endif
|
|
|
|
/*
|
|
* Firstly check if this is an EISA system. Do this by probing for
|
|
* the system board EISA ID. If this is not an EISA system then
|
|
* don't bother going any further!
|
|
*/
|
|
outb(0xc80, 0xff);
|
|
if (inb(0xc80) == 0xff)
|
|
return(0);
|
|
|
|
/*
|
|
* Try and read the EISA ID from the board at specified address.
|
|
* If one is present it will tell us the board type as well.
|
|
*/
|
|
outb((idp->id_iobase + 0xc80), 0xff);
|
|
eid = inb(idp->id_iobase + 0xc80);
|
|
eid |= inb(idp->id_iobase + 0xc81) << 8;
|
|
if (eid != STL_EISAID)
|
|
return(0);
|
|
|
|
btype = 0;
|
|
eid = inb(idp->id_iobase + 0xc82);
|
|
if (eid == ECP_EISAID)
|
|
btype = BRD_ECPE;
|
|
else if (eid == ONB_EISAID)
|
|
btype = BRD_ONBOARDE;
|
|
|
|
outb((idp->id_iobase + 0xc84), 0x1);
|
|
return(btype);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Probe for an MCA board type. Not really sure how to do this yet,
|
|
* so for now just use the supplied flag specifier as board type...
|
|
*/
|
|
|
|
static int stli_mcaprobe(struct isa_device *idp)
|
|
{
|
|
int btype;
|
|
|
|
#if DEBUG
|
|
printf("stli_mcaprobe(idp=%x): unit=%d iobase=%x flags=%x\n",
|
|
(int) idp, idp->id_unit, idp->id_iobase, idp->id_flags);
|
|
#endif
|
|
|
|
switch (idp->id_flags) {
|
|
case BRD_ONBOARD2:
|
|
case BRD_ONBOARD2_32:
|
|
case BRD_ONBOARDRS:
|
|
case BRD_ECHMC:
|
|
case BRD_ECPMC:
|
|
btype = idp->id_flags;
|
|
break;
|
|
default:
|
|
btype = 0;
|
|
break;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Probe for a board. This is involved, since we need to enable the
|
|
* shared memory region to see if the board is really there or not...
|
|
*/
|
|
|
|
static int stliprobe(struct isa_device *idp)
|
|
{
|
|
stlibrd_t *brdp;
|
|
int btype, bclass;
|
|
|
|
#if DEBUG
|
|
printf("stliprobe(idp=%x): unit=%d iobase=%x flags=%x\n", (int) idp,
|
|
idp->id_unit, idp->id_iobase, idp->id_flags);
|
|
#endif
|
|
|
|
if (idp->id_unit > STL_MAXBRDS)
|
|
return(0);
|
|
|
|
/*
|
|
* First up determine what bus type of board we might be dealing
|
|
* with. It is easy to separate out the ISA from the EISA and MCA
|
|
* boards, based on their IO addresses. We may not be able to tell
|
|
* the EISA and MCA apart on IO address alone...
|
|
*/
|
|
bclass = 0;
|
|
if ((idp->id_iobase > 0) && (idp->id_iobase < 0x400)) {
|
|
bclass |= BRD_ISA;
|
|
} else {
|
|
/* ONboard2 range */
|
|
if ((idp->id_iobase >= 0x700) && (idp->id_iobase < 0x900))
|
|
bclass |= BRD_MCA;
|
|
/* EC-MCA ranges */
|
|
if ((idp->id_iobase >= 0x7000) && (idp->id_iobase < 0x7400))
|
|
bclass |= BRD_MCA;
|
|
if ((idp->id_iobase >= 0x8000) && (idp->id_iobase < 0xc000))
|
|
bclass |= BRD_MCA;
|
|
/* EISA board range */
|
|
if ((idp->id_iobase & ~0xf000) == 0)
|
|
bclass |= BRD_EISA;
|
|
}
|
|
|
|
if ((bclass == 0) || (idp->id_iobase == 0))
|
|
return(0);
|
|
|
|
/*
|
|
* Based on the board bus type, try and figure out what it might be...
|
|
*/
|
|
btype = 0;
|
|
if (bclass & BRD_ISA)
|
|
btype = stli_isaprobe(idp);
|
|
if ((btype == 0) && (bclass & BRD_EISA))
|
|
btype = stli_eisaprobe(idp);
|
|
if ((btype == 0) && (bclass & BRD_MCA))
|
|
btype = stli_mcaprobe(idp);
|
|
if (btype == 0)
|
|
return(0);
|
|
|
|
/*
|
|
* Go ahead and try probing for the shared memory region now.
|
|
* This way we will really know if the board is here...
|
|
*/
|
|
if ((brdp = stli_brdalloc()) == (stlibrd_t *) NULL)
|
|
return(0);
|
|
|
|
brdp->brdnr = stli_findfreeunit();
|
|
brdp->brdtype = btype;
|
|
brdp->unitid = idp->id_unit;
|
|
brdp->iobase = idp->id_iobase;
|
|
brdp->vaddr = idp->id_maddr;
|
|
brdp->paddr = vtophys(idp->id_maddr);
|
|
|
|
#if DEBUG
|
|
printf("%s(%d): btype=%x unit=%d brd=%d io=%x mem=%lx(%p)\n",
|
|
__file__, __LINE__, btype, brdp->unitid, brdp->brdnr,
|
|
brdp->iobase, brdp->paddr, (void *) brdp->vaddr);
|
|
#endif
|
|
|
|
stli_stliprobed[idp->id_unit] = brdp->brdnr;
|
|
stli_brdinit(brdp);
|
|
if ((brdp->state & BST_FOUND) == 0) {
|
|
stli_brds[brdp->brdnr] = (stlibrd_t *) NULL;
|
|
return(0);
|
|
}
|
|
stli_nrbrds++;
|
|
return(1);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Allocate resources for and initialize a board.
|
|
*/
|
|
|
|
static int stliattach(struct isa_device *idp)
|
|
{
|
|
stlibrd_t *brdp;
|
|
int brdnr;
|
|
|
|
#if DEBUG
|
|
printf("stliattach(idp=%p): unit=%d iobase=%x\n", (void *) idp,
|
|
idp->id_unit, idp->id_iobase);
|
|
#endif
|
|
|
|
brdnr = stli_stliprobed[idp->id_unit];
|
|
brdp = stli_brds[brdnr];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
return(0);
|
|
if (brdp->state & BST_FOUND)
|
|
stli_brdattach(brdp);
|
|
if (0) {
|
|
make_dev(&stli_cdevsw, 0, 0, 0, 0, "istallion_is_broken");
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
STATIC int stliopen(dev_t dev, int flag, int mode, struct thread *td)
|
|
{
|
|
struct tty *tp;
|
|
stliport_t *portp;
|
|
int error, callout, x;
|
|
|
|
#if DEBUG
|
|
printf("stliopen(dev=%x,flag=%x,mode=%x,p=%x)\n", (int) dev, flag,
|
|
mode, (int) td);
|
|
#endif
|
|
|
|
/*
|
|
* Firstly check if the supplied device number is a valid device.
|
|
*/
|
|
if (minor(dev) & STL_MEMDEV)
|
|
return(0);
|
|
|
|
portp = stli_dev2port(dev);
|
|
if (portp == (stliport_t *) NULL)
|
|
return(ENXIO);
|
|
tp = &portp->tty;
|
|
dev->si_tty = tp;
|
|
callout = minor(dev) & STL_CALLOUTDEV;
|
|
error = 0;
|
|
|
|
x = spltty();
|
|
|
|
stliopen_restart:
|
|
/*
|
|
* Wait here for the DTR drop timeout period to expire.
|
|
*/
|
|
while (portp->state & ST_DTRWAIT) {
|
|
error = tsleep(&portp->dtrwait, (TTIPRI | PCATCH),
|
|
"stlidtr", 0);
|
|
if (error)
|
|
goto stliopen_end;
|
|
}
|
|
|
|
/*
|
|
* If the port is in its raw hardware initialization phase, then
|
|
* hold up here 'till it is done.
|
|
*/
|
|
while (portp->state & (ST_INITIALIZING | ST_CLOSING)) {
|
|
error = tsleep(&portp->state, (TTIPRI | PCATCH),
|
|
"stliraw", 0);
|
|
if (error)
|
|
goto stliopen_end;
|
|
}
|
|
|
|
/*
|
|
* We have a valid device, so now we check if it is already open.
|
|
* If not then initialize the port hardware and set up the tty
|
|
* struct as required.
|
|
*/
|
|
if ((tp->t_state & TS_ISOPEN) == 0) {
|
|
tp->t_oproc = stli_start;
|
|
tp->t_param = stli_param;
|
|
tp->t_stop = stli_stop;
|
|
tp->t_dev = dev;
|
|
tp->t_termios = callout ? portp->initouttios :
|
|
portp->initintios;
|
|
stli_initopen(portp);
|
|
wakeup(&portp->state);
|
|
if ((portp->sigs & TIOCM_CD) || callout)
|
|
(*linesw[tp->t_line].l_modem)(tp, 1);
|
|
} else {
|
|
if (callout) {
|
|
if (portp->callout == 0) {
|
|
error = EBUSY;
|
|
goto stliopen_end;
|
|
}
|
|
} else {
|
|
if (portp->callout != 0) {
|
|
if (flag & O_NONBLOCK) {
|
|
error = EBUSY;
|
|
goto stliopen_end;
|
|
}
|
|
error = tsleep(&portp->callout,
|
|
(TTIPRI | PCATCH), "stlicall", 0);
|
|
if (error)
|
|
goto stliopen_end;
|
|
goto stliopen_restart;
|
|
}
|
|
}
|
|
if ((tp->t_state & TS_XCLUDE) &&
|
|
suser(td)) {
|
|
error = EBUSY;
|
|
goto stliopen_end;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If this port is not the callout device and we do not have carrier
|
|
* then we need to sleep, waiting for it to be asserted.
|
|
*/
|
|
if (((tp->t_state & TS_CARR_ON) == 0) && !callout &&
|
|
((tp->t_cflag & CLOCAL) == 0) &&
|
|
((flag & O_NONBLOCK) == 0)) {
|
|
portp->waitopens++;
|
|
error = tsleep(TSA_CARR_ON(tp), (TTIPRI | PCATCH), "stlidcd",0);
|
|
portp->waitopens--;
|
|
if (error)
|
|
goto stliopen_end;
|
|
goto stliopen_restart;
|
|
}
|
|
|
|
/*
|
|
* Open the line discipline.
|
|
*/
|
|
error = (*linesw[tp->t_line].l_open)(dev, tp);
|
|
stli_ttyoptim(portp, &tp->t_termios);
|
|
if ((tp->t_state & TS_ISOPEN) && callout)
|
|
portp->callout = 1;
|
|
|
|
/*
|
|
* If for any reason we get to here and the port is not actually
|
|
* open then close of the physical hardware - no point leaving it
|
|
* active when the open failed...
|
|
*/
|
|
stliopen_end:
|
|
splx(x);
|
|
if (((tp->t_state & TS_ISOPEN) == 0) && (portp->waitopens == 0))
|
|
stli_shutdownclose(portp);
|
|
|
|
return(error);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
STATIC int stliclose(dev_t dev, int flag, int mode, struct thread *td)
|
|
{
|
|
struct tty *tp;
|
|
stliport_t *portp;
|
|
int x;
|
|
|
|
#if DEBUG
|
|
printf("stliclose(dev=%s,flag=%x,mode=%x,p=%p)\n",
|
|
devtoname(dev), flag, mode, (void *) td);
|
|
#endif
|
|
|
|
if (minor(dev) & STL_MEMDEV)
|
|
return(0);
|
|
|
|
portp = stli_dev2port(dev);
|
|
if (portp == (stliport_t *) NULL)
|
|
return(ENXIO);
|
|
tp = &portp->tty;
|
|
|
|
x = spltty();
|
|
(*linesw[tp->t_line].l_close)(tp, flag);
|
|
stli_ttyoptim(portp, &tp->t_termios);
|
|
stli_shutdownclose(portp);
|
|
ttyclose(tp);
|
|
splx(x);
|
|
return(0);
|
|
}
|
|
|
|
|
|
STATIC int stliread(dev_t dev, struct uio *uiop, int flag)
|
|
{
|
|
|
|
#if DEBUG
|
|
printf("stliread(dev=%s,uiop=%p,flag=%x)\n", devtoname(dev),
|
|
(void *) uiop, flag);
|
|
#endif
|
|
|
|
if (minor(dev) & STL_MEMDEV)
|
|
return(stli_memrw(dev, uiop, flag));
|
|
else
|
|
return(ttyread(dev, uiop, flag));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
#if VFREEBSD >= 220
|
|
|
|
STATIC void stli_stop(struct tty *tp, int rw)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_stop(tp=%x,rw=%x)\n", (int) tp, rw);
|
|
#endif
|
|
|
|
stli_flush((stliport_t *) tp, rw);
|
|
}
|
|
|
|
#else
|
|
|
|
STATIC int stlistop(struct tty *tp, int rw)
|
|
{
|
|
#if DEBUG
|
|
printf("stlistop(tp=%x,rw=%x)\n", (int) tp, rw);
|
|
#endif
|
|
|
|
stli_flush((stliport_t *) tp, rw);
|
|
return(0);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
STATIC int stliwrite(dev_t dev, struct uio *uiop, int flag)
|
|
{
|
|
#if DEBUG
|
|
printf("stliwrite(dev=%s,uiop=%p,flag=%x)\n", devtoname(dev),
|
|
(void *) uiop, flag);
|
|
#endif
|
|
|
|
if (minor(dev) & STL_MEMDEV)
|
|
return(stli_memrw(dev, uiop, flag));
|
|
else
|
|
return(ttywrite(dev, uiop, flag));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
STATIC int stliioctl(dev_t dev, unsigned long cmd, caddr_t data, int flag,
|
|
struct thread *td)
|
|
{
|
|
struct termios *newtios, *localtios;
|
|
struct tty *tp;
|
|
stlibrd_t *brdp;
|
|
stliport_t *portp;
|
|
long arg;
|
|
int error, i, x;
|
|
|
|
#if DEBUG
|
|
printf("stliioctl(dev=%s,cmd=%lx,data=%p,flag=%x,p=%p)\n",
|
|
devtoname(dev), cmd, (void *) data, flag, (void *) td);
|
|
#endif
|
|
|
|
if (minor(dev) & STL_MEMDEV)
|
|
return(stli_memioctl(dev, cmd, data, flag, td));
|
|
|
|
portp = stli_dev2port(dev);
|
|
if (portp == (stliport_t *) NULL)
|
|
return(ENODEV);
|
|
if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL)
|
|
return(ENODEV);
|
|
tp = &portp->tty;
|
|
error = 0;
|
|
|
|
/*
|
|
* First up handle ioctls on the control devices.
|
|
*/
|
|
if (minor(dev) & STL_CTRLDEV) {
|
|
if ((minor(dev) & STL_CTRLDEV) == STL_CTRLINIT)
|
|
localtios = (minor(dev) & STL_CALLOUTDEV) ?
|
|
&portp->initouttios : &portp->initintios;
|
|
else if ((minor(dev) & STL_CTRLDEV) == STL_CTRLLOCK)
|
|
localtios = (minor(dev) & STL_CALLOUTDEV) ?
|
|
&portp->lockouttios : &portp->lockintios;
|
|
else
|
|
return(ENODEV);
|
|
|
|
switch (cmd) {
|
|
case TIOCSETA:
|
|
if ((error = suser(td)) == 0)
|
|
*localtios = *((struct termios *) data);
|
|
break;
|
|
case TIOCGETA:
|
|
*((struct termios *) data) = *localtios;
|
|
break;
|
|
case TIOCGETD:
|
|
*((int *) data) = TTYDISC;
|
|
break;
|
|
case TIOCGWINSZ:
|
|
bzero(data, sizeof(struct winsize));
|
|
break;
|
|
default:
|
|
error = ENOTTY;
|
|
break;
|
|
}
|
|
return(error);
|
|
}
|
|
|
|
/*
|
|
* Deal with 4.3 compatibility issues if we have too...
|
|
*/
|
|
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
|
|
if (1) {
|
|
struct termios tios;
|
|
unsigned long oldcmd;
|
|
|
|
tios = tp->t_termios;
|
|
oldcmd = cmd;
|
|
if ((error = ttsetcompat(tp, &cmd, data, &tios)))
|
|
return(error);
|
|
if (cmd != oldcmd)
|
|
data = (caddr_t) &tios;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Carry out some pre-cmd processing work first...
|
|
* Hmmm, not so sure we want this, disable for now...
|
|
*/
|
|
if ((cmd == TIOCSETA) || (cmd == TIOCSETAW) || (cmd == TIOCSETAF)) {
|
|
newtios = (struct termios *) data;
|
|
localtios = (minor(dev) & STL_CALLOUTDEV) ? &portp->lockouttios :
|
|
&portp->lockintios;
|
|
|
|
newtios->c_iflag = (tp->t_iflag & localtios->c_iflag) |
|
|
(newtios->c_iflag & ~localtios->c_iflag);
|
|
newtios->c_oflag = (tp->t_oflag & localtios->c_oflag) |
|
|
(newtios->c_oflag & ~localtios->c_oflag);
|
|
newtios->c_cflag = (tp->t_cflag & localtios->c_cflag) |
|
|
(newtios->c_cflag & ~localtios->c_cflag);
|
|
newtios->c_lflag = (tp->t_lflag & localtios->c_lflag) |
|
|
(newtios->c_lflag & ~localtios->c_lflag);
|
|
for (i = 0; (i < NCCS); i++) {
|
|
if (localtios->c_cc[i] != 0)
|
|
newtios->c_cc[i] = tp->t_cc[i];
|
|
}
|
|
if (localtios->c_ispeed != 0)
|
|
newtios->c_ispeed = tp->t_ispeed;
|
|
if (localtios->c_ospeed != 0)
|
|
newtios->c_ospeed = tp->t_ospeed;
|
|
}
|
|
|
|
/*
|
|
* Call the line discipline and the common command processing to
|
|
* process this command (if they can).
|
|
*/
|
|
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
|
|
if (error != ENOIOCTL)
|
|
return(error);
|
|
|
|
x = spltty();
|
|
error = ttioctl(tp, cmd, data, flag);
|
|
stli_ttyoptim(portp, &tp->t_termios);
|
|
if (error != ENOIOCTL) {
|
|
splx(x);
|
|
return(error);
|
|
}
|
|
|
|
error = 0;
|
|
|
|
/*
|
|
* Process local commands here. These are all commands that only we
|
|
* can take care of (they all rely on actually doing something special
|
|
* to the actual hardware).
|
|
*/
|
|
switch (cmd) {
|
|
case TIOCSBRK:
|
|
arg = BREAKON;
|
|
error = stli_cmdwait(brdp, portp, A_BREAK, &arg,
|
|
sizeof(unsigned long), 0);
|
|
break;
|
|
case TIOCCBRK:
|
|
arg = BREAKOFF;
|
|
error = stli_cmdwait(brdp, portp, A_BREAK, &arg,
|
|
sizeof(unsigned long), 0);
|
|
break;
|
|
case TIOCSDTR:
|
|
stli_mkasysigs(&portp->asig, 1, -1);
|
|
error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
|
|
sizeof(asysigs_t), 0);
|
|
break;
|
|
case TIOCCDTR:
|
|
stli_mkasysigs(&portp->asig, 0, -1);
|
|
error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
|
|
sizeof(asysigs_t), 0);
|
|
break;
|
|
case TIOCMSET:
|
|
i = *((int *) data);
|
|
stli_mkasysigs(&portp->asig, ((i & TIOCM_DTR) ? 1 : 0),
|
|
((i & TIOCM_RTS) ? 1 : 0));
|
|
error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
|
|
sizeof(asysigs_t), 0);
|
|
break;
|
|
case TIOCMBIS:
|
|
i = *((int *) data);
|
|
stli_mkasysigs(&portp->asig, ((i & TIOCM_DTR) ? 1 : -1),
|
|
((i & TIOCM_RTS) ? 1 : -1));
|
|
error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
|
|
sizeof(asysigs_t), 0);
|
|
break;
|
|
case TIOCMBIC:
|
|
i = *((int *) data);
|
|
stli_mkasysigs(&portp->asig, ((i & TIOCM_DTR) ? 0 : -1),
|
|
((i & TIOCM_RTS) ? 0 : -1));
|
|
error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
|
|
sizeof(asysigs_t), 0);
|
|
break;
|
|
case TIOCMGET:
|
|
if ((error = stli_cmdwait(brdp, portp, A_GETSIGNALS,
|
|
&portp->asig, sizeof(asysigs_t), 1)) < 0)
|
|
break;
|
|
portp->sigs = stli_mktiocm(portp->asig.sigvalue);
|
|
*((int *) data) = (portp->sigs | TIOCM_LE);
|
|
break;
|
|
case TIOCMSDTRWAIT:
|
|
if ((error = suser(td)) == 0)
|
|
portp->dtrwait = *((int *) data) * hz / 100;
|
|
break;
|
|
case TIOCMGDTRWAIT:
|
|
*((int *) data) = portp->dtrwait * 100 / hz;
|
|
break;
|
|
case TIOCTIMESTAMP:
|
|
portp->dotimestamp = 1;
|
|
*((struct timeval *) data) = portp->timestamp;
|
|
break;
|
|
default:
|
|
error = ENOTTY;
|
|
break;
|
|
}
|
|
splx(x);
|
|
|
|
return(error);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Convert the specified minor device number into a port struct
|
|
* pointer. Return NULL if the device number is not a valid port.
|
|
*/
|
|
|
|
STATIC stliport_t *stli_dev2port(dev_t dev)
|
|
{
|
|
stlibrd_t *brdp;
|
|
|
|
brdp = stli_brds[MKDEV2BRD(dev)];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
return((stliport_t *) NULL);
|
|
if ((brdp->state & BST_STARTED) == 0)
|
|
return((stliport_t *) NULL);
|
|
return(brdp->ports[MKDEV2PORT(dev)]);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Carry out first open operations on a port. This involves a number of
|
|
* commands to be sent to the slave. We need to open the port, set the
|
|
* notification events, set the initial port settings, get and set the
|
|
* initial signal values. We sleep and wait in between each one. But
|
|
* this still all happens pretty quickly.
|
|
*/
|
|
|
|
static int stli_initopen(stliport_t *portp)
|
|
{
|
|
stlibrd_t *brdp;
|
|
asynotify_t nt;
|
|
asyport_t aport;
|
|
int rc;
|
|
|
|
#if DEBUG
|
|
printf("stli_initopen(portp=%x)\n", (int) portp);
|
|
#endif
|
|
|
|
if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL)
|
|
return(ENXIO);
|
|
if (portp->state & ST_INITIALIZED)
|
|
return(0);
|
|
portp->state |= ST_INITIALIZED;
|
|
|
|
if ((rc = stli_rawopen(brdp, portp, 0, 1)) < 0)
|
|
return(rc);
|
|
|
|
bzero(&nt, sizeof(asynotify_t));
|
|
nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK);
|
|
nt.signal = SG_DCD;
|
|
if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt,
|
|
sizeof(asynotify_t), 0)) < 0)
|
|
return(rc);
|
|
|
|
stli_mkasyport(portp, &aport, &portp->tty.t_termios);
|
|
if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport,
|
|
sizeof(asyport_t), 0)) < 0)
|
|
return(rc);
|
|
|
|
portp->state |= ST_GETSIGS;
|
|
if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig,
|
|
sizeof(asysigs_t), 1)) < 0)
|
|
return(rc);
|
|
if (portp->state & ST_GETSIGS) {
|
|
portp->sigs = stli_mktiocm(portp->asig.sigvalue);
|
|
portp->state &= ~ST_GETSIGS;
|
|
}
|
|
|
|
stli_mkasysigs(&portp->asig, 1, 1);
|
|
if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
|
|
sizeof(asysigs_t), 0)) < 0)
|
|
return(rc);
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Shutdown the hardware of a port.
|
|
*/
|
|
|
|
static int stli_shutdownclose(stliport_t *portp)
|
|
{
|
|
stlibrd_t *brdp;
|
|
struct tty *tp;
|
|
int x;
|
|
|
|
#if DEBUG
|
|
printf("stli_shutdownclose(portp=%p): brdnr=%d panelnr=%d portnr=%d\n",
|
|
(void *) portp, portp->brdnr, portp->panelnr, portp->portnr);
|
|
#endif
|
|
|
|
if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL)
|
|
return(ENXIO);
|
|
|
|
tp = &portp->tty;
|
|
stli_rawclose(brdp, portp, 0, 0);
|
|
stli_flush(portp, (FWRITE | FREAD));
|
|
if (tp->t_cflag & HUPCL) {
|
|
x = spltty();
|
|
stli_mkasysigs(&portp->asig, 0, 0);
|
|
if (portp->state & ST_CMDING) {
|
|
portp->state |= ST_DOSIGS;
|
|
} else {
|
|
stli_sendcmd(brdp, portp, A_SETSIGNALS,
|
|
&portp->asig, sizeof(asysigs_t), 0);
|
|
}
|
|
splx(x);
|
|
if (portp->dtrwait != 0) {
|
|
portp->state |= ST_DTRWAIT;
|
|
timeout(stli_dtrwakeup, portp, portp->dtrwait);
|
|
}
|
|
}
|
|
portp->callout = 0;
|
|
portp->state &= ~ST_INITIALIZED;
|
|
wakeup(&portp->callout);
|
|
wakeup(TSA_CARR_ON(tp));
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Clear the DTR waiting flag, and wake up any sleepers waiting for
|
|
* DTR wait period to finish.
|
|
*/
|
|
|
|
static void stli_dtrwakeup(void *arg)
|
|
{
|
|
stliport_t *portp;
|
|
|
|
portp = (stliport_t *) arg;
|
|
portp->state &= ~ST_DTRWAIT;
|
|
wakeup(&portp->dtrwait);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Send an open message to the slave. This will sleep waiting for the
|
|
* acknowledgement, so must have user context. We need to co-ordinate
|
|
* with close events here, since we don't want open and close events
|
|
* to overlap.
|
|
*/
|
|
|
|
static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait)
|
|
{
|
|
volatile cdkhdr_t *hdrp;
|
|
volatile cdkctrl_t *cp;
|
|
volatile unsigned char *bits;
|
|
int rc, x;
|
|
|
|
#if DEBUG
|
|
printf("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp,
|
|
(int) portp, (int) arg, wait);
|
|
#endif
|
|
|
|
x = spltty();
|
|
|
|
/*
|
|
* Slave is already closing this port. This can happen if a hangup
|
|
* occurs on this port. So we must wait until it is complete. The
|
|
* order of opens and closes may not be preserved across shared
|
|
* memory, so we must wait until it is complete.
|
|
*/
|
|
while (portp->state & ST_CLOSING) {
|
|
rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0);
|
|
if (rc) {
|
|
splx(x);
|
|
return(rc);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Everything is ready now, so write the open message into shared
|
|
* memory. Once the message is in set the service bits to say that
|
|
* this port wants service.
|
|
*/
|
|
EBRDENABLE(brdp);
|
|
cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
|
|
cp->openarg = arg;
|
|
cp->open = 1;
|
|
hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
|
|
bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
|
|
portp->portidx;
|
|
*bits |= portp->portbit;
|
|
EBRDDISABLE(brdp);
|
|
|
|
if (wait == 0) {
|
|
splx(x);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Slave is in action, so now we must wait for the open acknowledgment
|
|
* to come back.
|
|
*/
|
|
rc = 0;
|
|
portp->state |= ST_OPENING;
|
|
while (portp->state & ST_OPENING) {
|
|
rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0);
|
|
if (rc) {
|
|
splx(x);
|
|
return(rc);
|
|
}
|
|
}
|
|
splx(x);
|
|
|
|
if ((rc == 0) && (portp->rc != 0))
|
|
rc = EIO;
|
|
return(rc);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Send a close message to the slave. Normally this will sleep waiting
|
|
* for the acknowledgement, but if wait parameter is 0 it will not. If
|
|
* wait is true then must have user context (to sleep).
|
|
*/
|
|
|
|
static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait)
|
|
{
|
|
volatile cdkhdr_t *hdrp;
|
|
volatile cdkctrl_t *cp;
|
|
volatile unsigned char *bits;
|
|
int rc, x;
|
|
|
|
#if DEBUG
|
|
printf("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp,
|
|
(int) portp, (int) arg, wait);
|
|
#endif
|
|
|
|
x = spltty();
|
|
|
|
/*
|
|
* Slave is already closing this port. This can happen if a hangup
|
|
* occurs on this port.
|
|
*/
|
|
if (wait) {
|
|
while (portp->state & ST_CLOSING) {
|
|
rc = tsleep(&portp->state, (TTIPRI | PCATCH),
|
|
"stliraw", 0);
|
|
if (rc) {
|
|
splx(x);
|
|
return(rc);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write the close command into shared memory.
|
|
*/
|
|
EBRDENABLE(brdp);
|
|
cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
|
|
cp->closearg = arg;
|
|
cp->close = 1;
|
|
hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
|
|
bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
|
|
portp->portidx;
|
|
*bits |= portp->portbit;
|
|
EBRDDISABLE(brdp);
|
|
|
|
portp->state |= ST_CLOSING;
|
|
if (wait == 0) {
|
|
splx(x);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Slave is in action, so now we must wait for the open acknowledgment
|
|
* to come back.
|
|
*/
|
|
rc = 0;
|
|
while (portp->state & ST_CLOSING) {
|
|
rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0);
|
|
if (rc) {
|
|
splx(x);
|
|
return(rc);
|
|
}
|
|
}
|
|
splx(x);
|
|
|
|
if ((rc == 0) && (portp->rc != 0))
|
|
rc = EIO;
|
|
return(rc);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Send a command to the slave and wait for the response. This must
|
|
* have user context (it sleeps). This routine is generic in that it
|
|
* can send any type of command. Its purpose is to wait for that command
|
|
* to complete (as opposed to initiating the command then returning).
|
|
*/
|
|
|
|
static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
|
|
{
|
|
int rc, x;
|
|
|
|
#if DEBUG
|
|
printf("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,"
|
|
"copyback=%d)\n", (int) brdp, (int) portp, (int) cmd,
|
|
(int) arg, size, copyback);
|
|
#endif
|
|
|
|
x = spltty();
|
|
while (portp->state & ST_CMDING) {
|
|
rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0);
|
|
if (rc) {
|
|
splx(x);
|
|
return(rc);
|
|
}
|
|
}
|
|
|
|
stli_sendcmd(brdp, portp, cmd, arg, size, copyback);
|
|
|
|
while (portp->state & ST_CMDING) {
|
|
rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0);
|
|
if (rc) {
|
|
splx(x);
|
|
return(rc);
|
|
}
|
|
}
|
|
splx(x);
|
|
|
|
if (portp->rc != 0)
|
|
return(EIO);
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Start (or continue) the transfer of TX data on this port. If the
|
|
* port is not currently busy then load up the interrupt ring queue
|
|
* buffer and kick of the transmitter. If the port is running low on
|
|
* TX data then refill the ring queue. This routine is also used to
|
|
* activate input flow control!
|
|
*/
|
|
|
|
static void stli_start(struct tty *tp)
|
|
{
|
|
volatile cdkasy_t *ap;
|
|
volatile cdkhdr_t *hdrp;
|
|
volatile unsigned char *bits;
|
|
unsigned char *shbuf;
|
|
stliport_t *portp;
|
|
stlibrd_t *brdp;
|
|
unsigned int len, stlen, head, tail, size;
|
|
int count, x;
|
|
|
|
portp = (stliport_t *) tp;
|
|
|
|
#if DEBUG
|
|
printf("stli_start(tp=%x): brdnr=%d portnr=%d\n", (int) tp,
|
|
portp->brdnr, portp->portnr);
|
|
#endif
|
|
|
|
x = spltty();
|
|
|
|
#if VFREEBSD == 205
|
|
/*
|
|
* Check if the output cooked clist buffers are near empty, wake up
|
|
* the line discipline to fill it up.
|
|
*/
|
|
if (tp->t_outq.c_cc <= tp->t_lowat) {
|
|
if (tp->t_state & TS_ASLEEP) {
|
|
tp->t_state &= ~TS_ASLEEP;
|
|
wakeup(&tp->t_outq);
|
|
}
|
|
selwakeup(&tp->t_wsel);
|
|
}
|
|
#endif
|
|
|
|
if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
|
|
splx(x);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Copy data from the clists into the interrupt ring queue. This will
|
|
* require at most 2 copys... What we do is calculate how many chars
|
|
* can fit into the ring queue, and how many can fit in 1 copy. If after
|
|
* the first copy there is still more room then do the second copy.
|
|
*/
|
|
if (tp->t_outq.c_cc != 0) {
|
|
brdp = stli_brds[portp->brdnr];
|
|
if (brdp == (stlibrd_t *) NULL) {
|
|
splx(x);
|
|
return;
|
|
}
|
|
|
|
EBRDENABLE(brdp);
|
|
ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
|
|
head = (unsigned int) ap->txq.head;
|
|
tail = (unsigned int) ap->txq.tail;
|
|
if (tail != ((unsigned int) ap->txq.tail))
|
|
tail = (unsigned int) ap->txq.tail;
|
|
size = portp->txsize;
|
|
if (head >= tail) {
|
|
len = size - (head - tail) - 1;
|
|
stlen = size - head;
|
|
} else {
|
|
len = tail - head - 1;
|
|
stlen = len;
|
|
}
|
|
|
|
count = 0;
|
|
shbuf = (char *) EBRDGETMEMPTR(brdp, portp->txoffset);
|
|
|
|
if (len > 0) {
|
|
stlen = MIN(len, stlen);
|
|
count = q_to_b(&tp->t_outq, (shbuf + head), stlen);
|
|
len -= count;
|
|
head += count;
|
|
if (head >= size) {
|
|
head = 0;
|
|
if (len > 0) {
|
|
stlen = q_to_b(&tp->t_outq, shbuf, len);
|
|
head += stlen;
|
|
count += stlen;
|
|
}
|
|
}
|
|
}
|
|
|
|
ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
|
|
ap->txq.head = head;
|
|
hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
|
|
bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
|
|
portp->portidx;
|
|
*bits |= portp->portbit;
|
|
portp->state |= ST_TXBUSY;
|
|
tp->t_state |= TS_BUSY;
|
|
|
|
EBRDDISABLE(brdp);
|
|
}
|
|
|
|
#if VFREEBSD != 205
|
|
/*
|
|
* Do any writer wakeups.
|
|
*/
|
|
ttwwakeup(tp);
|
|
#endif
|
|
|
|
splx(x);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Send a new port configuration to the slave.
|
|
*/
|
|
|
|
static int stli_param(struct tty *tp, struct termios *tiosp)
|
|
{
|
|
stlibrd_t *brdp;
|
|
stliport_t *portp;
|
|
asyport_t aport;
|
|
int x, rc;
|
|
|
|
portp = (stliport_t *) tp;
|
|
if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL)
|
|
return(ENXIO);
|
|
|
|
x = spltty();
|
|
stli_mkasyport(portp, &aport, tiosp);
|
|
/* can we sleep here? */
|
|
rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0);
|
|
stli_ttyoptim(portp, tiosp);
|
|
splx(x);
|
|
return(rc);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Flush characters from the lower buffer. We may not have user context
|
|
* so we cannot sleep waiting for it to complete. Also we need to check
|
|
* if there is chars for this port in the TX cook buffer, and flush them
|
|
* as well.
|
|
*/
|
|
|
|
static void stli_flush(stliport_t *portp, int flag)
|
|
{
|
|
stlibrd_t *brdp;
|
|
unsigned long ftype;
|
|
int x;
|
|
|
|
#if DEBUG
|
|
printf("stli_flush(portp=%x,flag=%x)\n", (int) portp, flag);
|
|
#endif
|
|
|
|
if (portp == (stliport_t *) NULL)
|
|
return;
|
|
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
|
|
return;
|
|
brdp = stli_brds[portp->brdnr];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
return;
|
|
|
|
x = spltty();
|
|
if (portp->state & ST_CMDING) {
|
|
portp->state |= (flag & FWRITE) ? ST_DOFLUSHTX : 0;
|
|
portp->state |= (flag & FREAD) ? ST_DOFLUSHRX : 0;
|
|
} else {
|
|
ftype = (flag & FWRITE) ? FLUSHTX : 0;
|
|
ftype |= (flag & FREAD) ? FLUSHRX : 0;
|
|
portp->state &= ~(ST_DOFLUSHTX | ST_DOFLUSHRX);
|
|
stli_sendcmd(brdp, portp, A_FLUSH, &ftype,
|
|
sizeof(unsigned long), 0);
|
|
}
|
|
if ((flag & FREAD) && (stli_rxtmpport == portp))
|
|
stli_rxtmplen = 0;
|
|
splx(x);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Generic send command routine. This will send a message to the slave,
|
|
* of the specified type with the specified argument. Must be very
|
|
* carefull of data that will be copied out from shared memory -
|
|
* containing command results. The command completion is all done from
|
|
* a poll routine that does not have user coontext. Therefore you cannot
|
|
* copy back directly into user space, or to the kernel stack of a
|
|
* process. This routine does not sleep, so can be called from anywhere,
|
|
* and must be called with interrupt locks set.
|
|
*/
|
|
|
|
static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
|
|
{
|
|
volatile cdkhdr_t *hdrp;
|
|
volatile cdkctrl_t *cp;
|
|
volatile unsigned char *bits;
|
|
|
|
#if DEBUG
|
|
printf("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,"
|
|
"copyback=%d)\n", (int) brdp, (int) portp, (int) cmd,
|
|
(int) arg, size, copyback);
|
|
#endif
|
|
|
|
if (portp->state & ST_CMDING) {
|
|
printf("STALLION: command already busy, cmd=%x!\n", (int) cmd);
|
|
return;
|
|
}
|
|
|
|
EBRDENABLE(brdp);
|
|
cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
|
|
if (size > 0) {
|
|
bcopy(arg, (void *) &(cp->args[0]), size);
|
|
if (copyback) {
|
|
portp->argp = arg;
|
|
portp->argsize = size;
|
|
}
|
|
}
|
|
cp->status = 0;
|
|
cp->cmd = cmd;
|
|
hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
|
|
bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
|
|
portp->portidx;
|
|
*bits |= portp->portbit;
|
|
portp->state |= ST_CMDING;
|
|
EBRDDISABLE(brdp);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Read data from shared memory. This assumes that the shared memory
|
|
* is enabled and that interrupts are off. Basically we just empty out
|
|
* the shared memory buffer into the tty buffer. Must be carefull to
|
|
* handle the case where we fill up the tty buffer, but still have
|
|
* more chars to unload.
|
|
*/
|
|
|
|
static void stli_rxprocess(stlibrd_t *brdp, stliport_t *portp)
|
|
{
|
|
volatile cdkasyrq_t *rp;
|
|
volatile char *shbuf;
|
|
struct tty *tp;
|
|
unsigned int head, tail, size;
|
|
unsigned int len, stlen, i;
|
|
int ch;
|
|
|
|
#if DEBUG
|
|
printf("stli_rxprocess(brdp=%x,portp=%d)\n", (int) brdp, (int) portp);
|
|
#endif
|
|
|
|
tp = &portp->tty;
|
|
if ((tp->t_state & TS_ISOPEN) == 0) {
|
|
stli_flush(portp, FREAD);
|
|
return;
|
|
}
|
|
if (tp->t_state & TS_TBLOCK)
|
|
return;
|
|
|
|
rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
|
|
head = (unsigned int) rp->head;
|
|
if (head != ((unsigned int) rp->head))
|
|
head = (unsigned int) rp->head;
|
|
tail = (unsigned int) rp->tail;
|
|
size = portp->rxsize;
|
|
if (head >= tail) {
|
|
len = head - tail;
|
|
stlen = len;
|
|
} else {
|
|
len = size - (tail - head);
|
|
stlen = size - tail;
|
|
}
|
|
|
|
if (len == 0)
|
|
return;
|
|
|
|
shbuf = (volatile char *) EBRDGETMEMPTR(brdp, portp->rxoffset);
|
|
|
|
/*
|
|
* If we can bypass normal LD processing then just copy direct
|
|
* from board shared memory into the tty buffers.
|
|
*/
|
|
if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
|
|
if (((tp->t_rawq.c_cc + len) >= TTYHOG) &&
|
|
((tp->t_cflag & CRTS_IFLOW) || (tp->t_iflag & IXOFF)) &&
|
|
((tp->t_state & TS_TBLOCK) == 0)) {
|
|
ch = TTYHOG - tp->t_rawq.c_cc - 1;
|
|
len = (ch > 0) ? ch : 0;
|
|
stlen = MIN(stlen, len);
|
|
tp->t_state |= TS_TBLOCK;
|
|
}
|
|
i = b_to_q((char *) (shbuf + tail), stlen, &tp->t_rawq);
|
|
tail += stlen;
|
|
len -= stlen;
|
|
if (tail >= size) {
|
|
tail = 0;
|
|
i += b_to_q((char *) shbuf, len, &tp->t_rawq);
|
|
tail += len;
|
|
}
|
|
portp->rxlost += i;
|
|
ttwakeup(tp);
|
|
rp = &((volatile cdkasy_t *)
|
|
EBRDGETMEMPTR(brdp, portp->addr))->rxq;
|
|
rp->tail = tail;
|
|
|
|
} else {
|
|
/*
|
|
* Copy the data from board shared memory into a local
|
|
* memory buffer. Then feed them from here into the LD.
|
|
* We don't want to go into board shared memory one char
|
|
* at a time, it is too slow...
|
|
*/
|
|
if (len > TTYHOG) {
|
|
len = TTYHOG - 1;
|
|
stlen = min(len, stlen);
|
|
}
|
|
stli_rxtmpport = portp;
|
|
stli_rxtmplen = len;
|
|
bcopy((char *) (shbuf + tail), &stli_rxtmpbuf[0], stlen);
|
|
len -= stlen;
|
|
if (len > 0)
|
|
bcopy((char *) shbuf, &stli_rxtmpbuf[stlen], len);
|
|
|
|
for (i = 0; (i < stli_rxtmplen); i++) {
|
|
ch = (unsigned char) stli_rxtmpbuf[i];
|
|
(*linesw[tp->t_line].l_rint)(ch, tp);
|
|
}
|
|
EBRDENABLE(brdp);
|
|
rp = &((volatile cdkasy_t *)
|
|
EBRDGETMEMPTR(brdp, portp->addr))->rxq;
|
|
if (stli_rxtmplen == 0) {
|
|
head = (unsigned int) rp->head;
|
|
if (head != ((unsigned int) rp->head))
|
|
head = (unsigned int) rp->head;
|
|
tail = head;
|
|
} else {
|
|
tail += i;
|
|
if (tail >= size)
|
|
tail -= size;
|
|
}
|
|
rp->tail = tail;
|
|
stli_rxtmpport = (stliport_t *) NULL;
|
|
stli_rxtmplen = 0;
|
|
}
|
|
|
|
portp->state |= ST_RXING;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Set up and carry out any delayed commands. There is only a small set
|
|
* of slave commands that can be done "off-level". So it is not too
|
|
* difficult to deal with them as a special case here.
|
|
*/
|
|
|
|
static __inline void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp)
|
|
{
|
|
int cmd;
|
|
|
|
if (portp->state & ST_DOSIGS) {
|
|
if ((portp->state & ST_DOFLUSHTX) &&
|
|
(portp->state & ST_DOFLUSHRX))
|
|
cmd = A_SETSIGNALSF;
|
|
else if (portp->state & ST_DOFLUSHTX)
|
|
cmd = A_SETSIGNALSFTX;
|
|
else if (portp->state & ST_DOFLUSHRX)
|
|
cmd = A_SETSIGNALSFRX;
|
|
else
|
|
cmd = A_SETSIGNALS;
|
|
portp->state &= ~(ST_DOFLUSHTX | ST_DOFLUSHRX | ST_DOSIGS);
|
|
bcopy((void *) &portp->asig, (void *) &(cp->args[0]),
|
|
sizeof(asysigs_t));
|
|
cp->status = 0;
|
|
cp->cmd = cmd;
|
|
portp->state |= ST_CMDING;
|
|
} else if ((portp->state & ST_DOFLUSHTX) ||
|
|
(portp->state & ST_DOFLUSHRX)) {
|
|
cmd = ((portp->state & ST_DOFLUSHTX) ? FLUSHTX : 0);
|
|
cmd |= ((portp->state & ST_DOFLUSHRX) ? FLUSHRX : 0);
|
|
portp->state &= ~(ST_DOFLUSHTX | ST_DOFLUSHRX);
|
|
bcopy((void *) &cmd, (void *) &(cp->args[0]), sizeof(int));
|
|
cp->status = 0;
|
|
cp->cmd = A_FLUSH;
|
|
portp->state |= ST_CMDING;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Host command service checking. This handles commands or messages
|
|
* coming from the slave to the host. Must have board shared memory
|
|
* enabled and interrupts off when called. Notice that by servicing the
|
|
* read data last we don't need to change the shared memory pointer
|
|
* during processing (which is a slow IO operation).
|
|
* Return value indicates if this port is still awaiting actions from
|
|
* the slave (like open, command, or even TX data being sent). If 0
|
|
* then port is still busy, otherwise the port request bit flag is
|
|
* returned.
|
|
*/
|
|
|
|
static __inline int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp)
|
|
{
|
|
volatile cdkasy_t *ap;
|
|
volatile cdkctrl_t *cp;
|
|
asynotify_t nt;
|
|
unsigned long oldsigs;
|
|
unsigned int head, tail;
|
|
int rc, donerx;
|
|
|
|
#if DEBUG
|
|
printf("stli_hostcmd(brdp=%x,portp=%x)\n", (int) brdp, (int) portp);
|
|
#endif
|
|
|
|
ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
|
|
cp = &ap->ctrl;
|
|
|
|
/*
|
|
* Check if we are waiting for an open completion message.
|
|
*/
|
|
if (portp->state & ST_OPENING) {
|
|
rc = (int) cp->openarg;
|
|
if ((cp->open == 0) && (rc != 0)) {
|
|
if (rc > 0)
|
|
rc--;
|
|
cp->openarg = 0;
|
|
portp->rc = rc;
|
|
portp->state &= ~ST_OPENING;
|
|
wakeup(&portp->state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if we are waiting for a close completion message.
|
|
*/
|
|
if (portp->state & ST_CLOSING) {
|
|
rc = (int) cp->closearg;
|
|
if ((cp->close == 0) && (rc != 0)) {
|
|
if (rc > 0)
|
|
rc--;
|
|
cp->closearg = 0;
|
|
portp->rc = rc;
|
|
portp->state &= ~ST_CLOSING;
|
|
wakeup(&portp->state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if we are waiting for a command completion message. We may
|
|
* need to copy out the command results associated with this command.
|
|
*/
|
|
if (portp->state & ST_CMDING) {
|
|
rc = cp->status;
|
|
if ((cp->cmd == 0) && (rc != 0)) {
|
|
if (rc > 0)
|
|
rc--;
|
|
if (portp->argp != (void *) NULL) {
|
|
bcopy((void *) &(cp->args[0]), portp->argp,
|
|
portp->argsize);
|
|
portp->argp = (void *) NULL;
|
|
}
|
|
cp->status = 0;
|
|
portp->rc = rc;
|
|
portp->state &= ~ST_CMDING;
|
|
stli_dodelaycmd(portp, cp);
|
|
wakeup(&portp->state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for any notification messages ready. This includes lots of
|
|
* different types of events - RX chars ready, RX break received,
|
|
* TX data low or empty in the slave, modem signals changed state.
|
|
* Must be extremely carefull if we call to the LD, it may call
|
|
* other routines of ours that will disable the memory...
|
|
* Something else we need to be carefull of is race conditions on
|
|
* marking the TX as empty...
|
|
*/
|
|
donerx = 0;
|
|
|
|
if (ap->notify) {
|
|
struct tty *tp;
|
|
|
|
nt = ap->changed;
|
|
ap->notify = 0;
|
|
tp = &portp->tty;
|
|
|
|
if (nt.signal & SG_DCD) {
|
|
oldsigs = portp->sigs;
|
|
portp->sigs = stli_mktiocm(nt.sigvalue);
|
|
portp->state &= ~ST_GETSIGS;
|
|
(*linesw[tp->t_line].l_modem)(tp,
|
|
(portp->sigs & TIOCM_CD));
|
|
EBRDENABLE(brdp);
|
|
}
|
|
if (nt.data & DT_RXBUSY) {
|
|
donerx++;
|
|
stli_rxprocess(brdp, portp);
|
|
}
|
|
if (nt.data & DT_RXBREAK) {
|
|
(*linesw[tp->t_line].l_rint)(TTY_BI, tp);
|
|
EBRDENABLE(brdp);
|
|
}
|
|
if (nt.data & DT_TXEMPTY) {
|
|
ap = (volatile cdkasy_t *)
|
|
EBRDGETMEMPTR(brdp, portp->addr);
|
|
head = (unsigned int) ap->txq.head;
|
|
tail = (unsigned int) ap->txq.tail;
|
|
if (tail != ((unsigned int) ap->txq.tail))
|
|
tail = (unsigned int) ap->txq.tail;
|
|
head = (head >= tail) ? (head - tail) :
|
|
portp->txsize - (tail - head);
|
|
if (head == 0) {
|
|
portp->state &= ~ST_TXBUSY;
|
|
tp->t_state &= ~TS_BUSY;
|
|
}
|
|
}
|
|
if (nt.data & (DT_TXEMPTY | DT_TXLOW)) {
|
|
(*linesw[tp->t_line].l_start)(tp);
|
|
EBRDENABLE(brdp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* It might seem odd that we are checking for more RX chars here.
|
|
* But, we need to handle the case where the tty buffer was previously
|
|
* filled, but we had more characters to pass up. The slave will not
|
|
* send any more RX notify messages until the RX buffer has been emptied.
|
|
* But it will leave the service bits on (since the buffer is not empty).
|
|
* So from here we can try to process more RX chars.
|
|
*/
|
|
if ((!donerx) && (portp->state & ST_RXING)) {
|
|
portp->state &= ~ST_RXING;
|
|
stli_rxprocess(brdp, portp);
|
|
}
|
|
|
|
return((portp->state & (ST_OPENING | ST_CLOSING | ST_CMDING |
|
|
ST_TXBUSY | ST_RXING)) ? 0 : 1);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Service all ports on a particular board. Assumes that the boards
|
|
* shared memory is enabled, and that the page pointer is pointed
|
|
* at the cdk header structure.
|
|
*/
|
|
|
|
static __inline void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp)
|
|
{
|
|
stliport_t *portp;
|
|
unsigned char hostbits[(STL_MAXCHANS / 8) + 1];
|
|
unsigned char slavebits[(STL_MAXCHANS / 8) + 1];
|
|
unsigned char *slavep;
|
|
int bitpos, bitat, bitsize;
|
|
int channr, nrdevs, slavebitchange;
|
|
|
|
bitsize = brdp->bitsize;
|
|
nrdevs = brdp->nrdevs;
|
|
|
|
/*
|
|
* Check if slave wants any service. Basically we try to do as
|
|
* little work as possible here. There are 2 levels of service
|
|
* bits. So if there is nothing to do we bail early. We check
|
|
* 8 service bits at a time in the inner loop, so we can bypass
|
|
* the lot if none of them want service.
|
|
*/
|
|
bcopy((((unsigned char *) hdrp) + brdp->hostoffset), &hostbits[0],
|
|
bitsize);
|
|
|
|
bzero(&slavebits[0], bitsize);
|
|
slavebitchange = 0;
|
|
|
|
for (bitpos = 0; (bitpos < bitsize); bitpos++) {
|
|
if (hostbits[bitpos] == 0)
|
|
continue;
|
|
channr = bitpos * 8;
|
|
bitat = 0x1;
|
|
for (; (channr < nrdevs); channr++, bitat <<=1) {
|
|
if (hostbits[bitpos] & bitat) {
|
|
portp = brdp->ports[(channr - 1)];
|
|
if (stli_hostcmd(brdp, portp)) {
|
|
slavebitchange++;
|
|
slavebits[bitpos] |= bitat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If any of the ports are no longer busy then update them in the
|
|
* slave request bits. We need to do this after, since a host port
|
|
* service may initiate more slave requests...
|
|
*/
|
|
if (slavebitchange) {
|
|
hdrp = (volatile cdkhdr_t *)
|
|
EBRDGETMEMPTR(brdp, CDK_CDKADDR);
|
|
slavep = ((unsigned char *) hdrp) + brdp->slaveoffset;
|
|
for (bitpos = 0; (bitpos < bitsize); bitpos++) {
|
|
if (slavebits[bitpos])
|
|
slavep[bitpos] &= ~slavebits[bitpos];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Driver poll routine. This routine polls the boards in use and passes
|
|
* messages back up to host when neccesary. This is actually very
|
|
* CPU efficient, since we will always have the kernel poll clock, it
|
|
* adds only a few cycles when idle (since board service can be
|
|
* determined very easily), but when loaded generates no interrupts
|
|
* (with their expensive associated context change).
|
|
*/
|
|
|
|
static void stli_poll(void *arg)
|
|
{
|
|
volatile cdkhdr_t *hdrp;
|
|
stlibrd_t *brdp;
|
|
int brdnr, x;
|
|
|
|
x = spltty();
|
|
|
|
/*
|
|
* Check each board and do any servicing required.
|
|
*/
|
|
for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) {
|
|
brdp = stli_brds[brdnr];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
continue;
|
|
if ((brdp->state & BST_STARTED) == 0)
|
|
continue;
|
|
|
|
EBRDENABLE(brdp);
|
|
hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
|
|
if (hdrp->hostreq)
|
|
stli_brdpoll(brdp, hdrp);
|
|
EBRDDISABLE(brdp);
|
|
}
|
|
splx(x);
|
|
|
|
timeout(stli_poll, 0, 1);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Translate the termios settings into the port setting structure of
|
|
* the slave.
|
|
*/
|
|
|
|
static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", (int) portp,
|
|
(int) pp, (int) tiosp);
|
|
#endif
|
|
|
|
bzero(pp, sizeof(asyport_t));
|
|
|
|
/*
|
|
* Start of by setting the baud, char size, parity and stop bit info.
|
|
*/
|
|
if (tiosp->c_ispeed == 0)
|
|
tiosp->c_ispeed = tiosp->c_ospeed;
|
|
if ((tiosp->c_ospeed < 0) || (tiosp->c_ospeed > STL_MAXBAUD))
|
|
tiosp->c_ospeed = STL_MAXBAUD;
|
|
pp->baudout = tiosp->c_ospeed;
|
|
pp->baudin = pp->baudout;
|
|
|
|
switch (tiosp->c_cflag & CSIZE) {
|
|
case CS5:
|
|
pp->csize = 5;
|
|
break;
|
|
case CS6:
|
|
pp->csize = 6;
|
|
break;
|
|
case CS7:
|
|
pp->csize = 7;
|
|
break;
|
|
default:
|
|
pp->csize = 8;
|
|
break;
|
|
}
|
|
|
|
if (tiosp->c_cflag & CSTOPB)
|
|
pp->stopbs = PT_STOP2;
|
|
else
|
|
pp->stopbs = PT_STOP1;
|
|
|
|
if (tiosp->c_cflag & PARENB) {
|
|
if (tiosp->c_cflag & PARODD)
|
|
pp->parity = PT_ODDPARITY;
|
|
else
|
|
pp->parity = PT_EVENPARITY;
|
|
} else {
|
|
pp->parity = PT_NOPARITY;
|
|
}
|
|
|
|
if (tiosp->c_iflag & ISTRIP)
|
|
pp->iflag |= FI_ISTRIP;
|
|
|
|
/*
|
|
* Set up any flow control options enabled.
|
|
*/
|
|
if (tiosp->c_iflag & IXON) {
|
|
pp->flow |= F_IXON;
|
|
if (tiosp->c_iflag & IXANY)
|
|
pp->flow |= F_IXANY;
|
|
}
|
|
if (tiosp->c_iflag & IXOFF)
|
|
pp->flow |= F_IXOFF;
|
|
if (tiosp->c_cflag & CCTS_OFLOW)
|
|
pp->flow |= F_CTSFLOW;
|
|
if (tiosp->c_cflag & CRTS_IFLOW)
|
|
pp->flow |= F_RTSFLOW;
|
|
|
|
pp->startin = tiosp->c_cc[VSTART];
|
|
pp->stopin = tiosp->c_cc[VSTOP];
|
|
pp->startout = tiosp->c_cc[VSTART];
|
|
pp->stopout = tiosp->c_cc[VSTOP];
|
|
|
|
/*
|
|
* Set up the RX char marking mask with those RX error types we must
|
|
* catch. We can get the slave to help us out a little here, it will
|
|
* ignore parity errors and breaks for us, and mark parity errors in
|
|
* the data stream.
|
|
*/
|
|
if (tiosp->c_iflag & IGNPAR)
|
|
pp->iflag |= FI_IGNRXERRS;
|
|
if (tiosp->c_iflag & IGNBRK)
|
|
pp->iflag |= FI_IGNBREAK;
|
|
if (tiosp->c_iflag & (INPCK | PARMRK))
|
|
pp->iflag |= FI_1MARKRXERRS;
|
|
|
|
/*
|
|
* Transfer any persistent flags into the asyport structure.
|
|
*/
|
|
pp->pflag = portp->pflag;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Construct a slave signals structure for setting the DTR and RTS
|
|
* signals as specified.
|
|
*/
|
|
|
|
static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_mkasysigs(sp=%x,dtr=%d,rts=%d)\n", (int) sp, dtr, rts);
|
|
#endif
|
|
|
|
bzero(sp, sizeof(asysigs_t));
|
|
if (dtr >= 0) {
|
|
sp->signal |= SG_DTR;
|
|
sp->sigvalue |= ((dtr > 0) ? SG_DTR : 0);
|
|
}
|
|
if (rts >= 0) {
|
|
sp->signal |= SG_RTS;
|
|
sp->sigvalue |= ((rts > 0) ? SG_RTS : 0);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Convert the signals returned from the slave into a local TIOCM type
|
|
* signals value. We keep them localy in TIOCM format.
|
|
*/
|
|
|
|
static long stli_mktiocm(unsigned long sigvalue)
|
|
{
|
|
long tiocm;
|
|
|
|
#if DEBUG
|
|
printf("stli_mktiocm(sigvalue=%x)\n", (int) sigvalue);
|
|
#endif
|
|
|
|
tiocm = 0;
|
|
tiocm |= ((sigvalue & SG_DCD) ? TIOCM_CD : 0);
|
|
tiocm |= ((sigvalue & SG_CTS) ? TIOCM_CTS : 0);
|
|
tiocm |= ((sigvalue & SG_RI) ? TIOCM_RI : 0);
|
|
tiocm |= ((sigvalue & SG_DSR) ? TIOCM_DSR : 0);
|
|
tiocm |= ((sigvalue & SG_DTR) ? TIOCM_DTR : 0);
|
|
tiocm |= ((sigvalue & SG_RTS) ? TIOCM_RTS : 0);
|
|
return(tiocm);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Enable l_rint processing bypass mode if tty modes allow it.
|
|
*/
|
|
|
|
static void stli_ttyoptim(stliport_t *portp, struct termios *tiosp)
|
|
{
|
|
struct tty *tp;
|
|
|
|
tp = &portp->tty;
|
|
if (((tiosp->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR)) == 0) &&
|
|
(((tiosp->c_iflag & BRKINT) == 0) || (tiosp->c_iflag & IGNBRK)) &&
|
|
(((tiosp->c_iflag & PARMRK) == 0) ||
|
|
((tiosp->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))) &&
|
|
((tiosp->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) ==0) &&
|
|
(linesw[tp->t_line].l_rint == ttyinput))
|
|
tp->t_state |= TS_CAN_BYPASS_L_RINT;
|
|
else
|
|
tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
|
|
portp->hotchar = linesw[tp->t_line].l_hotchar;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* All panels and ports actually attached have been worked out. All
|
|
* we need to do here is set up the appropriate per port data structures.
|
|
*/
|
|
|
|
static int stli_initports(stlibrd_t *brdp)
|
|
{
|
|
stliport_t *portp;
|
|
int i, panelnr, panelport;
|
|
|
|
#if DEBUG
|
|
printf("stli_initports(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) {
|
|
portp = (stliport_t *) malloc(sizeof(stliport_t), M_TTYS,
|
|
M_NOWAIT | M_ZERO);
|
|
if (portp == (stliport_t *) NULL) {
|
|
printf("STALLION: failed to allocate port structure\n");
|
|
continue;
|
|
}
|
|
|
|
portp->portnr = i;
|
|
portp->brdnr = brdp->brdnr;
|
|
portp->panelnr = panelnr;
|
|
portp->initintios.c_ispeed = STL_DEFSPEED;
|
|
portp->initintios.c_ospeed = STL_DEFSPEED;
|
|
portp->initintios.c_cflag = STL_DEFCFLAG;
|
|
portp->initintios.c_iflag = 0;
|
|
portp->initintios.c_oflag = 0;
|
|
portp->initintios.c_lflag = 0;
|
|
bcopy(&ttydefchars[0], &portp->initintios.c_cc[0],
|
|
sizeof(portp->initintios.c_cc));
|
|
portp->initouttios = portp->initintios;
|
|
portp->dtrwait = 3 * hz;
|
|
|
|
panelport++;
|
|
if (panelport >= brdp->panels[panelnr]) {
|
|
panelport = 0;
|
|
panelnr++;
|
|
}
|
|
brdp->ports[i] = portp;
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* All the following routines are board specific hardware operations.
|
|
*/
|
|
|
|
static void stli_ecpinit(stlibrd_t *brdp)
|
|
{
|
|
unsigned long memconf;
|
|
|
|
#if DEBUG
|
|
printf("stli_ecpinit(brdp=%d)\n", (int) brdp);
|
|
#endif
|
|
|
|
outb((brdp->iobase + ECP_ATCONFR), ECP_ATSTOP);
|
|
DELAY(10);
|
|
outb((brdp->iobase + ECP_ATCONFR), ECP_ATDISABLE);
|
|
DELAY(100);
|
|
|
|
memconf = (brdp->paddr & ECP_ATADDRMASK) >> ECP_ATADDRSHFT;
|
|
outb((brdp->iobase + ECP_ATMEMAR), memconf);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_ecpenable(stlibrd_t *brdp)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_ecpenable(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
outb((brdp->iobase + ECP_ATCONFR), ECP_ATENABLE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_ecpdisable(stlibrd_t *brdp)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_ecpdisable(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
outb((brdp->iobase + ECP_ATCONFR), ECP_ATDISABLE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
|
|
{
|
|
void *ptr;
|
|
unsigned char val;
|
|
|
|
#if DEBUG
|
|
printf("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
|
|
(int) offset);
|
|
#endif
|
|
|
|
if (offset > brdp->memsize) {
|
|
printf("STALLION: shared memory pointer=%x out of range at "
|
|
"line=%d(%d), brd=%d\n", (int) offset, line,
|
|
__LINE__, brdp->brdnr);
|
|
ptr = 0;
|
|
val = 0;
|
|
} else {
|
|
ptr = (char *) brdp->vaddr + (offset % ECP_ATPAGESIZE);
|
|
val = (unsigned char) (offset / ECP_ATPAGESIZE);
|
|
}
|
|
outb((brdp->iobase + ECP_ATMEMPR), val);
|
|
return(ptr);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_ecpreset(stlibrd_t *brdp)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_ecpreset(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
outb((brdp->iobase + ECP_ATCONFR), ECP_ATSTOP);
|
|
DELAY(10);
|
|
outb((brdp->iobase + ECP_ATCONFR), ECP_ATDISABLE);
|
|
DELAY(500);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_ecpintr(stlibrd_t *brdp)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_ecpintr(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
outb(brdp->iobase, 0x1);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* The following set of functions act on ECP EISA boards.
|
|
*/
|
|
|
|
static void stli_ecpeiinit(stlibrd_t *brdp)
|
|
{
|
|
unsigned long memconf;
|
|
|
|
#if DEBUG
|
|
printf("stli_ecpeiinit(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
outb((brdp->iobase + ECP_EIBRDENAB), 0x1);
|
|
outb((brdp->iobase + ECP_EICONFR), ECP_EISTOP);
|
|
DELAY(10);
|
|
outb((brdp->iobase + ECP_EICONFR), ECP_EIDISABLE);
|
|
DELAY(500);
|
|
|
|
memconf = (brdp->paddr & ECP_EIADDRMASKL) >> ECP_EIADDRSHFTL;
|
|
outb((brdp->iobase + ECP_EIMEMARL), memconf);
|
|
memconf = (brdp->paddr & ECP_EIADDRMASKH) >> ECP_EIADDRSHFTH;
|
|
outb((brdp->iobase + ECP_EIMEMARH), memconf);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_ecpeienable(stlibrd_t *brdp)
|
|
{
|
|
outb((brdp->iobase + ECP_EICONFR), ECP_EIENABLE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_ecpeidisable(stlibrd_t *brdp)
|
|
{
|
|
outb((brdp->iobase + ECP_EICONFR), ECP_EIDISABLE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
|
|
{
|
|
void *ptr;
|
|
unsigned char val;
|
|
|
|
#if DEBUG
|
|
printf("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n",
|
|
(int) brdp, (int) offset, line);
|
|
#endif
|
|
|
|
if (offset > brdp->memsize) {
|
|
printf("STALLION: shared memory pointer=%x out of range at "
|
|
"line=%d(%d), brd=%d\n", (int) offset, line,
|
|
__LINE__, brdp->brdnr);
|
|
ptr = 0;
|
|
val = 0;
|
|
} else {
|
|
ptr = (char *) brdp->vaddr + (offset % ECP_EIPAGESIZE);
|
|
if (offset < ECP_EIPAGESIZE)
|
|
val = ECP_EIENABLE;
|
|
else
|
|
val = ECP_EIENABLE | 0x40;
|
|
}
|
|
outb((brdp->iobase + ECP_EICONFR), val);
|
|
return(ptr);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_ecpeireset(stlibrd_t *brdp)
|
|
{
|
|
outb((brdp->iobase + ECP_EICONFR), ECP_EISTOP);
|
|
DELAY(10);
|
|
outb((brdp->iobase + ECP_EICONFR), ECP_EIDISABLE);
|
|
DELAY(500);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* The following set of functions act on ECP MCA boards.
|
|
*/
|
|
|
|
static void stli_ecpmcenable(stlibrd_t *brdp)
|
|
{
|
|
outb((brdp->iobase + ECP_MCCONFR), ECP_MCENABLE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_ecpmcdisable(stlibrd_t *brdp)
|
|
{
|
|
outb((brdp->iobase + ECP_MCCONFR), ECP_MCDISABLE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
|
|
{
|
|
void *ptr;
|
|
unsigned char val;
|
|
|
|
if (offset > brdp->memsize) {
|
|
printf("STALLION: shared memory pointer=%x out of range at "
|
|
"line=%d(%d), brd=%d\n", (int) offset, line,
|
|
__LINE__, brdp->brdnr);
|
|
ptr = 0;
|
|
val = 0;
|
|
} else {
|
|
ptr = (char *) brdp->vaddr + (offset % ECP_MCPAGESIZE);
|
|
val = ((unsigned char) (offset / ECP_MCPAGESIZE)) | ECP_MCENABLE;
|
|
}
|
|
outb((brdp->iobase + ECP_MCCONFR), val);
|
|
return(ptr);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_ecpmcreset(stlibrd_t *brdp)
|
|
{
|
|
outb((brdp->iobase + ECP_MCCONFR), ECP_MCSTOP);
|
|
DELAY(10);
|
|
outb((brdp->iobase + ECP_MCCONFR), ECP_MCDISABLE);
|
|
DELAY(500);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* The following routines act on ONboards.
|
|
*/
|
|
|
|
static void stli_onbinit(stlibrd_t *brdp)
|
|
{
|
|
unsigned long memconf;
|
|
int i;
|
|
|
|
#if DEBUG
|
|
printf("stli_onbinit(brdp=%d)\n", (int) brdp);
|
|
#endif
|
|
|
|
outb((brdp->iobase + ONB_ATCONFR), ONB_ATSTOP);
|
|
DELAY(10);
|
|
outb((brdp->iobase + ONB_ATCONFR), ONB_ATDISABLE);
|
|
for (i = 0; (i < 1000); i++)
|
|
DELAY(1000);
|
|
|
|
memconf = (brdp->paddr & ONB_ATADDRMASK) >> ONB_ATADDRSHFT;
|
|
outb((brdp->iobase + ONB_ATMEMAR), memconf);
|
|
outb(brdp->iobase, 0x1);
|
|
DELAY(1000);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_onbenable(stlibrd_t *brdp)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_onbenable(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
outb((brdp->iobase + ONB_ATCONFR), (ONB_ATENABLE | brdp->confbits));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_onbdisable(stlibrd_t *brdp)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_onbdisable(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
outb((brdp->iobase + ONB_ATCONFR), (ONB_ATDISABLE | brdp->confbits));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
|
|
{
|
|
void *ptr;
|
|
|
|
#if DEBUG
|
|
printf("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
|
|
(int) offset);
|
|
#endif
|
|
|
|
if (offset > brdp->memsize) {
|
|
printf("STALLION: shared memory pointer=%x out of range at "
|
|
"line=%d(%d), brd=%d\n", (int) offset, line,
|
|
__LINE__, brdp->brdnr);
|
|
ptr = 0;
|
|
} else {
|
|
ptr = (char *) brdp->vaddr + (offset % ONB_ATPAGESIZE);
|
|
}
|
|
return(ptr);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_onbreset(stlibrd_t *brdp)
|
|
{
|
|
int i;
|
|
|
|
#if DEBUG
|
|
printf("stli_onbreset(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
outb((brdp->iobase + ONB_ATCONFR), ONB_ATSTOP);
|
|
DELAY(10);
|
|
outb((brdp->iobase + ONB_ATCONFR), ONB_ATDISABLE);
|
|
for (i = 0; (i < 1000); i++)
|
|
DELAY(1000);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* The following routines act on ONboard EISA.
|
|
*/
|
|
|
|
static void stli_onbeinit(stlibrd_t *brdp)
|
|
{
|
|
unsigned long memconf;
|
|
int i;
|
|
|
|
#if DEBUG
|
|
printf("stli_onbeinit(brdp=%d)\n", (int) brdp);
|
|
#endif
|
|
|
|
outb((brdp->iobase + ONB_EIBRDENAB), 0x1);
|
|
outb((brdp->iobase + ONB_EICONFR), ONB_EISTOP);
|
|
DELAY(10);
|
|
outb((brdp->iobase + ONB_EICONFR), ONB_EIDISABLE);
|
|
for (i = 0; (i < 1000); i++)
|
|
DELAY(1000);
|
|
|
|
memconf = (brdp->paddr & ONB_EIADDRMASKL) >> ONB_EIADDRSHFTL;
|
|
outb((brdp->iobase + ONB_EIMEMARL), memconf);
|
|
memconf = (brdp->paddr & ONB_EIADDRMASKH) >> ONB_EIADDRSHFTH;
|
|
outb((brdp->iobase + ONB_EIMEMARH), memconf);
|
|
outb(brdp->iobase, 0x1);
|
|
DELAY(1000);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_onbeenable(stlibrd_t *brdp)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_onbeenable(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
outb((brdp->iobase + ONB_EICONFR), ONB_EIENABLE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_onbedisable(stlibrd_t *brdp)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_onbedisable(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
outb((brdp->iobase + ONB_EICONFR), ONB_EIDISABLE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
|
|
{
|
|
void *ptr;
|
|
unsigned char val;
|
|
|
|
#if DEBUG
|
|
printf("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp,
|
|
(int) offset, line);
|
|
#endif
|
|
|
|
if (offset > brdp->memsize) {
|
|
printf("STALLION: shared memory pointer=%x out of range at "
|
|
"line=%d(%d), brd=%d\n", (int) offset, line,
|
|
__LINE__, brdp->brdnr);
|
|
ptr = 0;
|
|
val = 0;
|
|
} else {
|
|
ptr = (char *) brdp->vaddr + (offset % ONB_EIPAGESIZE);
|
|
if (offset < ONB_EIPAGESIZE)
|
|
val = ONB_EIENABLE;
|
|
else
|
|
val = ONB_EIENABLE | 0x40;
|
|
}
|
|
outb((brdp->iobase + ONB_EICONFR), val);
|
|
return(ptr);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_onbereset(stlibrd_t *brdp)
|
|
{
|
|
int i;
|
|
|
|
#if DEBUG
|
|
printf("stli_onbereset(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
outb((brdp->iobase + ONB_EICONFR), ONB_EISTOP);
|
|
DELAY(10);
|
|
outb((brdp->iobase + ONB_EICONFR), ONB_EIDISABLE);
|
|
for (i = 0; (i < 1000); i++)
|
|
DELAY(1000);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* The following routines act on Brumby boards.
|
|
*/
|
|
|
|
static void stli_bbyinit(stlibrd_t *brdp)
|
|
{
|
|
int i;
|
|
|
|
#if DEBUG
|
|
printf("stli_bbyinit(brdp=%d)\n", (int) brdp);
|
|
#endif
|
|
|
|
outb((brdp->iobase + BBY_ATCONFR), BBY_ATSTOP);
|
|
DELAY(10);
|
|
outb((brdp->iobase + BBY_ATCONFR), 0);
|
|
for (i = 0; (i < 1000); i++)
|
|
DELAY(1000);
|
|
outb(brdp->iobase, 0x1);
|
|
DELAY(1000);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
|
|
{
|
|
void *ptr;
|
|
unsigned char val;
|
|
|
|
#if DEBUG
|
|
printf("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
|
|
(int) offset);
|
|
#endif
|
|
|
|
if (offset > brdp->memsize) {
|
|
printf("STALLION: shared memory pointer=%x out of range at "
|
|
"line=%d(%d), brd=%d\n", (int) offset, line,
|
|
__LINE__, brdp->brdnr);
|
|
ptr = 0;
|
|
val = 0;
|
|
} else {
|
|
ptr = (char *) brdp->vaddr + (offset % BBY_PAGESIZE);
|
|
val = (unsigned char) (offset / BBY_PAGESIZE);
|
|
}
|
|
outb((brdp->iobase + BBY_ATCONFR), val);
|
|
return(ptr);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_bbyreset(stlibrd_t *brdp)
|
|
{
|
|
int i;
|
|
|
|
#if DEBUG
|
|
printf("stli_bbyreset(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
outb((brdp->iobase + BBY_ATCONFR), BBY_ATSTOP);
|
|
DELAY(10);
|
|
outb((brdp->iobase + BBY_ATCONFR), 0);
|
|
for (i = 0; (i < 1000); i++)
|
|
DELAY(1000);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* The following routines act on original old Stallion boards.
|
|
*/
|
|
|
|
static void stli_stalinit(stlibrd_t *brdp)
|
|
{
|
|
int i;
|
|
|
|
#if DEBUG
|
|
printf("stli_stalinit(brdp=%d)\n", (int) brdp);
|
|
#endif
|
|
|
|
outb(brdp->iobase, 0x1);
|
|
for (i = 0; (i < 1000); i++)
|
|
DELAY(1000);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
|
|
{
|
|
void *ptr;
|
|
|
|
#if DEBUG
|
|
printf("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
|
|
(int) offset);
|
|
#endif
|
|
|
|
if (offset > brdp->memsize) {
|
|
printf("STALLION: shared memory pointer=%x out of range at "
|
|
"line=%d(%d), brd=%d\n", (int) offset, line,
|
|
__LINE__, brdp->brdnr);
|
|
ptr = 0;
|
|
} else {
|
|
ptr = (char *) brdp->vaddr + (offset % STAL_PAGESIZE);
|
|
}
|
|
return(ptr);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void stli_stalreset(stlibrd_t *brdp)
|
|
{
|
|
volatile unsigned long *vecp;
|
|
int i;
|
|
|
|
#if DEBUG
|
|
printf("stli_stalreset(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
vecp = (volatile unsigned long *) ((char *) brdp->vaddr + 0x30);
|
|
*vecp = 0xffff0000;
|
|
outb(brdp->iobase, 0);
|
|
for (i = 0; (i < 1000); i++)
|
|
DELAY(1000);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Try to find an ECP board and initialize it. This handles only ECP
|
|
* board types.
|
|
*/
|
|
|
|
static int stli_initecp(stlibrd_t *brdp)
|
|
{
|
|
cdkecpsig_t sig;
|
|
cdkecpsig_t *sigsp;
|
|
unsigned int status, nxtid;
|
|
int panelnr;
|
|
|
|
#if DEBUG
|
|
printf("stli_initecp(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
/*
|
|
* Do a basic sanity check on the IO and memory addresses.
|
|
*/
|
|
if ((brdp->iobase == 0) || (brdp->paddr == 0))
|
|
return(EINVAL);
|
|
|
|
/*
|
|
* Based on the specific board type setup the common vars to access
|
|
* and enable shared memory. Set all board specific information now
|
|
* as well.
|
|
*/
|
|
switch (brdp->brdtype) {
|
|
case BRD_ECP:
|
|
brdp->memsize = ECP_MEMSIZE;
|
|
brdp->pagesize = ECP_ATPAGESIZE;
|
|
brdp->init = stli_ecpinit;
|
|
brdp->enable = stli_ecpenable;
|
|
brdp->reenable = stli_ecpenable;
|
|
brdp->disable = stli_ecpdisable;
|
|
brdp->getmemptr = stli_ecpgetmemptr;
|
|
brdp->intr = stli_ecpintr;
|
|
brdp->reset = stli_ecpreset;
|
|
break;
|
|
|
|
case BRD_ECPE:
|
|
brdp->memsize = ECP_MEMSIZE;
|
|
brdp->pagesize = ECP_EIPAGESIZE;
|
|
brdp->init = stli_ecpeiinit;
|
|
brdp->enable = stli_ecpeienable;
|
|
brdp->reenable = stli_ecpeienable;
|
|
brdp->disable = stli_ecpeidisable;
|
|
brdp->getmemptr = stli_ecpeigetmemptr;
|
|
brdp->intr = stli_ecpintr;
|
|
brdp->reset = stli_ecpeireset;
|
|
break;
|
|
|
|
case BRD_ECPMC:
|
|
brdp->memsize = ECP_MEMSIZE;
|
|
brdp->pagesize = ECP_MCPAGESIZE;
|
|
brdp->init = NULL;
|
|
brdp->enable = stli_ecpmcenable;
|
|
brdp->reenable = stli_ecpmcenable;
|
|
brdp->disable = stli_ecpmcdisable;
|
|
brdp->getmemptr = stli_ecpmcgetmemptr;
|
|
brdp->intr = stli_ecpintr;
|
|
brdp->reset = stli_ecpmcreset;
|
|
break;
|
|
|
|
default:
|
|
return(EINVAL);
|
|
}
|
|
|
|
/*
|
|
* The per-board operations structure is all setup, so now lets go
|
|
* and get the board operational. Firstly initialize board configuration
|
|
* registers.
|
|
*/
|
|
EBRDINIT(brdp);
|
|
|
|
/*
|
|
* Now that all specific code is set up, enable the shared memory and
|
|
* look for the a signature area that will tell us exactly what board
|
|
* this is, and what it is connected to it.
|
|
*/
|
|
EBRDENABLE(brdp);
|
|
sigsp = (cdkecpsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
|
|
bcopy(sigsp, &sig, sizeof(cdkecpsig_t));
|
|
EBRDDISABLE(brdp);
|
|
|
|
#if 0
|
|
printf("%s(%d): sig-> magic=%x rom=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n",
|
|
__file__, __LINE__, (int) sig.magic, sig.romver,
|
|
sig.panelid[0], (int) sig.panelid[1], (int) sig.panelid[2],
|
|
(int) sig.panelid[3], (int) sig.panelid[4],
|
|
(int) sig.panelid[5], (int) sig.panelid[6],
|
|
(int) sig.panelid[7]);
|
|
#endif
|
|
|
|
if (sig.magic != ECP_MAGIC)
|
|
return(ENXIO);
|
|
|
|
/*
|
|
* Scan through the signature looking at the panels connected to the
|
|
* board. Calculate the total number of ports as we go.
|
|
*/
|
|
for (panelnr = 0, nxtid = 0; (panelnr < STL_MAXPANELS); panelnr++) {
|
|
status = sig.panelid[nxtid];
|
|
if ((status & ECH_PNLIDMASK) != nxtid)
|
|
break;
|
|
brdp->panelids[panelnr] = status;
|
|
if (status & ECH_PNL16PORT) {
|
|
brdp->panels[panelnr] = 16;
|
|
brdp->nrports += 16;
|
|
nxtid += 2;
|
|
} else {
|
|
brdp->panels[panelnr] = 8;
|
|
brdp->nrports += 8;
|
|
nxtid++;
|
|
}
|
|
brdp->nrpanels++;
|
|
}
|
|
|
|
brdp->state |= BST_FOUND;
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Try to find an ONboard, Brumby or Stallion board and initialize it.
|
|
* This handles only these board types.
|
|
*/
|
|
|
|
static int stli_initonb(stlibrd_t *brdp)
|
|
{
|
|
cdkonbsig_t sig;
|
|
cdkonbsig_t *sigsp;
|
|
int i;
|
|
|
|
#if DEBUG
|
|
printf("stli_initonb(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
/*
|
|
* Do a basic sanity check on the IO and memory addresses.
|
|
*/
|
|
if ((brdp->iobase == 0) || (brdp->paddr == 0))
|
|
return(EINVAL);
|
|
|
|
/*
|
|
* Based on the specific board type setup the common vars to access
|
|
* and enable shared memory. Set all board specific information now
|
|
* as well.
|
|
*/
|
|
switch (brdp->brdtype) {
|
|
case BRD_ONBOARD:
|
|
case BRD_ONBOARD32:
|
|
case BRD_ONBOARD2:
|
|
case BRD_ONBOARD2_32:
|
|
case BRD_ONBOARDRS:
|
|
brdp->memsize = ONB_MEMSIZE;
|
|
brdp->pagesize = ONB_ATPAGESIZE;
|
|
brdp->init = stli_onbinit;
|
|
brdp->enable = stli_onbenable;
|
|
brdp->reenable = stli_onbenable;
|
|
brdp->disable = stli_onbdisable;
|
|
brdp->getmemptr = stli_onbgetmemptr;
|
|
brdp->intr = stli_ecpintr;
|
|
brdp->reset = stli_onbreset;
|
|
brdp->confbits = (brdp->paddr > 0x100000) ? ONB_HIMEMENAB : 0;
|
|
break;
|
|
|
|
case BRD_ONBOARDE:
|
|
brdp->memsize = ONB_EIMEMSIZE;
|
|
brdp->pagesize = ONB_EIPAGESIZE;
|
|
brdp->init = stli_onbeinit;
|
|
brdp->enable = stli_onbeenable;
|
|
brdp->reenable = stli_onbeenable;
|
|
brdp->disable = stli_onbedisable;
|
|
brdp->getmemptr = stli_onbegetmemptr;
|
|
brdp->intr = stli_ecpintr;
|
|
brdp->reset = stli_onbereset;
|
|
break;
|
|
|
|
case BRD_BRUMBY4:
|
|
case BRD_BRUMBY8:
|
|
case BRD_BRUMBY16:
|
|
brdp->memsize = BBY_MEMSIZE;
|
|
brdp->pagesize = BBY_PAGESIZE;
|
|
brdp->init = stli_bbyinit;
|
|
brdp->enable = NULL;
|
|
brdp->reenable = NULL;
|
|
brdp->disable = NULL;
|
|
brdp->getmemptr = stli_bbygetmemptr;
|
|
brdp->intr = stli_ecpintr;
|
|
brdp->reset = stli_bbyreset;
|
|
break;
|
|
|
|
case BRD_STALLION:
|
|
brdp->memsize = STAL_MEMSIZE;
|
|
brdp->pagesize = STAL_PAGESIZE;
|
|
brdp->init = stli_stalinit;
|
|
brdp->enable = NULL;
|
|
brdp->reenable = NULL;
|
|
brdp->disable = NULL;
|
|
brdp->getmemptr = stli_stalgetmemptr;
|
|
brdp->intr = stli_ecpintr;
|
|
brdp->reset = stli_stalreset;
|
|
break;
|
|
|
|
default:
|
|
return(EINVAL);
|
|
}
|
|
|
|
/*
|
|
* The per-board operations structure is all setup, so now lets go
|
|
* and get the board operational. Firstly initialize board configuration
|
|
* registers.
|
|
*/
|
|
EBRDINIT(brdp);
|
|
|
|
/*
|
|
* Now that all specific code is set up, enable the shared memory and
|
|
* look for the a signature area that will tell us exactly what board
|
|
* this is, and how many ports.
|
|
*/
|
|
EBRDENABLE(brdp);
|
|
sigsp = (cdkonbsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
|
|
bcopy(sigsp, &sig, sizeof(cdkonbsig_t));
|
|
EBRDDISABLE(brdp);
|
|
|
|
#if 0
|
|
printf("%s(%d): sig-> magic=%x:%x:%x:%x romver=%x amask=%x:%x:%x\n",
|
|
__file__, __LINE__, sig.magic0, sig.magic1, sig.magic2,
|
|
sig.magic3, sig.romver, sig.amask0, sig.amask1, sig.amask2);
|
|
#endif
|
|
|
|
if ((sig.magic0 != ONB_MAGIC0) || (sig.magic1 != ONB_MAGIC1) ||
|
|
(sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3))
|
|
return(ENXIO);
|
|
|
|
/*
|
|
* Scan through the signature alive mask and calculate how many ports
|
|
* there are on this board.
|
|
*/
|
|
brdp->nrpanels = 1;
|
|
if (sig.amask1) {
|
|
brdp->nrports = 32;
|
|
} else {
|
|
for (i = 0; (i < 16); i++) {
|
|
if (((sig.amask0 << i) & 0x8000) == 0)
|
|
break;
|
|
}
|
|
brdp->nrports = i;
|
|
}
|
|
brdp->panels[0] = brdp->nrports;
|
|
|
|
brdp->state |= BST_FOUND;
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Start up a running board. This routine is only called after the
|
|
* code has been down loaded to the board and is operational. It will
|
|
* read in the memory map, and get the show on the road...
|
|
*/
|
|
|
|
static int stli_startbrd(stlibrd_t *brdp)
|
|
{
|
|
volatile cdkhdr_t *hdrp;
|
|
volatile cdkmem_t *memp;
|
|
volatile cdkasy_t *ap;
|
|
stliport_t *portp;
|
|
int portnr, nrdevs, i, rc, x;
|
|
|
|
#if DEBUG
|
|
printf("stli_startbrd(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
rc = 0;
|
|
|
|
x = spltty();
|
|
EBRDENABLE(brdp);
|
|
hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
|
|
nrdevs = hdrp->nrdevs;
|
|
|
|
#if 0
|
|
printf("%s(%d): CDK version %d.%d.%d --> nrdevs=%d memp=%x hostp=%x "
|
|
"slavep=%x\n", __file__, __LINE__, hdrp->ver_release,
|
|
hdrp->ver_modification, hdrp->ver_fix, nrdevs,
|
|
(int) hdrp->memp, (int) hdrp->hostp, (int) hdrp->slavep);
|
|
#endif
|
|
|
|
if (nrdevs < (brdp->nrports + 1)) {
|
|
printf("STALLION: slave failed to allocate memory for all "
|
|
"devices, devices=%d\n", nrdevs);
|
|
brdp->nrports = nrdevs - 1;
|
|
}
|
|
brdp->nrdevs = nrdevs;
|
|
brdp->hostoffset = hdrp->hostp - CDK_CDKADDR;
|
|
brdp->slaveoffset = hdrp->slavep - CDK_CDKADDR;
|
|
brdp->bitsize = (nrdevs + 7) / 8;
|
|
memp = (volatile cdkmem_t *) (void *) (uintptr_t) hdrp->memp;
|
|
if (((uintptr_t) (void *) memp) > brdp->memsize) {
|
|
printf("STALLION: corrupted shared memory region?\n");
|
|
rc = EIO;
|
|
goto stli_donestartup;
|
|
}
|
|
memp = (volatile cdkmem_t *) EBRDGETMEMPTR(brdp,
|
|
(uintptr_t) (void *) memp);
|
|
if (memp->dtype != TYP_ASYNCTRL) {
|
|
printf("STALLION: no slave control device found\n");
|
|
rc = EIO;
|
|
goto stli_donestartup;
|
|
}
|
|
memp++;
|
|
|
|
/*
|
|
* Cycle through memory allocation of each port. We are guaranteed to
|
|
* have all ports inside the first page of slave window, so no need to
|
|
* change pages while reading memory map.
|
|
*/
|
|
for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++, memp++) {
|
|
if (memp->dtype != TYP_ASYNC)
|
|
break;
|
|
portp = brdp->ports[portnr];
|
|
if (portp == (stliport_t *) NULL)
|
|
break;
|
|
portp->devnr = i;
|
|
portp->addr = memp->offset;
|
|
portp->reqidx = (unsigned char) (i * 8 / nrdevs);
|
|
portp->reqbit = (unsigned char) (0x1 << portp->reqidx);
|
|
portp->portidx = (unsigned char) (i / 8);
|
|
portp->portbit = (unsigned char) (0x1 << (i % 8));
|
|
}
|
|
|
|
hdrp->slavereq = 0xff;
|
|
|
|
/*
|
|
* For each port setup a local copy of the RX and TX buffer offsets
|
|
* and sizes. We do this separate from the above, because we need to
|
|
* move the shared memory page...
|
|
*/
|
|
for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++) {
|
|
portp = brdp->ports[portnr];
|
|
if (portp == (stliport_t *) NULL)
|
|
break;
|
|
if (portp->addr == 0)
|
|
break;
|
|
ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
|
|
if (ap != (volatile cdkasy_t *) NULL) {
|
|
portp->rxsize = ap->rxq.size;
|
|
portp->txsize = ap->txq.size;
|
|
portp->rxoffset = ap->rxq.offset;
|
|
portp->txoffset = ap->txq.offset;
|
|
}
|
|
}
|
|
|
|
stli_donestartup:
|
|
EBRDDISABLE(brdp);
|
|
splx(x);
|
|
|
|
if (rc == 0)
|
|
brdp->state |= BST_STARTED;
|
|
|
|
if (stli_doingtimeout == 0) {
|
|
timeout(stli_poll, 0, 1);
|
|
stli_doingtimeout++;
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Probe and initialize the specified board.
|
|
*/
|
|
|
|
static int stli_brdinit(stlibrd_t *brdp)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_brdinit(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
stli_brds[brdp->brdnr] = brdp;
|
|
|
|
switch (brdp->brdtype) {
|
|
case BRD_ECP:
|
|
case BRD_ECPE:
|
|
case BRD_ECPMC:
|
|
stli_initecp(brdp);
|
|
break;
|
|
case BRD_ONBOARD:
|
|
case BRD_ONBOARDE:
|
|
case BRD_ONBOARD2:
|
|
case BRD_ONBOARD32:
|
|
case BRD_ONBOARD2_32:
|
|
case BRD_ONBOARDRS:
|
|
case BRD_BRUMBY4:
|
|
case BRD_BRUMBY8:
|
|
case BRD_BRUMBY16:
|
|
case BRD_STALLION:
|
|
stli_initonb(brdp);
|
|
break;
|
|
case BRD_EASYIO:
|
|
case BRD_ECH:
|
|
case BRD_ECHMC:
|
|
case BRD_ECHPCI:
|
|
printf("STALLION: %s board type not supported in this driver\n",
|
|
stli_brdnames[brdp->brdtype]);
|
|
return(ENODEV);
|
|
default:
|
|
printf("STALLION: unit=%d is unknown board type=%d\n",
|
|
brdp->brdnr, brdp->brdtype);
|
|
return(ENODEV);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Finish off the remaining initialization for a board.
|
|
*/
|
|
|
|
static int stli_brdattach(stlibrd_t *brdp)
|
|
{
|
|
#if DEBUG
|
|
printf("stli_brdattach(brdp=%x)\n", (int) brdp);
|
|
#endif
|
|
|
|
#if 0
|
|
if ((brdp->state & BST_FOUND) == 0) {
|
|
printf("STALLION: %s board not found, unit=%d io=%x mem=%x\n",
|
|
stli_brdnames[brdp->brdtype], brdp->brdnr,
|
|
brdp->iobase, (int) brdp->paddr);
|
|
return(ENXIO);
|
|
}
|
|
#endif
|
|
|
|
stli_initports(brdp);
|
|
printf("stli%d: %s (driver version %s), unit=%d nrpanels=%d "
|
|
"nrports=%d\n", brdp->unitid, stli_brdnames[brdp->brdtype],
|
|
stli_drvversion, brdp->brdnr, brdp->nrpanels, brdp->nrports);
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Check for possible shared memory sharing between boards.
|
|
* FIX: need to start this optimization somewhere...
|
|
*/
|
|
|
|
#ifdef notdef
|
|
static int stli_chksharemem()
|
|
{
|
|
stlibrd_t *brdp, *nxtbrdp;
|
|
int i, j;
|
|
|
|
#if DEBUG
|
|
printf("stli_chksharemem()\n");
|
|
#endif
|
|
|
|
/*
|
|
* All found boards are initialized. Now for a little optimization, if
|
|
* no boards are sharing the "shared memory" regions then we can just
|
|
* leave them all enabled. This is in fact the usual case.
|
|
*/
|
|
stli_shared = 0;
|
|
if (stli_nrbrds > 1) {
|
|
for (i = 0; (i < stli_nrbrds); i++) {
|
|
brdp = stli_brds[i];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
continue;
|
|
for (j = i + 1; (j < stli_nrbrds); j++) {
|
|
nxtbrdp = stli_brds[j];
|
|
if (nxtbrdp == (stlibrd_t *) NULL)
|
|
continue;
|
|
if ((brdp->paddr >= nxtbrdp->paddr) &&
|
|
(brdp->paddr <= (nxtbrdp->paddr +
|
|
nxtbrdp->memsize - 1))) {
|
|
stli_shared++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (stli_shared == 0) {
|
|
for (i = 0; (i < stli_nrbrds); i++) {
|
|
brdp = stli_brds[i];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
continue;
|
|
if (brdp->state & BST_FOUND) {
|
|
EBRDENABLE(brdp);
|
|
brdp->enable = NULL;
|
|
brdp->disable = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
#endif /* notdef */
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Return the board stats structure to user app.
|
|
*/
|
|
|
|
static int stli_getbrdstats(caddr_t data)
|
|
{
|
|
stlibrd_t *brdp;
|
|
int i;
|
|
|
|
#if DEBUG
|
|
printf("stli_getbrdstats(data=%p)\n", (void *) data);
|
|
#endif
|
|
|
|
stli_brdstats = *((combrd_t *) data);
|
|
if (stli_brdstats.brd >= STL_MAXBRDS)
|
|
return(-ENODEV);
|
|
brdp = stli_brds[stli_brdstats.brd];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
return(-ENODEV);
|
|
|
|
bzero(&stli_brdstats, sizeof(combrd_t));
|
|
stli_brdstats.brd = brdp->brdnr;
|
|
stli_brdstats.type = brdp->brdtype;
|
|
stli_brdstats.hwid = 0;
|
|
stli_brdstats.state = brdp->state;
|
|
stli_brdstats.ioaddr = brdp->iobase;
|
|
stli_brdstats.memaddr = brdp->paddr;
|
|
stli_brdstats.nrpanels = brdp->nrpanels;
|
|
stli_brdstats.nrports = brdp->nrports;
|
|
for (i = 0; (i < brdp->nrpanels); i++) {
|
|
stli_brdstats.panels[i].panel = i;
|
|
stli_brdstats.panels[i].hwid = brdp->panelids[i];
|
|
stli_brdstats.panels[i].nrports = brdp->panels[i];
|
|
}
|
|
|
|
*((combrd_t *) data) = stli_brdstats;
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Resolve the referenced port number into a port struct pointer.
|
|
*/
|
|
|
|
static stliport_t *stli_getport(int brdnr, int panelnr, int portnr)
|
|
{
|
|
stlibrd_t *brdp;
|
|
int i;
|
|
|
|
if ((brdnr < 0) || (brdnr >= STL_MAXBRDS))
|
|
return((stliport_t *) NULL);
|
|
brdp = stli_brds[brdnr];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
return((stliport_t *) NULL);
|
|
for (i = 0; (i < panelnr); i++)
|
|
portnr += brdp->panels[i];
|
|
if ((portnr < 0) || (portnr >= brdp->nrports))
|
|
return((stliport_t *) NULL);
|
|
return(brdp->ports[portnr]);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Return the port stats structure to user app. A NULL port struct
|
|
* pointer passed in means that we need to find out from the app
|
|
* what port to get stats for (used through board control device).
|
|
*/
|
|
|
|
static int stli_getportstats(stliport_t *portp, caddr_t data)
|
|
{
|
|
stlibrd_t *brdp;
|
|
int rc;
|
|
|
|
if (portp == (stliport_t *) NULL) {
|
|
stli_comstats = *((comstats_t *) data);
|
|
portp = stli_getport(stli_comstats.brd, stli_comstats.panel,
|
|
stli_comstats.port);
|
|
if (portp == (stliport_t *) NULL)
|
|
return(-ENODEV);
|
|
}
|
|
|
|
brdp = stli_brds[portp->brdnr];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
return(-ENODEV);
|
|
|
|
if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, &stli_cdkstats,
|
|
sizeof(asystats_t), 1)) < 0)
|
|
return(rc);
|
|
|
|
stli_comstats.brd = portp->brdnr;
|
|
stli_comstats.panel = portp->panelnr;
|
|
stli_comstats.port = portp->portnr;
|
|
stli_comstats.state = portp->state;
|
|
/*stli_comstats.flags = portp->flags;*/
|
|
stli_comstats.ttystate = portp->tty.t_state;
|
|
stli_comstats.cflags = portp->tty.t_cflag;
|
|
stli_comstats.iflags = portp->tty.t_iflag;
|
|
stli_comstats.oflags = portp->tty.t_oflag;
|
|
stli_comstats.lflags = portp->tty.t_lflag;
|
|
|
|
stli_comstats.txtotal = stli_cdkstats.txchars;
|
|
stli_comstats.rxtotal = stli_cdkstats.rxchars + stli_cdkstats.ringover;
|
|
stli_comstats.txbuffered = stli_cdkstats.txringq;
|
|
stli_comstats.rxbuffered = stli_cdkstats.rxringq;
|
|
stli_comstats.rxoverrun = stli_cdkstats.overruns;
|
|
stli_comstats.rxparity = stli_cdkstats.parity;
|
|
stli_comstats.rxframing = stli_cdkstats.framing;
|
|
stli_comstats.rxlost = stli_cdkstats.ringover + portp->rxlost;
|
|
stli_comstats.rxbreaks = stli_cdkstats.rxbreaks;
|
|
stli_comstats.txbreaks = stli_cdkstats.txbreaks;
|
|
stli_comstats.txxon = stli_cdkstats.txstart;
|
|
stli_comstats.txxoff = stli_cdkstats.txstop;
|
|
stli_comstats.rxxon = stli_cdkstats.rxstart;
|
|
stli_comstats.rxxoff = stli_cdkstats.rxstop;
|
|
stli_comstats.rxrtsoff = stli_cdkstats.rtscnt / 2;
|
|
stli_comstats.rxrtson = stli_cdkstats.rtscnt - stli_comstats.rxrtsoff;
|
|
stli_comstats.modem = stli_cdkstats.dcdcnt;
|
|
stli_comstats.hwid = stli_cdkstats.hwid;
|
|
stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals);
|
|
|
|
*((comstats_t *) data) = stli_comstats;;
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Clear the port stats structure. We also return it zeroed out...
|
|
*/
|
|
|
|
static int stli_clrportstats(stliport_t *portp, caddr_t data)
|
|
{
|
|
stlibrd_t *brdp;
|
|
int rc;
|
|
|
|
if (portp == (stliport_t *) NULL) {
|
|
stli_comstats = *((comstats_t *) data);
|
|
portp = stli_getport(stli_comstats.brd, stli_comstats.panel,
|
|
stli_comstats.port);
|
|
if (portp == (stliport_t *) NULL)
|
|
return(-ENODEV);
|
|
}
|
|
|
|
brdp = stli_brds[portp->brdnr];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
return(-ENODEV);
|
|
|
|
if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, 0, 0, 0)) < 0)
|
|
return(rc);
|
|
|
|
portp->rxlost = 0;
|
|
bzero(&stli_comstats, sizeof(comstats_t));
|
|
stli_comstats.brd = portp->brdnr;
|
|
stli_comstats.panel = portp->panelnr;
|
|
stli_comstats.port = portp->portnr;
|
|
|
|
*((comstats_t *) data) = stli_comstats;;
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Code to handle a "staliomem" read and write operations. This device
|
|
* is the contents of the board shared memory. It is used for down
|
|
* loading the slave image (and debugging :-)
|
|
*/
|
|
|
|
STATIC int stli_memrw(dev_t dev, struct uio *uiop, int flag)
|
|
{
|
|
stlibrd_t *brdp;
|
|
void *memptr;
|
|
int brdnr, size, n, error, x;
|
|
|
|
#if DEBUG
|
|
printf("stli_memrw(dev=%x,uiop=%x,flag=%x)\n", (int) dev,
|
|
(int) uiop, flag);
|
|
#endif
|
|
|
|
brdnr = minor(dev) & 0x7;
|
|
brdp = stli_brds[brdnr];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
return(ENODEV);
|
|
if (brdp->state == 0)
|
|
return(ENODEV);
|
|
|
|
if (uiop->uio_offset >= brdp->memsize)
|
|
return(0);
|
|
|
|
error = 0;
|
|
size = brdp->memsize - uiop->uio_offset;
|
|
|
|
x = spltty();
|
|
EBRDENABLE(brdp);
|
|
while (size > 0) {
|
|
memptr = (void *) EBRDGETMEMPTR(brdp, uiop->uio_offset);
|
|
n = MIN(size, (brdp->pagesize -
|
|
(((unsigned long) uiop->uio_offset) % brdp->pagesize)));
|
|
error = uiomove(memptr, n, uiop);
|
|
if ((uiop->uio_resid == 0) || error)
|
|
break;
|
|
}
|
|
EBRDDISABLE(brdp);
|
|
splx(x);
|
|
|
|
return(error);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* The "staliomem" device is also required to do some special operations
|
|
* on the board. We need to be able to send an interrupt to the board,
|
|
* reset it, and start/stop it.
|
|
*/
|
|
|
|
static int stli_memioctl(dev_t dev, unsigned long cmd, caddr_t data, int flag,
|
|
struct thread *td)
|
|
{
|
|
stlibrd_t *brdp;
|
|
int brdnr, rc;
|
|
|
|
#if DEBUG
|
|
printf("stli_memioctl(dev=%s,cmd=%lx,data=%p,flag=%x)\n",
|
|
devtoname(dev), cmd, (void *) data, flag);
|
|
#endif
|
|
|
|
brdnr = minor(dev) & 0x7;
|
|
brdp = stli_brds[brdnr];
|
|
if (brdp == (stlibrd_t *) NULL)
|
|
return(ENODEV);
|
|
if (brdp->state == 0)
|
|
return(ENODEV);
|
|
|
|
rc = 0;
|
|
|
|
switch (cmd) {
|
|
case STL_BINTR:
|
|
EBRDINTR(brdp);
|
|
break;
|
|
case STL_BSTART:
|
|
rc = stli_startbrd(brdp);
|
|
break;
|
|
case STL_BSTOP:
|
|
brdp->state &= ~BST_STARTED;
|
|
break;
|
|
case STL_BRESET:
|
|
brdp->state &= ~BST_STARTED;
|
|
EBRDRESET(brdp);
|
|
if (stli_shared == 0) {
|
|
if (brdp->reenable != NULL)
|
|
(* brdp->reenable)(brdp);
|
|
}
|
|
break;
|
|
case COM_GETPORTSTATS:
|
|
rc = stli_getportstats((stliport_t *) NULL, data);
|
|
break;
|
|
case COM_CLRPORTSTATS:
|
|
rc = stli_clrportstats((stliport_t *) NULL, data);
|
|
break;
|
|
case COM_GETBRDSTATS:
|
|
rc = stli_getbrdstats(data);
|
|
break;
|
|
default:
|
|
rc = ENOTTY;
|
|
break;
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
/*****************************************************************************/
|