2005-04-17 02:20:36 +04:00
/*****************************************************************************/
/*
* istallion . c - - stallion intelligent multiport serial driver .
*
* Copyright ( C ) 1996 - 1999 Stallion Technologies
* Copyright ( C ) 1994 - 1996 Greg Ungerer .
*
* This code is loosely based on the Linux serial driver , written by
* Linus Torvalds , Theodore T ' so and others .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
/*****************************************************************************/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <linux/serial.h>
# include <linux/cdk.h>
# include <linux/comstats.h>
# include <linux/istallion.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/devfs_fs_kernel.h>
# include <linux/device.h>
# include <linux/wait.h>
# include <asm/io.h>
# include <asm/uaccess.h>
# ifdef CONFIG_PCI
# include <linux/pci.h>
# 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_ECH64PCI 27
# define BRD_EASYIOPCI 28
# define BRD_ECPPCI 29
# define BRD_BRUMBY BRD_BRUMBY4
/*
* Define a configuration structure to hold the board configuration .
* Need to set this up in the code ( for now ) with the boards that are
* to be configured into the system . This is what needs to be modified
* when adding / removing / modifying boards . Each line entry in the
* stli_brdconf [ ] array is a board . Each line contains io / irq / memory
* ranges for that board ( as well as what type of board it is ) .
* Some examples :
* { BRD_ECP , 0x2a0 , 0 , 0xcc000 , 0 , 0 } ,
* This line will configure an EasyConnection 8 / 64 at io address 2 a0 ,
* and shared memory address of cc000 . Multiple EasyConnection 8 / 64
* boards can share the same shared memory address space . No interrupt
* is required for this board type .
* Another example :
* { BRD_ECPE , 0x5000 , 0 , 0x80000000 , 0 , 0 } ,
* This line will configure an EasyConnection 8 / 64 EISA in slot 5 and
* shared memory address of 0x80000000 ( 2 GByte ) . Multiple
* EasyConnection 8 / 64 EISA boards can share the same shared memory
* address space . No interrupt is required for this board type .
* Another example :
* { BRD_ONBOARD , 0x240 , 0 , 0xd0000 , 0 , 0 } ,
* This line will configure an ONboard ( ISA type ) at io address 240 ,
* and shared memory address of d0000 . Multiple ONboards can share
* the same shared memory address space . No interrupt required .
* Another example :
* { BRD_BRUMBY4 , 0x360 , 0 , 0xc8000 , 0 , 0 } ,
* This line will configure a Brumby board ( any number of ports ! ) at
* io address 360 and shared memory address of c8000 . All Brumby boards
* configured into a system must have their own separate io and memory
* addresses . No interrupt is required .
* Another example :
* { BRD_STALLION , 0x330 , 0 , 0xd0000 , 0 , 0 } ,
* This line will configure an original Stallion board at io address 330
* and shared memory address d0000 ( this would only be valid for a " V4.0 "
* or Rev . O Stallion board ) . All Stallion boards configured into the
* system must have their own separate io and memory addresses . No
* interrupt is required .
*/
typedef struct {
int brdtype ;
int ioaddr1 ;
int ioaddr2 ;
unsigned long memaddr ;
int irq ;
int irqtype ;
} stlconf_t ;
static stlconf_t stli_brdconf [ ] = {
/*{ BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },*/
} ;
static int stli_nrbrds = sizeof ( stli_brdconf ) / sizeof ( stlconf_t ) ;
/*
* There is some experimental EISA board detection code in this driver .
* By default it is disabled , but for those that want to try it out ,
* then set the define below to be 1.
*/
# define STLI_EISAPROBE 0
/*****************************************************************************/
/*
* Define some important driver characteristics . Device major numbers
* allocated as per Linux Device Registry .
*/
# ifndef STL_SIOMEMMAJOR
# define STL_SIOMEMMAJOR 28
# endif
# ifndef STL_SERIALMAJOR
# define STL_SERIALMAJOR 24
# endif
# ifndef STL_CALLOUTMAJOR
# define STL_CALLOUTMAJOR 25
# endif
/*****************************************************************************/
/*
* 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_drvtitle = " Stallion Intelligent Multiport Serial Driver " ;
static char * stli_drvname = " istallion " ;
static char * stli_drvversion = " 5.6.0 " ;
static char * stli_serialname = " ttyE " ;
static struct tty_driver * stli_serial ;
/*
* We will need to allocate a temporary write buffer for chars that
* come direct from user space . The problem is that a copy from user
* space might cause a page fault ( typically on a system that is
* swapping ! ) . All ports will share one buffer - since if the system
* is already swapping a shared buffer won ' t make things any worse .
*/
static char * stli_tmpwritebuf ;
static DECLARE_MUTEX ( stli_tmpwritesem ) ;
# define STLI_TXBUFSIZE 4096
/*
* Use a fast local buffer for cooked characters . Typically a whole
* bunch of cooked characters come in for a port , 1 at a time . So we
* save those up into a local buffer , then write out the whole lot
* with a large memcpy . Just use 1 buffer for all ports , since its
* use it is only need for short periods of time by each port .
*/
static char * stli_txcookbuf ;
static int stli_txcooksize ;
static int stli_txcookrealsize ;
static struct tty_struct * stli_txcooktty ;
/*
* Define a local default termios struct . All ports will be created
* with this termios initially . Basically all it defines is a raw port
* at 9600 baud , 8 data bits , no parity , 1 stop bit .
*/
static struct termios stli_deftermios = {
. c_cflag = ( B9600 | CS8 | CREAD | HUPCL | CLOCAL ) ,
. c_cc = INIT_C_CC ,
} ;
/*
* 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 ;
static stlibrd_t stli_dummybrd ;
static stliport_t stli_dummyport ;
/*****************************************************************************/
static stlibrd_t * stli_brds [ STL_MAXBRDS ] ;
static int stli_shared ;
/*
* 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 actually 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 . Most of them need to be updated atomically , so always
* use the bit setting operations ( unless protected by cli / sti ) .
*/
# define ST_INITIALIZING 1
# define ST_OPENING 2
# define ST_CLOSING 3
# define ST_CMDING 4
# define ST_TXBUSY 5
# define ST_RXING 6
# define ST_DOFLUSHRX 7
# define ST_DOFLUSHTX 8
# define ST_DOSIGS 9
# define ST_RXSTOP 10
# define ST_GETSIGS 11
/*
* 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 " ,
" EC8/64-PCI " ,
" EasyIO-PCI " ,
" EC/RA-PCI " ,
} ;
/*****************************************************************************/
# ifdef MODULE
/*
* Define some string labels for arguments passed from the module
* load line . These allow for easy board definitions , and easy
* modification of the io , memory and irq resoucres .
*/
static char * board0 [ 8 ] ;
static char * board1 [ 8 ] ;
static char * board2 [ 8 ] ;
static char * board3 [ 8 ] ;
static char * * stli_brdsp [ ] = {
( char * * ) & board0 ,
( char * * ) & board1 ,
( char * * ) & board2 ,
( char * * ) & board3
} ;
/*
* Define a set of common board names , and types . This is used to
* parse any module arguments .
*/
typedef struct stlibrdtype {
char * name ;
int type ;
} stlibrdtype_t ;
static stlibrdtype_t stli_brdstr [ ] = {
{ " stallion " , BRD_STALLION } ,
{ " 1 " , BRD_STALLION } ,
{ " brumby " , BRD_BRUMBY } ,
{ " brumby4 " , BRD_BRUMBY } ,
{ " brumby/4 " , BRD_BRUMBY } ,
{ " brumby-4 " , BRD_BRUMBY } ,
{ " brumby8 " , BRD_BRUMBY } ,
{ " brumby/8 " , BRD_BRUMBY } ,
{ " brumby-8 " , BRD_BRUMBY } ,
{ " brumby16 " , BRD_BRUMBY } ,
{ " brumby/16 " , BRD_BRUMBY } ,
{ " brumby-16 " , BRD_BRUMBY } ,
{ " 2 " , BRD_BRUMBY } ,
{ " onboard2 " , BRD_ONBOARD2 } ,
{ " onboard-2 " , BRD_ONBOARD2 } ,
{ " onboard/2 " , BRD_ONBOARD2 } ,
{ " onboard-mc " , BRD_ONBOARD2 } ,
{ " onboard/mc " , BRD_ONBOARD2 } ,
{ " onboard-mca " , BRD_ONBOARD2 } ,
{ " onboard/mca " , BRD_ONBOARD2 } ,
{ " 3 " , BRD_ONBOARD2 } ,
{ " onboard " , BRD_ONBOARD } ,
{ " onboardat " , BRD_ONBOARD } ,
{ " 4 " , BRD_ONBOARD } ,
{ " onboarde " , BRD_ONBOARDE } ,
{ " onboard-e " , BRD_ONBOARDE } ,
{ " onboard/e " , BRD_ONBOARDE } ,
{ " onboard-ei " , BRD_ONBOARDE } ,
{ " onboard/ei " , BRD_ONBOARDE } ,
{ " 7 " , BRD_ONBOARDE } ,
{ " ecp " , BRD_ECP } ,
{ " ecpat " , BRD_ECP } ,
{ " ec8/64 " , BRD_ECP } ,
{ " ec8/64-at " , BRD_ECP } ,
{ " ec8/64-isa " , BRD_ECP } ,
{ " 23 " , BRD_ECP } ,
{ " ecpe " , BRD_ECPE } ,
{ " ecpei " , BRD_ECPE } ,
{ " ec8/64-e " , BRD_ECPE } ,
{ " ec8/64-ei " , BRD_ECPE } ,
{ " 24 " , BRD_ECPE } ,
{ " ecpmc " , BRD_ECPMC } ,
{ " ec8/64-mc " , BRD_ECPMC } ,
{ " ec8/64-mca " , BRD_ECPMC } ,
{ " 25 " , BRD_ECPMC } ,
{ " ecppci " , BRD_ECPPCI } ,
{ " ec/ra " , BRD_ECPPCI } ,
{ " ec/ra-pc " , BRD_ECPPCI } ,
{ " ec/ra-pci " , BRD_ECPPCI } ,
{ " 29 " , BRD_ECPPCI } ,
} ;
/*
* Define the module agruments .
*/
MODULE_AUTHOR ( " Greg Ungerer " ) ;
MODULE_DESCRIPTION ( " Stallion Intelligent Multiport Serial Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_PARM ( board0 , " 1-3s " ) ;
MODULE_PARM_DESC ( board0 , " Board 0 config -> name[,ioaddr[,memaddr] " ) ;
MODULE_PARM ( board1 , " 1-3s " ) ;
MODULE_PARM_DESC ( board1 , " Board 1 config -> name[,ioaddr[,memaddr] " ) ;
MODULE_PARM ( board2 , " 1-3s " ) ;
MODULE_PARM_DESC ( board2 , " Board 2 config -> name[,ioaddr[,memaddr] " ) ;
MODULE_PARM ( board3 , " 1-3s " ) ;
MODULE_PARM_DESC ( board3 , " Board 3 config -> name[,ioaddr[,memaddr] " ) ;
# endif
/*
* Set up a default memory address table for EISA board probing .
* The default addresses are all bellow 1 Mbyte , which has to be the
* case anyway . They should be safe , since we only read values from
* them , and interrupts are disabled while we do it . If the higher
* memory support is compiled in then we also try probing around
* the 1 Gb , 2 Gb and 3 Gb areas as well . . .
*/
static unsigned long stli_eisamemprobeaddrs [ ] = {
0xc0000 , 0xd0000 , 0xe0000 , 0xf0000 ,
0x80000000 , 0x80010000 , 0x80020000 , 0x80030000 ,
0x40000000 , 0x40010000 , 0x40020000 , 0x40030000 ,
0xc0000000 , 0xc0010000 , 0xc0020000 , 0xc0030000 ,
0xff000000 , 0xff010000 , 0xff020000 , 0xff030000 ,
} ;
static int stli_eisamempsize = sizeof ( stli_eisamemprobeaddrs ) / sizeof ( unsigned long ) ;
/*
* Define the Stallion PCI vendor and device IDs .
*/
# ifdef CONFIG_PCI
# ifndef PCI_VENDOR_ID_STALLION
# define PCI_VENDOR_ID_STALLION 0x124d
# endif
# ifndef PCI_DEVICE_ID_ECRA
# define PCI_DEVICE_ID_ECRA 0x0004
# endif
static struct pci_device_id istallion_pci_tbl [ ] = {
{ PCI_VENDOR_ID_STALLION , PCI_DEVICE_ID_ECRA , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ 0 }
} ;
MODULE_DEVICE_TABLE ( pci , istallion_pci_tbl ) ;
# endif /* CONFIG_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 , MCA and PCI .
*/
# define ECP_IOSIZE 4
# define ECP_MEMSIZE (128 * 1024)
# define ECP_PCIMEMSIZE (256 * 1024)
# define ECP_ATPAGESIZE (4 * 1024)
# define ECP_MCPAGESIZE (4 * 1024)
# define ECP_EIPAGESIZE (64 * 1024)
# define ECP_PCIPAGESIZE (64 * 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
/*
* Important defines for the PCI class of ECP board .
* ( It has a lot in common with the other ECP boards . )
*/
# define ECP_PCIIREG 0
# define ECP_PCICONFR 1
# define ECP_PCISTOP 0x01
/*
* 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_MEMENABLO 0
# define ONB_MEMENABHI 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_PNLXPID 0x40
# 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 , and the default baud base for ports .
*/
# define STL_MAXBAUD 460800
# define STL_BAUDBASE 115200
# define STL_CLOSEDELAY (5 * HZ / 10)
/*****************************************************************************/
/*
* Define macros to extract a brd or port number from a minor number .
*/
# define MINOR2BRD(min) (((min) & 0xc0) >> 6)
# define MINOR2PORT(min) ((min) & 0x3f)
/*
* Define a baud rate table that converts termios baud rate selector
* into the actual baud rate value . All baud rate calculations are based
* on the actual baud rate required .
*/
static unsigned int stli_baudrates [ ] = {
0 , 50 , 75 , 110 , 134 , 150 , 200 , 300 , 600 , 1200 , 1800 , 2400 , 4800 ,
9600 , 19200 , 38400 , 57600 , 115200 , 230400 , 460800 , 921600
} ;
/*****************************************************************************/
/*
* Define some handy local macros . . .
*/
# undef MIN
# define MIN(a,b) (((a) <= (b)) ? (a) : (b))
# undef TOLOWER
# define TOLOWER(x) ((((x) >= 'A') && ((x) <= 'Z')) ? ((x) + 0x20) : (x))
/*****************************************************************************/
/*
* Prototype all functions in this driver !
*/
# ifdef MODULE
static void stli_argbrds ( void ) ;
static int stli_parsebrd ( stlconf_t * confp , char * * argp ) ;
static unsigned long stli_atol ( char * str ) ;
# endif
int stli_init ( void ) ;
static int stli_open ( struct tty_struct * tty , struct file * filp ) ;
static void stli_close ( struct tty_struct * tty , struct file * filp ) ;
static int stli_write ( struct tty_struct * tty , const unsigned char * buf , int count ) ;
static void stli_putchar ( struct tty_struct * tty , unsigned char ch ) ;
static void stli_flushchars ( struct tty_struct * tty ) ;
static int stli_writeroom ( struct tty_struct * tty ) ;
static int stli_charsinbuffer ( struct tty_struct * tty ) ;
static int stli_ioctl ( struct tty_struct * tty , struct file * file , unsigned int cmd , unsigned long arg ) ;
static void stli_settermios ( struct tty_struct * tty , struct termios * old ) ;
static void stli_throttle ( struct tty_struct * tty ) ;
static void stli_unthrottle ( struct tty_struct * tty ) ;
static void stli_stop ( struct tty_struct * tty ) ;
static void stli_start ( struct tty_struct * tty ) ;
static void stli_flushbuffer ( struct tty_struct * tty ) ;
static void stli_breakctl ( struct tty_struct * tty , int state ) ;
static void stli_waituntilsent ( struct tty_struct * tty , int timeout ) ;
static void stli_sendxchar ( struct tty_struct * tty , char ch ) ;
static void stli_hangup ( struct tty_struct * tty ) ;
static int stli_portinfo ( stlibrd_t * brdp , stliport_t * portp , int portnr , char * pos ) ;
static int stli_brdinit ( stlibrd_t * brdp ) ;
static int stli_startbrd ( stlibrd_t * brdp ) ;
static ssize_t stli_memread ( struct file * fp , char __user * buf , size_t count , loff_t * offp ) ;
static ssize_t stli_memwrite ( struct file * fp , const char __user * buf , size_t count , loff_t * offp ) ;
static int stli_memioctl ( struct inode * ip , struct file * fp , unsigned int cmd , unsigned long arg ) ;
static void stli_brdpoll ( stlibrd_t * brdp , volatile cdkhdr_t * hdrp ) ;
static void stli_poll ( unsigned long arg ) ;
static int stli_hostcmd ( stlibrd_t * brdp , stliport_t * portp ) ;
static int stli_initopen ( stlibrd_t * brdp , 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_waitcarrier ( stlibrd_t * brdp , stliport_t * portp , struct file * filp ) ;
static void stli_dohangup ( void * arg ) ;
static int stli_setport ( stliport_t * portp ) ;
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_dodelaycmd ( stliport_t * portp , volatile cdkctrl_t * cp ) ;
static void stli_mkasyport ( stliport_t * portp , asyport_t * pp , struct termios * tiosp ) ;
static void stli_mkasysigs ( asysigs_t * sp , int dtr , int rts ) ;
static long stli_mktiocm ( unsigned long sigvalue ) ;
static void stli_read ( stlibrd_t * brdp , stliport_t * portp ) ;
static int stli_getserial ( stliport_t * portp , struct serial_struct __user * sp ) ;
static int stli_setserial ( stliport_t * portp , struct serial_struct __user * sp ) ;
static int stli_getbrdstats ( combrd_t __user * bp ) ;
static int stli_getportstats ( stliport_t * portp , comstats_t __user * cp ) ;
static int stli_portcmdstats ( stliport_t * portp ) ;
static int stli_clrportstats ( stliport_t * portp , comstats_t __user * cp ) ;
static int stli_getportstruct ( stliport_t __user * arg ) ;
static int stli_getbrdstruct ( stlibrd_t __user * arg ) ;
static void * stli_memalloc ( int len ) ;
static stlibrd_t * stli_allocbrd ( void ) ;
static void stli_ecpinit ( stlibrd_t * brdp ) ;
static void stli_ecpenable ( stlibrd_t * brdp ) ;
static void stli_ecpdisable ( stlibrd_t * brdp ) ;
static char * stli_ecpgetmemptr ( stlibrd_t * brdp , unsigned long offset , int line ) ;
static void stli_ecpreset ( stlibrd_t * brdp ) ;
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 char * stli_ecpeigetmemptr ( stlibrd_t * brdp , unsigned long offset , int line ) ;
static void stli_ecpeireset ( stlibrd_t * brdp ) ;
static void stli_ecpmcenable ( stlibrd_t * brdp ) ;
static void stli_ecpmcdisable ( stlibrd_t * brdp ) ;
static char * stli_ecpmcgetmemptr ( stlibrd_t * brdp , unsigned long offset , int line ) ;
static void stli_ecpmcreset ( stlibrd_t * brdp ) ;
static void stli_ecppciinit ( stlibrd_t * brdp ) ;
static char * stli_ecppcigetmemptr ( stlibrd_t * brdp , unsigned long offset , int line ) ;
static void stli_ecppcireset ( stlibrd_t * brdp ) ;
static void stli_onbinit ( stlibrd_t * brdp ) ;
static void stli_onbenable ( stlibrd_t * brdp ) ;
static void stli_onbdisable ( stlibrd_t * brdp ) ;
static char * stli_onbgetmemptr ( stlibrd_t * brdp , unsigned long offset , int line ) ;
static void stli_onbreset ( stlibrd_t * brdp ) ;
static void stli_onbeinit ( stlibrd_t * brdp ) ;
static void stli_onbeenable ( stlibrd_t * brdp ) ;
static void stli_onbedisable ( stlibrd_t * brdp ) ;
static char * stli_onbegetmemptr ( stlibrd_t * brdp , unsigned long offset , int line ) ;
static void stli_onbereset ( stlibrd_t * brdp ) ;
static void stli_bbyinit ( stlibrd_t * brdp ) ;
static char * stli_bbygetmemptr ( stlibrd_t * brdp , unsigned long offset , int line ) ;
static void stli_bbyreset ( stlibrd_t * brdp ) ;
static void stli_stalinit ( stlibrd_t * brdp ) ;
static char * stli_stalgetmemptr ( stlibrd_t * brdp , unsigned long offset , int line ) ;
static void stli_stalreset ( stlibrd_t * brdp ) ;
static stliport_t * stli_getport ( int brdnr , int panelnr , int portnr ) ;
static int stli_initecp ( stlibrd_t * brdp ) ;
static int stli_initonb ( stlibrd_t * brdp ) ;
static int stli_eisamemprobe ( stlibrd_t * brdp ) ;
static int stli_initports ( stlibrd_t * brdp ) ;
# ifdef CONFIG_PCI
static int stli_initpcibrd ( int brdtype , struct pci_dev * devp ) ;
# endif
/*****************************************************************************/
/*
* Define the driver info for a user level shared memory device . This
* device will work sort of like the / dev / kmem device - except that it
* will give access to the shared memory on the Stallion intelligent
* board . This is also a very useful debugging tool .
*/
static struct file_operations stli_fsiomem = {
. owner = THIS_MODULE ,
. read = stli_memread ,
. write = stli_memwrite ,
. ioctl = stli_memioctl ,
} ;
/*****************************************************************************/
/*
* Define a timer_list entry for our poll routine . The slave board
* is polled every so often to see if anything needs doing . This is
* much cheaper on host cpu than using interrupts . It turns out to
* not increase character latency by much either . . .
*/
2005-09-10 00:10:40 +04:00
static DEFINE_TIMER ( stli_timerlist , stli_poll , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
static int stli_timeron ;
/*
* Define the calculation for the timeout routine .
*/
# define STLI_TIMEOUT (jiffies + 1)
/*****************************************************************************/
2005-03-23 20:53:09 +03:00
static struct class * istallion_class ;
2005-04-17 02:20:36 +04:00
# ifdef MODULE
/*
* Loadable module initialization stuff .
*/
static int __init istallion_module_init ( void )
{
unsigned long flags ;
# ifdef DEBUG
printk ( " init_module() \n " ) ;
# endif
save_flags ( flags ) ;
cli ( ) ;
stli_init ( ) ;
restore_flags ( flags ) ;
return ( 0 ) ;
}
/*****************************************************************************/
static void __exit istallion_module_exit ( void )
{
stlibrd_t * brdp ;
stliport_t * portp ;
unsigned long flags ;
int i , j ;
# ifdef DEBUG
printk ( " cleanup_module() \n " ) ;
# endif
printk ( KERN_INFO " Unloading %s: version %s \n " , stli_drvtitle ,
stli_drvversion ) ;
save_flags ( flags ) ;
cli ( ) ;
/*
* Free up all allocated resources used by the ports . This includes
* memory and interrupts .
*/
if ( stli_timeron ) {
stli_timeron = 0 ;
del_timer ( & stli_timerlist ) ;
}
i = tty_unregister_driver ( stli_serial ) ;
if ( i ) {
printk ( " STALLION: failed to un-register tty driver, "
" errno=%d \n " , - i ) ;
restore_flags ( flags ) ;
return ;
}
put_tty_driver ( stli_serial ) ;
for ( i = 0 ; i < 4 ; i + + ) {
devfs_remove ( " staliomem/%d " , i ) ;
2005-03-23 20:53:09 +03:00
class_device_destroy ( istallion_class , MKDEV ( STL_SIOMEMMAJOR , i ) ) ;
2005-04-17 02:20:36 +04:00
}
devfs_remove ( " staliomem " ) ;
2005-03-23 20:53:09 +03:00
class_destroy ( istallion_class ) ;
2005-04-17 02:20:36 +04:00
if ( ( i = unregister_chrdev ( STL_SIOMEMMAJOR , " staliomem " ) ) )
printk ( " STALLION: failed to un-register serial memory device, "
" errno=%d \n " , - i ) ;
if ( stli_tmpwritebuf ! = ( char * ) NULL )
kfree ( stli_tmpwritebuf ) ;
if ( stli_txcookbuf ! = ( char * ) NULL )
kfree ( stli_txcookbuf ) ;
for ( i = 0 ; ( i < stli_nrbrds ) ; i + + ) {
if ( ( brdp = stli_brds [ i ] ) = = ( stlibrd_t * ) NULL )
continue ;
for ( j = 0 ; ( j < STL_MAXPORTS ) ; j + + ) {
portp = brdp - > ports [ j ] ;
if ( portp ! = ( stliport_t * ) NULL ) {
if ( portp - > tty ! = ( struct tty_struct * ) NULL )
tty_hangup ( portp - > tty ) ;
kfree ( portp ) ;
}
}
iounmap ( brdp - > membase ) ;
if ( brdp - > iosize > 0 )
release_region ( brdp - > iobase , brdp - > iosize ) ;
kfree ( brdp ) ;
stli_brds [ i ] = ( stlibrd_t * ) NULL ;
}
restore_flags ( flags ) ;
}
module_init ( istallion_module_init ) ;
module_exit ( istallion_module_exit ) ;
/*****************************************************************************/
/*
* Check for any arguments passed in on the module load command line .
*/
static void stli_argbrds ( void )
{
stlconf_t conf ;
stlibrd_t * brdp ;
int nrargs , i ;
# ifdef DEBUG
printk ( " stli_argbrds() \n " ) ;
# endif
nrargs = sizeof ( stli_brdsp ) / sizeof ( char * * ) ;
for ( i = stli_nrbrds ; ( i < nrargs ) ; i + + ) {
memset ( & conf , 0 , sizeof ( conf ) ) ;
if ( stli_parsebrd ( & conf , stli_brdsp [ i ] ) = = 0 )
continue ;
if ( ( brdp = stli_allocbrd ( ) ) = = ( stlibrd_t * ) NULL )
continue ;
stli_nrbrds = i + 1 ;
brdp - > brdnr = i ;
brdp - > brdtype = conf . brdtype ;
brdp - > iobase = conf . ioaddr1 ;
brdp - > memaddr = conf . memaddr ;
stli_brdinit ( brdp ) ;
}
}
/*****************************************************************************/
/*
* Convert an ascii string number into an unsigned long .
*/
static unsigned long stli_atol ( char * str )
{
unsigned long val ;
int base , c ;
char * sp ;
val = 0 ;
sp = str ;
if ( ( * sp = = ' 0 ' ) & & ( * ( sp + 1 ) = = ' x ' ) ) {
base = 16 ;
sp + = 2 ;
} else if ( * sp = = ' 0 ' ) {
base = 8 ;
sp + + ;
} else {
base = 10 ;
}
for ( ; ( * sp ! = 0 ) ; sp + + ) {
c = ( * sp > ' 9 ' ) ? ( TOLOWER ( * sp ) - ' a ' + 10 ) : ( * sp - ' 0 ' ) ;
if ( ( c < 0 ) | | ( c > = base ) ) {
printk ( " STALLION: invalid argument %s \n " , str ) ;
val = 0 ;
break ;
}
val = ( val * base ) + c ;
}
return ( val ) ;
}
/*****************************************************************************/
/*
* Parse the supplied argument string , into the board conf struct .
*/
static int stli_parsebrd ( stlconf_t * confp , char * * argp )
{
char * sp ;
int nrbrdnames , i ;
# ifdef DEBUG
printk ( " stli_parsebrd(confp=%x,argp=%x) \n " , ( int ) confp , ( int ) argp ) ;
# endif
if ( ( argp [ 0 ] = = ( char * ) NULL ) | | ( * argp [ 0 ] = = 0 ) )
return ( 0 ) ;
for ( sp = argp [ 0 ] , i = 0 ; ( ( * sp ! = 0 ) & & ( i < 25 ) ) ; sp + + , i + + )
* sp = TOLOWER ( * sp ) ;
nrbrdnames = sizeof ( stli_brdstr ) / sizeof ( stlibrdtype_t ) ;
for ( i = 0 ; ( i < nrbrdnames ) ; i + + ) {
if ( strcmp ( stli_brdstr [ i ] . name , argp [ 0 ] ) = = 0 )
break ;
}
if ( i > = nrbrdnames ) {
printk ( " STALLION: unknown board name, %s? \n " , argp [ 0 ] ) ;
return ( 0 ) ;
}
confp - > brdtype = stli_brdstr [ i ] . type ;
if ( ( argp [ 1 ] ! = ( char * ) NULL ) & & ( * argp [ 1 ] ! = 0 ) )
confp - > ioaddr1 = stli_atol ( argp [ 1 ] ) ;
if ( ( argp [ 2 ] ! = ( char * ) NULL ) & & ( * argp [ 2 ] ! = 0 ) )
confp - > memaddr = stli_atol ( argp [ 2 ] ) ;
return ( 1 ) ;
}
# endif
/*****************************************************************************/
/*
* Local driver kernel malloc routine .
*/
static void * stli_memalloc ( int len )
{
return ( ( void * ) kmalloc ( len , GFP_KERNEL ) ) ;
}
/*****************************************************************************/
static int stli_open ( struct tty_struct * tty , struct file * filp )
{
stlibrd_t * brdp ;
stliport_t * portp ;
unsigned int minordev ;
int brdnr , portnr , rc ;
# ifdef DEBUG
printk ( " stli_open(tty=%x,filp=%x): device=%s \n " , ( int ) tty ,
( int ) filp , tty - > name ) ;
# endif
minordev = tty - > index ;
brdnr = MINOR2BRD ( minordev ) ;
if ( brdnr > = stli_nrbrds )
return ( - ENODEV ) ;
brdp = stli_brds [ brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( - ENODEV ) ;
if ( ( brdp - > state & BST_STARTED ) = = 0 )
return ( - ENODEV ) ;
portnr = MINOR2PORT ( minordev ) ;
if ( ( portnr < 0 ) | | ( portnr > brdp - > nrports ) )
return ( - ENODEV ) ;
portp = brdp - > ports [ portnr ] ;
if ( portp = = ( stliport_t * ) NULL )
return ( - ENODEV ) ;
if ( portp - > devnr < 1 )
return ( - ENODEV ) ;
/*
* Check if this port is in the middle of closing . If so then wait
* until it is closed then return error status based on flag settings .
* The sleep here does not need interrupt protection since the wakeup
* for it is done with the same context .
*/
if ( portp - > flags & ASYNC_CLOSING ) {
interruptible_sleep_on ( & portp - > close_wait ) ;
if ( portp - > flags & ASYNC_HUP_NOTIFY )
return ( - EAGAIN ) ;
return ( - ERESTARTSYS ) ;
}
/*
* On the first open of the device setup the port hardware , and
* initialize the per port data structure . Since initializing the port
* requires several commands to the board we will need to wait for any
* other open that is already initializing the port .
*/
portp - > tty = tty ;
tty - > driver_data = portp ;
portp - > refcount + + ;
wait_event_interruptible ( portp - > raw_wait ,
! test_bit ( ST_INITIALIZING , & portp - > state ) ) ;
if ( signal_pending ( current ) )
return ( - ERESTARTSYS ) ;
if ( ( portp - > flags & ASYNC_INITIALIZED ) = = 0 ) {
set_bit ( ST_INITIALIZING , & portp - > state ) ;
if ( ( rc = stli_initopen ( brdp , portp ) ) > = 0 ) {
portp - > flags | = ASYNC_INITIALIZED ;
clear_bit ( TTY_IO_ERROR , & tty - > flags ) ;
}
clear_bit ( ST_INITIALIZING , & portp - > state ) ;
wake_up_interruptible ( & portp - > raw_wait ) ;
if ( rc < 0 )
return ( rc ) ;
}
/*
* Check if this port is in the middle of closing . If so then wait
* until it is closed then return error status , based on flag settings .
* The sleep here does not need interrupt protection since the wakeup
* for it is done with the same context .
*/
if ( portp - > flags & ASYNC_CLOSING ) {
interruptible_sleep_on ( & portp - > close_wait ) ;
if ( portp - > flags & ASYNC_HUP_NOTIFY )
return ( - EAGAIN ) ;
return ( - ERESTARTSYS ) ;
}
/*
* Based on type of open being done check if it can overlap with any
* previous opens still in effect . If we are a normal serial device
* then also we might have to wait for carrier .
*/
if ( ! ( filp - > f_flags & O_NONBLOCK ) ) {
if ( ( rc = stli_waitcarrier ( brdp , portp , filp ) ) ! = 0 )
return ( rc ) ;
}
portp - > flags | = ASYNC_NORMAL_ACTIVE ;
return ( 0 ) ;
}
/*****************************************************************************/
static void stli_close ( struct tty_struct * tty , struct file * filp )
{
stlibrd_t * brdp ;
stliport_t * portp ;
unsigned long flags ;
# ifdef DEBUG
printk ( " stli_close(tty=%x,filp=%x) \n " , ( int ) tty , ( int ) filp ) ;
# endif
portp = tty - > driver_data ;
if ( portp = = ( stliport_t * ) NULL )
return ;
save_flags ( flags ) ;
cli ( ) ;
if ( tty_hung_up_p ( filp ) ) {
restore_flags ( flags ) ;
return ;
}
if ( ( tty - > count = = 1 ) & & ( portp - > refcount ! = 1 ) )
portp - > refcount = 1 ;
if ( portp - > refcount - - > 1 ) {
restore_flags ( flags ) ;
return ;
}
portp - > flags | = ASYNC_CLOSING ;
/*
* May want to wait for data to drain before closing . The BUSY flag
* keeps track of whether we are still transmitting or not . It is
* updated by messages from the slave - indicating when all chars
* really have drained .
*/
if ( tty = = stli_txcooktty )
stli_flushchars ( tty ) ;
tty - > closing = 1 ;
if ( portp - > closing_wait ! = ASYNC_CLOSING_WAIT_NONE )
tty_wait_until_sent ( tty , portp - > closing_wait ) ;
portp - > flags & = ~ ASYNC_INITIALIZED ;
brdp = stli_brds [ portp - > brdnr ] ;
stli_rawclose ( brdp , portp , 0 , 0 ) ;
if ( tty - > termios - > c_cflag & HUPCL ) {
stli_mkasysigs ( & portp - > asig , 0 , 0 ) ;
if ( test_bit ( ST_CMDING , & portp - > state ) )
set_bit ( ST_DOSIGS , & portp - > state ) ;
else
stli_sendcmd ( brdp , portp , A_SETSIGNALS , & portp - > asig ,
sizeof ( asysigs_t ) , 0 ) ;
}
clear_bit ( ST_TXBUSY , & portp - > state ) ;
clear_bit ( ST_RXSTOP , & portp - > state ) ;
set_bit ( TTY_IO_ERROR , & tty - > flags ) ;
if ( tty - > ldisc . flush_buffer )
( tty - > ldisc . flush_buffer ) ( tty ) ;
set_bit ( ST_DOFLUSHRX , & portp - > state ) ;
stli_flushbuffer ( tty ) ;
tty - > closing = 0 ;
portp - > tty = ( struct tty_struct * ) NULL ;
if ( portp - > openwaitcnt ) {
if ( portp - > close_delay )
msleep_interruptible ( jiffies_to_msecs ( portp - > close_delay ) ) ;
wake_up_interruptible ( & portp - > open_wait ) ;
}
portp - > flags & = ~ ( ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING ) ;
wake_up_interruptible ( & portp - > close_wait ) ;
restore_flags ( flags ) ;
}
/*****************************************************************************/
/*
* 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 ( stlibrd_t * brdp , stliport_t * portp )
{
struct tty_struct * tty ;
asynotify_t nt ;
asyport_t aport ;
int rc ;
# ifdef DEBUG
printk ( " stli_initopen(brdp=%x,portp=%x) \n " , ( int ) brdp , ( int ) portp ) ;
# endif
if ( ( rc = stli_rawopen ( brdp , portp , 0 , 1 ) ) < 0 )
return ( rc ) ;
memset ( & nt , 0 , 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 ) ;
tty = portp - > tty ;
if ( tty = = ( struct tty_struct * ) NULL )
return ( - ENODEV ) ;
stli_mkasyport ( portp , & aport , tty - > termios ) ;
if ( ( rc = stli_cmdwait ( brdp , portp , A_SETPORT , & aport ,
sizeof ( asyport_t ) , 0 ) ) < 0 )
return ( rc ) ;
set_bit ( ST_GETSIGS , & portp - > state ) ;
if ( ( rc = stli_cmdwait ( brdp , portp , A_GETSIGNALS , & portp - > asig ,
sizeof ( asysigs_t ) , 1 ) ) < 0 )
return ( rc ) ;
if ( test_and_clear_bit ( ST_GETSIGS , & portp - > state ) )
portp - > sigs = stli_mktiocm ( portp - > asig . sigvalue ) ;
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 ) ;
}
/*****************************************************************************/
/*
* 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 ;
unsigned long flags ;
int rc ;
# ifdef DEBUG
printk ( " stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d) \n " ,
( int ) brdp , ( int ) portp , ( int ) arg , wait ) ;
# endif
/*
* Send a message to the slave to open this port .
*/
save_flags ( flags ) ;
cli ( ) ;
/*
* 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 .
*/
wait_event_interruptible ( portp - > raw_wait ,
! test_bit ( ST_CLOSING , & portp - > state ) ) ;
if ( signal_pending ( current ) ) {
restore_flags ( flags ) ;
return - ERESTARTSYS ;
}
/*
* 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 ) {
restore_flags ( flags ) ;
return ( 0 ) ;
}
/*
* Slave is in action , so now we must wait for the open acknowledgment
* to come back .
*/
rc = 0 ;
set_bit ( ST_OPENING , & portp - > state ) ;
wait_event_interruptible ( portp - > raw_wait ,
! test_bit ( ST_OPENING , & portp - > state ) ) ;
if ( signal_pending ( current ) )
rc = - ERESTARTSYS ;
restore_flags ( flags ) ;
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 ;
unsigned long flags ;
int rc ;
# ifdef DEBUG
printk ( " stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d) \n " ,
( int ) brdp , ( int ) portp , ( int ) arg , wait ) ;
# endif
save_flags ( flags ) ;
cli ( ) ;
/*
* Slave is already closing this port . This can happen if a hangup
* occurs on this port .
*/
if ( wait ) {
wait_event_interruptible ( portp - > raw_wait ,
! test_bit ( ST_CLOSING , & portp - > state ) ) ;
if ( signal_pending ( current ) ) {
restore_flags ( flags ) ;
return - ERESTARTSYS ;
}
}
/*
* 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 ) ;
set_bit ( ST_CLOSING , & portp - > state ) ;
if ( wait = = 0 ) {
restore_flags ( flags ) ;
return ( 0 ) ;
}
/*
* Slave is in action , so now we must wait for the open acknowledgment
* to come back .
*/
rc = 0 ;
wait_event_interruptible ( portp - > raw_wait ,
! test_bit ( ST_CLOSING , & portp - > state ) ) ;
if ( signal_pending ( current ) )
rc = - ERESTARTSYS ;
restore_flags ( flags ) ;
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 )
{
unsigned long flags ;
# ifdef DEBUG
printk ( " 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
save_flags ( flags ) ;
cli ( ) ;
wait_event_interruptible ( portp - > raw_wait ,
! test_bit ( ST_CMDING , & portp - > state ) ) ;
if ( signal_pending ( current ) ) {
restore_flags ( flags ) ;
return - ERESTARTSYS ;
}
stli_sendcmd ( brdp , portp , cmd , arg , size , copyback ) ;
wait_event_interruptible ( portp - > raw_wait ,
! test_bit ( ST_CMDING , & portp - > state ) ) ;
if ( signal_pending ( current ) ) {
restore_flags ( flags ) ;
return - ERESTARTSYS ;
}
restore_flags ( flags ) ;
if ( portp - > rc ! = 0 )
return ( - EIO ) ;
return ( 0 ) ;
}
/*****************************************************************************/
/*
* Send the termios settings for this port to the slave . This sleeps
* waiting for the command to complete - so must have user context .
*/
static int stli_setport ( stliport_t * portp )
{
stlibrd_t * brdp ;
asyport_t aport ;
# ifdef DEBUG
printk ( " stli_setport(portp=%x) \n " , ( int ) portp ) ;
# endif
if ( portp = = ( stliport_t * ) NULL )
return ( - ENODEV ) ;
if ( portp - > tty = = ( struct tty_struct * ) NULL )
return ( - ENODEV ) ;
if ( ( portp - > brdnr < 0 ) & & ( portp - > brdnr > = stli_nrbrds ) )
return ( - ENODEV ) ;
brdp = stli_brds [ portp - > brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( - ENODEV ) ;
stli_mkasyport ( portp , & aport , portp - > tty - > termios ) ;
return ( stli_cmdwait ( brdp , portp , A_SETPORT , & aport , sizeof ( asyport_t ) , 0 ) ) ;
}
/*****************************************************************************/
/*
* Possibly need to wait for carrier ( DCD signal ) to come high . Say
* maybe because if we are clocal then we don ' t need to wait . . .
*/
static int stli_waitcarrier ( stlibrd_t * brdp , stliport_t * portp , struct file * filp )
{
unsigned long flags ;
int rc , doclocal ;
# ifdef DEBUG
printk ( " stli_waitcarrier(brdp=%x,portp=%x,filp=%x) \n " ,
( int ) brdp , ( int ) portp , ( int ) filp ) ;
# endif
rc = 0 ;
doclocal = 0 ;
if ( portp - > tty - > termios - > c_cflag & CLOCAL )
doclocal + + ;
save_flags ( flags ) ;
cli ( ) ;
portp - > openwaitcnt + + ;
if ( ! tty_hung_up_p ( filp ) )
portp - > refcount - - ;
for ( ; ; ) {
stli_mkasysigs ( & portp - > asig , 1 , 1 ) ;
if ( ( rc = stli_cmdwait ( brdp , portp , A_SETSIGNALS ,
& portp - > asig , sizeof ( asysigs_t ) , 0 ) ) < 0 )
break ;
if ( tty_hung_up_p ( filp ) | |
( ( portp - > flags & ASYNC_INITIALIZED ) = = 0 ) ) {
if ( portp - > flags & ASYNC_HUP_NOTIFY )
rc = - EBUSY ;
else
rc = - ERESTARTSYS ;
break ;
}
if ( ( ( portp - > flags & ASYNC_CLOSING ) = = 0 ) & &
( doclocal | | ( portp - > sigs & TIOCM_CD ) ) ) {
break ;
}
if ( signal_pending ( current ) ) {
rc = - ERESTARTSYS ;
break ;
}
interruptible_sleep_on ( & portp - > open_wait ) ;
}
if ( ! tty_hung_up_p ( filp ) )
portp - > refcount + + ;
portp - > openwaitcnt - - ;
restore_flags ( flags ) ;
return ( rc ) ;
}
/*****************************************************************************/
/*
* Write routine . Take the data and put it in the shared memory ring
* queue . If port is not already sending chars then need to mark the
* service bits for this port .
*/
static int stli_write ( struct tty_struct * tty , const unsigned char * buf , int count )
{
volatile cdkasy_t * ap ;
volatile cdkhdr_t * hdrp ;
volatile unsigned char * bits ;
unsigned char * shbuf , * chbuf ;
stliport_t * portp ;
stlibrd_t * brdp ;
unsigned int len , stlen , head , tail , size ;
unsigned long flags ;
# ifdef DEBUG
printk ( " stli_write(tty=%x,buf=%x,count=%d) \n " ,
( int ) tty , ( int ) buf , count ) ;
# endif
if ( ( tty = = ( struct tty_struct * ) NULL ) | |
( stli_tmpwritebuf = = ( char * ) NULL ) )
return ( 0 ) ;
if ( tty = = stli_txcooktty )
stli_flushchars ( tty ) ;
portp = tty - > driver_data ;
if ( portp = = ( stliport_t * ) NULL )
return ( 0 ) ;
if ( ( portp - > brdnr < 0 ) | | ( portp - > brdnr > = stli_nrbrds ) )
return ( 0 ) ;
brdp = stli_brds [ portp - > brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( 0 ) ;
chbuf = ( unsigned char * ) buf ;
/*
* All data is now local , shove as much as possible into shared memory .
*/
save_flags ( flags ) ;
cli ( ) ;
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 ;
}
len = MIN ( len , count ) ;
count = 0 ;
shbuf = ( char * ) EBRDGETMEMPTR ( brdp , portp - > txoffset ) ;
while ( len > 0 ) {
stlen = MIN ( len , stlen ) ;
memcpy ( ( shbuf + head ) , chbuf , stlen ) ;
chbuf + = stlen ;
len - = stlen ;
count + = stlen ;
head + = stlen ;
if ( head > = size ) {
head = 0 ;
stlen = tail ;
}
}
ap = ( volatile cdkasy_t * ) EBRDGETMEMPTR ( brdp , portp - > addr ) ;
ap - > txq . head = head ;
if ( test_bit ( ST_TXBUSY , & portp - > state ) ) {
if ( ap - > changed . data & DT_TXEMPTY )
ap - > changed . data & = ~ DT_TXEMPTY ;
}
hdrp = ( volatile cdkhdr_t * ) EBRDGETMEMPTR ( brdp , CDK_CDKADDR ) ;
bits = ( ( volatile unsigned char * ) hdrp ) + brdp - > slaveoffset +
portp - > portidx ;
* bits | = portp - > portbit ;
set_bit ( ST_TXBUSY , & portp - > state ) ;
EBRDDISABLE ( brdp ) ;
restore_flags ( flags ) ;
return ( count ) ;
}
/*****************************************************************************/
/*
* Output a single character . We put it into a temporary local buffer
* ( for speed ) then write out that buffer when the flushchars routine
* is called . There is a safety catch here so that if some other port
* writes chars before the current buffer has been , then we write them
* first them do the new ports .
*/
static void stli_putchar ( struct tty_struct * tty , unsigned char ch )
{
# ifdef DEBUG
printk ( " stli_putchar(tty=%x,ch=%x) \n " , ( int ) tty , ( int ) ch ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ;
if ( tty ! = stli_txcooktty ) {
if ( stli_txcooktty ! = ( struct tty_struct * ) NULL )
stli_flushchars ( stli_txcooktty ) ;
stli_txcooktty = tty ;
}
stli_txcookbuf [ stli_txcooksize + + ] = ch ;
}
/*****************************************************************************/
/*
* Transfer characters from the local TX cooking buffer to the board .
* We sort of ignore the tty that gets passed in here . We rely on the
* info stored with the TX cook buffer to tell us which port to flush
* the data on . In any case we clean out the TX cook buffer , for re - use
* by someone else .
*/
static void stli_flushchars ( struct tty_struct * tty )
{
volatile cdkhdr_t * hdrp ;
volatile unsigned char * bits ;
volatile cdkasy_t * ap ;
struct tty_struct * cooktty ;
stliport_t * portp ;
stlibrd_t * brdp ;
unsigned int len , stlen , head , tail , size , count , cooksize ;
unsigned char * buf , * shbuf ;
unsigned long flags ;
# ifdef DEBUG
printk ( " stli_flushchars(tty=%x) \n " , ( int ) tty ) ;
# endif
cooksize = stli_txcooksize ;
cooktty = stli_txcooktty ;
stli_txcooksize = 0 ;
stli_txcookrealsize = 0 ;
stli_txcooktty = ( struct tty_struct * ) NULL ;
if ( tty = = ( struct tty_struct * ) NULL )
return ;
if ( cooktty = = ( struct tty_struct * ) NULL )
return ;
if ( tty ! = cooktty )
tty = cooktty ;
if ( cooksize = = 0 )
return ;
portp = tty - > driver_data ;
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 ;
save_flags ( flags ) ;
cli ( ) ;
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 ;
}
len = MIN ( len , cooksize ) ;
count = 0 ;
shbuf = ( char * ) EBRDGETMEMPTR ( brdp , portp - > txoffset ) ;
buf = stli_txcookbuf ;
while ( len > 0 ) {
stlen = MIN ( len , stlen ) ;
memcpy ( ( shbuf + head ) , buf , stlen ) ;
buf + = stlen ;
len - = stlen ;
count + = stlen ;
head + = stlen ;
if ( head > = size ) {
head = 0 ;
stlen = tail ;
}
}
ap = ( volatile cdkasy_t * ) EBRDGETMEMPTR ( brdp , portp - > addr ) ;
ap - > txq . head = head ;
if ( test_bit ( ST_TXBUSY , & portp - > state ) ) {
if ( ap - > changed . data & DT_TXEMPTY )
ap - > changed . data & = ~ DT_TXEMPTY ;
}
hdrp = ( volatile cdkhdr_t * ) EBRDGETMEMPTR ( brdp , CDK_CDKADDR ) ;
bits = ( ( volatile unsigned char * ) hdrp ) + brdp - > slaveoffset +
portp - > portidx ;
* bits | = portp - > portbit ;
set_bit ( ST_TXBUSY , & portp - > state ) ;
EBRDDISABLE ( brdp ) ;
restore_flags ( flags ) ;
}
/*****************************************************************************/
static int stli_writeroom ( struct tty_struct * tty )
{
volatile cdkasyrq_t * rp ;
stliport_t * portp ;
stlibrd_t * brdp ;
unsigned int head , tail , len ;
unsigned long flags ;
# ifdef DEBUG
printk ( " stli_writeroom(tty=%x) \n " , ( int ) tty ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ( 0 ) ;
if ( tty = = stli_txcooktty ) {
if ( stli_txcookrealsize ! = 0 ) {
len = stli_txcookrealsize - stli_txcooksize ;
return ( len ) ;
}
}
portp = tty - > driver_data ;
if ( portp = = ( stliport_t * ) NULL )
return ( 0 ) ;
if ( ( portp - > brdnr < 0 ) | | ( portp - > brdnr > = stli_nrbrds ) )
return ( 0 ) ;
brdp = stli_brds [ portp - > brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( 0 ) ;
save_flags ( flags ) ;
cli ( ) ;
EBRDENABLE ( brdp ) ;
rp = & ( ( volatile cdkasy_t * ) EBRDGETMEMPTR ( brdp , portp - > addr ) ) - > txq ;
head = ( unsigned int ) rp - > head ;
tail = ( unsigned int ) rp - > tail ;
if ( tail ! = ( ( unsigned int ) rp - > tail ) )
tail = ( unsigned int ) rp - > tail ;
len = ( head > = tail ) ? ( portp - > txsize - ( head - tail ) ) : ( tail - head ) ;
len - - ;
EBRDDISABLE ( brdp ) ;
restore_flags ( flags ) ;
if ( tty = = stli_txcooktty ) {
stli_txcookrealsize = len ;
len - = stli_txcooksize ;
}
return ( len ) ;
}
/*****************************************************************************/
/*
* Return the number of characters in the transmit buffer . Normally we
* will return the number of chars in the shared memory ring queue .
* We need to kludge around the case where the shared memory buffer is
* empty but not all characters have drained yet , for this case just
* return that there is 1 character in the buffer !
*/
static int stli_charsinbuffer ( struct tty_struct * tty )
{
volatile cdkasyrq_t * rp ;
stliport_t * portp ;
stlibrd_t * brdp ;
unsigned int head , tail , len ;
unsigned long flags ;
# ifdef DEBUG
printk ( " stli_charsinbuffer(tty=%x) \n " , ( int ) tty ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ( 0 ) ;
if ( tty = = stli_txcooktty )
stli_flushchars ( tty ) ;
portp = tty - > driver_data ;
if ( portp = = ( stliport_t * ) NULL )
return ( 0 ) ;
if ( ( portp - > brdnr < 0 ) | | ( portp - > brdnr > = stli_nrbrds ) )
return ( 0 ) ;
brdp = stli_brds [ portp - > brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( 0 ) ;
save_flags ( flags ) ;
cli ( ) ;
EBRDENABLE ( brdp ) ;
rp = & ( ( volatile cdkasy_t * ) EBRDGETMEMPTR ( brdp , portp - > addr ) ) - > txq ;
head = ( unsigned int ) rp - > head ;
tail = ( unsigned int ) rp - > tail ;
if ( tail ! = ( ( unsigned int ) rp - > tail ) )
tail = ( unsigned int ) rp - > tail ;
len = ( head > = tail ) ? ( head - tail ) : ( portp - > txsize - ( tail - head ) ) ;
if ( ( len = = 0 ) & & test_bit ( ST_TXBUSY , & portp - > state ) )
len = 1 ;
EBRDDISABLE ( brdp ) ;
restore_flags ( flags ) ;
return ( len ) ;
}
/*****************************************************************************/
/*
* Generate the serial struct info .
*/
static int stli_getserial ( stliport_t * portp , struct serial_struct __user * sp )
{
struct serial_struct sio ;
stlibrd_t * brdp ;
# ifdef DEBUG
printk ( " stli_getserial(portp=%x,sp=%x) \n " , ( int ) portp , ( int ) sp ) ;
# endif
memset ( & sio , 0 , sizeof ( struct serial_struct ) ) ;
sio . type = PORT_UNKNOWN ;
sio . line = portp - > portnr ;
sio . irq = 0 ;
sio . flags = portp - > flags ;
sio . baud_base = portp - > baud_base ;
sio . close_delay = portp - > close_delay ;
sio . closing_wait = portp - > closing_wait ;
sio . custom_divisor = portp - > custom_divisor ;
sio . xmit_fifo_size = 0 ;
sio . hub6 = 0 ;
brdp = stli_brds [ portp - > brdnr ] ;
if ( brdp ! = ( stlibrd_t * ) NULL )
sio . port = brdp - > iobase ;
return copy_to_user ( sp , & sio , sizeof ( struct serial_struct ) ) ?
- EFAULT : 0 ;
}
/*****************************************************************************/
/*
* Set port according to the serial struct info .
* At this point we do not do any auto - configure stuff , so we will
* just quietly ignore any requests to change irq , etc .
*/
static int stli_setserial ( stliport_t * portp , struct serial_struct __user * sp )
{
struct serial_struct sio ;
int rc ;
# ifdef DEBUG
printk ( " stli_setserial(portp=%p,sp=%p) \n " , portp , sp ) ;
# endif
if ( copy_from_user ( & sio , sp , sizeof ( struct serial_struct ) ) )
return - EFAULT ;
if ( ! capable ( CAP_SYS_ADMIN ) ) {
if ( ( sio . baud_base ! = portp - > baud_base ) | |
( sio . close_delay ! = portp - > close_delay ) | |
( ( sio . flags & ~ ASYNC_USR_MASK ) ! =
( portp - > flags & ~ ASYNC_USR_MASK ) ) )
return ( - EPERM ) ;
}
portp - > flags = ( portp - > flags & ~ ASYNC_USR_MASK ) |
( sio . flags & ASYNC_USR_MASK ) ;
portp - > baud_base = sio . baud_base ;
portp - > close_delay = sio . close_delay ;
portp - > closing_wait = sio . closing_wait ;
portp - > custom_divisor = sio . custom_divisor ;
if ( ( rc = stli_setport ( portp ) ) < 0 )
return ( rc ) ;
return ( 0 ) ;
}
/*****************************************************************************/
static int stli_tiocmget ( struct tty_struct * tty , struct file * file )
{
stliport_t * portp = tty - > driver_data ;
stlibrd_t * brdp ;
int rc ;
if ( portp = = ( stliport_t * ) NULL )
return ( - ENODEV ) ;
if ( ( portp - > brdnr < 0 ) | | ( portp - > brdnr > = stli_nrbrds ) )
return ( 0 ) ;
brdp = stli_brds [ portp - > brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( 0 ) ;
if ( tty - > flags & ( 1 < < TTY_IO_ERROR ) )
return ( - EIO ) ;
if ( ( rc = stli_cmdwait ( brdp , portp , A_GETSIGNALS ,
& portp - > asig , sizeof ( asysigs_t ) , 1 ) ) < 0 )
return ( rc ) ;
return stli_mktiocm ( portp - > asig . sigvalue ) ;
}
static int stli_tiocmset ( struct tty_struct * tty , struct file * file ,
unsigned int set , unsigned int clear )
{
stliport_t * portp = tty - > driver_data ;
stlibrd_t * brdp ;
int rts = - 1 , dtr = - 1 ;
if ( portp = = ( stliport_t * ) NULL )
return ( - ENODEV ) ;
if ( ( portp - > brdnr < 0 ) | | ( portp - > brdnr > = stli_nrbrds ) )
return ( 0 ) ;
brdp = stli_brds [ portp - > brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( 0 ) ;
if ( tty - > flags & ( 1 < < TTY_IO_ERROR ) )
return ( - EIO ) ;
if ( set & TIOCM_RTS )
rts = 1 ;
if ( set & TIOCM_DTR )
dtr = 1 ;
if ( clear & TIOCM_RTS )
rts = 0 ;
if ( clear & TIOCM_DTR )
dtr = 0 ;
stli_mkasysigs ( & portp - > asig , dtr , rts ) ;
return stli_cmdwait ( brdp , portp , A_SETSIGNALS , & portp - > asig ,
sizeof ( asysigs_t ) , 0 ) ;
}
static int stli_ioctl ( struct tty_struct * tty , struct file * file , unsigned int cmd , unsigned long arg )
{
stliport_t * portp ;
stlibrd_t * brdp ;
unsigned int ival ;
int rc ;
void __user * argp = ( void __user * ) arg ;
# ifdef DEBUG
printk ( " stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x) \n " ,
( int ) tty , ( int ) file , cmd , ( int ) arg ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ( - ENODEV ) ;
portp = tty - > driver_data ;
if ( portp = = ( stliport_t * ) NULL )
return ( - ENODEV ) ;
if ( ( portp - > brdnr < 0 ) | | ( portp - > brdnr > = stli_nrbrds ) )
return ( 0 ) ;
brdp = stli_brds [ portp - > brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( 0 ) ;
if ( ( cmd ! = TIOCGSERIAL ) & & ( cmd ! = TIOCSSERIAL ) & &
( cmd ! = COM_GETPORTSTATS ) & & ( cmd ! = COM_CLRPORTSTATS ) ) {
if ( tty - > flags & ( 1 < < TTY_IO_ERROR ) )
return ( - EIO ) ;
}
rc = 0 ;
switch ( cmd ) {
case TIOCGSOFTCAR :
rc = put_user ( ( ( tty - > termios - > c_cflag & CLOCAL ) ? 1 : 0 ) ,
( unsigned __user * ) arg ) ;
break ;
case TIOCSSOFTCAR :
if ( ( rc = get_user ( ival , ( unsigned __user * ) arg ) ) = = 0 )
tty - > termios - > c_cflag =
( tty - > termios - > c_cflag & ~ CLOCAL ) |
( ival ? CLOCAL : 0 ) ;
break ;
case TIOCGSERIAL :
rc = stli_getserial ( portp , argp ) ;
break ;
case TIOCSSERIAL :
rc = stli_setserial ( portp , argp ) ;
break ;
case STL_GETPFLAG :
rc = put_user ( portp - > pflag , ( unsigned __user * ) argp ) ;
break ;
case STL_SETPFLAG :
if ( ( rc = get_user ( portp - > pflag , ( unsigned __user * ) argp ) ) = = 0 )
stli_setport ( portp ) ;
break ;
case COM_GETPORTSTATS :
rc = stli_getportstats ( portp , argp ) ;
break ;
case COM_CLRPORTSTATS :
rc = stli_clrportstats ( portp , argp ) ;
break ;
case TIOCSERCONFIG :
case TIOCSERGWILD :
case TIOCSERSWILD :
case TIOCSERGETLSR :
case TIOCSERGSTRUCT :
case TIOCSERGETMULTI :
case TIOCSERSETMULTI :
default :
rc = - ENOIOCTLCMD ;
break ;
}
return ( rc ) ;
}
/*****************************************************************************/
/*
* This routine assumes that we have user context and can sleep .
* Looks like it is true for the current ttys implementation . . ! !
*/
static void stli_settermios ( struct tty_struct * tty , struct termios * old )
{
stliport_t * portp ;
stlibrd_t * brdp ;
struct termios * tiosp ;
asyport_t aport ;
# ifdef DEBUG
printk ( " stli_settermios(tty=%x,old=%x) \n " , ( int ) tty , ( int ) old ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ;
portp = tty - > driver_data ;
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 ;
tiosp = tty - > termios ;
if ( ( tiosp - > c_cflag = = old - > c_cflag ) & &
( tiosp - > c_iflag = = old - > c_iflag ) )
return ;
stli_mkasyport ( portp , & aport , tiosp ) ;
stli_cmdwait ( brdp , portp , A_SETPORT , & aport , sizeof ( asyport_t ) , 0 ) ;
stli_mkasysigs ( & portp - > asig , ( ( tiosp - > c_cflag & CBAUD ) ? 1 : 0 ) , - 1 ) ;
stli_cmdwait ( brdp , portp , A_SETSIGNALS , & portp - > asig ,
sizeof ( asysigs_t ) , 0 ) ;
if ( ( old - > c_cflag & CRTSCTS ) & & ( ( tiosp - > c_cflag & CRTSCTS ) = = 0 ) )
tty - > hw_stopped = 0 ;
if ( ( ( old - > c_cflag & CLOCAL ) = = 0 ) & & ( tiosp - > c_cflag & CLOCAL ) )
wake_up_interruptible ( & portp - > open_wait ) ;
}
/*****************************************************************************/
/*
* Attempt to flow control who ever is sending us data . We won ' t really
* do any flow control action here . We can ' t directly , and even if we
* wanted to we would have to send a command to the slave . The slave
* knows how to flow control , and will do so when its buffers reach its
* internal high water marks . So what we will do is set a local state
* bit that will stop us sending any RX data up from the poll routine
* ( which is the place where RX data from the slave is handled ) .
*/
static void stli_throttle ( struct tty_struct * tty )
{
stliport_t * portp ;
# ifdef DEBUG
printk ( " stli_throttle(tty=%x) \n " , ( int ) tty ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ;
portp = tty - > driver_data ;
if ( portp = = ( stliport_t * ) NULL )
return ;
set_bit ( ST_RXSTOP , & portp - > state ) ;
}
/*****************************************************************************/
/*
* Unflow control the device sending us data . . . That means that all
* we have to do is clear the RXSTOP state bit . The next poll call
* will then be able to pass the RX data back up .
*/
static void stli_unthrottle ( struct tty_struct * tty )
{
stliport_t * portp ;
# ifdef DEBUG
printk ( " stli_unthrottle(tty=%x) \n " , ( int ) tty ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ;
portp = tty - > driver_data ;
if ( portp = = ( stliport_t * ) NULL )
return ;
clear_bit ( ST_RXSTOP , & portp - > state ) ;
}
/*****************************************************************************/
/*
* Stop the transmitter . Basically to do this we will just turn TX
* interrupts off .
*/
static void stli_stop ( struct tty_struct * tty )
{
stlibrd_t * brdp ;
stliport_t * portp ;
asyctrl_t actrl ;
# ifdef DEBUG
printk ( " stli_stop(tty=%x) \n " , ( int ) tty ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ;
portp = tty - > driver_data ;
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 ;
memset ( & actrl , 0 , sizeof ( asyctrl_t ) ) ;
actrl . txctrl = CT_STOPFLOW ;
#if 0
stli_cmdwait ( brdp , portp , A_PORTCTRL , & actrl , sizeof ( asyctrl_t ) , 0 ) ;
# endif
}
/*****************************************************************************/
/*
* Start the transmitter again . Just turn TX interrupts back on .
*/
static void stli_start ( struct tty_struct * tty )
{
stliport_t * portp ;
stlibrd_t * brdp ;
asyctrl_t actrl ;
# ifdef DEBUG
printk ( " stli_start(tty=%x) \n " , ( int ) tty ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ;
portp = tty - > driver_data ;
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 ;
memset ( & actrl , 0 , sizeof ( asyctrl_t ) ) ;
actrl . txctrl = CT_STARTFLOW ;
#if 0
stli_cmdwait ( brdp , portp , A_PORTCTRL , & actrl , sizeof ( asyctrl_t ) , 0 ) ;
# endif
}
/*****************************************************************************/
/*
* Scheduler called hang up routine . This is called from the scheduler ,
* not direct from the driver " poll " routine . We can ' t call it there
* since the real local hangup code will enable / disable the board and
* other things that we can ' t do while handling the poll . Much easier
* to deal with it some time later ( don ' t really care when , hangups
* aren ' t that time critical ) .
*/
static void stli_dohangup ( void * arg )
{
stliport_t * portp ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_dohangup(portp=%x) \n " , ( int ) arg ) ;
# endif
/*
* FIXME : There ' s a module removal race here : tty_hangup
* calls schedule_work which will call into this
* driver later .
*/
portp = ( stliport_t * ) arg ;
if ( portp ! = ( stliport_t * ) NULL ) {
if ( portp - > tty ! = ( struct tty_struct * ) NULL ) {
tty_hangup ( portp - > tty ) ;
}
}
}
/*****************************************************************************/
/*
* Hangup this port . This is pretty much like closing the port , only
* a little more brutal . No waiting for data to drain . Shutdown the
* port and maybe drop signals . This is rather tricky really . We want
* to close the port as well .
*/
static void stli_hangup ( struct tty_struct * tty )
{
stliport_t * portp ;
stlibrd_t * brdp ;
unsigned long flags ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_hangup(tty=%x) \n " , ( int ) tty ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ;
portp = tty - > driver_data ;
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 ;
portp - > flags & = ~ ASYNC_INITIALIZED ;
save_flags ( flags ) ;
cli ( ) ;
if ( ! test_bit ( ST_CLOSING , & portp - > state ) )
stli_rawclose ( brdp , portp , 0 , 0 ) ;
if ( tty - > termios - > c_cflag & HUPCL ) {
stli_mkasysigs ( & portp - > asig , 0 , 0 ) ;
if ( test_bit ( ST_CMDING , & portp - > state ) ) {
set_bit ( ST_DOSIGS , & portp - > state ) ;
set_bit ( ST_DOFLUSHTX , & portp - > state ) ;
set_bit ( ST_DOFLUSHRX , & portp - > state ) ;
} else {
stli_sendcmd ( brdp , portp , A_SETSIGNALSF ,
& portp - > asig , sizeof ( asysigs_t ) , 0 ) ;
}
}
restore_flags ( flags ) ;
clear_bit ( ST_TXBUSY , & portp - > state ) ;
clear_bit ( ST_RXSTOP , & portp - > state ) ;
set_bit ( TTY_IO_ERROR , & tty - > flags ) ;
portp - > tty = ( struct tty_struct * ) NULL ;
portp - > flags & = ~ ASYNC_NORMAL_ACTIVE ;
portp - > refcount = 0 ;
wake_up_interruptible ( & portp - > open_wait ) ;
}
/*****************************************************************************/
/*
* 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_flushbuffer ( struct tty_struct * tty )
{
stliport_t * portp ;
stlibrd_t * brdp ;
unsigned long ftype , flags ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_flushbuffer(tty=%x) \n " , ( int ) tty ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ;
portp = tty - > driver_data ;
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 ;
save_flags ( flags ) ;
cli ( ) ;
if ( tty = = stli_txcooktty ) {
stli_txcooktty = ( struct tty_struct * ) NULL ;
stli_txcooksize = 0 ;
stli_txcookrealsize = 0 ;
}
if ( test_bit ( ST_CMDING , & portp - > state ) ) {
set_bit ( ST_DOFLUSHTX , & portp - > state ) ;
} else {
ftype = FLUSHTX ;
if ( test_bit ( ST_DOFLUSHRX , & portp - > state ) ) {
ftype | = FLUSHRX ;
clear_bit ( ST_DOFLUSHRX , & portp - > state ) ;
}
stli_sendcmd ( brdp , portp , A_FLUSH , & ftype ,
sizeof ( unsigned long ) , 0 ) ;
}
restore_flags ( flags ) ;
wake_up_interruptible ( & tty - > write_wait ) ;
if ( ( tty - > flags & ( 1 < < TTY_DO_WRITE_WAKEUP ) ) & &
tty - > ldisc . write_wakeup )
( tty - > ldisc . write_wakeup ) ( tty ) ;
}
/*****************************************************************************/
static void stli_breakctl ( struct tty_struct * tty , int state )
{
stlibrd_t * brdp ;
stliport_t * portp ;
long arg ;
/* long savestate, savetime; */
# ifdef DEBUG
printk ( KERN_DEBUG " stli_breakctl(tty=%x,state=%d) \n " , ( int ) tty , state ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ;
portp = tty - > driver_data ;
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 ;
/*
* Due to a bug in the tty send_break ( ) code we need to preserve
* the current process state and timeout . . .
savetime = current - > timeout ;
savestate = current - > state ;
*/
arg = ( state = = - 1 ) ? BREAKON : BREAKOFF ;
stli_cmdwait ( brdp , portp , A_BREAK , & arg , sizeof ( long ) , 0 ) ;
/*
*
current - > timeout = savetime ;
current - > state = savestate ;
*/
}
/*****************************************************************************/
static void stli_waituntilsent ( struct tty_struct * tty , int timeout )
{
stliport_t * portp ;
unsigned long tend ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_waituntilsent(tty=%x,timeout=%x) \n " , ( int ) tty , timeout ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ;
portp = tty - > driver_data ;
if ( portp = = ( stliport_t * ) NULL )
return ;
if ( timeout = = 0 )
timeout = HZ ;
tend = jiffies + timeout ;
while ( test_bit ( ST_TXBUSY , & portp - > state ) ) {
if ( signal_pending ( current ) )
break ;
msleep_interruptible ( 20 ) ;
if ( time_after_eq ( jiffies , tend ) )
break ;
}
}
/*****************************************************************************/
static void stli_sendxchar ( struct tty_struct * tty , char ch )
{
stlibrd_t * brdp ;
stliport_t * portp ;
asyctrl_t actrl ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_sendxchar(tty=%x,ch=%x) \n " , ( int ) tty , ch ) ;
# endif
if ( tty = = ( struct tty_struct * ) NULL )
return ;
portp = tty - > driver_data ;
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 ;
memset ( & actrl , 0 , sizeof ( asyctrl_t ) ) ;
if ( ch = = STOP_CHAR ( tty ) ) {
actrl . rxctrl = CT_STOPFLOW ;
} else if ( ch = = START_CHAR ( tty ) ) {
actrl . rxctrl = CT_STARTFLOW ;
} else {
actrl . txctrl = CT_SENDCHR ;
actrl . tximdch = ch ;
}
stli_cmdwait ( brdp , portp , A_PORTCTRL , & actrl , sizeof ( asyctrl_t ) , 0 ) ;
}
/*****************************************************************************/
# define MAXLINE 80
/*
* Format info for a specified port . The line is deliberately limited
* to 80 characters . ( If it is too long it will be truncated , if too
* short then padded with spaces ) .
*/
static int stli_portinfo ( stlibrd_t * brdp , stliport_t * portp , int portnr , char * pos )
{
char * sp , * uart ;
int rc , cnt ;
rc = stli_portcmdstats ( portp ) ;
uart = " UNKNOWN " ;
if ( brdp - > state & BST_STARTED ) {
switch ( stli_comstats . hwid ) {
case 0 : uart = " 2681 " ; break ;
case 1 : uart = " SC26198 " ; break ;
default : uart = " CD1400 " ; break ;
}
}
sp = pos ;
sp + = sprintf ( sp , " %d: uart:%s " , portnr , uart ) ;
if ( ( brdp - > state & BST_STARTED ) & & ( rc > = 0 ) ) {
sp + = sprintf ( sp , " tx:%d rx:%d " , ( int ) stli_comstats . txtotal ,
( int ) stli_comstats . rxtotal ) ;
if ( stli_comstats . rxframing )
sp + = sprintf ( sp , " fe:%d " ,
( int ) stli_comstats . rxframing ) ;
if ( stli_comstats . rxparity )
sp + = sprintf ( sp , " pe:%d " ,
( int ) stli_comstats . rxparity ) ;
if ( stli_comstats . rxbreaks )
sp + = sprintf ( sp , " brk:%d " ,
( int ) stli_comstats . rxbreaks ) ;
if ( stli_comstats . rxoverrun )
sp + = sprintf ( sp , " oe:%d " ,
( int ) stli_comstats . rxoverrun ) ;
cnt = sprintf ( sp , " %s%s%s%s%s " ,
( stli_comstats . signals & TIOCM_RTS ) ? " |RTS " : " " ,
( stli_comstats . signals & TIOCM_CTS ) ? " |CTS " : " " ,
( stli_comstats . signals & TIOCM_DTR ) ? " |DTR " : " " ,
( stli_comstats . signals & TIOCM_CD ) ? " |DCD " : " " ,
( stli_comstats . signals & TIOCM_DSR ) ? " |DSR " : " " ) ;
* sp = ' ' ;
sp + = cnt ;
}
for ( cnt = ( sp - pos ) ; ( cnt < ( MAXLINE - 1 ) ) ; cnt + + )
* sp + + = ' ' ;
if ( cnt > = MAXLINE )
pos [ ( MAXLINE - 2 ) ] = ' + ' ;
pos [ ( MAXLINE - 1 ) ] = ' \n ' ;
return ( MAXLINE ) ;
}
/*****************************************************************************/
/*
* Port info , read from the / proc file system .
*/
static int stli_readproc ( char * page , char * * start , off_t off , int count , int * eof , void * data )
{
stlibrd_t * brdp ;
stliport_t * portp ;
int brdnr , portnr , totalport ;
int curoff , maxoff ;
char * pos ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_readproc(page=%x,start=%x,off=%x,count=%d,eof=%x, "
" data=%x \n " , ( int ) page , ( int ) start , ( int ) off , count ,
( int ) eof , ( int ) data ) ;
# endif
pos = page ;
totalport = 0 ;
curoff = 0 ;
if ( off = = 0 ) {
pos + = sprintf ( pos , " %s: version %s " , stli_drvtitle ,
stli_drvversion ) ;
while ( pos < ( page + MAXLINE - 1 ) )
* pos + + = ' ' ;
* pos + + = ' \n ' ;
}
curoff = MAXLINE ;
/*
* We scan through for each board , panel and port . The offset is
* calculated on the fly , and irrelevant ports are skipped .
*/
for ( brdnr = 0 ; ( brdnr < stli_nrbrds ) ; brdnr + + ) {
brdp = stli_brds [ brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
continue ;
if ( brdp - > state = = 0 )
continue ;
maxoff = curoff + ( brdp - > nrports * MAXLINE ) ;
if ( off > = maxoff ) {
curoff = maxoff ;
continue ;
}
totalport = brdnr * STL_MAXPORTS ;
for ( portnr = 0 ; ( portnr < brdp - > nrports ) ; portnr + + ,
totalport + + ) {
portp = brdp - > ports [ portnr ] ;
if ( portp = = ( stliport_t * ) NULL )
continue ;
if ( off > = ( curoff + = MAXLINE ) )
continue ;
if ( ( pos - page + MAXLINE ) > count )
goto stli_readdone ;
pos + = stli_portinfo ( brdp , portp , totalport , pos ) ;
}
}
* eof = 1 ;
stli_readdone :
* start = page ;
return ( pos - page ) ;
}
/*****************************************************************************/
/*
* Generic send command routine . This will send a message to the slave ,
* of the specified type with the specified argument . Must be very
* careful 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 context . 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 .
*/
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 ;
unsigned long flags ;
# ifdef DEBUG
printk ( KERN_DEBUG " 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
save_flags ( flags ) ;
cli ( ) ;
if ( test_bit ( ST_CMDING , & portp - > state ) ) {
printk ( KERN_ERR " STALLION: command already busy, cmd=%x! \n " ,
( int ) cmd ) ;
restore_flags ( flags ) ;
return ;
}
EBRDENABLE ( brdp ) ;
cp = & ( ( volatile cdkasy_t * ) EBRDGETMEMPTR ( brdp , portp - > addr ) ) - > ctrl ;
if ( size > 0 ) {
memcpy ( ( void * ) & ( cp - > args [ 0 ] ) , arg , 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 ;
set_bit ( ST_CMDING , & portp - > state ) ;
EBRDDISABLE ( brdp ) ;
restore_flags ( flags ) ;
}
/*****************************************************************************/
/*
* 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 careful to
* handle the case where we fill up the tty buffer , but still have
* more chars to unload .
*/
static void stli_read ( stlibrd_t * brdp , stliport_t * portp )
{
volatile cdkasyrq_t * rp ;
volatile char * shbuf ;
struct tty_struct * tty ;
unsigned int head , tail , size ;
unsigned int len , stlen ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_read(brdp=%x,portp=%d) \n " ,
( int ) brdp , ( int ) portp ) ;
# endif
if ( test_bit ( ST_RXSTOP , & portp - > state ) )
return ;
tty = portp - > tty ;
if ( tty = = ( struct tty_struct * ) NULL )
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 ;
}
len = MIN ( len , ( TTY_FLIPBUF_SIZE - tty - > flip . count ) ) ;
shbuf = ( volatile char * ) EBRDGETMEMPTR ( brdp , portp - > rxoffset ) ;
while ( len > 0 ) {
stlen = MIN ( len , stlen ) ;
memcpy ( tty - > flip . char_buf_ptr , ( char * ) ( shbuf + tail ) , stlen ) ;
memset ( tty - > flip . flag_buf_ptr , 0 , stlen ) ;
tty - > flip . char_buf_ptr + = stlen ;
tty - > flip . flag_buf_ptr + = stlen ;
tty - > flip . count + = stlen ;
len - = stlen ;
tail + = stlen ;
if ( tail > = size ) {
tail = 0 ;
stlen = head ;
}
}
rp = & ( ( volatile cdkasy_t * ) EBRDGETMEMPTR ( brdp , portp - > addr ) ) - > rxq ;
rp - > tail = tail ;
if ( head ! = tail )
set_bit ( ST_RXING , & portp - > state ) ;
tty_schedule_flip ( tty ) ;
}
/*****************************************************************************/
/*
* 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 here .
*/
static void stli_dodelaycmd ( stliport_t * portp , volatile cdkctrl_t * cp )
{
int cmd ;
if ( test_bit ( ST_DOSIGS , & portp - > state ) ) {
if ( test_bit ( ST_DOFLUSHTX , & portp - > state ) & &
test_bit ( ST_DOFLUSHRX , & portp - > state ) )
cmd = A_SETSIGNALSF ;
else if ( test_bit ( ST_DOFLUSHTX , & portp - > state ) )
cmd = A_SETSIGNALSFTX ;
else if ( test_bit ( ST_DOFLUSHRX , & portp - > state ) )
cmd = A_SETSIGNALSFRX ;
else
cmd = A_SETSIGNALS ;
clear_bit ( ST_DOFLUSHTX , & portp - > state ) ;
clear_bit ( ST_DOFLUSHRX , & portp - > state ) ;
clear_bit ( ST_DOSIGS , & portp - > state ) ;
memcpy ( ( void * ) & ( cp - > args [ 0 ] ) , ( void * ) & portp - > asig ,
sizeof ( asysigs_t ) ) ;
cp - > status = 0 ;
cp - > cmd = cmd ;
set_bit ( ST_CMDING , & portp - > state ) ;
} else if ( test_bit ( ST_DOFLUSHTX , & portp - > state ) | |
test_bit ( ST_DOFLUSHRX , & portp - > state ) ) {
cmd = ( ( test_bit ( ST_DOFLUSHTX , & portp - > state ) ) ? FLUSHTX : 0 ) ;
cmd | = ( ( test_bit ( ST_DOFLUSHRX , & portp - > state ) ) ? FLUSHRX : 0 ) ;
clear_bit ( ST_DOFLUSHTX , & portp - > state ) ;
clear_bit ( ST_DOFLUSHRX , & portp - > state ) ;
memcpy ( ( void * ) & ( cp - > args [ 0 ] ) , ( void * ) & cmd , sizeof ( int ) ) ;
cp - > status = 0 ;
cp - > cmd = A_FLUSH ;
set_bit ( ST_CMDING , & portp - > state ) ;
}
}
/*****************************************************************************/
/*
* 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 no longer busy .
*/
static int stli_hostcmd ( stlibrd_t * brdp , stliport_t * portp )
{
volatile cdkasy_t * ap ;
volatile cdkctrl_t * cp ;
struct tty_struct * tty ;
asynotify_t nt ;
unsigned long oldsigs ;
int rc , donerx ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_hostcmd(brdp=%x,channr=%d) \n " ,
( int ) brdp , channr ) ;
# endif
ap = ( volatile cdkasy_t * ) EBRDGETMEMPTR ( brdp , portp - > addr ) ;
cp = & ap - > ctrl ;
/*
* Check if we are waiting for an open completion message .
*/
if ( test_bit ( ST_OPENING , & portp - > state ) ) {
rc = ( int ) cp - > openarg ;
if ( ( cp - > open = = 0 ) & & ( rc ! = 0 ) ) {
if ( rc > 0 )
rc - - ;
cp - > openarg = 0 ;
portp - > rc = rc ;
clear_bit ( ST_OPENING , & portp - > state ) ;
wake_up_interruptible ( & portp - > raw_wait ) ;
}
}
/*
* Check if we are waiting for a close completion message .
*/
if ( test_bit ( ST_CLOSING , & portp - > state ) ) {
rc = ( int ) cp - > closearg ;
if ( ( cp - > close = = 0 ) & & ( rc ! = 0 ) ) {
if ( rc > 0 )
rc - - ;
cp - > closearg = 0 ;
portp - > rc = rc ;
clear_bit ( ST_CLOSING , & portp - > state ) ;
wake_up_interruptible ( & portp - > raw_wait ) ;
}
}
/*
* Check if we are waiting for a command completion message . We may
* need to copy out the command results associated with this command .
*/
if ( test_bit ( ST_CMDING , & portp - > state ) ) {
rc = cp - > status ;
if ( ( cp - > cmd = = 0 ) & & ( rc ! = 0 ) ) {
if ( rc > 0 )
rc - - ;
if ( portp - > argp ! = ( void * ) NULL ) {
memcpy ( portp - > argp , ( void * ) & ( cp - > args [ 0 ] ) ,
portp - > argsize ) ;
portp - > argp = ( void * ) NULL ;
}
cp - > status = 0 ;
portp - > rc = rc ;
clear_bit ( ST_CMDING , & portp - > state ) ;
stli_dodelaycmd ( portp , cp ) ;
wake_up_interruptible ( & portp - > raw_wait ) ;
}
}
/*
* 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 .
*/
donerx = 0 ;
if ( ap - > notify ) {
nt = ap - > changed ;
ap - > notify = 0 ;
tty = portp - > tty ;
if ( nt . signal & SG_DCD ) {
oldsigs = portp - > sigs ;
portp - > sigs = stli_mktiocm ( nt . sigvalue ) ;
clear_bit ( ST_GETSIGS , & portp - > state ) ;
if ( ( portp - > sigs & TIOCM_CD ) & &
( ( oldsigs & TIOCM_CD ) = = 0 ) )
wake_up_interruptible ( & portp - > open_wait ) ;
if ( ( oldsigs & TIOCM_CD ) & &
( ( portp - > sigs & TIOCM_CD ) = = 0 ) ) {
if ( portp - > flags & ASYNC_CHECK_CD ) {
if ( tty )
schedule_work ( & portp - > tqhangup ) ;
}
}
}
if ( nt . data & DT_TXEMPTY )
clear_bit ( ST_TXBUSY , & portp - > state ) ;
if ( nt . data & ( DT_TXEMPTY | DT_TXLOW ) ) {
if ( tty ! = ( struct tty_struct * ) NULL ) {
if ( ( tty - > flags & ( 1 < < TTY_DO_WRITE_WAKEUP ) ) & &
tty - > ldisc . write_wakeup ) {
( tty - > ldisc . write_wakeup ) ( tty ) ;
EBRDENABLE ( brdp ) ;
}
wake_up_interruptible ( & tty - > write_wait ) ;
}
}
if ( ( nt . data & DT_RXBREAK ) & & ( portp - > rxmarkmsk & BRKINT ) ) {
if ( tty ! = ( struct tty_struct * ) NULL ) {
if ( tty - > flip . count < TTY_FLIPBUF_SIZE ) {
tty - > flip . count + + ;
* tty - > flip . flag_buf_ptr + + = TTY_BREAK ;
* tty - > flip . char_buf_ptr + + = 0 ;
if ( portp - > flags & ASYNC_SAK ) {
do_SAK ( tty ) ;
EBRDENABLE ( brdp ) ;
}
tty_schedule_flip ( tty ) ;
}
}
}
if ( nt . data & DT_RXBUSY ) {
donerx + + ;
stli_read ( brdp , portp ) ;
}
}
/*
* 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 ) & & test_bit ( ST_RXING , & portp - > state ) ) {
clear_bit ( ST_RXING , & portp - > state ) ;
stli_read ( brdp , portp ) ;
}
return ( ( test_bit ( ST_OPENING , & portp - > state ) | |
test_bit ( ST_CLOSING , & portp - > state ) | |
test_bit ( ST_CMDING , & portp - > state ) | |
test_bit ( ST_TXBUSY , & portp - > state ) | |
test_bit ( ST_RXING , & portp - > state ) ) ? 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 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 .
*/
memcpy ( & hostbits [ 0 ] , ( ( ( unsigned char * ) hdrp ) + brdp - > hostoffset ) ,
bitsize ) ;
memset ( & slavebits [ 0 ] , 0 , bitsize ) ;
slavebitchange = 0 ;
for ( bitpos = 0 ; ( bitpos < bitsize ) ; bitpos + + ) {
if ( hostbits [ bitpos ] = = 0 )
continue ;
channr = bitpos * 8 ;
for ( bitat = 0x1 ; ( 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 necessary . 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 ( unsigned long arg )
{
volatile cdkhdr_t * hdrp ;
stlibrd_t * brdp ;
int brdnr ;
stli_timerlist . expires = STLI_TIMEOUT ;
add_timer ( & stli_timerlist ) ;
/*
* 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 ) ;
}
}
/*****************************************************************************/
/*
* 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 )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_mkasyport(portp=%x,pp=%x,tiosp=%d) \n " ,
( int ) portp , ( int ) pp , ( int ) tiosp ) ;
# endif
memset ( pp , 0 , sizeof ( asyport_t ) ) ;
/*
* Start of by setting the baud , char size , parity and stop bit info .
*/
pp - > baudout = tiosp - > c_cflag & CBAUD ;
if ( pp - > baudout & CBAUDEX ) {
pp - > baudout & = ~ CBAUDEX ;
if ( ( pp - > baudout < 1 ) | | ( pp - > baudout > 4 ) )
tiosp - > c_cflag & = ~ CBAUDEX ;
else
pp - > baudout + = 15 ;
}
pp - > baudout = stli_baudrates [ pp - > baudout ] ;
if ( ( tiosp - > c_cflag & CBAUD ) = = B38400 ) {
if ( ( portp - > flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_HI )
pp - > baudout = 57600 ;
else if ( ( portp - > flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_VHI )
pp - > baudout = 115200 ;
else if ( ( portp - > flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_SHI )
pp - > baudout = 230400 ;
else if ( ( portp - > flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_WARP )
pp - > baudout = 460800 ;
else if ( ( portp - > flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_CUST )
pp - > baudout = ( portp - > baud_base / portp - > custom_divisor ) ;
}
if ( pp - > baudout > STL_MAXBAUD )
pp - > baudout = STL_MAXBAUD ;
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 ;
}
/*
* 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_cflag & CRTSCTS )
pp - > flow | = ( F_RTSFLOW | F_CTSFLOW ) ;
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 ;
portp - > rxmarkmsk = 0 ;
if ( tiosp - > c_iflag & ( INPCK | PARMRK ) )
pp - > iflag | = FI_1MARKRXERRS ;
if ( tiosp - > c_iflag & BRKINT )
portp - > rxmarkmsk | = BRKINT ;
/*
* Set up clocal processing as required .
*/
if ( tiosp - > c_cflag & CLOCAL )
portp - > flags & = ~ ASYNC_CHECK_CD ;
else
portp - > flags | = ASYNC_CHECK_CD ;
/*
* Transfer any persistent flags into the asyport structure .
*/
pp - > pflag = ( portp - > pflag & 0xffff ) ;
pp - > vmin = ( portp - > pflag & P_RXIMIN ) ? 1 : 0 ;
pp - > vtime = ( portp - > pflag & P_RXITIME ) ? 1 : 0 ;
pp - > cc [ 1 ] = ( portp - > pflag & P_RXTHOLD ) ? 1 : 0 ;
}
/*****************************************************************************/
/*
* 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 )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_mkasysigs(sp=%x,dtr=%d,rts=%d) \n " ,
( int ) sp , dtr , rts ) ;
# endif
memset ( sp , 0 , 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 locally in TIOCM format .
*/
static long stli_mktiocm ( unsigned long sigvalue )
{
long tiocm ;
# ifdef DEBUG
printk ( KERN_DEBUG " 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 ) ;
}
/*****************************************************************************/
/*
* 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 ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_initports(brdp=%x) \n " , ( int ) brdp ) ;
# endif
for ( i = 0 , panelnr = 0 , panelport = 0 ; ( i < brdp - > nrports ) ; i + + ) {
portp = ( stliport_t * ) stli_memalloc ( sizeof ( stliport_t ) ) ;
if ( portp = = ( stliport_t * ) NULL ) {
printk ( " STALLION: failed to allocate port structure \n " ) ;
continue ;
}
memset ( portp , 0 , sizeof ( stliport_t ) ) ;
portp - > magic = STLI_PORTMAGIC ;
portp - > portnr = i ;
portp - > brdnr = brdp - > brdnr ;
portp - > panelnr = panelnr ;
portp - > baud_base = STL_BAUDBASE ;
portp - > close_delay = STL_CLOSEDELAY ;
portp - > closing_wait = 30 * HZ ;
INIT_WORK ( & portp - > tqhangup , stli_dohangup , portp ) ;
init_waitqueue_head ( & portp - > open_wait ) ;
init_waitqueue_head ( & portp - > close_wait ) ;
init_waitqueue_head ( & portp - > raw_wait ) ;
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 ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_ecpinit(brdp=%d) \n " , ( int ) brdp ) ;
# endif
outb ( ECP_ATSTOP , ( brdp - > iobase + ECP_ATCONFR ) ) ;
udelay ( 10 ) ;
outb ( ECP_ATDISABLE , ( brdp - > iobase + ECP_ATCONFR ) ) ;
udelay ( 100 ) ;
memconf = ( brdp - > memaddr & ECP_ATADDRMASK ) > > ECP_ATADDRSHFT ;
outb ( memconf , ( brdp - > iobase + ECP_ATMEMAR ) ) ;
}
/*****************************************************************************/
static void stli_ecpenable ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_ecpenable(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( ECP_ATENABLE , ( brdp - > iobase + ECP_ATCONFR ) ) ;
}
/*****************************************************************************/
static void stli_ecpdisable ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_ecpdisable(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( ECP_ATDISABLE , ( brdp - > iobase + ECP_ATCONFR ) ) ;
}
/*****************************************************************************/
static char * stli_ecpgetmemptr ( stlibrd_t * brdp , unsigned long offset , int line )
{
void * ptr ;
unsigned char val ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_ecpgetmemptr(brdp=%x,offset=%x) \n " , ( int ) brdp ,
( int ) offset ) ;
# endif
if ( offset > brdp - > memsize ) {
printk ( KERN_ERR " STALLION: shared memory pointer=%x out of "
" range at line=%d(%d), brd=%d \n " ,
( int ) offset , line , __LINE__ , brdp - > brdnr ) ;
ptr = NULL ;
val = 0 ;
} else {
ptr = brdp - > membase + ( offset % ECP_ATPAGESIZE ) ;
val = ( unsigned char ) ( offset / ECP_ATPAGESIZE ) ;
}
outb ( val , ( brdp - > iobase + ECP_ATMEMPR ) ) ;
return ( ptr ) ;
}
/*****************************************************************************/
static void stli_ecpreset ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_ecpreset(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( ECP_ATSTOP , ( brdp - > iobase + ECP_ATCONFR ) ) ;
udelay ( 10 ) ;
outb ( ECP_ATDISABLE , ( brdp - > iobase + ECP_ATCONFR ) ) ;
udelay ( 500 ) ;
}
/*****************************************************************************/
static void stli_ecpintr ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_ecpintr(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( 0x1 , brdp - > iobase ) ;
}
/*****************************************************************************/
/*
* The following set of functions act on ECP EISA boards .
*/
static void stli_ecpeiinit ( stlibrd_t * brdp )
{
unsigned long memconf ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_ecpeiinit(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( 0x1 , ( brdp - > iobase + ECP_EIBRDENAB ) ) ;
outb ( ECP_EISTOP , ( brdp - > iobase + ECP_EICONFR ) ) ;
udelay ( 10 ) ;
outb ( ECP_EIDISABLE , ( brdp - > iobase + ECP_EICONFR ) ) ;
udelay ( 500 ) ;
memconf = ( brdp - > memaddr & ECP_EIADDRMASKL ) > > ECP_EIADDRSHFTL ;
outb ( memconf , ( brdp - > iobase + ECP_EIMEMARL ) ) ;
memconf = ( brdp - > memaddr & ECP_EIADDRMASKH ) > > ECP_EIADDRSHFTH ;
outb ( memconf , ( brdp - > iobase + ECP_EIMEMARH ) ) ;
}
/*****************************************************************************/
static void stli_ecpeienable ( stlibrd_t * brdp )
{
outb ( ECP_EIENABLE , ( brdp - > iobase + ECP_EICONFR ) ) ;
}
/*****************************************************************************/
static void stli_ecpeidisable ( stlibrd_t * brdp )
{
outb ( ECP_EIDISABLE , ( brdp - > iobase + ECP_EICONFR ) ) ;
}
/*****************************************************************************/
static char * stli_ecpeigetmemptr ( stlibrd_t * brdp , unsigned long offset , int line )
{
void * ptr ;
unsigned char val ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d) \n " ,
( int ) brdp , ( int ) offset , line ) ;
# endif
if ( offset > brdp - > memsize ) {
printk ( KERN_ERR " STALLION: shared memory pointer=%x out of "
" range at line=%d(%d), brd=%d \n " ,
( int ) offset , line , __LINE__ , brdp - > brdnr ) ;
ptr = NULL ;
val = 0 ;
} else {
ptr = brdp - > membase + ( offset % ECP_EIPAGESIZE ) ;
if ( offset < ECP_EIPAGESIZE )
val = ECP_EIENABLE ;
else
val = ECP_EIENABLE | 0x40 ;
}
outb ( val , ( brdp - > iobase + ECP_EICONFR ) ) ;
return ( ptr ) ;
}
/*****************************************************************************/
static void stli_ecpeireset ( stlibrd_t * brdp )
{
outb ( ECP_EISTOP , ( brdp - > iobase + ECP_EICONFR ) ) ;
udelay ( 10 ) ;
outb ( ECP_EIDISABLE , ( brdp - > iobase + ECP_EICONFR ) ) ;
udelay ( 500 ) ;
}
/*****************************************************************************/
/*
* The following set of functions act on ECP MCA boards .
*/
static void stli_ecpmcenable ( stlibrd_t * brdp )
{
outb ( ECP_MCENABLE , ( brdp - > iobase + ECP_MCCONFR ) ) ;
}
/*****************************************************************************/
static void stli_ecpmcdisable ( stlibrd_t * brdp )
{
outb ( ECP_MCDISABLE , ( brdp - > iobase + ECP_MCCONFR ) ) ;
}
/*****************************************************************************/
static char * stli_ecpmcgetmemptr ( stlibrd_t * brdp , unsigned long offset , int line )
{
void * ptr ;
unsigned char val ;
if ( offset > brdp - > memsize ) {
printk ( KERN_ERR " STALLION: shared memory pointer=%x out of "
" range at line=%d(%d), brd=%d \n " ,
( int ) offset , line , __LINE__ , brdp - > brdnr ) ;
ptr = NULL ;
val = 0 ;
} else {
ptr = brdp - > membase + ( offset % ECP_MCPAGESIZE ) ;
val = ( ( unsigned char ) ( offset / ECP_MCPAGESIZE ) ) | ECP_MCENABLE ;
}
outb ( val , ( brdp - > iobase + ECP_MCCONFR ) ) ;
return ( ptr ) ;
}
/*****************************************************************************/
static void stli_ecpmcreset ( stlibrd_t * brdp )
{
outb ( ECP_MCSTOP , ( brdp - > iobase + ECP_MCCONFR ) ) ;
udelay ( 10 ) ;
outb ( ECP_MCDISABLE , ( brdp - > iobase + ECP_MCCONFR ) ) ;
udelay ( 500 ) ;
}
/*****************************************************************************/
/*
* The following set of functions act on ECP PCI boards .
*/
static void stli_ecppciinit ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_ecppciinit(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( ECP_PCISTOP , ( brdp - > iobase + ECP_PCICONFR ) ) ;
udelay ( 10 ) ;
outb ( 0 , ( brdp - > iobase + ECP_PCICONFR ) ) ;
udelay ( 500 ) ;
}
/*****************************************************************************/
static char * stli_ecppcigetmemptr ( stlibrd_t * brdp , unsigned long offset , int line )
{
void * ptr ;
unsigned char val ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_ecppcigetmemptr(brdp=%x,offset=%x,line=%d) \n " ,
( int ) brdp , ( int ) offset , line ) ;
# endif
if ( offset > brdp - > memsize ) {
printk ( KERN_ERR " STALLION: shared memory pointer=%x out of "
" range at line=%d(%d), board=%d \n " ,
( int ) offset , line , __LINE__ , brdp - > brdnr ) ;
ptr = NULL ;
val = 0 ;
} else {
ptr = brdp - > membase + ( offset % ECP_PCIPAGESIZE ) ;
val = ( offset / ECP_PCIPAGESIZE ) < < 1 ;
}
outb ( val , ( brdp - > iobase + ECP_PCICONFR ) ) ;
return ( ptr ) ;
}
/*****************************************************************************/
static void stli_ecppcireset ( stlibrd_t * brdp )
{
outb ( ECP_PCISTOP , ( brdp - > iobase + ECP_PCICONFR ) ) ;
udelay ( 10 ) ;
outb ( 0 , ( brdp - > iobase + ECP_PCICONFR ) ) ;
udelay ( 500 ) ;
}
/*****************************************************************************/
/*
* The following routines act on ONboards .
*/
static void stli_onbinit ( stlibrd_t * brdp )
{
unsigned long memconf ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_onbinit(brdp=%d) \n " , ( int ) brdp ) ;
# endif
outb ( ONB_ATSTOP , ( brdp - > iobase + ONB_ATCONFR ) ) ;
udelay ( 10 ) ;
outb ( ONB_ATDISABLE , ( brdp - > iobase + ONB_ATCONFR ) ) ;
mdelay ( 1000 ) ;
memconf = ( brdp - > memaddr & ONB_ATADDRMASK ) > > ONB_ATADDRSHFT ;
outb ( memconf , ( brdp - > iobase + ONB_ATMEMAR ) ) ;
outb ( 0x1 , brdp - > iobase ) ;
mdelay ( 1 ) ;
}
/*****************************************************************************/
static void stli_onbenable ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_onbenable(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( ( brdp - > enabval | ONB_ATENABLE ) , ( brdp - > iobase + ONB_ATCONFR ) ) ;
}
/*****************************************************************************/
static void stli_onbdisable ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_onbdisable(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( ( brdp - > enabval | ONB_ATDISABLE ) , ( brdp - > iobase + ONB_ATCONFR ) ) ;
}
/*****************************************************************************/
static char * stli_onbgetmemptr ( stlibrd_t * brdp , unsigned long offset , int line )
{
void * ptr ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_onbgetmemptr(brdp=%x,offset=%x) \n " , ( int ) brdp ,
( int ) offset ) ;
# endif
if ( offset > brdp - > memsize ) {
printk ( KERN_ERR " STALLION: shared memory pointer=%x out of "
" range at line=%d(%d), brd=%d \n " ,
( int ) offset , line , __LINE__ , brdp - > brdnr ) ;
ptr = NULL ;
} else {
ptr = brdp - > membase + ( offset % ONB_ATPAGESIZE ) ;
}
return ( ptr ) ;
}
/*****************************************************************************/
static void stli_onbreset ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_onbreset(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( ONB_ATSTOP , ( brdp - > iobase + ONB_ATCONFR ) ) ;
udelay ( 10 ) ;
outb ( ONB_ATDISABLE , ( brdp - > iobase + ONB_ATCONFR ) ) ;
mdelay ( 1000 ) ;
}
/*****************************************************************************/
/*
* The following routines act on ONboard EISA .
*/
static void stli_onbeinit ( stlibrd_t * brdp )
{
unsigned long memconf ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_onbeinit(brdp=%d) \n " , ( int ) brdp ) ;
# endif
outb ( 0x1 , ( brdp - > iobase + ONB_EIBRDENAB ) ) ;
outb ( ONB_EISTOP , ( brdp - > iobase + ONB_EICONFR ) ) ;
udelay ( 10 ) ;
outb ( ONB_EIDISABLE , ( brdp - > iobase + ONB_EICONFR ) ) ;
mdelay ( 1000 ) ;
memconf = ( brdp - > memaddr & ONB_EIADDRMASKL ) > > ONB_EIADDRSHFTL ;
outb ( memconf , ( brdp - > iobase + ONB_EIMEMARL ) ) ;
memconf = ( brdp - > memaddr & ONB_EIADDRMASKH ) > > ONB_EIADDRSHFTH ;
outb ( memconf , ( brdp - > iobase + ONB_EIMEMARH ) ) ;
outb ( 0x1 , brdp - > iobase ) ;
mdelay ( 1 ) ;
}
/*****************************************************************************/
static void stli_onbeenable ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_onbeenable(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( ONB_EIENABLE , ( brdp - > iobase + ONB_EICONFR ) ) ;
}
/*****************************************************************************/
static void stli_onbedisable ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_onbedisable(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( ONB_EIDISABLE , ( brdp - > iobase + ONB_EICONFR ) ) ;
}
/*****************************************************************************/
static char * stli_onbegetmemptr ( stlibrd_t * brdp , unsigned long offset , int line )
{
void * ptr ;
unsigned char val ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_onbegetmemptr(brdp=%x,offset=%x,line=%d) \n " ,
( int ) brdp , ( int ) offset , line ) ;
# endif
if ( offset > brdp - > memsize ) {
printk ( KERN_ERR " STALLION: shared memory pointer=%x out of "
" range at line=%d(%d), brd=%d \n " ,
( int ) offset , line , __LINE__ , brdp - > brdnr ) ;
ptr = NULL ;
val = 0 ;
} else {
ptr = brdp - > membase + ( offset % ONB_EIPAGESIZE ) ;
if ( offset < ONB_EIPAGESIZE )
val = ONB_EIENABLE ;
else
val = ONB_EIENABLE | 0x40 ;
}
outb ( val , ( brdp - > iobase + ONB_EICONFR ) ) ;
return ( ptr ) ;
}
/*****************************************************************************/
static void stli_onbereset ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_ERR " stli_onbereset(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( ONB_EISTOP , ( brdp - > iobase + ONB_EICONFR ) ) ;
udelay ( 10 ) ;
outb ( ONB_EIDISABLE , ( brdp - > iobase + ONB_EICONFR ) ) ;
mdelay ( 1000 ) ;
}
/*****************************************************************************/
/*
* The following routines act on Brumby boards .
*/
static void stli_bbyinit ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_ERR " stli_bbyinit(brdp=%d) \n " , ( int ) brdp ) ;
# endif
outb ( BBY_ATSTOP , ( brdp - > iobase + BBY_ATCONFR ) ) ;
udelay ( 10 ) ;
outb ( 0 , ( brdp - > iobase + BBY_ATCONFR ) ) ;
mdelay ( 1000 ) ;
outb ( 0x1 , brdp - > iobase ) ;
mdelay ( 1 ) ;
}
/*****************************************************************************/
static char * stli_bbygetmemptr ( stlibrd_t * brdp , unsigned long offset , int line )
{
void * ptr ;
unsigned char val ;
# ifdef DEBUG
printk ( KERN_ERR " stli_bbygetmemptr(brdp=%x,offset=%x) \n " , ( int ) brdp ,
( int ) offset ) ;
# endif
if ( offset > brdp - > memsize ) {
printk ( KERN_ERR " STALLION: shared memory pointer=%x out of "
" range at line=%d(%d), brd=%d \n " ,
( int ) offset , line , __LINE__ , brdp - > brdnr ) ;
ptr = NULL ;
val = 0 ;
} else {
ptr = brdp - > membase + ( offset % BBY_PAGESIZE ) ;
val = ( unsigned char ) ( offset / BBY_PAGESIZE ) ;
}
outb ( val , ( brdp - > iobase + BBY_ATCONFR ) ) ;
return ( ptr ) ;
}
/*****************************************************************************/
static void stli_bbyreset ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_bbyreset(brdp=%x) \n " , ( int ) brdp ) ;
# endif
outb ( BBY_ATSTOP , ( brdp - > iobase + BBY_ATCONFR ) ) ;
udelay ( 10 ) ;
outb ( 0 , ( brdp - > iobase + BBY_ATCONFR ) ) ;
mdelay ( 1000 ) ;
}
/*****************************************************************************/
/*
* The following routines act on original old Stallion boards .
*/
static void stli_stalinit ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " stli_stalinit(brdp=%d) \n " , ( int ) brdp ) ;
# endif
outb ( 0x1 , brdp - > iobase ) ;
mdelay ( 1000 ) ;
}
/*****************************************************************************/
static char * stli_stalgetmemptr ( stlibrd_t * brdp , unsigned long offset , int line )
{
void * ptr ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_stalgetmemptr(brdp=%x,offset=%x) \n " , ( int ) brdp ,
( int ) offset ) ;
# endif
if ( offset > brdp - > memsize ) {
printk ( KERN_ERR " STALLION: shared memory pointer=%x out of "
" range at line=%d(%d), brd=%d \n " ,
( int ) offset , line , __LINE__ , brdp - > brdnr ) ;
ptr = NULL ;
} else {
ptr = brdp - > membase + ( offset % STAL_PAGESIZE ) ;
}
return ( ptr ) ;
}
/*****************************************************************************/
static void stli_stalreset ( stlibrd_t * brdp )
{
volatile unsigned long * vecp ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_stalreset(brdp=%x) \n " , ( int ) brdp ) ;
# endif
vecp = ( volatile unsigned long * ) ( brdp - > membase + 0x30 ) ;
* vecp = 0xffff0000 ;
outb ( 0 , brdp - > iobase ) ;
mdelay ( 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 ;
char * name ;
int panelnr , nrports ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_initecp(brdp=%x) \n " , ( int ) brdp ) ;
# endif
if ( ! request_region ( brdp - > iobase , brdp - > iosize , " istallion " ) )
return - EIO ;
if ( ( brdp - > iobase = = 0 ) | | ( brdp - > memaddr = = 0 ) )
{
release_region ( brdp - > iobase , brdp - > iosize ) ;
return ( - ENODEV ) ;
}
brdp - > iosize = ECP_IOSIZE ;
/*
* 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 - > membase = ( void * ) brdp - > memaddr ;
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 ;
name = " serial(EC8/64) " ;
break ;
case BRD_ECPE :
brdp - > membase = ( void * ) brdp - > memaddr ;
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 ;
name = " serial(EC8/64-EI) " ;
break ;
case BRD_ECPMC :
brdp - > membase = ( void * ) brdp - > memaddr ;
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 ;
name = " serial(EC8/64-MCA) " ;
break ;
case BRD_ECPPCI :
brdp - > membase = ( void * ) brdp - > memaddr ;
brdp - > memsize = ECP_PCIMEMSIZE ;
brdp - > pagesize = ECP_PCIPAGESIZE ;
brdp - > init = stli_ecppciinit ;
brdp - > enable = NULL ;
brdp - > reenable = NULL ;
brdp - > disable = NULL ;
brdp - > getmemptr = stli_ecppcigetmemptr ;
brdp - > intr = stli_ecpintr ;
brdp - > reset = stli_ecppcireset ;
name = " serial(EC/RA-PCI) " ;
break ;
default :
release_region ( brdp - > iobase , brdp - > iosize ) ;
return ( - EINVAL ) ;
}
/*
* The per - board operations structure is all set up , so now let ' s go
* and get the board operational . Firstly initialize board configuration
* registers . Set the memory mapping info so we can get at the boards
* shared memory .
*/
EBRDINIT ( brdp ) ;
brdp - > membase = ioremap ( brdp - > memaddr , brdp - > memsize ) ;
if ( brdp - > membase = = ( void * ) NULL )
{
release_region ( brdp - > iobase , brdp - > iosize ) ;
return ( - ENOMEM ) ;
}
/*
* 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 ) ;
memcpy ( & sig , sigsp , sizeof ( cdkecpsig_t ) ) ;
EBRDDISABLE ( brdp ) ;
#if 0
printk ( " %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 )
{
release_region ( brdp - > iobase , brdp - > iosize ) ;
return ( - ENODEV ) ;
}
/*
* 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 ;
nrports = ( status & ECH_PNL16PORT ) ? 16 : 8 ;
if ( ( nrports = = 16 ) & & ( ( status & ECH_PNLXPID ) = = 0 ) )
nxtid + + ;
brdp - > panels [ panelnr ] = nrports ;
brdp - > nrports + = nrports ;
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 ;
char * name ;
int i ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_initonb(brdp=%x) \n " , ( int ) brdp ) ;
# endif
/*
* Do a basic sanity check on the IO and memory addresses .
*/
if ( ( brdp - > iobase = = 0 ) | | ( brdp - > memaddr = = 0 ) )
return ( - ENODEV ) ;
brdp - > iosize = ONB_IOSIZE ;
if ( ! request_region ( brdp - > iobase , brdp - > iosize , " istallion " ) )
return - EIO ;
/*
* 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 - > membase = ( void * ) brdp - > memaddr ;
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 ;
if ( brdp - > memaddr > 0x100000 )
brdp - > enabval = ONB_MEMENABHI ;
else
brdp - > enabval = ONB_MEMENABLO ;
name = " serial(ONBoard) " ;
break ;
case BRD_ONBOARDE :
brdp - > membase = ( void * ) brdp - > memaddr ;
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 ;
name = " serial(ONBoard/E) " ;
break ;
case BRD_BRUMBY4 :
case BRD_BRUMBY8 :
case BRD_BRUMBY16 :
brdp - > membase = ( void * ) brdp - > memaddr ;
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 ;
name = " serial(Brumby) " ;
break ;
case BRD_STALLION :
brdp - > membase = ( void * ) brdp - > memaddr ;
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 ;
name = " serial(Stallion) " ;
break ;
default :
release_region ( brdp - > iobase , brdp - > iosize ) ;
return ( - EINVAL ) ;
}
/*
* The per - board operations structure is all set up , so now let ' s go
* and get the board operational . Firstly initialize board configuration
* registers . Set the memory mapping info so we can get at the boards
* shared memory .
*/
EBRDINIT ( brdp ) ;
brdp - > membase = ioremap ( brdp - > memaddr , brdp - > memsize ) ;
if ( brdp - > membase = = ( void * ) NULL )
{
release_region ( brdp - > iobase , brdp - > iosize ) ;
return ( - ENOMEM ) ;
}
/*
* 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 ) ;
memcpy ( & sig , sigsp , sizeof ( cdkonbsig_t ) ) ;
EBRDDISABLE ( brdp ) ;
#if 0
printk ( " %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 ) )
{
release_region ( brdp - > iobase , brdp - > iosize ) ;
return ( - ENODEV ) ;
}
/*
* 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 ;
unsigned long flags ;
stliport_t * portp ;
int portnr , nrdevs , i , rc ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_startbrd(brdp=%x) \n " , ( int ) brdp ) ;
# endif
rc = 0 ;
save_flags ( flags ) ;
cli ( ) ;
EBRDENABLE ( brdp ) ;
hdrp = ( volatile cdkhdr_t * ) EBRDGETMEMPTR ( brdp , CDK_CDKADDR ) ;
nrdevs = hdrp - > nrdevs ;
#if 0
printk ( " %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 ) ) {
printk ( KERN_ERR " 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 * ) hdrp - > memp ;
if ( ( ( unsigned long ) memp ) > brdp - > memsize ) {
printk ( KERN_ERR " STALLION: corrupted shared memory region? \n " ) ;
rc = - EIO ;
goto stli_donestartup ;
}
memp = ( volatile cdkmem_t * ) EBRDGETMEMPTR ( brdp , ( unsigned long ) memp ) ;
if ( memp - > dtype ! = TYP_ASYNCTRL ) {
printk ( KERN_ERR " STALLION: no slave control device found \n " ) ;
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 - > reqbit = ( unsigned char ) ( 0x1 < < ( i * 8 / nrdevs ) ) ;
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 ) ;
restore_flags ( flags ) ;
if ( rc = = 0 )
brdp - > state | = BST_STARTED ;
if ( ! stli_timeron ) {
stli_timeron + + ;
stli_timerlist . expires = STLI_TIMEOUT ;
add_timer ( & stli_timerlist ) ;
}
return ( rc ) ;
}
/*****************************************************************************/
/*
* Probe and initialize the specified board .
*/
static int __init stli_brdinit ( stlibrd_t * brdp )
{
# ifdef DEBUG
printk ( KERN_DEBUG " 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 :
case BRD_ECPPCI :
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 :
printk ( KERN_ERR " STALLION: %s board type not supported in "
" this driver \n " , stli_brdnames [ brdp - > brdtype ] ) ;
return ( ENODEV ) ;
default :
printk ( KERN_ERR " STALLION: board=%d is unknown board "
" type=%d \n " , brdp - > brdnr , brdp - > brdtype ) ;
return ( ENODEV ) ;
}
if ( ( brdp - > state & BST_FOUND ) = = 0 ) {
printk ( KERN_ERR " STALLION: %s board not found, board=%d "
" io=%x mem=%x \n " ,
stli_brdnames [ brdp - > brdtype ] , brdp - > brdnr ,
brdp - > iobase , ( int ) brdp - > memaddr ) ;
return ( ENODEV ) ;
}
stli_initports ( brdp ) ;
printk ( KERN_INFO " STALLION: %s found, board=%d io=%x mem=%x "
" nrpanels=%d nrports=%d \n " , stli_brdnames [ brdp - > brdtype ] ,
brdp - > brdnr , brdp - > iobase , ( int ) brdp - > memaddr ,
brdp - > nrpanels , brdp - > nrports ) ;
return ( 0 ) ;
}
/*****************************************************************************/
/*
* Probe around trying to find where the EISA boards shared memory
* might be . This is a bit if hack , but it is the best we can do .
*/
static int stli_eisamemprobe ( stlibrd_t * brdp )
{
cdkecpsig_t ecpsig , * ecpsigp ;
cdkonbsig_t onbsig , * onbsigp ;
int i , foundit ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_eisamemprobe(brdp=%x) \n " , ( int ) brdp ) ;
# endif
/*
* First up we reset the board , to get it into a known state . There
* is only 2 board types here we need to worry about . Don ; t use the
* standard board init routine here , it programs up the shared
* memory address , and we don ' t know it yet . . .
*/
if ( brdp - > brdtype = = BRD_ECPE ) {
outb ( 0x1 , ( brdp - > iobase + ECP_EIBRDENAB ) ) ;
outb ( ECP_EISTOP , ( brdp - > iobase + ECP_EICONFR ) ) ;
udelay ( 10 ) ;
outb ( ECP_EIDISABLE , ( brdp - > iobase + ECP_EICONFR ) ) ;
udelay ( 500 ) ;
stli_ecpeienable ( brdp ) ;
} else if ( brdp - > brdtype = = BRD_ONBOARDE ) {
outb ( 0x1 , ( brdp - > iobase + ONB_EIBRDENAB ) ) ;
outb ( ONB_EISTOP , ( brdp - > iobase + ONB_EICONFR ) ) ;
udelay ( 10 ) ;
outb ( ONB_EIDISABLE , ( brdp - > iobase + ONB_EICONFR ) ) ;
mdelay ( 100 ) ;
outb ( 0x1 , brdp - > iobase ) ;
mdelay ( 1 ) ;
stli_onbeenable ( brdp ) ;
} else {
return ( - ENODEV ) ;
}
foundit = 0 ;
brdp - > memsize = ECP_MEMSIZE ;
/*
* Board shared memory is enabled , so now we have a poke around and
* see if we can find it .
*/
for ( i = 0 ; ( i < stli_eisamempsize ) ; i + + ) {
brdp - > memaddr = stli_eisamemprobeaddrs [ i ] ;
brdp - > membase = ( void * ) brdp - > memaddr ;
brdp - > membase = ioremap ( brdp - > memaddr , brdp - > memsize ) ;
if ( brdp - > membase = = ( void * ) NULL )
continue ;
if ( brdp - > brdtype = = BRD_ECPE ) {
ecpsigp = ( cdkecpsig_t * ) stli_ecpeigetmemptr ( brdp ,
CDK_SIGADDR , __LINE__ ) ;
memcpy ( & ecpsig , ecpsigp , sizeof ( cdkecpsig_t ) ) ;
if ( ecpsig . magic = = ECP_MAGIC )
foundit = 1 ;
} else {
onbsigp = ( cdkonbsig_t * ) stli_onbegetmemptr ( brdp ,
CDK_SIGADDR , __LINE__ ) ;
memcpy ( & onbsig , onbsigp , sizeof ( cdkonbsig_t ) ) ;
if ( ( onbsig . magic0 = = ONB_MAGIC0 ) & &
( onbsig . magic1 = = ONB_MAGIC1 ) & &
( onbsig . magic2 = = ONB_MAGIC2 ) & &
( onbsig . magic3 = = ONB_MAGIC3 ) )
foundit = 1 ;
}
iounmap ( brdp - > membase ) ;
if ( foundit )
break ;
}
/*
* Regardless of whether we found the shared memory or not we must
* disable the region . After that return success or failure .
*/
if ( brdp - > brdtype = = BRD_ECPE )
stli_ecpeidisable ( brdp ) ;
else
stli_onbedisable ( brdp ) ;
if ( ! foundit ) {
brdp - > memaddr = 0 ;
brdp - > membase = NULL ;
printk ( KERN_ERR " STALLION: failed to probe shared memory "
" region for %s in EISA slot=%d \n " ,
stli_brdnames [ brdp - > brdtype ] , ( brdp - > iobase > > 12 ) ) ;
return ( - ENODEV ) ;
}
return ( 0 ) ;
}
static int stli_getbrdnr ( void )
{
int i ;
for ( i = 0 ; i < STL_MAXBRDS ; i + + ) {
if ( ! stli_brds [ i ] ) {
if ( i > = stli_nrbrds )
stli_nrbrds = i + 1 ;
return i ;
}
}
return - 1 ;
}
/*****************************************************************************/
/*
* Probe around and try to find any EISA boards in system . The biggest
* problem here is finding out what memory address is associated with
* an EISA board after it is found . The registers of the ECPE and
* ONboardE are not readable - so we can ' t read them from there . We
* don ' t have access to the EISA CMOS ( or EISA BIOS ) so we don ' t
* actually have any way to find out the real value . The best we can
* do is go probing around in the usual places hoping we can find it .
*/
static int stli_findeisabrds ( void )
{
stlibrd_t * brdp ;
unsigned int iobase , eid ;
int i ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_findeisabrds() \n " ) ;
# 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 ( 0xff , 0xc80 ) ;
if ( inb ( 0xc80 ) = = 0xff )
return ( 0 ) ;
/*
* Looks like an EISA system , so go searching for EISA boards .
*/
for ( iobase = 0x1000 ; ( iobase < = 0xc000 ) ; iobase + = 0x1000 ) {
outb ( 0xff , ( iobase + 0xc80 ) ) ;
eid = inb ( iobase + 0xc80 ) ;
eid | = inb ( iobase + 0xc81 ) < < 8 ;
if ( eid ! = STL_EISAID )
continue ;
/*
* We have found a board . Need to check if this board was
* statically configured already ( just in case ! ) .
*/
for ( i = 0 ; ( i < STL_MAXBRDS ) ; i + + ) {
brdp = stli_brds [ i ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
continue ;
if ( brdp - > iobase = = iobase )
break ;
}
if ( i < STL_MAXBRDS )
continue ;
/*
* We have found a Stallion board and it is not configured already .
* Allocate a board structure and initialize it .
*/
if ( ( brdp = stli_allocbrd ( ) ) = = ( stlibrd_t * ) NULL )
return ( - ENOMEM ) ;
if ( ( brdp - > brdnr = stli_getbrdnr ( ) ) < 0 )
return ( - ENOMEM ) ;
eid = inb ( iobase + 0xc82 ) ;
if ( eid = = ECP_EISAID )
brdp - > brdtype = BRD_ECPE ;
else if ( eid = = ONB_EISAID )
brdp - > brdtype = BRD_ONBOARDE ;
else
brdp - > brdtype = BRD_UNKNOWN ;
brdp - > iobase = iobase ;
outb ( 0x1 , ( iobase + 0xc84 ) ) ;
if ( stli_eisamemprobe ( brdp ) )
outb ( 0 , ( iobase + 0xc84 ) ) ;
stli_brdinit ( brdp ) ;
}
return ( 0 ) ;
}
/*****************************************************************************/
/*
* Find the next available board number that is free .
*/
/*****************************************************************************/
# ifdef CONFIG_PCI
/*
* We have a Stallion board . Allocate a board structure and
* initialize it . Read its IO and MEMORY resources from PCI
* configuration space .
*/
static int stli_initpcibrd ( int brdtype , struct pci_dev * devp )
{
stlibrd_t * brdp ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_initpcibrd(brdtype=%d,busnr=%x,devnr=%x) \n " ,
brdtype , dev - > bus - > number , dev - > devfn ) ;
# endif
if ( pci_enable_device ( devp ) )
return ( - EIO ) ;
if ( ( brdp = stli_allocbrd ( ) ) = = ( stlibrd_t * ) NULL )
return ( - ENOMEM ) ;
if ( ( brdp - > brdnr = stli_getbrdnr ( ) ) < 0 ) {
printk ( KERN_INFO " STALLION: too many boards found, "
" maximum supported %d \n " , STL_MAXBRDS ) ;
return ( 0 ) ;
}
brdp - > brdtype = brdtype ;
# ifdef DEBUG
printk ( KERN_DEBUG " %s(%d): BAR[]=%lx,%lx,%lx,%lx \n " , __FILE__ , __LINE__ ,
pci_resource_start ( devp , 0 ) ,
pci_resource_start ( devp , 1 ) ,
pci_resource_start ( devp , 2 ) ,
pci_resource_start ( devp , 3 ) ) ;
# endif
/*
* We have all resources from the board , so lets setup the actual
* board structure now .
*/
brdp - > iobase = pci_resource_start ( devp , 3 ) ;
brdp - > memaddr = pci_resource_start ( devp , 2 ) ;
stli_brdinit ( brdp ) ;
return ( 0 ) ;
}
/*****************************************************************************/
/*
* Find all Stallion PCI boards that might be installed . Initialize each
* one as it is found .
*/
static int stli_findpcibrds ( void )
{
struct pci_dev * dev = NULL ;
int rc ;
# ifdef DEBUG
printk ( " stli_findpcibrds() \n " ) ;
# endif
while ( ( dev = pci_find_device ( PCI_VENDOR_ID_STALLION ,
PCI_DEVICE_ID_ECRA , dev ) ) ) {
if ( ( rc = stli_initpcibrd ( BRD_ECPPCI , dev ) ) )
return ( rc ) ;
}
return ( 0 ) ;
}
# endif
/*****************************************************************************/
/*
* Allocate a new board structure . Fill out the basic info in it .
*/
static stlibrd_t * stli_allocbrd ( void )
{
stlibrd_t * brdp ;
brdp = ( stlibrd_t * ) stli_memalloc ( sizeof ( stlibrd_t ) ) ;
if ( brdp = = ( stlibrd_t * ) NULL ) {
printk ( KERN_ERR " STALLION: failed to allocate memory "
" (size=%d) \n " , sizeof ( stlibrd_t ) ) ;
return ( ( stlibrd_t * ) NULL ) ;
}
memset ( brdp , 0 , sizeof ( stlibrd_t ) ) ;
brdp - > magic = STLI_BOARDMAGIC ;
return ( brdp ) ;
}
/*****************************************************************************/
/*
* Scan through all the boards in the configuration and see what we
* can find .
*/
static int stli_initbrds ( void )
{
stlibrd_t * brdp , * nxtbrdp ;
stlconf_t * confp ;
int i , j ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_initbrds() \n " ) ;
# endif
if ( stli_nrbrds > STL_MAXBRDS ) {
printk ( KERN_INFO " STALLION: too many boards in configuration "
" table, truncating to %d \n " , STL_MAXBRDS ) ;
stli_nrbrds = STL_MAXBRDS ;
}
/*
* Firstly scan the list of static boards configured . Allocate
* resources and initialize the boards as found . If this is a
* module then let the module args override static configuration .
*/
for ( i = 0 ; ( i < stli_nrbrds ) ; i + + ) {
confp = & stli_brdconf [ i ] ;
# ifdef MODULE
stli_parsebrd ( confp , stli_brdsp [ i ] ) ;
# endif
if ( ( brdp = stli_allocbrd ( ) ) = = ( stlibrd_t * ) NULL )
return ( - ENOMEM ) ;
brdp - > brdnr = i ;
brdp - > brdtype = confp - > brdtype ;
brdp - > iobase = confp - > ioaddr1 ;
brdp - > memaddr = confp - > memaddr ;
stli_brdinit ( brdp ) ;
}
/*
* Static configuration table done , so now use dynamic methods to
* see if any more boards should be configured .
*/
# ifdef MODULE
stli_argbrds ( ) ;
# endif
2005-06-26 01:59:03 +04:00
if ( STLI_EISAPROBE )
2005-04-17 02:20:36 +04:00
stli_findeisabrds ( ) ;
# ifdef CONFIG_PCI
stli_findpcibrds ( ) ;
# 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 - > membase > = nxtbrdp - > membase ) & &
( brdp - > membase < = ( nxtbrdp - > membase +
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 ) ;
}
/*****************************************************************************/
/*
* Code to handle an " staliomem " read operation . This device is the
* contents of the board shared memory . It is used for down loading
* the slave image ( and debugging : - )
*/
static ssize_t stli_memread ( struct file * fp , char __user * buf , size_t count , loff_t * offp )
{
unsigned long flags ;
void * memptr ;
stlibrd_t * brdp ;
int brdnr , size , n ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_memread(fp=%x,buf=%x,count=%x,offp=%x) \n " ,
( int ) fp , ( int ) buf , count , ( int ) offp ) ;
# endif
brdnr = iminor ( fp - > f_dentry - > d_inode ) ;
if ( brdnr > = stli_nrbrds )
return ( - ENODEV ) ;
brdp = stli_brds [ brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( - ENODEV ) ;
if ( brdp - > state = = 0 )
return ( - ENODEV ) ;
if ( fp - > f_pos > = brdp - > memsize )
return ( 0 ) ;
size = MIN ( count , ( brdp - > memsize - fp - > f_pos ) ) ;
save_flags ( flags ) ;
cli ( ) ;
EBRDENABLE ( brdp ) ;
while ( size > 0 ) {
memptr = ( void * ) EBRDGETMEMPTR ( brdp , fp - > f_pos ) ;
n = MIN ( size , ( brdp - > pagesize - ( ( ( unsigned long ) fp - > f_pos ) % brdp - > pagesize ) ) ) ;
if ( copy_to_user ( buf , memptr , n ) ) {
count = - EFAULT ;
goto out ;
}
fp - > f_pos + = n ;
buf + = n ;
size - = n ;
}
out :
EBRDDISABLE ( brdp ) ;
restore_flags ( flags ) ;
return ( count ) ;
}
/*****************************************************************************/
/*
* Code to handle an " staliomem " write operation . This device is the
* contents of the board shared memory . It is used for down loading
* the slave image ( and debugging : - )
*/
static ssize_t stli_memwrite ( struct file * fp , const char __user * buf , size_t count , loff_t * offp )
{
unsigned long flags ;
void * memptr ;
stlibrd_t * brdp ;
char __user * chbuf ;
int brdnr , size , n ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_memwrite(fp=%x,buf=%x,count=%x,offp=%x) \n " ,
( int ) fp , ( int ) buf , count , ( int ) offp ) ;
# endif
brdnr = iminor ( fp - > f_dentry - > d_inode ) ;
if ( brdnr > = stli_nrbrds )
return ( - ENODEV ) ;
brdp = stli_brds [ brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( - ENODEV ) ;
if ( brdp - > state = = 0 )
return ( - ENODEV ) ;
if ( fp - > f_pos > = brdp - > memsize )
return ( 0 ) ;
chbuf = ( char __user * ) buf ;
size = MIN ( count , ( brdp - > memsize - fp - > f_pos ) ) ;
save_flags ( flags ) ;
cli ( ) ;
EBRDENABLE ( brdp ) ;
while ( size > 0 ) {
memptr = ( void * ) EBRDGETMEMPTR ( brdp , fp - > f_pos ) ;
n = MIN ( size , ( brdp - > pagesize - ( ( ( unsigned long ) fp - > f_pos ) % brdp - > pagesize ) ) ) ;
if ( copy_from_user ( memptr , chbuf , n ) ) {
count = - EFAULT ;
goto out ;
}
fp - > f_pos + = n ;
chbuf + = n ;
size - = n ;
}
out :
EBRDDISABLE ( brdp ) ;
restore_flags ( flags ) ;
return ( count ) ;
}
/*****************************************************************************/
/*
* Return the board stats structure to user app .
*/
static int stli_getbrdstats ( combrd_t __user * bp )
{
stlibrd_t * brdp ;
int i ;
if ( copy_from_user ( & stli_brdstats , bp , sizeof ( combrd_t ) ) )
return - EFAULT ;
if ( stli_brdstats . brd > = STL_MAXBRDS )
return ( - ENODEV ) ;
brdp = stli_brds [ stli_brdstats . brd ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( - ENODEV ) ;
memset ( & stli_brdstats , 0 , 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 - > memaddr ;
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 ] ;
}
if ( copy_to_user ( bp , & stli_brdstats , sizeof ( combrd_t ) ) )
return - EFAULT ;
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_portcmdstats ( stliport_t * portp )
{
unsigned long flags ;
stlibrd_t * brdp ;
int rc ;
memset ( & stli_comstats , 0 , sizeof ( comstats_t ) ) ;
if ( portp = = ( stliport_t * ) NULL )
return ( - ENODEV ) ;
brdp = stli_brds [ portp - > brdnr ] ;
if ( brdp = = ( stlibrd_t * ) NULL )
return ( - ENODEV ) ;
if ( brdp - > state & BST_STARTED ) {
if ( ( rc = stli_cmdwait ( brdp , portp , A_GETSTATS ,
& stli_cdkstats , sizeof ( asystats_t ) , 1 ) ) < 0 )
return ( rc ) ;
} else {
memset ( & stli_cdkstats , 0 , sizeof ( asystats_t ) ) ;
}
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 ;
save_flags ( flags ) ;
cli ( ) ;
if ( portp - > tty ! = ( struct tty_struct * ) NULL ) {
if ( portp - > tty - > driver_data = = portp ) {
stli_comstats . ttystate = portp - > tty - > flags ;
stli_comstats . rxbuffered = portp - > tty - > flip . count ;
if ( portp - > tty - > termios ! = ( struct termios * ) NULL ) {
stli_comstats . cflags = portp - > tty - > termios - > c_cflag ;
stli_comstats . iflags = portp - > tty - > termios - > c_iflag ;
stli_comstats . oflags = portp - > tty - > termios - > c_oflag ;
stli_comstats . lflags = portp - > tty - > termios - > c_lflag ;
}
}
}
restore_flags ( flags ) ;
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 ;
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 ) ;
return ( 0 ) ;
}
/*****************************************************************************/
/*
* 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 , comstats_t __user * cp )
{
stlibrd_t * brdp ;
int rc ;
if ( ! portp ) {
if ( copy_from_user ( & stli_comstats , cp , sizeof ( comstats_t ) ) )
return - EFAULT ;
portp = stli_getport ( stli_comstats . brd , stli_comstats . panel ,
stli_comstats . port ) ;
if ( ! portp )
return - ENODEV ;
}
brdp = stli_brds [ portp - > brdnr ] ;
if ( ! brdp )
return - ENODEV ;
if ( ( rc = stli_portcmdstats ( portp ) ) < 0 )
return rc ;
return copy_to_user ( cp , & stli_comstats , sizeof ( comstats_t ) ) ?
- EFAULT : 0 ;
}
/*****************************************************************************/
/*
* Clear the port stats structure . We also return it zeroed out . . .
*/
static int stli_clrportstats ( stliport_t * portp , comstats_t __user * cp )
{
stlibrd_t * brdp ;
int rc ;
if ( ! portp ) {
if ( copy_from_user ( & stli_comstats , cp , sizeof ( comstats_t ) ) )
return - EFAULT ;
portp = stli_getport ( stli_comstats . brd , stli_comstats . panel ,
stli_comstats . port ) ;
if ( ! portp )
return - ENODEV ;
}
brdp = stli_brds [ portp - > brdnr ] ;
if ( ! brdp )
return - ENODEV ;
if ( brdp - > state & BST_STARTED ) {
if ( ( rc = stli_cmdwait ( brdp , portp , A_CLEARSTATS , NULL , 0 , 0 ) ) < 0 )
return rc ;
}
memset ( & stli_comstats , 0 , sizeof ( comstats_t ) ) ;
stli_comstats . brd = portp - > brdnr ;
stli_comstats . panel = portp - > panelnr ;
stli_comstats . port = portp - > portnr ;
if ( copy_to_user ( cp , & stli_comstats , sizeof ( comstats_t ) ) )
return - EFAULT ;
return 0 ;
}
/*****************************************************************************/
/*
* Return the entire driver ports structure to a user app .
*/
static int stli_getportstruct ( stliport_t __user * arg )
{
stliport_t * portp ;
if ( copy_from_user ( & stli_dummyport , arg , sizeof ( stliport_t ) ) )
return - EFAULT ;
portp = stli_getport ( stli_dummyport . brdnr , stli_dummyport . panelnr ,
stli_dummyport . portnr ) ;
if ( ! portp )
return - ENODEV ;
if ( copy_to_user ( arg , portp , sizeof ( stliport_t ) ) )
return - EFAULT ;
return 0 ;
}
/*****************************************************************************/
/*
* Return the entire driver board structure to a user app .
*/
static int stli_getbrdstruct ( stlibrd_t __user * arg )
{
stlibrd_t * brdp ;
if ( copy_from_user ( & stli_dummybrd , arg , sizeof ( stlibrd_t ) ) )
return - EFAULT ;
if ( ( stli_dummybrd . brdnr < 0 ) | | ( stli_dummybrd . brdnr > = STL_MAXBRDS ) )
return - ENODEV ;
brdp = stli_brds [ stli_dummybrd . brdnr ] ;
if ( ! brdp )
return - ENODEV ;
if ( copy_to_user ( arg , brdp , sizeof ( stlibrd_t ) ) )
return - EFAULT ;
return 0 ;
}
/*****************************************************************************/
/*
* 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 ( struct inode * ip , struct file * fp , unsigned int cmd , unsigned long arg )
{
stlibrd_t * brdp ;
int brdnr , rc , done ;
void __user * argp = ( void __user * ) arg ;
# ifdef DEBUG
printk ( KERN_DEBUG " stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x) \n " ,
( int ) ip , ( int ) fp , cmd , ( int ) arg ) ;
# endif
/*
* First up handle the board independent ioctls .
*/
done = 0 ;
rc = 0 ;
switch ( cmd ) {
case COM_GETPORTSTATS :
rc = stli_getportstats ( NULL , argp ) ;
done + + ;
break ;
case COM_CLRPORTSTATS :
rc = stli_clrportstats ( NULL , argp ) ;
done + + ;
break ;
case COM_GETBRDSTATS :
rc = stli_getbrdstats ( argp ) ;
done + + ;
break ;
case COM_READPORT :
rc = stli_getportstruct ( argp ) ;
done + + ;
break ;
case COM_READBOARD :
rc = stli_getbrdstruct ( argp ) ;
done + + ;
break ;
}
if ( done )
return ( rc ) ;
/*
* Now handle the board specific ioctls . These all depend on the
* minor number of the device they were called from .
*/
brdnr = iminor ( ip ) ;
if ( brdnr > = STL_MAXBRDS )
return ( - ENODEV ) ;
brdp = stli_brds [ brdnr ] ;
if ( ! brdp )
return ( - ENODEV ) ;
if ( brdp - > state = = 0 )
return ( - ENODEV ) ;
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 ;
default :
rc = - ENOIOCTLCMD ;
break ;
}
return ( rc ) ;
}
static struct tty_operations stli_ops = {
. open = stli_open ,
. close = stli_close ,
. write = stli_write ,
. put_char = stli_putchar ,
. flush_chars = stli_flushchars ,
. write_room = stli_writeroom ,
. chars_in_buffer = stli_charsinbuffer ,
. ioctl = stli_ioctl ,
. set_termios = stli_settermios ,
. throttle = stli_throttle ,
. unthrottle = stli_unthrottle ,
. stop = stli_stop ,
. start = stli_start ,
. hangup = stli_hangup ,
. flush_buffer = stli_flushbuffer ,
. break_ctl = stli_breakctl ,
. wait_until_sent = stli_waituntilsent ,
. send_xchar = stli_sendxchar ,
. read_proc = stli_readproc ,
. tiocmget = stli_tiocmget ,
. tiocmset = stli_tiocmset ,
} ;
/*****************************************************************************/
int __init stli_init ( void )
{
int i ;
printk ( KERN_INFO " %s: version %s \n " , stli_drvtitle , stli_drvversion ) ;
stli_initbrds ( ) ;
stli_serial = alloc_tty_driver ( STL_MAXBRDS * STL_MAXPORTS ) ;
if ( ! stli_serial )
return - ENOMEM ;
/*
* Allocate a temporary write buffer .
*/
stli_tmpwritebuf = ( char * ) stli_memalloc ( STLI_TXBUFSIZE ) ;
if ( stli_tmpwritebuf = = ( char * ) NULL )
printk ( KERN_ERR " STALLION: failed to allocate memory "
" (size=%d) \n " , STLI_TXBUFSIZE ) ;
stli_txcookbuf = stli_memalloc ( STLI_TXBUFSIZE ) ;
if ( stli_txcookbuf = = ( char * ) NULL )
printk ( KERN_ERR " STALLION: failed to allocate memory "
" (size=%d) \n " , STLI_TXBUFSIZE ) ;
/*
* Set up a character driver for the shared memory region . We need this
* to down load the slave code image . Also it is a useful debugging tool .
*/
if ( register_chrdev ( STL_SIOMEMMAJOR , " staliomem " , & stli_fsiomem ) )
printk ( KERN_ERR " STALLION: failed to register serial memory "
" device \n " ) ;
devfs_mk_dir ( " staliomem " ) ;
2005-03-23 20:53:09 +03:00
istallion_class = class_create ( THIS_MODULE , " staliomem " ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 4 ; i + + ) {
devfs_mk_cdev ( MKDEV ( STL_SIOMEMMAJOR , i ) ,
S_IFCHR | S_IRUSR | S_IWUSR ,
" staliomem/%d " , i ) ;
2005-03-23 20:53:09 +03:00
class_device_create ( istallion_class , MKDEV ( STL_SIOMEMMAJOR , i ) ,
2005-04-17 02:20:36 +04:00
NULL , " staliomem%d " , i ) ;
}
/*
* Set up the tty driver structure and register us as a driver .
*/
stli_serial - > owner = THIS_MODULE ;
stli_serial - > driver_name = stli_drvname ;
stli_serial - > name = stli_serialname ;
stli_serial - > major = STL_SERIALMAJOR ;
stli_serial - > minor_start = 0 ;
stli_serial - > type = TTY_DRIVER_TYPE_SERIAL ;
stli_serial - > subtype = SERIAL_TYPE_NORMAL ;
stli_serial - > init_termios = stli_deftermios ;
stli_serial - > flags = TTY_DRIVER_REAL_RAW ;
tty_set_operations ( stli_serial , & stli_ops ) ;
if ( tty_register_driver ( stli_serial ) ) {
put_tty_driver ( stli_serial ) ;
printk ( KERN_ERR " STALLION: failed to register serial driver \n " ) ;
return - EBUSY ;
}
return ( 0 ) ;
}
/*****************************************************************************/