/*****************************************************************************/ /* * 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 02139, USA. */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PCI #include #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 2a0, * 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 = ARRAY_SIZE(stli_brdconf); /* * 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; #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_param_array(board0, charp, NULL, 0); MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,memaddr]"); module_param_array(board1, charp, NULL, 0); MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,memaddr]"); module_param_array(board2, charp, NULL, 0); MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,memaddr]"); module_param_array(board3, charp, NULL, 0); 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 1Mbyte, 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 1Gb, 2Gb and 3Gb 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 = ARRAY_SIZE(stli_eisamemprobeaddrs); /* * 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 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... */ static DEFINE_TIMER(stli_timerlist, stli_poll, 0, 0); static int stli_timeron; /* * Define the calculation for the timeout routine. */ #define STLI_TIMEOUT (jiffies + 1) /*****************************************************************************/ static struct class *istallion_class; #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); class_device_destroy(istallion_class, MKDEV(STL_SIOMEMMAJOR, i)); } devfs_remove("staliomem"); class_destroy(istallion_class); if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) printk("STALLION: failed to un-register serial memory device, " "errno=%d\n", -i); kfree(stli_tmpwritebuf); 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 i; #ifdef DEBUG printk("stli_argbrds()\n"); #endif for (i = stli_nrbrds; i < ARRAY_SIZE(stli_brdsp); 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 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); for (i = 0; i < ARRAY_SIZE(stli_brdstr); i++) { if (strcmp(stli_brdstr[i].name, argp[0]) == 0) break; } if (i == ARRAY_SIZE(stli_brdstr)) { 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 /*****************************************************************************/ 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