/*****************************************************************************/ /* * 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" #define TTYDEFCHARS 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 proc *p); 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 = { /* open */ stliopen, /* close */ stliclose, /* read */ stliread, /* write */ stliwrite, /* ioctl */ stliioctl, /* poll */ ttypoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ stli_drvname, /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_TTY | D_KQFILTER, /* bmaj */ -1, /* 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; static int once; if (!once++) cdevsw_add(&stli_cdevsw); #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); return(1); } /*****************************************************************************/ STATIC int stliopen(dev_t dev, int flag, int mode, struct proc *p) { 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) p); #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(p)) { 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 proc *p) { 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 *) p); #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 proc *p) { 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 *) p); #endif if (minor(dev) & STL_MEMDEV) return(stli_memioctl(dev, cmd, data, flag, p)); 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(p)) == 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, p); 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(p)) == 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 an "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 proc *p) { 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); } /*****************************************************************************/