From: Greg Kroah-Hartman Date: Wed, 23 Feb 2011 00:57:21 +0000 (-0800) Subject: tty: move obsolete and broken tty drivers to drivers/staging/tty/ X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=4a6514e6d096716fb7bedf238efaaca877e2a7e8;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git tty: move obsolete and broken tty drivers to drivers/staging/tty/ As planned by Arnd Bergmann, this moves the following drivers to the drivers/staging/tty/ directory where they will be removed after 2.6.41 if no one steps up to claim them. epca epca ip2 istallion riscom8 serial167 specialix stallion Cc: Arnd Bergmann Cc: Alan Cox Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index bc9271b85759..a85e251c411f 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -554,14 +554,6 @@ config MVME147_SCC This is the driver for the serial ports on the Motorola MVME147 boards. Everyone using one of these boards should say Y here. -config SERIAL167 - bool "CD2401 support for MVME166/7 serial ports" - depends on MVME16x - help - This is the driver for the serial ports on the Motorola MVME166, - 167, and 172 boards. Everyone using one of these boards should say - Y here. - config MVME162_SCC bool "SCC support for MVME162 serial ports" depends on MVME16x && BROKEN diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 1adfac6a7b0b..7b8cf0295f6c 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -15,63 +15,6 @@ config DEVKMEM kind of kernel debugging operations. When in doubt, say "N". -config COMPUTONE - tristate "Computone IntelliPort Plus serial support" - depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) - ---help--- - This driver supports the entire family of Intelliport II/Plus - controllers with the exception of the MicroChannel controllers and - products previous to the Intelliport II. These are multiport cards, - which give you many serial ports. You would need something like this - to connect more than two modems to your Linux box, for instance in - order to become a dial-in server. If you have a card like that, say - Y here and read . - - To compile this driver as module, choose M here: the - module will be called ip2. - -config DIGIEPCA - tristate "Digiboard Intelligent Async Support" - depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) - ---help--- - This is a driver for Digi International's Xx, Xeve, and Xem series - of cards which provide multiple serial ports. You would need - something like this to connect more than two modems to your Linux - box, for instance in order to become a dial-in server. This driver - supports the original PC (ISA) boards as well as PCI, and EISA. If - you have a card like this, say Y here and read the file - . - - To compile this driver as a module, choose M here: the - module will be called epca. - -config RISCOM8 - tristate "SDL RISCom/8 card support" - depends on SERIAL_NONSTANDARD - help - This is a driver for the SDL Communications RISCom/8 multiport card, - which gives you many serial ports. You would need something like - this to connect more than two modems to your Linux box, for instance - in order to become a dial-in server. If you have a card like that, - say Y here and read the file . - - Also it's possible to say M here and compile this driver as kernel - loadable module; the module will be called riscom8. - -config SPECIALIX - tristate "Specialix IO8+ card support" - depends on SERIAL_NONSTANDARD - help - This is a driver for the Specialix IO8+ multiport card (both the - ISA and the PCI version) which gives you many serial ports. You - would need something like this to connect more than two modems to - your Linux box, for instance in order to become a dial-in server. - - If you have a card like that, say Y here and read the file - . Also it's possible to say - M here and compile this driver as kernel loadable module which will be - called specialix. - config SX tristate "Specialix SX (and SI) card support" depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) && BROKEN @@ -112,28 +55,6 @@ config STALDRV in this case. If you have never heard about all this, it's safe to say N. -config STALLION - tristate "Stallion EasyIO or EC8/32 support" - depends on STALDRV && (ISA || EISA || PCI) - help - If you have an EasyIO or EasyConnection 8/32 multiport Stallion - card, then this is for you; say Y. Make sure to read - . - - To compile this driver as a module, choose M here: the - module will be called stallion. - -config ISTALLION - tristate "Stallion EC8/64, ONboard, Brumby support" - depends on STALDRV && (ISA || EISA || PCI) - help - If you have an EasyConnection 8/64, ONboard, Brumby or Stallion - serial multiport card, say Y here. Make sure to read - . - - To compile this driver as a module, choose M here: the - module will be called istallion. - config A2232 tristate "Commodore A2232 serial support (EXPERIMENTAL)" depends on EXPERIMENTAL && ZORRO && BROKEN diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f5dc7c9bce6b..48bb8acbea49 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -8,15 +8,8 @@ obj-y += misc.o obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o -obj-$(CONFIG_SERIAL167) += serial167.o -obj-$(CONFIG_STALLION) += stallion.o -obj-$(CONFIG_ISTALLION) += istallion.o -obj-$(CONFIG_DIGIEPCA) += epca.o -obj-$(CONFIG_SPECIALIX) += specialix.o obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o -obj-$(CONFIG_COMPUTONE) += ip2/ -obj-$(CONFIG_RISCOM8) += riscom8.o obj-$(CONFIG_SX) += sx.o generic_serial.o obj-$(CONFIG_RIO) += rio/ generic_serial.o obj-$(CONFIG_RAW_DRIVER) += raw.o diff --git a/drivers/char/epca.c b/drivers/char/epca.c deleted file mode 100644 index 7ad3638967ae..000000000000 --- a/drivers/char/epca.c +++ /dev/null @@ -1,2784 +0,0 @@ -/* - Copyright (C) 1996 Digi International. - - For technical support please email digiLinux@dgii.com or - call Digi tech support at (612) 912-3456 - - ** This driver is no longer supported by Digi ** - - Much of this design and code came from epca.c which was - copyright (C) 1994, 1995 Troy De Jongh, and subsquently - modified by David Nugent, Christoph Lameter, Mike McLagan. - - 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. -*/ -/* See README.epca for change history --DAT*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "digiPCI.h" - - -#include "digi1.h" -#include "digiFep1.h" -#include "epca.h" -#include "epcaconfig.h" - -#define VERSION "1.3.0.1-LK2.6" - -/* This major needs to be submitted to Linux to join the majors list */ -#define DIGIINFOMAJOR 35 /* For Digi specific ioctl */ - - -#define MAXCARDS 7 -#define epcaassert(x, msg) if (!(x)) epca_error(__LINE__, msg) - -#define PFX "epca: " - -static int nbdevs, num_cards, liloconfig; -static int digi_poller_inhibited = 1 ; - -static int setup_error_code; -static int invalid_lilo_config; - -/* - * The ISA boards do window flipping into the same spaces so its only sane with - * a single lock. It's still pretty efficient. This lock guards the hardware - * and the tty_port lock guards the kernel side stuff like use counts. Take - * this lock inside the port lock if you must take both. - */ -static DEFINE_SPINLOCK(epca_lock); - -/* MAXBOARDS is typically 12, but ISA and EISA cards are restricted - to 7 below. */ -static struct board_info boards[MAXBOARDS]; - -static struct tty_driver *pc_driver; -static struct tty_driver *pc_info; - -/* ------------------ Begin Digi specific structures -------------------- */ - -/* - * digi_channels represents an array of structures that keep track of each - * channel of the Digi product. Information such as transmit and receive - * pointers, termio data, and signal definitions (DTR, CTS, etc ...) are stored - * here. This structure is NOT used to overlay the cards physical channel - * structure. - */ -static struct channel digi_channels[MAX_ALLOC]; - -/* - * card_ptr is an array used to hold the address of the first channel structure - * of each card. This array will hold the addresses of various channels located - * in digi_channels. - */ -static struct channel *card_ptr[MAXCARDS]; - -static struct timer_list epca_timer; - -/* - * Begin generic memory functions. These functions will be alias (point at) - * more specific functions dependent on the board being configured. - */ -static void memwinon(struct board_info *b, unsigned int win); -static void memwinoff(struct board_info *b, unsigned int win); -static void globalwinon(struct channel *ch); -static void rxwinon(struct channel *ch); -static void txwinon(struct channel *ch); -static void memoff(struct channel *ch); -static void assertgwinon(struct channel *ch); -static void assertmemoff(struct channel *ch); - -/* ---- Begin more 'specific' memory functions for cx_like products --- */ - -static void pcxem_memwinon(struct board_info *b, unsigned int win); -static void pcxem_memwinoff(struct board_info *b, unsigned int win); -static void pcxem_globalwinon(struct channel *ch); -static void pcxem_rxwinon(struct channel *ch); -static void pcxem_txwinon(struct channel *ch); -static void pcxem_memoff(struct channel *ch); - -/* ------ Begin more 'specific' memory functions for the pcxe ------- */ - -static void pcxe_memwinon(struct board_info *b, unsigned int win); -static void pcxe_memwinoff(struct board_info *b, unsigned int win); -static void pcxe_globalwinon(struct channel *ch); -static void pcxe_rxwinon(struct channel *ch); -static void pcxe_txwinon(struct channel *ch); -static void pcxe_memoff(struct channel *ch); - -/* ---- Begin more 'specific' memory functions for the pc64xe and pcxi ---- */ -/* Note : pc64xe and pcxi share the same windowing routines */ - -static void pcxi_memwinon(struct board_info *b, unsigned int win); -static void pcxi_memwinoff(struct board_info *b, unsigned int win); -static void pcxi_globalwinon(struct channel *ch); -static void pcxi_rxwinon(struct channel *ch); -static void pcxi_txwinon(struct channel *ch); -static void pcxi_memoff(struct channel *ch); - -/* - Begin 'specific' do nothing memory functions needed for some cards - */ - -static void dummy_memwinon(struct board_info *b, unsigned int win); -static void dummy_memwinoff(struct board_info *b, unsigned int win); -static void dummy_globalwinon(struct channel *ch); -static void dummy_rxwinon(struct channel *ch); -static void dummy_txwinon(struct channel *ch); -static void dummy_memoff(struct channel *ch); -static void dummy_assertgwinon(struct channel *ch); -static void dummy_assertmemoff(struct channel *ch); - -static struct channel *verifyChannel(struct tty_struct *); -static void pc_sched_event(struct channel *, int); -static void epca_error(int, char *); -static void pc_close(struct tty_struct *, struct file *); -static void shutdown(struct channel *, struct tty_struct *tty); -static void pc_hangup(struct tty_struct *); -static int pc_write_room(struct tty_struct *); -static int pc_chars_in_buffer(struct tty_struct *); -static void pc_flush_buffer(struct tty_struct *); -static void pc_flush_chars(struct tty_struct *); -static int pc_open(struct tty_struct *, struct file *); -static void post_fep_init(unsigned int crd); -static void epcapoll(unsigned long); -static void doevent(int); -static void fepcmd(struct channel *, int, int, int, int, int); -static unsigned termios2digi_h(struct channel *ch, unsigned); -static unsigned termios2digi_i(struct channel *ch, unsigned); -static unsigned termios2digi_c(struct channel *ch, unsigned); -static void epcaparam(struct tty_struct *, struct channel *); -static void receive_data(struct channel *, struct tty_struct *tty); -static int pc_ioctl(struct tty_struct *, - unsigned int, unsigned long); -static int info_ioctl(struct tty_struct *, - unsigned int, unsigned long); -static void pc_set_termios(struct tty_struct *, struct ktermios *); -static void do_softint(struct work_struct *work); -static void pc_stop(struct tty_struct *); -static void pc_start(struct tty_struct *); -static void pc_throttle(struct tty_struct *tty); -static void pc_unthrottle(struct tty_struct *tty); -static int pc_send_break(struct tty_struct *tty, int msec); -static void setup_empty_event(struct tty_struct *tty, struct channel *ch); - -static int pc_write(struct tty_struct *, const unsigned char *, int); -static int pc_init(void); -static int init_PCI(void); - -/* - * Table of functions for each board to handle memory. Mantaining parallelism - * is a *very* good idea here. The idea is for the runtime code to blindly call - * these functions, not knowing/caring about the underlying hardware. This - * stuff should contain no conditionals; if more functionality is needed a - * different entry should be established. These calls are the interface calls - * and are the only functions that should be accessed. Anyone caught making - * direct calls deserves what they get. - */ -static void memwinon(struct board_info *b, unsigned int win) -{ - b->memwinon(b, win); -} - -static void memwinoff(struct board_info *b, unsigned int win) -{ - b->memwinoff(b, win); -} - -static void globalwinon(struct channel *ch) -{ - ch->board->globalwinon(ch); -} - -static void rxwinon(struct channel *ch) -{ - ch->board->rxwinon(ch); -} - -static void txwinon(struct channel *ch) -{ - ch->board->txwinon(ch); -} - -static void memoff(struct channel *ch) -{ - ch->board->memoff(ch); -} -static void assertgwinon(struct channel *ch) -{ - ch->board->assertgwinon(ch); -} - -static void assertmemoff(struct channel *ch) -{ - ch->board->assertmemoff(ch); -} - -/* PCXEM windowing is the same as that used in the PCXR and CX series cards. */ -static void pcxem_memwinon(struct board_info *b, unsigned int win) -{ - outb_p(FEPWIN | win, b->port + 1); -} - -static void pcxem_memwinoff(struct board_info *b, unsigned int win) -{ - outb_p(0, b->port + 1); -} - -static void pcxem_globalwinon(struct channel *ch) -{ - outb_p(FEPWIN, (int)ch->board->port + 1); -} - -static void pcxem_rxwinon(struct channel *ch) -{ - outb_p(ch->rxwin, (int)ch->board->port + 1); -} - -static void pcxem_txwinon(struct channel *ch) -{ - outb_p(ch->txwin, (int)ch->board->port + 1); -} - -static void pcxem_memoff(struct channel *ch) -{ - outb_p(0, (int)ch->board->port + 1); -} - -/* ----------------- Begin pcxe memory window stuff ------------------ */ -static void pcxe_memwinon(struct board_info *b, unsigned int win) -{ - outb_p(FEPWIN | win, b->port + 1); -} - -static void pcxe_memwinoff(struct board_info *b, unsigned int win) -{ - outb_p(inb(b->port) & ~FEPMEM, b->port + 1); - outb_p(0, b->port + 1); -} - -static void pcxe_globalwinon(struct channel *ch) -{ - outb_p(FEPWIN, (int)ch->board->port + 1); -} - -static void pcxe_rxwinon(struct channel *ch) -{ - outb_p(ch->rxwin, (int)ch->board->port + 1); -} - -static void pcxe_txwinon(struct channel *ch) -{ - outb_p(ch->txwin, (int)ch->board->port + 1); -} - -static void pcxe_memoff(struct channel *ch) -{ - outb_p(0, (int)ch->board->port); - outb_p(0, (int)ch->board->port + 1); -} - -/* ------------- Begin pc64xe and pcxi memory window stuff -------------- */ -static void pcxi_memwinon(struct board_info *b, unsigned int win) -{ - outb_p(inb(b->port) | FEPMEM, b->port); -} - -static void pcxi_memwinoff(struct board_info *b, unsigned int win) -{ - outb_p(inb(b->port) & ~FEPMEM, b->port); -} - -static void pcxi_globalwinon(struct channel *ch) -{ - outb_p(FEPMEM, ch->board->port); -} - -static void pcxi_rxwinon(struct channel *ch) -{ - outb_p(FEPMEM, ch->board->port); -} - -static void pcxi_txwinon(struct channel *ch) -{ - outb_p(FEPMEM, ch->board->port); -} - -static void pcxi_memoff(struct channel *ch) -{ - outb_p(0, ch->board->port); -} - -static void pcxi_assertgwinon(struct channel *ch) -{ - epcaassert(inb(ch->board->port) & FEPMEM, "Global memory off"); -} - -static void pcxi_assertmemoff(struct channel *ch) -{ - epcaassert(!(inb(ch->board->port) & FEPMEM), "Memory on"); -} - -/* - * Not all of the cards need specific memory windowing routines. Some cards - * (Such as PCI) needs no windowing routines at all. We provide these do - * nothing routines so that the same code base can be used. The driver will - * ALWAYS call a windowing routine if it thinks it needs to; regardless of the - * card. However, dependent on the card the routine may or may not do anything. - */ -static void dummy_memwinon(struct board_info *b, unsigned int win) -{ -} - -static void dummy_memwinoff(struct board_info *b, unsigned int win) -{ -} - -static void dummy_globalwinon(struct channel *ch) -{ -} - -static void dummy_rxwinon(struct channel *ch) -{ -} - -static void dummy_txwinon(struct channel *ch) -{ -} - -static void dummy_memoff(struct channel *ch) -{ -} - -static void dummy_assertgwinon(struct channel *ch) -{ -} - -static void dummy_assertmemoff(struct channel *ch) -{ -} - -static struct channel *verifyChannel(struct tty_struct *tty) -{ - /* - * This routine basically provides a sanity check. It insures that the - * channel returned is within the proper range of addresses as well as - * properly initialized. If some bogus info gets passed in - * through tty->driver_data this should catch it. - */ - if (tty) { - struct channel *ch = tty->driver_data; - if (ch >= &digi_channels[0] && ch < &digi_channels[nbdevs]) { - if (ch->magic == EPCA_MAGIC) - return ch; - } - } - return NULL; -} - -static void pc_sched_event(struct channel *ch, int event) -{ - /* - * We call this to schedule interrupt processing on some event. The - * kernel sees our request and calls the related routine in OUR driver. - */ - ch->event |= 1 << event; - schedule_work(&ch->tqueue); -} - -static void epca_error(int line, char *msg) -{ - printk(KERN_ERR "epca_error (Digi): line = %d %s\n", line, msg); -} - -static void pc_close(struct tty_struct *tty, struct file *filp) -{ - struct channel *ch; - struct tty_port *port; - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - if (ch == NULL) - return; - port = &ch->port; - - if (tty_port_close_start(port, tty, filp) == 0) - return; - - pc_flush_buffer(tty); - shutdown(ch, tty); - - tty_port_close_end(port, tty); - ch->event = 0; /* FIXME: review ch->event locking */ - tty_port_tty_set(port, NULL); -} - -static void shutdown(struct channel *ch, struct tty_struct *tty) -{ - unsigned long flags; - struct board_chan __iomem *bc; - struct tty_port *port = &ch->port; - - if (!(port->flags & ASYNC_INITIALIZED)) - return; - - spin_lock_irqsave(&epca_lock, flags); - - globalwinon(ch); - bc = ch->brdchan; - - /* - * In order for an event to be generated on the receipt of data the - * idata flag must be set. Since we are shutting down, this is not - * necessary clear this flag. - */ - if (bc) - writeb(0, &bc->idata); - - /* If we're a modem control device and HUPCL is on, drop RTS & DTR. */ - if (tty->termios->c_cflag & HUPCL) { - ch->omodem &= ~(ch->m_rts | ch->m_dtr); - fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1); - } - memoff(ch); - - /* - * The channel has officialy been closed. The next time it is opened it - * will have to reinitialized. Set a flag to indicate this. - */ - /* Prevent future Digi programmed interrupts from coming active */ - port->flags &= ~ASYNC_INITIALIZED; - spin_unlock_irqrestore(&epca_lock, flags); -} - -static void pc_hangup(struct tty_struct *tty) -{ - struct channel *ch; - - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - if (ch != NULL) { - pc_flush_buffer(tty); - tty_ldisc_flush(tty); - shutdown(ch, tty); - - ch->event = 0; /* FIXME: review locking of ch->event */ - tty_port_hangup(&ch->port); - } -} - -static int pc_write(struct tty_struct *tty, - const unsigned char *buf, int bytesAvailable) -{ - unsigned int head, tail; - int dataLen; - int size; - int amountCopied; - struct channel *ch; - unsigned long flags; - int remain; - struct board_chan __iomem *bc; - - /* - * pc_write is primarily called directly by the kernel routine - * tty_write (Though it can also be called by put_char) found in - * tty_io.c. pc_write is passed a line discipline buffer where the data - * to be written out is stored. The line discipline implementation - * itself is done at the kernel level and is not brought into the - * driver. - */ - - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - if (ch == NULL) - return 0; - - /* Make a pointer to the channel data structure found on the board. */ - bc = ch->brdchan; - size = ch->txbufsize; - amountCopied = 0; - - spin_lock_irqsave(&epca_lock, flags); - globalwinon(ch); - - head = readw(&bc->tin) & (size - 1); - tail = readw(&bc->tout); - - if (tail != readw(&bc->tout)) - tail = readw(&bc->tout); - tail &= (size - 1); - - if (head >= tail) { - /* head has not wrapped */ - /* - * remain (much like dataLen above) represents the total amount - * of space available on the card for data. Here dataLen - * represents the space existing between the head pointer and - * the end of buffer. This is important because a memcpy cannot - * be told to automatically wrap around when it hits the buffer - * end. - */ - dataLen = size - head; - remain = size - (head - tail) - 1; - } else { - /* head has wrapped around */ - remain = tail - head - 1; - dataLen = remain; - } - /* - * Check the space on the card. If we have more data than space; reduce - * the amount of data to fit the space. - */ - bytesAvailable = min(remain, bytesAvailable); - txwinon(ch); - while (bytesAvailable > 0) { - /* there is data to copy onto card */ - - /* - * If head is not wrapped, the below will make sure the first - * data copy fills to the end of card buffer. - */ - dataLen = min(bytesAvailable, dataLen); - memcpy_toio(ch->txptr + head, buf, dataLen); - buf += dataLen; - head += dataLen; - amountCopied += dataLen; - bytesAvailable -= dataLen; - - if (head >= size) { - head = 0; - dataLen = tail; - } - } - ch->statusflags |= TXBUSY; - globalwinon(ch); - writew(head, &bc->tin); - - if ((ch->statusflags & LOWWAIT) == 0) { - ch->statusflags |= LOWWAIT; - writeb(1, &bc->ilow); - } - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - return amountCopied; -} - -static int pc_write_room(struct tty_struct *tty) -{ - int remain = 0; - struct channel *ch; - unsigned long flags; - unsigned int head, tail; - struct board_chan __iomem *bc; - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - if (ch != NULL) { - spin_lock_irqsave(&epca_lock, flags); - globalwinon(ch); - - bc = ch->brdchan; - head = readw(&bc->tin) & (ch->txbufsize - 1); - tail = readw(&bc->tout); - - if (tail != readw(&bc->tout)) - tail = readw(&bc->tout); - /* Wrap tail if necessary */ - tail &= (ch->txbufsize - 1); - remain = tail - head - 1; - if (remain < 0) - remain += ch->txbufsize; - - if (remain && (ch->statusflags & LOWWAIT) == 0) { - ch->statusflags |= LOWWAIT; - writeb(1, &bc->ilow); - } - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - } - /* Return how much room is left on card */ - return remain; -} - -static int pc_chars_in_buffer(struct tty_struct *tty) -{ - int chars; - unsigned int ctail, head, tail; - int remain; - unsigned long flags; - struct channel *ch; - struct board_chan __iomem *bc; - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - if (ch == NULL) - return 0; - - spin_lock_irqsave(&epca_lock, flags); - globalwinon(ch); - - bc = ch->brdchan; - tail = readw(&bc->tout); - head = readw(&bc->tin); - ctail = readw(&ch->mailbox->cout); - - if (tail == head && readw(&ch->mailbox->cin) == ctail && - readb(&bc->tbusy) == 0) - chars = 0; - else { /* Begin if some space on the card has been used */ - head = readw(&bc->tin) & (ch->txbufsize - 1); - tail &= (ch->txbufsize - 1); - /* - * The logic here is basically opposite of the above - * pc_write_room here we are finding the amount of bytes in the - * buffer filled. Not the amount of bytes empty. - */ - remain = tail - head - 1; - if (remain < 0) - remain += ch->txbufsize; - chars = (int)(ch->txbufsize - remain); - /* - * Make it possible to wakeup anything waiting for output in - * tty_ioctl.c, etc. - * - * If not already set. Setup an event to indicate when the - * transmit buffer empties. - */ - if (!(ch->statusflags & EMPTYWAIT)) - setup_empty_event(tty, ch); - } /* End if some space on the card has been used */ - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - /* Return number of characters residing on card. */ - return chars; -} - -static void pc_flush_buffer(struct tty_struct *tty) -{ - unsigned int tail; - unsigned long flags; - struct channel *ch; - struct board_chan __iomem *bc; - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - if (ch == NULL) - return; - - spin_lock_irqsave(&epca_lock, flags); - globalwinon(ch); - bc = ch->brdchan; - tail = readw(&bc->tout); - /* Have FEP move tout pointer; effectively flushing transmit buffer */ - fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0); - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - tty_wakeup(tty); -} - -static void pc_flush_chars(struct tty_struct *tty) -{ - struct channel *ch; - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - if (ch != NULL) { - unsigned long flags; - spin_lock_irqsave(&epca_lock, flags); - /* - * If not already set and the transmitter is busy setup an - * event to indicate when the transmit empties. - */ - if ((ch->statusflags & TXBUSY) && - !(ch->statusflags & EMPTYWAIT)) - setup_empty_event(tty, ch); - spin_unlock_irqrestore(&epca_lock, flags); - } -} - -static int epca_carrier_raised(struct tty_port *port) -{ - struct channel *ch = container_of(port, struct channel, port); - if (ch->imodem & ch->dcd) - return 1; - return 0; -} - -static void epca_dtr_rts(struct tty_port *port, int onoff) -{ -} - -static int pc_open(struct tty_struct *tty, struct file *filp) -{ - struct channel *ch; - struct tty_port *port; - unsigned long flags; - int line, retval, boardnum; - struct board_chan __iomem *bc; - unsigned int head; - - line = tty->index; - if (line < 0 || line >= nbdevs) - return -ENODEV; - - ch = &digi_channels[line]; - port = &ch->port; - boardnum = ch->boardnum; - - /* Check status of board configured in system. */ - - /* - * I check to see if the epca_setup routine detected a user error. It - * might be better to put this in pc_init, but for the moment it goes - * here. - */ - if (invalid_lilo_config) { - if (setup_error_code & INVALID_BOARD_TYPE) - printk(KERN_ERR "epca: pc_open: Invalid board type specified in kernel options.\n"); - if (setup_error_code & INVALID_NUM_PORTS) - printk(KERN_ERR "epca: pc_open: Invalid number of ports specified in kernel options.\n"); - if (setup_error_code & INVALID_MEM_BASE) - printk(KERN_ERR "epca: pc_open: Invalid board memory address specified in kernel options.\n"); - if (setup_error_code & INVALID_PORT_BASE) - printk(KERN_ERR "epca; pc_open: Invalid board port address specified in kernel options.\n"); - if (setup_error_code & INVALID_BOARD_STATUS) - printk(KERN_ERR "epca: pc_open: Invalid board status specified in kernel options.\n"); - if (setup_error_code & INVALID_ALTPIN) - printk(KERN_ERR "epca: pc_open: Invalid board altpin specified in kernel options;\n"); - tty->driver_data = NULL; /* Mark this device as 'down' */ - return -ENODEV; - } - if (boardnum >= num_cards || boards[boardnum].status == DISABLED) { - tty->driver_data = NULL; /* Mark this device as 'down' */ - return(-ENODEV); - } - - bc = ch->brdchan; - if (bc == NULL) { - tty->driver_data = NULL; - return -ENODEV; - } - - spin_lock_irqsave(&port->lock, flags); - /* - * Every time a channel is opened, increment a counter. This is - * necessary because we do not wish to flush and shutdown the channel - * until the last app holding the channel open, closes it. - */ - port->count++; - /* - * Set a kernel structures pointer to our local channel structure. This - * way we can get to it when passed only a tty struct. - */ - tty->driver_data = ch; - port->tty = tty; - /* - * If this is the first time the channel has been opened, initialize - * the tty->termios struct otherwise let pc_close handle it. - */ - spin_lock(&epca_lock); - globalwinon(ch); - ch->statusflags = 0; - - /* Save boards current modem status */ - ch->imodem = readb(&bc->mstat); - - /* - * Set receive head and tail ptrs to each other. This indicates no data - * available to read. - */ - head = readw(&bc->rin); - writew(head, &bc->rout); - - /* Set the channels associated tty structure */ - - /* - * The below routine generally sets up parity, baud, flow control - * issues, etc.... It effect both control flags and input flags. - */ - epcaparam(tty, ch); - memoff(ch); - spin_unlock(&epca_lock); - port->flags |= ASYNC_INITIALIZED; - spin_unlock_irqrestore(&port->lock, flags); - - retval = tty_port_block_til_ready(port, tty, filp); - if (retval) - return retval; - /* - * Set this again in case a hangup set it to zero while this open() was - * waiting for the line... - */ - spin_lock_irqsave(&port->lock, flags); - port->tty = tty; - spin_lock(&epca_lock); - globalwinon(ch); - /* Enable Digi Data events */ - writeb(1, &bc->idata); - memoff(ch); - spin_unlock(&epca_lock); - spin_unlock_irqrestore(&port->lock, flags); - return 0; -} - -static int __init epca_module_init(void) -{ - return pc_init(); -} -module_init(epca_module_init); - -static struct pci_driver epca_driver; - -static void __exit epca_module_exit(void) -{ - int count, crd; - struct board_info *bd; - struct channel *ch; - - del_timer_sync(&epca_timer); - - if (tty_unregister_driver(pc_driver) || - tty_unregister_driver(pc_info)) { - printk(KERN_WARNING "epca: cleanup_module failed to un-register tty driver\n"); - return; - } - put_tty_driver(pc_driver); - put_tty_driver(pc_info); - - for (crd = 0; crd < num_cards; crd++) { - bd = &boards[crd]; - if (!bd) { /* sanity check */ - printk(KERN_ERR " - Digi : cleanup_module failed\n"); - return; - } - ch = card_ptr[crd]; - for (count = 0; count < bd->numports; count++, ch++) { - struct tty_struct *tty = tty_port_tty_get(&ch->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - } - } - pci_unregister_driver(&epca_driver); -} -module_exit(epca_module_exit); - -static const struct tty_operations pc_ops = { - .open = pc_open, - .close = pc_close, - .write = pc_write, - .write_room = pc_write_room, - .flush_buffer = pc_flush_buffer, - .chars_in_buffer = pc_chars_in_buffer, - .flush_chars = pc_flush_chars, - .ioctl = pc_ioctl, - .set_termios = pc_set_termios, - .stop = pc_stop, - .start = pc_start, - .throttle = pc_throttle, - .unthrottle = pc_unthrottle, - .hangup = pc_hangup, - .break_ctl = pc_send_break -}; - -static const struct tty_port_operations epca_port_ops = { - .carrier_raised = epca_carrier_raised, - .dtr_rts = epca_dtr_rts, -}; - -static int info_open(struct tty_struct *tty, struct file *filp) -{ - return 0; -} - -static const struct tty_operations info_ops = { - .open = info_open, - .ioctl = info_ioctl, -}; - -static int __init pc_init(void) -{ - int crd; - struct board_info *bd; - unsigned char board_id = 0; - int err = -ENOMEM; - - int pci_boards_found, pci_count; - - pci_count = 0; - - pc_driver = alloc_tty_driver(MAX_ALLOC); - if (!pc_driver) - goto out1; - - pc_info = alloc_tty_driver(MAX_ALLOC); - if (!pc_info) - goto out2; - - /* - * If epca_setup has not been ran by LILO set num_cards to defaults; - * copy board structure defined by digiConfig into drivers board - * structure. Note : If LILO has ran epca_setup then epca_setup will - * handle defining num_cards as well as copying the data into the board - * structure. - */ - if (!liloconfig) { - /* driver has been configured via. epcaconfig */ - nbdevs = NBDEVS; - num_cards = NUMCARDS; - memcpy(&boards, &static_boards, - sizeof(struct board_info) * NUMCARDS); - } - - /* - * Note : If lilo was used to configure the driver and the ignore - * epcaconfig option was choosen (digiepca=2) then nbdevs and num_cards - * will equal 0 at this point. This is okay; PCI cards will still be - * picked up if detected. - */ - - /* - * Set up interrupt, we will worry about memory allocation in - * post_fep_init. - */ - printk(KERN_INFO "DIGI epca driver version %s loaded.\n", VERSION); - - /* - * NOTE : This code assumes that the number of ports found in the - * boards array is correct. This could be wrong if the card in question - * is PCI (And therefore has no ports entry in the boards structure.) - * The rest of the information will be valid for PCI because the - * beginning of pc_init scans for PCI and determines i/o and base - * memory addresses. I am not sure if it is possible to read the number - * of ports supported by the card prior to it being booted (Since that - * is the state it is in when pc_init is run). Because it is not - * possible to query the number of supported ports until after the card - * has booted; we are required to calculate the card_ptrs as the card - * is initialized (Inside post_fep_init). The negative thing about this - * approach is that digiDload's call to GET_INFO will have a bad port - * value. (Since this is called prior to post_fep_init.) - */ - pci_boards_found = 0; - if (num_cards < MAXBOARDS) - pci_boards_found += init_PCI(); - num_cards += pci_boards_found; - - pc_driver->owner = THIS_MODULE; - pc_driver->name = "ttyD"; - pc_driver->major = DIGI_MAJOR; - pc_driver->minor_start = 0; - pc_driver->type = TTY_DRIVER_TYPE_SERIAL; - pc_driver->subtype = SERIAL_TYPE_NORMAL; - pc_driver->init_termios = tty_std_termios; - pc_driver->init_termios.c_iflag = 0; - pc_driver->init_termios.c_oflag = 0; - pc_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; - pc_driver->init_termios.c_lflag = 0; - pc_driver->init_termios.c_ispeed = 9600; - pc_driver->init_termios.c_ospeed = 9600; - pc_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK; - tty_set_operations(pc_driver, &pc_ops); - - pc_info->owner = THIS_MODULE; - pc_info->name = "digi_ctl"; - pc_info->major = DIGIINFOMAJOR; - pc_info->minor_start = 0; - pc_info->type = TTY_DRIVER_TYPE_SERIAL; - pc_info->subtype = SERIAL_TYPE_INFO; - pc_info->init_termios = tty_std_termios; - pc_info->init_termios.c_iflag = 0; - pc_info->init_termios.c_oflag = 0; - pc_info->init_termios.c_lflag = 0; - pc_info->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; - pc_info->init_termios.c_ispeed = 9600; - pc_info->init_termios.c_ospeed = 9600; - pc_info->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(pc_info, &info_ops); - - - for (crd = 0; crd < num_cards; crd++) { - /* - * This is where the appropriate memory handlers for the - * hardware is set. Everything at runtime blindly jumps through - * these vectors. - */ - - /* defined in epcaconfig.h */ - bd = &boards[crd]; - - switch (bd->type) { - case PCXEM: - case EISAXEM: - bd->memwinon = pcxem_memwinon; - bd->memwinoff = pcxem_memwinoff; - bd->globalwinon = pcxem_globalwinon; - bd->txwinon = pcxem_txwinon; - bd->rxwinon = pcxem_rxwinon; - bd->memoff = pcxem_memoff; - bd->assertgwinon = dummy_assertgwinon; - bd->assertmemoff = dummy_assertmemoff; - break; - - case PCIXEM: - case PCIXRJ: - case PCIXR: - bd->memwinon = dummy_memwinon; - bd->memwinoff = dummy_memwinoff; - bd->globalwinon = dummy_globalwinon; - bd->txwinon = dummy_txwinon; - bd->rxwinon = dummy_rxwinon; - bd->memoff = dummy_memoff; - bd->assertgwinon = dummy_assertgwinon; - bd->assertmemoff = dummy_assertmemoff; - break; - - case PCXE: - case PCXEVE: - bd->memwinon = pcxe_memwinon; - bd->memwinoff = pcxe_memwinoff; - bd->globalwinon = pcxe_globalwinon; - bd->txwinon = pcxe_txwinon; - bd->rxwinon = pcxe_rxwinon; - bd->memoff = pcxe_memoff; - bd->assertgwinon = dummy_assertgwinon; - bd->assertmemoff = dummy_assertmemoff; - break; - - case PCXI: - case PC64XE: - bd->memwinon = pcxi_memwinon; - bd->memwinoff = pcxi_memwinoff; - bd->globalwinon = pcxi_globalwinon; - bd->txwinon = pcxi_txwinon; - bd->rxwinon = pcxi_rxwinon; - bd->memoff = pcxi_memoff; - bd->assertgwinon = pcxi_assertgwinon; - bd->assertmemoff = pcxi_assertmemoff; - break; - - default: - break; - } - - /* - * Some cards need a memory segment to be defined for use in - * transmit and receive windowing operations. These boards are - * listed in the below switch. In the case of the XI the amount - * of memory on the board is variable so the memory_seg is also - * variable. This code determines what they segment should be. - */ - switch (bd->type) { - case PCXE: - case PCXEVE: - case PC64XE: - bd->memory_seg = 0xf000; - break; - - case PCXI: - board_id = inb((int)bd->port); - if ((board_id & 0x1) == 0x1) { - /* it's an XI card */ - /* Is it a 64K board */ - if ((board_id & 0x30) == 0) - bd->memory_seg = 0xf000; - - /* Is it a 128K board */ - if ((board_id & 0x30) == 0x10) - bd->memory_seg = 0xe000; - - /* Is is a 256K board */ - if ((board_id & 0x30) == 0x20) - bd->memory_seg = 0xc000; - - /* Is it a 512K board */ - if ((board_id & 0x30) == 0x30) - bd->memory_seg = 0x8000; - } else - printk(KERN_ERR "epca: Board at 0x%x doesn't appear to be an XI\n", (int)bd->port); - break; - } - } - - err = tty_register_driver(pc_driver); - if (err) { - printk(KERN_ERR "Couldn't register Digi PC/ driver"); - goto out3; - } - - err = tty_register_driver(pc_info); - if (err) { - printk(KERN_ERR "Couldn't register Digi PC/ info "); - goto out4; - } - - /* Start up the poller to check for events on all enabled boards */ - init_timer(&epca_timer); - epca_timer.function = epcapoll; - mod_timer(&epca_timer, jiffies + HZ/25); - return 0; - -out4: - tty_unregister_driver(pc_driver); -out3: - put_tty_driver(pc_info); -out2: - put_tty_driver(pc_driver); -out1: - return err; -} - -static void post_fep_init(unsigned int crd) -{ - int i; - void __iomem *memaddr; - struct global_data __iomem *gd; - struct board_info *bd; - struct board_chan __iomem *bc; - struct channel *ch; - int shrinkmem = 0, lowwater; - - /* - * This call is made by the user via. the ioctl call DIGI_INIT. It is - * responsible for setting up all the card specific stuff. - */ - bd = &boards[crd]; - - /* - * If this is a PCI board, get the port info. Remember PCI cards do not - * have entries into the epcaconfig.h file, so we can't get the number - * of ports from it. Unfortunetly, this means that anyone doing a - * DIGI_GETINFO before the board has booted will get an invalid number - * of ports returned (It should return 0). Calls to DIGI_GETINFO after - * DIGI_INIT has been called will return the proper values. - */ - if (bd->type >= PCIXEM) { /* Begin get PCI number of ports */ - /* - * Below we use XEMPORTS as a memory offset regardless of which - * PCI card it is. This is because all of the supported PCI - * cards have the same memory offset for the channel data. This - * will have to be changed if we ever develop a PCI/XE card. - * NOTE : The FEP manual states that the port offset is 0xC22 - * as opposed to 0xC02. This is only true for PC/XE, and PC/XI - * cards; not for the XEM, or CX series. On the PCI cards the - * number of ports is determined by reading a ID PROM located - * in the box attached to the card. The card can then determine - * the index the id to determine the number of ports available. - * (FYI - The id should be located at 0x1ac (And may use up to - * 4 bytes if the box in question is a XEM or CX)). - */ - /* PCI cards are already remapped at this point ISA are not */ - bd->numports = readw(bd->re_map_membase + XEMPORTS); - epcaassert(bd->numports <= 64, "PCI returned a invalid number of ports"); - nbdevs += (bd->numports); - } else { - /* Fix up the mappings for ISA/EISA etc */ - /* FIXME: 64K - can we be smarter ? */ - bd->re_map_membase = ioremap_nocache(bd->membase, 0x10000); - } - - if (crd != 0) - card_ptr[crd] = card_ptr[crd-1] + boards[crd-1].numports; - else - card_ptr[crd] = &digi_channels[crd]; /* <- For card 0 only */ - - ch = card_ptr[crd]; - epcaassert(ch <= &digi_channels[nbdevs - 1], "ch out of range"); - - memaddr = bd->re_map_membase; - - /* - * The below assignment will set bc to point at the BEGINING of the - * cards channel structures. For 1 card there will be between 8 and 64 - * of these structures. - */ - bc = memaddr + CHANSTRUCT; - - /* - * The below assignment will set gd to point at the BEGINING of global - * memory address 0xc00. The first data in that global memory actually - * starts at address 0xc1a. The command in pointer begins at 0xd10. - */ - gd = memaddr + GLOBAL; - - /* - * XEPORTS (address 0xc22) points at the number of channels the card - * supports. (For 64XE, XI, XEM, and XR use 0xc02) - */ - if ((bd->type == PCXEVE || bd->type == PCXE) && - (readw(memaddr + XEPORTS) < 3)) - shrinkmem = 1; - if (bd->type < PCIXEM) - if (!request_region((int)bd->port, 4, board_desc[bd->type])) - return; - memwinon(bd, 0); - - /* - * Remember ch is the main drivers channels structure, while bc is the - * cards channel structure. - */ - for (i = 0; i < bd->numports; i++, ch++, bc++) { - unsigned long flags; - u16 tseg, rseg; - - tty_port_init(&ch->port); - ch->port.ops = &epca_port_ops; - ch->brdchan = bc; - ch->mailbox = gd; - INIT_WORK(&ch->tqueue, do_softint); - ch->board = &boards[crd]; - - spin_lock_irqsave(&epca_lock, flags); - switch (bd->type) { - /* - * Since some of the boards use different bitmaps for - * their control signals we cannot hard code these - * values and retain portability. We virtualize this - * data here. - */ - case EISAXEM: - case PCXEM: - case PCIXEM: - case PCIXRJ: - case PCIXR: - ch->m_rts = 0x02; - ch->m_dcd = 0x80; - ch->m_dsr = 0x20; - ch->m_cts = 0x10; - ch->m_ri = 0x40; - ch->m_dtr = 0x01; - break; - - case PCXE: - case PCXEVE: - case PCXI: - case PC64XE: - ch->m_rts = 0x02; - ch->m_dcd = 0x08; - ch->m_dsr = 0x10; - ch->m_cts = 0x20; - ch->m_ri = 0x40; - ch->m_dtr = 0x80; - break; - } - - if (boards[crd].altpin) { - ch->dsr = ch->m_dcd; - ch->dcd = ch->m_dsr; - ch->digiext.digi_flags |= DIGI_ALTPIN; - } else { - ch->dcd = ch->m_dcd; - ch->dsr = ch->m_dsr; - } - - ch->boardnum = crd; - ch->channelnum = i; - ch->magic = EPCA_MAGIC; - tty_port_tty_set(&ch->port, NULL); - - if (shrinkmem) { - fepcmd(ch, SETBUFFER, 32, 0, 0, 0); - shrinkmem = 0; - } - - tseg = readw(&bc->tseg); - rseg = readw(&bc->rseg); - - switch (bd->type) { - case PCIXEM: - case PCIXRJ: - case PCIXR: - /* Cover all the 2MEG cards */ - ch->txptr = memaddr + ((tseg << 4) & 0x1fffff); - ch->rxptr = memaddr + ((rseg << 4) & 0x1fffff); - ch->txwin = FEPWIN | (tseg >> 11); - ch->rxwin = FEPWIN | (rseg >> 11); - break; - - case PCXEM: - case EISAXEM: - /* Cover all the 32K windowed cards */ - /* Mask equal to window size - 1 */ - ch->txptr = memaddr + ((tseg << 4) & 0x7fff); - ch->rxptr = memaddr + ((rseg << 4) & 0x7fff); - ch->txwin = FEPWIN | (tseg >> 11); - ch->rxwin = FEPWIN | (rseg >> 11); - break; - - case PCXEVE: - case PCXE: - ch->txptr = memaddr + (((tseg - bd->memory_seg) << 4) - & 0x1fff); - ch->txwin = FEPWIN | ((tseg - bd->memory_seg) >> 9); - ch->rxptr = memaddr + (((rseg - bd->memory_seg) << 4) - & 0x1fff); - ch->rxwin = FEPWIN | ((rseg - bd->memory_seg) >> 9); - break; - - case PCXI: - case PC64XE: - ch->txptr = memaddr + ((tseg - bd->memory_seg) << 4); - ch->rxptr = memaddr + ((rseg - bd->memory_seg) << 4); - ch->txwin = ch->rxwin = 0; - break; - } - - ch->txbufhead = 0; - ch->txbufsize = readw(&bc->tmax) + 1; - - ch->rxbufhead = 0; - ch->rxbufsize = readw(&bc->rmax) + 1; - - lowwater = ch->txbufsize >= 2000 ? 1024 : (ch->txbufsize / 2); - - /* Set transmitter low water mark */ - fepcmd(ch, STXLWATER, lowwater, 0, 10, 0); - - /* Set receiver low water mark */ - fepcmd(ch, SRXLWATER, (ch->rxbufsize / 4), 0, 10, 0); - - /* Set receiver high water mark */ - fepcmd(ch, SRXHWATER, (3 * ch->rxbufsize / 4), 0, 10, 0); - - writew(100, &bc->edelay); - writeb(1, &bc->idata); - - ch->startc = readb(&bc->startc); - ch->stopc = readb(&bc->stopc); - ch->startca = readb(&bc->startca); - ch->stopca = readb(&bc->stopca); - - ch->fepcflag = 0; - ch->fepiflag = 0; - ch->fepoflag = 0; - ch->fepstartc = 0; - ch->fepstopc = 0; - ch->fepstartca = 0; - ch->fepstopca = 0; - - ch->port.close_delay = 50; - - spin_unlock_irqrestore(&epca_lock, flags); - } - - printk(KERN_INFO - "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", - VERSION, board_desc[bd->type], (long)bd->port, - (long)bd->membase, bd->numports); - memwinoff(bd, 0); -} - -static void epcapoll(unsigned long ignored) -{ - unsigned long flags; - int crd; - unsigned int head, tail; - struct channel *ch; - struct board_info *bd; - - /* - * This routine is called upon every timer interrupt. Even though the - * Digi series cards are capable of generating interrupts this method - * of non-looping polling is more efficient. This routine checks for - * card generated events (Such as receive data, are transmit buffer - * empty) and acts on those events. - */ - for (crd = 0; crd < num_cards; crd++) { - bd = &boards[crd]; - ch = card_ptr[crd]; - - if ((bd->status == DISABLED) || digi_poller_inhibited) - continue; - - /* - * assertmemoff is not needed here; indeed it is an empty - * subroutine. It is being kept because future boards may need - * this as well as some legacy boards. - */ - spin_lock_irqsave(&epca_lock, flags); - - assertmemoff(ch); - - globalwinon(ch); - - /* - * In this case head and tail actually refer to the event queue - * not the transmit or receive queue. - */ - head = readw(&ch->mailbox->ein); - tail = readw(&ch->mailbox->eout); - - /* If head isn't equal to tail we have an event */ - if (head != tail) - doevent(crd); - memoff(ch); - - spin_unlock_irqrestore(&epca_lock, flags); - } /* End for each card */ - mod_timer(&epca_timer, jiffies + (HZ / 25)); -} - -static void doevent(int crd) -{ - void __iomem *eventbuf; - struct channel *ch, *chan0; - static struct tty_struct *tty; - struct board_info *bd; - struct board_chan __iomem *bc; - unsigned int tail, head; - int event, channel; - int mstat, lstat; - - /* - * This subroutine is called by epcapoll when an event is detected - * in the event queue. This routine responds to those events. - */ - bd = &boards[crd]; - - chan0 = card_ptr[crd]; - epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range"); - assertgwinon(chan0); - while ((tail = readw(&chan0->mailbox->eout)) != - (head = readw(&chan0->mailbox->ein))) { - /* Begin while something in event queue */ - assertgwinon(chan0); - eventbuf = bd->re_map_membase + tail + ISTART; - /* Get the channel the event occurred on */ - channel = readb(eventbuf); - /* Get the actual event code that occurred */ - event = readb(eventbuf + 1); - /* - * The two assignments below get the current modem status - * (mstat) and the previous modem status (lstat). These are - * useful becuase an event could signal a change in modem - * signals itself. - */ - mstat = readb(eventbuf + 2); - lstat = readb(eventbuf + 3); - - ch = chan0 + channel; - if ((unsigned)channel >= bd->numports || !ch) { - if (channel >= bd->numports) - ch = chan0; - bc = ch->brdchan; - goto next; - } - - bc = ch->brdchan; - if (bc == NULL) - goto next; - - tty = tty_port_tty_get(&ch->port); - if (event & DATA_IND) { /* Begin DATA_IND */ - receive_data(ch, tty); - assertgwinon(ch); - } /* End DATA_IND */ - /* else *//* Fix for DCD transition missed bug */ - if (event & MODEMCHG_IND) { - /* A modem signal change has been indicated */ - ch->imodem = mstat; - if (test_bit(ASYNCB_CHECK_CD, &ch->port.flags)) { - /* We are now receiving dcd */ - if (mstat & ch->dcd) - wake_up_interruptible(&ch->port.open_wait); - else /* No dcd; hangup */ - pc_sched_event(ch, EPCA_EVENT_HANGUP); - } - } - if (tty) { - if (event & BREAK_IND) { - /* A break has been indicated */ - tty_insert_flip_char(tty, 0, TTY_BREAK); - tty_schedule_flip(tty); - } else if (event & LOWTX_IND) { - if (ch->statusflags & LOWWAIT) { - ch->statusflags &= ~LOWWAIT; - tty_wakeup(tty); - } - } else if (event & EMPTYTX_IND) { - /* This event is generated by - setup_empty_event */ - ch->statusflags &= ~TXBUSY; - if (ch->statusflags & EMPTYWAIT) { - ch->statusflags &= ~EMPTYWAIT; - tty_wakeup(tty); - } - } - tty_kref_put(tty); - } -next: - globalwinon(ch); - BUG_ON(!bc); - writew(1, &bc->idata); - writew((tail + 4) & (IMAX - ISTART - 4), &chan0->mailbox->eout); - globalwinon(chan0); - } /* End while something in event queue */ -} - -static void fepcmd(struct channel *ch, int cmd, int word_or_byte, - int byte2, int ncmds, int bytecmd) -{ - unchar __iomem *memaddr; - unsigned int head, cmdTail, cmdStart, cmdMax; - long count; - int n; - - /* This is the routine in which commands may be passed to the card. */ - - if (ch->board->status == DISABLED) - return; - assertgwinon(ch); - /* Remember head (As well as max) is just an offset not a base addr */ - head = readw(&ch->mailbox->cin); - /* cmdStart is a base address */ - cmdStart = readw(&ch->mailbox->cstart); - /* - * We do the addition below because we do not want a max pointer - * relative to cmdStart. We want a max pointer that points at the - * physical end of the command queue. - */ - cmdMax = (cmdStart + 4 + readw(&ch->mailbox->cmax)); - memaddr = ch->board->re_map_membase; - - if (head >= (cmdMax - cmdStart) || (head & 03)) { - printk(KERN_ERR "line %d: Out of range, cmd = %x, head = %x\n", - __LINE__, cmd, head); - printk(KERN_ERR "line %d: Out of range, cmdMax = %x, cmdStart = %x\n", - __LINE__, cmdMax, cmdStart); - return; - } - if (bytecmd) { - writeb(cmd, memaddr + head + cmdStart + 0); - writeb(ch->channelnum, memaddr + head + cmdStart + 1); - /* Below word_or_byte is bits to set */ - writeb(word_or_byte, memaddr + head + cmdStart + 2); - /* Below byte2 is bits to reset */ - writeb(byte2, memaddr + head + cmdStart + 3); - } else { - writeb(cmd, memaddr + head + cmdStart + 0); - writeb(ch->channelnum, memaddr + head + cmdStart + 1); - writeb(word_or_byte, memaddr + head + cmdStart + 2); - } - head = (head + 4) & (cmdMax - cmdStart - 4); - writew(head, &ch->mailbox->cin); - count = FEPTIMEOUT; - - for (;;) { - count--; - if (count == 0) { - printk(KERN_ERR " - Fep not responding in fepcmd()\n"); - return; - } - head = readw(&ch->mailbox->cin); - cmdTail = readw(&ch->mailbox->cout); - n = (head - cmdTail) & (cmdMax - cmdStart - 4); - /* - * Basically this will break when the FEP acknowledges the - * command by incrementing cmdTail (Making it equal to head). - */ - if (n <= ncmds * (sizeof(short) * 4)) - break; - } -} - -/* - * Digi products use fields in their channels structures that are very similar - * to the c_cflag and c_iflag fields typically found in UNIX termios - * structures. The below three routines allow mappings between these hardware - * "flags" and their respective Linux flags. - */ -static unsigned termios2digi_h(struct channel *ch, unsigned cflag) -{ - unsigned res = 0; - - if (cflag & CRTSCTS) { - ch->digiext.digi_flags |= (RTSPACE | CTSPACE); - res |= ((ch->m_cts) | (ch->m_rts)); - } - - if (ch->digiext.digi_flags & RTSPACE) - res |= ch->m_rts; - - if (ch->digiext.digi_flags & DTRPACE) - res |= ch->m_dtr; - - if (ch->digiext.digi_flags & CTSPACE) - res |= ch->m_cts; - - if (ch->digiext.digi_flags & DSRPACE) - res |= ch->dsr; - - if (ch->digiext.digi_flags & DCDPACE) - res |= ch->dcd; - - if (res & (ch->m_rts)) - ch->digiext.digi_flags |= RTSPACE; - - if (res & (ch->m_cts)) - ch->digiext.digi_flags |= CTSPACE; - - return res; -} - -static unsigned termios2digi_i(struct channel *ch, unsigned iflag) -{ - unsigned res = iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | - INPCK | ISTRIP | IXON | IXANY | IXOFF); - if (ch->digiext.digi_flags & DIGI_AIXON) - res |= IAIXON; - return res; -} - -static unsigned termios2digi_c(struct channel *ch, unsigned cflag) -{ - unsigned res = 0; - if (cflag & CBAUDEX) { - ch->digiext.digi_flags |= DIGI_FAST; - /* - * HUPCL bit is used by FEP to indicate fast baud table is to - * be used. - */ - res |= FEP_HUPCL; - } else - ch->digiext.digi_flags &= ~DIGI_FAST; - /* - * CBAUD has bit position 0x1000 set these days to indicate Linux - * baud rate remap. Digi hardware can't handle the bit assignment. - * (We use a different bit assignment for high speed.). Clear this - * bit out. - */ - res |= cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | CSTOPB | CSIZE); - /* - * This gets a little confusing. The Digi cards have their own - * representation of c_cflags controlling baud rate. For the most part - * this is identical to the Linux implementation. However; Digi - * supports one rate (76800) that Linux doesn't. This means that the - * c_cflag entry that would normally mean 76800 for Digi actually means - * 115200 under Linux. Without the below mapping, a stty 115200 would - * only drive the board at 76800. Since the rate 230400 is also found - * after 76800, the same problem afflicts us when we choose a rate of - * 230400. Without the below modificiation stty 230400 would actually - * give us 115200. - * - * There are two additional differences. The Linux value for CLOCAL - * (0x800; 0004000) has no meaning to the Digi hardware. Also in later - * releases of Linux; the CBAUD define has CBAUDEX (0x1000; 0010000) - * ored into it (CBAUD = 0x100f as opposed to 0xf). CBAUDEX should be - * checked for a screened out prior to termios2digi_c returning. Since - * CLOCAL isn't used by the board this can be ignored as long as the - * returned value is used only by Digi hardware. - */ - if (cflag & CBAUDEX) { - /* - * The below code is trying to guarantee that only baud rates - * 115200 and 230400 are remapped. We use exclusive or because - * the various baud rates share common bit positions and - * therefore can't be tested for easily. - */ - if ((!((cflag & 0x7) ^ (B115200 & ~CBAUDEX))) || - (!((cflag & 0x7) ^ (B230400 & ~CBAUDEX)))) - res += 1; - } - return res; -} - -/* Caller must hold the locks */ -static void epcaparam(struct tty_struct *tty, struct channel *ch) -{ - unsigned int cmdHead; - struct ktermios *ts; - struct board_chan __iomem *bc; - unsigned mval, hflow, cflag, iflag; - - bc = ch->brdchan; - epcaassert(bc != NULL, "bc out of range"); - - assertgwinon(ch); - ts = tty->termios; - if ((ts->c_cflag & CBAUD) == 0) { /* Begin CBAUD detected */ - cmdHead = readw(&bc->rin); - writew(cmdHead, &bc->rout); - cmdHead = readw(&bc->tin); - /* Changing baud in mid-stream transmission can be wonderful */ - /* - * Flush current transmit buffer by setting cmdTail pointer - * (tout) to cmdHead pointer (tin). Hopefully the transmit - * buffer is empty. - */ - fepcmd(ch, STOUT, (unsigned) cmdHead, 0, 0, 0); - mval = 0; - } else { /* Begin CBAUD not detected */ - /* - * c_cflags have changed but that change had nothing to do with - * BAUD. Propagate the change to the card. - */ - cflag = termios2digi_c(ch, ts->c_cflag); - if (cflag != ch->fepcflag) { - ch->fepcflag = cflag; - /* Set baud rate, char size, stop bits, parity */ - fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0); - } - /* - * If the user has not forced CLOCAL and if the device is not a - * CALLOUT device (Which is always CLOCAL) we set flags such - * that the driver will wait on carrier detect. - */ - if (ts->c_cflag & CLOCAL) - clear_bit(ASYNCB_CHECK_CD, &ch->port.flags); - else - set_bit(ASYNCB_CHECK_CD, &ch->port.flags); - mval = ch->m_dtr | ch->m_rts; - } /* End CBAUD not detected */ - iflag = termios2digi_i(ch, ts->c_iflag); - /* Check input mode flags */ - if (iflag != ch->fepiflag) { - ch->fepiflag = iflag; - /* - * Command sets channels iflag structure on the board. Such - * things as input soft flow control, handling of parity - * errors, and break handling are all set here. - * - * break handling, parity handling, input stripping, - * flow control chars - */ - fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0); - } - /* - * Set the board mint value for this channel. This will cause hardware - * events to be generated each time the DCD signal (Described in mint) - * changes. - */ - writeb(ch->dcd, &bc->mint); - if ((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD)) - if (ch->digiext.digi_flags & DIGI_FORCEDCD) - writeb(0, &bc->mint); - ch->imodem = readb(&bc->mstat); - hflow = termios2digi_h(ch, ts->c_cflag); - if (hflow != ch->hflow) { - ch->hflow = hflow; - /* - * Hard flow control has been selected but the board is not - * using it. Activate hard flow control now. - */ - fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1); - } - mval ^= ch->modemfake & (mval ^ ch->modem); - - if (ch->omodem ^ mval) { - ch->omodem = mval; - /* - * The below command sets the DTR and RTS mstat structure. If - * hard flow control is NOT active these changes will drive the - * output of the actual DTR and RTS lines. If hard flow control - * is active, the changes will be saved in the mstat structure - * and only asserted when hard flow control is turned off. - */ - - /* First reset DTR & RTS; then set them */ - fepcmd(ch, SETMODEM, 0, ((ch->m_dtr)|(ch->m_rts)), 0, 1); - fepcmd(ch, SETMODEM, mval, 0, 0, 1); - } - if (ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc) { - ch->fepstartc = ch->startc; - ch->fepstopc = ch->stopc; - /* - * The XON / XOFF characters have changed; propagate these - * changes to the card. - */ - fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); - } - if (ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca) { - ch->fepstartca = ch->startca; - ch->fepstopca = ch->stopca; - /* - * Similar to the above, this time the auxilarly XON / XOFF - * characters have changed; propagate these changes to the card. - */ - fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); - } -} - -/* Caller holds lock */ -static void receive_data(struct channel *ch, struct tty_struct *tty) -{ - unchar *rptr; - struct ktermios *ts = NULL; - struct board_chan __iomem *bc; - int dataToRead, wrapgap, bytesAvailable; - unsigned int tail, head; - unsigned int wrapmask; - - /* - * This routine is called by doint when a receive data event has taken - * place. - */ - globalwinon(ch); - if (ch->statusflags & RXSTOPPED) - return; - if (tty) - ts = tty->termios; - bc = ch->brdchan; - BUG_ON(!bc); - wrapmask = ch->rxbufsize - 1; - - /* - * Get the head and tail pointers to the receiver queue. Wrap the head - * pointer if it has reached the end of the buffer. - */ - head = readw(&bc->rin); - head &= wrapmask; - tail = readw(&bc->rout) & wrapmask; - - bytesAvailable = (head - tail) & wrapmask; - if (bytesAvailable == 0) - return; - - /* If CREAD bit is off or device not open, set TX tail to head */ - if (!tty || !ts || !(ts->c_cflag & CREAD)) { - writew(head, &bc->rout); - return; - } - - if (tty_buffer_request_room(tty, bytesAvailable + 1) == 0) - return; - - if (readb(&bc->orun)) { - writeb(0, &bc->orun); - printk(KERN_WARNING "epca; overrun! DigiBoard device %s\n", - tty->name); - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - rxwinon(ch); - while (bytesAvailable > 0) { - /* Begin while there is data on the card */ - wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail; - /* - * Even if head has wrapped around only report the amount of - * data to be equal to the size - tail. Remember memcpy can't - * automaticly wrap around the receive buffer. - */ - dataToRead = (wrapgap < bytesAvailable) ? wrapgap - : bytesAvailable; - /* Make sure we don't overflow the buffer */ - dataToRead = tty_prepare_flip_string(tty, &rptr, dataToRead); - if (dataToRead == 0) - break; - /* - * Move data read from our card into the line disciplines - * buffer for translation if necessary. - */ - memcpy_fromio(rptr, ch->rxptr + tail, dataToRead); - tail = (tail + dataToRead) & wrapmask; - bytesAvailable -= dataToRead; - } /* End while there is data on the card */ - globalwinon(ch); - writew(tail, &bc->rout); - /* Must be called with global data */ - tty_schedule_flip(tty); -} - -static int info_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case DIGI_GETINFO: - { - struct digi_info di; - int brd; - - if (get_user(brd, (unsigned int __user *)arg)) - return -EFAULT; - if (brd < 0 || brd >= num_cards || num_cards == 0) - return -ENODEV; - - memset(&di, 0, sizeof(di)); - - di.board = brd; - di.status = boards[brd].status; - di.type = boards[brd].type ; - di.numports = boards[brd].numports ; - /* Legacy fixups - just move along nothing to see */ - di.port = (unsigned char *)boards[brd].port ; - di.membase = (unsigned char *)boards[brd].membase ; - - if (copy_to_user((void __user *)arg, &di, sizeof(di))) - return -EFAULT; - break; - - } - - case DIGI_POLLER: - { - int brd = arg & 0xff000000 >> 16; - unsigned char state = arg & 0xff; - - if (brd < 0 || brd >= num_cards) { - printk(KERN_ERR "epca: DIGI POLLER : brd not valid!\n"); - return -ENODEV; - } - digi_poller_inhibited = state; - break; - } - - case DIGI_INIT: - { - /* - * This call is made by the apps to complete the - * initialization of the board(s). This routine is - * responsible for setting the card to its initial - * state and setting the drivers control fields to the - * sutianle settings for the card in question. - */ - int crd; - for (crd = 0; crd < num_cards; crd++) - post_fep_init(crd); - break; - } - default: - return -ENOTTY; - } - return 0; -} - -static int pc_tiocmget(struct tty_struct *tty) -{ - struct channel *ch = tty->driver_data; - struct board_chan __iomem *bc; - unsigned int mstat, mflag = 0; - unsigned long flags; - - if (ch) - bc = ch->brdchan; - else - return -EINVAL; - - spin_lock_irqsave(&epca_lock, flags); - globalwinon(ch); - mstat = readb(&bc->mstat); - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - - if (mstat & ch->m_dtr) - mflag |= TIOCM_DTR; - if (mstat & ch->m_rts) - mflag |= TIOCM_RTS; - if (mstat & ch->m_cts) - mflag |= TIOCM_CTS; - if (mstat & ch->dsr) - mflag |= TIOCM_DSR; - if (mstat & ch->m_ri) - mflag |= TIOCM_RI; - if (mstat & ch->dcd) - mflag |= TIOCM_CD; - return mflag; -} - -static int pc_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct channel *ch = tty->driver_data; - unsigned long flags; - - if (!ch) - return -EINVAL; - - spin_lock_irqsave(&epca_lock, flags); - /* - * I think this modemfake stuff is broken. It doesn't correctly reflect - * the behaviour desired by the TIOCM* ioctls. Therefore this is - * probably broken. - */ - if (set & TIOCM_RTS) { - ch->modemfake |= ch->m_rts; - ch->modem |= ch->m_rts; - } - if (set & TIOCM_DTR) { - ch->modemfake |= ch->m_dtr; - ch->modem |= ch->m_dtr; - } - if (clear & TIOCM_RTS) { - ch->modemfake |= ch->m_rts; - ch->modem &= ~ch->m_rts; - } - if (clear & TIOCM_DTR) { - ch->modemfake |= ch->m_dtr; - ch->modem &= ~ch->m_dtr; - } - globalwinon(ch); - /* - * The below routine generally sets up parity, baud, flow control - * issues, etc.... It effect both control flags and input flags. - */ - epcaparam(tty, ch); - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - return 0; -} - -static int pc_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - digiflow_t dflow; - unsigned long flags; - unsigned int mflag, mstat; - unsigned char startc, stopc; - struct board_chan __iomem *bc; - struct channel *ch = tty->driver_data; - void __user *argp = (void __user *)arg; - - if (ch) - bc = ch->brdchan; - else - return -EINVAL; - switch (cmd) { - case TIOCMODG: - mflag = pc_tiocmget(tty); - if (put_user(mflag, (unsigned long __user *)argp)) - return -EFAULT; - break; - case TIOCMODS: - if (get_user(mstat, (unsigned __user *)argp)) - return -EFAULT; - return pc_tiocmset(tty, mstat, ~mstat); - case TIOCSDTR: - spin_lock_irqsave(&epca_lock, flags); - ch->omodem |= ch->m_dtr; - globalwinon(ch); - fepcmd(ch, SETMODEM, ch->m_dtr, 0, 10, 1); - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - break; - - case TIOCCDTR: - spin_lock_irqsave(&epca_lock, flags); - ch->omodem &= ~ch->m_dtr; - globalwinon(ch); - fepcmd(ch, SETMODEM, 0, ch->m_dtr, 10, 1); - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - break; - case DIGI_GETA: - if (copy_to_user(argp, &ch->digiext, sizeof(digi_t))) - return -EFAULT; - break; - case DIGI_SETAW: - case DIGI_SETAF: - if (cmd == DIGI_SETAW) { - /* Setup an event to indicate when the transmit - buffer empties */ - spin_lock_irqsave(&epca_lock, flags); - setup_empty_event(tty, ch); - spin_unlock_irqrestore(&epca_lock, flags); - tty_wait_until_sent(tty, 0); - } else { - /* ldisc lock already held in ioctl */ - if (tty->ldisc->ops->flush_buffer) - tty->ldisc->ops->flush_buffer(tty); - } - /* Fall Thru */ - case DIGI_SETA: - if (copy_from_user(&ch->digiext, argp, sizeof(digi_t))) - return -EFAULT; - - if (ch->digiext.digi_flags & DIGI_ALTPIN) { - ch->dcd = ch->m_dsr; - ch->dsr = ch->m_dcd; - } else { - ch->dcd = ch->m_dcd; - ch->dsr = ch->m_dsr; - } - - spin_lock_irqsave(&epca_lock, flags); - globalwinon(ch); - - /* - * The below routine generally sets up parity, baud, flow - * control issues, etc.... It effect both control flags and - * input flags. - */ - epcaparam(tty, ch); - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - break; - - case DIGI_GETFLOW: - case DIGI_GETAFLOW: - spin_lock_irqsave(&epca_lock, flags); - globalwinon(ch); - if (cmd == DIGI_GETFLOW) { - dflow.startc = readb(&bc->startc); - dflow.stopc = readb(&bc->stopc); - } else { - dflow.startc = readb(&bc->startca); - dflow.stopc = readb(&bc->stopca); - } - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - - if (copy_to_user(argp, &dflow, sizeof(dflow))) - return -EFAULT; - break; - - case DIGI_SETAFLOW: - case DIGI_SETFLOW: - if (cmd == DIGI_SETFLOW) { - startc = ch->startc; - stopc = ch->stopc; - } else { - startc = ch->startca; - stopc = ch->stopca; - } - - if (copy_from_user(&dflow, argp, sizeof(dflow))) - return -EFAULT; - - if (dflow.startc != startc || dflow.stopc != stopc) { - /* Begin if setflow toggled */ - spin_lock_irqsave(&epca_lock, flags); - globalwinon(ch); - - if (cmd == DIGI_SETFLOW) { - ch->fepstartc = ch->startc = dflow.startc; - ch->fepstopc = ch->stopc = dflow.stopc; - fepcmd(ch, SONOFFC, ch->fepstartc, - ch->fepstopc, 0, 1); - } else { - ch->fepstartca = ch->startca = dflow.startc; - ch->fepstopca = ch->stopca = dflow.stopc; - fepcmd(ch, SAUXONOFFC, ch->fepstartca, - ch->fepstopca, 0, 1); - } - - if (ch->statusflags & TXSTOPPED) - pc_start(tty); - - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - } /* End if setflow toggled */ - break; - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static void pc_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct channel *ch; - unsigned long flags; - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - - if (ch != NULL) { /* Begin if channel valid */ - spin_lock_irqsave(&epca_lock, flags); - globalwinon(ch); - epcaparam(tty, ch); - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - - if ((old_termios->c_cflag & CRTSCTS) && - ((tty->termios->c_cflag & CRTSCTS) == 0)) - tty->hw_stopped = 0; - - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&ch->port.open_wait); - - } /* End if channel valid */ -} - -static void do_softint(struct work_struct *work) -{ - struct channel *ch = container_of(work, struct channel, tqueue); - /* Called in response to a modem change event */ - if (ch && ch->magic == EPCA_MAGIC) { - struct tty_struct *tty = tty_port_tty_get(&ch->port); - - if (tty && tty->driver_data) { - if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) { - tty_hangup(tty); - wake_up_interruptible(&ch->port.open_wait); - clear_bit(ASYNCB_NORMAL_ACTIVE, - &ch->port.flags); - } - } - tty_kref_put(tty); - } -} - -/* - * pc_stop and pc_start provide software flow control to the routine and the - * pc_ioctl routine. - */ -static void pc_stop(struct tty_struct *tty) -{ - struct channel *ch; - unsigned long flags; - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - if (ch != NULL) { - spin_lock_irqsave(&epca_lock, flags); - if ((ch->statusflags & TXSTOPPED) == 0) { - /* Begin if transmit stop requested */ - globalwinon(ch); - /* STOP transmitting now !! */ - fepcmd(ch, PAUSETX, 0, 0, 0, 0); - ch->statusflags |= TXSTOPPED; - memoff(ch); - } /* End if transmit stop requested */ - spin_unlock_irqrestore(&epca_lock, flags); - } -} - -static void pc_start(struct tty_struct *tty) -{ - struct channel *ch; - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - if (ch != NULL) { - unsigned long flags; - spin_lock_irqsave(&epca_lock, flags); - /* Just in case output was resumed because of a change - in Digi-flow */ - if (ch->statusflags & TXSTOPPED) { - /* Begin transmit resume requested */ - struct board_chan __iomem *bc; - globalwinon(ch); - bc = ch->brdchan; - if (ch->statusflags & LOWWAIT) - writeb(1, &bc->ilow); - /* Okay, you can start transmitting again... */ - fepcmd(ch, RESUMETX, 0, 0, 0, 0); - ch->statusflags &= ~TXSTOPPED; - memoff(ch); - } /* End transmit resume requested */ - spin_unlock_irqrestore(&epca_lock, flags); - } -} - -/* - * The below routines pc_throttle and pc_unthrottle are used to slow (And - * resume) the receipt of data into the kernels receive buffers. The exact - * occurrence of this depends on the size of the kernels receive buffer and - * what the 'watermarks' are set to for that buffer. See the n_ttys.c file for - * more details. - */ -static void pc_throttle(struct tty_struct *tty) -{ - struct channel *ch; - unsigned long flags; - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - if (ch != NULL) { - spin_lock_irqsave(&epca_lock, flags); - if ((ch->statusflags & RXSTOPPED) == 0) { - globalwinon(ch); - fepcmd(ch, PAUSERX, 0, 0, 0, 0); - ch->statusflags |= RXSTOPPED; - memoff(ch); - } - spin_unlock_irqrestore(&epca_lock, flags); - } -} - -static void pc_unthrottle(struct tty_struct *tty) -{ - struct channel *ch; - unsigned long flags; - /* - * verifyChannel returns the channel from the tty struct if it is - * valid. This serves as a sanity check. - */ - ch = verifyChannel(tty); - if (ch != NULL) { - /* Just in case output was resumed because of a change - in Digi-flow */ - spin_lock_irqsave(&epca_lock, flags); - if (ch->statusflags & RXSTOPPED) { - globalwinon(ch); - fepcmd(ch, RESUMERX, 0, 0, 0, 0); - ch->statusflags &= ~RXSTOPPED; - memoff(ch); - } - spin_unlock_irqrestore(&epca_lock, flags); - } -} - -static int pc_send_break(struct tty_struct *tty, int msec) -{ - struct channel *ch = tty->driver_data; - unsigned long flags; - - if (msec == -1) - msec = 0xFFFF; - else if (msec > 0xFFFE) - msec = 0xFFFE; - else if (msec < 1) - msec = 1; - - spin_lock_irqsave(&epca_lock, flags); - globalwinon(ch); - /* - * Maybe I should send an infinite break here, schedule() for msec - * amount of time, and then stop the break. This way, the user can't - * screw up the FEP by causing digi_send_break() to be called (i.e. via - * an ioctl()) more than once in msec amount of time. - * Try this for now... - */ - fepcmd(ch, SENDBREAK, msec, 0, 10, 0); - memoff(ch); - spin_unlock_irqrestore(&epca_lock, flags); - return 0; -} - -/* Caller MUST hold the lock */ -static void setup_empty_event(struct tty_struct *tty, struct channel *ch) -{ - struct board_chan __iomem *bc = ch->brdchan; - - globalwinon(ch); - ch->statusflags |= EMPTYWAIT; - /* - * When set the iempty flag request a event to be generated when the - * transmit buffer is empty (If there is no BREAK in progress). - */ - writeb(1, &bc->iempty); - memoff(ch); -} - -#ifndef MODULE -static void __init epca_setup(char *str, int *ints) -{ - struct board_info board; - int index, loop, last; - char *temp, *t2; - unsigned len; - - /* - * If this routine looks a little strange it is because it is only - * called if a LILO append command is given to boot the kernel with - * parameters. In this way, we can provide the user a method of - * changing his board configuration without rebuilding the kernel. - */ - if (!liloconfig) - liloconfig = 1; - - memset(&board, 0, sizeof(board)); - - /* Assume the data is int first, later we can change it */ - /* I think that array position 0 of ints holds the number of args */ - for (last = 0, index = 1; index <= ints[0]; index++) - switch (index) { /* Begin parse switch */ - case 1: - board.status = ints[index]; - /* - * We check for 2 (As opposed to 1; because 2 is a flag - * instructing the driver to ignore epcaconfig.) For - * this reason we check for 2. - */ - if (board.status == 2) { - /* Begin ignore epcaconfig as well as lilo cmd line */ - nbdevs = 0; - num_cards = 0; - return; - } /* End ignore epcaconfig as well as lilo cmd line */ - - if (board.status > 2) { - printk(KERN_ERR "epca_setup: Invalid board status 0x%x\n", - board.status); - invalid_lilo_config = 1; - setup_error_code |= INVALID_BOARD_STATUS; - return; - } - last = index; - break; - case 2: - board.type = ints[index]; - if (board.type >= PCIXEM) { - printk(KERN_ERR "epca_setup: Invalid board type 0x%x\n", board.type); - invalid_lilo_config = 1; - setup_error_code |= INVALID_BOARD_TYPE; - return; - } - last = index; - break; - case 3: - board.altpin = ints[index]; - if (board.altpin > 1) { - printk(KERN_ERR "epca_setup: Invalid board altpin 0x%x\n", board.altpin); - invalid_lilo_config = 1; - setup_error_code |= INVALID_ALTPIN; - return; - } - last = index; - break; - - case 4: - board.numports = ints[index]; - if (board.numports < 2 || board.numports > 256) { - printk(KERN_ERR "epca_setup: Invalid board numports 0x%x\n", board.numports); - invalid_lilo_config = 1; - setup_error_code |= INVALID_NUM_PORTS; - return; - } - nbdevs += board.numports; - last = index; - break; - - case 5: - board.port = ints[index]; - if (ints[index] <= 0) { - printk(KERN_ERR "epca_setup: Invalid io port 0x%x\n", (unsigned int)board.port); - invalid_lilo_config = 1; - setup_error_code |= INVALID_PORT_BASE; - return; - } - last = index; - break; - - case 6: - board.membase = ints[index]; - if (ints[index] <= 0) { - printk(KERN_ERR "epca_setup: Invalid memory base 0x%x\n", - (unsigned int)board.membase); - invalid_lilo_config = 1; - setup_error_code |= INVALID_MEM_BASE; - return; - } - last = index; - break; - - default: - printk(KERN_ERR " - epca_setup: Too many integer parms\n"); - return; - - } /* End parse switch */ - - while (str && *str) { /* Begin while there is a string arg */ - /* find the next comma or terminator */ - temp = str; - /* While string is not null, and a comma hasn't been found */ - while (*temp && (*temp != ',')) - temp++; - if (!*temp) - temp = NULL; - else - *temp++ = 0; - /* Set index to the number of args + 1 */ - index = last + 1; - - switch (index) { - case 1: - len = strlen(str); - if (strncmp("Disable", str, len) == 0) - board.status = 0; - else if (strncmp("Enable", str, len) == 0) - board.status = 1; - else { - printk(KERN_ERR "epca_setup: Invalid status %s\n", str); - invalid_lilo_config = 1; - setup_error_code |= INVALID_BOARD_STATUS; - return; - } - last = index; - break; - - case 2: - for (loop = 0; loop < EPCA_NUM_TYPES; loop++) - if (strcmp(board_desc[loop], str) == 0) - break; - /* - * If the index incremented above refers to a - * legitamate board type set it here. - */ - if (index < EPCA_NUM_TYPES) - board.type = loop; - else { - printk(KERN_ERR "epca_setup: Invalid board type: %s\n", str); - invalid_lilo_config = 1; - setup_error_code |= INVALID_BOARD_TYPE; - return; - } - last = index; - break; - - case 3: - len = strlen(str); - if (strncmp("Disable", str, len) == 0) - board.altpin = 0; - else if (strncmp("Enable", str, len) == 0) - board.altpin = 1; - else { - printk(KERN_ERR "epca_setup: Invalid altpin %s\n", str); - invalid_lilo_config = 1; - setup_error_code |= INVALID_ALTPIN; - return; - } - last = index; - break; - - case 4: - t2 = str; - while (isdigit(*t2)) - t2++; - - if (*t2) { - printk(KERN_ERR "epca_setup: Invalid port count %s\n", str); - invalid_lilo_config = 1; - setup_error_code |= INVALID_NUM_PORTS; - return; - } - - /* - * There is not a man page for simple_strtoul but the - * code can be found in vsprintf.c. The first argument - * is the string to translate (To an unsigned long - * obviously), the second argument can be the address - * of any character variable or a NULL. If a variable - * is given, the end pointer of the string will be - * stored in that variable; if a NULL is given the end - * pointer will not be returned. The last argument is - * the base to use. If a 0 is indicated, the routine - * will attempt to determine the proper base by looking - * at the values prefix (A '0' for octal, a 'x' for - * hex, etc ... If a value is given it will use that - * value as the base. - */ - board.numports = simple_strtoul(str, NULL, 0); - nbdevs += board.numports; - last = index; - break; - - case 5: - t2 = str; - while (isxdigit(*t2)) - t2++; - - if (*t2) { - printk(KERN_ERR "epca_setup: Invalid i/o address %s\n", str); - invalid_lilo_config = 1; - setup_error_code |= INVALID_PORT_BASE; - return; - } - - board.port = simple_strtoul(str, NULL, 16); - last = index; - break; - - case 6: - t2 = str; - while (isxdigit(*t2)) - t2++; - - if (*t2) { - printk(KERN_ERR "epca_setup: Invalid memory base %s\n", str); - invalid_lilo_config = 1; - setup_error_code |= INVALID_MEM_BASE; - return; - } - board.membase = simple_strtoul(str, NULL, 16); - last = index; - break; - default: - printk(KERN_ERR "epca: Too many string parms\n"); - return; - } - str = temp; - } /* End while there is a string arg */ - - if (last < 6) { - printk(KERN_ERR "epca: Insufficient parms specified\n"); - return; - } - - /* I should REALLY validate the stuff here */ - /* Copies our local copy of board into boards */ - memcpy((void *)&boards[num_cards], (void *)&board, sizeof(board)); - /* Does this get called once per lilo arg are what ? */ - printk(KERN_INFO "PC/Xx: Added board %i, %s %i ports at 0x%4.4X base 0x%6.6X\n", - num_cards, board_desc[board.type], - board.numports, (int)board.port, (unsigned int) board.membase); - num_cards++; -} - -static int __init epca_real_setup(char *str) -{ - int ints[11]; - - epca_setup(get_options(str, 11, ints), ints); - return 1; -} - -__setup("digiepca", epca_real_setup); -#endif - -enum epic_board_types { - brd_xr = 0, - brd_xem, - brd_cx, - brd_xrj, -}; - -/* indexed directly by epic_board_types enum */ -static struct { - unsigned char board_type; - unsigned bar_idx; /* PCI base address region */ -} epca_info_tbl[] = { - { PCIXR, 0, }, - { PCIXEM, 0, }, - { PCICX, 0, }, - { PCIXRJ, 2, }, -}; - -static int __devinit epca_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - static int board_num = -1; - int board_idx, info_idx = ent->driver_data; - unsigned long addr; - - if (pci_enable_device(pdev)) - return -EIO; - - board_num++; - board_idx = board_num + num_cards; - if (board_idx >= MAXBOARDS) - goto err_out; - - addr = pci_resource_start(pdev, epca_info_tbl[info_idx].bar_idx); - if (!addr) { - printk(KERN_ERR PFX "PCI region #%d not available (size 0)\n", - epca_info_tbl[info_idx].bar_idx); - goto err_out; - } - - boards[board_idx].status = ENABLED; - boards[board_idx].type = epca_info_tbl[info_idx].board_type; - boards[board_idx].numports = 0x0; - boards[board_idx].port = addr + PCI_IO_OFFSET; - boards[board_idx].membase = addr; - - if (!request_mem_region(addr + PCI_IO_OFFSET, 0x200000, "epca")) { - printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n", - 0x200000, addr + PCI_IO_OFFSET); - goto err_out; - } - - boards[board_idx].re_map_port = ioremap_nocache(addr + PCI_IO_OFFSET, - 0x200000); - if (!boards[board_idx].re_map_port) { - printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n", - 0x200000, addr + PCI_IO_OFFSET); - goto err_out_free_pciio; - } - - if (!request_mem_region(addr, 0x200000, "epca")) { - printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n", - 0x200000, addr); - goto err_out_free_iounmap; - } - - boards[board_idx].re_map_membase = ioremap_nocache(addr, 0x200000); - if (!boards[board_idx].re_map_membase) { - printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n", - 0x200000, addr + PCI_IO_OFFSET); - goto err_out_free_memregion; - } - - /* - * I don't know what the below does, but the hardware guys say its - * required on everything except PLX (In this case XRJ). - */ - if (info_idx != brd_xrj) { - pci_write_config_byte(pdev, 0x40, 0); - pci_write_config_byte(pdev, 0x46, 0); - } - - return 0; - -err_out_free_memregion: - release_mem_region(addr, 0x200000); -err_out_free_iounmap: - iounmap(boards[board_idx].re_map_port); -err_out_free_pciio: - release_mem_region(addr + PCI_IO_OFFSET, 0x200000); -err_out: - return -ENODEV; -} - - -static struct pci_device_id epca_pci_tbl[] = { - { PCI_VENDOR_DIGI, PCI_DEVICE_XR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xr }, - { PCI_VENDOR_DIGI, PCI_DEVICE_XEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xem }, - { PCI_VENDOR_DIGI, PCI_DEVICE_CX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_cx }, - { PCI_VENDOR_DIGI, PCI_DEVICE_XRJ, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xrj }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, epca_pci_tbl); - -static int __init init_PCI(void) -{ - memset(&epca_driver, 0, sizeof(epca_driver)); - epca_driver.name = "epca"; - epca_driver.id_table = epca_pci_tbl; - epca_driver.probe = epca_init_one; - - return pci_register_driver(&epca_driver); -} - -MODULE_LICENSE("GPL"); diff --git a/drivers/char/epca.h b/drivers/char/epca.h deleted file mode 100644 index d414bf2dbf7c..000000000000 --- a/drivers/char/epca.h +++ /dev/null @@ -1,158 +0,0 @@ -#define XEMPORTS 0xC02 -#define XEPORTS 0xC22 - -#define MAX_ALLOC 0x100 - -#define MAXBOARDS 12 -#define FEPCODESEG 0x0200L -#define FEPCODE 0x2000L -#define BIOSCODE 0xf800L - -#define MISCGLOBAL 0x0C00L -#define NPORT 0x0C22L -#define MBOX 0x0C40L -#define PORTBASE 0x0C90L - -/* Begin code defines used for epca_setup */ - -#define INVALID_BOARD_TYPE 0x1 -#define INVALID_NUM_PORTS 0x2 -#define INVALID_MEM_BASE 0x4 -#define INVALID_PORT_BASE 0x8 -#define INVALID_BOARD_STATUS 0x10 -#define INVALID_ALTPIN 0x20 - -/* End code defines used for epca_setup */ - - -#define FEPCLR 0x00 -#define FEPMEM 0x02 -#define FEPRST 0x04 -#define FEPINT 0x08 -#define FEPMASK 0x0e -#define FEPWIN 0x80 - -#define PCXE 0 -#define PCXEVE 1 -#define PCXEM 2 -#define EISAXEM 3 -#define PC64XE 4 -#define PCXI 5 -#define PCIXEM 7 -#define PCICX 8 -#define PCIXR 9 -#define PCIXRJ 10 -#define EPCA_NUM_TYPES 6 - - -static char *board_desc[] = -{ - "PC/Xe", - "PC/Xeve", - "PC/Xem", - "EISA/Xem", - "PC/64Xe", - "PC/Xi", - "unknown", - "PCI/Xem", - "PCI/CX", - "PCI/Xr", - "PCI/Xrj", -}; - -#define STARTC 021 -#define STOPC 023 -#define IAIXON 0x2000 - - -#define TXSTOPPED 0x1 -#define LOWWAIT 0x2 -#define EMPTYWAIT 0x4 -#define RXSTOPPED 0x8 -#define TXBUSY 0x10 - -#define DISABLED 0 -#define ENABLED 1 -#define OFF 0 -#define ON 1 - -#define FEPTIMEOUT 200000 -#define SERIAL_TYPE_INFO 3 -#define EPCA_EVENT_HANGUP 1 -#define EPCA_MAGIC 0x5c6df104L - -struct channel -{ - long magic; - struct tty_port port; - unsigned char boardnum; - unsigned char channelnum; - unsigned char omodem; /* FEP output modem status */ - unsigned char imodem; /* FEP input modem status */ - unsigned char modemfake; /* Modem values to be forced */ - unsigned char modem; /* Force values */ - unsigned char hflow; - unsigned char dsr; - unsigned char dcd; - unsigned char m_rts ; /* The bits used in whatever FEP */ - unsigned char m_dcd ; /* is indiginous to this board to */ - unsigned char m_dsr ; /* represent each of the physical */ - unsigned char m_cts ; /* handshake lines */ - unsigned char m_ri ; - unsigned char m_dtr ; - unsigned char stopc; - unsigned char startc; - unsigned char stopca; - unsigned char startca; - unsigned char fepstopc; - unsigned char fepstartc; - unsigned char fepstopca; - unsigned char fepstartca; - unsigned char txwin; - unsigned char rxwin; - unsigned short fepiflag; - unsigned short fepcflag; - unsigned short fepoflag; - unsigned short txbufhead; - unsigned short txbufsize; - unsigned short rxbufhead; - unsigned short rxbufsize; - int close_delay; - unsigned long event; - uint dev; - unsigned long statusflags; - unsigned long c_iflag; - unsigned long c_cflag; - unsigned long c_lflag; - unsigned long c_oflag; - unsigned char __iomem *txptr; - unsigned char __iomem *rxptr; - struct board_info *board; - struct board_chan __iomem *brdchan; - struct digi_struct digiext; - struct work_struct tqueue; - struct global_data __iomem *mailbox; -}; - -struct board_info -{ - unsigned char status; - unsigned char type; - unsigned char altpin; - unsigned short numports; - unsigned long port; - unsigned long membase; - void __iomem *re_map_port; - void __iomem *re_map_membase; - unsigned long memory_seg; - void ( * memwinon ) (struct board_info *, unsigned int) ; - void ( * memwinoff ) (struct board_info *, unsigned int) ; - void ( * globalwinon ) (struct channel *) ; - void ( * txwinon ) (struct channel *) ; - void ( * rxwinon ) (struct channel *) ; - void ( * memoff ) (struct channel *) ; - void ( * assertgwinon ) (struct channel *) ; - void ( * assertmemoff ) (struct channel *) ; - unsigned char poller_inhibited ; -}; - diff --git a/drivers/char/epcaconfig.h b/drivers/char/epcaconfig.h deleted file mode 100644 index 55dec067078e..000000000000 --- a/drivers/char/epcaconfig.h +++ /dev/null @@ -1,7 +0,0 @@ -#define NUMCARDS 0 -#define NBDEVS 0 - -struct board_info static_boards[NUMCARDS]={ -}; - -/* DO NOT HAND EDIT THIS FILE! */ diff --git a/drivers/char/ip2/Makefile b/drivers/char/ip2/Makefile deleted file mode 100644 index 7b78e0dfc5b0..000000000000 --- a/drivers/char/ip2/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for the Computone IntelliPort Plus Driver -# - -obj-$(CONFIG_COMPUTONE) += ip2.o - -ip2-y := ip2main.o - diff --git a/drivers/char/ip2/i2cmd.c b/drivers/char/ip2/i2cmd.c deleted file mode 100644 index e7af647800b6..000000000000 --- a/drivers/char/ip2/i2cmd.c +++ /dev/null @@ -1,210 +0,0 @@ -/******************************************************************************* -* -* (c) 1998 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Definition table for In-line and Bypass commands. Applicable -* only when the standard loadware is active. (This is included -* source code, not a separate compilation module.) -* -*******************************************************************************/ - -//------------------------------------------------------------------------------ -// -// Revision History: -// -// 10 October 1991 MAG First Draft -// 7 November 1991 MAG Reflects additional commands. -// 24 February 1992 MAG Additional commands for 1.4.x loadware -// 11 March 1992 MAG Additional commands -// 30 March 1992 MAG Additional command: CMD_DSS_NOW -// 18 May 1992 MAG Discovered commands 39 & 40 must be at the end of a -// packet: affects implementation. -//------------------------------------------------------------------------------ - -//************ -//* Includes * -//************ - -#include "i2cmd.h" /* To get some bit-defines */ - -//------------------------------------------------------------------------------ -// Here is the table of global arrays which represent each type of command -// supported in the IntelliPort standard loadware. See also i2cmd.h -// for a more complete explanation of what is going on. -//------------------------------------------------------------------------------ - -// Here are the various globals: note that the names are not used except through -// the macros defined in i2cmd.h. Also note that although they are character -// arrays here (for extendability) they are cast to structure pointers in the -// i2cmd.h macros. See i2cmd.h for flags definitions. - -// Length Flags Command -static UCHAR ct02[] = { 1, BTH, 0x02 }; // DTR UP -static UCHAR ct03[] = { 1, BTH, 0x03 }; // DTR DN -static UCHAR ct04[] = { 1, BTH, 0x04 }; // RTS UP -static UCHAR ct05[] = { 1, BTH, 0x05 }; // RTS DN -static UCHAR ct06[] = { 1, BYP, 0x06 }; // START FL -static UCHAR ct07[] = { 2, BTH, 0x07,0 }; // BAUD -static UCHAR ct08[] = { 2, BTH, 0x08,0 }; // BITS -static UCHAR ct09[] = { 2, BTH, 0x09,0 }; // STOP -static UCHAR ct10[] = { 2, BTH, 0x0A,0 }; // PARITY -static UCHAR ct11[] = { 2, BTH, 0x0B,0 }; // XON -static UCHAR ct12[] = { 2, BTH, 0x0C,0 }; // XOFF -static UCHAR ct13[] = { 1, BTH, 0x0D }; // STOP FL -static UCHAR ct14[] = { 1, BYP|VIP, 0x0E }; // ACK HOTK -//static UCHAR ct15[]={ 2, BTH|VIP, 0x0F,0 }; // IRQ SET -static UCHAR ct16[] = { 2, INL, 0x10,0 }; // IXONOPTS -static UCHAR ct17[] = { 2, INL, 0x11,0 }; // OXONOPTS -static UCHAR ct18[] = { 1, INL, 0x12 }; // CTSENAB -static UCHAR ct19[] = { 1, BTH, 0x13 }; // CTSDSAB -static UCHAR ct20[] = { 1, INL, 0x14 }; // DCDENAB -static UCHAR ct21[] = { 1, BTH, 0x15 }; // DCDDSAB -static UCHAR ct22[] = { 1, BTH, 0x16 }; // DSRENAB -static UCHAR ct23[] = { 1, BTH, 0x17 }; // DSRDSAB -static UCHAR ct24[] = { 1, BTH, 0x18 }; // RIENAB -static UCHAR ct25[] = { 1, BTH, 0x19 }; // RIDSAB -static UCHAR ct26[] = { 2, BTH, 0x1A,0 }; // BRKENAB -static UCHAR ct27[] = { 1, BTH, 0x1B }; // BRKDSAB -//static UCHAR ct28[]={ 2, BTH, 0x1C,0 }; // MAXBLOKSIZE -//static UCHAR ct29[]={ 2, 0, 0x1D,0 }; // reserved -static UCHAR ct30[] = { 1, INL, 0x1E }; // CTSFLOWENAB -static UCHAR ct31[] = { 1, INL, 0x1F }; // CTSFLOWDSAB -static UCHAR ct32[] = { 1, INL, 0x20 }; // RTSFLOWENAB -static UCHAR ct33[] = { 1, INL, 0x21 }; // RTSFLOWDSAB -static UCHAR ct34[] = { 2, BTH, 0x22,0 }; // ISTRIPMODE -static UCHAR ct35[] = { 2, BTH|END, 0x23,0 }; // SENDBREAK -static UCHAR ct36[] = { 2, BTH, 0x24,0 }; // SETERRMODE -//static UCHAR ct36a[]={ 3, INL, 0x24,0,0 }; // SET_REPLACE - -// The following is listed for completeness, but should never be sent directly -// by user-level code. It is sent only by library routines in response to data -// movement. -//static UCHAR ct37[]={ 5, BYP|VIP, 0x25,0,0,0,0 }; // FLOW PACKET - -// Back to normal -//static UCHAR ct38[] = {11, BTH|VAR, 0x26,0,0,0,0,0,0,0,0,0,0 }; // DEF KEY SEQ -//static UCHAR ct39[]={ 3, BTH|END, 0x27,0,0 }; // OPOSTON -//static UCHAR ct40[]={ 1, BTH|END, 0x28 }; // OPOSTOFF -static UCHAR ct41[] = { 1, BYP, 0x29 }; // RESUME -//static UCHAR ct42[]={ 2, BTH, 0x2A,0 }; // TXBAUD -//static UCHAR ct43[]={ 2, BTH, 0x2B,0 }; // RXBAUD -//static UCHAR ct44[]={ 2, BTH, 0x2C,0 }; // MS PING -//static UCHAR ct45[]={ 1, BTH, 0x2D }; // HOTENAB -//static UCHAR ct46[]={ 1, BTH, 0x2E }; // HOTDSAB -//static UCHAR ct47[]={ 7, BTH, 0x2F,0,0,0,0,0,0 }; // UNIX FLAGS -//static UCHAR ct48[]={ 1, BTH, 0x30 }; // DSRFLOWENAB -//static UCHAR ct49[]={ 1, BTH, 0x31 }; // DSRFLOWDSAB -//static UCHAR ct50[]={ 1, BTH, 0x32 }; // DTRFLOWENAB -//static UCHAR ct51[]={ 1, BTH, 0x33 }; // DTRFLOWDSAB -//static UCHAR ct52[]={ 1, BTH, 0x34 }; // BAUDTABRESET -//static UCHAR ct53[] = { 3, BTH, 0x35,0,0 }; // BAUDREMAP -static UCHAR ct54[] = { 3, BTH, 0x36,0,0 }; // CUSTOMBAUD1 -static UCHAR ct55[] = { 3, BTH, 0x37,0,0 }; // CUSTOMBAUD2 -static UCHAR ct56[] = { 2, BTH|END, 0x38,0 }; // PAUSE -static UCHAR ct57[] = { 1, BYP, 0x39 }; // SUSPEND -static UCHAR ct58[] = { 1, BYP, 0x3A }; // UNSUSPEND -static UCHAR ct59[] = { 2, BTH, 0x3B,0 }; // PARITYCHK -static UCHAR ct60[] = { 1, INL|VIP, 0x3C }; // BOOKMARKREQ -//static UCHAR ct61[]={ 2, BTH, 0x3D,0 }; // INTERNALLOOP -//static UCHAR ct62[]={ 2, BTH, 0x3E,0 }; // HOTKTIMEOUT -static UCHAR ct63[] = { 2, INL, 0x3F,0 }; // SETTXON -static UCHAR ct64[] = { 2, INL, 0x40,0 }; // SETTXOFF -//static UCHAR ct65[]={ 2, BTH, 0x41,0 }; // SETAUTORTS -//static UCHAR ct66[]={ 2, BTH, 0x42,0 }; // SETHIGHWAT -//static UCHAR ct67[]={ 2, BYP, 0x43,0 }; // STARTSELFL -//static UCHAR ct68[]={ 2, INL, 0x44,0 }; // ENDSELFL -//static UCHAR ct69[]={ 1, BYP, 0x45 }; // HWFLOW_OFF -//static UCHAR ct70[]={ 1, BTH, 0x46 }; // ODSRFL_ENAB -//static UCHAR ct71[]={ 1, BTH, 0x47 }; // ODSRFL_DSAB -//static UCHAR ct72[]={ 1, BTH, 0x48 }; // ODCDFL_ENAB -//static UCHAR ct73[]={ 1, BTH, 0x49 }; // ODCDFL_DSAB -//static UCHAR ct74[]={ 2, BTH, 0x4A,0 }; // LOADLEVEL -//static UCHAR ct75[]={ 2, BTH, 0x4B,0 }; // STATDATA -//static UCHAR ct76[]={ 1, BYP, 0x4C }; // BREAK_ON -//static UCHAR ct77[]={ 1, BYP, 0x4D }; // BREAK_OFF -//static UCHAR ct78[]={ 1, BYP, 0x4E }; // GETFC -static UCHAR ct79[] = { 2, BYP, 0x4F,0 }; // XMIT_NOW -//static UCHAR ct80[]={ 4, BTH, 0x50,0,0,0 }; // DIVISOR_LATCH -//static UCHAR ct81[]={ 1, BYP, 0x51 }; // GET_STATUS -//static UCHAR ct82[]={ 1, BYP, 0x52 }; // GET_TXCNT -//static UCHAR ct83[]={ 1, BYP, 0x53 }; // GET_RXCNT -//static UCHAR ct84[]={ 1, BYP, 0x54 }; // GET_BOXIDS -//static UCHAR ct85[]={10, BYP, 0x55,0,0,0,0,0,0,0,0,0 }; // ENAB_MULT -//static UCHAR ct86[]={ 2, BTH, 0x56,0 }; // RCV_ENABLE -static UCHAR ct87[] = { 1, BYP, 0x57 }; // HW_TEST -//static UCHAR ct88[]={ 3, BTH, 0x58,0,0 }; // RCV_THRESHOLD -//static UCHAR ct90[]={ 3, BYP, 0x5A,0,0 }; // Set SILO -//static UCHAR ct91[]={ 2, BYP, 0x5B,0 }; // timed break - -// Some composite commands as well -//static UCHAR cc01[]={ 2, BTH, 0x02,0x04 }; // DTR & RTS UP -//static UCHAR cc02[]={ 2, BTH, 0x03,0x05 }; // DTR & RTS DN - -//******** -//* Code * -//******** - -//****************************************************************************** -// Function: i2cmdUnixFlags(iflag, cflag, lflag) -// Parameters: Unix tty flags -// -// Returns: Pointer to command structure -// -// Description: -// -// This routine sets the parameters of command 47 and returns a pointer to the -// appropriate structure. -//****************************************************************************** -#if 0 -cmdSyntaxPtr -i2cmdUnixFlags(unsigned short iflag,unsigned short cflag,unsigned short lflag) -{ - cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct47; - - pCM->cmd[1] = (unsigned char) iflag; - pCM->cmd[2] = (unsigned char) (iflag >> 8); - pCM->cmd[3] = (unsigned char) cflag; - pCM->cmd[4] = (unsigned char) (cflag >> 8); - pCM->cmd[5] = (unsigned char) lflag; - pCM->cmd[6] = (unsigned char) (lflag >> 8); - return pCM; -} -#endif /* 0 */ - -//****************************************************************************** -// Function: i2cmdBaudDef(which, rate) -// Parameters: ? -// -// Returns: Pointer to command structure -// -// Description: -// -// This routine sets the parameters of commands 54 or 55 (according to the -// argument which), and returns a pointer to the appropriate structure. -//****************************************************************************** -static cmdSyntaxPtr -i2cmdBaudDef(int which, unsigned short rate) -{ - cmdSyntaxPtr pCM; - - switch(which) - { - case 1: - pCM = (cmdSyntaxPtr) ct54; - break; - default: - case 2: - pCM = (cmdSyntaxPtr) ct55; - break; - } - pCM->cmd[1] = (unsigned char) rate; - pCM->cmd[2] = (unsigned char) (rate >> 8); - return pCM; -} - diff --git a/drivers/char/ip2/i2cmd.h b/drivers/char/ip2/i2cmd.h deleted file mode 100644 index 29277ec6b8ed..000000000000 --- a/drivers/char/ip2/i2cmd.h +++ /dev/null @@ -1,630 +0,0 @@ -/******************************************************************************* -* -* (c) 1999 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Definitions and support for In-line and Bypass commands. -* Applicable only when the standard loadware is active. -* -*******************************************************************************/ -//------------------------------------------------------------------------------ -// Revision History: -// -// 10 October 1991 MAG First Draft -// 7 November 1991 MAG Reflects some new commands -// 20 February 1992 MAG CMD_HOTACK corrected: no argument. -// 24 February 1992 MAG Support added for new commands for 1.4.x loadware. -// 11 March 1992 MAG Additional commands. -// 16 March 1992 MAG Additional commands. -// 30 March 1992 MAG Additional command: CMD_DSS_NOW -// 18 May 1992 MAG Changed CMD_OPOST -// -//------------------------------------------------------------------------------ -#ifndef I2CMD_H // To prevent multiple includes -#define I2CMD_H 1 - -#include "ip2types.h" - -// This module is designed to provide a uniform method of sending commands to -// the board through command packets. The difficulty is, some commands take -// parameters, others do not. Furthermore, it is often useful to send several -// commands to the same channel as part of the same packet. (See also i2pack.h.) -// -// This module is designed so that the caller should not be responsible for -// remembering the exact syntax of each command, or at least so that the -// compiler could check things somewhat. I'll explain as we go... -// -// First, a structure which can embody the syntax of each type of command. -// -typedef struct _cmdSyntax -{ - UCHAR length; // Number of bytes in the command - UCHAR flags; // Information about the command (see below) - - // The command and its parameters, which may be of arbitrary length. Don't - // worry yet how the parameters will be initialized; macros later take care - // of it. Also, don't worry about the arbitrary length issue; this structure - // is never used to allocate space (see i2cmd.c). - UCHAR cmd[2]; -} cmdSyntax, *cmdSyntaxPtr; - -// Bit assignments for flags - -#define INL 1 // Set if suitable for inline commands -#define BYP 2 // Set if suitable for bypass commands -#define BTH (INL|BYP) // suitable for either! -#define END 4 // Set if this must be the last command in a block -#define VIP 8 // Set if this command is special in some way and really - // should only be sent from the library-level and not - // directly from user-level -#define VAR 0x10 // This command is of variable length! - -// Declarations for the global arrays used to bear the commands and their -// arguments. -// -// Note: Since these are globals and the arguments might change, it is important -// that the library routine COPY these into buffers from whence they would be -// sent, rather than merely storing the pointers. In multi-threaded -// environments, important that the copy should obtain before any context switch -// is allowed. Also, for parameterized commands, DO NOT ISSUE THE SAME COMMAND -// MORE THAN ONCE WITH THE SAME PARAMETERS in the same call. -// -static UCHAR ct02[]; -static UCHAR ct03[]; -static UCHAR ct04[]; -static UCHAR ct05[]; -static UCHAR ct06[]; -static UCHAR ct07[]; -static UCHAR ct08[]; -static UCHAR ct09[]; -static UCHAR ct10[]; -static UCHAR ct11[]; -static UCHAR ct12[]; -static UCHAR ct13[]; -static UCHAR ct14[]; -static UCHAR ct15[]; -static UCHAR ct16[]; -static UCHAR ct17[]; -static UCHAR ct18[]; -static UCHAR ct19[]; -static UCHAR ct20[]; -static UCHAR ct21[]; -static UCHAR ct22[]; -static UCHAR ct23[]; -static UCHAR ct24[]; -static UCHAR ct25[]; -static UCHAR ct26[]; -static UCHAR ct27[]; -static UCHAR ct28[]; -static UCHAR ct29[]; -static UCHAR ct30[]; -static UCHAR ct31[]; -static UCHAR ct32[]; -static UCHAR ct33[]; -static UCHAR ct34[]; -static UCHAR ct35[]; -static UCHAR ct36[]; -static UCHAR ct36a[]; -static UCHAR ct41[]; -static UCHAR ct42[]; -static UCHAR ct43[]; -static UCHAR ct44[]; -static UCHAR ct45[]; -static UCHAR ct46[]; -static UCHAR ct48[]; -static UCHAR ct49[]; -static UCHAR ct50[]; -static UCHAR ct51[]; -static UCHAR ct52[]; -static UCHAR ct56[]; -static UCHAR ct57[]; -static UCHAR ct58[]; -static UCHAR ct59[]; -static UCHAR ct60[]; -static UCHAR ct61[]; -static UCHAR ct62[]; -static UCHAR ct63[]; -static UCHAR ct64[]; -static UCHAR ct65[]; -static UCHAR ct66[]; -static UCHAR ct67[]; -static UCHAR ct68[]; -static UCHAR ct69[]; -static UCHAR ct70[]; -static UCHAR ct71[]; -static UCHAR ct72[]; -static UCHAR ct73[]; -static UCHAR ct74[]; -static UCHAR ct75[]; -static UCHAR ct76[]; -static UCHAR ct77[]; -static UCHAR ct78[]; -static UCHAR ct79[]; -static UCHAR ct80[]; -static UCHAR ct81[]; -static UCHAR ct82[]; -static UCHAR ct83[]; -static UCHAR ct84[]; -static UCHAR ct85[]; -static UCHAR ct86[]; -static UCHAR ct87[]; -static UCHAR ct88[]; -static UCHAR ct89[]; -static UCHAR ct90[]; -static UCHAR ct91[]; -static UCHAR cc01[]; -static UCHAR cc02[]; - -// Now, refer to i2cmd.c, and see the character arrays defined there. They are -// cast here to cmdSyntaxPtr. -// -// There are library functions for issuing bypass or inline commands. These -// functions take one or more arguments of the type cmdSyntaxPtr. The routine -// then can figure out how long each command is supposed to be and easily add it -// to the list. -// -// For ease of use, we define manifests which return pointers to appropriate -// cmdSyntaxPtr things. But some commands also take arguments. If a single -// argument is used, we define a macro which performs the single assignment and -// (through the expedient of a comma expression) references the appropriate -// pointer. For commands requiring several arguments, we actually define a -// function to perform the assignments. - -#define CMD_DTRUP (cmdSyntaxPtr)(ct02) // Raise DTR -#define CMD_DTRDN (cmdSyntaxPtr)(ct03) // Lower DTR -#define CMD_RTSUP (cmdSyntaxPtr)(ct04) // Raise RTS -#define CMD_RTSDN (cmdSyntaxPtr)(ct05) // Lower RTS -#define CMD_STARTFL (cmdSyntaxPtr)(ct06) // Start Flushing Data - -#define CMD_DTRRTS_UP (cmdSyntaxPtr)(cc01) // Raise DTR and RTS -#define CMD_DTRRTS_DN (cmdSyntaxPtr)(cc02) // Lower DTR and RTS - -// Set Baud Rate for transmit and receive -#define CMD_SETBAUD(arg) \ - (((cmdSyntaxPtr)(ct07))->cmd[1] = (arg),(cmdSyntaxPtr)(ct07)) - -#define CBR_50 1 -#define CBR_75 2 -#define CBR_110 3 -#define CBR_134 4 -#define CBR_150 5 -#define CBR_200 6 -#define CBR_300 7 -#define CBR_600 8 -#define CBR_1200 9 -#define CBR_1800 10 -#define CBR_2400 11 -#define CBR_4800 12 -#define CBR_9600 13 -#define CBR_19200 14 -#define CBR_38400 15 -#define CBR_2000 16 -#define CBR_3600 17 -#define CBR_7200 18 -#define CBR_56000 19 -#define CBR_57600 20 -#define CBR_64000 21 -#define CBR_76800 22 -#define CBR_115200 23 -#define CBR_C1 24 // Custom baud rate 1 -#define CBR_C2 25 // Custom baud rate 2 -#define CBR_153600 26 -#define CBR_230400 27 -#define CBR_307200 28 -#define CBR_460800 29 -#define CBR_921600 30 - -// Set Character size -// -#define CMD_SETBITS(arg) \ - (((cmdSyntaxPtr)(ct08))->cmd[1] = (arg),(cmdSyntaxPtr)(ct08)) - -#define CSZ_5 0 -#define CSZ_6 1 -#define CSZ_7 2 -#define CSZ_8 3 - -// Set number of stop bits -// -#define CMD_SETSTOP(arg) \ - (((cmdSyntaxPtr)(ct09))->cmd[1] = (arg),(cmdSyntaxPtr)(ct09)) - -#define CST_1 0 -#define CST_15 1 // 1.5 stop bits -#define CST_2 2 - -// Set parity option -// -#define CMD_SETPAR(arg) \ - (((cmdSyntaxPtr)(ct10))->cmd[1] = (arg),(cmdSyntaxPtr)(ct10)) - -#define CSP_NP 0 // no parity -#define CSP_OD 1 // odd parity -#define CSP_EV 2 // Even parity -#define CSP_SP 3 // Space parity -#define CSP_MK 4 // Mark parity - -// Define xon char for transmitter flow control -// -#define CMD_DEF_IXON(arg) \ - (((cmdSyntaxPtr)(ct11))->cmd[1] = (arg),(cmdSyntaxPtr)(ct11)) - -// Define xoff char for transmitter flow control -// -#define CMD_DEF_IXOFF(arg) \ - (((cmdSyntaxPtr)(ct12))->cmd[1] = (arg),(cmdSyntaxPtr)(ct12)) - -#define CMD_STOPFL (cmdSyntaxPtr)(ct13) // Stop Flushing data - -// Acknowledge receipt of hotkey signal -// -#define CMD_HOTACK (cmdSyntaxPtr)(ct14) - -// Define irq level to use. Should actually be sent by library-level code, not -// directly from user... -// -#define CMDVALUE_IRQ 15 // For library use at initialization. Until this command - // is sent, board processing doesn't really start. -#define CMD_SET_IRQ(arg) \ - (((cmdSyntaxPtr)(ct15))->cmd[1] = (arg),(cmdSyntaxPtr)(ct15)) - -#define CIR_POLL 0 // No IRQ - Poll -#define CIR_3 3 // IRQ 3 -#define CIR_4 4 // IRQ 4 -#define CIR_5 5 // IRQ 5 -#define CIR_7 7 // IRQ 7 -#define CIR_10 10 // IRQ 10 -#define CIR_11 11 // IRQ 11 -#define CIR_12 12 // IRQ 12 -#define CIR_15 15 // IRQ 15 - -// Select transmit flow xon/xoff options -// -#define CMD_IXON_OPT(arg) \ - (((cmdSyntaxPtr)(ct16))->cmd[1] = (arg),(cmdSyntaxPtr)(ct16)) - -#define CIX_NONE 0 // Incoming Xon/Xoff characters not special -#define CIX_XON 1 // Xoff disable, Xon enable -#define CIX_XANY 2 // Xoff disable, any key enable - -// Select receive flow xon/xoff options -// -#define CMD_OXON_OPT(arg) \ - (((cmdSyntaxPtr)(ct17))->cmd[1] = (arg),(cmdSyntaxPtr)(ct17)) - -#define COX_NONE 0 // Don't send Xon/Xoff -#define COX_XON 1 // Send xon/xoff to start/stop incoming data - - -#define CMD_CTS_REP (cmdSyntaxPtr)(ct18) // Enable CTS reporting -#define CMD_CTS_NREP (cmdSyntaxPtr)(ct19) // Disable CTS reporting - -#define CMD_DCD_REP (cmdSyntaxPtr)(ct20) // Enable DCD reporting -#define CMD_DCD_NREP (cmdSyntaxPtr)(ct21) // Disable DCD reporting - -#define CMD_DSR_REP (cmdSyntaxPtr)(ct22) // Enable DSR reporting -#define CMD_DSR_NREP (cmdSyntaxPtr)(ct23) // Disable DSR reporting - -#define CMD_RI_REP (cmdSyntaxPtr)(ct24) // Enable RI reporting -#define CMD_RI_NREP (cmdSyntaxPtr)(ct25) // Disable RI reporting - -// Enable break reporting and select style -// -#define CMD_BRK_REP(arg) \ - (((cmdSyntaxPtr)(ct26))->cmd[1] = (arg),(cmdSyntaxPtr)(ct26)) - -#define CBK_STAT 0x00 // Report breaks as a status (exception,irq) -#define CBK_NULL 0x01 // Report breaks as a good null -#define CBK_STAT_SEQ 0x02 // Report breaks as a status AND as in-band character - // sequence FFh, 01h, 10h -#define CBK_SEQ 0x03 // Report breaks as the in-band - //sequence FFh, 01h, 10h ONLY. -#define CBK_FLSH 0x04 // if this bit set also flush input data -#define CBK_POSIX 0x08 // if this bit set report as FF,0,0 sequence -#define CBK_SINGLE 0x10 // if this bit set with CBK_SEQ or CBK_STAT_SEQ - //then reports single null instead of triple - -#define CMD_BRK_NREP (cmdSyntaxPtr)(ct27) // Disable break reporting - -// Specify maximum block size for received data -// -#define CMD_MAX_BLOCK(arg) \ - (((cmdSyntaxPtr)(ct28))->cmd[1] = (arg),(cmdSyntaxPtr)(ct28)) - -// -- COMMAND 29 is reserved -- - -#define CMD_CTSFL_ENAB (cmdSyntaxPtr)(ct30) // Enable CTS flow control -#define CMD_CTSFL_DSAB (cmdSyntaxPtr)(ct31) // Disable CTS flow control -#define CMD_RTSFL_ENAB (cmdSyntaxPtr)(ct32) // Enable RTS flow control -#define CMD_RTSFL_DSAB (cmdSyntaxPtr)(ct33) // Disable RTS flow control - -// Specify istrip option -// -#define CMD_ISTRIP_OPT(arg) \ - (((cmdSyntaxPtr)(ct34))->cmd[1] = (arg),(cmdSyntaxPtr)(ct34)) - -#define CIS_NOSTRIP 0 // Strip characters to character size -#define CIS_STRIP 1 // Strip any 8-bit characters to 7 bits - -// Send a break of arg milliseconds -// -#define CMD_SEND_BRK(arg) \ - (((cmdSyntaxPtr)(ct35))->cmd[1] = (arg),(cmdSyntaxPtr)(ct35)) - -// Set error reporting mode -// -#define CMD_SET_ERROR(arg) \ - (((cmdSyntaxPtr)(ct36))->cmd[1] = (arg),(cmdSyntaxPtr)(ct36)) - -#define CSE_ESTAT 0 // Report error in a status packet -#define CSE_NOREP 1 // Treat character as though it were good -#define CSE_DROP 2 // Discard the character -#define CSE_NULL 3 // Replace with a null -#define CSE_MARK 4 // Replace with a 3-character sequence (as Unix) - -#define CSE_REPLACE 0x8 // Replace the errored character with the - // replacement character defined here - -#define CSE_STAT_REPLACE 0x18 // Replace the errored character with the - // replacement character defined here AND - // report the error as a status packet (as in - // CSE_ESTAT). - - -// COMMAND 37, to send flow control packets, is handled only by low-level -// library code in response to data movement and shouldn't ever be sent by the -// user code. See i2pack.h and the body of i2lib.c for details. - -// Enable on-board post-processing, using options given in oflag argument. -// Formerly, this command was automatically preceded by a CMD_OPOST_OFF command -// because the loadware does not permit sending back-to-back CMD_OPOST_ON -// commands without an intervening CMD_OPOST_OFF. BUT, WE LEARN 18 MAY 92, that -// CMD_OPOST_ON and CMD_OPOST_OFF must each be at the end of a packet (or in a -// solo packet). This means the caller must specify separately CMD_OPOST_OFF, -// CMD_OPOST_ON(parm) when he calls i2QueueCommands(). That function will ensure -// each gets a separate packet. Extra CMD_OPOST_OFF's are always ok. -// -#define CMD_OPOST_ON(oflag) \ - (*(USHORT *)(((cmdSyntaxPtr)(ct39))->cmd[1]) = (oflag), \ - (cmdSyntaxPtr)(ct39)) - -#define CMD_OPOST_OFF (cmdSyntaxPtr)(ct40) // Disable on-board post-proc - -#define CMD_RESUME (cmdSyntaxPtr)(ct41) // Resume: behave as though an XON - // were received; - -// Set Transmit baud rate (see command 7 for arguments) -// -#define CMD_SETBAUD_TX(arg) \ - (((cmdSyntaxPtr)(ct42))->cmd[1] = (arg),(cmdSyntaxPtr)(ct42)) - -// Set Receive baud rate (see command 7 for arguments) -// -#define CMD_SETBAUD_RX(arg) \ - (((cmdSyntaxPtr)(ct43))->cmd[1] = (arg),(cmdSyntaxPtr)(ct43)) - -// Request interrupt from board each arg milliseconds. Interrupt will specify -// "received data", even though there may be no data present. If arg == 0, -// disables any such interrupts. -// -#define CMD_PING_REQ(arg) \ - (((cmdSyntaxPtr)(ct44))->cmd[1] = (arg),(cmdSyntaxPtr)(ct44)) - -#define CMD_HOT_ENAB (cmdSyntaxPtr)(ct45) // Enable Hot-key checking -#define CMD_HOT_DSAB (cmdSyntaxPtr)(ct46) // Disable Hot-key checking - -#if 0 -// COMMAND 47: Send Protocol info via Unix flags: -// iflag = Unix tty t_iflag -// cflag = Unix tty t_cflag -// lflag = Unix tty t_lflag -// See System V Unix/Xenix documentation for the meanings of the bit fields -// within these flags -// -#define CMD_UNIX_FLAGS(iflag,cflag,lflag) i2cmdUnixFlags(iflag,cflag,lflag) -#endif /* 0 */ - -#define CMD_DSRFL_ENAB (cmdSyntaxPtr)(ct48) // Enable DSR receiver ctrl -#define CMD_DSRFL_DSAB (cmdSyntaxPtr)(ct49) // Disable DSR receiver ctrl -#define CMD_DTRFL_ENAB (cmdSyntaxPtr)(ct50) // Enable DTR flow control -#define CMD_DTRFL_DSAB (cmdSyntaxPtr)(ct51) // Disable DTR flow control -#define CMD_BAUD_RESET (cmdSyntaxPtr)(ct52) // Reset baudrate table - -// COMMAND 54: Define custom rate #1 -// rate = (short) 1/10 of the desired baud rate -// -#define CMD_BAUD_DEF1(rate) i2cmdBaudDef(1,rate) - -// COMMAND 55: Define custom rate #2 -// rate = (short) 1/10 of the desired baud rate -// -#define CMD_BAUD_DEF2(rate) i2cmdBaudDef(2,rate) - -// Pause arg hundredths of seconds. (Note, this is NOT milliseconds.) -// -#define CMD_PAUSE(arg) \ - (((cmdSyntaxPtr)(ct56))->cmd[1] = (arg),(cmdSyntaxPtr)(ct56)) - -#define CMD_SUSPEND (cmdSyntaxPtr)(ct57) // Suspend output -#define CMD_UNSUSPEND (cmdSyntaxPtr)(ct58) // Un-Suspend output - -// Set parity-checking options -// -#define CMD_PARCHK(arg) \ - (((cmdSyntaxPtr)(ct59))->cmd[1] = (arg),(cmdSyntaxPtr)(ct59)) - -#define CPK_ENAB 0 // Enable parity checking on input -#define CPK_DSAB 1 // Disable parity checking on input - -#define CMD_BMARK_REQ (cmdSyntaxPtr)(ct60) // Bookmark request - - -// Enable/Disable internal loopback mode -// -#define CMD_INLOOP(arg) \ - (((cmdSyntaxPtr)(ct61))->cmd[1] = (arg),(cmdSyntaxPtr)(ct61)) - -#define CIN_DISABLE 0 // Normal operation (default) -#define CIN_ENABLE 1 // Internal (local) loopback -#define CIN_REMOTE 2 // Remote loopback - -// Specify timeout for hotkeys: Delay will be (arg x 10) milliseconds, arg == 0 -// --> no timeout: wait forever. -// -#define CMD_HOT_TIME(arg) \ - (((cmdSyntaxPtr)(ct62))->cmd[1] = (arg),(cmdSyntaxPtr)(ct62)) - - -// Define (outgoing) xon for receive flow control -// -#define CMD_DEF_OXON(arg) \ - (((cmdSyntaxPtr)(ct63))->cmd[1] = (arg),(cmdSyntaxPtr)(ct63)) - -// Define (outgoing) xoff for receiver flow control -// -#define CMD_DEF_OXOFF(arg) \ - (((cmdSyntaxPtr)(ct64))->cmd[1] = (arg),(cmdSyntaxPtr)(ct64)) - -// Enable/Disable RTS on transmit (1/2 duplex-style) -// -#define CMD_RTS_XMIT(arg) \ - (((cmdSyntaxPtr)(ct65))->cmd[1] = (arg),(cmdSyntaxPtr)(ct65)) - -#define CHD_DISABLE 0 -#define CHD_ENABLE 1 - -// Set high-water-mark level (debugging use only) -// -#define CMD_SETHIGHWAT(arg) \ - (((cmdSyntaxPtr)(ct66))->cmd[1] = (arg),(cmdSyntaxPtr)(ct66)) - -// Start flushing tagged data (tag = 0-14) -// -#define CMD_START_SELFL(tag) \ - (((cmdSyntaxPtr)(ct67))->cmd[1] = (tag),(cmdSyntaxPtr)(ct67)) - -// End flushing tagged data (tag = 0-14) -// -#define CMD_END_SELFL(tag) \ - (((cmdSyntaxPtr)(ct68))->cmd[1] = (tag),(cmdSyntaxPtr)(ct68)) - -#define CMD_HWFLOW_OFF (cmdSyntaxPtr)(ct69) // Disable HW TX flow control -#define CMD_ODSRFL_ENAB (cmdSyntaxPtr)(ct70) // Enable DSR output f/c -#define CMD_ODSRFL_DSAB (cmdSyntaxPtr)(ct71) // Disable DSR output f/c -#define CMD_ODCDFL_ENAB (cmdSyntaxPtr)(ct72) // Enable DCD output f/c -#define CMD_ODCDFL_DSAB (cmdSyntaxPtr)(ct73) // Disable DCD output f/c - -// Set transmit interrupt load level. Count should be an even value 2-12 -// -#define CMD_LOADLEVEL(count) \ - (((cmdSyntaxPtr)(ct74))->cmd[1] = (count),(cmdSyntaxPtr)(ct74)) - -// If reporting DSS changes, map to character sequence FFh, 2, MSR -// -#define CMD_STATDATA(arg) \ - (((cmdSyntaxPtr)(ct75))->cmd[1] = (arg),(cmdSyntaxPtr)(ct75)) - -#define CSTD_DISABLE// Report DSS changes as status packets only (default) -#define CSTD_ENABLE // Report DSS changes as in-band data sequence as well as - // by status packet. - -#define CMD_BREAK_ON (cmdSyntaxPtr)(ct76)// Set break and stop xmit -#define CMD_BREAK_OFF (cmdSyntaxPtr)(ct77)// End break and restart xmit -#define CMD_GETFC (cmdSyntaxPtr)(ct78)// Request for flow control packet - // from board. - -// Transmit this character immediately -// -#define CMD_XMIT_NOW(ch) \ - (((cmdSyntaxPtr)(ct79))->cmd[1] = (ch),(cmdSyntaxPtr)(ct79)) - -// Set baud rate via "divisor latch" -// -#define CMD_DIVISOR_LATCH(which,value) \ - (((cmdSyntaxPtr)(ct80))->cmd[1] = (which), \ - *(USHORT *)(((cmdSyntaxPtr)(ct80))->cmd[2]) = (value), \ - (cmdSyntaxPtr)(ct80)) - -#define CDL_RX 1 // Set receiver rate -#define CDL_TX 2 // Set transmit rate - // (CDL_TX | CDL_RX) Set both rates - -// Request for special diagnostic status pkt from the board. -// -#define CMD_GET_STATUS (cmdSyntaxPtr)(ct81) - -// Request time-stamped transmit character count packet. -// -#define CMD_GET_TXCNT (cmdSyntaxPtr)(ct82) - -// Request time-stamped receive character count packet. -// -#define CMD_GET_RXCNT (cmdSyntaxPtr)(ct83) - -// Request for box/board I.D. packet. -#define CMD_GET_BOXIDS (cmdSyntaxPtr)(ct84) - -// Enable or disable multiple channels according to bit-mapped ushorts box 1-4 -// -#define CMD_ENAB_MULT(enable, box1, box2, box3, box4) \ - (((cmdSytaxPtr)(ct85))->cmd[1] = (enable), \ - *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[2]) = (box1), \ - *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[4]) = (box2), \ - *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[6]) = (box3), \ - *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[8]) = (box4), \ - (cmdSyntaxPtr)(ct85)) - -#define CEM_DISABLE 0 -#define CEM_ENABLE 1 - -// Enable or disable receiver or receiver interrupts (default both enabled) -// -#define CMD_RCV_ENABLE(ch) \ - (((cmdSyntaxPtr)(ct86))->cmd[1] = (ch),(cmdSyntaxPtr)(ct86)) - -#define CRE_OFF 0 // Disable the receiver -#define CRE_ON 1 // Enable the receiver -#define CRE_INTOFF 2 // Disable receiver interrupts (to loadware) -#define CRE_INTON 3 // Enable receiver interrupts (to loadware) - -// Starts up a hardware test process, which runs transparently, and sends a -// STAT_HWFAIL packet in case a hardware failure is detected. -// -#define CMD_HW_TEST (cmdSyntaxPtr)(ct87) - -// Change receiver threshold and timeout value: -// Defaults: timeout = 20mS -// threshold count = 8 when DTRflow not in use, -// threshold count = 5 when DTRflow in use. -// -#define CMD_RCV_THRESHOLD(count,ms) \ - (((cmdSyntaxPtr)(ct88))->cmd[1] = (count), \ - ((cmdSyntaxPtr)(ct88))->cmd[2] = (ms), \ - (cmdSyntaxPtr)(ct88)) - -// Makes the loadware report DSS signals for this channel immediately. -// -#define CMD_DSS_NOW (cmdSyntaxPtr)(ct89) - -// Set the receive silo parameters -// timeout is ms idle wait until delivery (~VTIME) -// threshold is max characters cause interrupt (~VMIN) -// -#define CMD_SET_SILO(timeout,threshold) \ - (((cmdSyntaxPtr)(ct90))->cmd[1] = (timeout), \ - ((cmdSyntaxPtr)(ct90))->cmd[2] = (threshold), \ - (cmdSyntaxPtr)(ct90)) - -// Set timed break in decisecond (1/10s) -// -#define CMD_LBREAK(ds) \ - (((cmdSyntaxPtr)(ct91))->cmd[1] = (ds),(cmdSyntaxPtr)(ct66)) - - - -#endif // I2CMD_H diff --git a/drivers/char/ip2/i2ellis.c b/drivers/char/ip2/i2ellis.c deleted file mode 100644 index 29db44de399f..000000000000 --- a/drivers/char/ip2/i2ellis.c +++ /dev/null @@ -1,1403 +0,0 @@ -/******************************************************************************* -* -* (c) 1998 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Low-level interface code for the device driver -* (This is included source code, not a separate compilation -* module.) -* -*******************************************************************************/ -//--------------------------------------------- -// Function declarations private to this module -//--------------------------------------------- -// Functions called only indirectly through i2eBordStr entries. - -static int iiWriteBuf16(i2eBordStrPtr, unsigned char *, int); -static int iiWriteBuf8(i2eBordStrPtr, unsigned char *, int); -static int iiReadBuf16(i2eBordStrPtr, unsigned char *, int); -static int iiReadBuf8(i2eBordStrPtr, unsigned char *, int); - -static unsigned short iiReadWord16(i2eBordStrPtr); -static unsigned short iiReadWord8(i2eBordStrPtr); -static void iiWriteWord16(i2eBordStrPtr, unsigned short); -static void iiWriteWord8(i2eBordStrPtr, unsigned short); - -static int iiWaitForTxEmptyII(i2eBordStrPtr, int); -static int iiWaitForTxEmptyIIEX(i2eBordStrPtr, int); -static int iiTxMailEmptyII(i2eBordStrPtr); -static int iiTxMailEmptyIIEX(i2eBordStrPtr); -static int iiTrySendMailII(i2eBordStrPtr, unsigned char); -static int iiTrySendMailIIEX(i2eBordStrPtr, unsigned char); - -static unsigned short iiGetMailII(i2eBordStrPtr); -static unsigned short iiGetMailIIEX(i2eBordStrPtr); - -static void iiEnableMailIrqII(i2eBordStrPtr); -static void iiEnableMailIrqIIEX(i2eBordStrPtr); -static void iiWriteMaskII(i2eBordStrPtr, unsigned char); -static void iiWriteMaskIIEX(i2eBordStrPtr, unsigned char); - -static void ii2Nop(void); - -//*************** -//* Static Data * -//*************** - -static int ii2Safe; // Safe I/O address for delay routine - -static int iiDelayed; // Set when the iiResetDelay function is - // called. Cleared when ANY board is reset. -static DEFINE_RWLOCK(Dl_spinlock); - -//******** -//* Code * -//******** - -//======================================================= -// Initialization Routines -// -// iiSetAddress -// iiReset -// iiResetDelay -// iiInitialize -//======================================================= - -//****************************************************************************** -// Function: iiSetAddress(pB, address, delay) -// Parameters: pB - pointer to the board structure -// address - the purported I/O address of the board -// delay - pointer to the 1-ms delay function to use -// in this and any future operations to this board -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// This routine (roughly) checks for address validity, sets the i2eValid OK and -// sets the state to II_STATE_COLD which means that we haven't even sent a reset -// yet. -// -//****************************************************************************** -static int -iiSetAddress( i2eBordStrPtr pB, int address, delayFunc_t delay ) -{ - // Should any failure occur before init is finished... - pB->i2eValid = I2E_INCOMPLETE; - - // Cannot check upper limit except extremely: Might be microchannel - // Address must be on an 8-byte boundary - - if ((unsigned int)address <= 0x100 - || (unsigned int)address >= 0xfff8 - || (address & 0x7) - ) - { - I2_COMPLETE(pB, I2EE_BADADDR); - } - - // Initialize accelerators - pB->i2eBase = address; - pB->i2eData = address + FIFO_DATA; - pB->i2eStatus = address + FIFO_STATUS; - pB->i2ePointer = address + FIFO_PTR; - pB->i2eXMail = address + FIFO_MAIL; - pB->i2eXMask = address + FIFO_MASK; - - // Initialize i/o address for ii2DelayIO - ii2Safe = address + FIFO_NOP; - - // Initialize the delay routine - pB->i2eDelay = ((delay != (delayFunc_t)NULL) ? delay : (delayFunc_t)ii2Nop); - - pB->i2eValid = I2E_MAGIC; - pB->i2eState = II_STATE_COLD; - - I2_COMPLETE(pB, I2EE_GOOD); -} - -//****************************************************************************** -// Function: iiReset(pB) -// Parameters: pB - pointer to the board structure -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// Attempts to reset the board (see also i2hw.h). Normally, we would use this to -// reset a board immediately after iiSetAddress(), but it is valid to reset a -// board from any state, say, in order to change or re-load loadware. (Under -// such circumstances, no reason to re-run iiSetAddress(), which is why it is a -// separate routine and not included in this routine. -// -//****************************************************************************** -static int -iiReset(i2eBordStrPtr pB) -{ - // Magic number should be set, else even the address is suspect - if (pB->i2eValid != I2E_MAGIC) - { - I2_COMPLETE(pB, I2EE_BADMAGIC); - } - - outb(0, pB->i2eBase + FIFO_RESET); /* Any data will do */ - iiDelay(pB, 50); // Pause between resets - outb(0, pB->i2eBase + FIFO_RESET); /* Second reset */ - - // We must wait before even attempting to read anything from the FIFO: the - // board's P.O.S.T may actually attempt to read and write its end of the - // FIFO in order to check flags, loop back (where supported), etc. On - // completion of this testing it would reset the FIFO, and on completion - // of all // P.O.S.T., write the message. We must not mistake data which - // might have been sent for testing as part of the reset message. To - // better utilize time, say, when resetting several boards, we allow the - // delay to be performed externally; in this way the caller can reset - // several boards, delay a single time, then call the initialization - // routine for all. - - pB->i2eState = II_STATE_RESET; - - iiDelayed = 0; // i.e., the delay routine hasn't been called since the most - // recent reset. - - // Ensure anything which would have been of use to standard loadware is - // blanked out, since board has now forgotten everything!. - - pB->i2eUsingIrq = I2_IRQ_UNDEFINED; /* to not use an interrupt so far */ - pB->i2eWaitingForEmptyFifo = 0; - pB->i2eOutMailWaiting = 0; - pB->i2eChannelPtr = NULL; - pB->i2eChannelCnt = 0; - - pB->i2eLeadoffWord[0] = 0; - pB->i2eFifoInInts = 0; - pB->i2eFifoOutInts = 0; - pB->i2eFatalTrap = NULL; - pB->i2eFatal = 0; - - I2_COMPLETE(pB, I2EE_GOOD); -} - -//****************************************************************************** -// Function: iiResetDelay(pB) -// Parameters: pB - pointer to the board structure -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// Using the delay defined in board structure, waits two seconds (for board to -// reset). -// -//****************************************************************************** -static int -iiResetDelay(i2eBordStrPtr pB) -{ - if (pB->i2eValid != I2E_MAGIC) { - I2_COMPLETE(pB, I2EE_BADMAGIC); - } - if (pB->i2eState != II_STATE_RESET) { - I2_COMPLETE(pB, I2EE_BADSTATE); - } - iiDelay(pB,2000); /* Now we wait for two seconds. */ - iiDelayed = 1; /* Delay has been called: ok to initialize */ - I2_COMPLETE(pB, I2EE_GOOD); -} - -//****************************************************************************** -// Function: iiInitialize(pB) -// Parameters: pB - pointer to the board structure -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// Attempts to read the Power-on reset message. Initializes any remaining fields -// in the pB structure. -// -// This should be called as the third step of a process beginning with -// iiReset(), then iiResetDelay(). This routine checks to see that the structure -// is "valid" and in the reset state, also confirms that the delay routine has -// been called since the latest reset (to any board! overly strong!). -// -//****************************************************************************** -static int -iiInitialize(i2eBordStrPtr pB) -{ - int itemp; - unsigned char c; - unsigned short utemp; - unsigned int ilimit; - - if (pB->i2eValid != I2E_MAGIC) - { - I2_COMPLETE(pB, I2EE_BADMAGIC); - } - - if (pB->i2eState != II_STATE_RESET || !iiDelayed) - { - I2_COMPLETE(pB, I2EE_BADSTATE); - } - - // In case there is a failure short of our completely reading the power-up - // message. - pB->i2eValid = I2E_INCOMPLETE; - - - // Now attempt to read the message. - - for (itemp = 0; itemp < sizeof(porStr); itemp++) - { - // We expect the entire message is ready. - if (!I2_HAS_INPUT(pB)) { - pB->i2ePomSize = itemp; - I2_COMPLETE(pB, I2EE_PORM_SHORT); - } - - pB->i2ePom.c[itemp] = c = inb(pB->i2eData); - - // We check the magic numbers as soon as they are supposed to be read - // (rather than after) to minimize effect of reading something we - // already suspect can't be "us". - if ( (itemp == POR_1_INDEX && c != POR_MAGIC_1) || - (itemp == POR_2_INDEX && c != POR_MAGIC_2)) - { - pB->i2ePomSize = itemp+1; - I2_COMPLETE(pB, I2EE_BADMAGIC); - } - } - - pB->i2ePomSize = itemp; - - // Ensure that this was all the data... - if (I2_HAS_INPUT(pB)) - I2_COMPLETE(pB, I2EE_PORM_LONG); - - // For now, we'll fail to initialize if P.O.S.T reports bad chip mapper: - // Implying we will not be able to download any code either: That's ok: the - // condition is pretty explicit. - if (pB->i2ePom.e.porDiag1 & POR_BAD_MAPPER) - { - I2_COMPLETE(pB, I2EE_POSTERR); - } - - // Determine anything which must be done differently depending on the family - // of boards! - switch (pB->i2ePom.e.porID & POR_ID_FAMILY) - { - case POR_ID_FII: // IntelliPort-II - - pB->i2eFifoStyle = FIFO_II; - pB->i2eFifoSize = 512; // 512 bytes, always - pB->i2eDataWidth16 = false; - - pB->i2eMaxIrq = 15; // Because board cannot tell us it is in an 8-bit - // slot, we do allow it to be done (documentation!) - - pB->i2eGoodMap[1] = - pB->i2eGoodMap[2] = - pB->i2eGoodMap[3] = - pB->i2eChannelMap[1] = - pB->i2eChannelMap[2] = - pB->i2eChannelMap[3] = 0; - - switch (pB->i2ePom.e.porID & POR_ID_SIZE) - { - case POR_ID_II_4: - pB->i2eGoodMap[0] = - pB->i2eChannelMap[0] = 0x0f; // four-port - - // Since porPorts1 is based on the Hardware ID register, the numbers - // should always be consistent for IntelliPort-II. Ditto below... - if (pB->i2ePom.e.porPorts1 != 4) - { - I2_COMPLETE(pB, I2EE_INCONSIST); - } - break; - - case POR_ID_II_8: - case POR_ID_II_8R: - pB->i2eGoodMap[0] = - pB->i2eChannelMap[0] = 0xff; // Eight port - if (pB->i2ePom.e.porPorts1 != 8) - { - I2_COMPLETE(pB, I2EE_INCONSIST); - } - break; - - case POR_ID_II_6: - pB->i2eGoodMap[0] = - pB->i2eChannelMap[0] = 0x3f; // Six Port - if (pB->i2ePom.e.porPorts1 != 6) - { - I2_COMPLETE(pB, I2EE_INCONSIST); - } - break; - } - - // Fix up the "good channel list based on any errors reported. - if (pB->i2ePom.e.porDiag1 & POR_BAD_UART1) - { - pB->i2eGoodMap[0] &= ~0x0f; - } - - if (pB->i2ePom.e.porDiag1 & POR_BAD_UART2) - { - pB->i2eGoodMap[0] &= ~0xf0; - } - - break; // POR_ID_FII case - - case POR_ID_FIIEX: // IntelliPort-IIEX - - pB->i2eFifoStyle = FIFO_IIEX; - - itemp = pB->i2ePom.e.porFifoSize; - - // Implicit assumption that fifo would not grow beyond 32k, - // nor would ever be less than 256. - - if (itemp < 8 || itemp > 15) - { - I2_COMPLETE(pB, I2EE_INCONSIST); - } - pB->i2eFifoSize = (1 << itemp); - - // These are based on what P.O.S.T thinks should be there, based on - // box ID registers - ilimit = pB->i2ePom.e.porNumBoxes; - if (ilimit > ABS_MAX_BOXES) - { - ilimit = ABS_MAX_BOXES; - } - - // For as many boxes as EXIST, gives the type of box. - // Added 8/6/93: check for the ISA-4 (asic) which looks like an - // expandable but for whom "8 or 16?" is not the right question. - - utemp = pB->i2ePom.e.porFlags; - if (utemp & POR_CEX4) - { - pB->i2eChannelMap[0] = 0x000f; - } else { - utemp &= POR_BOXES; - for (itemp = 0; itemp < ilimit; itemp++) - { - pB->i2eChannelMap[itemp] = - ((utemp & POR_BOX_16) ? 0xffff : 0x00ff); - utemp >>= 1; - } - } - - // These are based on what P.O.S.T actually found. - - utemp = (pB->i2ePom.e.porPorts2 << 8) + pB->i2ePom.e.porPorts1; - - for (itemp = 0; itemp < ilimit; itemp++) - { - pB->i2eGoodMap[itemp] = 0; - if (utemp & 1) pB->i2eGoodMap[itemp] |= 0x000f; - if (utemp & 2) pB->i2eGoodMap[itemp] |= 0x00f0; - if (utemp & 4) pB->i2eGoodMap[itemp] |= 0x0f00; - if (utemp & 8) pB->i2eGoodMap[itemp] |= 0xf000; - utemp >>= 4; - } - - // Now determine whether we should transfer in 8 or 16-bit mode. - switch (pB->i2ePom.e.porBus & (POR_BUS_SLOT16 | POR_BUS_DIP16) ) - { - case POR_BUS_SLOT16 | POR_BUS_DIP16: - pB->i2eDataWidth16 = true; - pB->i2eMaxIrq = 15; - break; - - case POR_BUS_SLOT16: - pB->i2eDataWidth16 = false; - pB->i2eMaxIrq = 15; - break; - - case 0: - case POR_BUS_DIP16: // In an 8-bit slot, DIP switch don't care. - default: - pB->i2eDataWidth16 = false; - pB->i2eMaxIrq = 7; - break; - } - break; // POR_ID_FIIEX case - - default: // Unknown type of board - I2_COMPLETE(pB, I2EE_BAD_FAMILY); - break; - } // End the switch based on family - - // Temporarily, claim there is no room in the outbound fifo. - // We will maintain this whenever we check for an empty outbound FIFO. - pB->i2eFifoRemains = 0; - - // Now, based on the bus type, should we expect to be able to re-configure - // interrupts (say, for testing purposes). - switch (pB->i2ePom.e.porBus & POR_BUS_TYPE) - { - case POR_BUS_T_ISA: - case POR_BUS_T_UNK: // If the type of bus is undeclared, assume ok. - case POR_BUS_T_MCA: - case POR_BUS_T_EISA: - break; - default: - I2_COMPLETE(pB, I2EE_BADBUS); - } - - if (pB->i2eDataWidth16) - { - pB->i2eWriteBuf = iiWriteBuf16; - pB->i2eReadBuf = iiReadBuf16; - pB->i2eWriteWord = iiWriteWord16; - pB->i2eReadWord = iiReadWord16; - } else { - pB->i2eWriteBuf = iiWriteBuf8; - pB->i2eReadBuf = iiReadBuf8; - pB->i2eWriteWord = iiWriteWord8; - pB->i2eReadWord = iiReadWord8; - } - - switch(pB->i2eFifoStyle) - { - case FIFO_II: - pB->i2eWaitForTxEmpty = iiWaitForTxEmptyII; - pB->i2eTxMailEmpty = iiTxMailEmptyII; - pB->i2eTrySendMail = iiTrySendMailII; - pB->i2eGetMail = iiGetMailII; - pB->i2eEnableMailIrq = iiEnableMailIrqII; - pB->i2eWriteMask = iiWriteMaskII; - - break; - - case FIFO_IIEX: - pB->i2eWaitForTxEmpty = iiWaitForTxEmptyIIEX; - pB->i2eTxMailEmpty = iiTxMailEmptyIIEX; - pB->i2eTrySendMail = iiTrySendMailIIEX; - pB->i2eGetMail = iiGetMailIIEX; - pB->i2eEnableMailIrq = iiEnableMailIrqIIEX; - pB->i2eWriteMask = iiWriteMaskIIEX; - - break; - - default: - I2_COMPLETE(pB, I2EE_INCONSIST); - } - - // Initialize state information. - pB->i2eState = II_STATE_READY; // Ready to load loadware. - - // Some Final cleanup: - // For some boards, the bootstrap firmware may perform some sort of test - // resulting in a stray character pending in the incoming mailbox. If one is - // there, it should be read and discarded, especially since for the standard - // firmware, it's the mailbox that interrupts the host. - - pB->i2eStartMail = iiGetMail(pB); - - // Throw it away and clear the mailbox structure element - pB->i2eStartMail = NO_MAIL_HERE; - - // Everything is ok now, return with good status/ - - pB->i2eValid = I2E_MAGIC; - I2_COMPLETE(pB, I2EE_GOOD); -} - -//****************************************************************************** -// Function: ii2DelayTimer(mseconds) -// Parameters: mseconds - number of milliseconds to delay -// -// Returns: Nothing -// -// Description: -// -// This routine delays for approximately mseconds milliseconds and is intended -// to be called indirectly through i2Delay field in i2eBordStr. It uses the -// Linux timer_list mechanism. -// -// The Linux timers use a unit called "jiffies" which are 10mS in the Intel -// architecture. This function rounds the delay period up to the next "jiffy". -// In the Alpha architecture the "jiffy" is 1mS, but this driver is not intended -// for Alpha platforms at this time. -// -//****************************************************************************** -static void -ii2DelayTimer(unsigned int mseconds) -{ - msleep_interruptible(mseconds); -} - -#if 0 -//static void ii2DelayIO(unsigned int); -//****************************************************************************** -// !!! Not Used, this is DOS crap, some of you young folks may be interested in -// in how things were done in the stone age of caculating machines !!! -// Function: ii2DelayIO(mseconds) -// Parameters: mseconds - number of milliseconds to delay -// -// Returns: Nothing -// -// Description: -// -// This routine delays for approximately mseconds milliseconds and is intended -// to be called indirectly through i2Delay field in i2eBordStr. It is intended -// for use where a clock-based function is impossible: for example, DOS drivers. -// -// This function uses the IN instruction to place bounds on the timing and -// assumes that ii2Safe has been set. This is because I/O instructions are not -// subject to caching and will therefore take a certain minimum time. To ensure -// the delay is at least long enough on fast machines, it is based on some -// fastest-case calculations. On slower machines this may cause VERY long -// delays. (3 x fastest case). In the fastest case, everything is cached except -// the I/O instruction itself. -// -// Timing calculations: -// The fastest bus speed for I/O operations is likely to be 10 MHz. The I/O -// operation in question is a byte operation to an odd address. For 8-bit -// operations, the architecture generally enforces two wait states. At 10 MHz, a -// single cycle time is 100nS. A read operation at two wait states takes 6 -// cycles for a total time of 600nS. Therefore approximately 1666 iterations -// would be required to generate a single millisecond delay. The worst -// (reasonable) case would be an 8MHz system with no cacheing. In this case, the -// I/O instruction would take 125nS x 6 cyles = 750 nS. More importantly, code -// fetch of other instructions in the loop would take time (zero wait states, -// however) and would be hard to estimate. This is minimized by using in-line -// assembler for the in inner loop of IN instructions. This consists of just a -// few bytes. So we'll guess about four code fetches per loop. Each code fetch -// should take four cycles, so we have 125nS * 8 = 1000nS. Worst case then is -// that what should have taken 1 mS takes instead 1666 * (1750) = 2.9 mS. -// -// So much for theoretical timings: results using 1666 value on some actual -// machines: -// IBM 286 6MHz 3.15 mS -// Zenith 386 33MHz 2.45 mS -// (brandX) 386 33MHz 1.90 mS (has cache) -// (brandY) 486 33MHz 2.35 mS -// NCR 486 ?? 1.65 mS (microchannel) -// -// For most machines, it is probably safe to scale this number back (remember, -// for robust operation use an actual timed delay if possible), so we are using -// a value of 1190. This yields 1.17 mS for the fastest machine in our sample, -// 1.75 mS for typical 386 machines, and 2.25 mS the absolute slowest machine. -// -// 1/29/93: -// The above timings are too slow. Actual cycle times might be faster. ISA cycle -// times could approach 500 nS, and ... -// The IBM model 77 being microchannel has no wait states for 8-bit reads and -// seems to be accessing the I/O at 440 nS per access (from start of one to -// start of next). This would imply we need 1000/.440 = 2272 iterations to -// guarantee we are fast enough. In actual testing, we see that 2 * 1190 are in -// fact enough. For diagnostics, we keep the level at 1190, but developers note -// this needs tuning. -// -// Safe assumption: 2270 i/o reads = 1 millisecond -// -//****************************************************************************** - - -static int ii2DelValue = 1190; // See timing calculations below - // 1666 for fastest theoretical machine - // 1190 safe for most fast 386 machines - // 1000 for fastest machine tested here - // 540 (sic) for AT286/6Mhz -static void -ii2DelayIO(unsigned int mseconds) -{ - if (!ii2Safe) - return; /* Do nothing if this variable uninitialized */ - - while(mseconds--) { - int i = ii2DelValue; - while ( i-- ) { - inb(ii2Safe); - } - } -} -#endif - -//****************************************************************************** -// Function: ii2Nop() -// Parameters: None -// -// Returns: Nothing -// -// Description: -// -// iiInitialize will set i2eDelay to this if the delay parameter is NULL. This -// saves checking for a NULL pointer at every call. -//****************************************************************************** -static void -ii2Nop(void) -{ - return; // no mystery here -} - -//======================================================= -// Routines which are available in 8/16-bit versions, or -// in different fifo styles. These are ALL called -// indirectly through the board structure. -//======================================================= - -//****************************************************************************** -// Function: iiWriteBuf16(pB, address, count) -// Parameters: pB - pointer to board structure -// address - address of data to write -// count - number of data bytes to write -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// Writes 'count' bytes from 'address' to the data fifo specified by the board -// structure pointer pB. Should count happen to be odd, an extra pad byte is -// sent (identity unknown...). Uses 16-bit (word) operations. Is called -// indirectly through pB->i2eWriteBuf. -// -//****************************************************************************** -static int -iiWriteBuf16(i2eBordStrPtr pB, unsigned char *address, int count) -{ - // Rudimentary sanity checking here. - if (pB->i2eValid != I2E_MAGIC) - I2_COMPLETE(pB, I2EE_INVALID); - - I2_OUTSW(pB->i2eData, address, count); - - I2_COMPLETE(pB, I2EE_GOOD); -} - -//****************************************************************************** -// Function: iiWriteBuf8(pB, address, count) -// Parameters: pB - pointer to board structure -// address - address of data to write -// count - number of data bytes to write -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// Writes 'count' bytes from 'address' to the data fifo specified by the board -// structure pointer pB. Should count happen to be odd, an extra pad byte is -// sent (identity unknown...). This is to be consistent with the 16-bit version. -// Uses 8-bit (byte) operations. Is called indirectly through pB->i2eWriteBuf. -// -//****************************************************************************** -static int -iiWriteBuf8(i2eBordStrPtr pB, unsigned char *address, int count) -{ - /* Rudimentary sanity checking here */ - if (pB->i2eValid != I2E_MAGIC) - I2_COMPLETE(pB, I2EE_INVALID); - - I2_OUTSB(pB->i2eData, address, count); - - I2_COMPLETE(pB, I2EE_GOOD); -} - -//****************************************************************************** -// Function: iiReadBuf16(pB, address, count) -// Parameters: pB - pointer to board structure -// address - address to put data read -// count - number of data bytes to read -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// Reads 'count' bytes into 'address' from the data fifo specified by the board -// structure pointer pB. Should count happen to be odd, an extra pad byte is -// received (identity unknown...). Uses 16-bit (word) operations. Is called -// indirectly through pB->i2eReadBuf. -// -//****************************************************************************** -static int -iiReadBuf16(i2eBordStrPtr pB, unsigned char *address, int count) -{ - // Rudimentary sanity checking here. - if (pB->i2eValid != I2E_MAGIC) - I2_COMPLETE(pB, I2EE_INVALID); - - I2_INSW(pB->i2eData, address, count); - - I2_COMPLETE(pB, I2EE_GOOD); -} - -//****************************************************************************** -// Function: iiReadBuf8(pB, address, count) -// Parameters: pB - pointer to board structure -// address - address to put data read -// count - number of data bytes to read -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// Reads 'count' bytes into 'address' from the data fifo specified by the board -// structure pointer pB. Should count happen to be odd, an extra pad byte is -// received (identity unknown...). This to match the 16-bit behaviour. Uses -// 8-bit (byte) operations. Is called indirectly through pB->i2eReadBuf. -// -//****************************************************************************** -static int -iiReadBuf8(i2eBordStrPtr pB, unsigned char *address, int count) -{ - // Rudimentary sanity checking here. - if (pB->i2eValid != I2E_MAGIC) - I2_COMPLETE(pB, I2EE_INVALID); - - I2_INSB(pB->i2eData, address, count); - - I2_COMPLETE(pB, I2EE_GOOD); -} - -//****************************************************************************** -// Function: iiReadWord16(pB) -// Parameters: pB - pointer to board structure -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// Returns the word read from the data fifo specified by the board-structure -// pointer pB. Uses a 16-bit operation. Is called indirectly through -// pB->i2eReadWord. -// -//****************************************************************************** -static unsigned short -iiReadWord16(i2eBordStrPtr pB) -{ - return inw(pB->i2eData); -} - -//****************************************************************************** -// Function: iiReadWord8(pB) -// Parameters: pB - pointer to board structure -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// Returns the word read from the data fifo specified by the board-structure -// pointer pB. Uses two 8-bit operations. Bytes are assumed to be LSB first. Is -// called indirectly through pB->i2eReadWord. -// -//****************************************************************************** -static unsigned short -iiReadWord8(i2eBordStrPtr pB) -{ - unsigned short urs; - - urs = inb(pB->i2eData); - - return (inb(pB->i2eData) << 8) | urs; -} - -//****************************************************************************** -// Function: iiWriteWord16(pB, value) -// Parameters: pB - pointer to board structure -// value - data to write -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// Writes the word 'value' to the data fifo specified by the board-structure -// pointer pB. Uses 16-bit operation. Is called indirectly through -// pB->i2eWriteWord. -// -//****************************************************************************** -static void -iiWriteWord16(i2eBordStrPtr pB, unsigned short value) -{ - outw((int)value, pB->i2eData); -} - -//****************************************************************************** -// Function: iiWriteWord8(pB, value) -// Parameters: pB - pointer to board structure -// value - data to write -// -// Returns: True if everything appears copacetic. -// False if there is any error: the pB->i2eError field has the error -// -// Description: -// -// Writes the word 'value' to the data fifo specified by the board-structure -// pointer pB. Uses two 8-bit operations (writes LSB first). Is called -// indirectly through pB->i2eWriteWord. -// -//****************************************************************************** -static void -iiWriteWord8(i2eBordStrPtr pB, unsigned short value) -{ - outb((char)value, pB->i2eData); - outb((char)(value >> 8), pB->i2eData); -} - -//****************************************************************************** -// Function: iiWaitForTxEmptyII(pB, mSdelay) -// Parameters: pB - pointer to board structure -// mSdelay - period to wait before returning -// -// Returns: True if the FIFO is empty. -// False if it not empty in the required time: the pB->i2eError -// field has the error. -// -// Description: -// -// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if -// not empty by the required time, returns false and error in pB->i2eError, -// otherwise returns true. -// -// mSdelay == 0 is taken to mean must be empty on the first test. -// -// This version operates on IntelliPort-II - style FIFO's -// -// Note this routine is organized so that if status is ok there is no delay at -// all called either before or after the test. Is called indirectly through -// pB->i2eWaitForTxEmpty. -// -//****************************************************************************** -static int -iiWaitForTxEmptyII(i2eBordStrPtr pB, int mSdelay) -{ - unsigned long flags; - int itemp; - - for (;;) - { - // This routine hinges on being able to see the "other" status register - // (as seen by the local processor). His incoming fifo is our outgoing - // FIFO. - // - // By the nature of this routine, you would be using this as part of a - // larger atomic context: i.e., you would use this routine to ensure the - // fifo empty, then act on this information. Between these two halves, - // you will generally not want to service interrupts or in any way - // disrupt the assumptions implicit in the larger context. - // - // Even worse, however, this routine "shifts" the status register to - // point to the local status register which is not the usual situation. - // Therefore for extra safety, we force the critical section to be - // completely atomic, and pick up after ourselves before allowing any - // interrupts of any kind. - - - write_lock_irqsave(&Dl_spinlock, flags); - outb(SEL_COMMAND, pB->i2ePointer); - outb(SEL_CMD_SH, pB->i2ePointer); - - itemp = inb(pB->i2eStatus); - - outb(SEL_COMMAND, pB->i2ePointer); - outb(SEL_CMD_UNSH, pB->i2ePointer); - - if (itemp & ST_IN_EMPTY) - { - I2_UPDATE_FIFO_ROOM(pB); - write_unlock_irqrestore(&Dl_spinlock, flags); - I2_COMPLETE(pB, I2EE_GOOD); - } - - write_unlock_irqrestore(&Dl_spinlock, flags); - - if (mSdelay-- == 0) - break; - - iiDelay(pB, 1); /* 1 mS granularity on checking condition */ - } - I2_COMPLETE(pB, I2EE_TXE_TIME); -} - -//****************************************************************************** -// Function: iiWaitForTxEmptyIIEX(pB, mSdelay) -// Parameters: pB - pointer to board structure -// mSdelay - period to wait before returning -// -// Returns: True if the FIFO is empty. -// False if it not empty in the required time: the pB->i2eError -// field has the error. -// -// Description: -// -// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if -// not empty by the required time, returns false and error in pB->i2eError, -// otherwise returns true. -// -// mSdelay == 0 is taken to mean must be empty on the first test. -// -// This version operates on IntelliPort-IIEX - style FIFO's -// -// Note this routine is organized so that if status is ok there is no delay at -// all called either before or after the test. Is called indirectly through -// pB->i2eWaitForTxEmpty. -// -//****************************************************************************** -static int -iiWaitForTxEmptyIIEX(i2eBordStrPtr pB, int mSdelay) -{ - unsigned long flags; - - for (;;) - { - // By the nature of this routine, you would be using this as part of a - // larger atomic context: i.e., you would use this routine to ensure the - // fifo empty, then act on this information. Between these two halves, - // you will generally not want to service interrupts or in any way - // disrupt the assumptions implicit in the larger context. - - write_lock_irqsave(&Dl_spinlock, flags); - - if (inb(pB->i2eStatus) & STE_OUT_MT) { - I2_UPDATE_FIFO_ROOM(pB); - write_unlock_irqrestore(&Dl_spinlock, flags); - I2_COMPLETE(pB, I2EE_GOOD); - } - write_unlock_irqrestore(&Dl_spinlock, flags); - - if (mSdelay-- == 0) - break; - - iiDelay(pB, 1); // 1 mS granularity on checking condition - } - I2_COMPLETE(pB, I2EE_TXE_TIME); -} - -//****************************************************************************** -// Function: iiTxMailEmptyII(pB) -// Parameters: pB - pointer to board structure -// -// Returns: True if the transmit mailbox is empty. -// False if it not empty. -// -// Description: -// -// Returns true or false according to whether the transmit mailbox is empty (and -// therefore able to accept more mail) -// -// This version operates on IntelliPort-II - style FIFO's -// -//****************************************************************************** -static int -iiTxMailEmptyII(i2eBordStrPtr pB) -{ - int port = pB->i2ePointer; - outb(SEL_OUTMAIL, port); - return inb(port) == 0; -} - -//****************************************************************************** -// Function: iiTxMailEmptyIIEX(pB) -// Parameters: pB - pointer to board structure -// -// Returns: True if the transmit mailbox is empty. -// False if it not empty. -// -// Description: -// -// Returns true or false according to whether the transmit mailbox is empty (and -// therefore able to accept more mail) -// -// This version operates on IntelliPort-IIEX - style FIFO's -// -//****************************************************************************** -static int -iiTxMailEmptyIIEX(i2eBordStrPtr pB) -{ - return !(inb(pB->i2eStatus) & STE_OUT_MAIL); -} - -//****************************************************************************** -// Function: iiTrySendMailII(pB,mail) -// Parameters: pB - pointer to board structure -// mail - value to write to mailbox -// -// Returns: True if the transmit mailbox is empty, and mail is sent. -// False if it not empty. -// -// Description: -// -// If outgoing mailbox is empty, sends mail and returns true. If outgoing -// mailbox is not empty, returns false. -// -// This version operates on IntelliPort-II - style FIFO's -// -//****************************************************************************** -static int -iiTrySendMailII(i2eBordStrPtr pB, unsigned char mail) -{ - int port = pB->i2ePointer; - - outb(SEL_OUTMAIL, port); - if (inb(port) == 0) { - outb(SEL_OUTMAIL, port); - outb(mail, port); - return 1; - } - return 0; -} - -//****************************************************************************** -// Function: iiTrySendMailIIEX(pB,mail) -// Parameters: pB - pointer to board structure -// mail - value to write to mailbox -// -// Returns: True if the transmit mailbox is empty, and mail is sent. -// False if it not empty. -// -// Description: -// -// If outgoing mailbox is empty, sends mail and returns true. If outgoing -// mailbox is not empty, returns false. -// -// This version operates on IntelliPort-IIEX - style FIFO's -// -//****************************************************************************** -static int -iiTrySendMailIIEX(i2eBordStrPtr pB, unsigned char mail) -{ - if (inb(pB->i2eStatus) & STE_OUT_MAIL) - return 0; - outb(mail, pB->i2eXMail); - return 1; -} - -//****************************************************************************** -// Function: iiGetMailII(pB,mail) -// Parameters: pB - pointer to board structure -// -// Returns: Mailbox data or NO_MAIL_HERE. -// -// Description: -// -// If no mail available, returns NO_MAIL_HERE otherwise returns the data from -// the mailbox, which is guaranteed != NO_MAIL_HERE. -// -// This version operates on IntelliPort-II - style FIFO's -// -//****************************************************************************** -static unsigned short -iiGetMailII(i2eBordStrPtr pB) -{ - if (I2_HAS_MAIL(pB)) { - outb(SEL_INMAIL, pB->i2ePointer); - return inb(pB->i2ePointer); - } else { - return NO_MAIL_HERE; - } -} - -//****************************************************************************** -// Function: iiGetMailIIEX(pB,mail) -// Parameters: pB - pointer to board structure -// -// Returns: Mailbox data or NO_MAIL_HERE. -// -// Description: -// -// If no mail available, returns NO_MAIL_HERE otherwise returns the data from -// the mailbox, which is guaranteed != NO_MAIL_HERE. -// -// This version operates on IntelliPort-IIEX - style FIFO's -// -//****************************************************************************** -static unsigned short -iiGetMailIIEX(i2eBordStrPtr pB) -{ - if (I2_HAS_MAIL(pB)) - return inb(pB->i2eXMail); - else - return NO_MAIL_HERE; -} - -//****************************************************************************** -// Function: iiEnableMailIrqII(pB) -// Parameters: pB - pointer to board structure -// -// Returns: Nothing -// -// Description: -// -// Enables board to interrupt host (only) by writing to host's in-bound mailbox. -// -// This version operates on IntelliPort-II - style FIFO's -// -//****************************************************************************** -static void -iiEnableMailIrqII(i2eBordStrPtr pB) -{ - outb(SEL_MASK, pB->i2ePointer); - outb(ST_IN_MAIL, pB->i2ePointer); -} - -//****************************************************************************** -// Function: iiEnableMailIrqIIEX(pB) -// Parameters: pB - pointer to board structure -// -// Returns: Nothing -// -// Description: -// -// Enables board to interrupt host (only) by writing to host's in-bound mailbox. -// -// This version operates on IntelliPort-IIEX - style FIFO's -// -//****************************************************************************** -static void -iiEnableMailIrqIIEX(i2eBordStrPtr pB) -{ - outb(MX_IN_MAIL, pB->i2eXMask); -} - -//****************************************************************************** -// Function: iiWriteMaskII(pB) -// Parameters: pB - pointer to board structure -// -// Returns: Nothing -// -// Description: -// -// Writes arbitrary value to the mask register. -// -// This version operates on IntelliPort-II - style FIFO's -// -//****************************************************************************** -static void -iiWriteMaskII(i2eBordStrPtr pB, unsigned char value) -{ - outb(SEL_MASK, pB->i2ePointer); - outb(value, pB->i2ePointer); -} - -//****************************************************************************** -// Function: iiWriteMaskIIEX(pB) -// Parameters: pB - pointer to board structure -// -// Returns: Nothing -// -// Description: -// -// Writes arbitrary value to the mask register. -// -// This version operates on IntelliPort-IIEX - style FIFO's -// -//****************************************************************************** -static void -iiWriteMaskIIEX(i2eBordStrPtr pB, unsigned char value) -{ - outb(value, pB->i2eXMask); -} - -//****************************************************************************** -// Function: iiDownloadBlock(pB, pSource, isStandard) -// Parameters: pB - pointer to board structure -// pSource - loadware block to download -// isStandard - True if "standard" loadware, else false. -// -// Returns: Success or Failure -// -// Description: -// -// Downloads a single block (at pSource)to the board referenced by pB. Caller -// sets isStandard to true/false according to whether the "standard" loadware is -// what's being loaded. The normal process, then, is to perform an iiInitialize -// to the board, then perform some number of iiDownloadBlocks using the returned -// state to determine when download is complete. -// -// Possible return values: (see I2ELLIS.H) -// II_DOWN_BADVALID -// II_DOWN_BADFILE -// II_DOWN_CONTINUING -// II_DOWN_GOOD -// II_DOWN_BAD -// II_DOWN_BADSTATE -// II_DOWN_TIMEOUT -// -// Uses the i2eState and i2eToLoad fields (initialized at iiInitialize) to -// determine whether this is the first block, whether to check for magic -// numbers, how many blocks there are to go... -// -//****************************************************************************** -static int -iiDownloadBlock ( i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard) -{ - int itemp; - int loadedFirst; - - if (pB->i2eValid != I2E_MAGIC) return II_DOWN_BADVALID; - - switch(pB->i2eState) - { - case II_STATE_READY: - - // Loading the first block after reset. Must check the magic number of the - // loadfile, store the number of blocks we expect to load. - if (pSource->e.loadMagic != MAGIC_LOADFILE) - { - return II_DOWN_BADFILE; - } - - // Next we store the total number of blocks to load, including this one. - pB->i2eToLoad = 1 + pSource->e.loadBlocksMore; - - // Set the state, store the version numbers. ('Cause this may have come - // from a file - we might want to report these versions and revisions in - // case of an error! - pB->i2eState = II_STATE_LOADING; - pB->i2eLVersion = pSource->e.loadVersion; - pB->i2eLRevision = pSource->e.loadRevision; - pB->i2eLSub = pSource->e.loadSubRevision; - - // The time and date of compilation is also available but don't bother - // storing it for normal purposes. - loadedFirst = 1; - break; - - case II_STATE_LOADING: - loadedFirst = 0; - break; - - default: - return II_DOWN_BADSTATE; - } - - // Now we must be in the II_STATE_LOADING state, and we assume i2eToLoad - // must be positive still, because otherwise we would have cleaned up last - // time and set the state to II_STATE_LOADED. - if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) { - return II_DOWN_TIMEOUT; - } - - if (!iiWriteBuf(pB, pSource->c, LOADWARE_BLOCK_SIZE)) { - return II_DOWN_BADVALID; - } - - // If we just loaded the first block, wait for the fifo to empty an extra - // long time to allow for any special startup code in the firmware, like - // sending status messages to the LCD's. - - if (loadedFirst) { - if (!iiWaitForTxEmpty(pB, MAX_DLOAD_START_TIME)) { - return II_DOWN_TIMEOUT; - } - } - - // Determine whether this was our last block! - if (--(pB->i2eToLoad)) { - return II_DOWN_CONTINUING; // more to come... - } - - // It WAS our last block: Clean up operations... - // ...Wait for last buffer to drain from the board... - if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) { - return II_DOWN_TIMEOUT; - } - // If there were only a single block written, this would come back - // immediately and be harmless, though not strictly necessary. - itemp = MAX_DLOAD_ACK_TIME/10; - while (--itemp) { - if (I2_HAS_INPUT(pB)) { - switch (inb(pB->i2eData)) { - case LOADWARE_OK: - pB->i2eState = - isStandard ? II_STATE_STDLOADED :II_STATE_LOADED; - - // Some revisions of the bootstrap firmware (e.g. ISA-8 1.0.2) - // will, // if there is a debug port attached, require some - // time to send information to the debug port now. It will do - // this before // executing any of the code we just downloaded. - // It may take up to 700 milliseconds. - if (pB->i2ePom.e.porDiag2 & POR_DEBUG_PORT) { - iiDelay(pB, 700); - } - - return II_DOWN_GOOD; - - case LOADWARE_BAD: - default: - return II_DOWN_BAD; - } - } - - iiDelay(pB, 10); // 10 mS granularity on checking condition - } - - // Drop-through --> timed out waiting for firmware confirmation - - pB->i2eState = II_STATE_BADLOAD; - return II_DOWN_TIMEOUT; -} - -//****************************************************************************** -// Function: iiDownloadAll(pB, pSource, isStandard, size) -// Parameters: pB - pointer to board structure -// pSource - loadware block to download -// isStandard - True if "standard" loadware, else false. -// size - size of data to download (in bytes) -// -// Returns: Success or Failure -// -// Description: -// -// Given a pointer to a board structure, a pointer to the beginning of some -// loadware, whether it is considered the "standard loadware", and the size of -// the array in bytes loads the entire array to the board as loadware. -// -// Assumes the board has been freshly reset and the power-up reset message read. -// (i.e., in II_STATE_READY). Complains if state is bad, or if there seems to be -// too much or too little data to load, or if iiDownloadBlock complains. -//****************************************************************************** -static int -iiDownloadAll(i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard, int size) -{ - int status; - - // We know (from context) board should be ready for the first block of - // download. Complain if not. - if (pB->i2eState != II_STATE_READY) return II_DOWN_BADSTATE; - - while (size > 0) { - size -= LOADWARE_BLOCK_SIZE; // How much data should there be left to - // load after the following operation ? - - // Note we just bump pSource by "one", because its size is actually that - // of an entire block, same as LOADWARE_BLOCK_SIZE. - status = iiDownloadBlock(pB, pSource++, isStandard); - - switch(status) - { - case II_DOWN_GOOD: - return ( (size > 0) ? II_DOWN_OVER : II_DOWN_GOOD); - - case II_DOWN_CONTINUING: - break; - - default: - return status; - } - } - - // We shouldn't drop out: it means "while" caught us with nothing left to - // download, yet the previous DownloadBlock did not return complete. Ergo, - // not enough data to match the size byte in the header. - return II_DOWN_UNDER; -} diff --git a/drivers/char/ip2/i2ellis.h b/drivers/char/ip2/i2ellis.h deleted file mode 100644 index fb6df2456018..000000000000 --- a/drivers/char/ip2/i2ellis.h +++ /dev/null @@ -1,566 +0,0 @@ -/******************************************************************************* -* -* (c) 1999 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Mainline code for the device driver -* -*******************************************************************************/ -//------------------------------------------------------------------------------ -// i2ellis.h -// -// IntelliPort-II and IntelliPort-IIEX -// -// Extremely -// Low -// Level -// Interface -// Services -// -// Structure Definitions and declarations for "ELLIS" service routines found in -// i2ellis.c -// -// These routines are based on properties of the IntelliPort-II and -IIEX -// hardware and bootstrap firmware, and are not sensitive to particular -// conventions of any particular loadware. -// -// Unlike i2hw.h, which provides IRONCLAD hardware definitions, the material -// here and in i2ellis.c is intended to provice a useful, but not required, -// layer of insulation from the hardware specifics. -//------------------------------------------------------------------------------ -#ifndef I2ELLIS_H /* To prevent multiple includes */ -#define I2ELLIS_H 1 -//------------------------------------------------ -// Revision History: -// -// 30 September 1991 MAG First Draft Started -// 12 October 1991 ...continued... -// -// 20 December 1996 AKM Linux version -//------------------------------------------------- - -//---------------------- -// Mandatory Includes: -//---------------------- -#include "ip2types.h" -#include "i2hw.h" // The hardware definitions - -//------------------------------------------ -// STAT_BOXIDS packets -//------------------------------------------ -#define MAX_BOX 4 - -typedef struct _bidStat -{ - unsigned char bid_value[MAX_BOX]; -} bidStat, *bidStatPtr; - -// This packet is sent in response to a CMD_GET_BOXIDS bypass command. For -IIEX -// boards, reports the hardware-specific "asynchronous resource register" on -// each expansion box. Boxes not present report 0xff. For -II boards, the first -// element contains 0x80 for 8-port, 0x40 for 4-port boards. - -// Box IDs aka ARR or Async Resource Register (more than you want to know) -// 7 6 5 4 3 2 1 0 -// F F N N L S S S -// ============================= -// F F - Product Family Designator -// =====+++++++++++++++++++++++++++++++ -// 0 0 - Intelliport II EX / ISA-8 -// 1 0 - IntelliServer -// 0 1 - SAC - Port Device (Intelliport III ??? ) -// =====+++++++++++++++++++++++++++++++++++++++ -// N N - Number of Ports -// 0 0 - 8 (eight) -// 0 1 - 4 (four) -// 1 0 - 12 (twelve) -// 1 1 - 16 (sixteen) -// =++++++++++++++++++++++++++++++++++ -// L - LCD Display Module Present -// 0 - No -// 1 - LCD module present -// =========+++++++++++++++++++++++++++++++++++++ -// S S S - Async Signals Supported Designator -// 0 0 0 - 8dss, Mod DCE DB25 Female -// 0 0 1 - 6dss, RJ-45 -// 0 1 0 - RS-232/422 dss, DB25 Female -// 0 1 1 - RS-232/422 dss, separate 232/422 DB25 Female -// 1 0 0 - 6dss, 921.6 I/F with ST654's -// 1 0 1 - RS-423/232 8dss, RJ-45 10Pin -// 1 1 0 - 6dss, Mod DCE DB25 Female -// 1 1 1 - NO BOX PRESENT - -#define FF(c) ((c & 0xC0) >> 6) -#define NN(c) ((c & 0x30) >> 4) -#define L(c) ((c & 0x08) >> 3) -#define SSS(c) (c & 0x07) - -#define BID_HAS_654(x) (SSS(x) == 0x04) -#define BID_NO_BOX 0xff /* no box */ -#define BID_8PORT 0x80 /* IP2-8 port */ -#define BID_4PORT 0x81 /* IP2-4 port */ -#define BID_EXP_MASK 0x30 /* IP2-EX */ -#define BID_EXP_8PORT 0x00 /* 8, */ -#define BID_EXP_4PORT 0x10 /* 4, */ -#define BID_EXP_UNDEF 0x20 /* UNDEF, */ -#define BID_EXP_16PORT 0x30 /* 16, */ -#define BID_LCD_CTRL 0x08 /* LCD Controller */ -#define BID_LCD_NONE 0x00 /* - no controller present */ -#define BID_LCD_PRES 0x08 /* - controller present */ -#define BID_CON_MASK 0x07 /* - connector pinouts */ -#define BID_CON_DB25 0x00 /* - DB-25 F */ -#define BID_CON_RJ45 0x01 /* - rj45 */ - -//------------------------------------------------------------------------------ -// i2eBordStr -// -// This structure contains all the information the ELLIS routines require in -// dealing with a particular board. -//------------------------------------------------------------------------------ -// There are some queues here which are guaranteed to never contain the entry -// for a single channel twice. So they must be slightly larger to allow -// unambiguous full/empty management -// -#define CH_QUEUE_SIZE ABS_MOST_PORTS+2 - -typedef struct _i2eBordStr -{ - porStr i2ePom; // Structure containing the power-on message. - - unsigned short i2ePomSize; - // The number of bytes actually read if - // different from sizeof i2ePom, indicates - // there is an error! - - unsigned short i2eStartMail; - // Contains whatever inbound mailbox data - // present at startup. NO_MAIL_HERE indicates - // nothing was present. No special - // significance as of this writing, but may be - // useful for diagnostic reasons. - - unsigned short i2eValid; - // Indicates validity of the structure; if - // i2eValid == I2E_MAGIC, then we can trust - // the other fields. Some (especially - // initialization) functions are good about - // checking for validity. Many functions do - // not, it being assumed that the larger - // context assures we are using a valid - // i2eBordStrPtr. - - unsigned short i2eError; - // Used for returning an error condition from - // several functions which use i2eBordStrPtr - // as an argument. - - // Accelerators to characterize separate features of a board, derived from a - // number of sources. - - unsigned short i2eFifoSize; - // Always, the size of the FIFO. For - // IntelliPort-II, always the same, for -IIEX - // taken from the Power-On reset message. - - volatile - unsigned short i2eFifoRemains; - // Used during normal operation to indicate a - // lower bound on the amount of data which - // might be in the outbound fifo. - - unsigned char i2eFifoStyle; - // Accelerator which tells which style (-II or - // -IIEX) FIFO we are using. - - unsigned char i2eDataWidth16; - // Accelerator which tells whether we should - // do 8 or 16-bit data transfers. - - unsigned char i2eMaxIrq; - // The highest allowable IRQ, based on the - // slot size. - - // Accelerators for various addresses on the board - int i2eBase; // I/O Address of the Board - int i2eData; // From here data transfers happen - int i2eStatus; // From here status reads happen - int i2ePointer; // (IntelliPort-II: pointer/commands) - int i2eXMail; // (IntelliPOrt-IIEX: mailboxes - int i2eXMask; // (IntelliPort-IIEX: mask write - - //------------------------------------------------------- - // Information presented in a common format across boards - // For each box, bit map of the channels present. Box closest to - // the host is box 0. LSB is channel 0. IntelliPort-II (non-expandable) - // is taken to be box 0. These are derived from product i.d. registers. - - unsigned short i2eChannelMap[ABS_MAX_BOXES]; - - // Same as above, except each is derived from firmware attempting to detect - // the uart presence (by reading a valid GFRCR register). If bits are set in - // i2eChannelMap and not in i2eGoodMap, there is a potential problem. - - unsigned short i2eGoodMap[ABS_MAX_BOXES]; - - // --------------------------- - // For indirect function calls - - // Routine to cause an N-millisecond delay: Patched by the ii2Initialize - // function. - - void (*i2eDelay)(unsigned int); - - // Routine to write N bytes to the board through the FIFO. Returns true if - // all copacetic, otherwise returns false and error is in i2eError field. - // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER. - - int (*i2eWriteBuf)(struct _i2eBordStr *, unsigned char *, int); - - // Routine to read N bytes from the board through the FIFO. Returns true if - // copacetic, otherwise returns false and error in i2eError. - // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER. - - int (*i2eReadBuf)(struct _i2eBordStr *, unsigned char *, int); - - // Returns a word from FIFO. Will use 2 byte operations if needed. - - unsigned short (*i2eReadWord)(struct _i2eBordStr *); - - // Writes a word to FIFO. Will use 2 byte operations if needed. - - void (*i2eWriteWord)(struct _i2eBordStr *, unsigned short); - - // Waits specified time for the Transmit FIFO to go empty. Returns true if - // ok, otherwise returns false and error in i2eError. - - int (*i2eWaitForTxEmpty)(struct _i2eBordStr *, int); - - // Returns true or false according to whether the outgoing mailbox is empty. - - int (*i2eTxMailEmpty)(struct _i2eBordStr *); - - // Checks whether outgoing mailbox is empty. If so, sends mail and returns - // true. Otherwise returns false. - - int (*i2eTrySendMail)(struct _i2eBordStr *, unsigned char); - - // If no mail available, returns NO_MAIL_HERE, else returns the value in the - // mailbox (guaranteed can't be NO_MAIL_HERE). - - unsigned short (*i2eGetMail)(struct _i2eBordStr *); - - // Enables the board to interrupt the host when it writes to the mailbox. - // Irqs will not occur, however, until the loadware separately enables - // interrupt generation to the host. The standard loadware does this in - // response to a command packet sent by the host. (Also, disables - // any other potential interrupt sources from the board -- other than the - // inbound mailbox). - - void (*i2eEnableMailIrq)(struct _i2eBordStr *); - - // Writes an arbitrary value to the mask register. - - void (*i2eWriteMask)(struct _i2eBordStr *, unsigned char); - - - // State information - - // During downloading, indicates the number of blocks remaining to download - // to the board. - - short i2eToLoad; - - // State of board (see manifests below) (e.g., whether in reset condition, - // whether standard loadware is installed, etc. - - unsigned char i2eState; - - // These three fields are only valid when there is loadware running on the - // board. (i2eState == II_STATE_LOADED or i2eState == II_STATE_STDLOADED ) - - unsigned char i2eLVersion; // Loadware version - unsigned char i2eLRevision; // Loadware revision - unsigned char i2eLSub; // Loadware subrevision - - // Flags which only have meaning in the context of the standard loadware. - // Somewhat violates the layering concept, but there is so little additional - // needed at the board level (while much additional at the channel level), - // that this beats maintaining two different per-board structures. - - // Indicates which IRQ the board has been initialized (from software) to use - // For MicroChannel boards, any value different from IRQ_UNDEFINED means - // that the software command has been sent to enable interrupts (or specify - // they are disabled). Special value: IRQ_UNDEFINED indicates that the - // software command to select the interrupt has not yet been sent, therefore - // (since the standard loadware insists that it be sent before any other - // packets are sent) no other packets should be sent yet. - - unsigned short i2eUsingIrq; - - // This is set when we hit the MB_OUT_STUFFED mailbox, which prevents us - // putting more in the mailbox until an appropriate mailbox message is - // received. - - unsigned char i2eWaitingForEmptyFifo; - - // Any mailbox bits waiting to be sent to the board are OR'ed in here. - - unsigned char i2eOutMailWaiting; - - // The head of any incoming packet is read into here, is then examined and - // we dispatch accordingly. - - unsigned short i2eLeadoffWord[1]; - - // Running counter of interrupts where the mailbox indicated incoming data. - - unsigned short i2eFifoInInts; - - // Running counter of interrupts where the mailbox indicated outgoing data - // had been stripped. - - unsigned short i2eFifoOutInts; - - // If not void, gives the address of a routine to call if fatal board error - // is found (only applies to standard l/w). - - void (*i2eFatalTrap)(struct _i2eBordStr *); - - // Will point to an array of some sort of channel structures (whose format - // is unknown at this level, being a function of what loadware is - // installed and the code configuration (max sizes of buffers, etc.)). - - void *i2eChannelPtr; - - // Set indicates that the board has gone fatal. - - unsigned short i2eFatal; - - // The number of elements pointed to by i2eChannelPtr. - - unsigned short i2eChannelCnt; - - // Ring-buffers of channel structures whose channels have particular needs. - - rwlock_t Fbuf_spinlock; - volatile - unsigned short i2Fbuf_strip; // Strip index - volatile - unsigned short i2Fbuf_stuff; // Stuff index - void *i2Fbuf[CH_QUEUE_SIZE]; // An array of channel pointers - // of channels who need to send - // flow control packets. - rwlock_t Dbuf_spinlock; - volatile - unsigned short i2Dbuf_strip; // Strip index - volatile - unsigned short i2Dbuf_stuff; // Stuff index - void *i2Dbuf[CH_QUEUE_SIZE]; // An array of channel pointers - // of channels who need to send - // data or in-line command packets. - rwlock_t Bbuf_spinlock; - volatile - unsigned short i2Bbuf_strip; // Strip index - volatile - unsigned short i2Bbuf_stuff; // Stuff index - void *i2Bbuf[CH_QUEUE_SIZE]; // An array of channel pointers - // of channels who need to send - // bypass command packets. - - /* - * A set of flags to indicate that certain events have occurred on at least - * one of the ports on this board. We use this to decide whether to spin - * through the channels looking for breaks, etc. - */ - int got_input; - int status_change; - bidStat channelBtypes; - - /* - * Debugging counters, etc. - */ - unsigned long debugFlowQueued; - unsigned long debugInlineQueued; - unsigned long debugDataQueued; - unsigned long debugBypassQueued; - unsigned long debugFlowCount; - unsigned long debugInlineCount; - unsigned long debugBypassCount; - - rwlock_t read_fifo_spinlock; - rwlock_t write_fifo_spinlock; - -// For queuing interrupt bottom half handlers. /\/\|=mhw=|\/\/ - struct work_struct tqueue_interrupt; - - struct timer_list SendPendingTimer; // Used by iiSendPending - unsigned int SendPendingRetry; -} i2eBordStr, *i2eBordStrPtr; - -//------------------------------------------------------------------- -// Macro Definitions for the indirect calls defined in the i2eBordStr -//------------------------------------------------------------------- -// -#define iiDelay(a,b) (*(a)->i2eDelay)(b) -#define iiWriteBuf(a,b,c) (*(a)->i2eWriteBuf)(a,b,c) -#define iiReadBuf(a,b,c) (*(a)->i2eReadBuf)(a,b,c) - -#define iiWriteWord(a,b) (*(a)->i2eWriteWord)(a,b) -#define iiReadWord(a) (*(a)->i2eReadWord)(a) - -#define iiWaitForTxEmpty(a,b) (*(a)->i2eWaitForTxEmpty)(a,b) - -#define iiTxMailEmpty(a) (*(a)->i2eTxMailEmpty)(a) -#define iiTrySendMail(a,b) (*(a)->i2eTrySendMail)(a,b) - -#define iiGetMail(a) (*(a)->i2eGetMail)(a) -#define iiEnableMailIrq(a) (*(a)->i2eEnableMailIrq)(a) -#define iiDisableMailIrq(a) (*(a)->i2eWriteMask)(a,0) -#define iiWriteMask(a,b) (*(a)->i2eWriteMask)(a,b) - -//------------------------------------------- -// Manifests for i2eBordStr: -//------------------------------------------- - -typedef void (*delayFunc_t)(unsigned int); - -// i2eValid -// -#define I2E_MAGIC 0x4251 // Structure is valid. -#define I2E_INCOMPLETE 0x1122 // Structure failed during init. - - -// i2eError -// -#define I2EE_GOOD 0 // Operation successful -#define I2EE_BADADDR 1 // Address out of range -#define I2EE_BADSTATE 2 // Attempt to perform a function when the board - // structure was in the incorrect state -#define I2EE_BADMAGIC 3 // Bad magic number from Power On test (i2ePomSize - // reflects what was read -#define I2EE_PORM_SHORT 4 // Power On message too short -#define I2EE_PORM_LONG 5 // Power On message too long -#define I2EE_BAD_FAMILY 6 // Un-supported board family type -#define I2EE_INCONSIST 7 // Firmware reports something impossible, - // e.g. unexpected number of ports... Almost no - // excuse other than bad FIFO... -#define I2EE_POSTERR 8 // Power-On self test reported a bad error -#define I2EE_BADBUS 9 // Unknown Bus type declared in message -#define I2EE_TXE_TIME 10 // Timed out waiting for TX Fifo to empty -#define I2EE_INVALID 11 // i2eValid field does not indicate a valid and - // complete board structure (for functions which - // require this be so.) -#define I2EE_BAD_PORT 12 // Discrepancy between channels actually found and - // what the product is supposed to have. Check - // i2eGoodMap vs i2eChannelMap for details. -#define I2EE_BAD_IRQ 13 // Someone specified an unsupported IRQ -#define I2EE_NOCHANNELS 14 // No channel structures have been defined (for - // functions requiring this). - -// i2eFifoStyle -// -#define FIFO_II 0 /* IntelliPort-II style: see also i2hw.h */ -#define FIFO_IIEX 1 /* IntelliPort-IIEX style */ - -// i2eGetMail -// -#define NO_MAIL_HERE 0x1111 // Since mail is unsigned char, cannot possibly - // promote to 0x1111. -// i2eState -// -#define II_STATE_COLD 0 // Addresses have been defined, but board not even - // reset yet. -#define II_STATE_RESET 1 // Board,if it exists, has just been reset -#define II_STATE_READY 2 // Board ready for its first block -#define II_STATE_LOADING 3 // Board continuing load -#define II_STATE_LOADED 4 // Board has finished load: status ok -#define II_STATE_BADLOAD 5 // Board has finished load: failed! -#define II_STATE_STDLOADED 6 // Board has finished load: standard firmware - -// i2eUsingIrq -// -#define I2_IRQ_UNDEFINED 0x1352 /* No valid irq (or polling = 0) can - * ever promote to this! */ -//------------------------------------------ -// Handy Macros for i2ellis.c and others -// Note these are common to -II and -IIEX -//------------------------------------------ - -// Given a pointer to the board structure, does the input FIFO have any data or -// not? -// -#define I2_HAS_INPUT(pB) !(inb(pB->i2eStatus) & ST_IN_EMPTY) - -// Given a pointer to the board structure, is there anything in the incoming -// mailbox? -// -#define I2_HAS_MAIL(pB) (inb(pB->i2eStatus) & ST_IN_MAIL) - -#define I2_UPDATE_FIFO_ROOM(pB) ((pB)->i2eFifoRemains = (pB)->i2eFifoSize) - -//------------------------------------------ -// Function Declarations for i2ellis.c -//------------------------------------------ -// -// Functions called directly -// -// Initialization of a board & structure is in four (five!) parts: -// -// 1) iiSetAddress() - Define the board address & delay function for a board. -// 2) iiReset() - Reset the board (provided it exists) -// -- Note you may do this to several boards -- -// 3) iiResetDelay() - Delay for 2 seconds (once for all boards) -// 4) iiInitialize() - Attempt to read Power-up message; further initialize -// accelerators -// -// Then you may use iiDownloadAll() or iiDownloadFile() (in i2file.c) to write -// loadware. To change loadware, you must begin again with step 2, resetting -// the board again (step 1 not needed). - -static int iiSetAddress(i2eBordStrPtr, int, delayFunc_t ); -static int iiReset(i2eBordStrPtr); -static int iiResetDelay(i2eBordStrPtr); -static int iiInitialize(i2eBordStrPtr); - -// Routine to validate that all channels expected are there. -// -extern int iiValidateChannels(i2eBordStrPtr); - -// Routine used to download a block of loadware. -// -static int iiDownloadBlock(i2eBordStrPtr, loadHdrStrPtr, int); - -// Return values given by iiDownloadBlock, iiDownloadAll, iiDownloadFile: -// -#define II_DOWN_BADVALID 0 // board structure is invalid -#define II_DOWN_CONTINUING 1 // So far, so good, firmware expects more -#define II_DOWN_GOOD 2 // Download complete, CRC good -#define II_DOWN_BAD 3 // Download complete, but CRC bad -#define II_DOWN_BADFILE 4 // Bad magic number in loadware file -#define II_DOWN_BADSTATE 5 // Board is in an inappropriate state for - // downloading loadware. (see i2eState) -#define II_DOWN_TIMEOUT 6 // Timeout waiting for firmware -#define II_DOWN_OVER 7 // Too much data -#define II_DOWN_UNDER 8 // Not enough data -#define II_DOWN_NOFILE 9 // Loadware file not found - -// Routine to download an entire loadware module: Return values are a subset of -// iiDownloadBlock's, excluding, of course, II_DOWN_CONTINUING -// -static int iiDownloadAll(i2eBordStrPtr, loadHdrStrPtr, int, int); - -// Many functions defined here return True if good, False otherwise, with an -// error code in i2eError field. Here is a handy macro for setting the error -// code and returning. -// -#define I2_COMPLETE(pB,code) do { \ - pB->i2eError = code; \ - return (code == I2EE_GOOD);\ - } while (0) - -#endif // I2ELLIS_H diff --git a/drivers/char/ip2/i2hw.h b/drivers/char/ip2/i2hw.h deleted file mode 100644 index c0ba6c05f0cd..000000000000 --- a/drivers/char/ip2/i2hw.h +++ /dev/null @@ -1,652 +0,0 @@ -/******************************************************************************* -* -* (c) 1999 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Definitions limited to properties of the hardware or the -* bootstrap firmware. As such, they are applicable regardless of -* operating system or loadware (standard or diagnostic). -* -*******************************************************************************/ -#ifndef I2HW_H -#define I2HW_H 1 -//------------------------------------------------------------------------------ -// Revision History: -// -// 23 September 1991 MAG First Draft Started...through... -// 11 October 1991 ... Continuing development... -// 6 August 1993 Added support for ISA-4 (asic) which is architected -// as an ISA-CEX with a single 4-port box. -// -// 20 December 1996 AKM Version for Linux -// -//------------------------------------------------------------------------------ -/*------------------------------------------------------------------------------ - -HARDWARE DESCRIPTION: - -Introduction: - -The IntelliPort-II and IntelliPort-IIEX products occupy a block of eight (8) -addresses in the host's I/O space. - -Some addresses are used to transfer data to/from the board, some to transfer -so-called "mailbox" messages, and some to read bit-mapped status information. -While all the products in the line are functionally similar, some use a 16-bit -data path to transfer data while others use an 8-bit path. Also, the use of -command /status/mailbox registers differs slightly between the II and IIEX -branches of the family. - -The host determines what type of board it is dealing with by reading a string of -sixteen characters from the board. These characters are always placed in the -fifo by the board's local processor whenever the board is reset (either from -power-on or under software control) and are known as the "Power-on Reset -Message." In order that this message can be read from either type of board, the -hardware registers used in reading this message are the same. Once this message -has been read by the host, then it has the information required to operate. - -General Differences between boards: - -The greatest structural difference is between the -II and -IIEX families of -product. The -II boards use the Am4701 dual 512x8 bidirectional fifo to support -the data path, mailbox registers, and status registers. This chip contains some -features which are not used in the IntelliPort-II products; a description of -these is omitted here. Because of these many features, it contains many -registers, too many to access directly within a small address space. They are -accessed by first writing a value to a "pointer" register. This value selects -the register to be accessed. The next read or write to that address accesses -the selected register rather than the pointer register. - -The -IIEX boards use a proprietary design similar to the Am4701 in function. But -because of a simpler, more streamlined design it doesn't require so many -registers. This means they can be accessed directly in single operations rather -than through a pointer register. - -Besides these differences, there are differences in whether 8-bit or 16-bit -transfers are used to move data to the board. - -The -II boards are capable only of 8-bit data transfers, while the -IIEX boards -may be configured for either 8-bit or 16-bit data transfers. If the on-board DIP -switch #8 is ON, and the card has been installed in a 16-bit slot, 16-bit -transfers are supported (and will be expected by the standard loadware). The -on-board firmware can determine the position of the switch, and whether the -board is installed in a 16-bit slot; it supplies this information to the host as -part of the power-up reset message. - -The configuration switch (#8) and slot selection do not directly configure the -hardware. It is up to the on-board loadware and host-based drivers to act -according to the selected options. That is, loadware and drivers could be -written to perform 8-bit transfers regardless of the state of the DIP switch or -slot (and in a diagnostic environment might well do so). Likewise, 16-bit -transfers could be performed as long as the card is in a 16-bit slot. - -Note the slot selection and DIP switch selection are provided separately: a -board running in 8-bit mode in a 16-bit slot has a greater range of possible -interrupts to choose from; information of potential use to the host. - -All 8-bit data transfers are done in the same way, regardless of whether on a --II board or a -IIEX board. - -The host must consider two things then: 1) whether a -II or -IIEX product is -being used, and 2) whether an 8-bit or 16-bit data path is used. - -A further difference is that -II boards always have a 512-byte fifo operating in -each direction. -IIEX boards may use fifos of varying size; this size is -reported as part of the power-up message. - -I/O Map Of IntelliPort-II and IntelliPort-IIEX boards: -(Relative to the chosen base address) - -Addr R/W IntelliPort-II IntelliPort-IIEX ----- --- -------------- ---------------- -0 R/W Data Port (byte) Data Port (byte or word) -1 R/W (Not used) (MSB of word-wide data written to Data Port) -2 R Status Register Status Register -2 W Pointer Register Interrupt Mask Register -3 R/W (Not used) Mailbox Registers (6 bits: 11111100) -4,5 -- Reserved for future products -6 -- Reserved for future products -7 R Guaranteed to have no effect -7 W Hardware reset of board. - - -Rules: -All data transfers are performed using the even i/o address. If byte-wide data -transfers are being used, do INB/OUTB operations on the data port. If word-wide -transfers are used, do INW/OUTW operations. In some circumstances (such as -reading the power-up message) you will do INB from the data port, but in this -case the MSB of each word read is lost. When accessing all other unreserved -registers, use byte operations only. -------------------------------------------------------------------------------*/ - -//------------------------------------------------ -// Mandatory Includes: -//------------------------------------------------ -// -#include "ip2types.h" - -//------------------------------------------------------------------------- -// Manifests for the I/O map: -//------------------------------------------------------------------------- -// R/W: Data port (byte) for IntelliPort-II, -// R/W: Data port (byte or word) for IntelliPort-IIEX -// Incoming or outgoing data passes through a FIFO, the status of which is -// available in some of the bits in FIFO_STATUS. This (bidirectional) FIFO is -// the primary means of transferring data, commands, flow-control, and status -// information between the host and board. -// -#define FIFO_DATA 0 - -// Another way of passing information between the board and the host is -// through "mailboxes". Unlike a FIFO, a mailbox holds only a single byte of -// data. Writing data to the mailbox causes a status bit to be set, and -// potentially interrupting the intended receiver. The sender has some way to -// determine whether the data has been read yet; as soon as it has, it may send -// more. The mailboxes are handled differently on -II and -IIEX products, as -// suggested below. -//------------------------------------------------------------------------------ -// Read: Status Register for IntelliPort-II or -IIEX -// The presence of any bit set here will cause an interrupt to the host, -// provided the corresponding bit has been unmasked in the interrupt mask -// register. Furthermore, interrupts to the host are disabled globally until the -// loadware selects the irq line to use. With the exception of STN_MR, the bits -// remain set so long as the associated condition is true. -// -#define FIFO_STATUS 2 - -// Bit map of status bits which are identical for -II and -IIEX -// -#define ST_OUT_FULL 0x40 // Outbound FIFO full -#define ST_IN_EMPTY 0x20 // Inbound FIFO empty -#define ST_IN_MAIL 0x04 // Inbound Mailbox full - -// The following exists only on the Intelliport-IIEX, and indicates that the -// board has not read the last outgoing mailbox data yet. In the IntelliPort-II, -// the outgoing mailbox may be read back: a zero indicates the board has read -// the data. -// -#define STE_OUT_MAIL 0x80 // Outbound mailbox full (!) - -// The following bits are defined differently for -II and -IIEX boards. Code -// which relies on these bits will need to be functionally different for the two -// types of boards and should be generally avoided because of the additional -// complexity this creates: - -// Bit map of status bits only on -II - -// Fifo has been RESET (cleared when the status register is read). Note that -// this condition cannot be masked and would always interrupt the host, except -// that the hardware reset also disables interrupts globally from the board -// until re-enabled by loadware. This could also arise from the -// Am4701-supported command to reset the chip, but this command is generally not -// used here. -// -#define STN_MR 0x80 - -// See the AMD Am4701 data sheet for details on the following four bits. They -// are not presently used by Computone drivers. -// -#define STN_OUT_AF 0x10 // Outbound FIFO almost full (programmable) -#define STN_IN_AE 0x08 // Inbound FIFO almost empty (programmable) -#define STN_BD 0x02 // Inbound byte detected -#define STN_PE 0x01 // Parity/Framing condition detected - -// Bit-map of status bits only on -IIEX -// -#define STE_OUT_HF 0x10 // Outbound FIFO half full -#define STE_IN_HF 0x08 // Inbound FIFO half full -#define STE_IN_FULL 0x02 // Inbound FIFO full -#define STE_OUT_MT 0x01 // Outbound FIFO empty - -//------------------------------------------------------------------------------ - -// Intelliport-II -- Write Only: the pointer register. -// Values are written to this register to select the Am4701 internal register to -// be accessed on the next operation. -// -#define FIFO_PTR 0x02 - -// Values for the pointer register -// -#define SEL_COMMAND 0x1 // Selects the Am4701 command register - -// Some possible commands: -// -#define SEL_CMD_MR 0x80 // Am4701 command to reset the chip -#define SEL_CMD_SH 0x40 // Am4701 command to map the "other" port into the - // status register. -#define SEL_CMD_UNSH 0 // Am4701 command to "unshift": port maps into its - // own status register. -#define SEL_MASK 0x2 // Selects the Am4701 interrupt mask register. The - // interrupt mask register is bit-mapped to match - // the status register (FIFO_STATUS) except for - // STN_MR. (See above.) -#define SEL_BYTE_DET 0x3 // Selects the Am4701 byte-detect register. (Not - // normally used except in diagnostics.) -#define SEL_OUTMAIL 0x4 // Selects the outbound mailbox (R/W). Reading back - // a value of zero indicates that the mailbox has - // been read by the board and is available for more - // data./ Writing to the mailbox optionally - // interrupts the board, depending on the loadware's - // setting of its interrupt mask register. -#define SEL_AEAF 0x5 // Selects AE/AF threshold register. -#define SEL_INMAIL 0x6 // Selects the inbound mailbox (Read) - -//------------------------------------------------------------------------------ -// IntelliPort-IIEX -- Write Only: interrupt mask (and misc flags) register: -// Unlike IntelliPort-II, bit assignments do NOT match those of the status -// register. -// -#define FIFO_MASK 0x2 - -// Mailbox readback select: -// If set, reads to FIFO_MAIL will read the OUTBOUND mailbox (host to board). If -// clear (default on reset) reads to FIFO_MAIL will read the INBOUND mailbox. -// This is the normal situation. The clearing of a mailbox is determined on -// -IIEX boards by waiting for the STE_OUT_MAIL bit to clear. Readback -// capability is provided for diagnostic purposes only. -// -#define MX_OUTMAIL_RSEL 0x80 - -#define MX_IN_MAIL 0x40 // Enables interrupts when incoming mailbox goes - // full (ST_IN_MAIL set). -#define MX_IN_FULL 0x20 // Enables interrupts when incoming FIFO goes full - // (STE_IN_FULL). -#define MX_IN_MT 0x08 // Enables interrupts when incoming FIFO goes empty - // (ST_IN_MT). -#define MX_OUT_FULL 0x04 // Enables interrupts when outgoing FIFO goes full - // (ST_OUT_FULL). -#define MX_OUT_MT 0x01 // Enables interrupts when outgoing FIFO goes empty - // (STE_OUT_MT). - -// Any remaining bits are reserved, and should be written to ZERO for -// compatibility with future Computone products. - -//------------------------------------------------------------------------------ -// IntelliPort-IIEX: -- These are only 6-bit mailboxes !!! -- 11111100 (low two -// bits always read back 0). -// Read: One of the mailboxes, usually Inbound. -// Inbound Mailbox (MX_OUTMAIL_RSEL = 0) -// Outbound Mailbox (MX_OUTMAIL_RSEL = 1) -// Write: Outbound Mailbox -// For the IntelliPort-II boards, the outbound mailbox is read back to determine -// whether the board has read the data (0 --> data has been read). For the -// IntelliPort-IIEX, this is done by reading a status register. To determine -// whether mailbox is available for more outbound data, use the STE_OUT_MAIL bit -// in FIFO_STATUS. Moreover, although the Outbound Mailbox can be read back by -// setting MX_OUTMAIL_RSEL, it is NOT cleared when the board reads it, as is the -// case with the -II boards. For this reason, FIFO_MAIL is normally used to read -// the inbound FIFO, and MX_OUTMAIL_RSEL kept clear. (See above for -// MX_OUTMAIL_RSEL description.) -// -#define FIFO_MAIL 0x3 - -//------------------------------------------------------------------------------ -// WRITE ONLY: Resets the board. (Data doesn't matter). -// -#define FIFO_RESET 0x7 - -//------------------------------------------------------------------------------ -// READ ONLY: Will have no effect. (Data is undefined.) -// Actually, there will be an effect, in that the operation is sure to generate -// a bus cycle: viz., an I/O byte Read. This fact can be used to enforce short -// delays when no comparable time constant is available. -// -#define FIFO_NOP 0x7 - -//------------------------------------------------------------------------------ -// RESET & POWER-ON RESET MESSAGE -/*------------------------------------------------------------------------------ -RESET: - -The IntelliPort-II and -IIEX boards are reset in three ways: Power-up, channel -reset, and via a write to the reset register described above. For products using -the ISA bus, these three sources of reset are equvalent. For MCA and EISA buses, -the Power-up and channel reset sources cause additional hardware initialization -which should only occur at system startup time. - -The third type of reset, called a "command reset", is done by writing any data -to the FIFO_RESET address described above. This resets the on-board processor, -FIFO, UARTS, and associated hardware. - -This passes control of the board to the bootstrap firmware, which performs a -Power-On Self Test and which detects its current configuration. For example, --IIEX products determine the size of FIFO which has been installed, and the -number and type of expansion boxes attached. - -This and other information is then written to the FIFO in a 16-byte data block -to be read by the host. This block is guaranteed to be present within two (2) -seconds of having received the command reset. The firmware is now ready to -receive loadware from the host. - -It is good practice to perform a command reset to the board explicitly as part -of your software initialization. This allows your code to properly restart from -a soft boot. (Many systems do not issue channel reset on soft boot). - -Because of a hardware reset problem on some of the Cirrus Logic 1400's which are -used on the product, it is recommended that you reset the board twice, separated -by an approximately 50 milliseconds delay. (VERY approximately: probably ok to -be off by a factor of five. The important point is that the first command reset -in fact generates a reset pulse on the board. This pulse is guaranteed to last -less than 10 milliseconds. The additional delay ensures the 1400 has had the -chance to respond sufficiently to the first reset. Why not a longer delay? Much -more than 50 milliseconds gets to be noticable, but the board would still work. - -Once all 16 bytes of the Power-on Reset Message have been read, the bootstrap -firmware is ready to receive loadware. - -Note on Power-on Reset Message format: -The various fields have been designed with future expansion in view. -Combinations of bitfields and values have been defined which define products -which may not currently exist. This has been done to allow drivers to anticipate -the possible introduction of products in a systematic fashion. This is not -intended to suggest that each potential product is actually under consideration. -------------------------------------------------------------------------------*/ - -//---------------------------------------- -// Format of Power-on Reset Message -//---------------------------------------- - -typedef union _porStr // "por" stands for Power On Reset -{ - unsigned char c[16]; // array used when considering the message as a - // string of undifferentiated characters - - struct // Elements used when considering values - { - // The first two bytes out of the FIFO are two magic numbers. These are - // intended to establish that there is indeed a member of the - // IntelliPort-II(EX) family present. The remaining bytes may be - // expected // to be valid. When reading the Power-on Reset message, - // if the magic numbers do not match it is probably best to stop - // reading immediately. You are certainly not reading our board (unless - // hardware is faulty), and may in fact be reading some other piece of - // hardware. - - unsigned char porMagic1; // magic number: first byte == POR_MAGIC_1 - unsigned char porMagic2; // magic number: second byte == POR_MAGIC_2 - - // The Version, Revision, and Subrevision are stored as absolute numbers - // and would normally be displayed in the format V.R.S (e.g. 1.0.2) - - unsigned char porVersion; // Bootstrap firmware version number - unsigned char porRevision; // Bootstrap firmware revision number - unsigned char porSubRev; // Bootstrap firmware sub-revision number - - unsigned char porID; // Product ID: Bit-mapped according to - // conventions described below. Among other - // things, this allows us to distinguish - // IntelliPort-II boards from IntelliPort-IIEX - // boards. - - unsigned char porBus; // IntelliPort-II: Unused - // IntelliPort-IIEX: Bus Information: - // Bit-mapped below - - unsigned char porMemory; // On-board DRAM size: in 32k blocks - - // porPorts1 (and porPorts2) are used to determine the ports which are - // available to the board. For non-expandable product, a single number - // is sufficient. For expandable product, the board may be connected - // to as many as four boxes. Each box may be (so far) either a 16-port - // or an 8-port size. Whenever an 8-port box is used, the remaining 8 - // ports leave gaps between existing channels. For that reason, - // expandable products must report a MAP of available channels. Since - // each UART supports four ports, we represent each UART found by a - // single bit. Using two bytes to supply the mapping information we - // report the presense or absense of up to 16 UARTS, or 64 ports in - // steps of 4 ports. For -IIEX products, the ports are numbered - // starting at the box closest to the controller in the "chain". - - // Interpreted Differently for IntelliPort-II and -IIEX. - // -II: Number of ports (Derived actually from product ID). See - // Diag1&2 to indicate if uart was actually detected. - // -IIEX: Bit-map of UARTS found, LSB (see below for MSB of this). This - // bitmap is based on detecting the uarts themselves; - // see porFlags for information from the box i.d's. - unsigned char porPorts1; - - unsigned char porDiag1; // Results of on-board P.O.S.T, 1st byte - unsigned char porDiag2; // Results of on-board P.O.S.T, 2nd byte - unsigned char porSpeed; // Speed of local CPU: given as MHz x10 - // e.g., 16.0 MHz CPU is reported as 160 - unsigned char porFlags; // Misc information (see manifests below) - // Bit-mapped: CPU type, UART's present - - unsigned char porPorts2; // -II: Undefined - // -IIEX: Bit-map of UARTS found, MSB (see - // above for LSB) - - // IntelliPort-II: undefined - // IntelliPort-IIEX: 1 << porFifoSize gives the size, in bytes, of the - // host interface FIFO, in each direction. When running the -IIEX in - // 8-bit mode, fifo capacity is halved. The bootstrap firmware will - // have already accounted for this fact in generating this number. - unsigned char porFifoSize; - - // IntelliPort-II: undefined - // IntelliPort-IIEX: The number of boxes connected. (Presently 1-4) - unsigned char porNumBoxes; - } e; -} porStr, *porStrPtr; - -//-------------------------- -// Values for porStr fields -//-------------------------- - -//--------------------- -// porMagic1, porMagic2 -//---------------------- -// -#define POR_MAGIC_1 0x96 // The only valid value for porMagic1 -#define POR_MAGIC_2 0x35 // The only valid value for porMagic2 -#define POR_1_INDEX 0 // Byte position of POR_MAGIC_1 -#define POR_2_INDEX 1 // Ditto for POR_MAGIC_2 - -//---------------------- -// porID -//---------------------- -// -#define POR_ID_FAMILY 0xc0 // These bits indicate the general family of - // product. -#define POR_ID_FII 0x00 // Family is "IntelliPort-II" -#define POR_ID_FIIEX 0x40 // Family is "IntelliPort-IIEX" - -// These bits are reserved, presently zero. May be used at a later date to -// convey other product information. -// -#define POR_ID_RESERVED 0x3c - -#define POR_ID_SIZE 0x03 // Remaining bits indicate number of ports & - // Connector information. -#define POR_ID_II_8 0x00 // For IntelliPort-II, indicates 8-port using - // standard brick. -#define POR_ID_II_8R 0x01 // For IntelliPort-II, indicates 8-port using - // RJ11's (no CTS) -#define POR_ID_II_6 0x02 // For IntelliPort-II, indicates 6-port using - // RJ45's -#define POR_ID_II_4 0x03 // For IntelliPort-II, indicates 4-port using - // 4xRJ45 connectors -#define POR_ID_EX 0x00 // For IntelliPort-IIEX, indicates standard - // expandable controller (other values reserved) - -//---------------------- -// porBus -//---------------------- - -// IntelliPort-IIEX only: Board is installed in a 16-bit slot -// -#define POR_BUS_SLOT16 0x20 - -// IntelliPort-IIEX only: DIP switch #8 is on, selecting 16-bit host interface -// operation. -// -#define POR_BUS_DIP16 0x10 - -// Bits 0-2 indicate type of bus: This information is stored in the bootstrap -// loadware, different loadware being used on different products for different -// buses. For most situations, the drivers do not need this information; but it -// is handy in a diagnostic environment. For example, on microchannel boards, -// you would not want to try to test several interrupts, only the one for which -// you were configured. -// -#define POR_BUS_TYPE 0x07 - -// Unknown: this product doesn't know what bus it is running in. (e.g. if same -// bootstrap firmware were wanted for two different buses.) -// -#define POR_BUS_T_UNK 0 - -// Note: existing firmware for ISA-8 and MC-8 currently report the POR_BUS_T_UNK -// state, since the same bootstrap firmware is used for each. - -#define POR_BUS_T_MCA 1 // MCA BUS */ -#define POR_BUS_T_EISA 2 // EISA BUS */ -#define POR_BUS_T_ISA 3 // ISA BUS */ - -// Values 4-7 Reserved - -// Remaining bits are reserved - -//---------------------- -// porDiag1 -//---------------------- - -#define POR_BAD_MAPPER 0x80 // HW failure on P.O.S.T: Chip mapper failed - -// These two bits valid only for the IntelliPort-II -// -#define POR_BAD_UART1 0x01 // First 1400 bad -#define POR_BAD_UART2 0x02 // Second 1400 bad - -//---------------------- -// porDiag2 -//---------------------- - -#define POR_DEBUG_PORT 0x80 // debug port was detected by the P.O.S.T -#define POR_DIAG_OK 0x00 // Indicates passage: Failure codes not yet - // available. - // Other bits undefined. -//---------------------- -// porFlags -//---------------------- - -#define POR_CPU 0x03 // These bits indicate supposed CPU type -#define POR_CPU_8 0x01 // Board uses an 80188 (no such thing yet) -#define POR_CPU_6 0x02 // Board uses an 80186 (all existing products) -#define POR_CEX4 0x04 // If set, this is an ISA-CEX/4: An ISA-4 (asic) - // which is architected like an ISA-CEX connected - // to a (hitherto impossible) 4-port box. -#define POR_BOXES 0xf0 // Valid for IntelliPort-IIEX only: Map of Box - // sizes based on box I.D. -#define POR_BOX_16 0x10 // Set indicates 16-port, clear 8-port - -//------------------------------------- -// LOADWARE and DOWNLOADING CODE -//------------------------------------- - -/* -Loadware may be sent to the board in two ways: -1) It may be read from a (binary image) data file block by block as each block - is sent to the board. This is only possible when the initialization is - performed by code which can access your file system. This is most suitable - for diagnostics and appications which use the interface library directly. - -2) It may be hard-coded into your source by including a .h file (typically - supplied by Computone), which declares a data array and initializes every - element. This achieves the same result as if an entire loadware file had - been read into the array. - - This requires more data space in your program, but access to the file system - is not required. This method is more suited to driver code, which typically - is running at a level too low to access the file system directly. - -At present, loadware can only be generated at Computone. - -All Loadware begins with a header area which has a particular format. This -includes a magic number which identifies the file as being (purportedly) -loadware, CRC (for the loader), and version information. -*/ - - -//----------------------------------------------------------------------------- -// Format of loadware block -// -// This is defined as a union so we can pass a pointer to one of these items -// and (if it is the first block) pick out the version information, etc. -// -// Otherwise, to deal with this as a simple character array -//------------------------------------------------------------------------------ - -#define LOADWARE_BLOCK_SIZE 512 // Number of bytes in each block of loadware - -typedef union _loadHdrStr -{ - unsigned char c[LOADWARE_BLOCK_SIZE]; // Valid for every block - - struct // These fields are valid for only the first block of loadware. - { - unsigned char loadMagic; // Magic number: see below - unsigned char loadBlocksMore; // How many more blocks? - unsigned char loadCRC[2]; // Two CRC bytes: used by loader - unsigned char loadVersion; // Version number - unsigned char loadRevision; // Revision number - unsigned char loadSubRevision; // Sub-revision number - unsigned char loadSpares[9]; // Presently unused - unsigned char loadDates[32]; // Null-terminated string which can give - // date and time of compilation - } e; -} loadHdrStr, *loadHdrStrPtr; - -//------------------------------------ -// Defines for downloading code: -//------------------------------------ - -// The loadMagic field in the first block of the loadfile must be this, else the -// file is not valid. -// -#define MAGIC_LOADFILE 0x3c - -// How do we know the load was successful? On completion of the load, the -// bootstrap firmware returns a code to indicate whether it thought the download -// was valid and intends to execute it. These are the only possible valid codes: -// -#define LOADWARE_OK 0xc3 // Download was ok -#define LOADWARE_BAD 0x5a // Download was bad (CRC error) - -// Constants applicable to writing blocks of loadware: -// The first block of loadware might take 600 mS to load, in extreme cases. -// (Expandable board: worst case for sending startup messages to the LCD's). -// The 600mS figure is not really a calculation, but a conservative -// guess/guarantee. Usually this will be within 100 mS, like subsequent blocks. -// -#define MAX_DLOAD_START_TIME 1000 // 1000 mS -#define MAX_DLOAD_READ_TIME 100 // 100 mS - -// Firmware should respond with status (see above) within this long of host -// having sent the final block. -// -#define MAX_DLOAD_ACK_TIME 100 // 100 mS, again! - -//------------------------------------------------------ -// MAXIMUM NUMBER OF PORTS PER BOARD: -// This is fixed for now (with the expandable), but may -// be expanding according to even newer products. -//------------------------------------------------------ -// -#define ABS_MAX_BOXES 4 // Absolute most boxes per board -#define ABS_BIGGEST_BOX 16 // Absolute the most ports per box -#define ABS_MOST_PORTS (ABS_MAX_BOXES * ABS_BIGGEST_BOX) - -#define I2_OUTSW(port, addr, count) outsw((port), (addr), (((count)+1)/2)) -#define I2_OUTSB(port, addr, count) outsb((port), (addr), (((count)+1))&-2) -#define I2_INSW(port, addr, count) insw((port), (addr), (((count)+1)/2)) -#define I2_INSB(port, addr, count) insb((port), (addr), (((count)+1))&-2) - -#endif // I2HW_H - diff --git a/drivers/char/ip2/i2lib.c b/drivers/char/ip2/i2lib.c deleted file mode 100644 index 0d10b89218ed..000000000000 --- a/drivers/char/ip2/i2lib.c +++ /dev/null @@ -1,2214 +0,0 @@ -/******************************************************************************* -* -* (c) 1999 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport -* serial I/O controllers. -* -* DESCRIPTION: High-level interface code for the device driver. Uses the -* Extremely Low Level Interface Support (i2ellis.c). Provides an -* interface to the standard loadware, to support drivers or -* application code. (This is included source code, not a separate -* compilation module.) -* -*******************************************************************************/ -//------------------------------------------------------------------------------ -// Note on Strategy: -// Once the board has been initialized, it will interrupt us when: -// 1) It has something in the fifo for us to read (incoming data, flow control -// packets, or whatever). -// 2) It has stripped whatever we have sent last time in the FIFO (and -// consequently is ready for more). -// -// Note also that the buffer sizes declared in i2lib.h are VERY SMALL. This -// worsens performance considerably, but is done so that a great many channels -// might use only a little memory. -//------------------------------------------------------------------------------ - -//------------------------------------------------------------------------------ -// Revision History: -// -// 0.00 - 4/16/91 --- First Draft -// 0.01 - 4/29/91 --- 1st beta release -// 0.02 - 6/14/91 --- Changes to allow small model compilation -// 0.03 - 6/17/91 MAG Break reporting protected from interrupts routines with -// in-line asm added for moving data to/from ring buffers, -// replacing a variety of methods used previously. -// 0.04 - 6/21/91 MAG Initial flow-control packets not queued until -// i2_enable_interrupts time. Former versions would enqueue -// them at i2_init_channel time, before we knew how many -// channels were supposed to exist! -// 0.05 - 10/12/91 MAG Major changes: works through the ellis.c routines now; -// supports new 16-bit protocol and expandable boards. -// - 10/24/91 MAG Most changes in place and stable. -// 0.06 - 2/20/92 MAG Format of CMD_HOTACK corrected: the command takes no -// argument. -// 0.07 -- 3/11/92 MAG Support added to store special packet types at interrupt -// level (mostly responses to specific commands.) -// 0.08 -- 3/30/92 MAG Support added for STAT_MODEM packet -// 0.09 -- 6/24/93 MAG i2Link... needed to update number of boards BEFORE -// turning on the interrupt. -// 0.10 -- 6/25/93 MAG To avoid gruesome death from a bad board, we sanity check -// some incoming. -// -// 1.1 - 12/25/96 AKM Linux version. -// - 10/09/98 DMC Revised Linux version. -//------------------------------------------------------------------------------ - -//************ -//* Includes * -//************ - -#include -#include "i2lib.h" - - -//*********************** -//* Function Prototypes * -//*********************** -static void i2QueueNeeds(i2eBordStrPtr, i2ChanStrPtr, int); -static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr, int ); -static void i2StripFifo(i2eBordStrPtr); -static void i2StuffFifoBypass(i2eBordStrPtr); -static void i2StuffFifoFlow(i2eBordStrPtr); -static void i2StuffFifoInline(i2eBordStrPtr); -static int i2RetryFlushOutput(i2ChanStrPtr); - -// Not a documented part of the library routines (careful...) but the Diagnostic -// i2diag.c finds them useful to help the throughput in certain limited -// single-threaded operations. -static void iiSendPendingMail(i2eBordStrPtr); -static void serviceOutgoingFifo(i2eBordStrPtr); - -// Functions defined in ip2.c as part of interrupt handling -static void do_input(struct work_struct *); -static void do_status(struct work_struct *); - -//*************** -//* Debug Data * -//*************** -#ifdef DEBUG_FIFO - -unsigned char DBGBuf[0x4000]; -unsigned short I = 0; - -static void -WriteDBGBuf(char *s, unsigned char *src, unsigned short n ) -{ - char *p = src; - - // XXX: We need a spin lock here if we ever use this again - - while (*s) { // copy label - DBGBuf[I] = *s++; - I = I++ & 0x3fff; - } - while (n--) { // copy data - DBGBuf[I] = *p++; - I = I++ & 0x3fff; - } -} - -static void -fatality(i2eBordStrPtr pB ) -{ - int i; - - for (i=0;i= ' ' && DBGBuf[i] <= '~') { - printk(" %c ",DBGBuf[i]); - } else { - printk(" . "); - } - } - printk("\n"); - printk("Last index %x\n",I); -} -#endif /* DEBUG_FIFO */ - -//******** -//* Code * -//******** - -static inline int -i2Validate ( i2ChanStrPtr pCh ) -{ - //ip2trace(pCh->port_index, ITRC_VERIFY,ITRC_ENTER,2,pCh->validity, - // (CHANNEL_MAGIC | CHANNEL_SUPPORT)); - return ((pCh->validity & (CHANNEL_MAGIC_BITS | CHANNEL_SUPPORT)) - == (CHANNEL_MAGIC | CHANNEL_SUPPORT)); -} - -static void iiSendPendingMail_t(unsigned long data) -{ - i2eBordStrPtr pB = (i2eBordStrPtr)data; - - iiSendPendingMail(pB); -} - -//****************************************************************************** -// Function: iiSendPendingMail(pB) -// Parameters: Pointer to a board structure -// Returns: Nothing -// -// Description: -// If any outgoing mail bits are set and there is outgoing mailbox is empty, -// send the mail and clear the bits. -//****************************************************************************** -static void -iiSendPendingMail(i2eBordStrPtr pB) -{ - if (pB->i2eOutMailWaiting && (!pB->i2eWaitingForEmptyFifo) ) - { - if (iiTrySendMail(pB, pB->i2eOutMailWaiting)) - { - /* If we were already waiting for fifo to empty, - * or just sent MB_OUT_STUFFED, then we are - * still waiting for it to empty, until we should - * receive an MB_IN_STRIPPED from the board. - */ - pB->i2eWaitingForEmptyFifo |= - (pB->i2eOutMailWaiting & MB_OUT_STUFFED); - pB->i2eOutMailWaiting = 0; - pB->SendPendingRetry = 0; - } else { -/* The only time we hit this area is when "iiTrySendMail" has - failed. That only occurs when the outbound mailbox is - still busy with the last message. We take a short breather - to let the board catch up with itself and then try again. - 16 Retries is the limit - then we got a borked board. - /\/\|=mhw=|\/\/ */ - - if( ++pB->SendPendingRetry < 16 ) { - setup_timer(&pB->SendPendingTimer, - iiSendPendingMail_t, (unsigned long)pB); - mod_timer(&pB->SendPendingTimer, jiffies + 1); - } else { - printk( KERN_ERR "IP2: iiSendPendingMail unable to queue outbound mail\n" ); - } - } - } -} - -//****************************************************************************** -// Function: i2InitChannels(pB, nChannels, pCh) -// Parameters: Pointer to Ellis Board structure -// Number of channels to initialize -// Pointer to first element in an array of channel structures -// Returns: Success or failure -// -// Description: -// -// This function patches pointers, back-pointers, and initializes all the -// elements in the channel structure array. -// -// This should be run after the board structure is initialized, through having -// loaded the standard loadware (otherwise it complains). -// -// In any case, it must be done before any serious work begins initializing the -// irq's or sending commands... -// -//****************************************************************************** -static int -i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh) -{ - int index, stuffIndex; - i2ChanStrPtr *ppCh; - - if (pB->i2eValid != I2E_MAGIC) { - I2_COMPLETE(pB, I2EE_BADMAGIC); - } - if (pB->i2eState != II_STATE_STDLOADED) { - I2_COMPLETE(pB, I2EE_BADSTATE); - } - - rwlock_init(&pB->read_fifo_spinlock); - rwlock_init(&pB->write_fifo_spinlock); - rwlock_init(&pB->Dbuf_spinlock); - rwlock_init(&pB->Bbuf_spinlock); - rwlock_init(&pB->Fbuf_spinlock); - - // NO LOCK needed yet - this is init - - pB->i2eChannelPtr = pCh; - pB->i2eChannelCnt = nChannels; - - pB->i2Fbuf_strip = pB->i2Fbuf_stuff = 0; - pB->i2Dbuf_strip = pB->i2Dbuf_stuff = 0; - pB->i2Bbuf_strip = pB->i2Bbuf_stuff = 0; - - pB->SendPendingRetry = 0; - - memset ( pCh, 0, sizeof (i2ChanStr) * nChannels ); - - for (index = stuffIndex = 0, ppCh = (i2ChanStrPtr *)(pB->i2Fbuf); - nChannels && index < ABS_MOST_PORTS; - index++) - { - if ( !(pB->i2eChannelMap[index >> 4] & (1 << (index & 0xf)) ) ) { - continue; - } - rwlock_init(&pCh->Ibuf_spinlock); - rwlock_init(&pCh->Obuf_spinlock); - rwlock_init(&pCh->Cbuf_spinlock); - rwlock_init(&pCh->Pbuf_spinlock); - // NO LOCK needed yet - this is init - // Set up validity flag according to support level - if (pB->i2eGoodMap[index >> 4] & (1 << (index & 0xf)) ) { - pCh->validity = CHANNEL_MAGIC | CHANNEL_SUPPORT; - } else { - pCh->validity = CHANNEL_MAGIC; - } - pCh->pMyBord = pB; /* Back-pointer */ - - // Prepare an outgoing flow-control packet to send as soon as the chance - // occurs. - if ( pCh->validity & CHANNEL_SUPPORT ) { - pCh->infl.hd.i2sChannel = index; - pCh->infl.hd.i2sCount = 5; - pCh->infl.hd.i2sType = PTYPE_BYPASS; - pCh->infl.fcmd = 37; - pCh->infl.asof = 0; - pCh->infl.room = IBUF_SIZE - 1; - - pCh->whenSendFlow = (IBUF_SIZE/5)*4; // when 80% full - - // The following is similar to calling i2QueueNeeds, except that this - // is done in longhand, since we are setting up initial conditions on - // many channels at once. - pCh->channelNeeds = NEED_FLOW; // Since starting from scratch - pCh->sinceLastFlow = 0; // No bytes received since last flow - // control packet was queued - stuffIndex++; - *ppCh++ = pCh; // List this channel as needing - // initial flow control packet sent - } - - // Don't allow anything to be sent until the status packets come in from - // the board. - - pCh->outfl.asof = 0; - pCh->outfl.room = 0; - - // Initialize all the ring buffers - - pCh->Ibuf_stuff = pCh->Ibuf_strip = 0; - pCh->Obuf_stuff = pCh->Obuf_strip = 0; - pCh->Cbuf_stuff = pCh->Cbuf_strip = 0; - - memset( &pCh->icount, 0, sizeof (struct async_icount) ); - pCh->hotKeyIn = HOT_CLEAR; - pCh->channelOptions = 0; - pCh->bookMarks = 0; - init_waitqueue_head(&pCh->pBookmarkWait); - - init_waitqueue_head(&pCh->open_wait); - init_waitqueue_head(&pCh->close_wait); - init_waitqueue_head(&pCh->delta_msr_wait); - - // Set base and divisor so default custom rate is 9600 - pCh->BaudBase = 921600; // MAX for ST654, changed after we get - pCh->BaudDivisor = 96; // the boxids (UART types) later - - pCh->dataSetIn = 0; - pCh->dataSetOut = 0; - - pCh->wopen = 0; - pCh->throttled = 0; - - pCh->speed = CBR_9600; - - pCh->flags = 0; - - pCh->ClosingDelay = 5*HZ/10; - pCh->ClosingWaitTime = 30*HZ; - - // Initialize task queue objects - INIT_WORK(&pCh->tqueue_input, do_input); - INIT_WORK(&pCh->tqueue_status, do_status); - -#ifdef IP2DEBUG_TRACE - pCh->trace = ip2trace; -#endif - - ++pCh; - --nChannels; - } - // No need to check for wrap here; this is initialization. - pB->i2Fbuf_stuff = stuffIndex; - I2_COMPLETE(pB, I2EE_GOOD); - -} - -//****************************************************************************** -// Function: i2DeQueueNeeds(pB, type) -// Parameters: Pointer to a board structure -// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW -// Returns: -// Pointer to a channel structure -// -// Description: Returns pointer struct of next channel that needs service of -// the type specified. Otherwise returns a NULL reference. -// -//****************************************************************************** -static i2ChanStrPtr -i2DeQueueNeeds(i2eBordStrPtr pB, int type) -{ - unsigned short queueIndex; - unsigned long flags; - - i2ChanStrPtr pCh = NULL; - - switch(type) { - - case NEED_INLINE: - - write_lock_irqsave(&pB->Dbuf_spinlock, flags); - if ( pB->i2Dbuf_stuff != pB->i2Dbuf_strip) - { - queueIndex = pB->i2Dbuf_strip; - pCh = pB->i2Dbuf[queueIndex]; - queueIndex++; - if (queueIndex >= CH_QUEUE_SIZE) { - queueIndex = 0; - } - pB->i2Dbuf_strip = queueIndex; - pCh->channelNeeds &= ~NEED_INLINE; - } - write_unlock_irqrestore(&pB->Dbuf_spinlock, flags); - break; - - case NEED_BYPASS: - - write_lock_irqsave(&pB->Bbuf_spinlock, flags); - if (pB->i2Bbuf_stuff != pB->i2Bbuf_strip) - { - queueIndex = pB->i2Bbuf_strip; - pCh = pB->i2Bbuf[queueIndex]; - queueIndex++; - if (queueIndex >= CH_QUEUE_SIZE) { - queueIndex = 0; - } - pB->i2Bbuf_strip = queueIndex; - pCh->channelNeeds &= ~NEED_BYPASS; - } - write_unlock_irqrestore(&pB->Bbuf_spinlock, flags); - break; - - case NEED_FLOW: - - write_lock_irqsave(&pB->Fbuf_spinlock, flags); - if (pB->i2Fbuf_stuff != pB->i2Fbuf_strip) - { - queueIndex = pB->i2Fbuf_strip; - pCh = pB->i2Fbuf[queueIndex]; - queueIndex++; - if (queueIndex >= CH_QUEUE_SIZE) { - queueIndex = 0; - } - pB->i2Fbuf_strip = queueIndex; - pCh->channelNeeds &= ~NEED_FLOW; - } - write_unlock_irqrestore(&pB->Fbuf_spinlock, flags); - break; - default: - printk(KERN_ERR "i2DeQueueNeeds called with bad type:%x\n",type); - break; - } - return pCh; -} - -//****************************************************************************** -// Function: i2QueueNeeds(pB, pCh, type) -// Parameters: Pointer to a board structure -// Pointer to a channel structure -// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW -// Returns: Nothing -// -// Description: -// For each type of need selected, if the given channel is not already in the -// queue, adds it, and sets the flag indicating it is in the queue. -//****************************************************************************** -static void -i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type) -{ - unsigned short queueIndex; - unsigned long flags; - - // We turn off all the interrupts during this brief process, since the - // interrupt-level code might want to put things on the queue as well. - - switch (type) { - - case NEED_INLINE: - - write_lock_irqsave(&pB->Dbuf_spinlock, flags); - if ( !(pCh->channelNeeds & NEED_INLINE) ) - { - pCh->channelNeeds |= NEED_INLINE; - queueIndex = pB->i2Dbuf_stuff; - pB->i2Dbuf[queueIndex++] = pCh; - if (queueIndex >= CH_QUEUE_SIZE) - queueIndex = 0; - pB->i2Dbuf_stuff = queueIndex; - } - write_unlock_irqrestore(&pB->Dbuf_spinlock, flags); - break; - - case NEED_BYPASS: - - write_lock_irqsave(&pB->Bbuf_spinlock, flags); - if ((type & NEED_BYPASS) && !(pCh->channelNeeds & NEED_BYPASS)) - { - pCh->channelNeeds |= NEED_BYPASS; - queueIndex = pB->i2Bbuf_stuff; - pB->i2Bbuf[queueIndex++] = pCh; - if (queueIndex >= CH_QUEUE_SIZE) - queueIndex = 0; - pB->i2Bbuf_stuff = queueIndex; - } - write_unlock_irqrestore(&pB->Bbuf_spinlock, flags); - break; - - case NEED_FLOW: - - write_lock_irqsave(&pB->Fbuf_spinlock, flags); - if ((type & NEED_FLOW) && !(pCh->channelNeeds & NEED_FLOW)) - { - pCh->channelNeeds |= NEED_FLOW; - queueIndex = pB->i2Fbuf_stuff; - pB->i2Fbuf[queueIndex++] = pCh; - if (queueIndex >= CH_QUEUE_SIZE) - queueIndex = 0; - pB->i2Fbuf_stuff = queueIndex; - } - write_unlock_irqrestore(&pB->Fbuf_spinlock, flags); - break; - - case NEED_CREDIT: - pCh->channelNeeds |= NEED_CREDIT; - break; - default: - printk(KERN_ERR "i2QueueNeeds called with bad type:%x\n",type); - break; - } - return; -} - -//****************************************************************************** -// Function: i2QueueCommands(type, pCh, timeout, nCommands, pCs,...) -// Parameters: type - PTYPE_BYPASS or PTYPE_INLINE -// pointer to the channel structure -// maximum period to wait -// number of commands (n) -// n commands -// Returns: Number of commands sent, or -1 for error -// -// get board lock before calling -// -// Description: -// Queues up some commands to be sent to a channel. To send possibly several -// bypass or inline commands to the given channel. The timeout parameter -// indicates how many HUNDREDTHS OF SECONDS to wait until there is room: -// 0 = return immediately if no room, -ive = wait forever, +ive = number of -// 1/100 seconds to wait. Return values: -// -1 Some kind of nasty error: bad channel structure or invalid arguments. -// 0 No room to send all the commands -// (+) Number of commands sent -//****************************************************************************** -static int -i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands, - cmdSyntaxPtr pCs0,...) -{ - int totalsize = 0; - int blocksize; - int lastended; - cmdSyntaxPtr *ppCs; - cmdSyntaxPtr pCs; - int count; - int flag; - i2eBordStrPtr pB; - - unsigned short maxBlock; - unsigned short maxBuff; - short bufroom; - unsigned short stuffIndex; - unsigned char *pBuf; - unsigned char *pInsert; - unsigned char *pDest, *pSource; - unsigned short channel; - int cnt; - unsigned long flags = 0; - rwlock_t *lock_var_p = NULL; - - // Make sure the channel exists, otherwise do nothing - if ( !i2Validate ( pCh ) ) { - return -1; - } - - ip2trace (CHANN, ITRC_QUEUE, ITRC_ENTER, 0 ); - - pB = pCh->pMyBord; - - // Board must also exist, and THE INTERRUPT COMMAND ALREADY SENT - if (pB->i2eValid != I2E_MAGIC || pB->i2eUsingIrq == I2_IRQ_UNDEFINED) - return -2; - // If the board has gone fatal, return bad, and also hit the trap routine if - // it exists. - if (pB->i2eFatal) { - if ( pB->i2eFatalTrap ) { - (*(pB)->i2eFatalTrap)(pB); - } - return -3; - } - // Set up some variables, Which buffers are we using? How big are they? - switch(type) - { - case PTYPE_INLINE: - flag = INL; - maxBlock = MAX_OBUF_BLOCK; - maxBuff = OBUF_SIZE; - pBuf = pCh->Obuf; - break; - case PTYPE_BYPASS: - flag = BYP; - maxBlock = MAX_CBUF_BLOCK; - maxBuff = CBUF_SIZE; - pBuf = pCh->Cbuf; - break; - default: - return -4; - } - // Determine the total size required for all the commands - totalsize = blocksize = sizeof(i2CmdHeader); - lastended = 0; - ppCs = &pCs0; - for ( count = nCommands; count; count--, ppCs++) - { - pCs = *ppCs; - cnt = pCs->length; - // Will a new block be needed for this one? - // Two possible reasons: too - // big or previous command has to be at the end of a packet. - if ((blocksize + cnt > maxBlock) || lastended) { - blocksize = sizeof(i2CmdHeader); - totalsize += sizeof(i2CmdHeader); - } - totalsize += cnt; - blocksize += cnt; - - // If this command had to end a block, then we will make sure to - // account for it should there be any more blocks. - lastended = pCs->flags & END; - } - for (;;) { - // Make sure any pending flush commands go out before we add more data. - if ( !( pCh->flush_flags && i2RetryFlushOutput( pCh ) ) ) { - // How much room (this time through) ? - switch(type) { - case PTYPE_INLINE: - lock_var_p = &pCh->Obuf_spinlock; - write_lock_irqsave(lock_var_p, flags); - stuffIndex = pCh->Obuf_stuff; - bufroom = pCh->Obuf_strip - stuffIndex; - break; - case PTYPE_BYPASS: - lock_var_p = &pCh->Cbuf_spinlock; - write_lock_irqsave(lock_var_p, flags); - stuffIndex = pCh->Cbuf_stuff; - bufroom = pCh->Cbuf_strip - stuffIndex; - break; - default: - return -5; - } - if (--bufroom < 0) { - bufroom += maxBuff; - } - - ip2trace (CHANN, ITRC_QUEUE, 2, 1, bufroom ); - - // Check for overflow - if (totalsize <= bufroom) { - // Normal Expected path - We still hold LOCK - break; /* from for()- Enough room: goto proceed */ - } - ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize); - write_unlock_irqrestore(lock_var_p, flags); - } else - ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize); - - /* Prepare to wait for buffers to empty */ - serviceOutgoingFifo(pB); // Dump what we got - - if (timeout == 0) { - return 0; // Tired of waiting - } - if (timeout > 0) - timeout--; // So negative values == forever - - if (!in_interrupt()) { - schedule_timeout_interruptible(1); // short nap - } else { - // we cannot sched/sleep in interrupt silly - return 0; - } - if (signal_pending(current)) { - return 0; // Wake up! Time to die!!! - } - - ip2trace (CHANN, ITRC_QUEUE, 4, 0 ); - - } // end of for(;;) - - // At this point we have room and the lock - stick them in. - channel = pCh->infl.hd.i2sChannel; - pInsert = &pBuf[stuffIndex]; // Pointer to start of packet - pDest = CMD_OF(pInsert); // Pointer to start of command - - // When we start counting, the block is the size of the header - for (blocksize = sizeof(i2CmdHeader), count = nCommands, - lastended = 0, ppCs = &pCs0; - count; - count--, ppCs++) - { - pCs = *ppCs; // Points to command protocol structure - - // If this is a bookmark request command, post the fact that a bookmark - // request is pending. NOTE THIS TRICK ONLY WORKS BECAUSE CMD_BMARK_REQ - // has no parameters! The more general solution would be to reference - // pCs->cmd[0]. - if (pCs == CMD_BMARK_REQ) { - pCh->bookMarks++; - - ip2trace (CHANN, ITRC_DRAIN, 30, 1, pCh->bookMarks ); - - } - cnt = pCs->length; - - // If this command would put us over the maximum block size or - // if the last command had to be at the end of a block, we end - // the existing block here and start a new one. - if ((blocksize + cnt > maxBlock) || lastended) { - - ip2trace (CHANN, ITRC_QUEUE, 5, 0 ); - - PTYPE_OF(pInsert) = type; - CHANNEL_OF(pInsert) = channel; - // count here does not include the header - CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader); - stuffIndex += blocksize; - if(stuffIndex >= maxBuff) { - stuffIndex = 0; - pInsert = pBuf; - } - pInsert = &pBuf[stuffIndex]; // Pointer to start of next pkt - pDest = CMD_OF(pInsert); - blocksize = sizeof(i2CmdHeader); - } - // Now we know there is room for this one in the current block - - blocksize += cnt; // Total bytes in this command - pSource = pCs->cmd; // Copy the command into the buffer - while (cnt--) { - *pDest++ = *pSource++; - } - // If this command had to end a block, then we will make sure to account - // for it should there be any more blocks. - lastended = pCs->flags & END; - } // end for - // Clean up the final block by writing header, etc - - PTYPE_OF(pInsert) = type; - CHANNEL_OF(pInsert) = channel; - // count here does not include the header - CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader); - stuffIndex += blocksize; - if(stuffIndex >= maxBuff) { - stuffIndex = 0; - pInsert = pBuf; - } - // Updates the index, and post the need for service. When adding these to - // the queue of channels, we turn off the interrupt while doing so, - // because at interrupt level we might want to push a channel back to the - // end of the queue. - switch(type) - { - case PTYPE_INLINE: - pCh->Obuf_stuff = stuffIndex; // Store buffer pointer - write_unlock_irqrestore(&pCh->Obuf_spinlock, flags); - - pB->debugInlineQueued++; - // Add the channel pointer to list of channels needing service (first - // come...), if it's not already there. - i2QueueNeeds(pB, pCh, NEED_INLINE); - break; - - case PTYPE_BYPASS: - pCh->Cbuf_stuff = stuffIndex; // Store buffer pointer - write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags); - - pB->debugBypassQueued++; - // Add the channel pointer to list of channels needing service (first - // come...), if it's not already there. - i2QueueNeeds(pB, pCh, NEED_BYPASS); - break; - } - - ip2trace (CHANN, ITRC_QUEUE, ITRC_RETURN, 1, nCommands ); - - return nCommands; // Good status: number of commands sent -} - -//****************************************************************************** -// Function: i2GetStatus(pCh,resetBits) -// Parameters: Pointer to a channel structure -// Bit map of status bits to clear -// Returns: Bit map of current status bits -// -// Description: -// Returns the state of data set signals, and whether a break has been received, -// (see i2lib.h for bit-mapped result). resetBits is a bit-map of any status -// bits to be cleared: I2_BRK, I2_PAR, I2_FRA, I2_OVR,... These are cleared -// AFTER the condition is passed. If pCh does not point to a valid channel, -// returns -1 (which would be impossible otherwise. -//****************************************************************************** -static int -i2GetStatus(i2ChanStrPtr pCh, int resetBits) -{ - unsigned short status; - i2eBordStrPtr pB; - - ip2trace (CHANN, ITRC_STATUS, ITRC_ENTER, 2, pCh->dataSetIn, resetBits ); - - // Make sure the channel exists, otherwise do nothing */ - if ( !i2Validate ( pCh ) ) - return -1; - - pB = pCh->pMyBord; - - status = pCh->dataSetIn; - - // Clear any specified error bits: but note that only actual error bits can - // be cleared, regardless of the value passed. - if (resetBits) - { - pCh->dataSetIn &= ~(resetBits & (I2_BRK | I2_PAR | I2_FRA | I2_OVR)); - pCh->dataSetIn &= ~(I2_DDCD | I2_DCTS | I2_DDSR | I2_DRI); - } - - ip2trace (CHANN, ITRC_STATUS, ITRC_RETURN, 1, pCh->dataSetIn ); - - return status; -} - -//****************************************************************************** -// Function: i2Input(pChpDest,count) -// Parameters: Pointer to a channel structure -// Pointer to data buffer -// Number of bytes to read -// Returns: Number of bytes read, or -1 for error -// -// Description: -// Strips data from the input buffer and writes it to pDest. If there is a -// collosal blunder, (invalid structure pointers or the like), returns -1. -// Otherwise, returns the number of bytes read. -//****************************************************************************** -static int -i2Input(i2ChanStrPtr pCh) -{ - int amountToMove; - unsigned short stripIndex; - int count; - unsigned long flags = 0; - - ip2trace (CHANN, ITRC_INPUT, ITRC_ENTER, 0); - - // Ensure channel structure seems real - if ( !i2Validate( pCh ) ) { - count = -1; - goto i2Input_exit; - } - write_lock_irqsave(&pCh->Ibuf_spinlock, flags); - - // initialize some accelerators and private copies - stripIndex = pCh->Ibuf_strip; - - count = pCh->Ibuf_stuff - stripIndex; - - // If buffer is empty or requested data count was 0, (trivial case) return - // without any further thought. - if ( count == 0 ) { - write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); - goto i2Input_exit; - } - // Adjust for buffer wrap - if ( count < 0 ) { - count += IBUF_SIZE; - } - // Don't give more than can be taken by the line discipline - amountToMove = pCh->pTTY->receive_room; - if (count > amountToMove) { - count = amountToMove; - } - // How much could we copy without a wrap? - amountToMove = IBUF_SIZE - stripIndex; - - if (amountToMove > count) { - amountToMove = count; - } - // Move the first block - pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY, - &(pCh->Ibuf[stripIndex]), NULL, amountToMove ); - // If we needed to wrap, do the second data move - if (count > amountToMove) { - pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY, - pCh->Ibuf, NULL, count - amountToMove ); - } - // Bump and wrap the stripIndex all at once by the amount of data read. This - // method is good regardless of whether the data was in one or two pieces. - stripIndex += count; - if (stripIndex >= IBUF_SIZE) { - stripIndex -= IBUF_SIZE; - } - pCh->Ibuf_strip = stripIndex; - - // Update our flow control information and possibly queue ourselves to send - // it, depending on how much data has been stripped since the last time a - // packet was sent. - pCh->infl.asof += count; - - if ((pCh->sinceLastFlow += count) >= pCh->whenSendFlow) { - pCh->sinceLastFlow -= pCh->whenSendFlow; - write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); - i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); - } else { - write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); - } - -i2Input_exit: - - ip2trace (CHANN, ITRC_INPUT, ITRC_RETURN, 1, count); - - return count; -} - -//****************************************************************************** -// Function: i2InputFlush(pCh) -// Parameters: Pointer to a channel structure -// Returns: Number of bytes stripped, or -1 for error -// -// Description: -// Strips any data from the input buffer. If there is a collosal blunder, -// (invalid structure pointers or the like), returns -1. Otherwise, returns the -// number of bytes stripped. -//****************************************************************************** -static int -i2InputFlush(i2ChanStrPtr pCh) -{ - int count; - unsigned long flags; - - // Ensure channel structure seems real - if ( !i2Validate ( pCh ) ) - return -1; - - ip2trace (CHANN, ITRC_INPUT, 10, 0); - - write_lock_irqsave(&pCh->Ibuf_spinlock, flags); - count = pCh->Ibuf_stuff - pCh->Ibuf_strip; - - // Adjust for buffer wrap - if (count < 0) { - count += IBUF_SIZE; - } - - // Expedient way to zero out the buffer - pCh->Ibuf_strip = pCh->Ibuf_stuff; - - - // Update our flow control information and possibly queue ourselves to send - // it, depending on how much data has been stripped since the last time a - // packet was sent. - - pCh->infl.asof += count; - - if ( (pCh->sinceLastFlow += count) >= pCh->whenSendFlow ) - { - pCh->sinceLastFlow -= pCh->whenSendFlow; - write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); - i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); - } else { - write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); - } - - ip2trace (CHANN, ITRC_INPUT, 19, 1, count); - - return count; -} - -//****************************************************************************** -// Function: i2InputAvailable(pCh) -// Parameters: Pointer to a channel structure -// Returns: Number of bytes available, or -1 for error -// -// Description: -// If there is a collosal blunder, (invalid structure pointers or the like), -// returns -1. Otherwise, returns the number of bytes stripped. Otherwise, -// returns the number of bytes available in the buffer. -//****************************************************************************** -#if 0 -static int -i2InputAvailable(i2ChanStrPtr pCh) -{ - int count; - - // Ensure channel structure seems real - if ( !i2Validate ( pCh ) ) return -1; - - - // initialize some accelerators and private copies - read_lock_irqsave(&pCh->Ibuf_spinlock, flags); - count = pCh->Ibuf_stuff - pCh->Ibuf_strip; - read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); - - // Adjust for buffer wrap - if (count < 0) - { - count += IBUF_SIZE; - } - - return count; -} -#endif - -//****************************************************************************** -// Function: i2Output(pCh, pSource, count) -// Parameters: Pointer to channel structure -// Pointer to source data -// Number of bytes to send -// Returns: Number of bytes sent, or -1 for error -// -// Description: -// Queues the data at pSource to be sent as data packets to the board. If there -// is a collosal blunder, (invalid structure pointers or the like), returns -1. -// Otherwise, returns the number of bytes written. What if there is not enough -// room for all the data? If pCh->channelOptions & CO_NBLOCK_WRITE is set, then -// we transfer as many characters as we can now, then return. If this bit is -// clear (default), routine will spin along until all the data is buffered. -// Should this occur, the 1-ms delay routine is called while waiting to avoid -// applications that one cannot break out of. -//****************************************************************************** -static int -i2Output(i2ChanStrPtr pCh, const char *pSource, int count) -{ - i2eBordStrPtr pB; - unsigned char *pInsert; - int amountToMove; - int countOriginal = count; - unsigned short channel; - unsigned short stuffIndex; - unsigned long flags; - - int bailout = 10; - - ip2trace (CHANN, ITRC_OUTPUT, ITRC_ENTER, 2, count, 0 ); - - // Ensure channel structure seems real - if ( !i2Validate ( pCh ) ) - return -1; - - // initialize some accelerators and private copies - pB = pCh->pMyBord; - channel = pCh->infl.hd.i2sChannel; - - // If the board has gone fatal, return bad, and also hit the trap routine if - // it exists. - if (pB->i2eFatal) { - if (pB->i2eFatalTrap) { - (*(pB)->i2eFatalTrap)(pB); - } - return -1; - } - // Proceed as though we would do everything - while ( count > 0 ) { - - // How much room in output buffer is there? - read_lock_irqsave(&pCh->Obuf_spinlock, flags); - amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; - read_unlock_irqrestore(&pCh->Obuf_spinlock, flags); - if (amountToMove < 0) { - amountToMove += OBUF_SIZE; - } - // Subtract off the headers size and see how much room there is for real - // data. If this is negative, we will discover later. - amountToMove -= sizeof (i2DataHeader); - - // Don't move more (now) than can go in a single packet - if ( amountToMove > (int)(MAX_OBUF_BLOCK - sizeof(i2DataHeader)) ) { - amountToMove = MAX_OBUF_BLOCK - sizeof(i2DataHeader); - } - // Don't move more than the count we were given - if (amountToMove > count) { - amountToMove = count; - } - // Now we know how much we must move: NB because the ring buffers have - // an overflow area at the end, we needn't worry about wrapping in the - // middle of a packet. - -// Small WINDOW here with no LOCK but I can't call Flush with LOCK -// We would be flushing (or ending flush) anyway - - ip2trace (CHANN, ITRC_OUTPUT, 10, 1, amountToMove ); - - if ( !(pCh->flush_flags && i2RetryFlushOutput(pCh) ) - && amountToMove > 0 ) - { - write_lock_irqsave(&pCh->Obuf_spinlock, flags); - stuffIndex = pCh->Obuf_stuff; - - // Had room to move some data: don't know whether the block size, - // buffer space, or what was the limiting factor... - pInsert = &(pCh->Obuf[stuffIndex]); - - // Set up the header - CHANNEL_OF(pInsert) = channel; - PTYPE_OF(pInsert) = PTYPE_DATA; - TAG_OF(pInsert) = 0; - ID_OF(pInsert) = ID_ORDINARY_DATA; - DATA_COUNT_OF(pInsert) = amountToMove; - - // Move the data - memcpy( (char*)(DATA_OF(pInsert)), pSource, amountToMove ); - // Adjust pointers and indices - pSource += amountToMove; - pCh->Obuf_char_count += amountToMove; - stuffIndex += amountToMove + sizeof(i2DataHeader); - count -= amountToMove; - - if (stuffIndex >= OBUF_SIZE) { - stuffIndex = 0; - } - pCh->Obuf_stuff = stuffIndex; - - write_unlock_irqrestore(&pCh->Obuf_spinlock, flags); - - ip2trace (CHANN, ITRC_OUTPUT, 13, 1, stuffIndex ); - - } else { - - // Cannot move data - // becuz we need to stuff a flush - // or amount to move is <= 0 - - ip2trace(CHANN, ITRC_OUTPUT, 14, 3, - amountToMove, pB->i2eFifoRemains, - pB->i2eWaitingForEmptyFifo ); - - // Put this channel back on queue - // this ultimatly gets more data or wakes write output - i2QueueNeeds(pB, pCh, NEED_INLINE); - - if ( pB->i2eWaitingForEmptyFifo ) { - - ip2trace (CHANN, ITRC_OUTPUT, 16, 0 ); - - // or schedule - if (!in_interrupt()) { - - ip2trace (CHANN, ITRC_OUTPUT, 61, 0 ); - - schedule_timeout_interruptible(2); - if (signal_pending(current)) { - break; - } - continue; - } else { - - ip2trace (CHANN, ITRC_OUTPUT, 62, 0 ); - - // let interrupt in = WAS restore_flags() - // We hold no lock nor is irq off anymore??? - - break; - } - break; // from while(count) - } - else if ( pB->i2eFifoRemains < 32 && !pB->i2eTxMailEmpty ( pB ) ) - { - ip2trace (CHANN, ITRC_OUTPUT, 19, 2, - pB->i2eFifoRemains, - pB->i2eTxMailEmpty ); - - break; // from while(count) - } else if ( pCh->channelNeeds & NEED_CREDIT ) { - - ip2trace (CHANN, ITRC_OUTPUT, 22, 0 ); - - break; // from while(count) - } else if ( --bailout) { - - // Try to throw more things (maybe not us) in the fifo if we're - // not already waiting for it. - - ip2trace (CHANN, ITRC_OUTPUT, 20, 0 ); - - serviceOutgoingFifo(pB); - //break; CONTINUE; - } else { - ip2trace (CHANN, ITRC_OUTPUT, 21, 3, - pB->i2eFifoRemains, - pB->i2eOutMailWaiting, - pB->i2eWaitingForEmptyFifo ); - - break; // from while(count) - } - } - } // End of while(count) - - i2QueueNeeds(pB, pCh, NEED_INLINE); - - // We drop through either when the count expires, or when there is some - // count left, but there was a non-blocking write. - if (countOriginal > count) { - - ip2trace (CHANN, ITRC_OUTPUT, 17, 2, countOriginal, count ); - - serviceOutgoingFifo( pB ); - } - - ip2trace (CHANN, ITRC_OUTPUT, ITRC_RETURN, 2, countOriginal, count ); - - return countOriginal - count; -} - -//****************************************************************************** -// Function: i2FlushOutput(pCh) -// Parameters: Pointer to a channel structure -// Returns: Nothing -// -// Description: -// Sends bypass command to start flushing (waiting possibly forever until there -// is room), then sends inline command to stop flushing output, (again waiting -// possibly forever). -//****************************************************************************** -static inline void -i2FlushOutput(i2ChanStrPtr pCh) -{ - - ip2trace (CHANN, ITRC_FLUSH, 1, 1, pCh->flush_flags ); - - if (pCh->flush_flags) - return; - - if ( 1 != i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) { - pCh->flush_flags = STARTFL_FLAG; // Failed - flag for later - - ip2trace (CHANN, ITRC_FLUSH, 2, 0 ); - - } else if ( 1 != i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL) ) { - pCh->flush_flags = STOPFL_FLAG; // Failed - flag for later - - ip2trace (CHANN, ITRC_FLUSH, 3, 0 ); - } -} - -static int -i2RetryFlushOutput(i2ChanStrPtr pCh) -{ - int old_flags = pCh->flush_flags; - - ip2trace (CHANN, ITRC_FLUSH, 14, 1, old_flags ); - - pCh->flush_flags = 0; // Clear flag so we can avoid recursion - // and queue the commands - - if ( old_flags & STARTFL_FLAG ) { - if ( 1 == i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) { - old_flags = STOPFL_FLAG; //Success - send stop flush - } else { - old_flags = STARTFL_FLAG; //Failure - Flag for retry later - } - - ip2trace (CHANN, ITRC_FLUSH, 15, 1, old_flags ); - - } - if ( old_flags & STOPFL_FLAG ) { - if (1 == i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL)) { - old_flags = 0; // Success - clear flags - } - - ip2trace (CHANN, ITRC_FLUSH, 16, 1, old_flags ); - } - pCh->flush_flags = old_flags; - - ip2trace (CHANN, ITRC_FLUSH, 17, 1, old_flags ); - - return old_flags; -} - -//****************************************************************************** -// Function: i2DrainOutput(pCh,timeout) -// Parameters: Pointer to a channel structure -// Maximum period to wait -// Returns: ? -// -// Description: -// Uses the bookmark request command to ask the board to send a bookmark back as -// soon as all the data is completely sent. -//****************************************************************************** -static void -i2DrainWakeup(unsigned long d) -{ - i2ChanStrPtr pCh = (i2ChanStrPtr)d; - - ip2trace (CHANN, ITRC_DRAIN, 10, 1, pCh->BookmarkTimer.expires ); - - pCh->BookmarkTimer.expires = 0; - wake_up_interruptible( &pCh->pBookmarkWait ); -} - -static void -i2DrainOutput(i2ChanStrPtr pCh, int timeout) -{ - wait_queue_t wait; - i2eBordStrPtr pB; - - ip2trace (CHANN, ITRC_DRAIN, ITRC_ENTER, 1, pCh->BookmarkTimer.expires); - - pB = pCh->pMyBord; - // If the board has gone fatal, return bad, - // and also hit the trap routine if it exists. - if (pB->i2eFatal) { - if (pB->i2eFatalTrap) { - (*(pB)->i2eFatalTrap)(pB); - } - return; - } - if ((timeout > 0) && (pCh->BookmarkTimer.expires == 0 )) { - // One per customer (channel) - setup_timer(&pCh->BookmarkTimer, i2DrainWakeup, - (unsigned long)pCh); - - ip2trace (CHANN, ITRC_DRAIN, 1, 1, pCh->BookmarkTimer.expires ); - - mod_timer(&pCh->BookmarkTimer, jiffies + timeout); - } - - i2QueueCommands( PTYPE_INLINE, pCh, -1, 1, CMD_BMARK_REQ ); - - init_waitqueue_entry(&wait, current); - add_wait_queue(&(pCh->pBookmarkWait), &wait); - set_current_state( TASK_INTERRUPTIBLE ); - - serviceOutgoingFifo( pB ); - - schedule(); // Now we take our interruptible sleep on - - // Clean up the queue - set_current_state( TASK_RUNNING ); - remove_wait_queue(&(pCh->pBookmarkWait), &wait); - - // if expires == 0 then timer poped, then do not need to del_timer - if ((timeout > 0) && pCh->BookmarkTimer.expires && - time_before(jiffies, pCh->BookmarkTimer.expires)) { - del_timer( &(pCh->BookmarkTimer) ); - pCh->BookmarkTimer.expires = 0; - - ip2trace (CHANN, ITRC_DRAIN, 3, 1, pCh->BookmarkTimer.expires ); - - } - ip2trace (CHANN, ITRC_DRAIN, ITRC_RETURN, 1, pCh->BookmarkTimer.expires ); - return; -} - -//****************************************************************************** -// Function: i2OutputFree(pCh) -// Parameters: Pointer to a channel structure -// Returns: Space in output buffer -// -// Description: -// Returns -1 if very gross error. Otherwise returns the amount of bytes still -// free in the output buffer. -//****************************************************************************** -static int -i2OutputFree(i2ChanStrPtr pCh) -{ - int amountToMove; - unsigned long flags; - - // Ensure channel structure seems real - if ( !i2Validate ( pCh ) ) { - return -1; - } - read_lock_irqsave(&pCh->Obuf_spinlock, flags); - amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; - read_unlock_irqrestore(&pCh->Obuf_spinlock, flags); - - if (amountToMove < 0) { - amountToMove += OBUF_SIZE; - } - // If this is negative, we will discover later - amountToMove -= sizeof(i2DataHeader); - - return (amountToMove < 0) ? 0 : amountToMove; -} -static void - -ip2_owake( PTTY tp) -{ - i2ChanStrPtr pCh; - - if (tp == NULL) return; - - pCh = tp->driver_data; - - ip2trace (CHANN, ITRC_SICMD, 10, 2, tp->flags, - (1 << TTY_DO_WRITE_WAKEUP) ); - - tty_wakeup(tp); -} - -static inline void -set_baud_params(i2eBordStrPtr pB) -{ - int i,j; - i2ChanStrPtr *pCh; - - pCh = (i2ChanStrPtr *) pB->i2eChannelPtr; - - for (i = 0; i < ABS_MAX_BOXES; i++) { - if (pB->channelBtypes.bid_value[i]) { - if (BID_HAS_654(pB->channelBtypes.bid_value[i])) { - for (j = 0; j < ABS_BIGGEST_BOX; j++) { - if (pCh[i*16+j] == NULL) - break; - (pCh[i*16+j])->BaudBase = 921600; // MAX for ST654 - (pCh[i*16+j])->BaudDivisor = 96; - } - } else { // has cirrus cd1400 - for (j = 0; j < ABS_BIGGEST_BOX; j++) { - if (pCh[i*16+j] == NULL) - break; - (pCh[i*16+j])->BaudBase = 115200; // MAX for CD1400 - (pCh[i*16+j])->BaudDivisor = 12; - } - } - } - } -} - -//****************************************************************************** -// Function: i2StripFifo(pB) -// Parameters: Pointer to a board structure -// Returns: ? -// -// Description: -// Strips all the available data from the incoming FIFO, identifies the type of -// packet, and either buffers the data or does what needs to be done. -// -// Note there is no overflow checking here: if the board sends more data than it -// ought to, we will not detect it here, but blindly overflow... -//****************************************************************************** - -// A buffer for reading in blocks for unknown channels -static unsigned char junkBuffer[IBUF_SIZE]; - -// A buffer to read in a status packet. Because of the size of the count field -// for these things, the maximum packet size must be less than MAX_CMD_PACK_SIZE -static unsigned char cmdBuffer[MAX_CMD_PACK_SIZE + 4]; - -// This table changes the bit order from MSR order given by STAT_MODEM packet to -// status bits used in our library. -static char xlatDss[16] = { -0 | 0 | 0 | 0 , -0 | 0 | 0 | I2_CTS , -0 | 0 | I2_DSR | 0 , -0 | 0 | I2_DSR | I2_CTS , -0 | I2_RI | 0 | 0 , -0 | I2_RI | 0 | I2_CTS , -0 | I2_RI | I2_DSR | 0 , -0 | I2_RI | I2_DSR | I2_CTS , -I2_DCD | 0 | 0 | 0 , -I2_DCD | 0 | 0 | I2_CTS , -I2_DCD | 0 | I2_DSR | 0 , -I2_DCD | 0 | I2_DSR | I2_CTS , -I2_DCD | I2_RI | 0 | 0 , -I2_DCD | I2_RI | 0 | I2_CTS , -I2_DCD | I2_RI | I2_DSR | 0 , -I2_DCD | I2_RI | I2_DSR | I2_CTS }; - -static inline void -i2StripFifo(i2eBordStrPtr pB) -{ - i2ChanStrPtr pCh; - int channel; - int count; - unsigned short stuffIndex; - int amountToRead; - unsigned char *pc, *pcLimit; - unsigned char uc; - unsigned char dss_change; - unsigned long bflags,cflags; - -// ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_ENTER, 0 ); - - while (I2_HAS_INPUT(pB)) { -// ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 2, 0 ); - - // Process packet from fifo a one atomic unit - write_lock_irqsave(&pB->read_fifo_spinlock, bflags); - - // The first word (or two bytes) will have channel number and type of - // packet, possibly other information - pB->i2eLeadoffWord[0] = iiReadWord(pB); - - switch(PTYPE_OF(pB->i2eLeadoffWord)) - { - case PTYPE_DATA: - pB->got_input = 1; - -// ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 3, 0 ); - - channel = CHANNEL_OF(pB->i2eLeadoffWord); /* Store channel */ - count = iiReadWord(pB); /* Count is in the next word */ - -// NEW: Check the count for sanity! Should the hardware fail, our death -// is more pleasant. While an oversize channel is acceptable (just more -// than the driver supports), an over-length count clearly means we are -// sick! - if ( ((unsigned int)count) > IBUF_SIZE ) { - pB->i2eFatal = 2; - write_unlock_irqrestore(&pB->read_fifo_spinlock, - bflags); - return; /* Bail out ASAP */ - } - // Channel is illegally big ? - if ((channel >= pB->i2eChannelCnt) || - (NULL==(pCh = ((i2ChanStrPtr*)pB->i2eChannelPtr)[channel]))) - { - iiReadBuf(pB, junkBuffer, count); - write_unlock_irqrestore(&pB->read_fifo_spinlock, - bflags); - break; /* From switch: ready for next packet */ - } - - // Channel should be valid, then - - // If this is a hot-key, merely post its receipt for now. These are - // always supposed to be 1-byte packets, so we won't even check the - // count. Also we will post an acknowledgement to the board so that - // more data can be forthcoming. Note that we are not trying to use - // these sequences in this driver, merely to robustly ignore them. - if(ID_OF(pB->i2eLeadoffWord) == ID_HOT_KEY) - { - pCh->hotKeyIn = iiReadWord(pB) & 0xff; - write_unlock_irqrestore(&pB->read_fifo_spinlock, - bflags); - i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_HOTACK); - break; /* From the switch: ready for next packet */ - } - - // Normal data! We crudely assume there is room for the data in our - // buffer because the board wouldn't have exceeded his credit limit. - write_lock_irqsave(&pCh->Ibuf_spinlock, cflags); - // We have 2 locks now - stuffIndex = pCh->Ibuf_stuff; - amountToRead = IBUF_SIZE - stuffIndex; - if (amountToRead > count) - amountToRead = count; - - // stuffIndex would have been already adjusted so there would - // always be room for at least one, and count is always at least - // one. - - iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead); - pCh->icount.rx += amountToRead; - - // Update the stuffIndex by the amount of data moved. Note we could - // never ask for more data than would just fit. However, we might - // have read in one more byte than we wanted because the read - // rounds up to even bytes. If this byte is on the end of the - // packet, and is padding, we ignore it. If the byte is part of - // the actual data, we need to move it. - - stuffIndex += amountToRead; - - if (stuffIndex >= IBUF_SIZE) { - if ((amountToRead & 1) && (count > amountToRead)) { - pCh->Ibuf[0] = pCh->Ibuf[IBUF_SIZE]; - amountToRead++; - stuffIndex = 1; - } else { - stuffIndex = 0; - } - } - - // If there is anything left over, read it as well - if (count > amountToRead) { - amountToRead = count - amountToRead; - iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead); - pCh->icount.rx += amountToRead; - stuffIndex += amountToRead; - } - - // Update stuff index - pCh->Ibuf_stuff = stuffIndex; - write_unlock_irqrestore(&pCh->Ibuf_spinlock, cflags); - write_unlock_irqrestore(&pB->read_fifo_spinlock, - bflags); - -#ifdef USE_IQ - schedule_work(&pCh->tqueue_input); -#else - do_input(&pCh->tqueue_input); -#endif - - // Note we do not need to maintain any flow-control credits at this - // time: if we were to increment .asof and decrement .room, there - // would be no net effect. Instead, when we strip data, we will - // increment .asof and leave .room unchanged. - - break; // From switch: ready for next packet - - case PTYPE_STATUS: - ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 4, 0 ); - - count = CMD_COUNT_OF(pB->i2eLeadoffWord); - - iiReadBuf(pB, cmdBuffer, count); - // We can release early with buffer grab - write_unlock_irqrestore(&pB->read_fifo_spinlock, - bflags); - - pc = cmdBuffer; - pcLimit = &(cmdBuffer[count]); - - while (pc < pcLimit) { - channel = *pc++; - - ip2trace (channel, ITRC_SFIFO, 7, 2, channel, *pc ); - - /* check for valid channel */ - if (channel < pB->i2eChannelCnt - && - (pCh = (((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])) != NULL - ) - { - dss_change = 0; - - switch (uc = *pc++) - { - /* Breaks and modem signals are easy: just update status */ - case STAT_CTS_UP: - if ( !(pCh->dataSetIn & I2_CTS) ) - { - pCh->dataSetIn |= I2_DCTS; - pCh->icount.cts++; - dss_change = 1; - } - pCh->dataSetIn |= I2_CTS; - break; - - case STAT_CTS_DN: - if ( pCh->dataSetIn & I2_CTS ) - { - pCh->dataSetIn |= I2_DCTS; - pCh->icount.cts++; - dss_change = 1; - } - pCh->dataSetIn &= ~I2_CTS; - break; - - case STAT_DCD_UP: - ip2trace (channel, ITRC_MODEM, 1, 1, pCh->dataSetIn ); - - if ( !(pCh->dataSetIn & I2_DCD) ) - { - ip2trace (CHANN, ITRC_MODEM, 2, 0 ); - pCh->dataSetIn |= I2_DDCD; - pCh->icount.dcd++; - dss_change = 1; - } - pCh->dataSetIn |= I2_DCD; - - ip2trace (channel, ITRC_MODEM, 3, 1, pCh->dataSetIn ); - break; - - case STAT_DCD_DN: - ip2trace (channel, ITRC_MODEM, 4, 1, pCh->dataSetIn ); - if ( pCh->dataSetIn & I2_DCD ) - { - ip2trace (channel, ITRC_MODEM, 5, 0 ); - pCh->dataSetIn |= I2_DDCD; - pCh->icount.dcd++; - dss_change = 1; - } - pCh->dataSetIn &= ~I2_DCD; - - ip2trace (channel, ITRC_MODEM, 6, 1, pCh->dataSetIn ); - break; - - case STAT_DSR_UP: - if ( !(pCh->dataSetIn & I2_DSR) ) - { - pCh->dataSetIn |= I2_DDSR; - pCh->icount.dsr++; - dss_change = 1; - } - pCh->dataSetIn |= I2_DSR; - break; - - case STAT_DSR_DN: - if ( pCh->dataSetIn & I2_DSR ) - { - pCh->dataSetIn |= I2_DDSR; - pCh->icount.dsr++; - dss_change = 1; - } - pCh->dataSetIn &= ~I2_DSR; - break; - - case STAT_RI_UP: - if ( !(pCh->dataSetIn & I2_RI) ) - { - pCh->dataSetIn |= I2_DRI; - pCh->icount.rng++; - dss_change = 1; - } - pCh->dataSetIn |= I2_RI ; - break; - - case STAT_RI_DN: - // to be compat with serial.c - //if ( pCh->dataSetIn & I2_RI ) - //{ - // pCh->dataSetIn |= I2_DRI; - // pCh->icount.rng++; - // dss_change = 1; - //} - pCh->dataSetIn &= ~I2_RI ; - break; - - case STAT_BRK_DET: - pCh->dataSetIn |= I2_BRK; - pCh->icount.brk++; - dss_change = 1; - break; - - // Bookmarks? one less request we're waiting for - case STAT_BMARK: - pCh->bookMarks--; - if (pCh->bookMarks <= 0 ) { - pCh->bookMarks = 0; - wake_up_interruptible( &pCh->pBookmarkWait ); - - ip2trace (channel, ITRC_DRAIN, 20, 1, pCh->BookmarkTimer.expires ); - } - break; - - // Flow control packets? Update the new credits, and if - // someone was waiting for output, queue him up again. - case STAT_FLOW: - pCh->outfl.room = - ((flowStatPtr)pc)->room - - (pCh->outfl.asof - ((flowStatPtr)pc)->asof); - - ip2trace (channel, ITRC_STFLW, 1, 1, pCh->outfl.room ); - - if (pCh->channelNeeds & NEED_CREDIT) - { - ip2trace (channel, ITRC_STFLW, 2, 1, pCh->channelNeeds); - - pCh->channelNeeds &= ~NEED_CREDIT; - i2QueueNeeds(pB, pCh, NEED_INLINE); - if ( pCh->pTTY ) - ip2_owake(pCh->pTTY); - } - - ip2trace (channel, ITRC_STFLW, 3, 1, pCh->channelNeeds); - - pc += sizeof(flowStat); - break; - - /* Special packets: */ - /* Just copy the information into the channel structure */ - - case STAT_STATUS: - - pCh->channelStatus = *((debugStatPtr)pc); - pc += sizeof(debugStat); - break; - - case STAT_TXCNT: - - pCh->channelTcount = *((cntStatPtr)pc); - pc += sizeof(cntStat); - break; - - case STAT_RXCNT: - - pCh->channelRcount = *((cntStatPtr)pc); - pc += sizeof(cntStat); - break; - - case STAT_BOXIDS: - pB->channelBtypes = *((bidStatPtr)pc); - pc += sizeof(bidStat); - set_baud_params(pB); - break; - - case STAT_HWFAIL: - i2QueueCommands (PTYPE_INLINE, pCh, 0, 1, CMD_HW_TEST); - pCh->channelFail = *((failStatPtr)pc); - pc += sizeof(failStat); - break; - - /* No explicit match? then - * Might be an error packet... - */ - default: - switch (uc & STAT_MOD_ERROR) - { - case STAT_ERROR: - if (uc & STAT_E_PARITY) { - pCh->dataSetIn |= I2_PAR; - pCh->icount.parity++; - } - if (uc & STAT_E_FRAMING){ - pCh->dataSetIn |= I2_FRA; - pCh->icount.frame++; - } - if (uc & STAT_E_OVERRUN){ - pCh->dataSetIn |= I2_OVR; - pCh->icount.overrun++; - } - break; - - case STAT_MODEM: - // the answer to DSS_NOW request (not change) - pCh->dataSetIn = (pCh->dataSetIn - & ~(I2_RI | I2_CTS | I2_DCD | I2_DSR) ) - | xlatDss[uc & 0xf]; - wake_up_interruptible ( &pCh->dss_now_wait ); - default: - break; - } - } /* End of switch on status type */ - if (dss_change) { -#ifdef USE_IQ - schedule_work(&pCh->tqueue_status); -#else - do_status(&pCh->tqueue_status); -#endif - } - } - else /* Or else, channel is invalid */ - { - // Even though the channel is invalid, we must test the - // status to see how much additional data it has (to be - // skipped) - switch (*pc++) - { - case STAT_FLOW: - pc += 4; /* Skip the data */ - break; - - default: - break; - } - } - } // End of while (there is still some status packet left) - break; - - default: // Neither packet? should be impossible - ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 5, 1, - PTYPE_OF(pB->i2eLeadoffWord) ); - write_unlock_irqrestore(&pB->read_fifo_spinlock, - bflags); - - break; - } // End of switch on type of packets - } /*while(board I2_HAS_INPUT)*/ - - ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_RETURN, 0 ); - - // Send acknowledgement to the board even if there was no data! - pB->i2eOutMailWaiting |= MB_IN_STRIPPED; - return; -} - -//****************************************************************************** -// Function: i2Write2Fifo(pB,address,count) -// Parameters: Pointer to a board structure, source address, byte count -// Returns: bytes written -// -// Description: -// Writes count bytes to board io address(implied) from source -// Adjusts count, leaves reserve for next time around bypass cmds -//****************************************************************************** -static int -i2Write2Fifo(i2eBordStrPtr pB, unsigned char *source, int count,int reserve) -{ - int rc = 0; - unsigned long flags; - write_lock_irqsave(&pB->write_fifo_spinlock, flags); - if (!pB->i2eWaitingForEmptyFifo) { - if (pB->i2eFifoRemains > (count+reserve)) { - pB->i2eFifoRemains -= count; - iiWriteBuf(pB, source, count); - pB->i2eOutMailWaiting |= MB_OUT_STUFFED; - rc = count; - } - } - write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); - return rc; -} -//****************************************************************************** -// Function: i2StuffFifoBypass(pB) -// Parameters: Pointer to a board structure -// Returns: Nothing -// -// Description: -// Stuffs as many bypass commands into the fifo as possible. This is simpler -// than stuffing data or inline commands to fifo, since we do not have -// flow-control to deal with. -//****************************************************************************** -static inline void -i2StuffFifoBypass(i2eBordStrPtr pB) -{ - i2ChanStrPtr pCh; - unsigned char *pRemove; - unsigned short stripIndex; - unsigned short packetSize; - unsigned short paddedSize; - unsigned short notClogged = 1; - unsigned long flags; - - int bailout = 1000; - - // Continue processing so long as there are entries, or there is room in the - // fifo. Each entry represents a channel with something to do. - while ( --bailout && notClogged && - (NULL != (pCh = i2DeQueueNeeds(pB,NEED_BYPASS)))) - { - write_lock_irqsave(&pCh->Cbuf_spinlock, flags); - stripIndex = pCh->Cbuf_strip; - - // as long as there are packets for this channel... - - while (stripIndex != pCh->Cbuf_stuff) { - pRemove = &(pCh->Cbuf[stripIndex]); - packetSize = CMD_COUNT_OF(pRemove) + sizeof(i2CmdHeader); - paddedSize = roundup(packetSize, 2); - - if (paddedSize > 0) { - if ( 0 == i2Write2Fifo(pB, pRemove, paddedSize,0)) { - notClogged = 0; /* fifo full */ - i2QueueNeeds(pB, pCh, NEED_BYPASS); // Put back on queue - break; // Break from the channel - } - } -#ifdef DEBUG_FIFO -WriteDBGBuf("BYPS", pRemove, paddedSize); -#endif /* DEBUG_FIFO */ - pB->debugBypassCount++; - - pRemove += packetSize; - stripIndex += packetSize; - if (stripIndex >= CBUF_SIZE) { - stripIndex = 0; - pRemove = pCh->Cbuf; - } - } - // Done with this channel. Move to next, removing this one from - // the queue of channels if we cleaned it out (i.e., didn't get clogged. - pCh->Cbuf_strip = stripIndex; - write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags); - } // Either clogged or finished all the work - -#ifdef IP2DEBUG_TRACE - if ( !bailout ) { - ip2trace (ITRC_NO_PORT, ITRC_ERROR, 1, 0 ); - } -#endif -} - -//****************************************************************************** -// Function: i2StuffFifoFlow(pB) -// Parameters: Pointer to a board structure -// Returns: Nothing -// -// Description: -// Stuffs as many flow control packets into the fifo as possible. This is easier -// even than doing normal bypass commands, because there is always at most one -// packet, already assembled, for each channel. -//****************************************************************************** -static inline void -i2StuffFifoFlow(i2eBordStrPtr pB) -{ - i2ChanStrPtr pCh; - unsigned short paddedSize = roundup(sizeof(flowIn), 2); - - ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_ENTER, 2, - pB->i2eFifoRemains, paddedSize ); - - // Continue processing so long as there are entries, or there is room in the - // fifo. Each entry represents a channel with something to do. - while ( (NULL != (pCh = i2DeQueueNeeds(pB,NEED_FLOW)))) { - pB->debugFlowCount++; - - // NO Chan LOCK needed ??? - if ( 0 == i2Write2Fifo(pB,(unsigned char *)&(pCh->infl),paddedSize,0)) { - break; - } -#ifdef DEBUG_FIFO - WriteDBGBuf("FLOW",(unsigned char *) &(pCh->infl), paddedSize); -#endif /* DEBUG_FIFO */ - - } // Either clogged or finished all the work - - ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_RETURN, 0 ); -} - -//****************************************************************************** -// Function: i2StuffFifoInline(pB) -// Parameters: Pointer to a board structure -// Returns: Nothing -// -// Description: -// Stuffs as much data and inline commands into the fifo as possible. This is -// the most complex fifo-stuffing operation, since there if now the channel -// flow-control issue to deal with. -//****************************************************************************** -static inline void -i2StuffFifoInline(i2eBordStrPtr pB) -{ - i2ChanStrPtr pCh; - unsigned char *pRemove; - unsigned short stripIndex; - unsigned short packetSize; - unsigned short paddedSize; - unsigned short notClogged = 1; - unsigned short flowsize; - unsigned long flags; - - int bailout = 1000; - int bailout2; - - ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_ENTER, 3, pB->i2eFifoRemains, - pB->i2Dbuf_strip, pB->i2Dbuf_stuff ); - - // Continue processing so long as there are entries, or there is room in the - // fifo. Each entry represents a channel with something to do. - while ( --bailout && notClogged && - (NULL != (pCh = i2DeQueueNeeds(pB,NEED_INLINE))) ) - { - write_lock_irqsave(&pCh->Obuf_spinlock, flags); - stripIndex = pCh->Obuf_strip; - - ip2trace (CHANN, ITRC_SICMD, 3, 2, stripIndex, pCh->Obuf_stuff ); - - // as long as there are packets for this channel... - bailout2 = 1000; - while ( --bailout2 && stripIndex != pCh->Obuf_stuff) { - pRemove = &(pCh->Obuf[stripIndex]); - - // Must determine whether this be a data or command packet to - // calculate correctly the header size and the amount of - // flow-control credit this type of packet will use. - if (PTYPE_OF(pRemove) == PTYPE_DATA) { - flowsize = DATA_COUNT_OF(pRemove); - packetSize = flowsize + sizeof(i2DataHeader); - } else { - flowsize = CMD_COUNT_OF(pRemove); - packetSize = flowsize + sizeof(i2CmdHeader); - } - flowsize = CREDIT_USAGE(flowsize); - paddedSize = roundup(packetSize, 2); - - ip2trace (CHANN, ITRC_SICMD, 4, 2, pB->i2eFifoRemains, paddedSize ); - - // If we don't have enough credits from the board to send the data, - // flag the channel that we are waiting for flow control credit, and - // break out. This will clean up this channel and remove us from the - // queue of hot things to do. - - ip2trace (CHANN, ITRC_SICMD, 5, 2, pCh->outfl.room, flowsize ); - - if (pCh->outfl.room <= flowsize) { - // Do Not have the credits to send this packet. - i2QueueNeeds(pB, pCh, NEED_CREDIT); - notClogged = 0; - break; // So to do next channel - } - if ( (paddedSize > 0) - && ( 0 == i2Write2Fifo(pB, pRemove, paddedSize, 128))) { - // Do Not have room in fifo to send this packet. - notClogged = 0; - i2QueueNeeds(pB, pCh, NEED_INLINE); - break; // Break from the channel - } -#ifdef DEBUG_FIFO -WriteDBGBuf("DATA", pRemove, paddedSize); -#endif /* DEBUG_FIFO */ - pB->debugInlineCount++; - - pCh->icount.tx += flowsize; - // Update current credits - pCh->outfl.room -= flowsize; - pCh->outfl.asof += flowsize; - if (PTYPE_OF(pRemove) == PTYPE_DATA) { - pCh->Obuf_char_count -= DATA_COUNT_OF(pRemove); - } - pRemove += packetSize; - stripIndex += packetSize; - - ip2trace (CHANN, ITRC_SICMD, 6, 2, stripIndex, pCh->Obuf_strip); - - if (stripIndex >= OBUF_SIZE) { - stripIndex = 0; - pRemove = pCh->Obuf; - - ip2trace (CHANN, ITRC_SICMD, 7, 1, stripIndex ); - - } - } /* while */ - if ( !bailout2 ) { - ip2trace (CHANN, ITRC_ERROR, 3, 0 ); - } - // Done with this channel. Move to next, removing this one from the - // queue of channels if we cleaned it out (i.e., didn't get clogged. - pCh->Obuf_strip = stripIndex; - write_unlock_irqrestore(&pCh->Obuf_spinlock, flags); - if ( notClogged ) - { - - ip2trace (CHANN, ITRC_SICMD, 8, 0 ); - - if ( pCh->pTTY ) { - ip2_owake(pCh->pTTY); - } - } - } // Either clogged or finished all the work - - if ( !bailout ) { - ip2trace (ITRC_NO_PORT, ITRC_ERROR, 4, 0 ); - } - - ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_RETURN, 1,pB->i2Dbuf_strip); -} - -//****************************************************************************** -// Function: serviceOutgoingFifo(pB) -// Parameters: Pointer to a board structure -// Returns: Nothing -// -// Description: -// Helper routine to put data in the outgoing fifo, if we aren't already waiting -// for something to be there. If the fifo has only room for a very little data, -// go head and hit the board with a mailbox hit immediately. Otherwise, it will -// have to happen later in the interrupt processing. Since this routine may be -// called both at interrupt and foreground time, we must turn off interrupts -// during the entire process. -//****************************************************************************** -static void -serviceOutgoingFifo(i2eBordStrPtr pB) -{ - // If we aren't currently waiting for the board to empty our fifo, service - // everything that is pending, in priority order (especially, Bypass before - // Inline). - if ( ! pB->i2eWaitingForEmptyFifo ) - { - i2StuffFifoFlow(pB); - i2StuffFifoBypass(pB); - i2StuffFifoInline(pB); - - iiSendPendingMail(pB); - } -} - -//****************************************************************************** -// Function: i2ServiceBoard(pB) -// Parameters: Pointer to a board structure -// Returns: Nothing -// -// Description: -// Normally this is called from interrupt level, but there is deliberately -// nothing in here specific to being called from interrupt level. All the -// hardware-specific, interrupt-specific things happen at the outer levels. -// -// For example, a timer interrupt could drive this routine for some sort of -// polled operation. The only requirement is that the programmer deal with any -// atomiticity/concurrency issues that result. -// -// This routine responds to the board's having sent mailbox information to the -// host (which would normally cause an interrupt). This routine reads the -// incoming mailbox. If there is no data in it, this board did not create the -// interrupt and/or has nothing to be done to it. (Except, if we have been -// waiting to write mailbox data to it, we may do so. -// -// Based on the value in the mailbox, we may take various actions. -// -// No checking here of pB validity: after all, it shouldn't have been called by -// the handler unless pB were on the list. -//****************************************************************************** -static inline int -i2ServiceBoard ( i2eBordStrPtr pB ) -{ - unsigned inmail; - unsigned long flags; - - - /* This should be atomic because of the way we are called... */ - if (NO_MAIL_HERE == ( inmail = pB->i2eStartMail ) ) { - inmail = iiGetMail(pB); - } - pB->i2eStartMail = NO_MAIL_HERE; - - ip2trace (ITRC_NO_PORT, ITRC_INTR, 2, 1, inmail ); - - if (inmail != NO_MAIL_HERE) { - // If the board has gone fatal, nothing to do but hit a bit that will - // alert foreground tasks to protest! - if ( inmail & MB_FATAL_ERROR ) { - pB->i2eFatal = 1; - goto exit_i2ServiceBoard; - } - - /* Assuming no fatal condition, we proceed to do work */ - if ( inmail & MB_IN_STUFFED ) { - pB->i2eFifoInInts++; - i2StripFifo(pB); /* There might be incoming packets */ - } - - if (inmail & MB_OUT_STRIPPED) { - pB->i2eFifoOutInts++; - write_lock_irqsave(&pB->write_fifo_spinlock, flags); - pB->i2eFifoRemains = pB->i2eFifoSize; - pB->i2eWaitingForEmptyFifo = 0; - write_unlock_irqrestore(&pB->write_fifo_spinlock, - flags); - - ip2trace (ITRC_NO_PORT, ITRC_INTR, 30, 1, pB->i2eFifoRemains ); - - } - serviceOutgoingFifo(pB); - } - - ip2trace (ITRC_NO_PORT, ITRC_INTR, 8, 0 ); - -exit_i2ServiceBoard: - - return 0; -} diff --git a/drivers/char/ip2/i2lib.h b/drivers/char/ip2/i2lib.h deleted file mode 100644 index e559e9bac06d..000000000000 --- a/drivers/char/ip2/i2lib.h +++ /dev/null @@ -1,351 +0,0 @@ -/******************************************************************************* -* -* (c) 1998 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Header file for high level library functions -* -*******************************************************************************/ -#ifndef I2LIB_H -#define I2LIB_H 1 -//------------------------------------------------------------------------------ -// I2LIB.H -// -// IntelliPort-II and IntelliPort-IIEX -// -// Defines, structure definitions, and external declarations for i2lib.c -//------------------------------------------------------------------------------ -//-------------------------------------- -// Mandatory Includes: -//-------------------------------------- -#include "ip2types.h" -#include "i2ellis.h" -#include "i2pack.h" -#include "i2cmd.h" -#include - -//------------------------------------------------------------------------------ -// i2ChanStr -- Channel Structure: -// Used to track per-channel information for the library routines using standard -// loadware. Note also, a pointer to an array of these structures is patched -// into the i2eBordStr (see i2ellis.h) -//------------------------------------------------------------------------------ -// -// If we make some limits on the maximum block sizes, we can avoid dealing with -// buffer wrap. The wrapping of the buffer is based on where the start of the -// packet is. Then there is always room for the packet contiguously. -// -// Maximum total length of an outgoing data or in-line command block. The limit -// of 36 on data is quite arbitrary and based more on DOS memory limitations -// than the board interface. However, for commands, the maximum packet length is -// MAX_CMD_PACK_SIZE, because the field size for the count is only a few bits -// (see I2PACK.H) in such packets. For data packets, the count field size is not -// the limiting factor. As of this writing, MAX_OBUF_BLOCK < MAX_CMD_PACK_SIZE, -// but be careful if wanting to modify either. -// -#define MAX_OBUF_BLOCK 36 - -// Another note on maximum block sizes: we are buffering packets here. Data is -// put into the buffer (if there is room) regardless of the credits from the -// board. The board sends new credits whenever it has removed from his buffers a -// number of characters equal to 80% of total buffer size. (Of course, the total -// buffer size is what is reported when the very first set of flow control -// status packets are received from the board. Therefore, to be robust, you must -// always fill the board to at least 80% of the current credit limit, else you -// might not give it enough to trigger a new report. These conditions are -// obtained here so long as the maximum output block size is less than 20% the -// size of the board's output buffers. This is true at present by "coincidence" -// or "infernal knowledge": the board's output buffers are at least 700 bytes -// long (20% = 140 bytes, at least). The 80% figure is "official", so the safest -// strategy might be to trap the first flow control report and guarantee that -// the effective maxObufBlock is the minimum of MAX_OBUF_BLOCK and 20% of first -// reported buffer credit. -// -#define MAX_CBUF_BLOCK 6 // Maximum total length of a bypass command block - -#define IBUF_SIZE 512 // character capacity of input buffer per channel -#define OBUF_SIZE 1024// character capacity of output buffer per channel -#define CBUF_SIZE 10 // character capacity of output bypass buffer - -typedef struct _i2ChanStr -{ - // First, back-pointers so that given a pointer to this structure, you can - // determine the correct board and channel number to reference, (say, when - // issuing commands, etc. (Note, channel number is in infl.hd.i2sChannel.) - - int port_index; // Index of port in channel structure array attached - // to board structure. - PTTY pTTY; // Pointer to tty structure for port (OS specific) - USHORT validity; // Indicates whether the given channel has been - // initialized, really exists (or is a missing - // channel, e.g. channel 9 on an 8-port box.) - - i2eBordStrPtr pMyBord; // Back-pointer to this channel's board structure - - int wopen; // waiting fer carrier - - int throttled; // Set if upper layer can take no data - - int flags; // Defined in tty.h - - PWAITQ open_wait; // Pointer for OS sleep function. - PWAITQ close_wait; // Pointer for OS sleep function. - PWAITQ delta_msr_wait;// Pointer for OS sleep function. - PWAITQ dss_now_wait; // Pointer for OS sleep function. - - struct timer_list BookmarkTimer; // Used by i2DrainOutput - wait_queue_head_t pBookmarkWait; // Used by i2DrainOutput - - int BaudBase; - int BaudDivisor; - - USHORT ClosingDelay; - USHORT ClosingWaitTime; - - volatile - flowIn infl; // This structure is initialized as a completely - // formed flow-control command packet, and as such - // has the channel number, also the capacity and - // "as-of" data needed continuously. - - USHORT sinceLastFlow; // Counts the number of characters read from input - // buffers, since the last time flow control info - // was sent. - - USHORT whenSendFlow; // Determines when new flow control is to be sent to - // the board. Note unlike earlier manifestations of - // the driver, these packets can be sent from - // in-place. - - USHORT channelNeeds; // Bit map of important things which must be done - // for this channel. (See bits below ) - - volatile - flowStat outfl; // Same type of structure is used to hold current - // flow control information used to control our - // output. "asof" is kept updated as data is sent, - // and "room" never goes to zero. - - // The incoming ring buffer - // Unlike the outgoing buffers, this holds raw data, not packets. The two - // extra bytes are used to hold the byte-padding when there is room for an - // odd number of bytes before we must wrap. - // - UCHAR Ibuf[IBUF_SIZE + 2]; - volatile - USHORT Ibuf_stuff; // Stuffing index - volatile - USHORT Ibuf_strip; // Stripping index - - // The outgoing ring-buffer: Holds Data and command packets. N.B., even - // though these are in the channel structure, the channel is also written - // here, the easier to send it to the fifo when ready. HOWEVER, individual - // packets here are NOT padded to even length: the routines for writing - // blocks to the fifo will pad to even byte counts. - // - UCHAR Obuf[OBUF_SIZE+MAX_OBUF_BLOCK+4]; - volatile - USHORT Obuf_stuff; // Stuffing index - volatile - USHORT Obuf_strip; // Stripping index - int Obuf_char_count; - - // The outgoing bypass-command buffer. Unlike earlier manifestations, the - // flow control packets are sent directly from the structures. As above, the - // channel number is included in the packet, but they are NOT padded to even - // size. - // - UCHAR Cbuf[CBUF_SIZE+MAX_CBUF_BLOCK+2]; - volatile - USHORT Cbuf_stuff; // Stuffing index - volatile - USHORT Cbuf_strip; // Stripping index - - // The temporary buffer for the Linux tty driver PutChar entry. - // - UCHAR Pbuf[MAX_OBUF_BLOCK - sizeof (i2DataHeader)]; - volatile - USHORT Pbuf_stuff; // Stuffing index - - // The state of incoming data-set signals - // - USHORT dataSetIn; // Bit-mapped according to below. Also indicates - // whether a break has been detected since last - // inquiry. - - // The state of outcoming data-set signals (as far as we can tell!) - // - USHORT dataSetOut; // Bit-mapped according to below. - - // Most recent hot-key identifier detected - // - USHORT hotKeyIn; // Hot key as sent by the board, HOT_CLEAR indicates - // no hot key detected since last examined. - - // Counter of outstanding requests for bookmarks - // - short bookMarks; // Number of outstanding bookmark requests, (+ive - // whenever a bookmark request if queued up, -ive - // whenever a bookmark is received). - - // Misc options - // - USHORT channelOptions; // See below - - // To store various incoming special packets - // - debugStat channelStatus; - cntStat channelRcount; - cntStat channelTcount; - failStat channelFail; - - // To store the last values for line characteristics we sent to the board. - // - int speed; - - int flush_flags; - - void (*trace)(unsigned short,unsigned char,unsigned char,unsigned long,...); - - /* - * Kernel counters for the 4 input interrupts - */ - struct async_icount icount; - - /* - * Task queues for processing input packets from the board. - */ - struct work_struct tqueue_input; - struct work_struct tqueue_status; - struct work_struct tqueue_hangup; - - rwlock_t Ibuf_spinlock; - rwlock_t Obuf_spinlock; - rwlock_t Cbuf_spinlock; - rwlock_t Pbuf_spinlock; - -} i2ChanStr, *i2ChanStrPtr; - -//--------------------------------------------------- -// Manifests and bit-maps for elements in i2ChanStr -//--------------------------------------------------- -// -// flush flags -// -#define STARTFL_FLAG 1 -#define STOPFL_FLAG 2 - -// validity -// -#define CHANNEL_MAGIC_BITS 0xff00 -#define CHANNEL_MAGIC 0x5300 // (validity & CHANNEL_MAGIC_BITS) == - // CHANNEL_MAGIC --> structure good - -#define CHANNEL_SUPPORT 0x0001 // Indicates channel is supported, exists, - // and passed P.O.S.T. - -// channelNeeds -// -#define NEED_FLOW 1 // Indicates flow control has been queued -#define NEED_INLINE 2 // Indicates inline commands or data queued -#define NEED_BYPASS 4 // Indicates bypass commands queued -#define NEED_CREDIT 8 // Indicates would be sending except has not sufficient - // credit. The data is still in the channel structure, - // but the channel is not enqueued in the board - // structure again until there is a credit received from - // the board. - -// dataSetIn (Also the bits for i2GetStatus return value) -// -#define I2_DCD 1 -#define I2_CTS 2 -#define I2_DSR 4 -#define I2_RI 8 - -// dataSetOut (Also the bits for i2GetStatus return value) -// -#define I2_DTR 1 -#define I2_RTS 2 - -// i2GetStatus() can optionally clear these bits -// -#define I2_BRK 0x10 // A break was detected -#define I2_PAR 0x20 // A parity error was received -#define I2_FRA 0x40 // A framing error was received -#define I2_OVR 0x80 // An overrun error was received - -// i2GetStatus() automatically clears these bits */ -// -#define I2_DDCD 0x100 // DCD changed from its former value -#define I2_DCTS 0x200 // CTS changed from its former value -#define I2_DDSR 0x400 // DSR changed from its former value -#define I2_DRI 0x800 // RI changed from its former value - -// hotKeyIn -// -#define HOT_CLEAR 0x1322 // Indicates that no hot-key has been detected - -// channelOptions -// -#define CO_NBLOCK_WRITE 1 // Writes don't block waiting for buffer. (Default - // is, they do wait.) - -// fcmodes -// -#define I2_OUTFLOW_CTS 0x0001 -#define I2_INFLOW_RTS 0x0002 -#define I2_INFLOW_DSR 0x0004 -#define I2_INFLOW_DTR 0x0008 -#define I2_OUTFLOW_DSR 0x0010 -#define I2_OUTFLOW_DTR 0x0020 -#define I2_OUTFLOW_XON 0x0040 -#define I2_OUTFLOW_XANY 0x0080 -#define I2_INFLOW_XON 0x0100 - -#define I2_CRTSCTS (I2_OUTFLOW_CTS|I2_INFLOW_RTS) -#define I2_IXANY_MODE (I2_OUTFLOW_XON|I2_OUTFLOW_XANY) - -//------------------------------------------- -// Macros used from user level like functions -//------------------------------------------- - -// Macros to set and clear channel options -// -#define i2SetOption(pCh, option) pCh->channelOptions |= option -#define i2ClrOption(pCh, option) pCh->channelOptions &= ~option - -// Macro to set fatal-error trap -// -#define i2SetFatalTrap(pB, routine) pB->i2eFatalTrap = routine - -//-------------------------------------------- -// Declarations and prototypes for i2lib.c -//-------------------------------------------- -// -static int i2InitChannels(i2eBordStrPtr, int, i2ChanStrPtr); -static int i2QueueCommands(int, i2ChanStrPtr, int, int, cmdSyntaxPtr,...); -static int i2GetStatus(i2ChanStrPtr, int); -static int i2Input(i2ChanStrPtr); -static int i2InputFlush(i2ChanStrPtr); -static int i2Output(i2ChanStrPtr, const char *, int); -static int i2OutputFree(i2ChanStrPtr); -static int i2ServiceBoard(i2eBordStrPtr); -static void i2DrainOutput(i2ChanStrPtr, int); - -#ifdef IP2DEBUG_TRACE -void ip2trace(unsigned short,unsigned char,unsigned char,unsigned long,...); -#else -#define ip2trace(a,b,c,d...) do {} while (0) -#endif - -// Argument to i2QueueCommands -// -#define C_IN_LINE 1 -#define C_BYPASS 0 - -#endif // I2LIB_H diff --git a/drivers/char/ip2/i2pack.h b/drivers/char/ip2/i2pack.h deleted file mode 100644 index 00342a677c90..000000000000 --- a/drivers/char/ip2/i2pack.h +++ /dev/null @@ -1,364 +0,0 @@ -/******************************************************************************* -* -* (c) 1998 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Definitions of the packets used to transfer data and commands -* Host <--> Board. Information provided here is only applicable -* when the standard loadware is active. -* -*******************************************************************************/ -#ifndef I2PACK_H -#define I2PACK_H 1 - -//----------------------------------------------- -// Revision History: -// -// 10 October 1991 MAG First draft -// 24 February 1992 MAG Additions for 1.4.x loadware -// 11 March 1992 MAG New status packets -// -//----------------------------------------------- - -//------------------------------------------------------------------------------ -// Packet Formats: -// -// Information passes between the host and board through the FIFO in packets. -// These have headers which indicate the type of packet. Because the fifo data -// path may be 16-bits wide, the protocol is constrained such that each packet -// is always padded to an even byte count. (The lower-level interface routines -// -- i2ellis.c -- are designed to do this). -// -// The sender (be it host or board) must place some number of complete packets -// in the fifo, then place a message in the mailbox that packets are available. -// Placing such a message interrupts the "receiver" (be it board or host), who -// reads the mailbox message and determines that there are incoming packets -// ready. Since there are no partial packets, and the length of a packet is -// given in the header, the remainder of the packet can be read without checking -// for FIFO empty condition. The process is repeated, packet by packet, until -// the incoming FIFO is empty. Then the receiver uses the outbound mailbox to -// signal the board that it has read the data. Only then can the sender place -// additional data in the fifo. -//------------------------------------------------------------------------------ -// -//------------------------------------------------ -// Definition of Packet Header Area -//------------------------------------------------ -// -// Caution: these only define header areas. In actual use the data runs off -// beyond the end of these structures. -// -// Since these structures are based on sequences of bytes which go to the board, -// there cannot be ANY padding between the elements. -#pragma pack(1) - -//---------------------------- -// DATA PACKETS -//---------------------------- - -typedef struct _i2DataHeader -{ - unsigned char i2sChannel; /* The channel number: 0-255 */ - - // -- Bitfields are allocated LSB first -- - - // For incoming data, indicates whether this is an ordinary packet or a - // special one (e.g., hot key hit). - unsigned i2sId : 2 __attribute__ ((__packed__)); - - // For tagging data packets. There are flush commands which flush only data - // packets bearing a particular tag. (used in implementing IntelliView and - // IntelliPrint). THE TAG VALUE 0xf is RESERVED and must not be used (it has - // meaning internally to the loadware). - unsigned i2sTag : 4; - - // These two bits determine the type of packet sent/received. - unsigned i2sType : 2; - - // The count of data to follow: does not include the possible additional - // padding byte. MAXIMUM COUNT: 4094. The top four bits must be 0. - unsigned short i2sCount; - -} i2DataHeader, *i2DataHeaderPtr; - -// Structure is immediately followed by the data, proper. - -//---------------------------- -// NON-DATA PACKETS -//---------------------------- - -typedef struct _i2CmdHeader -{ - unsigned char i2sChannel; // The channel number: 0-255 (Except where noted - // - see below - - // Number of bytes of commands, status or whatever to follow - unsigned i2sCount : 6; - - // These two bits determine the type of packet sent/received. - unsigned i2sType : 2; - -} i2CmdHeader, *i2CmdHeaderPtr; - -// Structure is immediately followed by the applicable data. - -//--------------------------------------- -// Flow Control Packets (Outbound) -//--------------------------------------- - -// One type of outbound command packet is so important that the entire structure -// is explicitly defined here. That is the flow-control packet. This is never -// sent by user-level code (as would be the commands to raise/lower DTR, for -// example). These are only sent by the library routines in response to reading -// incoming data into the buffers. -// -// The parameters inside the command block are maintained in place, then the -// block is sent at the appropriate time. - -typedef struct _flowIn -{ - i2CmdHeader hd; // Channel #, count, type (see above) - unsigned char fcmd; // The flow control command (37) - unsigned short asof; // As of byte number "asof" (LSB first!) I have room - // for "room" bytes - unsigned short room; -} flowIn, *flowInPtr; - -//---------------------------------------- -// (Incoming) Status Packets -//---------------------------------------- - -// Incoming packets which are non-data packets are status packets. In this case, -// the channel number in the header is unimportant. What follows are one or more -// sub-packets, the first word of which consists of the channel (first or low -// byte) and the status indicator (second or high byte), followed by possibly -// more data. - -#define STAT_CTS_UP 0 /* CTS raised (no other bytes) */ -#define STAT_CTS_DN 1 /* CTS dropped (no other bytes) */ -#define STAT_DCD_UP 2 /* DCD raised (no other bytes) */ -#define STAT_DCD_DN 3 /* DCD dropped (no other bytes) */ -#define STAT_DSR_UP 4 /* DSR raised (no other bytes) */ -#define STAT_DSR_DN 5 /* DSR dropped (no other bytes) */ -#define STAT_RI_UP 6 /* RI raised (no other bytes) */ -#define STAT_RI_DN 7 /* RI dropped (no other bytes) */ -#define STAT_BRK_DET 8 /* BRK detect (no other bytes) */ -#define STAT_FLOW 9 /* Flow control(-- more: see below */ -#define STAT_BMARK 10 /* Bookmark (no other bytes) - * Bookmark is sent as a response to - * a command 60: request for bookmark - */ -#define STAT_STATUS 11 /* Special packet: see below */ -#define STAT_TXCNT 12 /* Special packet: see below */ -#define STAT_RXCNT 13 /* Special packet: see below */ -#define STAT_BOXIDS 14 /* Special packet: see below */ -#define STAT_HWFAIL 15 /* Special packet: see below */ - -#define STAT_MOD_ERROR 0xc0 -#define STAT_MODEM 0xc0/* If status & STAT_MOD_ERROR: - * == STAT_MODEM, then this is a modem - * status packet, given in response to a - * CMD_DSS_NOW command. - * The low nibble has each data signal: - */ -#define STAT_MOD_DCD 0x8 -#define STAT_MOD_RI 0x4 -#define STAT_MOD_DSR 0x2 -#define STAT_MOD_CTS 0x1 - -#define STAT_ERROR 0x80/* If status & STAT_MOD_ERROR - * == STAT_ERROR, then - * sort of error on the channel. - * The remaining seven bits indicate - * what sort of error it is. - */ -/* The low three bits indicate parity, framing, or overrun errors */ - -#define STAT_E_PARITY 4 /* Parity error */ -#define STAT_E_FRAMING 2 /* Framing error */ -#define STAT_E_OVERRUN 1 /* (uxart) overrun error */ - -//--------------------------------------- -// STAT_FLOW packets -//--------------------------------------- - -typedef struct _flowStat -{ - unsigned short asof; - unsigned short room; -}flowStat, *flowStatPtr; - -// flowStat packets are received from the board to regulate the flow of outgoing -// data. A local copy of this structure is also kept to track the amount of -// credits used and credits remaining. "room" is the amount of space in the -// board's buffers, "as of" having received a certain byte number. When sending -// data to the fifo, you must calculate how much buffer space your packet will -// use. Add this to the current "asof" and subtract it from the current "room". -// -// The calculation for the board's buffer is given by CREDIT_USAGE, where size -// is the un-rounded count of either data characters or command characters. -// (Which is to say, the count rounded up, plus two). - -#define CREDIT_USAGE(size) (((size) + 3) & ~1) - -//--------------------------------------- -// STAT_STATUS packets -//--------------------------------------- - -typedef struct _debugStat -{ - unsigned char d_ccsr; - unsigned char d_txinh; - unsigned char d_stat1; - unsigned char d_stat2; -} debugStat, *debugStatPtr; - -// debugStat packets are sent to the host in response to a CMD_GET_STATUS -// command. Each byte is bit-mapped as described below: - -#define D_CCSR_XON 2 /* Has received XON, ready to transmit */ -#define D_CCSR_XOFF 4 /* Has received XOFF, not transmitting */ -#define D_CCSR_TXENAB 8 /* Transmitter is enabled */ -#define D_CCSR_RXENAB 0x80 /* Receiver is enabled */ - -#define D_TXINH_BREAK 1 /* We are sending a break */ -#define D_TXINH_EMPTY 2 /* No data to send */ -#define D_TXINH_SUSP 4 /* Output suspended via command 57 */ -#define D_TXINH_CMD 8 /* We are processing an in-line command */ -#define D_TXINH_LCD 0x10 /* LCD diagnostics are running */ -#define D_TXINH_PAUSE 0x20 /* We are processing a PAUSE command */ -#define D_TXINH_DCD 0x40 /* DCD is low, preventing transmission */ -#define D_TXINH_DSR 0x80 /* DSR is low, preventing transmission */ - -#define D_STAT1_TXEN 1 /* Transmit INTERRUPTS enabled */ -#define D_STAT1_RXEN 2 /* Receiver INTERRUPTS enabled */ -#define D_STAT1_MDEN 4 /* Modem (data set sigs) interrupts enabled */ -#define D_STAT1_RLM 8 /* Remote loopback mode selected */ -#define D_STAT1_LLM 0x10 /* Local internal loopback mode selected */ -#define D_STAT1_CTS 0x20 /* CTS is low, preventing transmission */ -#define D_STAT1_DTR 0x40 /* DTR is low, to stop remote transmission */ -#define D_STAT1_RTS 0x80 /* RTS is low, to stop remote transmission */ - -#define D_STAT2_TXMT 1 /* Transmit buffers are all empty */ -#define D_STAT2_RXMT 2 /* Receive buffers are all empty */ -#define D_STAT2_RXINH 4 /* Loadware has tried to inhibit remote - * transmission: dropped DTR, sent XOFF, - * whatever... - */ -#define D_STAT2_RXFLO 8 /* Loadware can send no more data to host - * until it receives a flow-control packet - */ -//----------------------------------------- -// STAT_TXCNT and STAT_RXCNT packets -//---------------------------------------- - -typedef struct _cntStat -{ - unsigned short cs_time; // (Assumes host is little-endian!) - unsigned short cs_count; -} cntStat, *cntStatPtr; - -// These packets are sent in response to a CMD_GET_RXCNT or a CMD_GET_TXCNT -// bypass command. cs_time is a running 1 Millisecond counter which acts as a -// time stamp. cs_count is a running counter of data sent or received from the -// uxarts. (Not including data added by the chip itself, as with CRLF -// processing). -//------------------------------------------ -// STAT_HWFAIL packets -//------------------------------------------ - -typedef struct _failStat -{ - unsigned char fs_written; - unsigned char fs_read; - unsigned short fs_address; -} failStat, *failStatPtr; - -// This packet is sent whenever the on-board diagnostic process detects an -// error. At startup, this process is dormant. The host can wake it up by -// issuing the bypass command CMD_HW_TEST. The process runs at low priority and -// performs continuous hardware verification; writing data to certain on-board -// registers, reading it back, and comparing. If it detects an error, this -// packet is sent to the host, and the process goes dormant again until the host -// sends another CMD_HW_TEST. It then continues with the next register to be -// tested. - -//------------------------------------------------------------------------------ -// Macros to deal with the headers more easily! Note that these are defined so -// they may be used as "left" as well as "right" expressions. -//------------------------------------------------------------------------------ - -// Given a pointer to the packet, reference the channel number -// -#define CHANNEL_OF(pP) ((i2DataHeaderPtr)(pP))->i2sChannel - -// Given a pointer to the packet, reference the Packet type -// -#define PTYPE_OF(pP) ((i2DataHeaderPtr)(pP))->i2sType - -// The possible types of packets -// -#define PTYPE_DATA 0 /* Host <--> Board */ -#define PTYPE_BYPASS 1 /* Host ---> Board */ -#define PTYPE_INLINE 2 /* Host ---> Board */ -#define PTYPE_STATUS 2 /* Host <--- Board */ - -// Given a pointer to a Data packet, reference the Tag -// -#define TAG_OF(pP) ((i2DataHeaderPtr)(pP))->i2sTag - -// Given a pointer to a Data packet, reference the data i.d. -// -#define ID_OF(pP) ((i2DataHeaderPtr)(pP))->i2sId - -// The possible types of ID's -// -#define ID_ORDINARY_DATA 0 -#define ID_HOT_KEY 1 - -// Given a pointer to a Data packet, reference the count -// -#define DATA_COUNT_OF(pP) ((i2DataHeaderPtr)(pP))->i2sCount - -// Given a pointer to a Data packet, reference the beginning of data -// -#define DATA_OF(pP) &((unsigned char *)(pP))[4] // 4 = size of header - -// Given a pointer to a Non-Data packet, reference the count -// -#define CMD_COUNT_OF(pP) ((i2CmdHeaderPtr)(pP))->i2sCount - -#define MAX_CMD_PACK_SIZE 62 // Maximum size of such a count - -// Given a pointer to a Non-Data packet, reference the beginning of data -// -#define CMD_OF(pP) &((unsigned char *)(pP))[2] // 2 = size of header - -//-------------------------------- -// MailBox Bits: -//-------------------------------- - -//-------------------------- -// Outgoing (host to board) -//-------------------------- -// -#define MB_OUT_STUFFED 0x80 // Host has placed output in fifo -#define MB_IN_STRIPPED 0x40 // Host has read in all input from fifo - -//-------------------------- -// Incoming (board to host) -//-------------------------- -// -#define MB_IN_STUFFED 0x80 // Board has placed input in fifo -#define MB_OUT_STRIPPED 0x40 // Board has read all output from fifo -#define MB_FATAL_ERROR 0x20 // Board has encountered a fatal error - -#pragma pack() // Reset padding to command-line default - -#endif // I2PACK_H - diff --git a/drivers/char/ip2/ip2.h b/drivers/char/ip2/ip2.h deleted file mode 100644 index 936ccc533949..000000000000 --- a/drivers/char/ip2/ip2.h +++ /dev/null @@ -1,107 +0,0 @@ -/******************************************************************************* -* -* (c) 1998 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Driver constants for configuration and tuning -* -* NOTES: -* -*******************************************************************************/ -#ifndef IP2_H -#define IP2_H - -#include "ip2types.h" -#include "i2cmd.h" - -/*************/ -/* Constants */ -/*************/ - -/* Device major numbers - since version 2.0.26. */ -#define IP2_TTY_MAJOR 71 -#define IP2_CALLOUT_MAJOR 72 -#define IP2_IPL_MAJOR 73 - -/* Board configuration array. - * This array defines the hardware irq and address for up to IP2_MAX_BOARDS - * (4 supported per ip2_types.h) ISA board addresses and irqs MUST be specified, - * PCI and EISA boards are probed for and automagicly configed - * iff the addresses are set to 1 and 2 respectivily. - * 0x0100 - 0x03f0 == ISA - * 1 == PCI - * 2 == EISA - * 0 == (skip this board) - * This array defines the hardware addresses for them. Special - * addresses are EISA and PCI which go sniffing for boards. - - * In a multiboard system the position in the array determines which port - * devices are assigned to each board: - * board 0 is assigned ttyF0.. to ttyF63, - * board 1 is assigned ttyF64 to ttyF127, - * board 2 is assigned ttyF128 to ttyF191, - * board 3 is assigned ttyF192 to ttyF255. - * - * In PCI and EISA bus systems each range is mapped to card in - * monotonically increasing slot number order, ISA position is as specified - * here. - - * If the irqs are ALL set to 0,0,0,0 all boards operate in - * polled mode. For interrupt operation ISA boards require that the IRQ be - * specified, while PCI and EISA boards any nonzero entry - * will enable interrupts using the BIOS configured irq for the board. - * An invalid irq entry will default to polled mode for that card and print - * console warning. - - * When the driver is loaded as a module these setting can be overridden on the - * modprobe command line or on an option line in /etc/modprobe.conf. - * If the driver is built-in the configuration must be - * set here for ISA cards and address set to 1 and 2 for PCI and EISA. - * - * Here is an example that shows most if not all possibe combinations: - - *static ip2config_t ip2config = - *{ - * {11,1,0,0}, // irqs - * { // Addresses - * 0x0308, // Board 0, ttyF0 - ttyF63// ISA card at io=0x308, irq=11 - * 0x0001, // Board 1, ttyF64 - ttyF127//PCI card configured by BIOS - * 0x0000, // Board 2, ttyF128 - ttyF191// Slot skipped - * 0x0002 // Board 3, ttyF192 - ttyF255//EISA card configured by BIOS - * // but polled not irq driven - * } - *}; - */ - - /* this structure is zeroed out because the suggested method is to configure - * the driver as a module, set up the parameters with an options line in - * /etc/modprobe.conf and load with modprobe or kmod, the kernel - * module loader - */ - - /* This structure is NOW always initialized when the driver is initialized. - * Compiled in defaults MUST be added to the io and irq arrays in - * ip2.c. Those values are configurable from insmod parameters in the - * case of modules or from command line parameters (ip2=io,irq) when - * compiled in. - */ - -static ip2config_t ip2config = -{ - {0,0,0,0}, // irqs - { // Addresses - /* Do NOT set compile time defaults HERE! Use the arrays in - ip2.c! These WILL be overwritten! =mhw= */ - 0x0000, // Board 0, ttyF0 - ttyF63 - 0x0000, // Board 1, ttyF64 - ttyF127 - 0x0000, // Board 2, ttyF128 - ttyF191 - 0x0000 // Board 3, ttyF192 - ttyF255 - } -}; - -#endif diff --git a/drivers/char/ip2/ip2ioctl.h b/drivers/char/ip2/ip2ioctl.h deleted file mode 100644 index aa0a9da85e05..000000000000 --- a/drivers/char/ip2/ip2ioctl.h +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* -* -* (c) 1998 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Driver constants for configuration and tuning -* -* NOTES: -* -*******************************************************************************/ - -#ifndef IP2IOCTL_H -#define IP2IOCTL_H - -//************* -//* Constants * -//************* - -// High baud rates (if not defined elsewhere. -#ifndef B153600 -# define B153600 0010005 -#endif -#ifndef B307200 -# define B307200 0010006 -#endif -#ifndef B921600 -# define B921600 0010007 -#endif - -#endif diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c deleted file mode 100644 index ea7a8fb08283..000000000000 --- a/drivers/char/ip2/ip2main.c +++ /dev/null @@ -1,3234 +0,0 @@ -/* -* -* (c) 1999 by Computone Corporation -* -******************************************************************************** -* -* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Mainline code for the device driver -* -*******************************************************************************/ -// ToDo: -// -// Fix the immediate DSS_NOW problem. -// Work over the channel stats return logic in ip2_ipl_ioctl so they -// make sense for all 256 possible channels and so the user space -// utilities will compile and work properly. -// -// Done: -// -// 1.2.14 /\/\|=mhw=|\/\/ -// Added bounds checking to ip2_ipl_ioctl to avoid potential terroristic acts. -// Changed the definition of ip2trace to be more consistent with kernel style -// Thanks to Andreas Dilger for these updates -// -// 1.2.13 /\/\|=mhw=|\/\/ -// DEVFS: Renamed ttf/{n} to tts/F{n} and cuf/{n} to cua/F{n} to conform -// to agreed devfs serial device naming convention. -// -// 1.2.12 /\/\|=mhw=|\/\/ -// Cleaned up some remove queue cut and paste errors -// -// 1.2.11 /\/\|=mhw=|\/\/ -// Clean up potential NULL pointer dereferences -// Clean up devfs registration -// Add kernel command line parsing for io and irq -// Compile defaults for io and irq are now set in ip2.c not ip2.h! -// Reworked poll_only hack for explicit parameter setting -// You must now EXPLICITLY set poll_only = 1 or set all irqs to 0 -// Merged ip2_loadmain and old_ip2_init -// Converted all instances of interruptible_sleep_on into queue calls -// Most of these had no race conditions but better to clean up now -// -// 1.2.10 /\/\|=mhw=|\/\/ -// Fixed the bottom half interrupt handler and enabled USE_IQI -// to split the interrupt handler into a formal top-half / bottom-half -// Fixed timing window on high speed processors that queued messages to -// the outbound mail fifo faster than the board could handle. -// -// 1.2.9 -// Four box EX was barfing on >128k kmalloc, made structure smaller by -// reducing output buffer size -// -// 1.2.8 -// Device file system support (MHW) -// -// 1.2.7 -// Fixed -// Reload of ip2 without unloading ip2main hangs system on cat of /proc/modules -// -// 1.2.6 -//Fixes DCD problems -// DCD was not reported when CLOCAL was set on call to TIOCMGET -// -//Enhancements: -// TIOCMGET requests and waits for status return -// No DSS interrupts enabled except for DCD when needed -// -// For internal use only -// -//#define IP2DEBUG_INIT -//#define IP2DEBUG_OPEN -//#define IP2DEBUG_WRITE -//#define IP2DEBUG_READ -//#define IP2DEBUG_IOCTL -//#define IP2DEBUG_IPL - -//#define IP2DEBUG_TRACE -//#define DEBUG_FIFO - -/************/ -/* Includes */ -/************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include - -#include "ip2types.h" -#include "ip2trace.h" -#include "ip2ioctl.h" -#include "ip2.h" -#include "i2ellis.h" -#include "i2lib.h" - -/***************** - * /proc/ip2mem * - *****************/ - -#include -#include - -static DEFINE_MUTEX(ip2_mutex); -static const struct file_operations ip2mem_proc_fops; -static const struct file_operations ip2_proc_fops; - -/********************/ -/* Type Definitions */ -/********************/ - -/*************/ -/* Constants */ -/*************/ - -/* String constants to identify ourselves */ -static const char pcName[] = "Computone IntelliPort Plus multiport driver"; -static const char pcVersion[] = "1.2.14"; - -/* String constants for port names */ -static const char pcDriver_name[] = "ip2"; -static const char pcIpl[] = "ip2ipl"; - -/***********************/ -/* Function Prototypes */ -/***********************/ - -/* Global module entry functions */ - -/* Private (static) functions */ -static int ip2_open(PTTY, struct file *); -static void ip2_close(PTTY, struct file *); -static int ip2_write(PTTY, const unsigned char *, int); -static int ip2_putchar(PTTY, unsigned char); -static void ip2_flush_chars(PTTY); -static int ip2_write_room(PTTY); -static int ip2_chars_in_buf(PTTY); -static void ip2_flush_buffer(PTTY); -static int ip2_ioctl(PTTY, UINT, ULONG); -static void ip2_set_termios(PTTY, struct ktermios *); -static void ip2_set_line_discipline(PTTY); -static void ip2_throttle(PTTY); -static void ip2_unthrottle(PTTY); -static void ip2_stop(PTTY); -static void ip2_start(PTTY); -static void ip2_hangup(PTTY); -static int ip2_tiocmget(struct tty_struct *tty); -static int ip2_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); -static int ip2_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount); - -static void set_irq(int, int); -static void ip2_interrupt_bh(struct work_struct *work); -static irqreturn_t ip2_interrupt(int irq, void *dev_id); -static void ip2_poll(unsigned long arg); -static inline void service_all_boards(void); -static void do_input(struct work_struct *); -static void do_status(struct work_struct *); - -static void ip2_wait_until_sent(PTTY,int); - -static void set_params (i2ChanStrPtr, struct ktermios *); -static int get_serial_info(i2ChanStrPtr, struct serial_struct __user *); -static int set_serial_info(i2ChanStrPtr, struct serial_struct __user *); - -static ssize_t ip2_ipl_read(struct file *, char __user *, size_t, loff_t *); -static ssize_t ip2_ipl_write(struct file *, const char __user *, size_t, loff_t *); -static long ip2_ipl_ioctl(struct file *, UINT, ULONG); -static int ip2_ipl_open(struct inode *, struct file *); - -static int DumpTraceBuffer(char __user *, int); -static int DumpFifoBuffer( char __user *, int); - -static void ip2_init_board(int, const struct firmware *); -static unsigned short find_eisa_board(int); -static int ip2_setup(char *str); - -/***************/ -/* Static Data */ -/***************/ - -static struct tty_driver *ip2_tty_driver; - -/* Here, then is a table of board pointers which the interrupt routine should - * scan through to determine who it must service. - */ -static unsigned short i2nBoards; // Number of boards here - -static i2eBordStrPtr i2BoardPtrTable[IP2_MAX_BOARDS]; - -static i2ChanStrPtr DevTable[IP2_MAX_PORTS]; -//DevTableMem just used to save addresses for kfree -static void *DevTableMem[IP2_MAX_BOARDS]; - -/* This is the driver descriptor for the ip2ipl device, which is used to - * download the loadware to the boards. - */ -static const struct file_operations ip2_ipl = { - .owner = THIS_MODULE, - .read = ip2_ipl_read, - .write = ip2_ipl_write, - .unlocked_ioctl = ip2_ipl_ioctl, - .open = ip2_ipl_open, - .llseek = noop_llseek, -}; - -static unsigned long irq_counter; -static unsigned long bh_counter; - -// Use immediate queue to service interrupts -#define USE_IQI -//#define USE_IQ // PCI&2.2 needs work - -/* The timer_list entry for our poll routine. If interrupt operation is not - * selected, the board is serviced periodically to see if anything needs doing. - */ -#define POLL_TIMEOUT (jiffies + 1) -static DEFINE_TIMER(PollTimer, ip2_poll, 0, 0); - -#ifdef IP2DEBUG_TRACE -/* Trace (debug) buffer data */ -#define TRACEMAX 1000 -static unsigned long tracebuf[TRACEMAX]; -static int tracestuff; -static int tracestrip; -static int tracewrap; -#endif - -/**********/ -/* Macros */ -/**********/ - -#ifdef IP2DEBUG_OPEN -#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] ttyc=%d, modc=%x -> %s\n", \ - tty->name,(pCh->flags), \ - tty->count,/*GET_USE_COUNT(module)*/0,s) -#else -#define DBG_CNT(s) -#endif - -/********/ -/* Code */ -/********/ - -#include "i2ellis.c" /* Extremely low-level interface services */ -#include "i2cmd.c" /* Standard loadware command definitions */ -#include "i2lib.c" /* High level interface services */ - -/* Configuration area for modprobe */ - -MODULE_AUTHOR("Doug McNash"); -MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); -MODULE_LICENSE("GPL"); - -#define MAX_CMD_STR 50 - -static int poll_only; -static char cmd[MAX_CMD_STR]; - -static int Eisa_irq; -static int Eisa_slot; - -static int iindx; -static char rirqs[IP2_MAX_BOARDS]; -static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0}; - -/* Note: Add compiled in defaults to these arrays, not to the structure - in ip2.h any longer. That structure WILL get overridden - by these values, or command line values, or insmod values!!! =mhw= -*/ -static int io[IP2_MAX_BOARDS]; -static int irq[IP2_MAX_BOARDS] = { -1, -1, -1, -1 }; - -MODULE_AUTHOR("Doug McNash"); -MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); -module_param_array(irq, int, NULL, 0); -MODULE_PARM_DESC(irq, "Interrupts for IntelliPort Cards"); -module_param_array(io, int, NULL, 0); -MODULE_PARM_DESC(io, "I/O ports for IntelliPort Cards"); -module_param(poll_only, bool, 0); -MODULE_PARM_DESC(poll_only, "Do not use card interrupts"); -module_param_string(ip2, cmd, MAX_CMD_STR, 0); -MODULE_PARM_DESC(ip2, "Contains module parameter passed with 'ip2='"); - -/* for sysfs class support */ -static struct class *ip2_class; - -/* Some functions to keep track of what irqs we have */ - -static int __init is_valid_irq(int irq) -{ - int *i = Valid_Irqs; - - while (*i != 0 && *i != irq) - i++; - - return *i; -} - -static void __init mark_requested_irq(char irq) -{ - rirqs[iindx++] = irq; -} - -static int __exit clear_requested_irq(char irq) -{ - int i; - for (i = 0; i < IP2_MAX_BOARDS; ++i) { - if (rirqs[i] == irq) { - rirqs[i] = 0; - return 1; - } - } - return 0; -} - -static int have_requested_irq(char irq) -{ - /* array init to zeros so 0 irq will not be requested as a side - * effect */ - int i; - for (i = 0; i < IP2_MAX_BOARDS; ++i) - if (rirqs[i] == irq) - return 1; - return 0; -} - -/******************************************************************************/ -/* Function: cleanup_module() */ -/* Parameters: None */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* This is a required entry point for an installable module. It has to return */ -/* the device and the driver to a passive state. It should not be necessary */ -/* to reset the board fully, especially as the loadware is downloaded */ -/* externally rather than in the driver. We just want to disable the board */ -/* and clear the loadware to a reset state. To allow this there has to be a */ -/* way to detect whether the board has the loadware running at init time to */ -/* handle subsequent installations of the driver. All memory allocated by the */ -/* driver should be returned since it may be unloaded from memory. */ -/******************************************************************************/ -static void __exit ip2_cleanup_module(void) -{ - int err; - int i; - - del_timer_sync(&PollTimer); - - /* Reset the boards we have. */ - for (i = 0; i < IP2_MAX_BOARDS; i++) - if (i2BoardPtrTable[i]) - iiReset(i2BoardPtrTable[i]); - - /* The following is done at most once, if any boards were installed. */ - for (i = 0; i < IP2_MAX_BOARDS; i++) { - if (i2BoardPtrTable[i]) { - iiResetDelay(i2BoardPtrTable[i]); - /* free io addresses and Tibet */ - release_region(ip2config.addr[i], 8); - device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i)); - device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, - 4 * i + 1)); - } - /* Disable and remove interrupt handler. */ - if (ip2config.irq[i] > 0 && - have_requested_irq(ip2config.irq[i])) { - free_irq(ip2config.irq[i], (void *)&pcName); - clear_requested_irq(ip2config.irq[i]); - } - } - class_destroy(ip2_class); - err = tty_unregister_driver(ip2_tty_driver); - if (err) - printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", - err); - put_tty_driver(ip2_tty_driver); - unregister_chrdev(IP2_IPL_MAJOR, pcIpl); - remove_proc_entry("ip2mem", NULL); - - /* free memory */ - for (i = 0; i < IP2_MAX_BOARDS; i++) { - void *pB; -#ifdef CONFIG_PCI - if (ip2config.type[i] == PCI && ip2config.pci_dev[i]) { - pci_disable_device(ip2config.pci_dev[i]); - pci_dev_put(ip2config.pci_dev[i]); - ip2config.pci_dev[i] = NULL; - } -#endif - pB = i2BoardPtrTable[i]; - if (pB != NULL) { - kfree(pB); - i2BoardPtrTable[i] = NULL; - } - if (DevTableMem[i] != NULL) { - kfree(DevTableMem[i]); - DevTableMem[i] = NULL; - } - } -} -module_exit(ip2_cleanup_module); - -static const struct tty_operations ip2_ops = { - .open = ip2_open, - .close = ip2_close, - .write = ip2_write, - .put_char = ip2_putchar, - .flush_chars = ip2_flush_chars, - .write_room = ip2_write_room, - .chars_in_buffer = ip2_chars_in_buf, - .flush_buffer = ip2_flush_buffer, - .ioctl = ip2_ioctl, - .throttle = ip2_throttle, - .unthrottle = ip2_unthrottle, - .set_termios = ip2_set_termios, - .set_ldisc = ip2_set_line_discipline, - .stop = ip2_stop, - .start = ip2_start, - .hangup = ip2_hangup, - .tiocmget = ip2_tiocmget, - .tiocmset = ip2_tiocmset, - .get_icount = ip2_get_icount, - .proc_fops = &ip2_proc_fops, -}; - -/******************************************************************************/ -/* Function: ip2_loadmain() */ -/* Parameters: irq, io from command line of insmod et. al. */ -/* pointer to fip firmware and firmware size for boards */ -/* Returns: Success (0) */ -/* */ -/* Description: */ -/* This was the required entry point for all drivers (now in ip2.c) */ -/* It performs all */ -/* initialisation of the devices and driver structures, and registers itself */ -/* with the relevant kernel modules. */ -/******************************************************************************/ -/* IRQF_DISABLED - if set blocks all interrupts else only this line */ -/* IRQF_SHARED - for shared irq PCI or maybe EISA only */ -/* SA_RANDOM - can be source for cert. random number generators */ -#define IP2_SA_FLAGS 0 - - -static const struct firmware *ip2_request_firmware(void) -{ - struct platform_device *pdev; - const struct firmware *fw; - - pdev = platform_device_register_simple("ip2", 0, NULL, 0); - if (IS_ERR(pdev)) { - printk(KERN_ERR "Failed to register platform device for ip2\n"); - return NULL; - } - if (request_firmware(&fw, "intelliport2.bin", &pdev->dev)) { - printk(KERN_ERR "Failed to load firmware 'intelliport2.bin'\n"); - fw = NULL; - } - platform_device_unregister(pdev); - return fw; -} - -/****************************************************************************** - * ip2_setup: - * str: kernel command line string - * - * Can't autoprobe the boards so user must specify configuration on - * kernel command line. Sane people build it modular but the others - * come here. - * - * Alternating pairs of io,irq for up to 4 boards. - * ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3 - * - * io=0 => No board - * io=1 => PCI - * io=2 => EISA - * else => ISA I/O address - * - * irq=0 or invalid for ISA will revert to polling mode - * - * Any value = -1, do not overwrite compiled in value. - * - ******************************************************************************/ -static int __init ip2_setup(char *str) -{ - int j, ints[10]; /* 4 boards, 2 parameters + 2 */ - unsigned int i; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - for (i = 0, j = 1; i < 4; i++) { - if (j > ints[0]) - break; - if (ints[j] >= 0) - io[i] = ints[j]; - j++; - if (j > ints[0]) - break; - if (ints[j] >= 0) - irq[i] = ints[j]; - j++; - } - return 1; -} -__setup("ip2=", ip2_setup); - -static int __init ip2_loadmain(void) -{ - int i, j, box; - int err = 0; - i2eBordStrPtr pB = NULL; - int rc = -1; - const struct firmware *fw = NULL; - char *str; - - str = cmd; - - if (poll_only) { - /* Hard lock the interrupts to zero */ - irq[0] = irq[1] = irq[2] = irq[3] = poll_only = 0; - } - - /* Check module parameter with 'ip2=' has been passed or not */ - if (!poll_only && (!strncmp(str, "ip2=", 4))) - ip2_setup(str); - - ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0); - - /* process command line arguments to modprobe or - insmod i.e. iop & irqp */ - /* irqp and iop should ALWAYS be specified now... But we check - them individually just to be sure, anyways... */ - for (i = 0; i < IP2_MAX_BOARDS; ++i) { - ip2config.addr[i] = io[i]; - if (irq[i] >= 0) - ip2config.irq[i] = irq[i]; - else - ip2config.irq[i] = 0; - /* This is a little bit of a hack. If poll_only=1 on command - line back in ip2.c OR all IRQs on all specified boards are - explicitly set to 0, then drop to poll only mode and override - PCI or EISA interrupts. This superceeds the old hack of - triggering if all interrupts were zero (like da default). - Still a hack but less prone to random acts of terrorism. - - What we really should do, now that the IRQ default is set - to -1, is to use 0 as a hard coded, do not probe. - - /\/\|=mhw=|\/\/ - */ - poll_only |= irq[i]; - } - poll_only = !poll_only; - - /* Announce our presence */ - printk(KERN_INFO "%s version %s\n", pcName, pcVersion); - - ip2_tty_driver = alloc_tty_driver(IP2_MAX_PORTS); - if (!ip2_tty_driver) - return -ENOMEM; - - /* Initialise all the boards we can find (up to the maximum). */ - for (i = 0; i < IP2_MAX_BOARDS; ++i) { - switch (ip2config.addr[i]) { - case 0: /* skip this slot even if card is present */ - break; - default: /* ISA */ - /* ISA address must be specified */ - if (ip2config.addr[i] < 0x100 || - ip2config.addr[i] > 0x3f8) { - printk(KERN_ERR "IP2: Bad ISA board %d " - "address %x\n", i, - ip2config.addr[i]); - ip2config.addr[i] = 0; - break; - } - ip2config.type[i] = ISA; - - /* Check for valid irq argument, set for polling if - * invalid */ - if (ip2config.irq[i] && - !is_valid_irq(ip2config.irq[i])) { - printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n", - ip2config.irq[i]); - /* 0 is polling and is valid in that sense */ - ip2config.irq[i] = 0; - } - break; - case PCI: -#ifdef CONFIG_PCI - { - struct pci_dev *pdev = NULL; - u32 addr; - int status; - - pdev = pci_get_device(PCI_VENDOR_ID_COMPUTONE, - PCI_DEVICE_ID_COMPUTONE_IP2EX, pdev); - if (pdev == NULL) { - ip2config.addr[i] = 0; - printk(KERN_ERR "IP2: PCI board %d not " - "found\n", i); - break; - } - - if (pci_enable_device(pdev)) { - dev_err(&pdev->dev, "can't enable device\n"); - goto out; - } - ip2config.type[i] = PCI; - ip2config.pci_dev[i] = pci_dev_get(pdev); - status = pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1, - &addr); - if (addr & 1) - ip2config.addr[i] = (USHORT)(addr & 0xfffe); - else - dev_err(&pdev->dev, "I/O address error\n"); - - ip2config.irq[i] = pdev->irq; -out: - pci_dev_put(pdev); - } -#else - printk(KERN_ERR "IP2: PCI card specified but PCI " - "support not enabled.\n"); - printk(KERN_ERR "IP2: Recompile kernel with CONFIG_PCI " - "defined!\n"); -#endif /* CONFIG_PCI */ - break; - case EISA: - ip2config.addr[i] = find_eisa_board(Eisa_slot + 1); - if (ip2config.addr[i] != 0) { - /* Eisa_irq set as side effect, boo */ - ip2config.type[i] = EISA; - } - ip2config.irq[i] = Eisa_irq; - break; - } /* switch */ - } /* for */ - - for (i = 0; i < IP2_MAX_BOARDS; ++i) { - if (ip2config.addr[i]) { - pB = kzalloc(sizeof(i2eBordStr), GFP_KERNEL); - if (pB) { - i2BoardPtrTable[i] = pB; - iiSetAddress(pB, ip2config.addr[i], - ii2DelayTimer); - iiReset(pB); - } else - printk(KERN_ERR "IP2: board memory allocation " - "error\n"); - } - } - for (i = 0; i < IP2_MAX_BOARDS; ++i) { - pB = i2BoardPtrTable[i]; - if (pB != NULL) { - iiResetDelay(pB); - break; - } - } - for (i = 0; i < IP2_MAX_BOARDS; ++i) { - /* We don't want to request the firmware unless we have at - least one board */ - if (i2BoardPtrTable[i] != NULL) { - if (!fw) - fw = ip2_request_firmware(); - if (!fw) - break; - ip2_init_board(i, fw); - } - } - if (fw) - release_firmware(fw); - - ip2trace(ITRC_NO_PORT, ITRC_INIT, 2, 0); - - ip2_tty_driver->owner = THIS_MODULE; - ip2_tty_driver->name = "ttyF"; - ip2_tty_driver->driver_name = pcDriver_name; - ip2_tty_driver->major = IP2_TTY_MAJOR; - ip2_tty_driver->minor_start = 0; - ip2_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - ip2_tty_driver->subtype = SERIAL_TYPE_NORMAL; - ip2_tty_driver->init_termios = tty_std_termios; - ip2_tty_driver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; - ip2_tty_driver->flags = TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV; - tty_set_operations(ip2_tty_driver, &ip2_ops); - - ip2trace(ITRC_NO_PORT, ITRC_INIT, 3, 0); - - err = tty_register_driver(ip2_tty_driver); - if (err) { - printk(KERN_ERR "IP2: failed to register tty driver\n"); - put_tty_driver(ip2_tty_driver); - return err; /* leaking resources */ - } - - err = register_chrdev(IP2_IPL_MAJOR, pcIpl, &ip2_ipl); - if (err) { - printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", - err); - } else { - /* create the sysfs class */ - ip2_class = class_create(THIS_MODULE, "ip2"); - if (IS_ERR(ip2_class)) { - err = PTR_ERR(ip2_class); - goto out_chrdev; - } - } - /* Register the read_procmem thing */ - if (!proc_create("ip2mem",0,NULL,&ip2mem_proc_fops)) { - printk(KERN_ERR "IP2: failed to register read_procmem\n"); - return -EIO; /* leaking resources */ - } - - ip2trace(ITRC_NO_PORT, ITRC_INIT, 4, 0); - /* Register the interrupt handler or poll handler, depending upon the - * specified interrupt. - */ - - for (i = 0; i < IP2_MAX_BOARDS; ++i) { - if (ip2config.addr[i] == 0) - continue; - - pB = i2BoardPtrTable[i]; - if (pB != NULL) { - device_create(ip2_class, NULL, - MKDEV(IP2_IPL_MAJOR, 4 * i), - NULL, "ipl%d", i); - device_create(ip2_class, NULL, - MKDEV(IP2_IPL_MAJOR, 4 * i + 1), - NULL, "stat%d", i); - - for (box = 0; box < ABS_MAX_BOXES; box++) - for (j = 0; j < ABS_BIGGEST_BOX; j++) - if (pB->i2eChannelMap[box] & (1 << j)) - tty_register_device( - ip2_tty_driver, - j + ABS_BIGGEST_BOX * - (box+i*ABS_MAX_BOXES), - NULL); - } - - if (poll_only) { - /* Poll only forces driver to only use polling and - to ignore the probed PCI or EISA interrupts. */ - ip2config.irq[i] = CIR_POLL; - } - if (ip2config.irq[i] == CIR_POLL) { -retry: - if (!timer_pending(&PollTimer)) { - mod_timer(&PollTimer, POLL_TIMEOUT); - printk(KERN_INFO "IP2: polling\n"); - } - } else { - if (have_requested_irq(ip2config.irq[i])) - continue; - rc = request_irq(ip2config.irq[i], ip2_interrupt, - IP2_SA_FLAGS | - (ip2config.type[i] == PCI ? IRQF_SHARED : 0), - pcName, i2BoardPtrTable[i]); - if (rc) { - printk(KERN_ERR "IP2: request_irq failed: " - "error %d\n", rc); - ip2config.irq[i] = CIR_POLL; - printk(KERN_INFO "IP2: Polling %ld/sec.\n", - (POLL_TIMEOUT - jiffies)); - goto retry; - } - mark_requested_irq(ip2config.irq[i]); - /* Initialise the interrupt handler bottom half - * (aka slih). */ - } - } - - for (i = 0; i < IP2_MAX_BOARDS; ++i) { - if (i2BoardPtrTable[i]) { - /* set and enable board interrupt */ - set_irq(i, ip2config.irq[i]); - } - } - - ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0); - - return 0; - -out_chrdev: - unregister_chrdev(IP2_IPL_MAJOR, "ip2"); - /* unregister and put tty here */ - return err; -} -module_init(ip2_loadmain); - -/******************************************************************************/ -/* Function: ip2_init_board() */ -/* Parameters: Index of board in configuration structure */ -/* Returns: Success (0) */ -/* */ -/* Description: */ -/* This function initializes the specified board. The loadware is copied to */ -/* the board, the channel structures are initialized, and the board details */ -/* are reported on the console. */ -/******************************************************************************/ -static void -ip2_init_board(int boardnum, const struct firmware *fw) -{ - int i; - int nports = 0, nboxes = 0; - i2ChanStrPtr pCh; - i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; - - if ( !iiInitialize ( pB ) ) { - printk ( KERN_ERR "IP2: Failed to initialize board at 0x%x, error %d\n", - pB->i2eBase, pB->i2eError ); - goto err_initialize; - } - printk(KERN_INFO "IP2: Board %d: addr=0x%x irq=%d\n", boardnum + 1, - ip2config.addr[boardnum], ip2config.irq[boardnum] ); - - if (!request_region( ip2config.addr[boardnum], 8, pcName )) { - printk(KERN_ERR "IP2: bad addr=0x%x\n", ip2config.addr[boardnum]); - goto err_initialize; - } - - if ( iiDownloadAll ( pB, (loadHdrStrPtr)fw->data, 1, fw->size ) - != II_DOWN_GOOD ) { - printk ( KERN_ERR "IP2: failed to download loadware\n" ); - goto err_release_region; - } else { - printk ( KERN_INFO "IP2: fv=%d.%d.%d lv=%d.%d.%d\n", - pB->i2ePom.e.porVersion, - pB->i2ePom.e.porRevision, - pB->i2ePom.e.porSubRev, pB->i2eLVersion, - pB->i2eLRevision, pB->i2eLSub ); - } - - switch ( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) { - - default: - printk( KERN_ERR "IP2: Unknown board type, ID = %x\n", - pB->i2ePom.e.porID ); - nports = 0; - goto err_release_region; - break; - - case POR_ID_II_4: /* IntelliPort-II, ISA-4 (4xRJ45) */ - printk ( KERN_INFO "IP2: ISA-4\n" ); - nports = 4; - break; - - case POR_ID_II_8: /* IntelliPort-II, 8-port using standard brick. */ - printk ( KERN_INFO "IP2: ISA-8 std\n" ); - nports = 8; - break; - - case POR_ID_II_8R: /* IntelliPort-II, 8-port using RJ11's (no CTS) */ - printk ( KERN_INFO "IP2: ISA-8 RJ11\n" ); - nports = 8; - break; - - case POR_ID_FIIEX: /* IntelliPort IIEX */ - { - int portnum = IP2_PORTS_PER_BOARD * boardnum; - int box; - - for( box = 0; box < ABS_MAX_BOXES; ++box ) { - if ( pB->i2eChannelMap[box] != 0 ) { - ++nboxes; - } - for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { - if ( pB->i2eChannelMap[box] & 1<< i ) { - ++nports; - } - } - } - DevTableMem[boardnum] = pCh = - kmalloc( sizeof(i2ChanStr) * nports, GFP_KERNEL ); - if ( !pCh ) { - printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n"); - goto err_release_region; - } - if ( !i2InitChannels( pB, nports, pCh ) ) { - printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError); - kfree ( pCh ); - goto err_release_region; - } - pB->i2eChannelPtr = &DevTable[portnum]; - pB->i2eChannelCnt = ABS_MOST_PORTS; - - for( box = 0; box < ABS_MAX_BOXES; ++box, portnum += ABS_BIGGEST_BOX ) { - for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { - if ( pB->i2eChannelMap[box] & (1 << i) ) { - DevTable[portnum + i] = pCh; - pCh->port_index = portnum + i; - pCh++; - } - } - } - printk(KERN_INFO "IP2: EX box=%d ports=%d %d bit\n", - nboxes, nports, pB->i2eDataWidth16 ? 16 : 8 ); - } - goto ex_exit; - } - DevTableMem[boardnum] = pCh = - kmalloc ( sizeof (i2ChanStr) * nports, GFP_KERNEL ); - if ( !pCh ) { - printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n"); - goto err_release_region; - } - pB->i2eChannelPtr = pCh; - pB->i2eChannelCnt = nports; - if ( !i2InitChannels( pB, nports, pCh ) ) { - printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError); - kfree ( pCh ); - goto err_release_region; - } - pB->i2eChannelPtr = &DevTable[IP2_PORTS_PER_BOARD * boardnum]; - - for( i = 0; i < pB->i2eChannelCnt; ++i ) { - DevTable[IP2_PORTS_PER_BOARD * boardnum + i] = pCh; - pCh->port_index = (IP2_PORTS_PER_BOARD * boardnum) + i; - pCh++; - } -ex_exit: - INIT_WORK(&pB->tqueue_interrupt, ip2_interrupt_bh); - return; - -err_release_region: - release_region(ip2config.addr[boardnum], 8); -err_initialize: - kfree ( pB ); - i2BoardPtrTable[boardnum] = NULL; - return; -} - -/******************************************************************************/ -/* Function: find_eisa_board ( int start_slot ) */ -/* Parameters: First slot to check */ -/* Returns: Address of EISA IntelliPort II controller */ -/* */ -/* Description: */ -/* This function searches for an EISA IntelliPort controller, starting */ -/* from the specified slot number. If the motherboard is not identified as an */ -/* EISA motherboard, or no valid board ID is selected it returns 0. Otherwise */ -/* it returns the base address of the controller. */ -/******************************************************************************/ -static unsigned short -find_eisa_board( int start_slot ) -{ - int i, j; - unsigned int idm = 0; - unsigned int idp = 0; - unsigned int base = 0; - unsigned int value; - int setup_address; - int setup_irq; - int ismine = 0; - - /* - * First a check for an EISA motherboard, which we do by comparing the - * EISA ID registers for the system board and the first couple of slots. - * No slot ID should match the system board ID, but on an ISA or PCI - * machine the odds are that an empty bus will return similar values for - * each slot. - */ - i = 0x0c80; - value = (inb(i) << 24) + (inb(i+1) << 16) + (inb(i+2) << 8) + inb(i+3); - for( i = 0x1c80; i <= 0x4c80; i += 0x1000 ) { - j = (inb(i)<<24)+(inb(i+1)<<16)+(inb(i+2)<<8)+inb(i+3); - if ( value == j ) - return 0; - } - - /* - * OK, so we are inclined to believe that this is an EISA machine. Find - * an IntelliPort controller. - */ - for( i = start_slot; i < 16; i++ ) { - base = i << 12; - idm = (inb(base + 0xc80) << 8) | (inb(base + 0xc81) & 0xff); - idp = (inb(base + 0xc82) << 8) | (inb(base + 0xc83) & 0xff); - ismine = 0; - if ( idm == 0x0e8e ) { - if ( idp == 0x0281 || idp == 0x0218 ) { - ismine = 1; - } else if ( idp == 0x0282 || idp == 0x0283 ) { - ismine = 3; /* Can do edge-trigger */ - } - if ( ismine ) { - Eisa_slot = i; - break; - } - } - } - if ( !ismine ) - return 0; - - /* It's some sort of EISA card, but at what address is it configured? */ - - setup_address = base + 0xc88; - value = inb(base + 0xc86); - setup_irq = (value & 8) ? Valid_Irqs[value & 7] : 0; - - if ( (ismine & 2) && !(value & 0x10) ) { - ismine = 1; /* Could be edging, but not */ - } - - if ( Eisa_irq == 0 ) { - Eisa_irq = setup_irq; - } else if ( Eisa_irq != setup_irq ) { - printk ( KERN_ERR "IP2: EISA irq mismatch between EISA controllers\n" ); - } - -#ifdef IP2DEBUG_INIT -printk(KERN_DEBUG "Computone EISA board in slot %d, I.D. 0x%x%x, Address 0x%x", - base >> 12, idm, idp, setup_address); - if ( Eisa_irq ) { - printk(KERN_DEBUG ", Interrupt %d %s\n", - setup_irq, (ismine & 2) ? "(edge)" : "(level)"); - } else { - printk(KERN_DEBUG ", (polled)\n"); - } -#endif - return setup_address; -} - -/******************************************************************************/ -/* Function: set_irq() */ -/* Parameters: index to board in board table */ -/* IRQ to use */ -/* Returns: Success (0) */ -/* */ -/* Description: */ -/******************************************************************************/ -static void -set_irq( int boardnum, int boardIrq ) -{ - unsigned char tempCommand[16]; - i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; - unsigned long flags; - - /* - * Notify the boards they may generate interrupts. This is done by - * sending an in-line command to channel 0 on each board. This is why - * the channels have to be defined already. For each board, if the - * interrupt has never been defined, we must do so NOW, directly, since - * board will not send flow control or even give an interrupt until this - * is done. If polling we must send 0 as the interrupt parameter. - */ - - // We will get an interrupt here at the end of this function - - iiDisableMailIrq(pB); - - /* We build up the entire packet header. */ - CHANNEL_OF(tempCommand) = 0; - PTYPE_OF(tempCommand) = PTYPE_INLINE; - CMD_COUNT_OF(tempCommand) = 2; - (CMD_OF(tempCommand))[0] = CMDVALUE_IRQ; - (CMD_OF(tempCommand))[1] = boardIrq; - /* - * Write to FIFO; don't bother to adjust fifo capacity for this, since - * board will respond almost immediately after SendMail hit. - */ - write_lock_irqsave(&pB->write_fifo_spinlock, flags); - iiWriteBuf(pB, tempCommand, 4); - write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); - pB->i2eUsingIrq = boardIrq; - pB->i2eOutMailWaiting |= MB_OUT_STUFFED; - - /* Need to update number of boards before you enable mailbox int */ - ++i2nBoards; - - CHANNEL_OF(tempCommand) = 0; - PTYPE_OF(tempCommand) = PTYPE_BYPASS; - CMD_COUNT_OF(tempCommand) = 6; - (CMD_OF(tempCommand))[0] = 88; // SILO - (CMD_OF(tempCommand))[1] = 64; // chars - (CMD_OF(tempCommand))[2] = 32; // ms - - (CMD_OF(tempCommand))[3] = 28; // MAX_BLOCK - (CMD_OF(tempCommand))[4] = 64; // chars - - (CMD_OF(tempCommand))[5] = 87; // HW_TEST - write_lock_irqsave(&pB->write_fifo_spinlock, flags); - iiWriteBuf(pB, tempCommand, 8); - write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); - - CHANNEL_OF(tempCommand) = 0; - PTYPE_OF(tempCommand) = PTYPE_BYPASS; - CMD_COUNT_OF(tempCommand) = 1; - (CMD_OF(tempCommand))[0] = 84; /* get BOX_IDS */ - iiWriteBuf(pB, tempCommand, 3); - -#ifdef XXX - // enable heartbeat for test porpoises - CHANNEL_OF(tempCommand) = 0; - PTYPE_OF(tempCommand) = PTYPE_BYPASS; - CMD_COUNT_OF(tempCommand) = 2; - (CMD_OF(tempCommand))[0] = 44; /* get ping */ - (CMD_OF(tempCommand))[1] = 200; /* 200 ms */ - write_lock_irqsave(&pB->write_fifo_spinlock, flags); - iiWriteBuf(pB, tempCommand, 4); - write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); -#endif - - iiEnableMailIrq(pB); - iiSendPendingMail(pB); -} - -/******************************************************************************/ -/* Interrupt Handler Section */ -/******************************************************************************/ - -static inline void -service_all_boards(void) -{ - int i; - i2eBordStrPtr pB; - - /* Service every board on the list */ - for( i = 0; i < IP2_MAX_BOARDS; ++i ) { - pB = i2BoardPtrTable[i]; - if ( pB ) { - i2ServiceBoard( pB ); - } - } -} - - -/******************************************************************************/ -/* Function: ip2_interrupt_bh(work) */ -/* Parameters: work - pointer to the board structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* Service the board in a bottom half interrupt handler and then */ -/* reenable the board's interrupts if it has an IRQ number */ -/* */ -/******************************************************************************/ -static void -ip2_interrupt_bh(struct work_struct *work) -{ - i2eBordStrPtr pB = container_of(work, i2eBordStr, tqueue_interrupt); -// pB better well be set or we have a problem! We can only get -// here from the IMMEDIATE queue. Here, we process the boards. -// Checking pB doesn't cost much and it saves us from the sanity checkers. - - bh_counter++; - - if ( pB ) { - i2ServiceBoard( pB ); - if( pB->i2eUsingIrq ) { -// Re-enable his interrupts - iiEnableMailIrq(pB); - } - } -} - - -/******************************************************************************/ -/* Function: ip2_interrupt(int irq, void *dev_id) */ -/* Parameters: irq - interrupt number */ -/* pointer to optional device ID structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* */ -/* Our task here is simply to identify each board which needs servicing. */ -/* If we are queuing then, queue it to be serviced, and disable its irq */ -/* mask otherwise process the board directly. */ -/* */ -/* We could queue by IRQ but that just complicates things on both ends */ -/* with very little gain in performance (how many instructions does */ -/* it take to iterate on the immediate queue). */ -/* */ -/* */ -/******************************************************************************/ -static void -ip2_irq_work(i2eBordStrPtr pB) -{ -#ifdef USE_IQI - if (NO_MAIL_HERE != ( pB->i2eStartMail = iiGetMail(pB))) { -// Disable his interrupt (will be enabled when serviced) -// This is mostly to protect from reentrancy. - iiDisableMailIrq(pB); - -// Park the board on the immediate queue for processing. - schedule_work(&pB->tqueue_interrupt); - -// Make sure the immediate queue is flagged to fire. - } -#else - -// We are using immediate servicing here. This sucks and can -// cause all sorts of havoc with ppp and others. The failsafe -// check on iiSendPendingMail could also throw a hairball. - - i2ServiceBoard( pB ); - -#endif /* USE_IQI */ -} - -static void -ip2_polled_interrupt(void) -{ - int i; - i2eBordStrPtr pB; - - ip2trace(ITRC_NO_PORT, ITRC_INTR, 99, 1, 0); - - /* Service just the boards on the list using this irq */ - for( i = 0; i < i2nBoards; ++i ) { - pB = i2BoardPtrTable[i]; - -// Only process those boards which match our IRQ. -// IRQ = 0 for polled boards, we won't poll "IRQ" boards - - if (pB && pB->i2eUsingIrq == 0) - ip2_irq_work(pB); - } - - ++irq_counter; - - ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); -} - -static irqreturn_t -ip2_interrupt(int irq, void *dev_id) -{ - i2eBordStrPtr pB = dev_id; - - ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, pB->i2eUsingIrq ); - - ip2_irq_work(pB); - - ++irq_counter; - - ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); - return IRQ_HANDLED; -} - -/******************************************************************************/ -/* Function: ip2_poll(unsigned long arg) */ -/* Parameters: ? */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* This function calls the library routine i2ServiceBoard for each board in */ -/* the board table. This is used instead of the interrupt routine when polled */ -/* mode is specified. */ -/******************************************************************************/ -static void -ip2_poll(unsigned long arg) -{ - ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 ); - - // Just polled boards, IRQ = 0 will hit all non-interrupt boards. - // It will NOT poll boards handled by hard interrupts. - // The issue of queued BH interrupts is handled in ip2_interrupt(). - ip2_polled_interrupt(); - - mod_timer(&PollTimer, POLL_TIMEOUT); - - ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); -} - -static void do_input(struct work_struct *work) -{ - i2ChanStrPtr pCh = container_of(work, i2ChanStr, tqueue_input); - unsigned long flags; - - ip2trace(CHANN, ITRC_INPUT, 21, 0 ); - - // Data input - if ( pCh->pTTY != NULL ) { - read_lock_irqsave(&pCh->Ibuf_spinlock, flags); - if (!pCh->throttled && (pCh->Ibuf_stuff != pCh->Ibuf_strip)) { - read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); - i2Input( pCh ); - } else - read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); - } else { - ip2trace(CHANN, ITRC_INPUT, 22, 0 ); - - i2InputFlush( pCh ); - } -} - -// code duplicated from n_tty (ldisc) -static inline void isig(int sig, struct tty_struct *tty, int flush) -{ - /* FIXME: This is completely bogus */ - if (tty->pgrp) - kill_pgrp(tty->pgrp, sig, 1); - if (flush || !L_NOFLSH(tty)) { - if ( tty->ldisc->ops->flush_buffer ) - tty->ldisc->ops->flush_buffer(tty); - i2InputFlush( tty->driver_data ); - } -} - -static void do_status(struct work_struct *work) -{ - i2ChanStrPtr pCh = container_of(work, i2ChanStr, tqueue_status); - int status; - - status = i2GetStatus( pCh, (I2_BRK|I2_PAR|I2_FRA|I2_OVR) ); - - ip2trace (CHANN, ITRC_STATUS, 21, 1, status ); - - if (pCh->pTTY && (status & (I2_BRK|I2_PAR|I2_FRA|I2_OVR)) ) { - if ( (status & I2_BRK) ) { - // code duplicated from n_tty (ldisc) - if (I_IGNBRK(pCh->pTTY)) - goto skip_this; - if (I_BRKINT(pCh->pTTY)) { - isig(SIGINT, pCh->pTTY, 1); - goto skip_this; - } - wake_up_interruptible(&pCh->pTTY->read_wait); - } -#ifdef NEVER_HAPPENS_AS_SETUP_XXX - // and can't work because we don't know the_char - // as the_char is reported on a separate path - // The intelligent board does this stuff as setup - { - char brkf = TTY_NORMAL; - unsigned char brkc = '\0'; - unsigned char tmp; - if ( (status & I2_BRK) ) { - brkf = TTY_BREAK; - brkc = '\0'; - } - else if (status & I2_PAR) { - brkf = TTY_PARITY; - brkc = the_char; - } else if (status & I2_FRA) { - brkf = TTY_FRAME; - brkc = the_char; - } else if (status & I2_OVR) { - brkf = TTY_OVERRUN; - brkc = the_char; - } - tmp = pCh->pTTY->real_raw; - pCh->pTTY->real_raw = 0; - pCh->pTTY->ldisc->ops.receive_buf( pCh->pTTY, &brkc, &brkf, 1 ); - pCh->pTTY->real_raw = tmp; - } -#endif /* NEVER_HAPPENS_AS_SETUP_XXX */ - } -skip_this: - - if ( status & (I2_DDCD | I2_DDSR | I2_DCTS | I2_DRI) ) { - wake_up_interruptible(&pCh->delta_msr_wait); - - if ( (pCh->flags & ASYNC_CHECK_CD) && (status & I2_DDCD) ) { - if ( status & I2_DCD ) { - if ( pCh->wopen ) { - wake_up_interruptible ( &pCh->open_wait ); - } - } else { - if (pCh->pTTY && (!(pCh->pTTY->termios->c_cflag & CLOCAL)) ) { - tty_hangup( pCh->pTTY ); - } - } - } - } - - ip2trace (CHANN, ITRC_STATUS, 26, 0 ); -} - -/******************************************************************************/ -/* Device Open/Close/Ioctl Entry Point Section */ -/******************************************************************************/ - -/******************************************************************************/ -/* Function: open_sanity_check() */ -/* Parameters: Pointer to tty structure */ -/* Pointer to file structure */ -/* Returns: Success or failure */ -/* */ -/* Description: */ -/* Verifies the structure magic numbers and cross links. */ -/******************************************************************************/ -#ifdef IP2DEBUG_OPEN -static void -open_sanity_check( i2ChanStrPtr pCh, i2eBordStrPtr pBrd ) -{ - if ( pBrd->i2eValid != I2E_MAGIC ) { - printk(KERN_ERR "IP2: invalid board structure\n" ); - } else if ( pBrd != pCh->pMyBord ) { - printk(KERN_ERR "IP2: board structure pointer mismatch (%p)\n", - pCh->pMyBord ); - } else if ( pBrd->i2eChannelCnt < pCh->port_index ) { - printk(KERN_ERR "IP2: bad device index (%d)\n", pCh->port_index ); - } else if (&((i2ChanStrPtr)pBrd->i2eChannelPtr)[pCh->port_index] != pCh) { - } else { - printk(KERN_INFO "IP2: all pointers check out!\n" ); - } -} -#endif - - -/******************************************************************************/ -/* Function: ip2_open() */ -/* Parameters: Pointer to tty structure */ -/* Pointer to file structure */ -/* Returns: Success or failure */ -/* */ -/* Description: (MANDATORY) */ -/* A successful device open has to run a gauntlet of checks before it */ -/* completes. After some sanity checking and pointer setup, the function */ -/* blocks until all conditions are satisfied. It then initialises the port to */ -/* the default characteristics and returns. */ -/******************************************************************************/ -static int -ip2_open( PTTY tty, struct file *pFile ) -{ - wait_queue_t wait; - int rc = 0; - int do_clocal = 0; - i2ChanStrPtr pCh = DevTable[tty->index]; - - ip2trace (tty->index, ITRC_OPEN, ITRC_ENTER, 0 ); - - if ( pCh == NULL ) { - return -ENODEV; - } - /* Setup pointer links in device and tty structures */ - pCh->pTTY = tty; - tty->driver_data = pCh; - -#ifdef IP2DEBUG_OPEN - printk(KERN_DEBUG \ - "IP2:open(tty=%p,pFile=%p):dev=%s,ch=%d,idx=%d\n", - tty, pFile, tty->name, pCh->infl.hd.i2sChannel, pCh->port_index); - open_sanity_check ( pCh, pCh->pMyBord ); -#endif - - i2QueueCommands(PTYPE_INLINE, pCh, 100, 3, CMD_DTRUP,CMD_RTSUP,CMD_DCD_REP); - pCh->dataSetOut |= (I2_DTR | I2_RTS); - serviceOutgoingFifo( pCh->pMyBord ); - - /* Block here until the port is ready (per serial and istallion) */ - /* - * 1. If the port is in the middle of closing wait for the completion - * and then return the appropriate error. - */ - init_waitqueue_entry(&wait, current); - add_wait_queue(&pCh->close_wait, &wait); - set_current_state( TASK_INTERRUPTIBLE ); - - if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) { - if ( pCh->flags & ASYNC_CLOSING ) { - tty_unlock(); - schedule(); - tty_lock(); - } - if ( tty_hung_up_p(pFile) ) { - set_current_state( TASK_RUNNING ); - remove_wait_queue(&pCh->close_wait, &wait); - return( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS; - } - } - set_current_state( TASK_RUNNING ); - remove_wait_queue(&pCh->close_wait, &wait); - - /* - * 3. Handle a non-blocking open of a normal port. - */ - if ( (pFile->f_flags & O_NONBLOCK) || (tty->flags & (1<flags |= ASYNC_NORMAL_ACTIVE; - goto noblock; - } - /* - * 4. Now loop waiting for the port to be free and carrier present - * (if required). - */ - if ( tty->termios->c_cflag & CLOCAL ) - do_clocal = 1; - -#ifdef IP2DEBUG_OPEN - printk(KERN_DEBUG "OpenBlock: do_clocal = %d\n", do_clocal); -#endif - - ++pCh->wopen; - - init_waitqueue_entry(&wait, current); - add_wait_queue(&pCh->open_wait, &wait); - - for(;;) { - i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); - pCh->dataSetOut |= (I2_DTR | I2_RTS); - set_current_state( TASK_INTERRUPTIBLE ); - serviceOutgoingFifo( pCh->pMyBord ); - if ( tty_hung_up_p(pFile) ) { - set_current_state( TASK_RUNNING ); - remove_wait_queue(&pCh->open_wait, &wait); - return ( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EBUSY : -ERESTARTSYS; - } - if (!(pCh->flags & ASYNC_CLOSING) && - (do_clocal || (pCh->dataSetIn & I2_DCD) )) { - rc = 0; - break; - } - -#ifdef IP2DEBUG_OPEN - printk(KERN_DEBUG "ASYNC_CLOSING = %s\n", - (pCh->flags & ASYNC_CLOSING)?"True":"False"); - printk(KERN_DEBUG "OpenBlock: waiting for CD or signal\n"); -#endif - ip2trace (CHANN, ITRC_OPEN, 3, 2, 0, - (pCh->flags & ASYNC_CLOSING) ); - /* check for signal */ - if (signal_pending(current)) { - rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS); - break; - } - tty_unlock(); - schedule(); - tty_lock(); - } - set_current_state( TASK_RUNNING ); - remove_wait_queue(&pCh->open_wait, &wait); - - --pCh->wopen; //why count? - - ip2trace (CHANN, ITRC_OPEN, 4, 0 ); - - if (rc != 0 ) { - return rc; - } - pCh->flags |= ASYNC_NORMAL_ACTIVE; - -noblock: - - /* first open - Assign termios structure to port */ - if ( tty->count == 1 ) { - i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); - /* Now we must send the termios settings to the loadware */ - set_params( pCh, NULL ); - } - - /* - * Now set any i2lib options. These may go away if the i2lib code ends - * up rolled into the mainline. - */ - pCh->channelOptions |= CO_NBLOCK_WRITE; - -#ifdef IP2DEBUG_OPEN - printk (KERN_DEBUG "IP2: open completed\n" ); -#endif - serviceOutgoingFifo( pCh->pMyBord ); - - ip2trace (CHANN, ITRC_OPEN, ITRC_RETURN, 0 ); - - return 0; -} - -/******************************************************************************/ -/* Function: ip2_close() */ -/* Parameters: Pointer to tty structure */ -/* Pointer to file structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static void -ip2_close( PTTY tty, struct file *pFile ) -{ - i2ChanStrPtr pCh = tty->driver_data; - - if ( !pCh ) { - return; - } - - ip2trace (CHANN, ITRC_CLOSE, ITRC_ENTER, 0 ); - -#ifdef IP2DEBUG_OPEN - printk(KERN_DEBUG "IP2:close %s:\n",tty->name); -#endif - - if ( tty_hung_up_p ( pFile ) ) { - - ip2trace (CHANN, ITRC_CLOSE, 2, 1, 2 ); - - return; - } - if ( tty->count > 1 ) { /* not the last close */ - - ip2trace (CHANN, ITRC_CLOSE, 2, 1, 3 ); - - return; - } - pCh->flags |= ASYNC_CLOSING; // last close actually - - tty->closing = 1; - - if (pCh->ClosingWaitTime != ASYNC_CLOSING_WAIT_NONE) { - /* - * Before we drop DTR, make sure the transmitter has completely drained. - * This uses an timeout, after which the close - * completes. - */ - ip2_wait_until_sent(tty, pCh->ClosingWaitTime ); - } - /* - * At this point we stop accepting input. Here we flush the channel - * input buffer which will allow the board to send up more data. Any - * additional input is tossed at interrupt/poll time. - */ - i2InputFlush( pCh ); - - /* disable DSS reporting */ - i2QueueCommands(PTYPE_INLINE, pCh, 100, 4, - CMD_DCD_NREP, CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); - if (tty->termios->c_cflag & HUPCL) { - i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN); - pCh->dataSetOut &= ~(I2_DTR | I2_RTS); - i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); - } - - serviceOutgoingFifo ( pCh->pMyBord ); - - tty_ldisc_flush(tty); - tty_driver_flush_buffer(tty); - tty->closing = 0; - - pCh->pTTY = NULL; - - if (pCh->wopen) { - if (pCh->ClosingDelay) { - msleep_interruptible(jiffies_to_msecs(pCh->ClosingDelay)); - } - wake_up_interruptible(&pCh->open_wait); - } - - pCh->flags &=~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&pCh->close_wait); - -#ifdef IP2DEBUG_OPEN - DBG_CNT("ip2_close: after wakeups--"); -#endif - - - ip2trace (CHANN, ITRC_CLOSE, ITRC_RETURN, 1, 1 ); - - return; -} - -/******************************************************************************/ -/* Function: ip2_hangup() */ -/* Parameters: Pointer to tty structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static void -ip2_hangup ( PTTY tty ) -{ - i2ChanStrPtr pCh = tty->driver_data; - - if( !pCh ) { - return; - } - - ip2trace (CHANN, ITRC_HANGUP, ITRC_ENTER, 0 ); - - ip2_flush_buffer(tty); - - /* disable DSS reporting */ - - i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_DCD_NREP); - i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); - if ( (tty->termios->c_cflag & HUPCL) ) { - i2QueueCommands(PTYPE_BYPASS, pCh, 0, 2, CMD_RTSDN, CMD_DTRDN); - pCh->dataSetOut &= ~(I2_DTR | I2_RTS); - i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); - } - i2QueueCommands(PTYPE_INLINE, pCh, 1, 3, - CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); - serviceOutgoingFifo ( pCh->pMyBord ); - - wake_up_interruptible ( &pCh->delta_msr_wait ); - - pCh->flags &= ~ASYNC_NORMAL_ACTIVE; - pCh->pTTY = NULL; - wake_up_interruptible ( &pCh->open_wait ); - - ip2trace (CHANN, ITRC_HANGUP, ITRC_RETURN, 0 ); -} - -/******************************************************************************/ -/******************************************************************************/ -/* Device Output Section */ -/******************************************************************************/ -/******************************************************************************/ - -/******************************************************************************/ -/* Function: ip2_write() */ -/* Parameters: Pointer to tty structure */ -/* Flag denoting data is in user (1) or kernel (0) space */ -/* Pointer to data */ -/* Number of bytes to write */ -/* Returns: Number of bytes actually written */ -/* */ -/* Description: (MANDATORY) */ -/* */ -/* */ -/******************************************************************************/ -static int -ip2_write( PTTY tty, const unsigned char *pData, int count) -{ - i2ChanStrPtr pCh = tty->driver_data; - int bytesSent = 0; - unsigned long flags; - - ip2trace (CHANN, ITRC_WRITE, ITRC_ENTER, 2, count, -1 ); - - /* Flush out any buffered data left over from ip2_putchar() calls. */ - ip2_flush_chars( tty ); - - /* This is the actual move bit. Make sure it does what we need!!!!! */ - write_lock_irqsave(&pCh->Pbuf_spinlock, flags); - bytesSent = i2Output( pCh, pData, count); - write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); - - ip2trace (CHANN, ITRC_WRITE, ITRC_RETURN, 1, bytesSent ); - - return bytesSent > 0 ? bytesSent : 0; -} - -/******************************************************************************/ -/* Function: ip2_putchar() */ -/* Parameters: Pointer to tty structure */ -/* Character to write */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static int -ip2_putchar( PTTY tty, unsigned char ch ) -{ - i2ChanStrPtr pCh = tty->driver_data; - unsigned long flags; - -// ip2trace (CHANN, ITRC_PUTC, ITRC_ENTER, 1, ch ); - - write_lock_irqsave(&pCh->Pbuf_spinlock, flags); - pCh->Pbuf[pCh->Pbuf_stuff++] = ch; - if ( pCh->Pbuf_stuff == sizeof pCh->Pbuf ) { - write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); - ip2_flush_chars( tty ); - } else - write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); - return 1; - -// ip2trace (CHANN, ITRC_PUTC, ITRC_RETURN, 1, ch ); -} - -/******************************************************************************/ -/* Function: ip2_flush_chars() */ -/* Parameters: Pointer to tty structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* */ -/******************************************************************************/ -static void -ip2_flush_chars( PTTY tty ) -{ - int strip; - i2ChanStrPtr pCh = tty->driver_data; - unsigned long flags; - - write_lock_irqsave(&pCh->Pbuf_spinlock, flags); - if ( pCh->Pbuf_stuff ) { - -// ip2trace (CHANN, ITRC_PUTC, 10, 1, strip ); - - // - // We may need to restart i2Output if it does not fullfill this request - // - strip = i2Output( pCh, pCh->Pbuf, pCh->Pbuf_stuff); - if ( strip != pCh->Pbuf_stuff ) { - memmove( pCh->Pbuf, &pCh->Pbuf[strip], pCh->Pbuf_stuff - strip ); - } - pCh->Pbuf_stuff -= strip; - } - write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); -} - -/******************************************************************************/ -/* Function: ip2_write_room() */ -/* Parameters: Pointer to tty structure */ -/* Returns: Number of bytes that the driver can accept */ -/* */ -/* Description: */ -/* */ -/******************************************************************************/ -static int -ip2_write_room ( PTTY tty ) -{ - int bytesFree; - i2ChanStrPtr pCh = tty->driver_data; - unsigned long flags; - - read_lock_irqsave(&pCh->Pbuf_spinlock, flags); - bytesFree = i2OutputFree( pCh ) - pCh->Pbuf_stuff; - read_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); - - ip2trace (CHANN, ITRC_WRITE, 11, 1, bytesFree ); - - return ((bytesFree > 0) ? bytesFree : 0); -} - -/******************************************************************************/ -/* Function: ip2_chars_in_buf() */ -/* Parameters: Pointer to tty structure */ -/* Returns: Number of bytes queued for transmission */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static int -ip2_chars_in_buf ( PTTY tty ) -{ - i2ChanStrPtr pCh = tty->driver_data; - int rc; - unsigned long flags; - - ip2trace (CHANN, ITRC_WRITE, 12, 1, pCh->Obuf_char_count + pCh->Pbuf_stuff ); - -#ifdef IP2DEBUG_WRITE - printk (KERN_DEBUG "IP2: chars in buffer = %d (%d,%d)\n", - pCh->Obuf_char_count + pCh->Pbuf_stuff, - pCh->Obuf_char_count, pCh->Pbuf_stuff ); -#endif - read_lock_irqsave(&pCh->Obuf_spinlock, flags); - rc = pCh->Obuf_char_count; - read_unlock_irqrestore(&pCh->Obuf_spinlock, flags); - read_lock_irqsave(&pCh->Pbuf_spinlock, flags); - rc += pCh->Pbuf_stuff; - read_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); - return rc; -} - -/******************************************************************************/ -/* Function: ip2_flush_buffer() */ -/* Parameters: Pointer to tty structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static void -ip2_flush_buffer( PTTY tty ) -{ - i2ChanStrPtr pCh = tty->driver_data; - unsigned long flags; - - ip2trace (CHANN, ITRC_FLUSH, ITRC_ENTER, 0 ); - -#ifdef IP2DEBUG_WRITE - printk (KERN_DEBUG "IP2: flush buffer\n" ); -#endif - write_lock_irqsave(&pCh->Pbuf_spinlock, flags); - pCh->Pbuf_stuff = 0; - write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); - i2FlushOutput( pCh ); - ip2_owake(tty); - - ip2trace (CHANN, ITRC_FLUSH, ITRC_RETURN, 0 ); - -} - -/******************************************************************************/ -/* Function: ip2_wait_until_sent() */ -/* Parameters: Pointer to tty structure */ -/* Timeout for wait. */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* This function is used in place of the normal tty_wait_until_sent, which */ -/* only waits for the driver buffers to be empty (or rather, those buffers */ -/* reported by chars_in_buffer) which doesn't work for IP2 due to the */ -/* indeterminate number of bytes buffered on the board. */ -/******************************************************************************/ -static void -ip2_wait_until_sent ( PTTY tty, int timeout ) -{ - int i = jiffies; - i2ChanStrPtr pCh = tty->driver_data; - - tty_wait_until_sent(tty, timeout ); - if ( (i = timeout - (jiffies -i)) > 0) - i2DrainOutput( pCh, i ); -} - -/******************************************************************************/ -/******************************************************************************/ -/* Device Input Section */ -/******************************************************************************/ -/******************************************************************************/ - -/******************************************************************************/ -/* Function: ip2_throttle() */ -/* Parameters: Pointer to tty structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static void -ip2_throttle ( PTTY tty ) -{ - i2ChanStrPtr pCh = tty->driver_data; - -#ifdef IP2DEBUG_READ - printk (KERN_DEBUG "IP2: throttle\n" ); -#endif - /* - * Signal the poll/interrupt handlers not to forward incoming data to - * the line discipline. This will cause the buffers to fill up in the - * library and thus cause the library routines to send the flow control - * stuff. - */ - pCh->throttled = 1; -} - -/******************************************************************************/ -/* Function: ip2_unthrottle() */ -/* Parameters: Pointer to tty structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static void -ip2_unthrottle ( PTTY tty ) -{ - i2ChanStrPtr pCh = tty->driver_data; - unsigned long flags; - -#ifdef IP2DEBUG_READ - printk (KERN_DEBUG "IP2: unthrottle\n" ); -#endif - - /* Pass incoming data up to the line discipline again. */ - pCh->throttled = 0; - i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME); - serviceOutgoingFifo( pCh->pMyBord ); - read_lock_irqsave(&pCh->Ibuf_spinlock, flags); - if ( pCh->Ibuf_stuff != pCh->Ibuf_strip ) { - read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); -#ifdef IP2DEBUG_READ - printk (KERN_DEBUG "i2Input called from unthrottle\n" ); -#endif - i2Input( pCh ); - } else - read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); -} - -static void -ip2_start ( PTTY tty ) -{ - i2ChanStrPtr pCh = DevTable[tty->index]; - - i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME); - i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_UNSUSPEND); - i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_RESUME); -#ifdef IP2DEBUG_WRITE - printk (KERN_DEBUG "IP2: start tx\n" ); -#endif -} - -static void -ip2_stop ( PTTY tty ) -{ - i2ChanStrPtr pCh = DevTable[tty->index]; - - i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_SUSPEND); -#ifdef IP2DEBUG_WRITE - printk (KERN_DEBUG "IP2: stop tx\n" ); -#endif -} - -/******************************************************************************/ -/* Device Ioctl Section */ -/******************************************************************************/ - -static int ip2_tiocmget(struct tty_struct *tty) -{ - i2ChanStrPtr pCh = DevTable[tty->index]; -#ifdef ENABLE_DSSNOW - wait_queue_t wait; -#endif - - if (pCh == NULL) - return -ENODEV; - -/* - FIXME - the following code is causing a NULL pointer dereference in - 2.3.51 in an interrupt handler. It's suppose to prompt the board - to return the DSS signal status immediately. Why doesn't it do - the same thing in 2.2.14? -*/ - -/* This thing is still busted in the 1.2.12 driver on 2.4.x - and even hoses the serial console so the oops can be trapped. - /\/\|=mhw=|\/\/ */ - -#ifdef ENABLE_DSSNOW - i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DSS_NOW); - - init_waitqueue_entry(&wait, current); - add_wait_queue(&pCh->dss_now_wait, &wait); - set_current_state( TASK_INTERRUPTIBLE ); - - serviceOutgoingFifo( pCh->pMyBord ); - - schedule(); - - set_current_state( TASK_RUNNING ); - remove_wait_queue(&pCh->dss_now_wait, &wait); - - if (signal_pending(current)) { - return -EINTR; - } -#endif - return ((pCh->dataSetOut & I2_RTS) ? TIOCM_RTS : 0) - | ((pCh->dataSetOut & I2_DTR) ? TIOCM_DTR : 0) - | ((pCh->dataSetIn & I2_DCD) ? TIOCM_CAR : 0) - | ((pCh->dataSetIn & I2_RI) ? TIOCM_RNG : 0) - | ((pCh->dataSetIn & I2_DSR) ? TIOCM_DSR : 0) - | ((pCh->dataSetIn & I2_CTS) ? TIOCM_CTS : 0); -} - -static int ip2_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - i2ChanStrPtr pCh = DevTable[tty->index]; - - if (pCh == NULL) - return -ENODEV; - - if (set & TIOCM_RTS) { - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP); - pCh->dataSetOut |= I2_RTS; - } - if (set & TIOCM_DTR) { - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP); - pCh->dataSetOut |= I2_DTR; - } - - if (clear & TIOCM_RTS) { - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN); - pCh->dataSetOut &= ~I2_RTS; - } - if (clear & TIOCM_DTR) { - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN); - pCh->dataSetOut &= ~I2_DTR; - } - serviceOutgoingFifo( pCh->pMyBord ); - return 0; -} - -/******************************************************************************/ -/* Function: ip2_ioctl() */ -/* Parameters: Pointer to tty structure */ -/* Pointer to file structure */ -/* Command */ -/* Argument */ -/* Returns: Success or failure */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static int -ip2_ioctl ( PTTY tty, UINT cmd, ULONG arg ) -{ - wait_queue_t wait; - i2ChanStrPtr pCh = DevTable[tty->index]; - i2eBordStrPtr pB; - struct async_icount cprev, cnow; /* kernel counter temps */ - int rc = 0; - unsigned long flags; - void __user *argp = (void __user *)arg; - - if ( pCh == NULL ) - return -ENODEV; - - pB = pCh->pMyBord; - - ip2trace (CHANN, ITRC_IOCTL, ITRC_ENTER, 2, cmd, arg ); - -#ifdef IP2DEBUG_IOCTL - printk(KERN_DEBUG "IP2: ioctl cmd (%x), arg (%lx)\n", cmd, arg ); -#endif - - switch(cmd) { - case TIOCGSERIAL: - - ip2trace (CHANN, ITRC_IOCTL, 2, 1, rc ); - - rc = get_serial_info(pCh, argp); - if (rc) - return rc; - break; - - case TIOCSSERIAL: - - ip2trace (CHANN, ITRC_IOCTL, 3, 1, rc ); - - rc = set_serial_info(pCh, argp); - if (rc) - return rc; - break; - - case TCXONC: - rc = tty_check_change(tty); - if (rc) - return rc; - switch (arg) { - case TCOOFF: - //return -ENOIOCTLCMD; - break; - case TCOON: - //return -ENOIOCTLCMD; - break; - case TCIOFF: - if (STOP_CHAR(tty) != __DISABLED_CHAR) { - i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1, - CMD_XMIT_NOW(STOP_CHAR(tty))); - } - break; - case TCION: - if (START_CHAR(tty) != __DISABLED_CHAR) { - i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1, - CMD_XMIT_NOW(START_CHAR(tty))); - } - break; - default: - return -EINVAL; - } - return 0; - - case TCSBRK: /* SVID version: non-zero arg --> no break */ - rc = tty_check_change(tty); - - ip2trace (CHANN, ITRC_IOCTL, 4, 1, rc ); - - if (!rc) { - ip2_wait_until_sent(tty,0); - if (!arg) { - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SEND_BRK(250)); - serviceOutgoingFifo( pCh->pMyBord ); - } - } - break; - - case TCSBRKP: /* support for POSIX tcsendbreak() */ - rc = tty_check_change(tty); - - ip2trace (CHANN, ITRC_IOCTL, 5, 1, rc ); - - if (!rc) { - ip2_wait_until_sent(tty,0); - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, - CMD_SEND_BRK(arg ? arg*100 : 250)); - serviceOutgoingFifo ( pCh->pMyBord ); - } - break; - - case TIOCGSOFTCAR: - - ip2trace (CHANN, ITRC_IOCTL, 6, 1, rc ); - - rc = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *)argp); - if (rc) - return rc; - break; - - case TIOCSSOFTCAR: - - ip2trace (CHANN, ITRC_IOCTL, 7, 1, rc ); - - rc = get_user(arg,(unsigned long __user *) argp); - if (rc) - return rc; - tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) - | (arg ? CLOCAL : 0)); - - break; - - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - mask - * passed in arg for lines of interest (use |'ed TIOCM_RNG/DSR/CD/CTS - * for masking). Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: - write_lock_irqsave(&pB->read_fifo_spinlock, flags); - cprev = pCh->icount; /* note the counters on entry */ - write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); - i2QueueCommands(PTYPE_BYPASS, pCh, 100, 4, - CMD_DCD_REP, CMD_CTS_REP, CMD_DSR_REP, CMD_RI_REP); - init_waitqueue_entry(&wait, current); - add_wait_queue(&pCh->delta_msr_wait, &wait); - set_current_state( TASK_INTERRUPTIBLE ); - - serviceOutgoingFifo( pCh->pMyBord ); - for(;;) { - ip2trace (CHANN, ITRC_IOCTL, 10, 0 ); - - schedule(); - - ip2trace (CHANN, ITRC_IOCTL, 11, 0 ); - - /* see if a signal did it */ - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - write_lock_irqsave(&pB->read_fifo_spinlock, flags); - cnow = pCh->icount; /* atomic copy */ - write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { - rc = -EIO; /* no change => rc */ - break; - } - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { - rc = 0; - break; - } - cprev = cnow; - } - set_current_state( TASK_RUNNING ); - remove_wait_queue(&pCh->delta_msr_wait, &wait); - - i2QueueCommands(PTYPE_BYPASS, pCh, 100, 3, - CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); - if ( ! (pCh->flags & ASYNC_CHECK_CD)) { - i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DCD_NREP); - } - serviceOutgoingFifo( pCh->pMyBord ); - return rc; - break; - - /* - * The rest are not supported by this driver. By returning -ENOIOCTLCMD they - * will be passed to the line discipline for it to handle. - */ - case TIOCSERCONFIG: - case TIOCSERGWILD: - case TIOCSERGETLSR: - case TIOCSERSWILD: - case TIOCSERGSTRUCT: - case TIOCSERGETMULTI: - case TIOCSERSETMULTI: - - default: - ip2trace (CHANN, ITRC_IOCTL, 12, 0 ); - - rc = -ENOIOCTLCMD; - break; - } - - ip2trace (CHANN, ITRC_IOCTL, ITRC_RETURN, 0 ); - - return rc; -} - -static int ip2_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - i2ChanStrPtr pCh = DevTable[tty->index]; - i2eBordStrPtr pB; - struct async_icount cnow; /* kernel counter temp */ - unsigned long flags; - - if ( pCh == NULL ) - return -ENODEV; - - pB = pCh->pMyBord; - - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for RI where - * only 0->1 is counted. The controller is quite capable of counting - * both, but this done to preserve compatibility with the standard - * serial driver. - */ - - write_lock_irqsave(&pB->read_fifo_spinlock, flags); - cnow = pCh->icount; - write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - return 0; -} - -/******************************************************************************/ -/* Function: GetSerialInfo() */ -/* Parameters: Pointer to channel structure */ -/* Pointer to old termios structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* This is to support the setserial command, and requires processing of the */ -/* standard Linux serial structure. */ -/******************************************************************************/ -static int -get_serial_info ( i2ChanStrPtr pCh, struct serial_struct __user *retinfo ) -{ - struct serial_struct tmp; - - memset ( &tmp, 0, sizeof(tmp) ); - tmp.type = pCh->pMyBord->channelBtypes.bid_value[(pCh->port_index & (IP2_PORTS_PER_BOARD-1))/16]; - if (BID_HAS_654(tmp.type)) { - tmp.type = PORT_16650; - } else { - tmp.type = PORT_CIRRUS; - } - tmp.line = pCh->port_index; - tmp.port = pCh->pMyBord->i2eBase; - tmp.irq = ip2config.irq[pCh->port_index/64]; - tmp.flags = pCh->flags; - tmp.baud_base = pCh->BaudBase; - tmp.close_delay = pCh->ClosingDelay; - tmp.closing_wait = pCh->ClosingWaitTime; - tmp.custom_divisor = pCh->BaudDivisor; - return copy_to_user(retinfo,&tmp,sizeof(*retinfo)); -} - -/******************************************************************************/ -/* Function: SetSerialInfo() */ -/* Parameters: Pointer to channel structure */ -/* Pointer to old termios structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* This function provides support for setserial, which uses the TIOCSSERIAL */ -/* ioctl. Not all setserial parameters are relevant. If the user attempts to */ -/* change the IRQ, address or type of the port the ioctl fails. */ -/******************************************************************************/ -static int -set_serial_info( i2ChanStrPtr pCh, struct serial_struct __user *new_info ) -{ - struct serial_struct ns; - int old_flags, old_baud_divisor; - - if (copy_from_user(&ns, new_info, sizeof (ns))) - return -EFAULT; - - /* - * We don't allow setserial to change IRQ, board address, type or baud - * base. Also line nunber as such is meaningless but we use it for our - * array index so it is fixed also. - */ - if ( (ns.irq != ip2config.irq[pCh->port_index]) - || ((int) ns.port != ((int) (pCh->pMyBord->i2eBase))) - || (ns.baud_base != pCh->BaudBase) - || (ns.line != pCh->port_index) ) { - return -EINVAL; - } - - old_flags = pCh->flags; - old_baud_divisor = pCh->BaudDivisor; - - if ( !capable(CAP_SYS_ADMIN) ) { - if ( ( ns.close_delay != pCh->ClosingDelay ) || - ( (ns.flags & ~ASYNC_USR_MASK) != - (pCh->flags & ~ASYNC_USR_MASK) ) ) { - return -EPERM; - } - - pCh->flags = (pCh->flags & ~ASYNC_USR_MASK) | - (ns.flags & ASYNC_USR_MASK); - pCh->BaudDivisor = ns.custom_divisor; - } else { - pCh->flags = (pCh->flags & ~ASYNC_FLAGS) | - (ns.flags & ASYNC_FLAGS); - pCh->BaudDivisor = ns.custom_divisor; - pCh->ClosingDelay = ns.close_delay * HZ/100; - pCh->ClosingWaitTime = ns.closing_wait * HZ/100; - } - - if ( ( (old_flags & ASYNC_SPD_MASK) != (pCh->flags & ASYNC_SPD_MASK) ) - || (old_baud_divisor != pCh->BaudDivisor) ) { - // Invalidate speed and reset parameters - set_params( pCh, NULL ); - } - - return 0; -} - -/******************************************************************************/ -/* Function: ip2_set_termios() */ -/* Parameters: Pointer to tty structure */ -/* Pointer to old termios structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static void -ip2_set_termios( PTTY tty, struct ktermios *old_termios ) -{ - i2ChanStrPtr pCh = (i2ChanStrPtr)tty->driver_data; - -#ifdef IP2DEBUG_IOCTL - printk (KERN_DEBUG "IP2: set termios %p\n", old_termios ); -#endif - - set_params( pCh, old_termios ); -} - -/******************************************************************************/ -/* Function: ip2_set_line_discipline() */ -/* Parameters: Pointer to tty structure */ -/* Returns: Nothing */ -/* */ -/* Description: Does nothing */ -/* */ -/* */ -/******************************************************************************/ -static void -ip2_set_line_discipline ( PTTY tty ) -{ -#ifdef IP2DEBUG_IOCTL - printk (KERN_DEBUG "IP2: set line discipline\n" ); -#endif - - ip2trace (((i2ChanStrPtr)tty->driver_data)->port_index, ITRC_IOCTL, 16, 0 ); - -} - -/******************************************************************************/ -/* Function: SetLine Characteristics() */ -/* Parameters: Pointer to channel structure */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* This routine is called to update the channel structure with the new line */ -/* characteristics, and send the appropriate commands to the board when they */ -/* change. */ -/******************************************************************************/ -static void -set_params( i2ChanStrPtr pCh, struct ktermios *o_tios ) -{ - tcflag_t cflag, iflag, lflag; - char stop_char, start_char; - struct ktermios dummy; - - lflag = pCh->pTTY->termios->c_lflag; - cflag = pCh->pTTY->termios->c_cflag; - iflag = pCh->pTTY->termios->c_iflag; - - if (o_tios == NULL) { - dummy.c_lflag = ~lflag; - dummy.c_cflag = ~cflag; - dummy.c_iflag = ~iflag; - o_tios = &dummy; - } - - { - switch ( cflag & CBAUD ) { - case B0: - i2QueueCommands( PTYPE_BYPASS, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN); - pCh->dataSetOut &= ~(I2_DTR | I2_RTS); - i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); - pCh->pTTY->termios->c_cflag |= (CBAUD & o_tios->c_cflag); - goto service_it; - break; - case B38400: - /* - * This is the speed that is overloaded with all the other high - * speeds, depending upon the flag settings. - */ - if ( ( pCh->flags & ASYNC_SPD_MASK ) == ASYNC_SPD_HI ) { - pCh->speed = CBR_57600; - } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI ) { - pCh->speed = CBR_115200; - } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST ) { - pCh->speed = CBR_C1; - } else { - pCh->speed = CBR_38400; - } - break; - case B50: pCh->speed = CBR_50; break; - case B75: pCh->speed = CBR_75; break; - case B110: pCh->speed = CBR_110; break; - case B134: pCh->speed = CBR_134; break; - case B150: pCh->speed = CBR_150; break; - case B200: pCh->speed = CBR_200; break; - case B300: pCh->speed = CBR_300; break; - case B600: pCh->speed = CBR_600; break; - case B1200: pCh->speed = CBR_1200; break; - case B1800: pCh->speed = CBR_1800; break; - case B2400: pCh->speed = CBR_2400; break; - case B4800: pCh->speed = CBR_4800; break; - case B9600: pCh->speed = CBR_9600; break; - case B19200: pCh->speed = CBR_19200; break; - case B57600: pCh->speed = CBR_57600; break; - case B115200: pCh->speed = CBR_115200; break; - case B153600: pCh->speed = CBR_153600; break; - case B230400: pCh->speed = CBR_230400; break; - case B307200: pCh->speed = CBR_307200; break; - case B460800: pCh->speed = CBR_460800; break; - case B921600: pCh->speed = CBR_921600; break; - default: pCh->speed = CBR_9600; break; - } - if ( pCh->speed == CBR_C1 ) { - // Process the custom speed parameters. - int bps = pCh->BaudBase / pCh->BaudDivisor; - if ( bps == 921600 ) { - pCh->speed = CBR_921600; - } else { - bps = bps/10; - i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_BAUD_DEF1(bps) ); - } - } - i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_SETBAUD(pCh->speed)); - - i2QueueCommands ( PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); - pCh->dataSetOut |= (I2_DTR | I2_RTS); - } - if ( (CSTOPB & cflag) ^ (CSTOPB & o_tios->c_cflag)) - { - i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, - CMD_SETSTOP( ( cflag & CSTOPB ) ? CST_2 : CST_1)); - } - if (((PARENB|PARODD) & cflag) ^ ((PARENB|PARODD) & o_tios->c_cflag)) - { - i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, - CMD_SETPAR( - (cflag & PARENB ? (cflag & PARODD ? CSP_OD : CSP_EV) : CSP_NP) - ) - ); - } - /* byte size and parity */ - if ( (CSIZE & cflag)^(CSIZE & o_tios->c_cflag)) - { - int datasize; - switch ( cflag & CSIZE ) { - case CS5: datasize = CSZ_5; break; - case CS6: datasize = CSZ_6; break; - case CS7: datasize = CSZ_7; break; - case CS8: datasize = CSZ_8; break; - default: datasize = CSZ_5; break; /* as per serial.c */ - } - i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, CMD_SETBITS(datasize) ); - } - /* Process CTS flow control flag setting */ - if ( (cflag & CRTSCTS) ) { - i2QueueCommands(PTYPE_INLINE, pCh, 100, - 2, CMD_CTSFL_ENAB, CMD_RTSFL_ENAB); - } else { - i2QueueCommands(PTYPE_INLINE, pCh, 100, - 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); - } - // - // Process XON/XOFF flow control flags settings - // - stop_char = STOP_CHAR(pCh->pTTY); - start_char = START_CHAR(pCh->pTTY); - - //////////// can't be \000 - if (stop_char == __DISABLED_CHAR ) - { - stop_char = ~__DISABLED_CHAR; - } - if (start_char == __DISABLED_CHAR ) - { - start_char = ~__DISABLED_CHAR; - } - ///////////////////////////////// - - if ( o_tios->c_cc[VSTART] != start_char ) - { - i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXON(start_char)); - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXON(start_char)); - } - if ( o_tios->c_cc[VSTOP] != stop_char ) - { - i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXOFF(stop_char)); - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXOFF(stop_char)); - } - if (stop_char == __DISABLED_CHAR ) - { - stop_char = ~__DISABLED_CHAR; //TEST123 - goto no_xoff; - } - if ((iflag & (IXOFF))^(o_tios->c_iflag & (IXOFF))) - { - if ( iflag & IXOFF ) { // Enable XOFF output flow control - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_XON)); - } else { // Disable XOFF output flow control -no_xoff: - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_NONE)); - } - } - if (start_char == __DISABLED_CHAR ) - { - goto no_xon; - } - if ((iflag & (IXON|IXANY)) ^ (o_tios->c_iflag & (IXON|IXANY))) - { - if ( iflag & IXON ) { - if ( iflag & IXANY ) { // Enable XON/XANY output flow control - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XANY)); - } else { // Enable XON output flow control - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XON)); - } - } else { // Disable XON output flow control -no_xon: - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_NONE)); - } - } - if ( (iflag & ISTRIP) ^ ( o_tios->c_iflag & (ISTRIP)) ) - { - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, - CMD_ISTRIP_OPT((iflag & ISTRIP ? 1 : 0))); - } - if ( (iflag & INPCK) ^ ( o_tios->c_iflag & (INPCK)) ) - { - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, - CMD_PARCHK((iflag & INPCK) ? CPK_ENAB : CPK_DSAB)); - } - - if ( (iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) - ^ ( o_tios->c_iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) ) - { - char brkrpt = 0; - char parrpt = 0; - - if ( iflag & IGNBRK ) { /* Ignore breaks altogether */ - /* Ignore breaks altogether */ - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_NREP); - } else { - if ( iflag & BRKINT ) { - if ( iflag & PARMRK ) { - brkrpt = 0x0a; // exception an inline triple - } else { - brkrpt = 0x1a; // exception and NULL - } - brkrpt |= 0x04; // flush input - } else { - if ( iflag & PARMRK ) { - brkrpt = 0x0b; //POSIX triple \0377 \0 \0 - } else { - brkrpt = 0x01; // Null only - } - } - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_REP(brkrpt)); - } - - if (iflag & IGNPAR) { - parrpt = 0x20; - /* would be 2 for not cirrus bug */ - /* would be 0x20 cept for cirrus bug */ - } else { - if ( iflag & PARMRK ) { - /* - * Replace error characters with 3-byte sequence (\0377,\0,char) - */ - parrpt = 0x04 ; - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_ISTRIP_OPT((char)0)); - } else { - parrpt = 0x03; - } - } - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SET_ERROR(parrpt)); - } - if (cflag & CLOCAL) { - // Status reporting fails for DCD if this is off - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_NREP); - pCh->flags &= ~ASYNC_CHECK_CD; - } else { - i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_REP); - pCh->flags |= ASYNC_CHECK_CD; - } - -service_it: - i2DrainOutput( pCh, 100 ); -} - -/******************************************************************************/ -/* IPL Device Section */ -/******************************************************************************/ - -/******************************************************************************/ -/* Function: ip2_ipl_read() */ -/* Parameters: Pointer to device inode */ -/* Pointer to file structure */ -/* Pointer to data */ -/* Number of bytes to read */ -/* Returns: Success or failure */ -/* */ -/* Description: Ugly */ -/* */ -/* */ -/******************************************************************************/ - -static -ssize_t -ip2_ipl_read(struct file *pFile, char __user *pData, size_t count, loff_t *off ) -{ - unsigned int minor = iminor(pFile->f_path.dentry->d_inode); - int rc = 0; - -#ifdef IP2DEBUG_IPL - printk (KERN_DEBUG "IP2IPL: read %p, %d bytes\n", pData, count ); -#endif - - switch( minor ) { - case 0: // IPL device - rc = -EINVAL; - break; - case 1: // Status dump - rc = -EINVAL; - break; - case 2: // Ping device - rc = -EINVAL; - break; - case 3: // Trace device - rc = DumpTraceBuffer ( pData, count ); - break; - case 4: // Trace device - rc = DumpFifoBuffer ( pData, count ); - break; - default: - rc = -ENODEV; - break; - } - return rc; -} - -static int -DumpFifoBuffer ( char __user *pData, int count ) -{ -#ifdef DEBUG_FIFO - int rc; - rc = copy_to_user(pData, DBGBuf, count); - - printk(KERN_DEBUG "Last index %d\n", I ); - - return count; -#endif /* DEBUG_FIFO */ - return 0; -} - -static int -DumpTraceBuffer ( char __user *pData, int count ) -{ -#ifdef IP2DEBUG_TRACE - int rc; - int dumpcount; - int chunk; - int *pIndex = (int __user *)pData; - - if ( count < (sizeof(int) * 6) ) { - return -EIO; - } - rc = put_user(tracewrap, pIndex ); - rc = put_user(TRACEMAX, ++pIndex ); - rc = put_user(tracestrip, ++pIndex ); - rc = put_user(tracestuff, ++pIndex ); - pData += sizeof(int) * 6; - count -= sizeof(int) * 6; - - dumpcount = tracestuff - tracestrip; - if ( dumpcount < 0 ) { - dumpcount += TRACEMAX; - } - if ( dumpcount > count ) { - dumpcount = count; - } - chunk = TRACEMAX - tracestrip; - if ( dumpcount > chunk ) { - rc = copy_to_user(pData, &tracebuf[tracestrip], - chunk * sizeof(tracebuf[0]) ); - pData += chunk * sizeof(tracebuf[0]); - tracestrip = 0; - chunk = dumpcount - chunk; - } else { - chunk = dumpcount; - } - rc = copy_to_user(pData, &tracebuf[tracestrip], - chunk * sizeof(tracebuf[0]) ); - tracestrip += chunk; - tracewrap = 0; - - rc = put_user(tracestrip, ++pIndex ); - rc = put_user(tracestuff, ++pIndex ); - - return dumpcount; -#else - return 0; -#endif -} - -/******************************************************************************/ -/* Function: ip2_ipl_write() */ -/* Parameters: */ -/* Pointer to file structure */ -/* Pointer to data */ -/* Number of bytes to write */ -/* Returns: Success or failure */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static ssize_t -ip2_ipl_write(struct file *pFile, const char __user *pData, size_t count, loff_t *off) -{ -#ifdef IP2DEBUG_IPL - printk (KERN_DEBUG "IP2IPL: write %p, %d bytes\n", pData, count ); -#endif - return 0; -} - -/******************************************************************************/ -/* Function: ip2_ipl_ioctl() */ -/* Parameters: Pointer to device inode */ -/* Pointer to file structure */ -/* Command */ -/* Argument */ -/* Returns: Success or failure */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static long -ip2_ipl_ioctl (struct file *pFile, UINT cmd, ULONG arg ) -{ - unsigned int iplminor = iminor(pFile->f_path.dentry->d_inode); - int rc = 0; - void __user *argp = (void __user *)arg; - ULONG __user *pIndex = argp; - i2eBordStrPtr pB = i2BoardPtrTable[iplminor / 4]; - i2ChanStrPtr pCh; - -#ifdef IP2DEBUG_IPL - printk (KERN_DEBUG "IP2IPL: ioctl cmd %d, arg %ld\n", cmd, arg ); -#endif - - mutex_lock(&ip2_mutex); - - switch ( iplminor ) { - case 0: // IPL device - rc = -EINVAL; - break; - case 1: // Status dump - case 5: - case 9: - case 13: - switch ( cmd ) { - case 64: /* Driver - ip2stat */ - rc = put_user(-1, pIndex++ ); - rc = put_user(irq_counter, pIndex++ ); - rc = put_user(bh_counter, pIndex++ ); - break; - - case 65: /* Board - ip2stat */ - if ( pB ) { - rc = copy_to_user(argp, pB, sizeof(i2eBordStr)); - rc = put_user(inb(pB->i2eStatus), - (ULONG __user *)(arg + (ULONG)(&pB->i2eStatus) - (ULONG)pB ) ); - } else { - rc = -ENODEV; - } - break; - - default: - if (cmd < IP2_MAX_PORTS) { - pCh = DevTable[cmd]; - if ( pCh ) - { - rc = copy_to_user(argp, pCh, sizeof(i2ChanStr)); - if (rc) - rc = -EFAULT; - } else { - rc = -ENODEV; - } - } else { - rc = -EINVAL; - } - } - break; - - case 2: // Ping device - rc = -EINVAL; - break; - case 3: // Trace device - /* - * akpm: This used to write a whole bunch of function addresses - * to userspace, which generated lots of put_user() warnings. - * I killed it all. Just return "success" and don't do - * anything. - */ - if (cmd == 1) - rc = 0; - else - rc = -EINVAL; - break; - - default: - rc = -ENODEV; - break; - } - mutex_unlock(&ip2_mutex); - return rc; -} - -/******************************************************************************/ -/* Function: ip2_ipl_open() */ -/* Parameters: Pointer to device inode */ -/* Pointer to file structure */ -/* Returns: Success or failure */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -static int -ip2_ipl_open( struct inode *pInode, struct file *pFile ) -{ - -#ifdef IP2DEBUG_IPL - printk (KERN_DEBUG "IP2IPL: open\n" ); -#endif - return 0; -} - -static int -proc_ip2mem_show(struct seq_file *m, void *v) -{ - i2eBordStrPtr pB; - i2ChanStrPtr pCh; - PTTY tty; - int i; - -#define FMTLINE "%3d: 0x%08x 0x%08x 0%011o 0%011o\n" -#define FMTLIN2 " 0x%04x 0x%04x tx flow 0x%x\n" -#define FMTLIN3 " 0x%04x 0x%04x rc flow\n" - - seq_printf(m,"\n"); - - for( i = 0; i < IP2_MAX_BOARDS; ++i ) { - pB = i2BoardPtrTable[i]; - if ( pB ) { - seq_printf(m,"board %d:\n",i); - seq_printf(m,"\tFifo rem: %d mty: %x outM %x\n", - pB->i2eFifoRemains,pB->i2eWaitingForEmptyFifo,pB->i2eOutMailWaiting); - } - } - - seq_printf(m,"#: tty flags, port flags, cflags, iflags\n"); - for (i=0; i < IP2_MAX_PORTS; i++) { - pCh = DevTable[i]; - if (pCh) { - tty = pCh->pTTY; - if (tty && tty->count) { - seq_printf(m,FMTLINE,i,(int)tty->flags,pCh->flags, - tty->termios->c_cflag,tty->termios->c_iflag); - - seq_printf(m,FMTLIN2, - pCh->outfl.asof,pCh->outfl.room,pCh->channelNeeds); - seq_printf(m,FMTLIN3,pCh->infl.asof,pCh->infl.room); - } - } - } - return 0; -} - -static int proc_ip2mem_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_ip2mem_show, NULL); -} - -static const struct file_operations ip2mem_proc_fops = { - .owner = THIS_MODULE, - .open = proc_ip2mem_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* - * This is the handler for /proc/tty/driver/ip2 - * - * This stretch of code has been largely plagerized from at least three - * different sources including ip2mkdev.c and a couple of other drivers. - * The bugs are all mine. :-) =mhw= - */ -static int ip2_proc_show(struct seq_file *m, void *v) -{ - int i, j, box; - int boxes = 0; - int ports = 0; - int tports = 0; - i2eBordStrPtr pB; - char *sep; - - seq_printf(m, "ip2info: 1.0 driver: %s\n", pcVersion); - seq_printf(m, "Driver: SMajor=%d CMajor=%d IMajor=%d MaxBoards=%d MaxBoxes=%d MaxPorts=%d\n", - IP2_TTY_MAJOR, IP2_CALLOUT_MAJOR, IP2_IPL_MAJOR, - IP2_MAX_BOARDS, ABS_MAX_BOXES, ABS_BIGGEST_BOX); - - for( i = 0; i < IP2_MAX_BOARDS; ++i ) { - /* This need to be reset for a board by board count... */ - boxes = 0; - pB = i2BoardPtrTable[i]; - if( pB ) { - switch( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) - { - case POR_ID_FIIEX: - seq_printf(m, "Board %d: EX ports=", i); - sep = ""; - for( box = 0; box < ABS_MAX_BOXES; ++box ) - { - ports = 0; - - if( pB->i2eChannelMap[box] != 0 ) ++boxes; - for( j = 0; j < ABS_BIGGEST_BOX; ++j ) - { - if( pB->i2eChannelMap[box] & 1<< j ) { - ++ports; - } - } - seq_printf(m, "%s%d", sep, ports); - sep = ","; - tports += ports; - } - seq_printf(m, " boxes=%d width=%d", boxes, pB->i2eDataWidth16 ? 16 : 8); - break; - - case POR_ID_II_4: - seq_printf(m, "Board %d: ISA-4 ports=4 boxes=1", i); - tports = ports = 4; - break; - - case POR_ID_II_8: - seq_printf(m, "Board %d: ISA-8-std ports=8 boxes=1", i); - tports = ports = 8; - break; - - case POR_ID_II_8R: - seq_printf(m, "Board %d: ISA-8-RJ11 ports=8 boxes=1", i); - tports = ports = 8; - break; - - default: - seq_printf(m, "Board %d: unknown", i); - /* Don't try and probe for minor numbers */ - tports = ports = 0; - } - - } else { - /* Don't try and probe for minor numbers */ - seq_printf(m, "Board %d: vacant", i); - tports = ports = 0; - } - - if( tports ) { - seq_puts(m, " minors="); - sep = ""; - for ( box = 0; box < ABS_MAX_BOXES; ++box ) - { - for ( j = 0; j < ABS_BIGGEST_BOX; ++j ) - { - if ( pB->i2eChannelMap[box] & (1 << j) ) - { - seq_printf(m, "%s%d", sep, - j + ABS_BIGGEST_BOX * - (box+i*ABS_MAX_BOXES)); - sep = ","; - } - } - } - } - seq_putc(m, '\n'); - } - return 0; - } - -static int ip2_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, ip2_proc_show, NULL); -} - -static const struct file_operations ip2_proc_fops = { - .owner = THIS_MODULE, - .open = ip2_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/******************************************************************************/ -/* Function: ip2trace() */ -/* Parameters: Value to add to trace buffer */ -/* Returns: Nothing */ -/* */ -/* Description: */ -/* */ -/* */ -/******************************************************************************/ -#ifdef IP2DEBUG_TRACE -void -ip2trace (unsigned short pn, unsigned char cat, unsigned char label, unsigned long codes, ...) -{ - long flags; - unsigned long *pCode = &codes; - union ip2breadcrumb bc; - i2ChanStrPtr pCh; - - - tracebuf[tracestuff++] = jiffies; - if ( tracestuff == TRACEMAX ) { - tracestuff = 0; - } - if ( tracestuff == tracestrip ) { - if ( ++tracestrip == TRACEMAX ) { - tracestrip = 0; - } - ++tracewrap; - } - - bc.hdr.port = 0xff & pn; - bc.hdr.cat = cat; - bc.hdr.codes = (unsigned char)( codes & 0xff ); - bc.hdr.label = label; - tracebuf[tracestuff++] = bc.value; - - for (;;) { - if ( tracestuff == TRACEMAX ) { - tracestuff = 0; - } - if ( tracestuff == tracestrip ) { - if ( ++tracestrip == TRACEMAX ) { - tracestrip = 0; - } - ++tracewrap; - } - - if ( !codes-- ) - break; - - tracebuf[tracestuff++] = *++pCode; - } -} -#endif - - -MODULE_LICENSE("GPL"); - -static struct pci_device_id ip2main_pci_tbl[] __devinitdata __used = { - { PCI_DEVICE(PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_IP2EX) }, - { } -}; - -MODULE_DEVICE_TABLE(pci, ip2main_pci_tbl); - -MODULE_FIRMWARE("intelliport2.bin"); diff --git a/drivers/char/ip2/ip2trace.h b/drivers/char/ip2/ip2trace.h deleted file mode 100644 index da20435dc8a6..000000000000 --- a/drivers/char/ip2/ip2trace.h +++ /dev/null @@ -1,42 +0,0 @@ - -// -union ip2breadcrumb -{ - struct { - unsigned char port, cat, codes, label; - } __attribute__ ((packed)) hdr; - unsigned long value; -}; - -#define ITRC_NO_PORT 0xFF -#define CHANN (pCh->port_index) - -#define ITRC_ERROR '!' -#define ITRC_INIT 'A' -#define ITRC_OPEN 'B' -#define ITRC_CLOSE 'C' -#define ITRC_DRAIN 'D' -#define ITRC_IOCTL 'E' -#define ITRC_FLUSH 'F' -#define ITRC_STATUS 'G' -#define ITRC_HANGUP 'H' -#define ITRC_INTR 'I' -#define ITRC_SFLOW 'J' -#define ITRC_SBCMD 'K' -#define ITRC_SICMD 'L' -#define ITRC_MODEM 'M' -#define ITRC_INPUT 'N' -#define ITRC_OUTPUT 'O' -#define ITRC_PUTC 'P' -#define ITRC_QUEUE 'Q' -#define ITRC_STFLW 'R' -#define ITRC_SFIFO 'S' -#define ITRC_VERIFY 'V' -#define ITRC_WRITE 'W' - -#define ITRC_ENTER 0x00 -#define ITRC_RETURN 0xFF - -#define ITRC_QUEUE_ROOM 2 -#define ITRC_QUEUE_CMD 6 - diff --git a/drivers/char/ip2/ip2types.h b/drivers/char/ip2/ip2types.h deleted file mode 100644 index 9d67b260b2f6..000000000000 --- a/drivers/char/ip2/ip2types.h +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* -* -* (c) 1998 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Driver constants and type definitions. -* -* NOTES: -* -*******************************************************************************/ -#ifndef IP2TYPES_H -#define IP2TYPES_H - -//************* -//* Constants * -//************* - -// Define some limits for this driver. Ports per board is a hardware limitation -// that will not change. Current hardware limits this to 64 ports per board. -// Boards per driver is a self-imposed limit. -// -#define IP2_MAX_BOARDS 4 -#define IP2_PORTS_PER_BOARD ABS_MOST_PORTS -#define IP2_MAX_PORTS (IP2_MAX_BOARDS*IP2_PORTS_PER_BOARD) - -#define ISA 0 -#define PCI 1 -#define EISA 2 - -//******************** -//* Type Definitions * -//******************** - -typedef struct tty_struct * PTTY; -typedef wait_queue_head_t PWAITQ; - -typedef unsigned char UCHAR; -typedef unsigned int UINT; -typedef unsigned short USHORT; -typedef unsigned long ULONG; - -typedef struct -{ - short irq[IP2_MAX_BOARDS]; - unsigned short addr[IP2_MAX_BOARDS]; - int type[IP2_MAX_BOARDS]; -#ifdef CONFIG_PCI - struct pci_dev *pci_dev[IP2_MAX_BOARDS]; -#endif -} ip2config_t; - -#endif diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c deleted file mode 100644 index 0b266272cccd..000000000000 --- a/drivers/char/istallion.c +++ /dev/null @@ -1,4507 +0,0 @@ -/*****************************************************************************/ - -/* - * 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. - * - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -/*****************************************************************************/ - -/* - * 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_ONBOARDE 7 -#define BRD_ECP 23 -#define BRD_ECPE 24 -#define BRD_ECPMC 25 -#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. - */ - -struct stlconf { - int brdtype; - int ioaddr1; - int ioaddr2; - unsigned long memaddr; - int irq; - int irqtype; -}; - -static unsigned int stli_nrbrds; - -/* stli_lock must NOT be taken holding brd_lock */ -static spinlock_t stli_lock; /* TTY logic lock */ -static spinlock_t brd_lock; /* Board logic lock */ - -/* - * 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; -static const struct tty_port_operations stli_port_ops; - -#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 ktermios stli_deftermios = { - .c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL), - .c_cc = INIT_C_CC, - .c_ispeed = 9600, - .c_ospeed = 9600, -}; - -/* - * 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 struct asystats stli_cdkstats; - -/*****************************************************************************/ - -static DEFINE_MUTEX(stli_brdslock); -static struct stlibrd *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 0 -#define BST_STARTED 1 -#define BST_PROBED 2 - -/* - * 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_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", - NULL, - "ONboard", - "ONboard-MC", - "ONboard-MC", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - 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", -}; - -/*****************************************************************************/ - -/* - * 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. - */ - -static struct stlibrdtype { - char *name; - int type; -} 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]"); - -#if STLI_EISAPROBE != 0 -/* - * 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); -#endif - -/* - * Define the Stallion PCI vendor and device IDs. - */ -#ifndef PCI_DEVICE_ID_ECRA -#define PCI_DEVICE_ID_ECRA 0x0004 -#endif - -static struct pci_device_id istallion_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECRA), }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, istallion_pci_tbl); - -static struct pci_driver stli_pcidriver; - -/*****************************************************************************/ - -/* - * 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) - -/*****************************************************************************/ - -/* - * Prototype all functions in this driver! - */ - -static int stli_parsebrd(struct stlconf *confp, char **argp); -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 int 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, unsigned int cmd, unsigned long arg); -static void stli_settermios(struct tty_struct *tty, struct ktermios *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 int 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_brdinit(struct stlibrd *brdp); -static int stli_startbrd(struct stlibrd *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 long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg); -static void stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp); -static void stli_poll(unsigned long arg); -static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp); -static int stli_initopen(struct tty_struct *tty, struct stlibrd *brdp, struct stliport *portp); -static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); -static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); -static int stli_setport(struct tty_struct *tty); -static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); -static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); -static void __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); -static void stli_dodelaycmd(struct stliport *portp, cdkctrl_t __iomem *cp); -static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp, asyport_t *pp, struct ktermios *tiosp); -static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts); -static long stli_mktiocm(unsigned long sigvalue); -static void stli_read(struct stlibrd *brdp, struct stliport *portp); -static int stli_getserial(struct stliport *portp, struct serial_struct __user *sp); -static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp); -static int stli_getbrdstats(combrd_t __user *bp); -static int stli_getportstats(struct tty_struct *tty, struct stliport *portp, comstats_t __user *cp); -static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp); -static int stli_clrportstats(struct stliport *portp, comstats_t __user *cp); -static int stli_getportstruct(struct stliport __user *arg); -static int stli_getbrdstruct(struct stlibrd __user *arg); -static struct stlibrd *stli_allocbrd(void); - -static void stli_ecpinit(struct stlibrd *brdp); -static void stli_ecpenable(struct stlibrd *brdp); -static void stli_ecpdisable(struct stlibrd *brdp); -static void __iomem *stli_ecpgetmemptr(struct stlibrd *brdp, unsigned long offset, int line); -static void stli_ecpreset(struct stlibrd *brdp); -static void stli_ecpintr(struct stlibrd *brdp); -static void stli_ecpeiinit(struct stlibrd *brdp); -static void stli_ecpeienable(struct stlibrd *brdp); -static void stli_ecpeidisable(struct stlibrd *brdp); -static void __iomem *stli_ecpeigetmemptr(struct stlibrd *brdp, unsigned long offset, int line); -static void stli_ecpeireset(struct stlibrd *brdp); -static void stli_ecpmcenable(struct stlibrd *brdp); -static void stli_ecpmcdisable(struct stlibrd *brdp); -static void __iomem *stli_ecpmcgetmemptr(struct stlibrd *brdp, unsigned long offset, int line); -static void stli_ecpmcreset(struct stlibrd *brdp); -static void stli_ecppciinit(struct stlibrd *brdp); -static void __iomem *stli_ecppcigetmemptr(struct stlibrd *brdp, unsigned long offset, int line); -static void stli_ecppcireset(struct stlibrd *brdp); - -static void stli_onbinit(struct stlibrd *brdp); -static void stli_onbenable(struct stlibrd *brdp); -static void stli_onbdisable(struct stlibrd *brdp); -static void __iomem *stli_onbgetmemptr(struct stlibrd *brdp, unsigned long offset, int line); -static void stli_onbreset(struct stlibrd *brdp); -static void stli_onbeinit(struct stlibrd *brdp); -static void stli_onbeenable(struct stlibrd *brdp); -static void stli_onbedisable(struct stlibrd *brdp); -static void __iomem *stli_onbegetmemptr(struct stlibrd *brdp, unsigned long offset, int line); -static void stli_onbereset(struct stlibrd *brdp); -static void stli_bbyinit(struct stlibrd *brdp); -static void __iomem *stli_bbygetmemptr(struct stlibrd *brdp, unsigned long offset, int line); -static void stli_bbyreset(struct stlibrd *brdp); -static void stli_stalinit(struct stlibrd *brdp); -static void __iomem *stli_stalgetmemptr(struct stlibrd *brdp, unsigned long offset, int line); -static void stli_stalreset(struct stlibrd *brdp); - -static struct stliport *stli_getport(unsigned int brdnr, unsigned int panelnr, unsigned int portnr); - -static int stli_initecp(struct stlibrd *brdp); -static int stli_initonb(struct stlibrd *brdp); -#if STLI_EISAPROBE != 0 -static int stli_eisamemprobe(struct stlibrd *brdp); -#endif -static int stli_initports(struct stlibrd *brdp); - -/*****************************************************************************/ - -/* - * 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 const struct file_operations stli_fsiomem = { - .owner = THIS_MODULE, - .read = stli_memread, - .write = stli_memwrite, - .unlocked_ioctl = stli_memioctl, - .llseek = default_llseek, -}; - -/*****************************************************************************/ - -/* - * 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; - -static void stli_cleanup_ports(struct stlibrd *brdp) -{ - struct stliport *portp; - unsigned int j; - struct tty_struct *tty; - - for (j = 0; j < STL_MAXPORTS; j++) { - portp = brdp->ports[j]; - if (portp != NULL) { - tty = tty_port_tty_get(&portp->port); - if (tty != NULL) { - tty_hangup(tty); - tty_kref_put(tty); - } - kfree(portp); - } - } -} - -/*****************************************************************************/ - -/* - * Parse the supplied argument string, into the board conf struct. - */ - -static int stli_parsebrd(struct stlconf *confp, char **argp) -{ - unsigned int i; - char *sp; - - if (argp[0] == 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(KERN_WARNING "istallion: unknown board name, %s?\n", argp[0]); - return 0; - } - - confp->brdtype = stli_brdstr[i].type; - if (argp[1] != NULL && *argp[1] != 0) - confp->ioaddr1 = simple_strtoul(argp[1], NULL, 0); - if (argp[2] != NULL && *argp[2] != 0) - confp->memaddr = simple_strtoul(argp[2], NULL, 0); - return(1); -} - -/*****************************************************************************/ - -/* - * 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. - * - * Locking: protected by the port mutex. - */ - -static int stli_activate(struct tty_port *port, struct tty_struct *tty) -{ - struct stliport *portp = container_of(port, struct stliport, port); - struct stlibrd *brdp = stli_brds[portp->brdnr]; - int rc; - - if ((rc = stli_initopen(tty, brdp, portp)) >= 0) - clear_bit(TTY_IO_ERROR, &tty->flags); - wake_up_interruptible(&portp->raw_wait); - return rc; -} - -static int stli_open(struct tty_struct *tty, struct file *filp) -{ - struct stlibrd *brdp; - struct stliport *portp; - unsigned int minordev, brdnr, portnr; - - minordev = tty->index; - brdnr = MINOR2BRD(minordev); - if (brdnr >= stli_nrbrds) - return -ENODEV; - brdp = stli_brds[brdnr]; - if (brdp == NULL) - return -ENODEV; - if (!test_bit(BST_STARTED, &brdp->state)) - return -ENODEV; - portnr = MINOR2PORT(minordev); - if (portnr > brdp->nrports) - return -ENODEV; - - portp = brdp->ports[portnr]; - if (portp == NULL) - return -ENODEV; - if (portp->devnr < 1) - return -ENODEV; - - tty->driver_data = portp; - return tty_port_open(&portp->port, tty, filp); -} - - -/*****************************************************************************/ - -static void stli_shutdown(struct tty_port *port) -{ - struct stlibrd *brdp; - unsigned long ftype; - unsigned long flags; - struct stliport *portp = container_of(port, struct stliport, port); - - if (portp->brdnr >= stli_nrbrds) - return; - brdp = stli_brds[portp->brdnr]; - if (brdp == NULL) - return; - - /* - * 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 (!test_bit(ST_CLOSING, &portp->state)) - stli_rawclose(brdp, portp, 0, 0); - - spin_lock_irqsave(&stli_lock, flags); - clear_bit(ST_TXBUSY, &portp->state); - clear_bit(ST_RXSTOP, &portp->state); - spin_unlock_irqrestore(&stli_lock, flags); - - ftype = FLUSHTX | FLUSHRX; - stli_cmdwait(brdp, portp, A_FLUSH, &ftype, sizeof(u32), 0); -} - -static void stli_close(struct tty_struct *tty, struct file *filp) -{ - struct stliport *portp = tty->driver_data; - unsigned long flags; - if (portp == NULL) - return; - spin_lock_irqsave(&stli_lock, flags); - /* Flush any internal buffering out first */ - if (tty == stli_txcooktty) - stli_flushchars(tty); - spin_unlock_irqrestore(&stli_lock, flags); - tty_port_close(&portp->port, tty, filp); -} - -/*****************************************************************************/ - -/* - * 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(struct tty_struct *tty, - struct stlibrd *brdp, struct stliport *portp) -{ - asynotify_t nt; - asyport_t aport; - int rc; - - 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; - - stli_mkasyport(tty, 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(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait) -{ - cdkhdr_t __iomem *hdrp; - cdkctrl_t __iomem *cp; - unsigned char __iomem *bits; - unsigned long flags; - int rc; - -/* - * Send a message to the slave to open this port. - */ - -/* - * 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_tty(portp->raw_wait, - !test_bit(ST_CLOSING, &portp->state)); - if (signal_pending(current)) { - 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. - */ - spin_lock_irqsave(&brd_lock, flags); - EBRDENABLE(brdp); - cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; - writel(arg, &cp->openarg); - writeb(1, &cp->open); - hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset + - portp->portidx; - writeb(readb(bits) | portp->portbit, bits); - EBRDDISABLE(brdp); - - if (wait == 0) { - spin_unlock_irqrestore(&brd_lock, 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); - spin_unlock_irqrestore(&brd_lock, flags); - - wait_event_interruptible_tty(portp->raw_wait, - !test_bit(ST_OPENING, &portp->state)); - if (signal_pending(current)) - rc = -ERESTARTSYS; - - 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(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait) -{ - cdkhdr_t __iomem *hdrp; - cdkctrl_t __iomem *cp; - unsigned char __iomem *bits; - unsigned long flags; - int rc; - -/* - * Slave is already closing this port. This can happen if a hangup - * occurs on this port. - */ - if (wait) { - wait_event_interruptible_tty(portp->raw_wait, - !test_bit(ST_CLOSING, &portp->state)); - if (signal_pending(current)) { - return -ERESTARTSYS; - } - } - -/* - * Write the close command into shared memory. - */ - spin_lock_irqsave(&brd_lock, flags); - EBRDENABLE(brdp); - cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; - writel(arg, &cp->closearg); - writeb(1, &cp->close); - hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset + - portp->portidx; - writeb(readb(bits) |portp->portbit, bits); - EBRDDISABLE(brdp); - - set_bit(ST_CLOSING, &portp->state); - spin_unlock_irqrestore(&brd_lock, flags); - - if (wait == 0) - return 0; - -/* - * Slave is in action, so now we must wait for the open acknowledgment - * to come back. - */ - rc = 0; - wait_event_interruptible_tty(portp->raw_wait, - !test_bit(ST_CLOSING, &portp->state)); - if (signal_pending(current)) - rc = -ERESTARTSYS; - - 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(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback) -{ - /* - * no need for wait_event_tty because clearing ST_CMDING cannot block - * on BTM - */ - wait_event_interruptible(portp->raw_wait, - !test_bit(ST_CMDING, &portp->state)); - if (signal_pending(current)) - 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)) - return -ERESTARTSYS; - - 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(struct tty_struct *tty) -{ - struct stliport *portp = tty->driver_data; - struct stlibrd *brdp; - asyport_t aport; - - if (portp == NULL) - return -ENODEV; - if (portp->brdnr >= stli_nrbrds) - return -ENODEV; - brdp = stli_brds[portp->brdnr]; - if (brdp == NULL) - return -ENODEV; - - stli_mkasyport(tty, portp, &aport, tty->termios); - return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)); -} - -/*****************************************************************************/ - -static int stli_carrier_raised(struct tty_port *port) -{ - struct stliport *portp = container_of(port, struct stliport, port); - return (portp->sigs & TIOCM_CD) ? 1 : 0; -} - -static void stli_dtr_rts(struct tty_port *port, int on) -{ - struct stliport *portp = container_of(port, struct stliport, port); - struct stlibrd *brdp = stli_brds[portp->brdnr]; - stli_mkasysigs(&portp->asig, on, on); - if (stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, - sizeof(asysigs_t), 0) < 0) - printk(KERN_WARNING "istallion: dtr set failed.\n"); -} - - -/*****************************************************************************/ - -/* - * 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) -{ - cdkasy_t __iomem *ap; - cdkhdr_t __iomem *hdrp; - unsigned char __iomem *bits; - unsigned char __iomem *shbuf; - unsigned char *chbuf; - struct stliport *portp; - struct stlibrd *brdp; - unsigned int len, stlen, head, tail, size; - unsigned long flags; - - if (tty == stli_txcooktty) - stli_flushchars(tty); - portp = tty->driver_data; - if (portp == NULL) - return 0; - if (portp->brdnr >= stli_nrbrds) - return 0; - brdp = stli_brds[portp->brdnr]; - if (brdp == NULL) - return 0; - chbuf = (unsigned char *) buf; - -/* - * All data is now local, shove as much as possible into shared memory. - */ - spin_lock_irqsave(&brd_lock, flags); - EBRDENABLE(brdp); - ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr); - head = (unsigned int) readw(&ap->txq.head); - tail = (unsigned int) readw(&ap->txq.tail); - if (tail != ((unsigned int) readw(&ap->txq.tail))) - tail = (unsigned int) readw(&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, (unsigned int)count); - count = 0; - shbuf = (char __iomem *) EBRDGETMEMPTR(brdp, portp->txoffset); - - while (len > 0) { - stlen = min(len, stlen); - memcpy_toio(shbuf + head, chbuf, stlen); - chbuf += stlen; - len -= stlen; - count += stlen; - head += stlen; - if (head >= size) { - head = 0; - stlen = tail; - } - } - - ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr); - writew(head, &ap->txq.head); - if (test_bit(ST_TXBUSY, &portp->state)) { - if (readl(&ap->changed.data) & DT_TXEMPTY) - writel(readl(&ap->changed.data) & ~DT_TXEMPTY, &ap->changed.data); - } - hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset + - portp->portidx; - writeb(readb(bits) | portp->portbit, bits); - set_bit(ST_TXBUSY, &portp->state); - EBRDDISABLE(brdp); - spin_unlock_irqrestore(&brd_lock, 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 int stli_putchar(struct tty_struct *tty, unsigned char ch) -{ - if (tty != stli_txcooktty) { - if (stli_txcooktty != NULL) - stli_flushchars(stli_txcooktty); - stli_txcooktty = tty; - } - - stli_txcookbuf[stli_txcooksize++] = ch; - return 0; -} - -/*****************************************************************************/ - -/* - * 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) -{ - cdkhdr_t __iomem *hdrp; - unsigned char __iomem *bits; - cdkasy_t __iomem *ap; - struct tty_struct *cooktty; - struct stliport *portp; - struct stlibrd *brdp; - unsigned int len, stlen, head, tail, size, count, cooksize; - unsigned char *buf; - unsigned char __iomem *shbuf; - unsigned long flags; - - cooksize = stli_txcooksize; - cooktty = stli_txcooktty; - stli_txcooksize = 0; - stli_txcookrealsize = 0; - stli_txcooktty = NULL; - - if (cooktty == NULL) - return; - if (tty != cooktty) - tty = cooktty; - if (cooksize == 0) - return; - - portp = tty->driver_data; - if (portp == NULL) - return; - if (portp->brdnr >= stli_nrbrds) - return; - brdp = stli_brds[portp->brdnr]; - if (brdp == NULL) - return; - - spin_lock_irqsave(&brd_lock, flags); - EBRDENABLE(brdp); - - ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr); - head = (unsigned int) readw(&ap->txq.head); - tail = (unsigned int) readw(&ap->txq.tail); - if (tail != ((unsigned int) readw(&ap->txq.tail))) - tail = (unsigned int) readw(&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 = EBRDGETMEMPTR(brdp, portp->txoffset); - buf = stli_txcookbuf; - - while (len > 0) { - stlen = min(len, stlen); - memcpy_toio(shbuf + head, buf, stlen); - buf += stlen; - len -= stlen; - count += stlen; - head += stlen; - if (head >= size) { - head = 0; - stlen = tail; - } - } - - ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr); - writew(head, &ap->txq.head); - - if (test_bit(ST_TXBUSY, &portp->state)) { - if (readl(&ap->changed.data) & DT_TXEMPTY) - writel(readl(&ap->changed.data) & ~DT_TXEMPTY, &ap->changed.data); - } - hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset + - portp->portidx; - writeb(readb(bits) | portp->portbit, bits); - set_bit(ST_TXBUSY, &portp->state); - - EBRDDISABLE(brdp); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -static int stli_writeroom(struct tty_struct *tty) -{ - cdkasyrq_t __iomem *rp; - struct stliport *portp; - struct stlibrd *brdp; - unsigned int head, tail, len; - unsigned long flags; - - if (tty == stli_txcooktty) { - if (stli_txcookrealsize != 0) { - len = stli_txcookrealsize - stli_txcooksize; - return len; - } - } - - portp = tty->driver_data; - if (portp == NULL) - return 0; - if (portp->brdnr >= stli_nrbrds) - return 0; - brdp = stli_brds[portp->brdnr]; - if (brdp == NULL) - return 0; - - spin_lock_irqsave(&brd_lock, flags); - EBRDENABLE(brdp); - rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->txq; - head = (unsigned int) readw(&rp->head); - tail = (unsigned int) readw(&rp->tail); - if (tail != ((unsigned int) readw(&rp->tail))) - tail = (unsigned int) readw(&rp->tail); - len = (head >= tail) ? (portp->txsize - (head - tail)) : (tail - head); - len--; - EBRDDISABLE(brdp); - spin_unlock_irqrestore(&brd_lock, 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) -{ - cdkasyrq_t __iomem *rp; - struct stliport *portp; - struct stlibrd *brdp; - unsigned int head, tail, len; - unsigned long flags; - - if (tty == stli_txcooktty) - stli_flushchars(tty); - portp = tty->driver_data; - if (portp == NULL) - return 0; - if (portp->brdnr >= stli_nrbrds) - return 0; - brdp = stli_brds[portp->brdnr]; - if (brdp == NULL) - return 0; - - spin_lock_irqsave(&brd_lock, flags); - EBRDENABLE(brdp); - rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->txq; - head = (unsigned int) readw(&rp->head); - tail = (unsigned int) readw(&rp->tail); - if (tail != ((unsigned int) readw(&rp->tail))) - tail = (unsigned int) readw(&rp->tail); - len = (head >= tail) ? (head - tail) : (portp->txsize - (tail - head)); - if ((len == 0) && test_bit(ST_TXBUSY, &portp->state)) - len = 1; - EBRDDISABLE(brdp); - spin_unlock_irqrestore(&brd_lock, flags); - - return len; -} - -/*****************************************************************************/ - -/* - * Generate the serial struct info. - */ - -static int stli_getserial(struct stliport *portp, struct serial_struct __user *sp) -{ - struct serial_struct sio; - struct stlibrd *brdp; - - memset(&sio, 0, sizeof(struct serial_struct)); - sio.type = PORT_UNKNOWN; - sio.line = portp->portnr; - sio.irq = 0; - sio.flags = portp->port.flags; - sio.baud_base = portp->baud_base; - sio.close_delay = portp->port.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 != 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(struct tty_struct *tty, struct serial_struct __user *sp) -{ - struct serial_struct sio; - int rc; - struct stliport *portp = tty->driver_data; - - 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->port.close_delay) || - ((sio.flags & ~ASYNC_USR_MASK) != - (portp->port.flags & ~ASYNC_USR_MASK))) - return -EPERM; - } - - portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) | - (sio.flags & ASYNC_USR_MASK); - portp->baud_base = sio.baud_base; - portp->port.close_delay = sio.close_delay; - portp->closing_wait = sio.closing_wait; - portp->custom_divisor = sio.custom_divisor; - - if ((rc = stli_setport(tty)) < 0) - return rc; - return 0; -} - -/*****************************************************************************/ - -static int stli_tiocmget(struct tty_struct *tty) -{ - struct stliport *portp = tty->driver_data; - struct stlibrd *brdp; - int rc; - - if (portp == NULL) - return -ENODEV; - if (portp->brdnr >= stli_nrbrds) - return 0; - brdp = stli_brds[portp->brdnr]; - if (brdp == 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, - unsigned int set, unsigned int clear) -{ - struct stliport *portp = tty->driver_data; - struct stlibrd *brdp; - int rts = -1, dtr = -1; - - if (portp == NULL) - return -ENODEV; - if (portp->brdnr >= stli_nrbrds) - return 0; - brdp = stli_brds[portp->brdnr]; - if (brdp == 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, unsigned int cmd, unsigned long arg) -{ - struct stliport *portp; - struct stlibrd *brdp; - int rc; - void __user *argp = (void __user *)arg; - - portp = tty->driver_data; - if (portp == NULL) - return -ENODEV; - if (portp->brdnr >= stli_nrbrds) - return 0; - brdp = stli_brds[portp->brdnr]; - if (brdp == 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 TIOCGSERIAL: - rc = stli_getserial(portp, argp); - break; - case TIOCSSERIAL: - rc = stli_setserial(tty, 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(tty); - break; - case COM_GETPORTSTATS: - rc = stli_getportstats(tty, 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 ktermios *old) -{ - struct stliport *portp; - struct stlibrd *brdp; - struct ktermios *tiosp; - asyport_t aport; - - portp = tty->driver_data; - if (portp == NULL) - return; - if (portp->brdnr >= stli_nrbrds) - return; - brdp = stli_brds[portp->brdnr]; - if (brdp == NULL) - return; - - tiosp = tty->termios; - - stli_mkasyport(tty, 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->port.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) -{ - struct stliport *portp = tty->driver_data; - if (portp == 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) -{ - struct stliport *portp = tty->driver_data; - if (portp == NULL) - return; - clear_bit(ST_RXSTOP, &portp->state); -} - -/*****************************************************************************/ - -/* - * Stop the transmitter. - */ - -static void stli_stop(struct tty_struct *tty) -{ -} - -/*****************************************************************************/ - -/* - * Start the transmitter again. - */ - -static void stli_start(struct tty_struct *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) -{ - struct stliport *portp = tty->driver_data; - tty_port_hangup(&portp->port); -} - -/*****************************************************************************/ - -/* - * 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) -{ - struct stliport *portp; - struct stlibrd *brdp; - unsigned long ftype, flags; - - portp = tty->driver_data; - if (portp == NULL) - return; - if (portp->brdnr >= stli_nrbrds) - return; - brdp = stli_brds[portp->brdnr]; - if (brdp == NULL) - return; - - spin_lock_irqsave(&brd_lock, flags); - if (tty == stli_txcooktty) { - stli_txcooktty = 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(u32), 0); - } - spin_unlock_irqrestore(&brd_lock, flags); - tty_wakeup(tty); -} - -/*****************************************************************************/ - -static int stli_breakctl(struct tty_struct *tty, int state) -{ - struct stlibrd *brdp; - struct stliport *portp; - long arg; - - portp = tty->driver_data; - if (portp == NULL) - return -EINVAL; - if (portp->brdnr >= stli_nrbrds) - return -EINVAL; - brdp = stli_brds[portp->brdnr]; - if (brdp == NULL) - return -EINVAL; - - arg = (state == -1) ? BREAKON : BREAKOFF; - stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0); - return 0; -} - -/*****************************************************************************/ - -static void stli_waituntilsent(struct tty_struct *tty, int timeout) -{ - struct stliport *portp; - unsigned long tend; - - portp = tty->driver_data; - if (portp == 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) -{ - struct stlibrd *brdp; - struct stliport *portp; - asyctrl_t actrl; - - portp = tty->driver_data; - if (portp == NULL) - return; - if (portp->brdnr >= stli_nrbrds) - return; - brdp = stli_brds[portp->brdnr]; - if (brdp == 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); -} - -static void stli_portinfo(struct seq_file *m, struct stlibrd *brdp, struct stliport *portp, int portnr) -{ - char *uart; - int rc; - - rc = stli_portcmdstats(NULL, portp); - - uart = "UNKNOWN"; - if (test_bit(BST_STARTED, &brdp->state)) { - switch (stli_comstats.hwid) { - case 0: uart = "2681"; break; - case 1: uart = "SC26198"; break; - default:uart = "CD1400"; break; - } - } - seq_printf(m, "%d: uart:%s ", portnr, uart); - - if (test_bit(BST_STARTED, &brdp->state) && rc >= 0) { - char sep; - - seq_printf(m, "tx:%d rx:%d", (int) stli_comstats.txtotal, - (int) stli_comstats.rxtotal); - - if (stli_comstats.rxframing) - seq_printf(m, " fe:%d", - (int) stli_comstats.rxframing); - if (stli_comstats.rxparity) - seq_printf(m, " pe:%d", - (int) stli_comstats.rxparity); - if (stli_comstats.rxbreaks) - seq_printf(m, " brk:%d", - (int) stli_comstats.rxbreaks); - if (stli_comstats.rxoverrun) - seq_printf(m, " oe:%d", - (int) stli_comstats.rxoverrun); - - sep = ' '; - if (stli_comstats.signals & TIOCM_RTS) { - seq_printf(m, "%c%s", sep, "RTS"); - sep = '|'; - } - if (stli_comstats.signals & TIOCM_CTS) { - seq_printf(m, "%c%s", sep, "CTS"); - sep = '|'; - } - if (stli_comstats.signals & TIOCM_DTR) { - seq_printf(m, "%c%s", sep, "DTR"); - sep = '|'; - } - if (stli_comstats.signals & TIOCM_CD) { - seq_printf(m, "%c%s", sep, "DCD"); - sep = '|'; - } - if (stli_comstats.signals & TIOCM_DSR) { - seq_printf(m, "%c%s", sep, "DSR"); - sep = '|'; - } - } - seq_putc(m, '\n'); -} - -/*****************************************************************************/ - -/* - * Port info, read from the /proc file system. - */ - -static int stli_proc_show(struct seq_file *m, void *v) -{ - struct stlibrd *brdp; - struct stliport *portp; - unsigned int brdnr, portnr, totalport; - - totalport = 0; - - seq_printf(m, "%s: version %s\n", stli_drvtitle, stli_drvversion); - -/* - * 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 == NULL) - continue; - if (brdp->state == 0) - continue; - - totalport = brdnr * STL_MAXPORTS; - for (portnr = 0; (portnr < brdp->nrports); portnr++, - totalport++) { - portp = brdp->ports[portnr]; - if (portp == NULL) - continue; - stli_portinfo(m, brdp, portp, totalport); - } - } - return 0; -} - -static int stli_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, stli_proc_show, NULL); -} - -static const struct file_operations stli_proc_fops = { - .owner = THIS_MODULE, - .open = stli_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/*****************************************************************************/ - -/* - * 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. - * - * The caller must hold the brd_lock (see also stli_sendcmd the usual - * entry point) - */ - -static void __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback) -{ - cdkhdr_t __iomem *hdrp; - cdkctrl_t __iomem *cp; - unsigned char __iomem *bits; - - if (test_bit(ST_CMDING, &portp->state)) { - printk(KERN_ERR "istallion: command already busy, cmd=%x!\n", - (int) cmd); - return; - } - - EBRDENABLE(brdp); - cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; - if (size > 0) { - memcpy_toio((void __iomem *) &(cp->args[0]), arg, size); - if (copyback) { - portp->argp = arg; - portp->argsize = size; - } - } - writel(0, &cp->status); - writel(cmd, &cp->cmd); - hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset + - portp->portidx; - writeb(readb(bits) | portp->portbit, bits); - set_bit(ST_CMDING, &portp->state); - EBRDDISABLE(brdp); -} - -static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback) -{ - unsigned long flags; - - spin_lock_irqsave(&brd_lock, flags); - __stli_sendcmd(brdp, portp, cmd, arg, size, copyback); - spin_unlock_irqrestore(&brd_lock, 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(struct stlibrd *brdp, struct stliport *portp) -{ - cdkasyrq_t __iomem *rp; - char __iomem *shbuf; - struct tty_struct *tty; - unsigned int head, tail, size; - unsigned int len, stlen; - - if (test_bit(ST_RXSTOP, &portp->state)) - return; - tty = tty_port_tty_get(&portp->port); - if (tty == NULL) - return; - - rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->rxq; - head = (unsigned int) readw(&rp->head); - if (head != ((unsigned int) readw(&rp->head))) - head = (unsigned int) readw(&rp->head); - tail = (unsigned int) readw(&rp->tail); - size = portp->rxsize; - if (head >= tail) { - len = head - tail; - stlen = len; - } else { - len = size - (tail - head); - stlen = size - tail; - } - - len = tty_buffer_request_room(tty, len); - - shbuf = (char __iomem *) EBRDGETMEMPTR(brdp, portp->rxoffset); - - while (len > 0) { - unsigned char *cptr; - - stlen = min(len, stlen); - tty_prepare_flip_string(tty, &cptr, stlen); - memcpy_fromio(cptr, shbuf + tail, stlen); - len -= stlen; - tail += stlen; - if (tail >= size) { - tail = 0; - stlen = head; - } - } - rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->rxq; - writew(tail, &rp->tail); - - if (head != tail) - set_bit(ST_RXING, &portp->state); - - tty_schedule_flip(tty); - tty_kref_put(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(struct stliport *portp, cdkctrl_t __iomem *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_toio((void __iomem *) &(cp->args[0]), (void *) &portp->asig, - sizeof(asysigs_t)); - writel(0, &cp->status); - writel(cmd, &cp->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_toio((void __iomem *) &(cp->args[0]), (void *) &cmd, sizeof(int)); - writel(0, &cp->status); - writel(A_FLUSH, &cp->cmd); - 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(struct stlibrd *brdp, struct stliport *portp) -{ - cdkasy_t __iomem *ap; - cdkctrl_t __iomem *cp; - struct tty_struct *tty; - asynotify_t nt; - unsigned long oldsigs; - int rc, donerx; - - ap = (cdkasy_t __iomem *) 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 = readl(&cp->openarg); - if (readb(&cp->open) == 0 && rc != 0) { - if (rc > 0) - rc--; - writel(0, &cp->openarg); - 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) readl(&cp->closearg); - if (readb(&cp->close) == 0 && rc != 0) { - if (rc > 0) - rc--; - writel(0, &cp->closearg); - 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 = readl(&cp->status); - if (readl(&cp->cmd) == 0 && rc != 0) { - if (rc > 0) - rc--; - if (portp->argp != NULL) { - memcpy_fromio(portp->argp, (void __iomem *) &(cp->args[0]), - portp->argsize); - portp->argp = NULL; - } - writel(0, &cp->status); - 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 = tty_port_tty_get(&portp->port); - - 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->port.open_wait); - if ((oldsigs & TIOCM_CD) && - ((portp->sigs & TIOCM_CD) == 0)) { - if (portp->port.flags & ASYNC_CHECK_CD) { - if (tty) - tty_hangup(tty); - } - } - } - - if (nt.data & DT_TXEMPTY) - clear_bit(ST_TXBUSY, &portp->state); - if (nt.data & (DT_TXEMPTY | DT_TXLOW)) { - if (tty != NULL) { - tty_wakeup(tty); - EBRDENABLE(brdp); - } - } - - if ((nt.data & DT_RXBREAK) && (portp->rxmarkmsk & BRKINT)) { - if (tty != NULL) { - tty_insert_flip_char(tty, 0, TTY_BREAK); - if (portp->port.flags & ASYNC_SAK) { - do_SAK(tty); - EBRDENABLE(brdp); - } - tty_schedule_flip(tty); - } - } - tty_kref_put(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(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp) -{ - struct stliport *portp; - unsigned char hostbits[(STL_MAXCHANS / 8) + 1]; - unsigned char slavebits[(STL_MAXCHANS / 8) + 1]; - unsigned char __iomem *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_fromio(&hostbits[0], (((unsigned char __iomem *) 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 = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - slavep = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset; - for (bitpos = 0; (bitpos < bitsize); bitpos++) { - if (readb(slavebits + bitpos)) - writeb(readb(slavep + bitpos) & ~slavebits[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) -{ - cdkhdr_t __iomem *hdrp; - struct stlibrd *brdp; - unsigned int brdnr; - - mod_timer(&stli_timerlist, STLI_TIMEOUT); - -/* - * Check each board and do any servicing required. - */ - for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) { - brdp = stli_brds[brdnr]; - if (brdp == NULL) - continue; - if (!test_bit(BST_STARTED, &brdp->state)) - continue; - - spin_lock(&brd_lock); - EBRDENABLE(brdp); - hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - if (readb(&hdrp->hostreq)) - stli_brdpoll(brdp, hdrp); - EBRDDISABLE(brdp); - spin_unlock(&brd_lock); - } -} - -/*****************************************************************************/ - -/* - * Translate the termios settings into the port setting structure of - * the slave. - */ - -static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp, - asyport_t *pp, struct ktermios *tiosp) -{ - memset(pp, 0, sizeof(asyport_t)); - -/* - * Start of by setting the baud, char size, parity and stop bit info. - */ - pp->baudout = tty_get_baud_rate(tty); - if ((tiosp->c_cflag & CBAUD) == B38400) { - if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - pp->baudout = 57600; - else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - pp->baudout = 115200; - else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - pp->baudout = 230400; - else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - pp->baudout = 460800; - else if ((portp->port.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->port.flags &= ~ASYNC_CHECK_CD; - else - portp->port.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) -{ - 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 = 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(struct stlibrd *brdp) -{ - struct stliport *portp; - unsigned int i, panelnr, panelport; - - for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) { - portp = kzalloc(sizeof(struct stliport), GFP_KERNEL); - if (!portp) { - printk(KERN_WARNING "istallion: failed to allocate port structure\n"); - continue; - } - tty_port_init(&portp->port); - portp->port.ops = &stli_port_ops; - portp->magic = STLI_PORTMAGIC; - portp->portnr = i; - portp->brdnr = brdp->brdnr; - portp->panelnr = panelnr; - portp->baud_base = STL_BAUDBASE; - portp->port.close_delay = STL_CLOSEDELAY; - portp->closing_wait = 30 * HZ; - init_waitqueue_head(&portp->port.open_wait); - init_waitqueue_head(&portp->port.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(struct stlibrd *brdp) -{ - unsigned long memconf; - - 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(struct stlibrd *brdp) -{ - outb(ECP_ATENABLE, (brdp->iobase + ECP_ATCONFR)); -} - -/*****************************************************************************/ - -static void stli_ecpdisable(struct stlibrd *brdp) -{ - outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR)); -} - -/*****************************************************************************/ - -static void __iomem *stli_ecpgetmemptr(struct stlibrd *brdp, unsigned long offset, int line) -{ - void __iomem *ptr; - unsigned char val; - - if (offset > brdp->memsize) { - printk(KERN_ERR "istallion: 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(struct stlibrd *brdp) -{ - outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR)); - udelay(10); - outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR)); - udelay(500); -} - -/*****************************************************************************/ - -static void stli_ecpintr(struct stlibrd *brdp) -{ - outb(0x1, brdp->iobase); -} - -/*****************************************************************************/ - -/* - * The following set of functions act on ECP EISA boards. - */ - -static void stli_ecpeiinit(struct stlibrd *brdp) -{ - unsigned long memconf; - - 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(struct stlibrd *brdp) -{ - outb(ECP_EIENABLE, (brdp->iobase + ECP_EICONFR)); -} - -/*****************************************************************************/ - -static void stli_ecpeidisable(struct stlibrd *brdp) -{ - outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR)); -} - -/*****************************************************************************/ - -static void __iomem *stli_ecpeigetmemptr(struct stlibrd *brdp, unsigned long offset, int line) -{ - void __iomem *ptr; - unsigned char val; - - if (offset > brdp->memsize) { - printk(KERN_ERR "istallion: 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(struct stlibrd *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(struct stlibrd *brdp) -{ - outb(ECP_MCENABLE, (brdp->iobase + ECP_MCCONFR)); -} - -/*****************************************************************************/ - -static void stli_ecpmcdisable(struct stlibrd *brdp) -{ - outb(ECP_MCDISABLE, (brdp->iobase + ECP_MCCONFR)); -} - -/*****************************************************************************/ - -static void __iomem *stli_ecpmcgetmemptr(struct stlibrd *brdp, unsigned long offset, int line) -{ - void __iomem *ptr; - unsigned char val; - - if (offset > brdp->memsize) { - printk(KERN_ERR "istallion: 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(struct stlibrd *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(struct stlibrd *brdp) -{ - outb(ECP_PCISTOP, (brdp->iobase + ECP_PCICONFR)); - udelay(10); - outb(0, (brdp->iobase + ECP_PCICONFR)); - udelay(500); -} - -/*****************************************************************************/ - -static void __iomem *stli_ecppcigetmemptr(struct stlibrd *brdp, unsigned long offset, int line) -{ - void __iomem *ptr; - unsigned char val; - - if (offset > brdp->memsize) { - printk(KERN_ERR "istallion: 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(struct stlibrd *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(struct stlibrd *brdp) -{ - unsigned long memconf; - - 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(struct stlibrd *brdp) -{ - outb((brdp->enabval | ONB_ATENABLE), (brdp->iobase + ONB_ATCONFR)); -} - -/*****************************************************************************/ - -static void stli_onbdisable(struct stlibrd *brdp) -{ - outb((brdp->enabval | ONB_ATDISABLE), (brdp->iobase + ONB_ATCONFR)); -} - -/*****************************************************************************/ - -static void __iomem *stli_onbgetmemptr(struct stlibrd *brdp, unsigned long offset, int line) -{ - void __iomem *ptr; - - if (offset > brdp->memsize) { - printk(KERN_ERR "istallion: 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(struct stlibrd *brdp) -{ - 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(struct stlibrd *brdp) -{ - unsigned long memconf; - - 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(struct stlibrd *brdp) -{ - outb(ONB_EIENABLE, (brdp->iobase + ONB_EICONFR)); -} - -/*****************************************************************************/ - -static void stli_onbedisable(struct stlibrd *brdp) -{ - outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR)); -} - -/*****************************************************************************/ - -static void __iomem *stli_onbegetmemptr(struct stlibrd *brdp, unsigned long offset, int line) -{ - void __iomem *ptr; - unsigned char val; - - if (offset > brdp->memsize) { - printk(KERN_ERR "istallion: 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(struct stlibrd *brdp) -{ - 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(struct stlibrd *brdp) -{ - outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR)); - udelay(10); - outb(0, (brdp->iobase + BBY_ATCONFR)); - mdelay(1000); - outb(0x1, brdp->iobase); - mdelay(1); -} - -/*****************************************************************************/ - -static void __iomem *stli_bbygetmemptr(struct stlibrd *brdp, unsigned long offset, int line) -{ - void __iomem *ptr; - unsigned char val; - - BUG_ON(offset > brdp->memsize); - - ptr = brdp->membase + (offset % BBY_PAGESIZE); - val = (unsigned char) (offset / BBY_PAGESIZE); - outb(val, (brdp->iobase + BBY_ATCONFR)); - return(ptr); -} - -/*****************************************************************************/ - -static void stli_bbyreset(struct stlibrd *brdp) -{ - 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(struct stlibrd *brdp) -{ - outb(0x1, brdp->iobase); - mdelay(1000); -} - -/*****************************************************************************/ - -static void __iomem *stli_stalgetmemptr(struct stlibrd *brdp, unsigned long offset, int line) -{ - BUG_ON(offset > brdp->memsize); - return brdp->membase + (offset % STAL_PAGESIZE); -} - -/*****************************************************************************/ - -static void stli_stalreset(struct stlibrd *brdp) -{ - u32 __iomem *vecp; - - vecp = (u32 __iomem *) (brdp->membase + 0x30); - writel(0xffff0000, vecp); - 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(struct stlibrd *brdp) -{ - cdkecpsig_t sig; - cdkecpsig_t __iomem *sigsp; - unsigned int status, nxtid; - char *name; - int retval, panelnr, nrports; - - if ((brdp->iobase == 0) || (brdp->memaddr == 0)) { - retval = -ENODEV; - goto err; - } - - brdp->iosize = ECP_IOSIZE; - - if (!request_region(brdp->iobase, brdp->iosize, "istallion")) { - retval = -EIO; - goto err; - } - -/* - * Based on the specific board type setup the common vars to access - * and enable shared memory. Set all board specific information now - * as well. - */ - switch (brdp->brdtype) { - case BRD_ECP: - brdp->memsize = ECP_MEMSIZE; - brdp->pagesize = ECP_ATPAGESIZE; - brdp->init = stli_ecpinit; - brdp->enable = stli_ecpenable; - brdp->reenable = stli_ecpenable; - brdp->disable = stli_ecpdisable; - brdp->getmemptr = stli_ecpgetmemptr; - brdp->intr = stli_ecpintr; - brdp->reset = stli_ecpreset; - name = "serial(EC8/64)"; - break; - - case BRD_ECPE: - brdp->memsize = ECP_MEMSIZE; - brdp->pagesize = ECP_EIPAGESIZE; - brdp->init = stli_ecpeiinit; - brdp->enable = stli_ecpeienable; - brdp->reenable = stli_ecpeienable; - brdp->disable = stli_ecpeidisable; - brdp->getmemptr = stli_ecpeigetmemptr; - brdp->intr = stli_ecpintr; - brdp->reset = stli_ecpeireset; - name = "serial(EC8/64-EI)"; - break; - - case BRD_ECPMC: - brdp->memsize = ECP_MEMSIZE; - brdp->pagesize = ECP_MCPAGESIZE; - brdp->init = NULL; - brdp->enable = stli_ecpmcenable; - brdp->reenable = stli_ecpmcenable; - brdp->disable = stli_ecpmcdisable; - brdp->getmemptr = stli_ecpmcgetmemptr; - brdp->intr = stli_ecpintr; - brdp->reset = stli_ecpmcreset; - name = "serial(EC8/64-MCA)"; - break; - - case BRD_ECPPCI: - 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: - retval = -EINVAL; - goto err_reg; - } - -/* - * 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_nocache(brdp->memaddr, brdp->memsize); - if (brdp->membase == NULL) { - retval = -ENOMEM; - goto err_reg; - } - -/* - * 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 __iomem *) EBRDGETMEMPTR(brdp, CDK_SIGADDR); - memcpy_fromio(&sig, sigsp, sizeof(cdkecpsig_t)); - EBRDDISABLE(brdp); - - if (sig.magic != cpu_to_le32(ECP_MAGIC)) { - retval = -ENODEV; - goto err_unmap; - } - -/* - * 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++; - } - - - set_bit(BST_FOUND, &brdp->state); - return 0; -err_unmap: - iounmap(brdp->membase); - brdp->membase = NULL; -err_reg: - release_region(brdp->iobase, brdp->iosize); -err: - return retval; -} - -/*****************************************************************************/ - -/* - * Try to find an ONboard, Brumby or Stallion board and initialize it. - * This handles only these board types. - */ - -static int stli_initonb(struct stlibrd *brdp) -{ - cdkonbsig_t sig; - cdkonbsig_t __iomem *sigsp; - char *name; - int i, retval; - -/* - * Do a basic sanity check on the IO and memory addresses. - */ - if (brdp->iobase == 0 || brdp->memaddr == 0) { - retval = -ENODEV; - goto err; - } - - brdp->iosize = ONB_IOSIZE; - - if (!request_region(brdp->iobase, brdp->iosize, "istallion")) { - retval = -EIO; - goto err; - } - -/* - * 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_ONBOARD2: - 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->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: - 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->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: - retval = -EINVAL; - goto err_reg; - } - -/* - * 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_nocache(brdp->memaddr, brdp->memsize); - if (brdp->membase == NULL) { - retval = -ENOMEM; - goto err_reg; - } - -/* - * 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 __iomem *) EBRDGETMEMPTR(brdp, CDK_SIGADDR); - memcpy_fromio(&sig, sigsp, sizeof(cdkonbsig_t)); - EBRDDISABLE(brdp); - - if (sig.magic0 != cpu_to_le16(ONB_MAGIC0) || - sig.magic1 != cpu_to_le16(ONB_MAGIC1) || - sig.magic2 != cpu_to_le16(ONB_MAGIC2) || - sig.magic3 != cpu_to_le16(ONB_MAGIC3)) { - retval = -ENODEV; - goto err_unmap; - } - -/* - * 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; - - - set_bit(BST_FOUND, &brdp->state); - return 0; -err_unmap: - iounmap(brdp->membase); - brdp->membase = NULL; -err_reg: - release_region(brdp->iobase, brdp->iosize); -err: - return retval; -} - -/*****************************************************************************/ - -/* - * 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(struct stlibrd *brdp) -{ - cdkhdr_t __iomem *hdrp; - cdkmem_t __iomem *memp; - cdkasy_t __iomem *ap; - unsigned long flags; - unsigned int portnr, nrdevs, i; - struct stliport *portp; - int rc = 0; - u32 memoff; - - spin_lock_irqsave(&brd_lock, flags); - EBRDENABLE(brdp); - hdrp = (cdkhdr_t __iomem *) 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__, readb(&hdrp->ver_release), readb(&hdrp->ver_modification), - readb(&hdrp->ver_fix), nrdevs, (int) readl(&hdrp->memp), readl(&hdrp->hostp), - readl(&hdrp->slavep)); -#endif - - if (nrdevs < (brdp->nrports + 1)) { - printk(KERN_ERR "istallion: 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; - memoff = readl(&hdrp->memp); - if (memoff > brdp->memsize) { - printk(KERN_ERR "istallion: corrupted shared memory region?\n"); - rc = -EIO; - goto stli_donestartup; - } - memp = (cdkmem_t __iomem *) EBRDGETMEMPTR(brdp, memoff); - if (readw(&memp->dtype) != TYP_ASYNCTRL) { - printk(KERN_ERR "istallion: 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 (readw(&memp->dtype) != TYP_ASYNC) - break; - portp = brdp->ports[portnr]; - if (portp == NULL) - break; - portp->devnr = i; - portp->addr = readl(&memp->offset); - portp->reqbit = (unsigned char) (0x1 << (i * 8 / nrdevs)); - portp->portidx = (unsigned char) (i / 8); - portp->portbit = (unsigned char) (0x1 << (i % 8)); - } - - writeb(0xff, &hdrp->slavereq); - -/* - * 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 == NULL) - break; - if (portp->addr == 0) - break; - ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr); - if (ap != NULL) { - portp->rxsize = readw(&ap->rxq.size); - portp->txsize = readw(&ap->txq.size); - portp->rxoffset = readl(&ap->rxq.offset); - portp->txoffset = readl(&ap->txq.offset); - } - } - -stli_donestartup: - EBRDDISABLE(brdp); - spin_unlock_irqrestore(&brd_lock, flags); - - if (rc == 0) - set_bit(BST_STARTED, &brdp->state); - - if (! stli_timeron) { - stli_timeron++; - mod_timer(&stli_timerlist, STLI_TIMEOUT); - } - - return rc; -} - -/*****************************************************************************/ - -/* - * Probe and initialize the specified board. - */ - -static int __devinit stli_brdinit(struct stlibrd *brdp) -{ - int retval; - - switch (brdp->brdtype) { - case BRD_ECP: - case BRD_ECPE: - case BRD_ECPMC: - case BRD_ECPPCI: - retval = stli_initecp(brdp); - break; - case BRD_ONBOARD: - case BRD_ONBOARDE: - case BRD_ONBOARD2: - case BRD_BRUMBY4: - case BRD_STALLION: - retval = stli_initonb(brdp); - break; - default: - printk(KERN_ERR "istallion: board=%d is unknown board " - "type=%d\n", brdp->brdnr, brdp->brdtype); - retval = -ENODEV; - } - - if (retval) - return retval; - - stli_initports(brdp); - printk(KERN_INFO "istallion: %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; -} - -#if STLI_EISAPROBE != 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(struct stlibrd *brdp) -{ - cdkecpsig_t ecpsig, __iomem *ecpsigp; - cdkonbsig_t onbsig, __iomem *onbsigp; - int i, foundit; - -/* - * 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 = ioremap_nocache(brdp->memaddr, brdp->memsize); - if (brdp->membase == NULL) - continue; - - if (brdp->brdtype == BRD_ECPE) { - ecpsigp = stli_ecpeigetmemptr(brdp, - CDK_SIGADDR, __LINE__); - memcpy_fromio(&ecpsig, ecpsigp, sizeof(cdkecpsig_t)); - if (ecpsig.magic == cpu_to_le32(ECP_MAGIC)) - foundit = 1; - } else { - onbsigp = (cdkonbsig_t __iomem *) stli_onbegetmemptr(brdp, - CDK_SIGADDR, __LINE__); - memcpy_fromio(&onbsig, onbsigp, sizeof(cdkonbsig_t)); - if ((onbsig.magic0 == cpu_to_le16(ONB_MAGIC0)) && - (onbsig.magic1 == cpu_to_le16(ONB_MAGIC1)) && - (onbsig.magic2 == cpu_to_le16(ONB_MAGIC2)) && - (onbsig.magic3 == cpu_to_le16(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 "istallion: failed to probe shared memory " - "region for %s in EISA slot=%d\n", - stli_brdnames[brdp->brdtype], (brdp->iobase >> 12)); - return -ENODEV; - } - return 0; -} -#endif - -static int stli_getbrdnr(void) -{ - unsigned 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; -} - -#if STLI_EISAPROBE != 0 -/*****************************************************************************/ - -/* - * 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 __init stli_findeisabrds(void) -{ - struct stlibrd *brdp; - unsigned int iobase, eid, i; - int brdnr, found = 0; - -/* - * Firstly check if this is an EISA system. If this is not an EISA system then - * don't bother going any further! - */ - if (EISA_bus) - 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 == 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()) == NULL) - return found ? : -ENOMEM; - brdnr = stli_getbrdnr(); - if (brdnr < 0) - return found ? : -ENOMEM; - brdp->brdnr = (unsigned int)brdnr; - 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)); - if (stli_brdinit(brdp) < 0) { - kfree(brdp); - continue; - } - - stli_brds[brdp->brdnr] = brdp; - found++; - - for (i = 0; i < brdp->nrports; i++) - tty_register_device(stli_serial, - brdp->brdnr * STL_MAXPORTS + i, NULL); - } - - return found; -} -#else -static inline int stli_findeisabrds(void) { return 0; } -#endif - -/*****************************************************************************/ - -/* - * Find the next available board number that is free. - */ - -/*****************************************************************************/ - -/* - * We have a Stallion board. Allocate a board structure and - * initialize it. Read its IO and MEMORY resources from PCI - * configuration space. - */ - -static int __devinit stli_pciprobe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct stlibrd *brdp; - unsigned int i; - int brdnr, retval = -EIO; - - retval = pci_enable_device(pdev); - if (retval) - goto err; - brdp = stli_allocbrd(); - if (brdp == NULL) { - retval = -ENOMEM; - goto err; - } - mutex_lock(&stli_brdslock); - brdnr = stli_getbrdnr(); - if (brdnr < 0) { - printk(KERN_INFO "istallion: too many boards found, " - "maximum supported %d\n", STL_MAXBRDS); - mutex_unlock(&stli_brdslock); - retval = -EIO; - goto err_fr; - } - brdp->brdnr = (unsigned int)brdnr; - stli_brds[brdp->brdnr] = brdp; - mutex_unlock(&stli_brdslock); - brdp->brdtype = BRD_ECPPCI; -/* - * We have all resources from the board, so lets setup the actual - * board structure now. - */ - brdp->iobase = pci_resource_start(pdev, 3); - brdp->memaddr = pci_resource_start(pdev, 2); - retval = stli_brdinit(brdp); - if (retval) - goto err_null; - - set_bit(BST_PROBED, &brdp->state); - pci_set_drvdata(pdev, brdp); - - EBRDENABLE(brdp); - brdp->enable = NULL; - brdp->disable = NULL; - - for (i = 0; i < brdp->nrports; i++) - tty_register_device(stli_serial, brdp->brdnr * STL_MAXPORTS + i, - &pdev->dev); - - return 0; -err_null: - stli_brds[brdp->brdnr] = NULL; -err_fr: - kfree(brdp); -err: - return retval; -} - -static void __devexit stli_pciremove(struct pci_dev *pdev) -{ - struct stlibrd *brdp = pci_get_drvdata(pdev); - - stli_cleanup_ports(brdp); - - iounmap(brdp->membase); - if (brdp->iosize > 0) - release_region(brdp->iobase, brdp->iosize); - - stli_brds[brdp->brdnr] = NULL; - kfree(brdp); -} - -static struct pci_driver stli_pcidriver = { - .name = "istallion", - .id_table = istallion_pci_tbl, - .probe = stli_pciprobe, - .remove = __devexit_p(stli_pciremove) -}; -/*****************************************************************************/ - -/* - * Allocate a new board structure. Fill out the basic info in it. - */ - -static struct stlibrd *stli_allocbrd(void) -{ - struct stlibrd *brdp; - - brdp = kzalloc(sizeof(struct stlibrd), GFP_KERNEL); - if (!brdp) { - printk(KERN_ERR "istallion: failed to allocate memory " - "(size=%Zd)\n", sizeof(struct stlibrd)); - return NULL; - } - brdp->magic = STLI_BOARDMAGIC; - return brdp; -} - -/*****************************************************************************/ - -/* - * Scan through all the boards in the configuration and see what we - * can find. - */ - -static int __init stli_initbrds(void) -{ - struct stlibrd *brdp, *nxtbrdp; - struct stlconf conf; - unsigned int i, j, found = 0; - int retval; - - for (stli_nrbrds = 0; stli_nrbrds < ARRAY_SIZE(stli_brdsp); - stli_nrbrds++) { - memset(&conf, 0, sizeof(conf)); - if (stli_parsebrd(&conf, stli_brdsp[stli_nrbrds]) == 0) - continue; - if ((brdp = stli_allocbrd()) == NULL) - continue; - brdp->brdnr = stli_nrbrds; - brdp->brdtype = conf.brdtype; - brdp->iobase = conf.ioaddr1; - brdp->memaddr = conf.memaddr; - if (stli_brdinit(brdp) < 0) { - kfree(brdp); - continue; - } - stli_brds[brdp->brdnr] = brdp; - found++; - - for (i = 0; i < brdp->nrports; i++) - tty_register_device(stli_serial, - brdp->brdnr * STL_MAXPORTS + i, NULL); - } - - retval = stli_findeisabrds(); - if (retval > 0) - found += retval; - -/* - * 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 == NULL) - continue; - for (j = i + 1; (j < stli_nrbrds); j++) { - nxtbrdp = stli_brds[j]; - if (nxtbrdp == 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 == NULL) - continue; - if (test_bit(BST_FOUND, &brdp->state)) { - EBRDENABLE(brdp); - brdp->enable = NULL; - brdp->disable = NULL; - } - } - } - - retval = pci_register_driver(&stli_pcidriver); - if (retval && found == 0) { - printk(KERN_ERR "Neither isa nor eisa cards found nor pci " - "driver can be registered!\n"); - goto err; - } - - return 0; -err: - return retval; -} - -/*****************************************************************************/ - -/* - * 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 __iomem *memptr; - struct stlibrd *brdp; - unsigned int brdnr; - int size, n; - void *p; - loff_t off = *offp; - - brdnr = iminor(fp->f_path.dentry->d_inode); - if (brdnr >= stli_nrbrds) - return -ENODEV; - brdp = stli_brds[brdnr]; - if (brdp == NULL) - return -ENODEV; - if (brdp->state == 0) - return -ENODEV; - if (off >= brdp->memsize || off + count < off) - return 0; - - size = min(count, (size_t)(brdp->memsize - off)); - - /* - * Copy the data a page at a time - */ - - p = (void *)__get_free_page(GFP_KERNEL); - if(p == NULL) - return -ENOMEM; - - while (size > 0) { - spin_lock_irqsave(&brd_lock, flags); - EBRDENABLE(brdp); - memptr = EBRDGETMEMPTR(brdp, off); - n = min(size, (int)(brdp->pagesize - (((unsigned long) off) % brdp->pagesize))); - n = min(n, (int)PAGE_SIZE); - memcpy_fromio(p, memptr, n); - EBRDDISABLE(brdp); - spin_unlock_irqrestore(&brd_lock, flags); - if (copy_to_user(buf, p, n)) { - count = -EFAULT; - goto out; - } - off += n; - buf += n; - size -= n; - } -out: - *offp = off; - free_page((unsigned long)p); - 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 :-) - * - * FIXME: copy under lock - */ - -static ssize_t stli_memwrite(struct file *fp, const char __user *buf, size_t count, loff_t *offp) -{ - unsigned long flags; - void __iomem *memptr; - struct stlibrd *brdp; - char __user *chbuf; - unsigned int brdnr; - int size, n; - void *p; - loff_t off = *offp; - - brdnr = iminor(fp->f_path.dentry->d_inode); - - if (brdnr >= stli_nrbrds) - return -ENODEV; - brdp = stli_brds[brdnr]; - if (brdp == NULL) - return -ENODEV; - if (brdp->state == 0) - return -ENODEV; - if (off >= brdp->memsize || off + count < off) - return 0; - - chbuf = (char __user *) buf; - size = min(count, (size_t)(brdp->memsize - off)); - - /* - * Copy the data a page at a time - */ - - p = (void *)__get_free_page(GFP_KERNEL); - if(p == NULL) - return -ENOMEM; - - while (size > 0) { - n = min(size, (int)(brdp->pagesize - (((unsigned long) off) % brdp->pagesize))); - n = min(n, (int)PAGE_SIZE); - if (copy_from_user(p, chbuf, n)) { - if (count == 0) - count = -EFAULT; - goto out; - } - spin_lock_irqsave(&brd_lock, flags); - EBRDENABLE(brdp); - memptr = EBRDGETMEMPTR(brdp, off); - memcpy_toio(memptr, p, n); - EBRDDISABLE(brdp); - spin_unlock_irqrestore(&brd_lock, flags); - off += n; - chbuf += n; - size -= n; - } -out: - free_page((unsigned long) p); - *offp = off; - return count; -} - -/*****************************************************************************/ - -/* - * Return the board stats structure to user app. - */ - -static int stli_getbrdstats(combrd_t __user *bp) -{ - struct stlibrd *brdp; - unsigned 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 == 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 struct stliport *stli_getport(unsigned int brdnr, unsigned int panelnr, - unsigned int portnr) -{ - struct stlibrd *brdp; - unsigned int i; - - if (brdnr >= STL_MAXBRDS) - return NULL; - brdp = stli_brds[brdnr]; - if (brdp == NULL) - return NULL; - for (i = 0; (i < panelnr); i++) - portnr += brdp->panels[i]; - if (portnr >= brdp->nrports) - return 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(struct tty_struct *tty, struct stliport *portp) -{ - unsigned long flags; - struct stlibrd *brdp; - int rc; - - memset(&stli_comstats, 0, sizeof(comstats_t)); - - if (portp == NULL) - return -ENODEV; - brdp = stli_brds[portp->brdnr]; - if (brdp == NULL) - return -ENODEV; - - mutex_lock(&portp->port.mutex); - if (test_bit(BST_STARTED, &brdp->state)) { - if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, - &stli_cdkstats, sizeof(asystats_t), 1)) < 0) { - mutex_unlock(&portp->port.mutex); - 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->port.flags; - - spin_lock_irqsave(&brd_lock, flags); - if (tty != NULL) { - if (portp->port.tty == tty) { - stli_comstats.ttystate = tty->flags; - stli_comstats.rxbuffered = -1; - if (tty->termios != NULL) { - stli_comstats.cflags = tty->termios->c_cflag; - stli_comstats.iflags = tty->termios->c_iflag; - stli_comstats.oflags = tty->termios->c_oflag; - stli_comstats.lflags = tty->termios->c_lflag; - } - } - } - spin_unlock_irqrestore(&brd_lock, 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); - mutex_unlock(&portp->port.mutex); - - 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(struct tty_struct *tty, struct stliport *portp, - comstats_t __user *cp) -{ - struct stlibrd *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(tty, 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(struct stliport *portp, comstats_t __user *cp) -{ - struct stlibrd *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; - - mutex_lock(&portp->port.mutex); - - if (test_bit(BST_STARTED, &brdp->state)) { - if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, NULL, 0, 0)) < 0) { - mutex_unlock(&portp->port.mutex); - return rc; - } - } - - memset(&stli_comstats, 0, sizeof(comstats_t)); - stli_comstats.brd = portp->brdnr; - stli_comstats.panel = portp->panelnr; - stli_comstats.port = portp->portnr; - mutex_unlock(&portp->port.mutex); - - 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(struct stliport __user *arg) -{ - struct stliport stli_dummyport; - struct stliport *portp; - - if (copy_from_user(&stli_dummyport, arg, sizeof(struct stliport))) - 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(struct stliport))) - return -EFAULT; - return 0; -} - -/*****************************************************************************/ - -/* - * Return the entire driver board structure to a user app. - */ - -static int stli_getbrdstruct(struct stlibrd __user *arg) -{ - struct stlibrd stli_dummybrd; - struct stlibrd *brdp; - - if (copy_from_user(&stli_dummybrd, arg, sizeof(struct stlibrd))) - return -EFAULT; - if (stli_dummybrd.brdnr >= STL_MAXBRDS) - return -ENODEV; - brdp = stli_brds[stli_dummybrd.brdnr]; - if (!brdp) - return -ENODEV; - if (copy_to_user(arg, brdp, sizeof(struct stlibrd))) - 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 long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg) -{ - struct stlibrd *brdp; - int brdnr, rc, done; - void __user *argp = (void __user *)arg; - -/* - * First up handle the board independent ioctls. - */ - done = 0; - rc = 0; - - switch (cmd) { - case COM_GETPORTSTATS: - rc = stli_getportstats(NULL, 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(fp->f_dentry->d_inode); - 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: - clear_bit(BST_STARTED, &brdp->state); - break; - case STL_BRESET: - clear_bit(BST_STARTED, &brdp->state); - EBRDRESET(brdp); - if (stli_shared == 0) { - if (brdp->reenable != NULL) - (* brdp->reenable)(brdp); - } - break; - default: - rc = -ENOIOCTLCMD; - break; - } - return rc; -} - -static const 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, - .tiocmget = stli_tiocmget, - .tiocmset = stli_tiocmset, - .proc_fops = &stli_proc_fops, -}; - -static const struct tty_port_operations stli_port_ops = { - .carrier_raised = stli_carrier_raised, - .dtr_rts = stli_dtr_rts, - .activate = stli_activate, - .shutdown = stli_shutdown, -}; - -/*****************************************************************************/ -/* - * Loadable module initialization stuff. - */ - -static void istallion_cleanup_isa(void) -{ - struct stlibrd *brdp; - unsigned int j; - - for (j = 0; (j < stli_nrbrds); j++) { - if ((brdp = stli_brds[j]) == NULL || - test_bit(BST_PROBED, &brdp->state)) - continue; - - stli_cleanup_ports(brdp); - - iounmap(brdp->membase); - if (brdp->iosize > 0) - release_region(brdp->iobase, brdp->iosize); - kfree(brdp); - stli_brds[j] = NULL; - } -} - -static int __init istallion_module_init(void) -{ - unsigned int i; - int retval; - - printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion); - - spin_lock_init(&stli_lock); - spin_lock_init(&brd_lock); - - stli_txcookbuf = kmalloc(STLI_TXBUFSIZE, GFP_KERNEL); - if (!stli_txcookbuf) { - printk(KERN_ERR "istallion: failed to allocate memory " - "(size=%d)\n", STLI_TXBUFSIZE); - retval = -ENOMEM; - goto err; - } - - stli_serial = alloc_tty_driver(STL_MAXBRDS * STL_MAXPORTS); - if (!stli_serial) { - retval = -ENOMEM; - goto err_free; - } - - 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_DRIVER_DYNAMIC_DEV; - tty_set_operations(stli_serial, &stli_ops); - - retval = tty_register_driver(stli_serial); - if (retval) { - printk(KERN_ERR "istallion: failed to register serial driver\n"); - goto err_ttyput; - } - - retval = stli_initbrds(); - if (retval) - goto err_ttyunr; - -/* - * 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. - */ - retval = register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem); - if (retval) { - printk(KERN_ERR "istallion: failed to register serial memory " - "device\n"); - goto err_deinit; - } - - istallion_class = class_create(THIS_MODULE, "staliomem"); - for (i = 0; i < 4; i++) - device_create(istallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i), - NULL, "staliomem%d", i); - - return 0; -err_deinit: - pci_unregister_driver(&stli_pcidriver); - istallion_cleanup_isa(); -err_ttyunr: - tty_unregister_driver(stli_serial); -err_ttyput: - put_tty_driver(stli_serial); -err_free: - kfree(stli_txcookbuf); -err: - return retval; -} - -/*****************************************************************************/ - -static void __exit istallion_module_exit(void) -{ - unsigned int j; - - printk(KERN_INFO "Unloading %s: version %s\n", stli_drvtitle, - stli_drvversion); - - if (stli_timeron) { - stli_timeron = 0; - del_timer_sync(&stli_timerlist); - } - - unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"); - - for (j = 0; j < 4; j++) - device_destroy(istallion_class, MKDEV(STL_SIOMEMMAJOR, j)); - class_destroy(istallion_class); - - pci_unregister_driver(&stli_pcidriver); - istallion_cleanup_isa(); - - tty_unregister_driver(stli_serial); - put_tty_driver(stli_serial); - - kfree(stli_txcookbuf); -} - -module_init(istallion_module_init); -module_exit(istallion_module_exit); diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c deleted file mode 100644 index 602643a40b4b..000000000000 --- a/drivers/char/riscom8.c +++ /dev/null @@ -1,1560 +0,0 @@ -/* - * linux/drivers/char/riscom.c -- RISCom/8 multiport serial driver. - * - * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) - * - * This code is loosely based on the Linux serial driver, written by - * Linus Torvalds, Theodore T'so and others. The RISCom/8 card - * programming info was obtained from various drivers for other OSes - * (FreeBSD, ISC, etc), but no source code from those drivers were - * directly included in this driver. - * - * - * 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. - * - * Revision 1.1 - * - * ChangeLog: - * Arnaldo Carvalho de Melo - 27-Jun-2001 - * - get rid of check_region and several cleanups - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "riscom8.h" -#include "riscom8_reg.h" - -/* Am I paranoid or not ? ;-) */ -#define RISCOM_PARANOIA_CHECK - -/* - * Crazy InteliCom/8 boards sometimes have swapped CTS & DSR signals. - * You can slightly speed up things by #undefing the following option, - * if you are REALLY sure that your board is correct one. - */ - -#define RISCOM_BRAIN_DAMAGED_CTS - -/* - * The following defines are mostly for testing purposes. But if you need - * some nice reporting in your syslog, you can define them also. - */ -#undef RC_REPORT_FIFO -#undef RC_REPORT_OVERRUN - - -#define RISCOM_LEGAL_FLAGS \ - (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \ - ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ - ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) - -static struct tty_driver *riscom_driver; - -static DEFINE_SPINLOCK(riscom_lock); - -static struct riscom_board rc_board[RC_NBOARD] = { - { - .base = RC_IOBASE1, - }, - { - .base = RC_IOBASE2, - }, - { - .base = RC_IOBASE3, - }, - { - .base = RC_IOBASE4, - }, -}; - -static struct riscom_port rc_port[RC_NBOARD * RC_NPORT]; - -/* RISCom/8 I/O ports addresses (without address translation) */ -static unsigned short rc_ioport[] = { -#if 1 - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c, -#else - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c, 0x10, - 0x11, 0x12, 0x18, 0x28, 0x31, 0x32, 0x39, 0x3a, 0x40, 0x41, 0x61, 0x62, - 0x63, 0x64, 0x6b, 0x70, 0x71, 0x78, 0x7a, 0x7b, 0x7f, 0x100, 0x101 -#endif -}; -#define RC_NIOPORT ARRAY_SIZE(rc_ioport) - - -static int rc_paranoia_check(struct riscom_port const *port, - char *name, const char *routine) -{ -#ifdef RISCOM_PARANOIA_CHECK - static const char badmagic[] = KERN_INFO - "rc: Warning: bad riscom port magic number for device %s in %s\n"; - static const char badinfo[] = KERN_INFO - "rc: Warning: null riscom port for device %s in %s\n"; - - if (!port) { - printk(badinfo, name, routine); - return 1; - } - if (port->magic != RISCOM8_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - -/* - * - * Service functions for RISCom/8 driver. - * - */ - -/* Get board number from pointer */ -static inline int board_No(struct riscom_board const *bp) -{ - return bp - rc_board; -} - -/* Get port number from pointer */ -static inline int port_No(struct riscom_port const *port) -{ - return RC_PORT(port - rc_port); -} - -/* Get pointer to board from pointer to port */ -static inline struct riscom_board *port_Board(struct riscom_port const *port) -{ - return &rc_board[RC_BOARD(port - rc_port)]; -} - -/* Input Byte from CL CD180 register */ -static inline unsigned char rc_in(struct riscom_board const *bp, - unsigned short reg) -{ - return inb(bp->base + RC_TO_ISA(reg)); -} - -/* Output Byte to CL CD180 register */ -static inline void rc_out(struct riscom_board const *bp, unsigned short reg, - unsigned char val) -{ - outb(val, bp->base + RC_TO_ISA(reg)); -} - -/* Wait for Channel Command Register ready */ -static void rc_wait_CCR(struct riscom_board const *bp) -{ - unsigned long delay; - - /* FIXME: need something more descriptive then 100000 :) */ - for (delay = 100000; delay; delay--) - if (!rc_in(bp, CD180_CCR)) - return; - - printk(KERN_INFO "rc%d: Timeout waiting for CCR.\n", board_No(bp)); -} - -/* - * RISCom/8 probe functions. - */ - -static int rc_request_io_range(struct riscom_board * const bp) -{ - int i; - - for (i = 0; i < RC_NIOPORT; i++) - if (!request_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1, - "RISCom/8")) { - goto out_release; - } - return 0; -out_release: - printk(KERN_INFO "rc%d: Skipping probe at 0x%03x. IO address in use.\n", - board_No(bp), bp->base); - while (--i >= 0) - release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1); - return 1; -} - -static void rc_release_io_range(struct riscom_board * const bp) -{ - int i; - - for (i = 0; i < RC_NIOPORT; i++) - release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1); -} - -/* Reset and setup CD180 chip */ -static void __init rc_init_CD180(struct riscom_board const *bp) -{ - unsigned long flags; - - spin_lock_irqsave(&riscom_lock, flags); - - rc_out(bp, RC_CTOUT, 0); /* Clear timeout */ - rc_wait_CCR(bp); /* Wait for CCR ready */ - rc_out(bp, CD180_CCR, CCR_HARDRESET); /* Reset CD180 chip */ - spin_unlock_irqrestore(&riscom_lock, flags); - msleep(50); /* Delay 0.05 sec */ - spin_lock_irqsave(&riscom_lock, flags); - rc_out(bp, CD180_GIVR, RC_ID); /* Set ID for this chip */ - rc_out(bp, CD180_GICR, 0); /* Clear all bits */ - rc_out(bp, CD180_PILR1, RC_ACK_MINT); /* Prio for modem intr */ - rc_out(bp, CD180_PILR2, RC_ACK_TINT); /* Prio for tx intr */ - rc_out(bp, CD180_PILR3, RC_ACK_RINT); /* Prio for rx intr */ - - /* Setting up prescaler. We need 4 ticks per 1 ms */ - rc_out(bp, CD180_PPRH, (RC_OSCFREQ/(1000000/RISCOM_TPS)) >> 8); - rc_out(bp, CD180_PPRL, (RC_OSCFREQ/(1000000/RISCOM_TPS)) & 0xff); - - spin_unlock_irqrestore(&riscom_lock, flags); -} - -/* Main probing routine, also sets irq. */ -static int __init rc_probe(struct riscom_board *bp) -{ - unsigned char val1, val2; - int irqs = 0; - int retries; - - bp->irq = 0; - - if (rc_request_io_range(bp)) - return 1; - - /* Are the I/O ports here ? */ - rc_out(bp, CD180_PPRL, 0x5a); - outb(0xff, 0x80); - val1 = rc_in(bp, CD180_PPRL); - rc_out(bp, CD180_PPRL, 0xa5); - outb(0x00, 0x80); - val2 = rc_in(bp, CD180_PPRL); - - if ((val1 != 0x5a) || (val2 != 0xa5)) { - printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not found.\n", - board_No(bp), bp->base); - goto out_release; - } - - /* It's time to find IRQ for this board */ - for (retries = 0; retries < 5 && irqs <= 0; retries++) { - irqs = probe_irq_on(); - rc_init_CD180(bp); /* Reset CD180 chip */ - rc_out(bp, CD180_CAR, 2); /* Select port 2 */ - rc_wait_CCR(bp); - rc_out(bp, CD180_CCR, CCR_TXEN); /* Enable transmitter */ - rc_out(bp, CD180_IER, IER_TXRDY);/* Enable tx empty intr */ - msleep(50); - irqs = probe_irq_off(irqs); - val1 = rc_in(bp, RC_BSR); /* Get Board Status reg */ - val2 = rc_in(bp, RC_ACK_TINT); /* ACK interrupt */ - rc_init_CD180(bp); /* Reset CD180 again */ - - if ((val1 & RC_BSR_TINT) || (val2 != (RC_ID | GIVR_IT_TX))) { - printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not " - "found.\n", board_No(bp), bp->base); - goto out_release; - } - } - - if (irqs <= 0) { - printk(KERN_ERR "rc%d: Can't find IRQ for RISCom/8 board " - "at 0x%03x.\n", board_No(bp), bp->base); - goto out_release; - } - bp->irq = irqs; - bp->flags |= RC_BOARD_PRESENT; - - printk(KERN_INFO "rc%d: RISCom/8 Rev. %c board detected at " - "0x%03x, IRQ %d.\n", - board_No(bp), - (rc_in(bp, CD180_GFRCR) & 0x0f) + 'A', /* Board revision */ - bp->base, bp->irq); - - return 0; -out_release: - rc_release_io_range(bp); - return 1; -} - -/* - * - * Interrupt processing routines. - * - */ - -static struct riscom_port *rc_get_port(struct riscom_board const *bp, - unsigned char const *what) -{ - unsigned char channel; - struct riscom_port *port; - - channel = rc_in(bp, CD180_GICR) >> GICR_CHAN_OFF; - if (channel < CD180_NCH) { - port = &rc_port[board_No(bp) * RC_NPORT + channel]; - if (port->port.flags & ASYNC_INITIALIZED) - return port; - } - printk(KERN_ERR "rc%d: %s interrupt from invalid port %d\n", - board_No(bp), what, channel); - return NULL; -} - -static void rc_receive_exc(struct riscom_board const *bp) -{ - struct riscom_port *port; - struct tty_struct *tty; - unsigned char status; - unsigned char ch, flag; - - port = rc_get_port(bp, "Receive"); - if (port == NULL) - return; - - tty = tty_port_tty_get(&port->port); - -#ifdef RC_REPORT_OVERRUN - status = rc_in(bp, CD180_RCSR); - if (status & RCSR_OE) - port->overrun++; - status &= port->mark_mask; -#else - status = rc_in(bp, CD180_RCSR) & port->mark_mask; -#endif - ch = rc_in(bp, CD180_RDR); - if (!status) - goto out; - if (status & RCSR_TOUT) { - printk(KERN_WARNING "rc%d: port %d: Receiver timeout. " - "Hardware problems ?\n", - board_No(bp), port_No(port)); - goto out; - - } else if (status & RCSR_BREAK) { - printk(KERN_INFO "rc%d: port %d: Handling break...\n", - board_No(bp), port_No(port)); - flag = TTY_BREAK; - if (tty && (port->port.flags & ASYNC_SAK)) - do_SAK(tty); - - } else if (status & RCSR_PE) - flag = TTY_PARITY; - - else if (status & RCSR_FE) - flag = TTY_FRAME; - - else if (status & RCSR_OE) - flag = TTY_OVERRUN; - else - flag = TTY_NORMAL; - - if (tty) { - tty_insert_flip_char(tty, ch, flag); - tty_flip_buffer_push(tty); - } -out: - tty_kref_put(tty); -} - -static void rc_receive(struct riscom_board const *bp) -{ - struct riscom_port *port; - struct tty_struct *tty; - unsigned char count; - - port = rc_get_port(bp, "Receive"); - if (port == NULL) - return; - - tty = tty_port_tty_get(&port->port); - - count = rc_in(bp, CD180_RDCR); - -#ifdef RC_REPORT_FIFO - port->hits[count > 8 ? 9 : count]++; -#endif - - while (count--) { - u8 ch = rc_in(bp, CD180_RDR); - if (tty) - tty_insert_flip_char(tty, ch, TTY_NORMAL); - } - if (tty) { - tty_flip_buffer_push(tty); - tty_kref_put(tty); - } -} - -static void rc_transmit(struct riscom_board const *bp) -{ - struct riscom_port *port; - struct tty_struct *tty; - unsigned char count; - - port = rc_get_port(bp, "Transmit"); - if (port == NULL) - return; - - tty = tty_port_tty_get(&port->port); - - if (port->IER & IER_TXEMPTY) { - /* FIFO drained */ - rc_out(bp, CD180_CAR, port_No(port)); - port->IER &= ~IER_TXEMPTY; - rc_out(bp, CD180_IER, port->IER); - goto out; - } - - if ((port->xmit_cnt <= 0 && !port->break_length) - || (tty && (tty->stopped || tty->hw_stopped))) { - rc_out(bp, CD180_CAR, port_No(port)); - port->IER &= ~IER_TXRDY; - rc_out(bp, CD180_IER, port->IER); - goto out; - } - - if (port->break_length) { - if (port->break_length > 0) { - if (port->COR2 & COR2_ETC) { - rc_out(bp, CD180_TDR, CD180_C_ESC); - rc_out(bp, CD180_TDR, CD180_C_SBRK); - port->COR2 &= ~COR2_ETC; - } - count = min_t(int, port->break_length, 0xff); - rc_out(bp, CD180_TDR, CD180_C_ESC); - rc_out(bp, CD180_TDR, CD180_C_DELAY); - rc_out(bp, CD180_TDR, count); - port->break_length -= count; - if (port->break_length == 0) - port->break_length--; - } else { - rc_out(bp, CD180_TDR, CD180_C_ESC); - rc_out(bp, CD180_TDR, CD180_C_EBRK); - rc_out(bp, CD180_COR2, port->COR2); - rc_wait_CCR(bp); - rc_out(bp, CD180_CCR, CCR_CORCHG2); - port->break_length = 0; - } - goto out; - } - - count = CD180_NFIFO; - do { - rc_out(bp, CD180_TDR, port->port.xmit_buf[port->xmit_tail++]); - port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); - if (--port->xmit_cnt <= 0) - break; - } while (--count > 0); - - if (port->xmit_cnt <= 0) { - rc_out(bp, CD180_CAR, port_No(port)); - port->IER &= ~IER_TXRDY; - rc_out(bp, CD180_IER, port->IER); - } - if (tty && port->xmit_cnt <= port->wakeup_chars) - tty_wakeup(tty); -out: - tty_kref_put(tty); -} - -static void rc_check_modem(struct riscom_board const *bp) -{ - struct riscom_port *port; - struct tty_struct *tty; - unsigned char mcr; - - port = rc_get_port(bp, "Modem"); - if (port == NULL) - return; - - tty = tty_port_tty_get(&port->port); - - mcr = rc_in(bp, CD180_MCR); - if (mcr & MCR_CDCHG) { - if (rc_in(bp, CD180_MSVR) & MSVR_CD) - wake_up_interruptible(&port->port.open_wait); - else if (tty) - tty_hangup(tty); - } - -#ifdef RISCOM_BRAIN_DAMAGED_CTS - if (mcr & MCR_CTSCHG) { - if (rc_in(bp, CD180_MSVR) & MSVR_CTS) { - port->IER |= IER_TXRDY; - if (tty) { - tty->hw_stopped = 0; - if (port->xmit_cnt <= port->wakeup_chars) - tty_wakeup(tty); - } - } else { - if (tty) - tty->hw_stopped = 1; - port->IER &= ~IER_TXRDY; - } - rc_out(bp, CD180_IER, port->IER); - } - if (mcr & MCR_DSRCHG) { - if (rc_in(bp, CD180_MSVR) & MSVR_DSR) { - port->IER |= IER_TXRDY; - if (tty) { - tty->hw_stopped = 0; - if (port->xmit_cnt <= port->wakeup_chars) - tty_wakeup(tty); - } - } else { - if (tty) - tty->hw_stopped = 1; - port->IER &= ~IER_TXRDY; - } - rc_out(bp, CD180_IER, port->IER); - } -#endif /* RISCOM_BRAIN_DAMAGED_CTS */ - - /* Clear change bits */ - rc_out(bp, CD180_MCR, 0); - tty_kref_put(tty); -} - -/* The main interrupt processing routine */ -static irqreturn_t rc_interrupt(int dummy, void *dev_id) -{ - unsigned char status; - unsigned char ack; - struct riscom_board *bp = dev_id; - unsigned long loop = 0; - int handled = 0; - - if (!(bp->flags & RC_BOARD_ACTIVE)) - return IRQ_NONE; - - while ((++loop < 16) && ((status = ~(rc_in(bp, RC_BSR))) & - (RC_BSR_TOUT | RC_BSR_TINT | - RC_BSR_MINT | RC_BSR_RINT))) { - handled = 1; - if (status & RC_BSR_TOUT) - printk(KERN_WARNING "rc%d: Got timeout. Hardware " - "error?\n", board_No(bp)); - else if (status & RC_BSR_RINT) { - ack = rc_in(bp, RC_ACK_RINT); - if (ack == (RC_ID | GIVR_IT_RCV)) - rc_receive(bp); - else if (ack == (RC_ID | GIVR_IT_REXC)) - rc_receive_exc(bp); - else - printk(KERN_WARNING "rc%d: Bad receive ack " - "0x%02x.\n", - board_No(bp), ack); - } else if (status & RC_BSR_TINT) { - ack = rc_in(bp, RC_ACK_TINT); - if (ack == (RC_ID | GIVR_IT_TX)) - rc_transmit(bp); - else - printk(KERN_WARNING "rc%d: Bad transmit ack " - "0x%02x.\n", - board_No(bp), ack); - } else /* if (status & RC_BSR_MINT) */ { - ack = rc_in(bp, RC_ACK_MINT); - if (ack == (RC_ID | GIVR_IT_MODEM)) - rc_check_modem(bp); - else - printk(KERN_WARNING "rc%d: Bad modem ack " - "0x%02x.\n", - board_No(bp), ack); - } - rc_out(bp, CD180_EOIR, 0); /* Mark end of interrupt */ - rc_out(bp, RC_CTOUT, 0); /* Clear timeout flag */ - } - return IRQ_RETVAL(handled); -} - -/* - * Routines for open & close processing. - */ - -/* Called with disabled interrupts */ -static int rc_setup_board(struct riscom_board *bp) -{ - int error; - - if (bp->flags & RC_BOARD_ACTIVE) - return 0; - - error = request_irq(bp->irq, rc_interrupt, IRQF_DISABLED, - "RISCom/8", bp); - if (error) - return error; - - rc_out(bp, RC_CTOUT, 0); /* Just in case */ - bp->DTR = ~0; - rc_out(bp, RC_DTR, bp->DTR); /* Drop DTR on all ports */ - - bp->flags |= RC_BOARD_ACTIVE; - - return 0; -} - -/* Called with disabled interrupts */ -static void rc_shutdown_board(struct riscom_board *bp) -{ - if (!(bp->flags & RC_BOARD_ACTIVE)) - return; - - bp->flags &= ~RC_BOARD_ACTIVE; - - free_irq(bp->irq, NULL); - - bp->DTR = ~0; - rc_out(bp, RC_DTR, bp->DTR); /* Drop DTR on all ports */ - -} - -/* - * Setting up port characteristics. - * Must be called with disabled interrupts - */ -static void rc_change_speed(struct tty_struct *tty, struct riscom_board *bp, - struct riscom_port *port) -{ - unsigned long baud; - long tmp; - unsigned char cor1 = 0, cor3 = 0; - unsigned char mcor1 = 0, mcor2 = 0; - - port->IER = 0; - port->COR2 = 0; - port->MSVR = MSVR_RTS; - - baud = tty_get_baud_rate(tty); - - /* Select port on the board */ - rc_out(bp, CD180_CAR, port_No(port)); - - if (!baud) { - /* Drop DTR & exit */ - bp->DTR |= (1u << port_No(port)); - rc_out(bp, RC_DTR, bp->DTR); - return; - } else { - /* Set DTR on */ - bp->DTR &= ~(1u << port_No(port)); - rc_out(bp, RC_DTR, bp->DTR); - } - - /* - * Now we must calculate some speed depended things - */ - - /* Set baud rate for port */ - tmp = (((RC_OSCFREQ + baud/2) / baud + - CD180_TPC/2) / CD180_TPC); - - rc_out(bp, CD180_RBPRH, (tmp >> 8) & 0xff); - rc_out(bp, CD180_TBPRH, (tmp >> 8) & 0xff); - rc_out(bp, CD180_RBPRL, tmp & 0xff); - rc_out(bp, CD180_TBPRL, tmp & 0xff); - - baud = (baud + 5) / 10; /* Estimated CPS */ - - /* Two timer ticks seems enough to wakeup something like SLIP driver */ - tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO; - port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? - SERIAL_XMIT_SIZE - 1 : tmp); - - /* Receiver timeout will be transmission time for 1.5 chars */ - tmp = (RISCOM_TPS + RISCOM_TPS/2 + baud/2) / baud; - tmp = (tmp > 0xff) ? 0xff : tmp; - rc_out(bp, CD180_RTPR, tmp); - - switch (C_CSIZE(tty)) { - case CS5: - cor1 |= COR1_5BITS; - break; - case CS6: - cor1 |= COR1_6BITS; - break; - case CS7: - cor1 |= COR1_7BITS; - break; - case CS8: - cor1 |= COR1_8BITS; - break; - } - if (C_CSTOPB(tty)) - cor1 |= COR1_2SB; - - cor1 |= COR1_IGNORE; - if (C_PARENB(tty)) { - cor1 |= COR1_NORMPAR; - if (C_PARODD(tty)) - cor1 |= COR1_ODDP; - if (I_INPCK(tty)) - cor1 &= ~COR1_IGNORE; - } - /* Set marking of some errors */ - port->mark_mask = RCSR_OE | RCSR_TOUT; - if (I_INPCK(tty)) - port->mark_mask |= RCSR_FE | RCSR_PE; - if (I_BRKINT(tty) || I_PARMRK(tty)) - port->mark_mask |= RCSR_BREAK; - if (I_IGNPAR(tty)) - port->mark_mask &= ~(RCSR_FE | RCSR_PE); - if (I_IGNBRK(tty)) { - port->mark_mask &= ~RCSR_BREAK; - if (I_IGNPAR(tty)) - /* Real raw mode. Ignore all */ - port->mark_mask &= ~RCSR_OE; - } - /* Enable Hardware Flow Control */ - if (C_CRTSCTS(tty)) { -#ifdef RISCOM_BRAIN_DAMAGED_CTS - port->IER |= IER_DSR | IER_CTS; - mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; - mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; - tty->hw_stopped = !(rc_in(bp, CD180_MSVR) & - (MSVR_CTS|MSVR_DSR)); -#else - port->COR2 |= COR2_CTSAE; -#endif - } - /* Enable Software Flow Control. FIXME: I'm not sure about this */ - /* Some people reported that it works, but I still doubt */ - if (I_IXON(tty)) { - port->COR2 |= COR2_TXIBE; - cor3 |= (COR3_FCT | COR3_SCDE); - if (I_IXANY(tty)) - port->COR2 |= COR2_IXM; - rc_out(bp, CD180_SCHR1, START_CHAR(tty)); - rc_out(bp, CD180_SCHR2, STOP_CHAR(tty)); - rc_out(bp, CD180_SCHR3, START_CHAR(tty)); - rc_out(bp, CD180_SCHR4, STOP_CHAR(tty)); - } - if (!C_CLOCAL(tty)) { - /* Enable CD check */ - port->IER |= IER_CD; - mcor1 |= MCOR1_CDZD; - mcor2 |= MCOR2_CDOD; - } - - if (C_CREAD(tty)) - /* Enable receiver */ - port->IER |= IER_RXD; - - /* Set input FIFO size (1-8 bytes) */ - cor3 |= RISCOM_RXFIFO; - /* Setting up CD180 channel registers */ - rc_out(bp, CD180_COR1, cor1); - rc_out(bp, CD180_COR2, port->COR2); - rc_out(bp, CD180_COR3, cor3); - /* Make CD180 know about registers change */ - rc_wait_CCR(bp); - rc_out(bp, CD180_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); - /* Setting up modem option registers */ - rc_out(bp, CD180_MCOR1, mcor1); - rc_out(bp, CD180_MCOR2, mcor2); - /* Enable CD180 transmitter & receiver */ - rc_wait_CCR(bp); - rc_out(bp, CD180_CCR, CCR_TXEN | CCR_RXEN); - /* Enable interrupts */ - rc_out(bp, CD180_IER, port->IER); - /* And finally set RTS on */ - rc_out(bp, CD180_MSVR, port->MSVR); -} - -/* Must be called with interrupts enabled */ -static int rc_activate_port(struct tty_port *port, struct tty_struct *tty) -{ - struct riscom_port *rp = container_of(port, struct riscom_port, port); - struct riscom_board *bp = port_Board(rp); - unsigned long flags; - - if (tty_port_alloc_xmit_buf(port) < 0) - return -ENOMEM; - - spin_lock_irqsave(&riscom_lock, flags); - - clear_bit(TTY_IO_ERROR, &tty->flags); - bp->count++; - rp->xmit_cnt = rp->xmit_head = rp->xmit_tail = 0; - rc_change_speed(tty, bp, rp); - spin_unlock_irqrestore(&riscom_lock, flags); - return 0; -} - -/* Must be called with interrupts disabled */ -static void rc_shutdown_port(struct tty_struct *tty, - struct riscom_board *bp, struct riscom_port *port) -{ -#ifdef RC_REPORT_OVERRUN - printk(KERN_INFO "rc%d: port %d: Total %ld overruns were detected.\n", - board_No(bp), port_No(port), port->overrun); -#endif -#ifdef RC_REPORT_FIFO - { - int i; - - printk(KERN_INFO "rc%d: port %d: FIFO hits [ ", - board_No(bp), port_No(port)); - for (i = 0; i < 10; i++) - printk("%ld ", port->hits[i]); - printk("].\n"); - } -#endif - tty_port_free_xmit_buf(&port->port); - - /* Select port */ - rc_out(bp, CD180_CAR, port_No(port)); - /* Reset port */ - rc_wait_CCR(bp); - rc_out(bp, CD180_CCR, CCR_SOFTRESET); - /* Disable all interrupts from this port */ - port->IER = 0; - rc_out(bp, CD180_IER, port->IER); - - set_bit(TTY_IO_ERROR, &tty->flags); - - if (--bp->count < 0) { - printk(KERN_INFO "rc%d: rc_shutdown_port: " - "bad board count: %d\n", - board_No(bp), bp->count); - bp->count = 0; - } - /* - * If this is the last opened port on the board - * shutdown whole board - */ - if (!bp->count) - rc_shutdown_board(bp); -} - -static int carrier_raised(struct tty_port *port) -{ - struct riscom_port *p = container_of(port, struct riscom_port, port); - struct riscom_board *bp = port_Board(p); - unsigned long flags; - int CD; - - spin_lock_irqsave(&riscom_lock, flags); - rc_out(bp, CD180_CAR, port_No(p)); - CD = rc_in(bp, CD180_MSVR) & MSVR_CD; - rc_out(bp, CD180_MSVR, MSVR_RTS); - bp->DTR &= ~(1u << port_No(p)); - rc_out(bp, RC_DTR, bp->DTR); - spin_unlock_irqrestore(&riscom_lock, flags); - return CD; -} - -static void dtr_rts(struct tty_port *port, int onoff) -{ - struct riscom_port *p = container_of(port, struct riscom_port, port); - struct riscom_board *bp = port_Board(p); - unsigned long flags; - - spin_lock_irqsave(&riscom_lock, flags); - bp->DTR &= ~(1u << port_No(p)); - if (onoff == 0) - bp->DTR |= (1u << port_No(p)); - rc_out(bp, RC_DTR, bp->DTR); - spin_unlock_irqrestore(&riscom_lock, flags); -} - -static int rc_open(struct tty_struct *tty, struct file *filp) -{ - int board; - int error; - struct riscom_port *port; - struct riscom_board *bp; - - board = RC_BOARD(tty->index); - if (board >= RC_NBOARD || !(rc_board[board].flags & RC_BOARD_PRESENT)) - return -ENODEV; - - bp = &rc_board[board]; - port = rc_port + board * RC_NPORT + RC_PORT(tty->index); - if (rc_paranoia_check(port, tty->name, "rc_open")) - return -ENODEV; - - error = rc_setup_board(bp); - if (error) - return error; - - tty->driver_data = port; - return tty_port_open(&port->port, tty, filp); -} - -static void rc_flush_buffer(struct tty_struct *tty) -{ - struct riscom_port *port = tty->driver_data; - unsigned long flags; - - if (rc_paranoia_check(port, tty->name, "rc_flush_buffer")) - return; - - spin_lock_irqsave(&riscom_lock, flags); - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - spin_unlock_irqrestore(&riscom_lock, flags); - - tty_wakeup(tty); -} - -static void rc_close_port(struct tty_port *port) -{ - unsigned long flags; - struct riscom_port *rp = container_of(port, struct riscom_port, port); - struct riscom_board *bp = port_Board(rp); - unsigned long timeout; - - /* - * At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - - spin_lock_irqsave(&riscom_lock, flags); - rp->IER &= ~IER_RXD; - - rp->IER &= ~IER_TXRDY; - rp->IER |= IER_TXEMPTY; - rc_out(bp, CD180_CAR, port_No(rp)); - rc_out(bp, CD180_IER, rp->IER); - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - timeout = jiffies + HZ; - while (rp->IER & IER_TXEMPTY) { - spin_unlock_irqrestore(&riscom_lock, flags); - msleep_interruptible(jiffies_to_msecs(rp->timeout)); - spin_lock_irqsave(&riscom_lock, flags); - if (time_after(jiffies, timeout)) - break; - } - rc_shutdown_port(port->tty, bp, rp); - spin_unlock_irqrestore(&riscom_lock, flags); -} - -static void rc_close(struct tty_struct *tty, struct file *filp) -{ - struct riscom_port *port = tty->driver_data; - - if (!port || rc_paranoia_check(port, tty->name, "close")) - return; - tty_port_close(&port->port, tty, filp); -} - -static int rc_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct riscom_port *port = tty->driver_data; - struct riscom_board *bp; - int c, total = 0; - unsigned long flags; - - if (rc_paranoia_check(port, tty->name, "rc_write")) - return 0; - - bp = port_Board(port); - - while (1) { - spin_lock_irqsave(&riscom_lock, flags); - - c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, - SERIAL_XMIT_SIZE - port->xmit_head)); - if (c <= 0) - break; /* lock continues to be held */ - - memcpy(port->port.xmit_buf + port->xmit_head, buf, c); - port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - port->xmit_cnt += c; - - spin_unlock_irqrestore(&riscom_lock, flags); - - buf += c; - count -= c; - total += c; - } - - if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && - !(port->IER & IER_TXRDY)) { - port->IER |= IER_TXRDY; - rc_out(bp, CD180_CAR, port_No(port)); - rc_out(bp, CD180_IER, port->IER); - } - - spin_unlock_irqrestore(&riscom_lock, flags); - - return total; -} - -static int rc_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct riscom_port *port = tty->driver_data; - unsigned long flags; - int ret = 0; - - if (rc_paranoia_check(port, tty->name, "rc_put_char")) - return 0; - - spin_lock_irqsave(&riscom_lock, flags); - - if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) - goto out; - - port->port.xmit_buf[port->xmit_head++] = ch; - port->xmit_head &= SERIAL_XMIT_SIZE - 1; - port->xmit_cnt++; - ret = 1; - -out: - spin_unlock_irqrestore(&riscom_lock, flags); - return ret; -} - -static void rc_flush_chars(struct tty_struct *tty) -{ - struct riscom_port *port = tty->driver_data; - unsigned long flags; - - if (rc_paranoia_check(port, tty->name, "rc_flush_chars")) - return; - - if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped) - return; - - spin_lock_irqsave(&riscom_lock, flags); - - port->IER |= IER_TXRDY; - rc_out(port_Board(port), CD180_CAR, port_No(port)); - rc_out(port_Board(port), CD180_IER, port->IER); - - spin_unlock_irqrestore(&riscom_lock, flags); -} - -static int rc_write_room(struct tty_struct *tty) -{ - struct riscom_port *port = tty->driver_data; - int ret; - - if (rc_paranoia_check(port, tty->name, "rc_write_room")) - return 0; - - ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; -} - -static int rc_chars_in_buffer(struct tty_struct *tty) -{ - struct riscom_port *port = tty->driver_data; - - if (rc_paranoia_check(port, tty->name, "rc_chars_in_buffer")) - return 0; - - return port->xmit_cnt; -} - -static int rc_tiocmget(struct tty_struct *tty) -{ - struct riscom_port *port = tty->driver_data; - struct riscom_board *bp; - unsigned char status; - unsigned int result; - unsigned long flags; - - if (rc_paranoia_check(port, tty->name, __func__)) - return -ENODEV; - - bp = port_Board(port); - - spin_lock_irqsave(&riscom_lock, flags); - - rc_out(bp, CD180_CAR, port_No(port)); - status = rc_in(bp, CD180_MSVR); - result = rc_in(bp, RC_RI) & (1u << port_No(port)) ? 0 : TIOCM_RNG; - - spin_unlock_irqrestore(&riscom_lock, flags); - - result |= ((status & MSVR_RTS) ? TIOCM_RTS : 0) - | ((status & MSVR_DTR) ? TIOCM_DTR : 0) - | ((status & MSVR_CD) ? TIOCM_CAR : 0) - | ((status & MSVR_DSR) ? TIOCM_DSR : 0) - | ((status & MSVR_CTS) ? TIOCM_CTS : 0); - return result; -} - -static int rc_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct riscom_port *port = tty->driver_data; - unsigned long flags; - struct riscom_board *bp; - - if (rc_paranoia_check(port, tty->name, __func__)) - return -ENODEV; - - bp = port_Board(port); - - spin_lock_irqsave(&riscom_lock, flags); - - if (set & TIOCM_RTS) - port->MSVR |= MSVR_RTS; - if (set & TIOCM_DTR) - bp->DTR &= ~(1u << port_No(port)); - - if (clear & TIOCM_RTS) - port->MSVR &= ~MSVR_RTS; - if (clear & TIOCM_DTR) - bp->DTR |= (1u << port_No(port)); - - rc_out(bp, CD180_CAR, port_No(port)); - rc_out(bp, CD180_MSVR, port->MSVR); - rc_out(bp, RC_DTR, bp->DTR); - - spin_unlock_irqrestore(&riscom_lock, flags); - - return 0; -} - -static int rc_send_break(struct tty_struct *tty, int length) -{ - struct riscom_port *port = tty->driver_data; - struct riscom_board *bp = port_Board(port); - unsigned long flags; - - if (length == 0 || length == -1) - return -EOPNOTSUPP; - - spin_lock_irqsave(&riscom_lock, flags); - - port->break_length = RISCOM_TPS / HZ * length; - port->COR2 |= COR2_ETC; - port->IER |= IER_TXRDY; - rc_out(bp, CD180_CAR, port_No(port)); - rc_out(bp, CD180_COR2, port->COR2); - rc_out(bp, CD180_IER, port->IER); - rc_wait_CCR(bp); - rc_out(bp, CD180_CCR, CCR_CORCHG2); - rc_wait_CCR(bp); - - spin_unlock_irqrestore(&riscom_lock, flags); - return 0; -} - -static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port, - struct serial_struct __user *newinfo) -{ - struct serial_struct tmp; - struct riscom_board *bp = port_Board(port); - int change_speed; - - if (copy_from_user(&tmp, newinfo, sizeof(tmp))) - return -EFAULT; - - mutex_lock(&port->port.mutex); - change_speed = ((port->port.flags & ASYNC_SPD_MASK) != - (tmp.flags & ASYNC_SPD_MASK)); - - if (!capable(CAP_SYS_ADMIN)) { - if ((tmp.close_delay != port->port.close_delay) || - (tmp.closing_wait != port->port.closing_wait) || - ((tmp.flags & ~ASYNC_USR_MASK) != - (port->port.flags & ~ASYNC_USR_MASK))) { - mutex_unlock(&port->port.mutex); - return -EPERM; - } - port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | - (tmp.flags & ASYNC_USR_MASK)); - } else { - port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | - (tmp.flags & ASYNC_FLAGS)); - port->port.close_delay = tmp.close_delay; - port->port.closing_wait = tmp.closing_wait; - } - if (change_speed) { - unsigned long flags; - - spin_lock_irqsave(&riscom_lock, flags); - rc_change_speed(tty, bp, port); - spin_unlock_irqrestore(&riscom_lock, flags); - } - mutex_unlock(&port->port.mutex); - return 0; -} - -static int rc_get_serial_info(struct riscom_port *port, - struct serial_struct __user *retinfo) -{ - struct serial_struct tmp; - struct riscom_board *bp = port_Board(port); - - memset(&tmp, 0, sizeof(tmp)); - tmp.type = PORT_CIRRUS; - tmp.line = port - rc_port; - - mutex_lock(&port->port.mutex); - tmp.port = bp->base; - tmp.irq = bp->irq; - tmp.flags = port->port.flags; - tmp.baud_base = (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC; - tmp.close_delay = port->port.close_delay * HZ/100; - tmp.closing_wait = port->port.closing_wait * HZ/100; - mutex_unlock(&port->port.mutex); - tmp.xmit_fifo_size = CD180_NFIFO; - return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0; -} - -static int rc_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct riscom_port *port = tty->driver_data; - void __user *argp = (void __user *)arg; - int retval; - - if (rc_paranoia_check(port, tty->name, "rc_ioctl")) - return -ENODEV; - - switch (cmd) { - case TIOCGSERIAL: - retval = rc_get_serial_info(port, argp); - break; - case TIOCSSERIAL: - retval = rc_set_serial_info(tty, port, argp); - break; - default: - retval = -ENOIOCTLCMD; - } - return retval; -} - -static void rc_throttle(struct tty_struct *tty) -{ - struct riscom_port *port = tty->driver_data; - struct riscom_board *bp; - unsigned long flags; - - if (rc_paranoia_check(port, tty->name, "rc_throttle")) - return; - bp = port_Board(port); - - spin_lock_irqsave(&riscom_lock, flags); - port->MSVR &= ~MSVR_RTS; - rc_out(bp, CD180_CAR, port_No(port)); - if (I_IXOFF(tty)) { - rc_wait_CCR(bp); - rc_out(bp, CD180_CCR, CCR_SSCH2); - rc_wait_CCR(bp); - } - rc_out(bp, CD180_MSVR, port->MSVR); - spin_unlock_irqrestore(&riscom_lock, flags); -} - -static void rc_unthrottle(struct tty_struct *tty) -{ - struct riscom_port *port = tty->driver_data; - struct riscom_board *bp; - unsigned long flags; - - if (rc_paranoia_check(port, tty->name, "rc_unthrottle")) - return; - bp = port_Board(port); - - spin_lock_irqsave(&riscom_lock, flags); - port->MSVR |= MSVR_RTS; - rc_out(bp, CD180_CAR, port_No(port)); - if (I_IXOFF(tty)) { - rc_wait_CCR(bp); - rc_out(bp, CD180_CCR, CCR_SSCH1); - rc_wait_CCR(bp); - } - rc_out(bp, CD180_MSVR, port->MSVR); - spin_unlock_irqrestore(&riscom_lock, flags); -} - -static void rc_stop(struct tty_struct *tty) -{ - struct riscom_port *port = tty->driver_data; - struct riscom_board *bp; - unsigned long flags; - - if (rc_paranoia_check(port, tty->name, "rc_stop")) - return; - - bp = port_Board(port); - - spin_lock_irqsave(&riscom_lock, flags); - port->IER &= ~IER_TXRDY; - rc_out(bp, CD180_CAR, port_No(port)); - rc_out(bp, CD180_IER, port->IER); - spin_unlock_irqrestore(&riscom_lock, flags); -} - -static void rc_start(struct tty_struct *tty) -{ - struct riscom_port *port = tty->driver_data; - struct riscom_board *bp; - unsigned long flags; - - if (rc_paranoia_check(port, tty->name, "rc_start")) - return; - - bp = port_Board(port); - - spin_lock_irqsave(&riscom_lock, flags); - - if (port->xmit_cnt && port->port.xmit_buf && !(port->IER & IER_TXRDY)) { - port->IER |= IER_TXRDY; - rc_out(bp, CD180_CAR, port_No(port)); - rc_out(bp, CD180_IER, port->IER); - } - spin_unlock_irqrestore(&riscom_lock, flags); -} - -static void rc_hangup(struct tty_struct *tty) -{ - struct riscom_port *port = tty->driver_data; - - if (rc_paranoia_check(port, tty->name, "rc_hangup")) - return; - - tty_port_hangup(&port->port); -} - -static void rc_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct riscom_port *port = tty->driver_data; - unsigned long flags; - - if (rc_paranoia_check(port, tty->name, "rc_set_termios")) - return; - - spin_lock_irqsave(&riscom_lock, flags); - rc_change_speed(tty, port_Board(port), port); - spin_unlock_irqrestore(&riscom_lock, flags); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - rc_start(tty); - } -} - -static const struct tty_operations riscom_ops = { - .open = rc_open, - .close = rc_close, - .write = rc_write, - .put_char = rc_put_char, - .flush_chars = rc_flush_chars, - .write_room = rc_write_room, - .chars_in_buffer = rc_chars_in_buffer, - .flush_buffer = rc_flush_buffer, - .ioctl = rc_ioctl, - .throttle = rc_throttle, - .unthrottle = rc_unthrottle, - .set_termios = rc_set_termios, - .stop = rc_stop, - .start = rc_start, - .hangup = rc_hangup, - .tiocmget = rc_tiocmget, - .tiocmset = rc_tiocmset, - .break_ctl = rc_send_break, -}; - -static const struct tty_port_operations riscom_port_ops = { - .carrier_raised = carrier_raised, - .dtr_rts = dtr_rts, - .shutdown = rc_close_port, - .activate = rc_activate_port, -}; - - -static int __init rc_init_drivers(void) -{ - int error; - int i; - - riscom_driver = alloc_tty_driver(RC_NBOARD * RC_NPORT); - if (!riscom_driver) - return -ENOMEM; - - riscom_driver->owner = THIS_MODULE; - riscom_driver->name = "ttyL"; - riscom_driver->major = RISCOM8_NORMAL_MAJOR; - riscom_driver->type = TTY_DRIVER_TYPE_SERIAL; - riscom_driver->subtype = SERIAL_TYPE_NORMAL; - riscom_driver->init_termios = tty_std_termios; - riscom_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - riscom_driver->init_termios.c_ispeed = 9600; - riscom_driver->init_termios.c_ospeed = 9600; - riscom_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK; - tty_set_operations(riscom_driver, &riscom_ops); - error = tty_register_driver(riscom_driver); - if (error != 0) { - put_tty_driver(riscom_driver); - printk(KERN_ERR "rc: Couldn't register RISCom/8 driver, " - "error = %d\n", error); - return 1; - } - memset(rc_port, 0, sizeof(rc_port)); - for (i = 0; i < RC_NPORT * RC_NBOARD; i++) { - tty_port_init(&rc_port[i].port); - rc_port[i].port.ops = &riscom_port_ops; - rc_port[i].magic = RISCOM8_MAGIC; - } - return 0; -} - -static void rc_release_drivers(void) -{ - tty_unregister_driver(riscom_driver); - put_tty_driver(riscom_driver); -} - -#ifndef MODULE -/* - * Called at boot time. - * - * You can specify IO base for up to RC_NBOARD cards, - * using line "riscom8=0xiobase1,0xiobase2,.." at LILO prompt. - * Note that there will be no probing at default - * addresses in this case. - * - */ -static int __init riscom8_setup(char *str) -{ - int ints[RC_NBOARD]; - int i; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - for (i = 0; i < RC_NBOARD; i++) { - if (i < ints[0]) - rc_board[i].base = ints[i+1]; - else - rc_board[i].base = 0; - } - return 1; -} - -__setup("riscom8=", riscom8_setup); -#endif - -static char banner[] __initdata = - KERN_INFO "rc: SDL RISCom/8 card driver v1.1, (c) D.Gorodchanin " - "1994-1996.\n"; -static char no_boards_msg[] __initdata = - KERN_INFO "rc: No RISCom/8 boards detected.\n"; - -/* - * This routine must be called by kernel at boot time - */ -static int __init riscom8_init(void) -{ - int i; - int found = 0; - - printk(banner); - - if (rc_init_drivers()) - return -EIO; - - for (i = 0; i < RC_NBOARD; i++) - if (rc_board[i].base && !rc_probe(&rc_board[i])) - found++; - if (!found) { - rc_release_drivers(); - printk(no_boards_msg); - return -EIO; - } - return 0; -} - -#ifdef MODULE -static int iobase; -static int iobase1; -static int iobase2; -static int iobase3; -module_param(iobase, int, 0); -module_param(iobase1, int, 0); -module_param(iobase2, int, 0); -module_param(iobase3, int, 0); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(RISCOM8_NORMAL_MAJOR); -#endif /* MODULE */ - -/* - * You can setup up to 4 boards (current value of RC_NBOARD) - * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter. - * - */ -static int __init riscom8_init_module(void) -{ -#ifdef MODULE - int i; - - if (iobase || iobase1 || iobase2 || iobase3) { - for (i = 0; i < RC_NBOARD; i++) - rc_board[i].base = 0; - } - - if (iobase) - rc_board[0].base = iobase; - if (iobase1) - rc_board[1].base = iobase1; - if (iobase2) - rc_board[2].base = iobase2; - if (iobase3) - rc_board[3].base = iobase3; -#endif /* MODULE */ - - return riscom8_init(); -} - -static void __exit riscom8_exit_module(void) -{ - int i; - - rc_release_drivers(); - for (i = 0; i < RC_NBOARD; i++) - if (rc_board[i].flags & RC_BOARD_PRESENT) - rc_release_io_range(&rc_board[i]); - -} - -module_init(riscom8_init_module); -module_exit(riscom8_exit_module); diff --git a/drivers/char/riscom8.h b/drivers/char/riscom8.h deleted file mode 100644 index c9876b3f9714..000000000000 --- a/drivers/char/riscom8.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * linux/drivers/char/riscom8.h -- RISCom/8 multiport serial driver. - * - * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) - * - * This code is loosely based on the Linux serial driver, written by - * Linus Torvalds, Theodore T'so and others. The RISCom/8 card - * programming info was obtained from various drivers for other OSes - * (FreeBSD, ISC, etc), but no source code from those drivers were - * directly included in this driver. - * - * - * 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. - */ - -#ifndef __LINUX_RISCOM8_H -#define __LINUX_RISCOM8_H - -#include - -#ifdef __KERNEL__ - -#define RC_NBOARD 4 -/* NOTE: RISCom decoder recognizes 16 addresses... */ -#define RC_NPORT 8 -#define RC_BOARD(line) (((line) >> 3) & 0x07) -#define RC_PORT(line) ((line) & (RC_NPORT - 1)) - -/* Ticks per sec. Used for setting receiver timeout and break length */ -#define RISCOM_TPS 4000 - -/* Yeah, after heavy testing I decided it must be 6. - * Sure, You can change it if needed. - */ -#define RISCOM_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ - -#define RISCOM8_MAGIC 0x0907 - -#define RC_IOBASE1 0x220 -#define RC_IOBASE2 0x240 -#define RC_IOBASE3 0x250 -#define RC_IOBASE4 0x260 - -struct riscom_board { - unsigned long flags; - unsigned short base; - unsigned char irq; - signed char count; - unsigned char DTR; -}; - -#define RC_BOARD_PRESENT 0x00000001 -#define RC_BOARD_ACTIVE 0x00000002 - -struct riscom_port { - int magic; - struct tty_port port; - int baud_base; - int timeout; - int custom_divisor; - int xmit_head; - int xmit_tail; - int xmit_cnt; - short wakeup_chars; - short break_length; - unsigned char mark_mask; - unsigned char IER; - unsigned char MSVR; - unsigned char COR2; -#ifdef RC_REPORT_OVERRUN - unsigned long overrun; -#endif -#ifdef RC_REPORT_FIFO - unsigned long hits[10]; -#endif -}; - -#endif /* __KERNEL__ */ -#endif /* __LINUX_RISCOM8_H */ diff --git a/drivers/char/riscom8_reg.h b/drivers/char/riscom8_reg.h deleted file mode 100644 index a32475ed0d18..000000000000 --- a/drivers/char/riscom8_reg.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * linux/drivers/char/riscom8_reg.h -- RISCom/8 multiport serial driver. - */ - -/* - * Definitions for RISCom/8 Async Mux card by SDL Communications, Inc. - */ - -/* - * Address mapping between Cirrus Logic CD180 chip internal registers - * and ISA port addresses: - * - * CL-CD180 A6 A5 A4 A3 A2 A1 A0 - * ISA A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0 - */ -#define RC_TO_ISA(r) ((((r)&0x07)<<1) | (((r)&~0x07)<<7)) - - -/* RISCom/8 On-Board Registers (assuming address translation) */ - -#define RC_RI 0x100 /* Ring Indicator Register (R/O) */ -#define RC_DTR 0x100 /* DTR Register (W/O) */ -#define RC_BSR 0x101 /* Board Status Register (R/O) */ -#define RC_CTOUT 0x101 /* Clear Timeout (W/O) */ - - -/* Board Status Register */ - -#define RC_BSR_TOUT 0x08 /* Hardware Timeout */ -#define RC_BSR_RINT 0x04 /* Receiver Interrupt */ -#define RC_BSR_TINT 0x02 /* Transmitter Interrupt */ -#define RC_BSR_MINT 0x01 /* Modem Ctl Interrupt */ - - -/* On-board oscillator frequency (in Hz) */ -#define RC_OSCFREQ 9830400 - -/* Values of choice for Interrupt ACKs */ -#define RC_ACK_MINT 0x81 /* goes to PILR1 */ -#define RC_ACK_RINT 0x82 /* goes to PILR3 */ -#define RC_ACK_TINT 0x84 /* goes to PILR2 */ - -/* Chip ID (sorry, only one chip now) */ -#define RC_ID 0x10 - -/* Definitions for Cirrus Logic CL-CD180 8-port async mux chip */ - -#define CD180_NCH 8 /* Total number of channels */ -#define CD180_TPC 16 /* Ticks per character */ -#define CD180_NFIFO 8 /* TX FIFO size */ - - -/* Global registers */ - -#define CD180_GIVR 0x40 /* Global Interrupt Vector Register */ -#define CD180_GICR 0x41 /* Global Interrupting Channel Register */ -#define CD180_PILR1 0x61 /* Priority Interrupt Level Register 1 */ -#define CD180_PILR2 0x62 /* Priority Interrupt Level Register 2 */ -#define CD180_PILR3 0x63 /* Priority Interrupt Level Register 3 */ -#define CD180_CAR 0x64 /* Channel Access Register */ -#define CD180_GFRCR 0x6b /* Global Firmware Revision Code Register */ -#define CD180_PPRH 0x70 /* Prescaler Period Register High */ -#define CD180_PPRL 0x71 /* Prescaler Period Register Low */ -#define CD180_RDR 0x78 /* Receiver Data Register */ -#define CD180_RCSR 0x7a /* Receiver Character Status Register */ -#define CD180_TDR 0x7b /* Transmit Data Register */ -#define CD180_EOIR 0x7f /* End of Interrupt Register */ - - -/* Channel Registers */ - -#define CD180_CCR 0x01 /* Channel Command Register */ -#define CD180_IER 0x02 /* Interrupt Enable Register */ -#define CD180_COR1 0x03 /* Channel Option Register 1 */ -#define CD180_COR2 0x04 /* Channel Option Register 2 */ -#define CD180_COR3 0x05 /* Channel Option Register 3 */ -#define CD180_CCSR 0x06 /* Channel Control Status Register */ -#define CD180_RDCR 0x07 /* Receive Data Count Register */ -#define CD180_SCHR1 0x09 /* Special Character Register 1 */ -#define CD180_SCHR2 0x0a /* Special Character Register 2 */ -#define CD180_SCHR3 0x0b /* Special Character Register 3 */ -#define CD180_SCHR4 0x0c /* Special Character Register 4 */ -#define CD180_MCOR1 0x10 /* Modem Change Option 1 Register */ -#define CD180_MCOR2 0x11 /* Modem Change Option 2 Register */ -#define CD180_MCR 0x12 /* Modem Change Register */ -#define CD180_RTPR 0x18 /* Receive Timeout Period Register */ -#define CD180_MSVR 0x28 /* Modem Signal Value Register */ -#define CD180_RBPRH 0x31 /* Receive Baud Rate Period Register High */ -#define CD180_RBPRL 0x32 /* Receive Baud Rate Period Register Low */ -#define CD180_TBPRH 0x39 /* Transmit Baud Rate Period Register High */ -#define CD180_TBPRL 0x3a /* Transmit Baud Rate Period Register Low */ - - -/* Global Interrupt Vector Register (R/W) */ - -#define GIVR_ITMASK 0x07 /* Interrupt type mask */ -#define GIVR_IT_MODEM 0x01 /* Modem Signal Change Interrupt */ -#define GIVR_IT_TX 0x02 /* Transmit Data Interrupt */ -#define GIVR_IT_RCV 0x03 /* Receive Good Data Interrupt */ -#define GIVR_IT_REXC 0x07 /* Receive Exception Interrupt */ - - -/* Global Interrupt Channel Register (R/W) */ - -#define GICR_CHAN 0x1c /* Channel Number Mask */ -#define GICR_CHAN_OFF 2 /* Channel Number Offset */ - - -/* Channel Address Register (R/W) */ - -#define CAR_CHAN 0x07 /* Channel Number Mask */ -#define CAR_A7 0x08 /* A7 Address Extension (unused) */ - - -/* Receive Character Status Register (R/O) */ - -#define RCSR_TOUT 0x80 /* Rx Timeout */ -#define RCSR_SCDET 0x70 /* Special Character Detected Mask */ -#define RCSR_NO_SC 0x00 /* No Special Characters Detected */ -#define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */ -#define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */ -#define RCSR_SC_3 0x30 /* Special Char 3 Detected */ -#define RCSR_SC_4 0x40 /* Special Char 4 Detected */ -#define RCSR_BREAK 0x08 /* Break has been detected */ -#define RCSR_PE 0x04 /* Parity Error */ -#define RCSR_FE 0x02 /* Frame Error */ -#define RCSR_OE 0x01 /* Overrun Error */ - - -/* Channel Command Register (R/W) (commands in groups can be OR-ed) */ - -#define CCR_HARDRESET 0x81 /* Reset the chip */ - -#define CCR_SOFTRESET 0x80 /* Soft Channel Reset */ - -#define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */ -#define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */ -#define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */ - -#define CCR_SSCH1 0x21 /* Send Special Character 1 */ - -#define CCR_SSCH2 0x22 /* Send Special Character 2 */ - -#define CCR_SSCH3 0x23 /* Send Special Character 3 */ - -#define CCR_SSCH4 0x24 /* Send Special Character 4 */ - -#define CCR_TXEN 0x18 /* Enable Transmitter */ -#define CCR_RXEN 0x12 /* Enable Receiver */ - -#define CCR_TXDIS 0x14 /* Disable Transmitter */ -#define CCR_RXDIS 0x11 /* Disable Receiver */ - - -/* Interrupt Enable Register (R/W) */ - -#define IER_DSR 0x80 /* Enable interrupt on DSR change */ -#define IER_CD 0x40 /* Enable interrupt on CD change */ -#define IER_CTS 0x20 /* Enable interrupt on CTS change */ -#define IER_RXD 0x10 /* Enable interrupt on Receive Data */ -#define IER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */ -#define IER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */ -#define IER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */ -#define IER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */ - - -/* Channel Option Register 1 (R/W) */ - -#define COR1_ODDP 0x80 /* Odd Parity */ -#define COR1_PARMODE 0x60 /* Parity Mode mask */ -#define COR1_NOPAR 0x00 /* No Parity */ -#define COR1_FORCEPAR 0x20 /* Force Parity */ -#define COR1_NORMPAR 0x40 /* Normal Parity */ -#define COR1_IGNORE 0x10 /* Ignore Parity on RX */ -#define COR1_STOPBITS 0x0c /* Number of Stop Bits */ -#define COR1_1SB 0x00 /* 1 Stop Bit */ -#define COR1_15SB 0x04 /* 1.5 Stop Bits */ -#define COR1_2SB 0x08 /* 2 Stop Bits */ -#define COR1_CHARLEN 0x03 /* Character Length */ -#define COR1_5BITS 0x00 /* 5 bits */ -#define COR1_6BITS 0x01 /* 6 bits */ -#define COR1_7BITS 0x02 /* 7 bits */ -#define COR1_8BITS 0x03 /* 8 bits */ - - -/* Channel Option Register 2 (R/W) */ - -#define COR2_IXM 0x80 /* Implied XON mode */ -#define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */ -#define COR2_ETC 0x20 /* Embedded Tx Commands Enable */ -#define COR2_LLM 0x10 /* Local Loopback Mode */ -#define COR2_RLM 0x08 /* Remote Loopback Mode */ -#define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */ -#define COR2_CTSAE 0x02 /* CTS Automatic Enable */ -#define COR2_DSRAE 0x01 /* DSR Automatic Enable */ - - -/* Channel Option Register 3 (R/W) */ - -#define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */ -#define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */ -#define COR3_FCT 0x20 /* Flow-Control Transparency Mode */ -#define COR3_SCDE 0x10 /* Special Character Detection Enable */ -#define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */ - - -/* Channel Control Status Register (R/O) */ - -#define CCSR_RXEN 0x80 /* Receiver Enabled */ -#define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */ -#define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */ -#define CCSR_TXEN 0x08 /* Transmitter Enabled */ -#define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */ -#define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */ - - -/* Modem Change Option Register 1 (R/W) */ - -#define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */ -#define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */ -#define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */ -#define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */ -#define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */ - - -/* Modem Change Option Register 2 (R/W) */ - -#define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */ -#define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */ -#define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */ - - -/* Modem Change Register (R/W) */ - -#define MCR_DSRCHG 0x80 /* DSR Changed */ -#define MCR_CDCHG 0x40 /* CD Changed */ -#define MCR_CTSCHG 0x20 /* CTS Changed */ - - -/* Modem Signal Value Register (R/W) */ - -#define MSVR_DSR 0x80 /* Current state of DSR input */ -#define MSVR_CD 0x40 /* Current state of CD input */ -#define MSVR_CTS 0x20 /* Current state of CTS input */ -#define MSVR_DTR 0x02 /* Current state of DTR output */ -#define MSVR_RTS 0x01 /* Current state of RTS output */ - - -/* Escape characters */ - -#define CD180_C_ESC 0x00 /* Escape character */ -#define CD180_C_SBRK 0x81 /* Start sending BREAK */ -#define CD180_C_DELAY 0x82 /* Delay output */ -#define CD180_C_EBRK 0x83 /* Stop sending BREAK */ diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c deleted file mode 100644 index 674af6933978..000000000000 --- a/drivers/char/serial167.c +++ /dev/null @@ -1,2489 +0,0 @@ -/* - * linux/drivers/char/serial167.c - * - * Driver for MVME166/7 board serial ports, which are via a CD2401. - * Based very much on cyclades.c. - * - * MVME166/7 work by Richard Hirst [richard@sleepie.demon.co.uk] - * - * ============================================================== - * - * static char rcsid[] = - * "$Revision: 1.36.1.4 $$Date: 1995/03/29 06:14:14 $"; - * - * linux/kernel/cyclades.c - * - * Maintained by Marcio Saito (cyclades@netcom.com) and - * Randolph Bentson (bentson@grieg.seaslug.org) - * - * Much of the design and some of the code came from serial.c - * which was copyright (C) 1991, 1992 Linus Torvalds. It was - * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92, - * and then fixed as suggested by Michael K. Johnson 12/12/92. - * - * This version does not support shared irq's. - * - * $Log: cyclades.c,v $ - * Revision 1.36.1.4 1995/03/29 06:14:14 bentson - * disambiguate between Cyclom-16Y and Cyclom-32Ye; - * - * Changes: - * - * 200 lines of changes record removed - RGH 11-10-95, starting work on - * converting this to drive serial ports on mvme166 (cd2401). - * - * Arnaldo Carvalho de Melo - 2000/08/25 - * - get rid of verify_area - * - use get_user to access memory from userspace in set_threshold, - * set_default_threshold and set_timeout - * - don't use the panic function in serial167_init - * - do resource release on failure on serial167_init - * - include missing restore_flags in mvme167_serial_console_setup - * - * Kars de Jong - 2004/09/06 - * - replace bottom half handler with task queue handler - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#define SERIAL_PARANOIA_CHECK -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_THROTTLE -#undef SERIAL_DEBUG_OTHER -#undef SERIAL_DEBUG_IO -#undef SERIAL_DEBUG_COUNT -#undef SERIAL_DEBUG_DTR -#undef CYCLOM_16Y_HACK -#define CYCLOM_ENABLE_MONITORING - -#define WAKEUP_CHARS 256 - -#define STD_COM_FLAGS (0) - -static struct tty_driver *cy_serial_driver; -extern int serial_console; -static struct cyclades_port *serial_console_info = NULL; -static unsigned int serial_console_cflag = 0; -u_char initial_console_speed; - -/* Base address of cd2401 chip on mvme166/7 */ - -#define BASE_ADDR (0xfff45000) -#define pcc2chip ((volatile u_char *)0xfff42000) -#define PccSCCMICR 0x1d -#define PccSCCTICR 0x1e -#define PccSCCRICR 0x1f -#define PccTPIACKR 0x25 -#define PccRPIACKR 0x27 -#define PccIMLR 0x3f - -/* This is the per-port data structure */ -struct cyclades_port cy_port[] = { - /* CARD# */ - {-1}, /* ttyS0 */ - {-1}, /* ttyS1 */ - {-1}, /* ttyS2 */ - {-1}, /* ttyS3 */ -}; - -#define NR_PORTS ARRAY_SIZE(cy_port) - -/* - * This is used to look up the divisor speeds and the timeouts - * We're normally limited to 15 distinct baud rates. The extra - * are accessed via settings in info->flags. - * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - * HI VHI - */ -static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, - 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000, - 0 -}; - -#if 0 -static char baud_co[] = { /* 25 MHz clock option table */ - /* value => 00 01 02 03 04 */ - /* divide by 8 32 128 512 2048 */ - 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, - 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static char baud_bpr[] = { /* 25 MHz baud rate period table */ - 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, - 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15 -}; -#endif - -/* I think 166 brd clocks 2401 at 20MHz.... */ - -/* These values are written directly to tcor, and >> 5 for writing to rcor */ -static u_char baud_co[] = { /* 20 MHz clock option table */ - 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x60, 0x60, 0x40, - 0x40, 0x40, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -/* These values written directly to tbpr/rbpr */ -static u_char baud_bpr[] = { /* 20 MHz baud rate period table */ - 0x00, 0xc0, 0x80, 0x58, 0x6c, 0x40, 0xc0, 0x81, 0x40, 0x81, - 0x57, 0x40, 0x81, 0x40, 0x81, 0x40, 0x2b, 0x20, 0x15, 0x10 -}; - -static u_char baud_cor4[] = { /* receive threshold */ - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07 -}; - -static void shutdown(struct cyclades_port *); -static int startup(struct cyclades_port *); -static void cy_throttle(struct tty_struct *); -static void cy_unthrottle(struct tty_struct *); -static void config_setup(struct cyclades_port *); -#ifdef CYCLOM_SHOW_STATUS -static void show_status(int); -#endif - -/* - * I have my own version of udelay(), as it is needed when initialising - * the chip, before the delay loop has been calibrated. Should probably - * reference one of the vmechip2 or pccchip2 counter for an accurate - * delay, but this wild guess will do for now. - */ - -void my_udelay(long us) -{ - u_char x; - volatile u_char *p = &x; - int i; - - while (us--) - for (i = 100; i; i--) - x |= *p; -} - -static inline int serial_paranoia_check(struct cyclades_port *info, char *name, - const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - if (!info) { - printk("Warning: null cyclades_port for (%s) in %s\n", name, - routine); - return 1; - } - - if (info < &cy_port[0] || info >= &cy_port[NR_PORTS]) { - printk("Warning: cyclades_port out of range for (%s) in %s\n", - name, routine); - return 1; - } - - if (info->magic != CYCLADES_MAGIC) { - printk("Warning: bad magic number for serial struct (%s) in " - "%s\n", name, routine); - return 1; - } -#endif - return 0; -} /* serial_paranoia_check */ - -#if 0 -/* The following diagnostic routines allow the driver to spew - information on the screen, even (especially!) during interrupts. - */ -void SP(char *data) -{ - unsigned long flags; - local_irq_save(flags); - printk(KERN_EMERG "%s", data); - local_irq_restore(flags); -} - -char scrn[2]; -void CP(char data) -{ - unsigned long flags; - local_irq_save(flags); - scrn[0] = data; - printk(KERN_EMERG "%c", scrn); - local_irq_restore(flags); -} /* CP */ - -void CP1(int data) -{ - (data < 10) ? CP(data + '0') : CP(data + 'A' - 10); -} /* CP1 */ -void CP2(int data) -{ - CP1((data >> 4) & 0x0f); - CP1(data & 0x0f); -} /* CP2 */ -void CP4(int data) -{ - CP2((data >> 8) & 0xff); - CP2(data & 0xff); -} /* CP4 */ -void CP8(long data) -{ - CP4((data >> 16) & 0xffff); - CP4(data & 0xffff); -} /* CP8 */ -#endif - -/* This routine waits up to 1000 micro-seconds for the previous - command to the Cirrus chip to complete and then issues the - new command. An error is returned if the previous command - didn't finish within the time limit. - */ -u_short write_cy_cmd(volatile u_char * base_addr, u_char cmd) -{ - unsigned long flags; - volatile int i; - - local_irq_save(flags); - /* Check to see that the previous command has completed */ - for (i = 0; i < 100; i++) { - if (base_addr[CyCCR] == 0) { - break; - } - my_udelay(10L); - } - /* if the CCR never cleared, the previous command - didn't finish within the "reasonable time" */ - if (i == 10) { - local_irq_restore(flags); - return (-1); - } - - /* Issue the new command */ - base_addr[CyCCR] = cmd; - local_irq_restore(flags); - return (0); -} /* write_cy_cmd */ - -/* cy_start and cy_stop provide software output flow control as a - function of XON/XOFF, software CTS, and other such stuff. */ - -static void cy_stop(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; - int channel; - unsigned long flags; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_stop %s\n", tty->name); /* */ -#endif - - if (serial_paranoia_check(info, tty->name, "cy_stop")) - return; - - channel = info->line; - - local_irq_save(flags); - base_addr[CyCAR] = (u_char) (channel); /* index channel */ - base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); - local_irq_restore(flags); -} /* cy_stop */ - -static void cy_start(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; - int channel; - unsigned long flags; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_start %s\n", tty->name); /* */ -#endif - - if (serial_paranoia_check(info, tty->name, "cy_start")) - return; - - channel = info->line; - - local_irq_save(flags); - base_addr[CyCAR] = (u_char) (channel); - base_addr[CyIER] |= CyTxMpty; - local_irq_restore(flags); -} /* cy_start */ - -/* The real interrupt service routines are called - whenever the card wants its hand held--chars - received, out buffer empty, modem change, etc. - */ -static irqreturn_t cd2401_rxerr_interrupt(int irq, void *dev_id) -{ - struct tty_struct *tty; - struct cyclades_port *info; - volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; - unsigned char err, rfoc; - int channel; - char data; - - /* determine the channel and change to that context */ - channel = (u_short) (base_addr[CyLICR] >> 2); - info = &cy_port[channel]; - info->last_active = jiffies; - - if ((err = base_addr[CyRISR]) & CyTIMEOUT) { - /* This is a receive timeout interrupt, ignore it */ - base_addr[CyREOIR] = CyNOTRANS; - return IRQ_HANDLED; - } - - /* Read a byte of data if there is any - assume the error - * is associated with this character */ - - if ((rfoc = base_addr[CyRFOC]) != 0) - data = base_addr[CyRDR]; - else - data = 0; - - /* if there is nowhere to put the data, discard it */ - if (info->tty == 0) { - base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; - return IRQ_HANDLED; - } else { /* there is an open port for this data */ - tty = info->tty; - if (err & info->ignore_status_mask) { - base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; - return IRQ_HANDLED; - } - if (tty_buffer_request_room(tty, 1) != 0) { - if (err & info->read_status_mask) { - if (err & CyBREAK) { - tty_insert_flip_char(tty, data, - TTY_BREAK); - if (info->flags & ASYNC_SAK) { - do_SAK(tty); - } - } else if (err & CyFRAME) { - tty_insert_flip_char(tty, data, - TTY_FRAME); - } else if (err & CyPARITY) { - tty_insert_flip_char(tty, data, - TTY_PARITY); - } else if (err & CyOVERRUN) { - tty_insert_flip_char(tty, 0, - TTY_OVERRUN); - /* - If the flip buffer itself is - overflowing, we still lose - the next incoming character. - */ - if (tty_buffer_request_room(tty, 1) != - 0) { - tty_insert_flip_char(tty, data, - TTY_FRAME); - } - /* These two conditions may imply */ - /* a normal read should be done. */ - /* else if(data & CyTIMEOUT) */ - /* else if(data & CySPECHAR) */ - } else { - tty_insert_flip_char(tty, 0, - TTY_NORMAL); - } - } else { - tty_insert_flip_char(tty, data, TTY_NORMAL); - } - } else { - /* there was a software buffer overrun - and nothing could be done about it!!! */ - } - } - tty_schedule_flip(tty); - /* end of service */ - base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; - return IRQ_HANDLED; -} /* cy_rxerr_interrupt */ - -static irqreturn_t cd2401_modem_interrupt(int irq, void *dev_id) -{ - struct cyclades_port *info; - volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; - int channel; - int mdm_change; - int mdm_status; - - /* determine the channel and change to that context */ - channel = (u_short) (base_addr[CyLICR] >> 2); - info = &cy_port[channel]; - info->last_active = jiffies; - - mdm_change = base_addr[CyMISR]; - mdm_status = base_addr[CyMSVR1]; - - if (info->tty == 0) { /* nowhere to put the data, ignore it */ - ; - } else { - if ((mdm_change & CyDCD) - && (info->flags & ASYNC_CHECK_CD)) { - if (mdm_status & CyDCD) { -/* CP('!'); */ - wake_up_interruptible(&info->open_wait); - } else { -/* CP('@'); */ - tty_hangup(info->tty); - wake_up_interruptible(&info->open_wait); - info->flags &= ~ASYNC_NORMAL_ACTIVE; - } - } - if ((mdm_change & CyCTS) - && (info->flags & ASYNC_CTS_FLOW)) { - if (info->tty->stopped) { - if (mdm_status & CyCTS) { - /* !!! cy_start isn't used because... */ - info->tty->stopped = 0; - base_addr[CyIER] |= CyTxMpty; - tty_wakeup(info->tty); - } - } else { - if (!(mdm_status & CyCTS)) { - /* !!! cy_stop isn't used because... */ - info->tty->stopped = 1; - base_addr[CyIER] &= - ~(CyTxMpty | CyTxRdy); - } - } - } - if (mdm_status & CyDSR) { - } - } - base_addr[CyMEOIR] = 0; - return IRQ_HANDLED; -} /* cy_modem_interrupt */ - -static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id) -{ - struct cyclades_port *info; - volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; - int channel; - int char_count, saved_cnt; - int outch; - - /* determine the channel and change to that context */ - channel = (u_short) (base_addr[CyLICR] >> 2); - - /* validate the port number (as configured and open) */ - if ((channel < 0) || (NR_PORTS <= channel)) { - base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); - base_addr[CyTEOIR] = CyNOTRANS; - return IRQ_HANDLED; - } - info = &cy_port[channel]; - info->last_active = jiffies; - if (info->tty == 0) { - base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); - base_addr[CyTEOIR] = CyNOTRANS; - return IRQ_HANDLED; - } - - /* load the on-chip space available for outbound data */ - saved_cnt = char_count = base_addr[CyTFTC]; - - if (info->x_char) { /* send special char */ - outch = info->x_char; - base_addr[CyTDR] = outch; - char_count--; - info->x_char = 0; - } - - if (info->x_break) { - /* The Cirrus chip requires the "Embedded Transmit - Commands" of start break, delay, and end break - sequences to be sent. The duration of the - break is given in TICs, which runs at HZ - (typically 100) and the PPR runs at 200 Hz, - so the delay is duration * 200/HZ, and thus a - break can run from 1/100 sec to about 5/4 sec. - Need to check these values - RGH 141095. - */ - base_addr[CyTDR] = 0; /* start break */ - base_addr[CyTDR] = 0x81; - base_addr[CyTDR] = 0; /* delay a bit */ - base_addr[CyTDR] = 0x82; - base_addr[CyTDR] = info->x_break * 200 / HZ; - base_addr[CyTDR] = 0; /* terminate break */ - base_addr[CyTDR] = 0x83; - char_count -= 7; - info->x_break = 0; - } - - while (char_count > 0) { - if (!info->xmit_cnt) { - base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); - break; - } - if (info->xmit_buf == 0) { - base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); - break; - } - if (info->tty->stopped || info->tty->hw_stopped) { - base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); - break; - } - /* Because the Embedded Transmit Commands have been - enabled, we must check to see if the escape - character, NULL, is being sent. If it is, we - must ensure that there is room for it to be - doubled in the output stream. Therefore we - no longer advance the pointer when the character - is fetched, but rather wait until after the check - for a NULL output character. (This is necessary - because there may not be room for the two chars - needed to send a NULL. - */ - outch = info->xmit_buf[info->xmit_tail]; - if (outch) { - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR] = outch; - char_count--; - } else { - if (char_count > 1) { - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR] = outch; - base_addr[CyTDR] = 0; - char_count--; - char_count--; - } else { - break; - } - } - } - - if (info->xmit_cnt < WAKEUP_CHARS) - tty_wakeup(info->tty); - - base_addr[CyTEOIR] = (char_count != saved_cnt) ? 0 : CyNOTRANS; - return IRQ_HANDLED; -} /* cy_tx_interrupt */ - -static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id) -{ - struct tty_struct *tty; - struct cyclades_port *info; - volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; - int channel; - char data; - int char_count; - int save_cnt; - - /* determine the channel and change to that context */ - channel = (u_short) (base_addr[CyLICR] >> 2); - info = &cy_port[channel]; - info->last_active = jiffies; - save_cnt = char_count = base_addr[CyRFOC]; - - /* if there is nowhere to put the data, discard it */ - if (info->tty == 0) { - while (char_count--) { - data = base_addr[CyRDR]; - } - } else { /* there is an open port for this data */ - tty = info->tty; - /* load # characters available from the chip */ - -#ifdef CYCLOM_ENABLE_MONITORING - ++info->mon.int_count; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; -#endif - while (char_count--) { - data = base_addr[CyRDR]; - tty_insert_flip_char(tty, data, TTY_NORMAL); -#ifdef CYCLOM_16Y_HACK - udelay(10L); -#endif - } - tty_schedule_flip(tty); - } - /* end of service */ - base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS; - return IRQ_HANDLED; -} /* cy_rx_interrupt */ - -/* This is called whenever a port becomes active; - interrupts are enabled and DTR & RTS are turned on. - */ -static int startup(struct cyclades_port *info) -{ - unsigned long flags; - volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; - int channel; - - if (info->flags & ASYNC_INITIALIZED) { - return 0; - } - - if (!info->type) { - if (info->tty) { - set_bit(TTY_IO_ERROR, &info->tty->flags); - } - return 0; - } - if (!info->xmit_buf) { - info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); - if (!info->xmit_buf) { - return -ENOMEM; - } - } - - config_setup(info); - - channel = info->line; - -#ifdef SERIAL_DEBUG_OPEN - printk("startup channel %d\n", channel); -#endif - - local_irq_save(flags); - base_addr[CyCAR] = (u_char) channel; - write_cy_cmd(base_addr, CyENB_RCVR | CyENB_XMTR); - - base_addr[CyCAR] = (u_char) channel; /* !!! Is this needed? */ - base_addr[CyMSVR1] = CyRTS; -/* CP('S');CP('1'); */ - base_addr[CyMSVR2] = CyDTR; - -#ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], - base_addr[CyMSVR2]); -#endif - - base_addr[CyIER] |= CyRxData; - info->flags |= ASYNC_INITIALIZED; - - if (info->tty) { - clear_bit(TTY_IO_ERROR, &info->tty->flags); - } - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - local_irq_restore(flags); - -#ifdef SERIAL_DEBUG_OPEN - printk(" done\n"); -#endif - return 0; -} /* startup */ - -void start_xmit(struct cyclades_port *info) -{ - unsigned long flags; - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int channel; - - channel = info->line; - local_irq_save(flags); - base_addr[CyCAR] = channel; - base_addr[CyIER] |= CyTxMpty; - local_irq_restore(flags); -} /* start_xmit */ - -/* - * This routine shuts down a serial port; interrupts are disabled, - * and DTR is dropped if the hangup on close termio flag is on. - */ -static void shutdown(struct cyclades_port *info) -{ - unsigned long flags; - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int channel; - - if (!(info->flags & ASYNC_INITIALIZED)) { -/* CP('$'); */ - return; - } - - channel = info->line; - -#ifdef SERIAL_DEBUG_OPEN - printk("shutdown channel %d\n", channel); -#endif - - /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE - SENT BEFORE DROPPING THE LINE !!! (Perhaps - set some flag that is read when XMTY happens.) - Other choices are to delay some fixed interval - or schedule some later processing. - */ - local_irq_save(flags); - if (info->xmit_buf) { - free_page((unsigned long)info->xmit_buf); - info->xmit_buf = NULL; - } - - base_addr[CyCAR] = (u_char) channel; - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { - base_addr[CyMSVR1] = 0; -/* CP('C');CP('1'); */ - base_addr[CyMSVR2] = 0; -#ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: dropping DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], - base_addr[CyMSVR2]); -#endif - } - write_cy_cmd(base_addr, CyDIS_RCVR); - /* it may be appropriate to clear _XMIT at - some later date (after testing)!!! */ - - if (info->tty) { - set_bit(TTY_IO_ERROR, &info->tty->flags); - } - info->flags &= ~ASYNC_INITIALIZED; - local_irq_restore(flags); - -#ifdef SERIAL_DEBUG_OPEN - printk(" done\n"); -#endif -} /* shutdown */ - -/* - * This routine finds or computes the various line characteristics. - */ -static void config_setup(struct cyclades_port *info) -{ - unsigned long flags; - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int channel; - unsigned cflag; - int i; - unsigned char ti, need_init_chan = 0; - - if (!info->tty || !info->tty->termios) { - return; - } - if (info->line == -1) { - return; - } - cflag = info->tty->termios->c_cflag; - - /* baud rate */ - i = cflag & CBAUD; -#ifdef CBAUDEX -/* Starting with kernel 1.1.65, there is direct support for - higher baud rates. The following code supports those - changes. The conditional aspect allows this driver to be - used for earlier as well as later kernel versions. (The - mapping is slightly different from serial.c because there - is still the possibility of supporting 75 kbit/sec with - the Cyclades board.) - */ - if (i & CBAUDEX) { - if (i == B57600) - i = 16; - else if (i == B115200) - i = 18; -#ifdef B78600 - else if (i == B78600) - i = 17; -#endif - else - info->tty->termios->c_cflag &= ~CBAUDEX; - } -#endif - if (i == 15) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - i += 1; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - i += 3; - } - /* Don't ever change the speed of the console port. It will - * run at the speed specified in bootinfo, or at 19.2K */ - /* Actually, it should run at whatever speed 166Bug was using */ - /* Note info->timeout isn't used at present */ - if (info != serial_console_info) { - info->tbpr = baud_bpr[i]; /* Tx BPR */ - info->tco = baud_co[i]; /* Tx CO */ - info->rbpr = baud_bpr[i]; /* Rx BPR */ - info->rco = baud_co[i] >> 5; /* Rx CO */ - if (baud_table[i] == 134) { - info->timeout = - (info->xmit_fifo_size * HZ * 30 / 269) + 2; - /* get it right for 134.5 baud */ - } else if (baud_table[i]) { - info->timeout = - (info->xmit_fifo_size * HZ * 15 / baud_table[i]) + - 2; - /* this needs to be propagated into the card info */ - } else { - info->timeout = 0; - } - } - /* By tradition (is it a standard?) a baud rate of zero - implies the line should be/has been closed. A bit - later in this routine such a test is performed. */ - - /* byte size and parity */ - info->cor7 = 0; - info->cor6 = 0; - info->cor5 = 0; - info->cor4 = (info->default_threshold ? info->default_threshold : baud_cor4[i]); /* receive threshold */ - /* Following two lines added 101295, RGH. */ - /* It is obviously wrong to access CyCORx, and not info->corx here, - * try and remember to fix it later! */ - channel = info->line; - base_addr[CyCAR] = (u_char) channel; - if (C_CLOCAL(info->tty)) { - if (base_addr[CyIER] & CyMdmCh) - base_addr[CyIER] &= ~CyMdmCh; /* without modem intr */ - /* ignore 1->0 modem transitions */ - if (base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD)) - base_addr[CyCOR4] &= ~(CyDSR | CyCTS | CyDCD); - /* ignore 0->1 modem transitions */ - if (base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD)) - base_addr[CyCOR5] &= ~(CyDSR | CyCTS | CyDCD); - } else { - if ((base_addr[CyIER] & CyMdmCh) != CyMdmCh) - base_addr[CyIER] |= CyMdmCh; /* with modem intr */ - /* act on 1->0 modem transitions */ - if ((base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD)) != - (CyDSR | CyCTS | CyDCD)) - base_addr[CyCOR4] |= CyDSR | CyCTS | CyDCD; - /* act on 0->1 modem transitions */ - if ((base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD)) != - (CyDSR | CyCTS | CyDCD)) - base_addr[CyCOR5] |= CyDSR | CyCTS | CyDCD; - } - info->cor3 = (cflag & CSTOPB) ? Cy_2_STOP : Cy_1_STOP; - info->cor2 = CyETC; - switch (cflag & CSIZE) { - case CS5: - info->cor1 = Cy_5_BITS; - break; - case CS6: - info->cor1 = Cy_6_BITS; - break; - case CS7: - info->cor1 = Cy_7_BITS; - break; - case CS8: - info->cor1 = Cy_8_BITS; - break; - } - if (cflag & PARENB) { - if (cflag & PARODD) { - info->cor1 |= CyPARITY_O; - } else { - info->cor1 |= CyPARITY_E; - } - } else { - info->cor1 |= CyPARITY_NONE; - } - - /* CTS flow control flag */ -#if 0 - /* Don't complcate matters for now! RGH 141095 */ - if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; - info->cor2 |= CyCtsAE; - } else { - info->flags &= ~ASYNC_CTS_FLOW; - info->cor2 &= ~CyCtsAE; - } -#endif - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else - info->flags |= ASYNC_CHECK_CD; - - /*********************************************** - The hardware option, CyRtsAO, presents RTS when - the chip has characters to send. Since most modems - use RTS as reverse (inbound) flow control, this - option is not used. If inbound flow control is - necessary, DTR can be programmed to provide the - appropriate signals for use with a non-standard - cable. Contact Marcio Saito for details. - ***********************************************/ - - channel = info->line; - - local_irq_save(flags); - base_addr[CyCAR] = (u_char) channel; - - /* CyCMR set once only in mvme167_init_serial() */ - if (base_addr[CyLICR] != channel << 2) - base_addr[CyLICR] = channel << 2; - if (base_addr[CyLIVR] != 0x5c) - base_addr[CyLIVR] = 0x5c; - - /* tx and rx baud rate */ - - if (base_addr[CyCOR1] != info->cor1) - need_init_chan = 1; - if (base_addr[CyTCOR] != info->tco) - base_addr[CyTCOR] = info->tco; - if (base_addr[CyTBPR] != info->tbpr) - base_addr[CyTBPR] = info->tbpr; - if (base_addr[CyRCOR] != info->rco) - base_addr[CyRCOR] = info->rco; - if (base_addr[CyRBPR] != info->rbpr) - base_addr[CyRBPR] = info->rbpr; - - /* set line characteristics according configuration */ - - if (base_addr[CySCHR1] != START_CHAR(info->tty)) - base_addr[CySCHR1] = START_CHAR(info->tty); - if (base_addr[CySCHR2] != STOP_CHAR(info->tty)) - base_addr[CySCHR2] = STOP_CHAR(info->tty); - if (base_addr[CySCRL] != START_CHAR(info->tty)) - base_addr[CySCRL] = START_CHAR(info->tty); - if (base_addr[CySCRH] != START_CHAR(info->tty)) - base_addr[CySCRH] = START_CHAR(info->tty); - if (base_addr[CyCOR1] != info->cor1) - base_addr[CyCOR1] = info->cor1; - if (base_addr[CyCOR2] != info->cor2) - base_addr[CyCOR2] = info->cor2; - if (base_addr[CyCOR3] != info->cor3) - base_addr[CyCOR3] = info->cor3; - if (base_addr[CyCOR4] != info->cor4) - base_addr[CyCOR4] = info->cor4; - if (base_addr[CyCOR5] != info->cor5) - base_addr[CyCOR5] = info->cor5; - if (base_addr[CyCOR6] != info->cor6) - base_addr[CyCOR6] = info->cor6; - if (base_addr[CyCOR7] != info->cor7) - base_addr[CyCOR7] = info->cor7; - - if (need_init_chan) - write_cy_cmd(base_addr, CyINIT_CHAN); - - base_addr[CyCAR] = (u_char) channel; /* !!! Is this needed? */ - - /* 2ms default rx timeout */ - ti = info->default_timeout ? info->default_timeout : 0x02; - if (base_addr[CyRTPRL] != ti) - base_addr[CyRTPRL] = ti; - if (base_addr[CyRTPRH] != 0) - base_addr[CyRTPRH] = 0; - - /* Set up RTS here also ????? RGH 141095 */ - if (i == 0) { /* baud rate is zero, turn off line */ - if ((base_addr[CyMSVR2] & CyDTR) == CyDTR) - base_addr[CyMSVR2] = 0; -#ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: dropping DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], - base_addr[CyMSVR2]); -#endif - } else { - if ((base_addr[CyMSVR2] & CyDTR) != CyDTR) - base_addr[CyMSVR2] = CyDTR; -#ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], - base_addr[CyMSVR2]); -#endif - } - - if (info->tty) { - clear_bit(TTY_IO_ERROR, &info->tty->flags); - } - - local_irq_restore(flags); - -} /* config_setup */ - -static int cy_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct cyclades_port *info = tty->driver_data; - unsigned long flags; - -#ifdef SERIAL_DEBUG_IO - printk("cy_put_char %s(0x%02x)\n", tty->name, ch); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_put_char")) - return 0; - - if (!info->xmit_buf) - return 0; - - local_irq_save(flags); - if (info->xmit_cnt >= PAGE_SIZE - 1) { - local_irq_restore(flags); - return 0; - } - - info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= PAGE_SIZE - 1; - info->xmit_cnt++; - local_irq_restore(flags); - return 1; -} /* cy_put_char */ - -static void cy_flush_chars(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - unsigned long flags; - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int channel; - -#ifdef SERIAL_DEBUG_IO - printk("cy_flush_chars %s\n", tty->name); /* */ -#endif - - if (serial_paranoia_check(info, tty->name, "cy_flush_chars")) - return; - - if (info->xmit_cnt <= 0 || tty->stopped - || tty->hw_stopped || !info->xmit_buf) - return; - - channel = info->line; - - local_irq_save(flags); - base_addr[CyCAR] = channel; - base_addr[CyIER] |= CyTxMpty; - local_irq_restore(flags); -} /* cy_flush_chars */ - -/* This routine gets called when tty_write has put something into - the write_queue. If the port is not already transmitting stuff, - start it off by enabling interrupts. The interrupt service - routine will then ensure that the characters are sent. If the - port is already active, there is no need to kick it. - */ -static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - struct cyclades_port *info = tty->driver_data; - unsigned long flags; - int c, total = 0; - -#ifdef SERIAL_DEBUG_IO - printk("cy_write %s\n", tty->name); /* */ -#endif - - if (serial_paranoia_check(info, tty->name, "cy_write")) { - return 0; - } - - if (!info->xmit_buf) { - return 0; - } - - while (1) { - local_irq_save(flags); - c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0) { - local_irq_restore(flags); - break; - } - - memcpy(info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = - (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1); - info->xmit_cnt += c; - local_irq_restore(flags); - - buf += c; - count -= c; - total += c; - } - - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { - start_xmit(info); - } - return total; -} /* cy_write */ - -static int cy_write_room(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - int ret; - -#ifdef SERIAL_DEBUG_IO - printk("cy_write_room %s\n", tty->name); /* */ -#endif - - if (serial_paranoia_check(info, tty->name, "cy_write_room")) - return 0; - ret = PAGE_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; -} /* cy_write_room */ - -static int cy_chars_in_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - -#ifdef SERIAL_DEBUG_IO - printk("cy_chars_in_buffer %s %d\n", tty->name, info->xmit_cnt); /* */ -#endif - - if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer")) - return 0; - - return info->xmit_cnt; -} /* cy_chars_in_buffer */ - -static void cy_flush_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - unsigned long flags; - -#ifdef SERIAL_DEBUG_IO - printk("cy_flush_buffer %s\n", tty->name); /* */ -#endif - - if (serial_paranoia_check(info, tty->name, "cy_flush_buffer")) - return; - local_irq_save(flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - local_irq_restore(flags); - tty_wakeup(tty); -} /* cy_flush_buffer */ - -/* This routine is called by the upper-layer tty layer to signal - that incoming characters should be throttled or that the - throttle should be released. - */ -static void cy_throttle(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - unsigned long flags; - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int channel; - -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); - printk("cy_throttle %s\n", tty->name); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_nthrottle")) { - return; - } - - if (I_IXOFF(tty)) { - info->x_char = STOP_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ - } - - channel = info->line; - - local_irq_save(flags); - base_addr[CyCAR] = (u_char) channel; - base_addr[CyMSVR1] = 0; - local_irq_restore(flags); -} /* cy_throttle */ - -static void cy_unthrottle(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - unsigned long flags; - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int channel; - -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); - printk("cy_unthrottle %s\n", tty->name); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_nthrottle")) { - return; - } - - if (I_IXOFF(tty)) { - info->x_char = START_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ - } - - channel = info->line; - - local_irq_save(flags); - base_addr[CyCAR] = (u_char) channel; - base_addr[CyMSVR1] = CyRTS; - local_irq_restore(flags); -} /* cy_unthrottle */ - -static int -get_serial_info(struct cyclades_port *info, - struct serial_struct __user * retinfo) -{ - struct serial_struct tmp; - -/* CP('g'); */ - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->line; - tmp.port = info->line; - tmp.irq = 0; - tmp.flags = info->flags; - tmp.baud_base = 0; /*!!! */ - tmp.close_delay = info->close_delay; - tmp.custom_divisor = 0; /*!!! */ - tmp.hub6 = 0; /*!!! */ - return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; -} /* get_serial_info */ - -static int -set_serial_info(struct cyclades_port *info, - struct serial_struct __user * new_info) -{ - struct serial_struct new_serial; - struct cyclades_port old_info; - -/* CP('s'); */ - if (!new_info) - return -EFAULT; - if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; - old_info = *info; - - if (!capable(CAP_SYS_ADMIN)) { - if ((new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != - (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) - return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - goto check_and_exit; - } - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - info->flags = ((info->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - info->close_delay = new_serial.close_delay; - -check_and_exit: - if (info->flags & ASYNC_INITIALIZED) { - config_setup(info); - return 0; - } - return startup(info); -} /* set_serial_info */ - -static int cy_tiocmget(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - int channel; - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - unsigned long flags; - unsigned char status; - - channel = info->line; - - local_irq_save(flags); - base_addr[CyCAR] = (u_char) channel; - status = base_addr[CyMSVR1] | base_addr[CyMSVR2]; - local_irq_restore(flags); - - return ((status & CyRTS) ? TIOCM_RTS : 0) - | ((status & CyDTR) ? TIOCM_DTR : 0) - | ((status & CyDCD) ? TIOCM_CAR : 0) - | ((status & CyDSR) ? TIOCM_DSR : 0) - | ((status & CyCTS) ? TIOCM_CTS : 0); -} /* cy_tiocmget */ - -static int -cy_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) -{ - struct cyclades_port *info = tty->driver_data; - int channel; - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - unsigned long flags; - - channel = info->line; - - if (set & TIOCM_RTS) { - local_irq_save(flags); - base_addr[CyCAR] = (u_char) channel; - base_addr[CyMSVR1] = CyRTS; - local_irq_restore(flags); - } - if (set & TIOCM_DTR) { - local_irq_save(flags); - base_addr[CyCAR] = (u_char) channel; -/* CP('S');CP('2'); */ - base_addr[CyMSVR2] = CyDTR; -#ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], - base_addr[CyMSVR2]); -#endif - local_irq_restore(flags); - } - - if (clear & TIOCM_RTS) { - local_irq_save(flags); - base_addr[CyCAR] = (u_char) channel; - base_addr[CyMSVR1] = 0; - local_irq_restore(flags); - } - if (clear & TIOCM_DTR) { - local_irq_save(flags); - base_addr[CyCAR] = (u_char) channel; -/* CP('C');CP('2'); */ - base_addr[CyMSVR2] = 0; -#ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: dropping DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], - base_addr[CyMSVR2]); -#endif - local_irq_restore(flags); - } - - return 0; -} /* set_modem_info */ - -static void send_break(struct cyclades_port *info, int duration) -{ /* Let the transmit ISR take care of this (since it - requires stuffing characters into the output stream). - */ - info->x_break = duration; - if (!info->xmit_cnt) { - start_xmit(info); - } -} /* send_break */ - -static int -get_mon_info(struct cyclades_port *info, struct cyclades_monitor __user * mon) -{ - - if (copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor))) - return -EFAULT; - info->mon.int_count = 0; - info->mon.char_count = 0; - info->mon.char_max = 0; - info->mon.char_last = 0; - return 0; -} - -static int set_threshold(struct cyclades_port *info, unsigned long __user * arg) -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - unsigned long value; - int channel; - - if (get_user(value, arg)) - return -EFAULT; - - channel = info->line; - info->cor4 &= ~CyREC_FIFO; - info->cor4 |= value & CyREC_FIFO; - base_addr[CyCOR4] = info->cor4; - return 0; -} - -static int -get_threshold(struct cyclades_port *info, unsigned long __user * value) -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int channel; - unsigned long tmp; - - channel = info->line; - - tmp = base_addr[CyCOR4] & CyREC_FIFO; - return put_user(tmp, value); -} - -static int -set_default_threshold(struct cyclades_port *info, unsigned long __user * arg) -{ - unsigned long value; - - if (get_user(value, arg)) - return -EFAULT; - - info->default_threshold = value & 0x0f; - return 0; -} - -static int -get_default_threshold(struct cyclades_port *info, unsigned long __user * value) -{ - return put_user(info->default_threshold, value); -} - -static int set_timeout(struct cyclades_port *info, unsigned long __user * arg) -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int channel; - unsigned long value; - - if (get_user(value, arg)) - return -EFAULT; - - channel = info->line; - - base_addr[CyRTPRL] = value & 0xff; - base_addr[CyRTPRH] = (value >> 8) & 0xff; - return 0; -} - -static int get_timeout(struct cyclades_port *info, unsigned long __user * value) -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int channel; - unsigned long tmp; - - channel = info->line; - - tmp = base_addr[CyRTPRL]; - return put_user(tmp, value); -} - -static int set_default_timeout(struct cyclades_port *info, unsigned long value) -{ - info->default_timeout = value & 0xff; - return 0; -} - -static int -get_default_timeout(struct cyclades_port *info, unsigned long __user * value) -{ - return put_user(info->default_timeout, value); -} - -static int -cy_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct cyclades_port *info = tty->driver_data; - int ret_val = 0; - void __user *argp = (void __user *)arg; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg); /* */ -#endif - - tty_lock(); - - switch (cmd) { - case CYGETMON: - ret_val = get_mon_info(info, argp); - break; - case CYGETTHRESH: - ret_val = get_threshold(info, argp); - break; - case CYSETTHRESH: - ret_val = set_threshold(info, argp); - break; - case CYGETDEFTHRESH: - ret_val = get_default_threshold(info, argp); - break; - case CYSETDEFTHRESH: - ret_val = set_default_threshold(info, argp); - break; - case CYGETTIMEOUT: - ret_val = get_timeout(info, argp); - break; - case CYSETTIMEOUT: - ret_val = set_timeout(info, argp); - break; - case CYGETDEFTIMEOUT: - ret_val = get_default_timeout(info, argp); - break; - case CYSETDEFTIMEOUT: - ret_val = set_default_timeout(info, (unsigned long)arg); - break; - case TCSBRK: /* SVID version: non-zero arg --> no break */ - ret_val = tty_check_change(tty); - if (ret_val) - break; - tty_wait_until_sent(tty, 0); - if (!arg) - send_break(info, HZ / 4); /* 1/4 second */ - break; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - ret_val = tty_check_change(tty); - if (ret_val) - break; - tty_wait_until_sent(tty, 0); - send_break(info, arg ? arg * (HZ / 10) : HZ / 4); - break; - -/* The following commands are incompletely implemented!!! */ - case TIOCGSERIAL: - ret_val = get_serial_info(info, argp); - break; - case TIOCSSERIAL: - ret_val = set_serial_info(info, argp); - break; - default: - ret_val = -ENOIOCTLCMD; - } - tty_unlock(); - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_ioctl done\n"); -#endif - - return ret_val; -} /* cy_ioctl */ - -static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct cyclades_port *info = tty->driver_data; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_set_termios %s\n", tty->name); -#endif - - if (tty->termios->c_cflag == old_termios->c_cflag) - return; - config_setup(info); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->stopped = 0; - cy_start(tty); - } -#ifdef tytso_patch_94Nov25_1726 - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); -#endif -} /* cy_set_termios */ - -static void cy_close(struct tty_struct *tty, struct file *filp) -{ - struct cyclades_port *info = tty->driver_data; - -/* CP('C'); */ -#ifdef SERIAL_DEBUG_OTHER - printk("cy_close %s\n", tty->name); -#endif - - if (!info || serial_paranoia_check(info, tty->name, "cy_close")) { - return; - } -#ifdef SERIAL_DEBUG_OPEN - printk("cy_close %s, count = %d\n", tty->name, info->count); -#endif - - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("cy_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: decrementing count to %d\n", __LINE__, - info->count - 1); -#endif - if (--info->count < 0) { - printk("cy_close: bad serial port count for ttys%d: %d\n", - info->line, info->count); -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: setting count to 0\n", __LINE__); -#endif - info->count = 0; - } - if (info->count) - return; - info->flags |= ASYNC_CLOSING; - if (info->flags & ASYNC_INITIALIZED) - tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ - shutdown(info); - cy_flush_buffer(tty); - tty_ldisc_flush(tty); - info->tty = NULL; - if (info->blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs - (info->close_delay)); - } - wake_up_interruptible(&info->open_wait); - } - info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_close done\n"); -#endif -} /* cy_close */ - -/* - * cy_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -void cy_hangup(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_hangup %s\n", tty->name); /* */ -#endif - - if (serial_paranoia_check(info, tty->name, "cy_hangup")) - return; - - shutdown(info); -#if 0 - info->event = 0; - info->count = 0; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: setting count to 0\n", __LINE__); -#endif - info->tty = 0; -#endif - info->flags &= ~ASYNC_NORMAL_ACTIVE; - wake_up_interruptible(&info->open_wait); -} /* cy_hangup */ - -/* - * ------------------------------------------------------------ - * cy_open() and friends - * ------------------------------------------------------------ - */ - -static int -block_til_ready(struct tty_struct *tty, struct file *filp, - struct cyclades_port *info) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int channel; - int retval; - volatile u_char *base_addr = (u_char *) BASE_ADDR; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&info->close_wait); - if (info->flags & ASYNC_HUP_NOTIFY) { - return -EAGAIN; - } else { - return -ERESTARTSYS; - } - } - - /* - * If non-blocking mode is set, then make the check up front - * and then exit. - */ - if (filp->f_flags & O_NONBLOCK) { - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * cy_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready before block: %s, count = %d\n", - tty->name, info->count); - /**/ -#endif - info->count--; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count); -#endif - info->blocked_open++; - - channel = info->line; - - while (1) { - local_irq_save(flags); - base_addr[CyCAR] = (u_char) channel; - base_addr[CyMSVR1] = CyRTS; -/* CP('S');CP('4'); */ - base_addr[CyMSVR2] = CyDTR; -#ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], - base_addr[CyMSVR2]); -#endif - local_irq_restore(flags); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) - || !(info->flags & ASYNC_INITIALIZED)) { - if (info->flags & ASYNC_HUP_NOTIFY) { - retval = -EAGAIN; - } else { - retval = -ERESTARTSYS; - } - break; - } - local_irq_save(flags); - base_addr[CyCAR] = (u_char) channel; -/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */ - if (!(info->flags & ASYNC_CLOSING) - && (C_CLOCAL(tty) - || (base_addr[CyMSVR1] & CyDCD))) { - local_irq_restore(flags); - break; - } - local_irq_restore(flags); - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: %s, count = %d\n", - tty->name, info->count); - /**/ -#endif - tty_unlock(); - schedule(); - tty_lock(); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) { - info->count++; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: incrementing count to %d\n", __LINE__, - info->count); -#endif - } - info->blocked_open--; -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: %s, count = %d\n", - tty->name, info->count); - /**/ -#endif - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} /* block_til_ready */ - -/* - * This routine is called whenever a serial port is opened. It - * performs the serial-specific initialization for the tty structure. - */ -int cy_open(struct tty_struct *tty, struct file *filp) -{ - struct cyclades_port *info; - int retval, line; - -/* CP('O'); */ - line = tty->index; - if ((line < 0) || (NR_PORTS <= line)) { - return -ENODEV; - } - info = &cy_port[line]; - if (info->line < 0) { - return -ENODEV; - } -#ifdef SERIAL_DEBUG_OTHER - printk("cy_open %s\n", tty->name); /* */ -#endif - if (serial_paranoia_check(info, tty->name, "cy_open")) { - return -ENODEV; - } -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open %s, count = %d\n", tty->name, info->count); - /**/ -#endif - info->count++; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count); -#endif - tty->driver_data = info; - info->tty = tty; - - /* - * Start up serial port - */ - retval = startup(info); - if (retval) { - return retval; - } - - retval = block_til_ready(tty, filp, info); - if (retval) { -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open returning after block_til_ready with %d\n", - retval); -#endif - return retval; - } -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open done\n"); - /**/ -#endif - return 0; -} /* cy_open */ - -/* - * --------------------------------------------------------------------- - * serial167_init() and friends - * - * serial167_init() is called at boot-time to initialize the serial driver. - * --------------------------------------------------------------------- - */ - -/* - * This routine prints out the appropriate serial driver version - * number, and identifies which options were configured into this - * driver. - */ -static void show_version(void) -{ - printk("MVME166/167 cd2401 driver\n"); -} /* show_version */ - -/* initialize chips on card -- return number of valid - chips (which is number of ports/4) */ - -/* - * This initialises the hardware to a reasonable state. It should - * probe the chip first so as to copy 166-Bug setup as a default for - * port 0. It initialises CMR to CyASYNC; that is never done again, so - * as to limit the number of CyINIT_CHAN commands in normal running. - * - * ... I wonder what I should do if this fails ... - */ - -void mvme167_serial_console_setup(int cflag) -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int ch; - u_char spd; - u_char rcor, rbpr, badspeed = 0; - unsigned long flags; - - local_irq_save(flags); - - /* - * First probe channel zero of the chip, to see what speed has - * been selected. - */ - - base_addr[CyCAR] = 0; - - rcor = base_addr[CyRCOR] << 5; - rbpr = base_addr[CyRBPR]; - - for (spd = 0; spd < sizeof(baud_bpr); spd++) - if (rbpr == baud_bpr[spd] && rcor == baud_co[spd]) - break; - if (spd >= sizeof(baud_bpr)) { - spd = 14; /* 19200 */ - badspeed = 1; /* Failed to identify speed */ - } - initial_console_speed = spd; - - /* OK, we have chosen a speed, now reset and reinitialise */ - - my_udelay(20000L); /* Allow time for any active o/p to complete */ - if (base_addr[CyCCR] != 0x00) { - local_irq_restore(flags); - /* printk(" chip is never idle (CCR != 0)\n"); */ - return; - } - - base_addr[CyCCR] = CyCHIP_RESET; /* Reset the chip */ - my_udelay(1000L); - - if (base_addr[CyGFRCR] == 0x00) { - local_irq_restore(flags); - /* printk(" chip is not responding (GFRCR stayed 0)\n"); */ - return; - } - - /* - * System clock is 20Mhz, divided by 2048, so divide by 10 for a 1.0ms - * tick - */ - - base_addr[CyTPR] = 10; - - base_addr[CyPILR1] = 0x01; /* Interrupt level for modem change */ - base_addr[CyPILR2] = 0x02; /* Interrupt level for tx ints */ - base_addr[CyPILR3] = 0x03; /* Interrupt level for rx ints */ - - /* - * Attempt to set up all channels to something reasonable, and - * bang out a INIT_CHAN command. We should then be able to limit - * the amount of fiddling we have to do in normal running. - */ - - for (ch = 3; ch >= 0; ch--) { - base_addr[CyCAR] = (u_char) ch; - base_addr[CyIER] = 0; - base_addr[CyCMR] = CyASYNC; - base_addr[CyLICR] = (u_char) ch << 2; - base_addr[CyLIVR] = 0x5c; - base_addr[CyTCOR] = baud_co[spd]; - base_addr[CyTBPR] = baud_bpr[spd]; - base_addr[CyRCOR] = baud_co[spd] >> 5; - base_addr[CyRBPR] = baud_bpr[spd]; - base_addr[CySCHR1] = 'Q' & 0x1f; - base_addr[CySCHR2] = 'X' & 0x1f; - base_addr[CySCRL] = 0; - base_addr[CySCRH] = 0; - base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE; - base_addr[CyCOR2] = 0; - base_addr[CyCOR3] = Cy_1_STOP; - base_addr[CyCOR4] = baud_cor4[spd]; - base_addr[CyCOR5] = 0; - base_addr[CyCOR6] = 0; - base_addr[CyCOR7] = 0; - base_addr[CyRTPRL] = 2; - base_addr[CyRTPRH] = 0; - base_addr[CyMSVR1] = 0; - base_addr[CyMSVR2] = 0; - write_cy_cmd(base_addr, CyINIT_CHAN | CyDIS_RCVR | CyDIS_XMTR); - } - - /* - * Now do specials for channel zero.... - */ - - base_addr[CyMSVR1] = CyRTS; - base_addr[CyMSVR2] = CyDTR; - base_addr[CyIER] = CyRxData; - write_cy_cmd(base_addr, CyENB_RCVR | CyENB_XMTR); - - local_irq_restore(flags); - - my_udelay(20000L); /* Let it all settle down */ - - printk("CD2401 initialised, chip is rev 0x%02x\n", base_addr[CyGFRCR]); - if (badspeed) - printk - (" WARNING: Failed to identify line speed, rcor=%02x,rbpr=%02x\n", - rcor >> 5, rbpr); -} /* serial_console_init */ - -static const struct tty_operations cy_ops = { - .open = cy_open, - .close = cy_close, - .write = cy_write, - .put_char = cy_put_char, - .flush_chars = cy_flush_chars, - .write_room = cy_write_room, - .chars_in_buffer = cy_chars_in_buffer, - .flush_buffer = cy_flush_buffer, - .ioctl = cy_ioctl, - .throttle = cy_throttle, - .unthrottle = cy_unthrottle, - .set_termios = cy_set_termios, - .stop = cy_stop, - .start = cy_start, - .hangup = cy_hangup, - .tiocmget = cy_tiocmget, - .tiocmset = cy_tiocmset, -}; - -/* The serial driver boot-time initialization code! - Hardware I/O ports are mapped to character special devices on a - first found, first allocated manner. That is, this code searches - for Cyclom cards in the system. As each is found, it is probed - to discover how many chips (and thus how many ports) are present. - These ports are mapped to the tty ports 64 and upward in monotonic - fashion. If an 8-port card is replaced with a 16-port card, the - port mapping on a following card will shift. - - This approach is different from what is used in the other serial - device driver because the Cyclom is more properly a multiplexer, - not just an aggregation of serial ports on one card. - - If there are more cards with more ports than have been statically - allocated above, a warning is printed and the extra ports are ignored. - */ -static int __init serial167_init(void) -{ - struct cyclades_port *info; - int ret = 0; - int good_ports = 0; - int port_num = 0; - int index; - int DefSpeed; -#ifdef notyet - struct sigaction sa; -#endif - - if (!(mvme16x_config & MVME16x_CONFIG_GOT_CD2401)) - return 0; - - cy_serial_driver = alloc_tty_driver(NR_PORTS); - if (!cy_serial_driver) - return -ENOMEM; - -#if 0 - scrn[1] = '\0'; -#endif - - show_version(); - - /* Has "console=0,9600n8" been used in bootinfo to change speed? */ - if (serial_console_cflag) - DefSpeed = serial_console_cflag & 0017; - else { - DefSpeed = initial_console_speed; - serial_console_info = &cy_port[0]; - serial_console_cflag = DefSpeed | CS8; -#if 0 - serial_console = 64; /*callout_driver.minor_start */ -#endif - } - - /* Initialize the tty_driver structure */ - - cy_serial_driver->owner = THIS_MODULE; - cy_serial_driver->name = "ttyS"; - cy_serial_driver->major = TTY_MAJOR; - cy_serial_driver->minor_start = 64; - cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - cy_serial_driver->subtype = SERIAL_TYPE_NORMAL; - cy_serial_driver->init_termios = tty_std_termios; - cy_serial_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - cy_serial_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(cy_serial_driver, &cy_ops); - - ret = tty_register_driver(cy_serial_driver); - if (ret) { - printk(KERN_ERR "Couldn't register MVME166/7 serial driver\n"); - put_tty_driver(cy_serial_driver); - return ret; - } - - port_num = 0; - info = cy_port; - for (index = 0; index < 1; index++) { - - good_ports = 4; - - if (port_num < NR_PORTS) { - while (good_ports-- && port_num < NR_PORTS) { - /*** initialize port ***/ - info->magic = CYCLADES_MAGIC; - info->type = PORT_CIRRUS; - info->card = index; - info->line = port_num; - info->flags = STD_COM_FLAGS; - info->tty = NULL; - info->xmit_fifo_size = 12; - info->cor1 = CyPARITY_NONE | Cy_8_BITS; - info->cor2 = CyETC; - info->cor3 = Cy_1_STOP; - info->cor4 = 0x08; /* _very_ small receive threshold */ - info->cor5 = 0; - info->cor6 = 0; - info->cor7 = 0; - info->tbpr = baud_bpr[DefSpeed]; /* Tx BPR */ - info->tco = baud_co[DefSpeed]; /* Tx CO */ - info->rbpr = baud_bpr[DefSpeed]; /* Rx BPR */ - info->rco = baud_co[DefSpeed] >> 5; /* Rx CO */ - info->close_delay = 0; - info->x_char = 0; - info->count = 0; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: setting count to 0\n", - __LINE__); -#endif - info->blocked_open = 0; - info->default_threshold = 0; - info->default_timeout = 0; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - /* info->session */ - /* info->pgrp */ -/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/ - info->read_status_mask = - CyTIMEOUT | CySPECHAR | CyBREAK | CyPARITY | - CyFRAME | CyOVERRUN; - /* info->timeout */ - - printk("ttyS%d ", info->line); - port_num++; - info++; - if (!(port_num & 7)) { - printk("\n "); - } - } - } - printk("\n"); - } - while (port_num < NR_PORTS) { - info->line = -1; - port_num++; - info++; - } - - ret = request_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0, - "cd2401_errors", cd2401_rxerr_interrupt); - if (ret) { - printk(KERN_ERR "Could't get cd2401_errors IRQ"); - goto cleanup_serial_driver; - } - - ret = request_irq(MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt, 0, - "cd2401_modem", cd2401_modem_interrupt); - if (ret) { - printk(KERN_ERR "Could't get cd2401_modem IRQ"); - goto cleanup_irq_cd2401_errors; - } - - ret = request_irq(MVME167_IRQ_SER_TX, cd2401_tx_interrupt, 0, - "cd2401_txints", cd2401_tx_interrupt); - if (ret) { - printk(KERN_ERR "Could't get cd2401_txints IRQ"); - goto cleanup_irq_cd2401_modem; - } - - ret = request_irq(MVME167_IRQ_SER_RX, cd2401_rx_interrupt, 0, - "cd2401_rxints", cd2401_rx_interrupt); - if (ret) { - printk(KERN_ERR "Could't get cd2401_rxints IRQ"); - goto cleanup_irq_cd2401_txints; - } - - /* Now we have registered the interrupt handlers, allow the interrupts */ - - pcc2chip[PccSCCMICR] = 0x15; /* Serial ints are level 5 */ - pcc2chip[PccSCCTICR] = 0x15; - pcc2chip[PccSCCRICR] = 0x15; - - pcc2chip[PccIMLR] = 3; /* Allow PCC2 ints above 3!? */ - - return 0; -cleanup_irq_cd2401_txints: - free_irq(MVME167_IRQ_SER_TX, cd2401_tx_interrupt); -cleanup_irq_cd2401_modem: - free_irq(MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt); -cleanup_irq_cd2401_errors: - free_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt); -cleanup_serial_driver: - if (tty_unregister_driver(cy_serial_driver)) - printk(KERN_ERR - "Couldn't unregister MVME166/7 serial driver\n"); - put_tty_driver(cy_serial_driver); - return ret; -} /* serial167_init */ - -module_init(serial167_init); - -#ifdef CYCLOM_SHOW_STATUS -static void show_status(int line_num) -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int channel; - struct cyclades_port *info; - unsigned long flags; - - info = &cy_port[line_num]; - channel = info->line; - printk(" channel %d\n", channel); - /**/ printk(" cy_port\n"); - printk(" card line flags = %d %d %x\n", - info->card, info->line, info->flags); - printk - (" *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n", - (long)info->tty, info->read_status_mask, info->timeout, - info->xmit_fifo_size); - printk(" cor1,cor2,cor3,cor4,cor5,cor6,cor7 = %x %x %x %x %x %x %x\n", - info->cor1, info->cor2, info->cor3, info->cor4, info->cor5, - info->cor6, info->cor7); - printk(" tbpr,tco,rbpr,rco = %d %d %d %d\n", info->tbpr, info->tco, - info->rbpr, info->rco); - printk(" close_delay event count = %d %d %d\n", info->close_delay, - info->event, info->count); - printk(" x_char blocked_open = %x %x\n", info->x_char, - info->blocked_open); - printk(" open_wait = %lx %lx %lx\n", (long)info->open_wait); - - local_irq_save(flags); - -/* Global Registers */ - - printk(" CyGFRCR %x\n", base_addr[CyGFRCR]); - printk(" CyCAR %x\n", base_addr[CyCAR]); - printk(" CyRISR %x\n", base_addr[CyRISR]); - printk(" CyTISR %x\n", base_addr[CyTISR]); - printk(" CyMISR %x\n", base_addr[CyMISR]); - printk(" CyRIR %x\n", base_addr[CyRIR]); - printk(" CyTIR %x\n", base_addr[CyTIR]); - printk(" CyMIR %x\n", base_addr[CyMIR]); - printk(" CyTPR %x\n", base_addr[CyTPR]); - - base_addr[CyCAR] = (u_char) channel; - -/* Virtual Registers */ - -#if 0 - printk(" CyRIVR %x\n", base_addr[CyRIVR]); - printk(" CyTIVR %x\n", base_addr[CyTIVR]); - printk(" CyMIVR %x\n", base_addr[CyMIVR]); - printk(" CyMISR %x\n", base_addr[CyMISR]); -#endif - -/* Channel Registers */ - - printk(" CyCCR %x\n", base_addr[CyCCR]); - printk(" CyIER %x\n", base_addr[CyIER]); - printk(" CyCOR1 %x\n", base_addr[CyCOR1]); - printk(" CyCOR2 %x\n", base_addr[CyCOR2]); - printk(" CyCOR3 %x\n", base_addr[CyCOR3]); - printk(" CyCOR4 %x\n", base_addr[CyCOR4]); - printk(" CyCOR5 %x\n", base_addr[CyCOR5]); -#if 0 - printk(" CyCCSR %x\n", base_addr[CyCCSR]); - printk(" CyRDCR %x\n", base_addr[CyRDCR]); -#endif - printk(" CySCHR1 %x\n", base_addr[CySCHR1]); - printk(" CySCHR2 %x\n", base_addr[CySCHR2]); -#if 0 - printk(" CySCHR3 %x\n", base_addr[CySCHR3]); - printk(" CySCHR4 %x\n", base_addr[CySCHR4]); - printk(" CySCRL %x\n", base_addr[CySCRL]); - printk(" CySCRH %x\n", base_addr[CySCRH]); - printk(" CyLNC %x\n", base_addr[CyLNC]); - printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]); - printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]); -#endif - printk(" CyRTPRL %x\n", base_addr[CyRTPRL]); - printk(" CyRTPRH %x\n", base_addr[CyRTPRH]); - printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]); - printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]); - printk(" CyRBPR %x\n", base_addr[CyRBPR]); - printk(" CyRCOR %x\n", base_addr[CyRCOR]); - printk(" CyTBPR %x\n", base_addr[CyTBPR]); - printk(" CyTCOR %x\n", base_addr[CyTCOR]); - - local_irq_restore(flags); -} /* show_status */ -#endif - -#if 0 -/* Dummy routine in mvme16x/config.c for now */ - -/* Serial console setup. Called from linux/init/main.c */ - -void console_setup(char *str, int *ints) -{ - char *s; - int baud, bits, parity; - int cflag = 0; - - /* Sanity check. */ - if (ints[0] > 3 || ints[1] > 3) - return; - - /* Get baud, bits and parity */ - baud = 2400; - bits = 8; - parity = 'n'; - if (ints[2]) - baud = ints[2]; - if ((s = strchr(str, ','))) { - do { - s++; - } while (*s >= '0' && *s <= '9'); - if (*s) - parity = *s++; - if (*s) - bits = *s - '0'; - } - - /* Now construct a cflag setting. */ - switch (baud) { - case 1200: - cflag |= B1200; - break; - case 9600: - cflag |= B9600; - break; - case 19200: - cflag |= B19200; - break; - case 38400: - cflag |= B38400; - break; - case 2400: - default: - cflag |= B2400; - break; - } - switch (bits) { - case 7: - cflag |= CS7; - break; - default: - case 8: - cflag |= CS8; - break; - } - switch (parity) { - case 'o': - case 'O': - cflag |= PARODD; - break; - case 'e': - case 'E': - cflag |= PARENB; - break; - } - - serial_console_info = &cy_port[ints[1]]; - serial_console_cflag = cflag; - serial_console = ints[1] + 64; /*callout_driver.minor_start */ -} -#endif - -/* - * The following is probably out of date for 2.1.x serial console stuff. - * - * The console is registered early on from arch/m68k/kernel/setup.c, and - * it therefore relies on the chip being setup correctly by 166-Bug. This - * seems reasonable, as the serial port has been used to invoke the system - * boot. It also means that this function must not rely on any data - * initialisation performed by serial167_init() etc. - * - * Of course, once the console has been registered, we had better ensure - * that serial167_init() doesn't leave the chip non-functional. - * - * The console must be locked when we get here. - */ - -void serial167_console_write(struct console *co, const char *str, - unsigned count) -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - unsigned long flags; - volatile u_char sink; - u_char ier; - int port; - u_char do_lf = 0; - int i = 0; - - local_irq_save(flags); - - /* Ensure transmitter is enabled! */ - - port = 0; - base_addr[CyCAR] = (u_char) port; - while (base_addr[CyCCR]) - ; - base_addr[CyCCR] = CyENB_XMTR; - - ier = base_addr[CyIER]; - base_addr[CyIER] = CyTxMpty; - - while (1) { - if (pcc2chip[PccSCCTICR] & 0x20) { - /* We have a Tx int. Acknowledge it */ - sink = pcc2chip[PccTPIACKR]; - if ((base_addr[CyLICR] >> 2) == port) { - if (i == count) { - /* Last char of string is now output */ - base_addr[CyTEOIR] = CyNOTRANS; - break; - } - if (do_lf) { - base_addr[CyTDR] = '\n'; - str++; - i++; - do_lf = 0; - } else if (*str == '\n') { - base_addr[CyTDR] = '\r'; - do_lf = 1; - } else { - base_addr[CyTDR] = *str++; - i++; - } - base_addr[CyTEOIR] = 0; - } else - base_addr[CyTEOIR] = CyNOTRANS; - } - } - - base_addr[CyIER] = ier; - - local_irq_restore(flags); -} - -static struct tty_driver *serial167_console_device(struct console *c, - int *index) -{ - *index = c->index; - return cy_serial_driver; -} - -static struct console sercons = { - .name = "ttyS", - .write = serial167_console_write, - .device = serial167_console_device, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -static int __init serial167_console_init(void) -{ - if (vme_brdtype == VME_TYPE_MVME166 || - vme_brdtype == VME_TYPE_MVME167 || - vme_brdtype == VME_TYPE_MVME177) { - mvme167_serial_console_setup(0); - register_console(&sercons); - } - return 0; -} - -console_initcall(serial167_console_init); - -MODULE_LICENSE("GPL"); diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c deleted file mode 100644 index 47e5753f732a..000000000000 --- a/drivers/char/specialix.c +++ /dev/null @@ -1,2368 +0,0 @@ -/* - * specialix.c -- specialix IO8+ multiport serial driver. - * - * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) - * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) - * - * Specialix pays for the development and support of this driver. - * Please DO contact io8-linux@specialix.co.uk if you require - * support. But please read the documentation (specialix.txt) - * first. - * - * This driver was developped in the BitWizard linux device - * driver service. If you require a linux device driver for your - * product, please contact devices@BitWizard.nl for a quote. - * - * This code is firmly based on the riscom/8 serial driver, - * written by Dmitry Gorodchanin. The specialix IO8+ card - * programming information was obtained from the CL-CD1865 Data - * Book, and Specialix document number 6200059: IO8+ Hardware - * Functional Specification. - * - * 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. - * - * Revision history: - * - * Revision 1.0: April 1st 1997. - * Initial release for alpha testing. - * Revision 1.1: April 14th 1997. - * Incorporated Richard Hudsons suggestions, - * removed some debugging printk's. - * Revision 1.2: April 15th 1997. - * Ported to 2.1.x kernels. - * Revision 1.3: April 17th 1997 - * Backported to 2.0. (Compatibility macros). - * Revision 1.4: April 18th 1997 - * Fixed DTR/RTS bug that caused the card to indicate - * "don't send data" to a modem after the password prompt. - * Fixed bug for premature (fake) interrupts. - * Revision 1.5: April 19th 1997 - * fixed a minor typo in the header file, cleanup a little. - * performance warnings are now MAXed at once per minute. - * Revision 1.6: May 23 1997 - * Changed the specialix=... format to include interrupt. - * Revision 1.7: May 27 1997 - * Made many more debug printk's a compile time option. - * Revision 1.8: Jul 1 1997 - * port to linux-2.1.43 kernel. - * Revision 1.9: Oct 9 1998 - * Added stuff for the IO8+/PCI version. - * Revision 1.10: Oct 22 1999 / Jan 21 2000. - * Added stuff for setserial. - * Nicolas Mailhot (Nicolas.Mailhot@email.enst.fr) - * - */ - -#define VERSION "1.11" - - -/* - * There is a bunch of documentation about the card, jumpers, config - * settings, restrictions, cables, device names and numbers in - * Documentation/serial/specialix.txt - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "specialix_io8.h" -#include "cd1865.h" - - -/* - This driver can spew a whole lot of debugging output at you. If you - need maximum performance, you should disable the DEBUG define. To - aid in debugging in the field, I'm leaving the compile-time debug - features enabled, and disable them "runtime". That allows me to - instruct people with problems to enable debugging without requiring - them to recompile... -*/ -#define DEBUG - -static int sx_debug; -static int sx_rxfifo = SPECIALIX_RXFIFO; -static int sx_rtscts; - -#ifdef DEBUG -#define dprintk(f, str...) if (sx_debug & f) printk(str) -#else -#define dprintk(f, str...) /* nothing */ -#endif - -#define SX_DEBUG_FLOW 0x0001 -#define SX_DEBUG_DATA 0x0002 -#define SX_DEBUG_PROBE 0x0004 -#define SX_DEBUG_CHAN 0x0008 -#define SX_DEBUG_INIT 0x0010 -#define SX_DEBUG_RX 0x0020 -#define SX_DEBUG_TX 0x0040 -#define SX_DEBUG_IRQ 0x0080 -#define SX_DEBUG_OPEN 0x0100 -#define SX_DEBUG_TERMIOS 0x0200 -#define SX_DEBUG_SIGNALS 0x0400 -#define SX_DEBUG_FIFO 0x0800 - - -#define func_enter() dprintk(SX_DEBUG_FLOW, "io8: enter %s\n", __func__) -#define func_exit() dprintk(SX_DEBUG_FLOW, "io8: exit %s\n", __func__) - - -/* Configurable options: */ - -/* Am I paranoid or not ? ;-) */ -#define SPECIALIX_PARANOIA_CHECK - -/* - * The following defines are mostly for testing purposes. But if you need - * some nice reporting in your syslog, you can define them also. - */ -#undef SX_REPORT_FIFO -#undef SX_REPORT_OVERRUN - - - - -#define SPECIALIX_LEGAL_FLAGS \ - (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \ - ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ - ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) - -static struct tty_driver *specialix_driver; - -static struct specialix_board sx_board[SX_NBOARD] = { - { 0, SX_IOBASE1, 9, }, - { 0, SX_IOBASE2, 11, }, - { 0, SX_IOBASE3, 12, }, - { 0, SX_IOBASE4, 15, }, -}; - -static struct specialix_port sx_port[SX_NBOARD * SX_NPORT]; - - -static int sx_paranoia_check(struct specialix_port const *port, - char *name, const char *routine) -{ -#ifdef SPECIALIX_PARANOIA_CHECK - static const char *badmagic = KERN_ERR - "sx: Warning: bad specialix port magic number for device %s in %s\n"; - static const char *badinfo = KERN_ERR - "sx: Warning: null specialix port for device %s in %s\n"; - - if (!port) { - printk(badinfo, name, routine); - return 1; - } - if (port->magic != SPECIALIX_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - - -/* - * - * Service functions for specialix IO8+ driver. - * - */ - -/* Get board number from pointer */ -static inline int board_No(struct specialix_board *bp) -{ - return bp - sx_board; -} - - -/* Get port number from pointer */ -static inline int port_No(struct specialix_port const *port) -{ - return SX_PORT(port - sx_port); -} - - -/* Get pointer to board from pointer to port */ -static inline struct specialix_board *port_Board( - struct specialix_port const *port) -{ - return &sx_board[SX_BOARD(port - sx_port)]; -} - - -/* Input Byte from CL CD186x register */ -static inline unsigned char sx_in(struct specialix_board *bp, - unsigned short reg) -{ - bp->reg = reg | 0x80; - outb(reg | 0x80, bp->base + SX_ADDR_REG); - return inb(bp->base + SX_DATA_REG); -} - - -/* Output Byte to CL CD186x register */ -static inline void sx_out(struct specialix_board *bp, unsigned short reg, - unsigned char val) -{ - bp->reg = reg | 0x80; - outb(reg | 0x80, bp->base + SX_ADDR_REG); - outb(val, bp->base + SX_DATA_REG); -} - - -/* Input Byte from CL CD186x register */ -static inline unsigned char sx_in_off(struct specialix_board *bp, - unsigned short reg) -{ - bp->reg = reg; - outb(reg, bp->base + SX_ADDR_REG); - return inb(bp->base + SX_DATA_REG); -} - - -/* Output Byte to CL CD186x register */ -static inline void sx_out_off(struct specialix_board *bp, - unsigned short reg, unsigned char val) -{ - bp->reg = reg; - outb(reg, bp->base + SX_ADDR_REG); - outb(val, bp->base + SX_DATA_REG); -} - - -/* Wait for Channel Command Register ready */ -static void sx_wait_CCR(struct specialix_board *bp) -{ - unsigned long delay, flags; - unsigned char ccr; - - for (delay = SX_CCR_TIMEOUT; delay; delay--) { - spin_lock_irqsave(&bp->lock, flags); - ccr = sx_in(bp, CD186x_CCR); - spin_unlock_irqrestore(&bp->lock, flags); - if (!ccr) - return; - udelay(1); - } - - printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); -} - - -/* Wait for Channel Command Register ready */ -static void sx_wait_CCR_off(struct specialix_board *bp) -{ - unsigned long delay; - unsigned char crr; - unsigned long flags; - - for (delay = SX_CCR_TIMEOUT; delay; delay--) { - spin_lock_irqsave(&bp->lock, flags); - crr = sx_in_off(bp, CD186x_CCR); - spin_unlock_irqrestore(&bp->lock, flags); - if (!crr) - return; - udelay(1); - } - - printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); -} - - -/* - * specialix IO8+ IO range functions. - */ - -static int sx_request_io_range(struct specialix_board *bp) -{ - return request_region(bp->base, - bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE, - "specialix IO8+") == NULL; -} - - -static void sx_release_io_range(struct specialix_board *bp) -{ - release_region(bp->base, bp->flags & SX_BOARD_IS_PCI ? - SX_PCI_IO_SPACE : SX_IO_SPACE); -} - - -/* Set the IRQ using the RTS lines that run to the PAL on the board.... */ -static int sx_set_irq(struct specialix_board *bp) -{ - int virq; - int i; - unsigned long flags; - - if (bp->flags & SX_BOARD_IS_PCI) - return 1; - switch (bp->irq) { - /* In the same order as in the docs... */ - case 15: - virq = 0; - break; - case 12: - virq = 1; - break; - case 11: - virq = 2; - break; - case 9: - virq = 3; - break; - default:printk(KERN_ERR - "Speclialix: cannot set irq to %d.\n", bp->irq); - return 0; - } - spin_lock_irqsave(&bp->lock, flags); - for (i = 0; i < 2; i++) { - sx_out(bp, CD186x_CAR, i); - sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0); - } - spin_unlock_irqrestore(&bp->lock, flags); - return 1; -} - - -/* Reset and setup CD186x chip */ -static int sx_init_CD186x(struct specialix_board *bp) -{ - unsigned long flags; - int scaler; - int rv = 1; - - func_enter(); - sx_wait_CCR_off(bp); /* Wait for CCR ready */ - spin_lock_irqsave(&bp->lock, flags); - sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */ - spin_unlock_irqrestore(&bp->lock, flags); - msleep(50); /* Delay 0.05 sec */ - spin_lock_irqsave(&bp->lock, flags); - sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */ - sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */ - sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */ - sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */ - sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */ - /* Set RegAckEn */ - sx_out_off(bp, CD186x_SRCR, sx_in(bp, CD186x_SRCR) | SRCR_REGACKEN); - - /* Setting up prescaler. We need 4 ticks per 1 ms */ - scaler = SX_OSCFREQ/SPECIALIX_TPS; - - sx_out_off(bp, CD186x_PPRH, scaler >> 8); - sx_out_off(bp, CD186x_PPRL, scaler & 0xff); - spin_unlock_irqrestore(&bp->lock, flags); - - if (!sx_set_irq(bp)) { - /* Figure out how to pass this along... */ - printk(KERN_ERR "Cannot set irq to %d.\n", bp->irq); - rv = 0; - } - - func_exit(); - return rv; -} - - -static int read_cross_byte(struct specialix_board *bp, int reg, int bit) -{ - int i; - int t; - unsigned long flags; - - spin_lock_irqsave(&bp->lock, flags); - for (i = 0, t = 0; i < 8; i++) { - sx_out_off(bp, CD186x_CAR, i); - if (sx_in_off(bp, reg) & bit) - t |= 1 << i; - } - spin_unlock_irqrestore(&bp->lock, flags); - - return t; -} - - -/* Main probing routine, also sets irq. */ -static int sx_probe(struct specialix_board *bp) -{ - unsigned char val1, val2; - int rev; - int chip; - - func_enter(); - - if (sx_request_io_range(bp)) { - func_exit(); - return 1; - } - - /* Are the I/O ports here ? */ - sx_out_off(bp, CD186x_PPRL, 0x5a); - udelay(1); - val1 = sx_in_off(bp, CD186x_PPRL); - - sx_out_off(bp, CD186x_PPRL, 0xa5); - udelay(1); - val2 = sx_in_off(bp, CD186x_PPRL); - - - if (val1 != 0x5a || val2 != 0xa5) { - printk(KERN_INFO - "sx%d: specialix IO8+ Board at 0x%03x not found.\n", - board_No(bp), bp->base); - sx_release_io_range(bp); - func_exit(); - return 1; - } - - /* Check the DSR lines that Specialix uses as board - identification */ - val1 = read_cross_byte(bp, CD186x_MSVR, MSVR_DSR); - val2 = read_cross_byte(bp, CD186x_MSVR, MSVR_RTS); - dprintk(SX_DEBUG_INIT, - "sx%d: DSR lines are: %02x, rts lines are: %02x\n", - board_No(bp), val1, val2); - - /* They managed to switch the bit order between the docs and - the IO8+ card. The new PCI card now conforms to old docs. - They changed the PCI docs to reflect the situation on the - old card. */ - val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2; - if (val1 != val2) { - printk(KERN_INFO - "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n", - board_No(bp), val2, bp->base, val1); - sx_release_io_range(bp); - func_exit(); - return 1; - } - - - /* Reset CD186x again */ - if (!sx_init_CD186x(bp)) { - sx_release_io_range(bp); - func_exit(); - return 1; - } - - sx_request_io_range(bp); - bp->flags |= SX_BOARD_PRESENT; - - /* Chip revcode pkgtype - GFRCR SRCR bit 7 - CD180 rev B 0x81 0 - CD180 rev C 0x82 0 - CD1864 rev A 0x82 1 - CD1865 rev A 0x83 1 -- Do not use!!! Does not work. - CD1865 rev B 0x84 1 - -- Thanks to Gwen Wang, Cirrus Logic. - */ - - switch (sx_in_off(bp, CD186x_GFRCR)) { - case 0x82: - chip = 1864; - rev = 'A'; - break; - case 0x83: - chip = 1865; - rev = 'A'; - break; - case 0x84: - chip = 1865; - rev = 'B'; - break; - case 0x85: - chip = 1865; - rev = 'C'; - break; /* Does not exist at this time */ - default: - chip = -1; - rev = 'x'; - } - - dprintk(SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR)); - - printk(KERN_INFO - "sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n", - board_No(bp), bp->base, bp->irq, chip, rev); - - func_exit(); - return 0; -} - -/* - * - * Interrupt processing routines. - * */ - -static struct specialix_port *sx_get_port(struct specialix_board *bp, - unsigned char const *what) -{ - unsigned char channel; - struct specialix_port *port = NULL; - - channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF; - dprintk(SX_DEBUG_CHAN, "channel: %d\n", channel); - if (channel < CD186x_NCH) { - port = &sx_port[board_No(bp) * SX_NPORT + channel]; - dprintk(SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n", - board_No(bp) * SX_NPORT + channel, port, - port->port.flags & ASYNC_INITIALIZED); - - if (port->port.flags & ASYNC_INITIALIZED) { - dprintk(SX_DEBUG_CHAN, "port: %d %p\n", channel, port); - func_exit(); - return port; - } - } - printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", - board_No(bp), what, channel); - return NULL; -} - - -static void sx_receive_exc(struct specialix_board *bp) -{ - struct specialix_port *port; - struct tty_struct *tty; - unsigned char status; - unsigned char ch, flag; - - func_enter(); - - port = sx_get_port(bp, "Receive"); - if (!port) { - dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n"); - func_exit(); - return; - } - tty = port->port.tty; - - status = sx_in(bp, CD186x_RCSR); - - dprintk(SX_DEBUG_RX, "status: 0x%x\n", status); - if (status & RCSR_OE) { - port->overrun++; - dprintk(SX_DEBUG_FIFO, - "sx%d: port %d: Overrun. Total %ld overruns.\n", - board_No(bp), port_No(port), port->overrun); - } - status &= port->mark_mask; - - /* This flip buffer check needs to be below the reading of the - status register to reset the chip's IRQ.... */ - if (tty_buffer_request_room(tty, 1) == 0) { - dprintk(SX_DEBUG_FIFO, - "sx%d: port %d: Working around flip buffer overflow.\n", - board_No(bp), port_No(port)); - func_exit(); - return; - } - - ch = sx_in(bp, CD186x_RDR); - if (!status) { - func_exit(); - return; - } - if (status & RCSR_TOUT) { - printk(KERN_INFO - "sx%d: port %d: Receiver timeout. Hardware problems ?\n", - board_No(bp), port_No(port)); - func_exit(); - return; - - } else if (status & RCSR_BREAK) { - dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n", - board_No(bp), port_No(port)); - flag = TTY_BREAK; - if (port->port.flags & ASYNC_SAK) - do_SAK(tty); - - } else if (status & RCSR_PE) - flag = TTY_PARITY; - - else if (status & RCSR_FE) - flag = TTY_FRAME; - - else if (status & RCSR_OE) - flag = TTY_OVERRUN; - - else - flag = TTY_NORMAL; - - if (tty_insert_flip_char(tty, ch, flag)) - tty_flip_buffer_push(tty); - func_exit(); -} - - -static void sx_receive(struct specialix_board *bp) -{ - struct specialix_port *port; - struct tty_struct *tty; - unsigned char count; - - func_enter(); - - port = sx_get_port(bp, "Receive"); - if (port == NULL) { - dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n"); - func_exit(); - return; - } - tty = port->port.tty; - - count = sx_in(bp, CD186x_RDCR); - dprintk(SX_DEBUG_RX, "port: %p: count: %d\n", port, count); - port->hits[count > 8 ? 9 : count]++; - - while (count--) - tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL); - tty_flip_buffer_push(tty); - func_exit(); -} - - -static void sx_transmit(struct specialix_board *bp) -{ - struct specialix_port *port; - struct tty_struct *tty; - unsigned char count; - - func_enter(); - port = sx_get_port(bp, "Transmit"); - if (port == NULL) { - func_exit(); - return; - } - dprintk(SX_DEBUG_TX, "port: %p\n", port); - tty = port->port.tty; - - if (port->IER & IER_TXEMPTY) { - /* FIFO drained */ - sx_out(bp, CD186x_CAR, port_No(port)); - port->IER &= ~IER_TXEMPTY; - sx_out(bp, CD186x_IER, port->IER); - func_exit(); - return; - } - - if ((port->xmit_cnt <= 0 && !port->break_length) - || tty->stopped || tty->hw_stopped) { - sx_out(bp, CD186x_CAR, port_No(port)); - port->IER &= ~IER_TXRDY; - sx_out(bp, CD186x_IER, port->IER); - func_exit(); - return; - } - - if (port->break_length) { - if (port->break_length > 0) { - if (port->COR2 & COR2_ETC) { - sx_out(bp, CD186x_TDR, CD186x_C_ESC); - sx_out(bp, CD186x_TDR, CD186x_C_SBRK); - port->COR2 &= ~COR2_ETC; - } - count = min_t(int, port->break_length, 0xff); - sx_out(bp, CD186x_TDR, CD186x_C_ESC); - sx_out(bp, CD186x_TDR, CD186x_C_DELAY); - sx_out(bp, CD186x_TDR, count); - port->break_length -= count; - if (port->break_length == 0) - port->break_length--; - } else { - sx_out(bp, CD186x_TDR, CD186x_C_ESC); - sx_out(bp, CD186x_TDR, CD186x_C_EBRK); - sx_out(bp, CD186x_COR2, port->COR2); - sx_wait_CCR(bp); - sx_out(bp, CD186x_CCR, CCR_CORCHG2); - port->break_length = 0; - } - - func_exit(); - return; - } - - count = CD186x_NFIFO; - do { - sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]); - port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); - if (--port->xmit_cnt <= 0) - break; - } while (--count > 0); - - if (port->xmit_cnt <= 0) { - sx_out(bp, CD186x_CAR, port_No(port)); - port->IER &= ~IER_TXRDY; - sx_out(bp, CD186x_IER, port->IER); - } - if (port->xmit_cnt <= port->wakeup_chars) - tty_wakeup(tty); - - func_exit(); -} - - -static void sx_check_modem(struct specialix_board *bp) -{ - struct specialix_port *port; - struct tty_struct *tty; - unsigned char mcr; - int msvr_cd; - - dprintk(SX_DEBUG_SIGNALS, "Modem intr. "); - port = sx_get_port(bp, "Modem"); - if (port == NULL) - return; - - tty = port->port.tty; - - mcr = sx_in(bp, CD186x_MCR); - - if ((mcr & MCR_CDCHG)) { - dprintk(SX_DEBUG_SIGNALS, "CD just changed... "); - msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD; - if (msvr_cd) { - dprintk(SX_DEBUG_SIGNALS, "Waking up guys in open.\n"); - wake_up_interruptible(&port->port.open_wait); - } else { - dprintk(SX_DEBUG_SIGNALS, "Sending HUP.\n"); - tty_hangup(tty); - } - } - -#ifdef SPECIALIX_BRAIN_DAMAGED_CTS - if (mcr & MCR_CTSCHG) { - if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) { - tty->hw_stopped = 0; - port->IER |= IER_TXRDY; - if (port->xmit_cnt <= port->wakeup_chars) - tty_wakeup(tty); - } else { - tty->hw_stopped = 1; - port->IER &= ~IER_TXRDY; - } - sx_out(bp, CD186x_IER, port->IER); - } - if (mcr & MCR_DSSXHG) { - if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) { - tty->hw_stopped = 0; - port->IER |= IER_TXRDY; - if (port->xmit_cnt <= port->wakeup_chars) - tty_wakeup(tty); - } else { - tty->hw_stopped = 1; - port->IER &= ~IER_TXRDY; - } - sx_out(bp, CD186x_IER, port->IER); - } -#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */ - - /* Clear change bits */ - sx_out(bp, CD186x_MCR, 0); -} - - -/* The main interrupt processing routine */ -static irqreturn_t sx_interrupt(int dummy, void *dev_id) -{ - unsigned char status; - unsigned char ack; - struct specialix_board *bp = dev_id; - unsigned long loop = 0; - int saved_reg; - unsigned long flags; - - func_enter(); - - spin_lock_irqsave(&bp->lock, flags); - - dprintk(SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__, - port_No(sx_get_port(bp, "INT")), - SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1); - if (!(bp->flags & SX_BOARD_ACTIVE)) { - dprintk(SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", - bp->irq); - spin_unlock_irqrestore(&bp->lock, flags); - func_exit(); - return IRQ_NONE; - } - - saved_reg = bp->reg; - - while (++loop < 16) { - status = sx_in(bp, CD186x_SRSR) & - (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint); - if (status == 0) - break; - if (status & SRSR_RREQint) { - ack = sx_in(bp, CD186x_RRAR); - - if (ack == (SX_ID | GIVR_IT_RCV)) - sx_receive(bp); - else if (ack == (SX_ID | GIVR_IT_REXC)) - sx_receive_exc(bp); - else - printk(KERN_ERR - "sx%d: status: 0x%x Bad receive ack 0x%02x.\n", - board_No(bp), status, ack); - - } else if (status & SRSR_TREQint) { - ack = sx_in(bp, CD186x_TRAR); - - if (ack == (SX_ID | GIVR_IT_TX)) - sx_transmit(bp); - else - printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n", - board_No(bp), status, ack, - port_No(sx_get_port(bp, "Int"))); - } else if (status & SRSR_MREQint) { - ack = sx_in(bp, CD186x_MRAR); - - if (ack == (SX_ID | GIVR_IT_MODEM)) - sx_check_modem(bp); - else - printk(KERN_ERR - "sx%d: status: 0x%x Bad modem ack 0x%02x.\n", - board_No(bp), status, ack); - - } - - sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */ - } - bp->reg = saved_reg; - outb(bp->reg, bp->base + SX_ADDR_REG); - spin_unlock_irqrestore(&bp->lock, flags); - func_exit(); - return IRQ_HANDLED; -} - - -/* - * Routines for open & close processing. - */ - -static void turn_ints_off(struct specialix_board *bp) -{ - unsigned long flags; - - func_enter(); - spin_lock_irqsave(&bp->lock, flags); - (void) sx_in_off(bp, 0); /* Turn off interrupts. */ - spin_unlock_irqrestore(&bp->lock, flags); - - func_exit(); -} - -static void turn_ints_on(struct specialix_board *bp) -{ - unsigned long flags; - - func_enter(); - - spin_lock_irqsave(&bp->lock, flags); - (void) sx_in(bp, 0); /* Turn ON interrupts. */ - spin_unlock_irqrestore(&bp->lock, flags); - - func_exit(); -} - - -/* Called with disabled interrupts */ -static int sx_setup_board(struct specialix_board *bp) -{ - int error; - - if (bp->flags & SX_BOARD_ACTIVE) - return 0; - - if (bp->flags & SX_BOARD_IS_PCI) - error = request_irq(bp->irq, sx_interrupt, - IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp); - else - error = request_irq(bp->irq, sx_interrupt, - IRQF_DISABLED, "specialix IO8+", bp); - - if (error) - return error; - - turn_ints_on(bp); - bp->flags |= SX_BOARD_ACTIVE; - - return 0; -} - - -/* Called with disabled interrupts */ -static void sx_shutdown_board(struct specialix_board *bp) -{ - func_enter(); - - if (!(bp->flags & SX_BOARD_ACTIVE)) { - func_exit(); - return; - } - - bp->flags &= ~SX_BOARD_ACTIVE; - - dprintk(SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n", - bp->irq, board_No(bp)); - free_irq(bp->irq, bp); - turn_ints_off(bp); - func_exit(); -} - -static unsigned int sx_crtscts(struct tty_struct *tty) -{ - if (sx_rtscts) - return C_CRTSCTS(tty); - return 1; -} - -/* - * Setting up port characteristics. - * Must be called with disabled interrupts - */ -static void sx_change_speed(struct specialix_board *bp, - struct specialix_port *port) -{ - struct tty_struct *tty; - unsigned long baud; - long tmp; - unsigned char cor1 = 0, cor3 = 0; - unsigned char mcor1 = 0, mcor2 = 0; - static unsigned long again; - unsigned long flags; - - func_enter(); - - tty = port->port.tty; - if (!tty || !tty->termios) { - func_exit(); - return; - } - - port->IER = 0; - port->COR2 = 0; - /* Select port on the board */ - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CAR, port_No(port)); - - /* The Specialix board doens't implement the RTS lines. - They are used to set the IRQ level. Don't touch them. */ - if (sx_crtscts(tty)) - port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS); - else - port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS); - spin_unlock_irqrestore(&bp->lock, flags); - dprintk(SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR); - baud = tty_get_baud_rate(tty); - - if (baud == 38400) { - if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - baud = 57600; - if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - baud = 115200; - } - - if (!baud) { - /* Drop DTR & exit */ - dprintk(SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n"); - if (!sx_crtscts(tty)) { - port->MSVR &= ~MSVR_DTR; - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_MSVR, port->MSVR); - spin_unlock_irqrestore(&bp->lock, flags); - } else - dprintk(SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n"); - return; - } else { - /* Set DTR on */ - if (!sx_crtscts(tty)) - port->MSVR |= MSVR_DTR; - } - - /* - * Now we must calculate some speed depended things - */ - - /* Set baud rate for port */ - tmp = port->custom_divisor ; - if (tmp) - printk(KERN_INFO - "sx%d: Using custom baud rate divisor %ld. \n" - "This is an untested option, please be careful.\n", - port_No(port), tmp); - else - tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) / - CD186x_TPC); - - if (tmp < 0x10 && time_before(again, jiffies)) { - again = jiffies + HZ * 60; - /* Page 48 of version 2.0 of the CL-CD1865 databook */ - if (tmp >= 12) { - printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n" - "Performance degradation is possible.\n" - "Read specialix.txt for more info.\n", - port_No(port), tmp); - } else { - printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n" - "Warning: overstressing Cirrus chip. This might not work.\n" - "Read specialix.txt for more info.\n", port_No(port), tmp); - } - } - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); - sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); - sx_out(bp, CD186x_RBPRL, tmp & 0xff); - sx_out(bp, CD186x_TBPRL, tmp & 0xff); - spin_unlock_irqrestore(&bp->lock, flags); - if (port->custom_divisor) - baud = (SX_OSCFREQ + port->custom_divisor/2) / - port->custom_divisor; - baud = (baud + 5) / 10; /* Estimated CPS */ - - /* Two timer ticks seems enough to wakeup something like SLIP driver */ - tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO; - port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? - SERIAL_XMIT_SIZE - 1 : tmp); - - /* Receiver timeout will be transmission time for 1.5 chars */ - tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud; - tmp = (tmp > 0xff) ? 0xff : tmp; - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_RTPR, tmp); - spin_unlock_irqrestore(&bp->lock, flags); - switch (C_CSIZE(tty)) { - case CS5: - cor1 |= COR1_5BITS; - break; - case CS6: - cor1 |= COR1_6BITS; - break; - case CS7: - cor1 |= COR1_7BITS; - break; - case CS8: - cor1 |= COR1_8BITS; - break; - } - - if (C_CSTOPB(tty)) - cor1 |= COR1_2SB; - - cor1 |= COR1_IGNORE; - if (C_PARENB(tty)) { - cor1 |= COR1_NORMPAR; - if (C_PARODD(tty)) - cor1 |= COR1_ODDP; - if (I_INPCK(tty)) - cor1 &= ~COR1_IGNORE; - } - /* Set marking of some errors */ - port->mark_mask = RCSR_OE | RCSR_TOUT; - if (I_INPCK(tty)) - port->mark_mask |= RCSR_FE | RCSR_PE; - if (I_BRKINT(tty) || I_PARMRK(tty)) - port->mark_mask |= RCSR_BREAK; - if (I_IGNPAR(tty)) - port->mark_mask &= ~(RCSR_FE | RCSR_PE); - if (I_IGNBRK(tty)) { - port->mark_mask &= ~RCSR_BREAK; - if (I_IGNPAR(tty)) - /* Real raw mode. Ignore all */ - port->mark_mask &= ~RCSR_OE; - } - /* Enable Hardware Flow Control */ - if (C_CRTSCTS(tty)) { -#ifdef SPECIALIX_BRAIN_DAMAGED_CTS - port->IER |= IER_DSR | IER_CTS; - mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; - mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; - spin_lock_irqsave(&bp->lock, flags); - tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & - (MSVR_CTS|MSVR_DSR)); - spin_unlock_irqrestore(&bp->lock, flags); -#else - port->COR2 |= COR2_CTSAE; -#endif - } - /* Enable Software Flow Control. FIXME: I'm not sure about this */ - /* Some people reported that it works, but I still doubt it */ - if (I_IXON(tty)) { - port->COR2 |= COR2_TXIBE; - cor3 |= (COR3_FCT | COR3_SCDE); - if (I_IXANY(tty)) - port->COR2 |= COR2_IXM; - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_SCHR1, START_CHAR(tty)); - sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty)); - sx_out(bp, CD186x_SCHR3, START_CHAR(tty)); - sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty)); - spin_unlock_irqrestore(&bp->lock, flags); - } - if (!C_CLOCAL(tty)) { - /* Enable CD check */ - port->IER |= IER_CD; - mcor1 |= MCOR1_CDZD; - mcor2 |= MCOR2_CDOD; - } - - if (C_CREAD(tty)) - /* Enable receiver */ - port->IER |= IER_RXD; - - /* Set input FIFO size (1-8 bytes) */ - cor3 |= sx_rxfifo; - /* Setting up CD186x channel registers */ - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_COR1, cor1); - sx_out(bp, CD186x_COR2, port->COR2); - sx_out(bp, CD186x_COR3, cor3); - spin_unlock_irqrestore(&bp->lock, flags); - /* Make CD186x know about registers change */ - sx_wait_CCR(bp); - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); - /* Setting up modem option registers */ - dprintk(SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", - mcor1, mcor2); - sx_out(bp, CD186x_MCOR1, mcor1); - sx_out(bp, CD186x_MCOR2, mcor2); - spin_unlock_irqrestore(&bp->lock, flags); - /* Enable CD186x transmitter & receiver */ - sx_wait_CCR(bp); - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN); - /* Enable interrupts */ - sx_out(bp, CD186x_IER, port->IER); - /* And finally set the modem lines... */ - sx_out(bp, CD186x_MSVR, port->MSVR); - spin_unlock_irqrestore(&bp->lock, flags); - - func_exit(); -} - - -/* Must be called with interrupts enabled */ -static int sx_setup_port(struct specialix_board *bp, - struct specialix_port *port) -{ - unsigned long flags; - - func_enter(); - - if (port->port.flags & ASYNC_INITIALIZED) { - func_exit(); - return 0; - } - - if (!port->xmit_buf) { - /* We may sleep in get_zeroed_page() */ - unsigned long tmp; - - tmp = get_zeroed_page(GFP_KERNEL); - if (tmp == 0L) { - func_exit(); - return -ENOMEM; - } - - if (port->xmit_buf) { - free_page(tmp); - func_exit(); - return -ERESTARTSYS; - } - port->xmit_buf = (unsigned char *) tmp; - } - - spin_lock_irqsave(&port->lock, flags); - - if (port->port.tty) - clear_bit(TTY_IO_ERROR, &port->port.tty->flags); - - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - sx_change_speed(bp, port); - port->port.flags |= ASYNC_INITIALIZED; - - spin_unlock_irqrestore(&port->lock, flags); - - - func_exit(); - return 0; -} - - -/* Must be called with interrupts disabled */ -static void sx_shutdown_port(struct specialix_board *bp, - struct specialix_port *port) -{ - struct tty_struct *tty; - int i; - unsigned long flags; - - func_enter(); - - if (!(port->port.flags & ASYNC_INITIALIZED)) { - func_exit(); - return; - } - - if (sx_debug & SX_DEBUG_FIFO) { - dprintk(SX_DEBUG_FIFO, - "sx%d: port %d: %ld overruns, FIFO hits [ ", - board_No(bp), port_No(port), port->overrun); - for (i = 0; i < 10; i++) - dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]); - dprintk(SX_DEBUG_FIFO, "].\n"); - } - - if (port->xmit_buf) { - free_page((unsigned long) port->xmit_buf); - port->xmit_buf = NULL; - } - - /* Select port */ - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CAR, port_No(port)); - - tty = port->port.tty; - if (tty == NULL || C_HUPCL(tty)) { - /* Drop DTR */ - sx_out(bp, CD186x_MSVDTR, 0); - } - spin_unlock_irqrestore(&bp->lock, flags); - /* Reset port */ - sx_wait_CCR(bp); - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CCR, CCR_SOFTRESET); - /* Disable all interrupts from this port */ - port->IER = 0; - sx_out(bp, CD186x_IER, port->IER); - spin_unlock_irqrestore(&bp->lock, flags); - if (tty) - set_bit(TTY_IO_ERROR, &tty->flags); - port->port.flags &= ~ASYNC_INITIALIZED; - - if (!bp->count) - sx_shutdown_board(bp); - func_exit(); -} - - -static int block_til_ready(struct tty_struct *tty, struct file *filp, - struct specialix_port *port) -{ - DECLARE_WAITQUEUE(wait, current); - struct specialix_board *bp = port_Board(port); - int retval; - int do_clocal = 0; - int CD; - unsigned long flags; - - func_enter(); - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) { - interruptible_sleep_on(&port->port.close_wait); - if (port->port.flags & ASYNC_HUP_NOTIFY) { - func_exit(); - return -EAGAIN; - } else { - func_exit(); - return -ERESTARTSYS; - } - } - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - port->port.flags |= ASYNC_NORMAL_ACTIVE; - func_exit(); - return 0; - } - - if (C_CLOCAL(tty)) - do_clocal = 1; - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * rs_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&port->port.open_wait, &wait); - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->port.count--; - spin_unlock_irqrestore(&port->lock, flags); - port->port.blocked_open++; - while (1) { - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CAR, port_No(port)); - CD = sx_in(bp, CD186x_MSVR) & MSVR_CD; - if (sx_crtscts(tty)) { - /* Activate RTS */ - port->MSVR |= MSVR_DTR; /* WTF? */ - sx_out(bp, CD186x_MSVR, port->MSVR); - } else { - /* Activate DTR */ - port->MSVR |= MSVR_DTR; - sx_out(bp, CD186x_MSVR, port->MSVR); - } - spin_unlock_irqrestore(&bp->lock, flags); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(port->port.flags & ASYNC_INITIALIZED)) { - if (port->port.flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; - break; - } - if (!(port->port.flags & ASYNC_CLOSING) && - (do_clocal || CD)) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - tty_unlock(); - schedule(); - tty_lock(); - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->port.open_wait, &wait); - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->port.count++; - port->port.blocked_open--; - spin_unlock_irqrestore(&port->lock, flags); - if (retval) { - func_exit(); - return retval; - } - - port->port.flags |= ASYNC_NORMAL_ACTIVE; - func_exit(); - return 0; -} - - -static int sx_open(struct tty_struct *tty, struct file *filp) -{ - int board; - int error; - struct specialix_port *port; - struct specialix_board *bp; - int i; - unsigned long flags; - - func_enter(); - - board = SX_BOARD(tty->index); - - if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) { - func_exit(); - return -ENODEV; - } - - bp = &sx_board[board]; - port = sx_port + board * SX_NPORT + SX_PORT(tty->index); - port->overrun = 0; - for (i = 0; i < 10; i++) - port->hits[i] = 0; - - dprintk(SX_DEBUG_OPEN, - "Board = %d, bp = %p, port = %p, portno = %d.\n", - board, bp, port, SX_PORT(tty->index)); - - if (sx_paranoia_check(port, tty->name, "sx_open")) { - func_enter(); - return -ENODEV; - } - - error = sx_setup_board(bp); - if (error) { - func_exit(); - return error; - } - - spin_lock_irqsave(&bp->lock, flags); - port->port.count++; - bp->count++; - tty->driver_data = port; - port->port.tty = tty; - spin_unlock_irqrestore(&bp->lock, flags); - - error = sx_setup_port(bp, port); - if (error) { - func_enter(); - return error; - } - - error = block_til_ready(tty, filp, port); - if (error) { - func_enter(); - return error; - } - - func_exit(); - return 0; -} - -static void sx_flush_buffer(struct tty_struct *tty) -{ - struct specialix_port *port = tty->driver_data; - unsigned long flags; - struct specialix_board *bp; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) { - func_exit(); - return; - } - - bp = port_Board(port); - spin_lock_irqsave(&port->lock, flags); - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - spin_unlock_irqrestore(&port->lock, flags); - tty_wakeup(tty); - - func_exit(); -} - -static void sx_close(struct tty_struct *tty, struct file *filp) -{ - struct specialix_port *port = tty->driver_data; - struct specialix_board *bp; - unsigned long flags; - unsigned long timeout; - - func_enter(); - if (!port || sx_paranoia_check(port, tty->name, "close")) { - func_exit(); - return; - } - spin_lock_irqsave(&port->lock, flags); - - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&port->lock, flags); - func_exit(); - return; - } - - bp = port_Board(port); - if (tty->count == 1 && port->port.count != 1) { - printk(KERN_ERR "sx%d: sx_close: bad port count;" - " tty->count is 1, port count is %d\n", - board_No(bp), port->port.count); - port->port.count = 1; - } - - if (port->port.count > 1) { - port->port.count--; - bp->count--; - - spin_unlock_irqrestore(&port->lock, flags); - - func_exit(); - return; - } - port->port.flags |= ASYNC_CLOSING; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - spin_unlock_irqrestore(&port->lock, flags); - dprintk(SX_DEBUG_OPEN, "Closing\n"); - if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, port->port.closing_wait); - /* - * At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - dprintk(SX_DEBUG_OPEN, "Closed\n"); - port->IER &= ~IER_RXD; - if (port->port.flags & ASYNC_INITIALIZED) { - port->IER &= ~IER_TXRDY; - port->IER |= IER_TXEMPTY; - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CAR, port_No(port)); - sx_out(bp, CD186x_IER, port->IER); - spin_unlock_irqrestore(&bp->lock, flags); - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - timeout = jiffies+HZ; - while (port->IER & IER_TXEMPTY) { - set_current_state(TASK_INTERRUPTIBLE); - msleep_interruptible(jiffies_to_msecs(port->timeout)); - if (time_after(jiffies, timeout)) { - printk(KERN_INFO "Timeout waiting for close\n"); - break; - } - } - - } - - if (--bp->count < 0) { - printk(KERN_ERR - "sx%d: sx_shutdown_port: bad board count: %d port: %d\n", - board_No(bp), bp->count, tty->index); - bp->count = 0; - } - if (--port->port.count < 0) { - printk(KERN_ERR - "sx%d: sx_close: bad port count for tty%d: %d\n", - board_No(bp), port_No(port), port->port.count); - port->port.count = 0; - } - - sx_shutdown_port(bp, port); - sx_flush_buffer(tty); - tty_ldisc_flush(tty); - spin_lock_irqsave(&port->lock, flags); - tty->closing = 0; - port->port.tty = NULL; - spin_unlock_irqrestore(&port->lock, flags); - if (port->port.blocked_open) { - if (port->port.close_delay) - msleep_interruptible( - jiffies_to_msecs(port->port.close_delay)); - wake_up_interruptible(&port->port.open_wait); - } - port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&port->port.close_wait); - - func_exit(); -} - - -static int sx_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct specialix_port *port = tty->driver_data; - struct specialix_board *bp; - int c, total = 0; - unsigned long flags; - - func_enter(); - if (sx_paranoia_check(port, tty->name, "sx_write")) { - func_exit(); - return 0; - } - - bp = port_Board(port); - - if (!port->xmit_buf) { - func_exit(); - return 0; - } - - while (1) { - spin_lock_irqsave(&port->lock, flags); - c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, - SERIAL_XMIT_SIZE - port->xmit_head)); - if (c <= 0) { - spin_unlock_irqrestore(&port->lock, flags); - break; - } - memcpy(port->xmit_buf + port->xmit_head, buf, c); - port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - port->xmit_cnt += c; - spin_unlock_irqrestore(&port->lock, flags); - - buf += c; - count -= c; - total += c; - } - - spin_lock_irqsave(&bp->lock, flags); - if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && - !(port->IER & IER_TXRDY)) { - port->IER |= IER_TXRDY; - sx_out(bp, CD186x_CAR, port_No(port)); - sx_out(bp, CD186x_IER, port->IER); - } - spin_unlock_irqrestore(&bp->lock, flags); - func_exit(); - - return total; -} - - -static int sx_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct specialix_port *port = tty->driver_data; - unsigned long flags; - struct specialix_board *bp; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_put_char")) { - func_exit(); - return 0; - } - dprintk(SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf); - if (!port->xmit_buf) { - func_exit(); - return 0; - } - bp = port_Board(port); - spin_lock_irqsave(&port->lock, flags); - - dprintk(SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", - port->xmit_cnt, port->xmit_buf); - if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1 || !port->xmit_buf) { - spin_unlock_irqrestore(&port->lock, flags); - dprintk(SX_DEBUG_TX, "Exit size\n"); - func_exit(); - return 0; - } - dprintk(SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf); - port->xmit_buf[port->xmit_head++] = ch; - port->xmit_head &= SERIAL_XMIT_SIZE - 1; - port->xmit_cnt++; - spin_unlock_irqrestore(&port->lock, flags); - - func_exit(); - return 1; -} - - -static void sx_flush_chars(struct tty_struct *tty) -{ - struct specialix_port *port = tty->driver_data; - unsigned long flags; - struct specialix_board *bp = port_Board(port); - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) { - func_exit(); - return; - } - if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !port->xmit_buf) { - func_exit(); - return; - } - spin_lock_irqsave(&bp->lock, flags); - port->IER |= IER_TXRDY; - sx_out(port_Board(port), CD186x_CAR, port_No(port)); - sx_out(port_Board(port), CD186x_IER, port->IER); - spin_unlock_irqrestore(&bp->lock, flags); - - func_exit(); -} - - -static int sx_write_room(struct tty_struct *tty) -{ - struct specialix_port *port = tty->driver_data; - int ret; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_write_room")) { - func_exit(); - return 0; - } - - ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; - if (ret < 0) - ret = 0; - - func_exit(); - return ret; -} - - -static int sx_chars_in_buffer(struct tty_struct *tty) -{ - struct specialix_port *port = tty->driver_data; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) { - func_exit(); - return 0; - } - func_exit(); - return port->xmit_cnt; -} - -static int sx_tiocmget(struct tty_struct *tty) -{ - struct specialix_port *port = tty->driver_data; - struct specialix_board *bp; - unsigned char status; - unsigned int result; - unsigned long flags; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, __func__)) { - func_exit(); - return -ENODEV; - } - - bp = port_Board(port); - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CAR, port_No(port)); - status = sx_in(bp, CD186x_MSVR); - spin_unlock_irqrestore(&bp->lock, flags); - dprintk(SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n", - port_No(port), status, sx_in(bp, CD186x_CAR)); - dprintk(SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port); - if (sx_crtscts(port->port.tty)) { - result = TIOCM_DTR | TIOCM_DSR - | ((status & MSVR_DTR) ? TIOCM_RTS : 0) - | ((status & MSVR_CD) ? TIOCM_CAR : 0) - | ((status & MSVR_CTS) ? TIOCM_CTS : 0); - } else { - result = TIOCM_RTS | TIOCM_DSR - | ((status & MSVR_DTR) ? TIOCM_DTR : 0) - | ((status & MSVR_CD) ? TIOCM_CAR : 0) - | ((status & MSVR_CTS) ? TIOCM_CTS : 0); - } - - func_exit(); - - return result; -} - - -static int sx_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct specialix_port *port = tty->driver_data; - unsigned long flags; - struct specialix_board *bp; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, __func__)) { - func_exit(); - return -ENODEV; - } - - bp = port_Board(port); - - spin_lock_irqsave(&port->lock, flags); - if (sx_crtscts(port->port.tty)) { - if (set & TIOCM_RTS) - port->MSVR |= MSVR_DTR; - } else { - if (set & TIOCM_DTR) - port->MSVR |= MSVR_DTR; - } - if (sx_crtscts(port->port.tty)) { - if (clear & TIOCM_RTS) - port->MSVR &= ~MSVR_DTR; - } else { - if (clear & TIOCM_DTR) - port->MSVR &= ~MSVR_DTR; - } - spin_lock(&bp->lock); - sx_out(bp, CD186x_CAR, port_No(port)); - sx_out(bp, CD186x_MSVR, port->MSVR); - spin_unlock(&bp->lock); - spin_unlock_irqrestore(&port->lock, flags); - func_exit(); - return 0; -} - - -static int sx_send_break(struct tty_struct *tty, int length) -{ - struct specialix_port *port = tty->driver_data; - struct specialix_board *bp = port_Board(port); - unsigned long flags; - - func_enter(); - if (length == 0 || length == -1) - return -EOPNOTSUPP; - - spin_lock_irqsave(&port->lock, flags); - port->break_length = SPECIALIX_TPS / HZ * length; - port->COR2 |= COR2_ETC; - port->IER |= IER_TXRDY; - spin_lock(&bp->lock); - sx_out(bp, CD186x_CAR, port_No(port)); - sx_out(bp, CD186x_COR2, port->COR2); - sx_out(bp, CD186x_IER, port->IER); - spin_unlock(&bp->lock); - spin_unlock_irqrestore(&port->lock, flags); - sx_wait_CCR(bp); - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CCR, CCR_CORCHG2); - spin_unlock_irqrestore(&bp->lock, flags); - sx_wait_CCR(bp); - - func_exit(); - return 0; -} - - -static int sx_set_serial_info(struct specialix_port *port, - struct serial_struct __user *newinfo) -{ - struct serial_struct tmp; - struct specialix_board *bp = port_Board(port); - int change_speed; - - func_enter(); - - if (copy_from_user(&tmp, newinfo, sizeof(tmp))) { - func_enter(); - return -EFAULT; - } - - mutex_lock(&port->port.mutex); - change_speed = ((port->port.flags & ASYNC_SPD_MASK) != - (tmp.flags & ASYNC_SPD_MASK)); - change_speed |= (tmp.custom_divisor != port->custom_divisor); - - if (!capable(CAP_SYS_ADMIN)) { - if ((tmp.close_delay != port->port.close_delay) || - (tmp.closing_wait != port->port.closing_wait) || - ((tmp.flags & ~ASYNC_USR_MASK) != - (port->port.flags & ~ASYNC_USR_MASK))) { - func_exit(); - mutex_unlock(&port->port.mutex); - return -EPERM; - } - port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | - (tmp.flags & ASYNC_USR_MASK)); - port->custom_divisor = tmp.custom_divisor; - } else { - port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | - (tmp.flags & ASYNC_FLAGS)); - port->port.close_delay = tmp.close_delay; - port->port.closing_wait = tmp.closing_wait; - port->custom_divisor = tmp.custom_divisor; - } - if (change_speed) - sx_change_speed(bp, port); - - func_exit(); - mutex_unlock(&port->port.mutex); - return 0; -} - - -static int sx_get_serial_info(struct specialix_port *port, - struct serial_struct __user *retinfo) -{ - struct serial_struct tmp; - struct specialix_board *bp = port_Board(port); - - func_enter(); - - memset(&tmp, 0, sizeof(tmp)); - mutex_lock(&port->port.mutex); - tmp.type = PORT_CIRRUS; - tmp.line = port - sx_port; - tmp.port = bp->base; - tmp.irq = bp->irq; - tmp.flags = port->port.flags; - tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC; - tmp.close_delay = port->port.close_delay * HZ/100; - tmp.closing_wait = port->port.closing_wait * HZ/100; - tmp.custom_divisor = port->custom_divisor; - tmp.xmit_fifo_size = CD186x_NFIFO; - mutex_unlock(&port->port.mutex); - if (copy_to_user(retinfo, &tmp, sizeof(tmp))) { - func_exit(); - return -EFAULT; - } - - func_exit(); - return 0; -} - - -static int sx_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct specialix_port *port = tty->driver_data; - void __user *argp = (void __user *)arg; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_ioctl")) { - func_exit(); - return -ENODEV; - } - - switch (cmd) { - case TIOCGSERIAL: - func_exit(); - return sx_get_serial_info(port, argp); - case TIOCSSERIAL: - func_exit(); - return sx_set_serial_info(port, argp); - default: - func_exit(); - return -ENOIOCTLCMD; - } - func_exit(); - return 0; -} - - -static void sx_throttle(struct tty_struct *tty) -{ - struct specialix_port *port = tty->driver_data; - struct specialix_board *bp; - unsigned long flags; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_throttle")) { - func_exit(); - return; - } - - bp = port_Board(port); - - /* Use DTR instead of RTS ! */ - if (sx_crtscts(tty)) - port->MSVR &= ~MSVR_DTR; - else { - /* Auch!!! I think the system shouldn't call this then. */ - /* Or maybe we're supposed (allowed?) to do our side of hw - handshake anyway, even when hardware handshake is off. - When you see this in your logs, please report.... */ - printk(KERN_ERR - "sx%d: Need to throttle, but can't (hardware hs is off)\n", - port_No(port)); - } - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CAR, port_No(port)); - spin_unlock_irqrestore(&bp->lock, flags); - if (I_IXOFF(tty)) { - sx_wait_CCR(bp); - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CCR, CCR_SSCH2); - spin_unlock_irqrestore(&bp->lock, flags); - sx_wait_CCR(bp); - } - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_MSVR, port->MSVR); - spin_unlock_irqrestore(&bp->lock, flags); - - func_exit(); -} - - -static void sx_unthrottle(struct tty_struct *tty) -{ - struct specialix_port *port = tty->driver_data; - struct specialix_board *bp; - unsigned long flags; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) { - func_exit(); - return; - } - - bp = port_Board(port); - - spin_lock_irqsave(&port->lock, flags); - /* XXXX Use DTR INSTEAD???? */ - if (sx_crtscts(tty)) - port->MSVR |= MSVR_DTR; - /* Else clause: see remark in "sx_throttle"... */ - spin_lock(&bp->lock); - sx_out(bp, CD186x_CAR, port_No(port)); - spin_unlock(&bp->lock); - if (I_IXOFF(tty)) { - spin_unlock_irqrestore(&port->lock, flags); - sx_wait_CCR(bp); - spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_CCR, CCR_SSCH1); - spin_unlock_irqrestore(&bp->lock, flags); - sx_wait_CCR(bp); - spin_lock_irqsave(&port->lock, flags); - } - spin_lock(&bp->lock); - sx_out(bp, CD186x_MSVR, port->MSVR); - spin_unlock(&bp->lock); - spin_unlock_irqrestore(&port->lock, flags); - - func_exit(); -} - - -static void sx_stop(struct tty_struct *tty) -{ - struct specialix_port *port = tty->driver_data; - struct specialix_board *bp; - unsigned long flags; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_stop")) { - func_exit(); - return; - } - - bp = port_Board(port); - - spin_lock_irqsave(&port->lock, flags); - port->IER &= ~IER_TXRDY; - spin_lock(&bp->lock); - sx_out(bp, CD186x_CAR, port_No(port)); - sx_out(bp, CD186x_IER, port->IER); - spin_unlock(&bp->lock); - spin_unlock_irqrestore(&port->lock, flags); - - func_exit(); -} - - -static void sx_start(struct tty_struct *tty) -{ - struct specialix_port *port = tty->driver_data; - struct specialix_board *bp; - unsigned long flags; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_start")) { - func_exit(); - return; - } - - bp = port_Board(port); - - spin_lock_irqsave(&port->lock, flags); - if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { - port->IER |= IER_TXRDY; - spin_lock(&bp->lock); - sx_out(bp, CD186x_CAR, port_No(port)); - sx_out(bp, CD186x_IER, port->IER); - spin_unlock(&bp->lock); - } - spin_unlock_irqrestore(&port->lock, flags); - - func_exit(); -} - -static void sx_hangup(struct tty_struct *tty) -{ - struct specialix_port *port = tty->driver_data; - struct specialix_board *bp; - unsigned long flags; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_hangup")) { - func_exit(); - return; - } - - bp = port_Board(port); - - sx_shutdown_port(bp, port); - spin_lock_irqsave(&port->lock, flags); - bp->count -= port->port.count; - if (bp->count < 0) { - printk(KERN_ERR - "sx%d: sx_hangup: bad board count: %d port: %d\n", - board_No(bp), bp->count, tty->index); - bp->count = 0; - } - port->port.count = 0; - port->port.flags &= ~ASYNC_NORMAL_ACTIVE; - port->port.tty = NULL; - spin_unlock_irqrestore(&port->lock, flags); - wake_up_interruptible(&port->port.open_wait); - - func_exit(); -} - - -static void sx_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct specialix_port *port = tty->driver_data; - unsigned long flags; - struct specialix_board *bp; - - if (sx_paranoia_check(port, tty->name, "sx_set_termios")) - return; - - bp = port_Board(port); - spin_lock_irqsave(&port->lock, flags); - sx_change_speed(port_Board(port), port); - spin_unlock_irqrestore(&port->lock, flags); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - sx_start(tty); - } -} - -static const struct tty_operations sx_ops = { - .open = sx_open, - .close = sx_close, - .write = sx_write, - .put_char = sx_put_char, - .flush_chars = sx_flush_chars, - .write_room = sx_write_room, - .chars_in_buffer = sx_chars_in_buffer, - .flush_buffer = sx_flush_buffer, - .ioctl = sx_ioctl, - .throttle = sx_throttle, - .unthrottle = sx_unthrottle, - .set_termios = sx_set_termios, - .stop = sx_stop, - .start = sx_start, - .hangup = sx_hangup, - .tiocmget = sx_tiocmget, - .tiocmset = sx_tiocmset, - .break_ctl = sx_send_break, -}; - -static int sx_init_drivers(void) -{ - int error; - int i; - - func_enter(); - - specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT); - if (!specialix_driver) { - printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n"); - func_exit(); - return 1; - } - - specialix_driver->owner = THIS_MODULE; - specialix_driver->name = "ttyW"; - specialix_driver->major = SPECIALIX_NORMAL_MAJOR; - specialix_driver->type = TTY_DRIVER_TYPE_SERIAL; - specialix_driver->subtype = SERIAL_TYPE_NORMAL; - specialix_driver->init_termios = tty_std_termios; - specialix_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - specialix_driver->init_termios.c_ispeed = 9600; - specialix_driver->init_termios.c_ospeed = 9600; - specialix_driver->flags = TTY_DRIVER_REAL_RAW | - TTY_DRIVER_HARDWARE_BREAK; - tty_set_operations(specialix_driver, &sx_ops); - - error = tty_register_driver(specialix_driver); - if (error) { - put_tty_driver(specialix_driver); - printk(KERN_ERR - "sx: Couldn't register specialix IO8+ driver, error = %d\n", - error); - func_exit(); - return 1; - } - memset(sx_port, 0, sizeof(sx_port)); - for (i = 0; i < SX_NPORT * SX_NBOARD; i++) { - sx_port[i].magic = SPECIALIX_MAGIC; - tty_port_init(&sx_port[i].port); - spin_lock_init(&sx_port[i].lock); - } - - func_exit(); - return 0; -} - -static void sx_release_drivers(void) -{ - func_enter(); - - tty_unregister_driver(specialix_driver); - put_tty_driver(specialix_driver); - func_exit(); -} - -/* - * This routine must be called by kernel at boot time - */ -static int __init specialix_init(void) -{ - int i; - int found = 0; - - func_enter(); - - printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n"); - printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n"); - if (sx_rtscts) - printk(KERN_INFO - "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n"); - else - printk(KERN_INFO "sx: DTR/RTS pin is always RTS.\n"); - - for (i = 0; i < SX_NBOARD; i++) - spin_lock_init(&sx_board[i].lock); - - if (sx_init_drivers()) { - func_exit(); - return -EIO; - } - - for (i = 0; i < SX_NBOARD; i++) - if (sx_board[i].base && !sx_probe(&sx_board[i])) - found++; - -#ifdef CONFIG_PCI - { - struct pci_dev *pdev = NULL; - - i = 0; - while (i < SX_NBOARD) { - if (sx_board[i].flags & SX_BOARD_PRESENT) { - i++; - continue; - } - pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX, - PCI_DEVICE_ID_SPECIALIX_IO8, pdev); - if (!pdev) - break; - - if (pci_enable_device(pdev)) - continue; - - sx_board[i].irq = pdev->irq; - - sx_board[i].base = pci_resource_start(pdev, 2); - - sx_board[i].flags |= SX_BOARD_IS_PCI; - if (!sx_probe(&sx_board[i])) - found++; - } - /* May exit pci_get sequence early with lots of boards */ - if (pdev != NULL) - pci_dev_put(pdev); - } -#endif - - if (!found) { - sx_release_drivers(); - printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n"); - func_exit(); - return -EIO; - } - - func_exit(); - return 0; -} - -static int iobase[SX_NBOARD] = {0,}; -static int irq[SX_NBOARD] = {0,}; - -module_param_array(iobase, int, NULL, 0); -module_param_array(irq, int, NULL, 0); -module_param(sx_debug, int, 0); -module_param(sx_rtscts, int, 0); -module_param(sx_rxfifo, int, 0); - -/* - * You can setup up to 4 boards. - * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter. - * You should specify the IRQs too in that case "irq=....,...". - * - * More than 4 boards in one computer is not possible, as the card can - * only use 4 different interrupts. - * - */ -static int __init specialix_init_module(void) -{ - int i; - - func_enter(); - - if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) { - for (i = 0; i < SX_NBOARD; i++) { - sx_board[i].base = iobase[i]; - sx_board[i].irq = irq[i]; - sx_board[i].count = 0; - } - } - - func_exit(); - - return specialix_init(); -} - -static void __exit specialix_exit_module(void) -{ - int i; - - func_enter(); - - sx_release_drivers(); - for (i = 0; i < SX_NBOARD; i++) - if (sx_board[i].flags & SX_BOARD_PRESENT) - sx_release_io_range(&sx_board[i]); - func_exit(); -} - -static struct pci_device_id specialx_pci_tbl[] __devinitdata __used = { - { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) }, - { } -}; -MODULE_DEVICE_TABLE(pci, specialx_pci_tbl); - -module_init(specialix_init_module); -module_exit(specialix_exit_module); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(SPECIALIX_NORMAL_MAJOR); diff --git a/drivers/char/specialix_io8.h b/drivers/char/specialix_io8.h deleted file mode 100644 index c63005274d9b..000000000000 --- a/drivers/char/specialix_io8.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * linux/drivers/char/specialix_io8.h -- - * Specialix IO8+ multiport serial driver. - * - * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) - * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) - * - * - * Specialix pays for the development and support of this driver. - * Please DO contact io8-linux@specialix.co.uk if you require - * support. - * - * This driver was developped in the BitWizard linux device - * driver service. If you require a linux device driver for your - * product, please contact devices@BitWizard.nl for a quote. - * - * This code is firmly based on the riscom/8 serial driver, - * written by Dmitry Gorodchanin. The specialix IO8+ card - * programming information was obtained from the CL-CD1865 Data - * Book, and Specialix document number 6200059: IO8+ Hardware - * Functional Specification. - * - * 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. - * */ - -#ifndef __LINUX_SPECIALIX_H -#define __LINUX_SPECIALIX_H - -#include - -#ifdef __KERNEL__ - -/* You can have max 4 ISA cards in one PC, and I recommend not much -more than a few PCI versions of the card. */ - -#define SX_NBOARD 8 - -/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */ -#define SX_IO_SPACE 4 -/* The PCI version decodes 8 addresses, but still only 2 are used. */ -#define SX_PCI_IO_SPACE 8 - -/* eight ports per board. */ -#define SX_NPORT 8 -#define SX_BOARD(line) ((line) / SX_NPORT) -#define SX_PORT(line) ((line) & (SX_NPORT - 1)) - - -#define SX_DATA_REG 0 /* Base+0 : Data register */ -#define SX_ADDR_REG 1 /* base+1 : Address register. */ - -#define MHz *1000000 /* I'm ashamed of myself. */ - -/* On-board oscillator frequency */ -#define SX_OSCFREQ (25 MHz/2) -/* There is a 25MHz crystal on the board, but the chip is in /2 mode */ - - -/* Ticks per sec. Used for setting receiver timeout and break length */ -#define SPECIALIX_TPS 4000 - -/* Yeah, after heavy testing I decided it must be 6. - * Sure, You can change it if needed. - */ -#define SPECIALIX_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ - -#define SPECIALIX_MAGIC 0x0907 - -#define SX_CCR_TIMEOUT 10000 /* CCR timeout. You may need to wait upto - 10 milliseconds before the internal - processor is available again after - you give it a command */ - -#define SX_IOBASE1 0x100 -#define SX_IOBASE2 0x180 -#define SX_IOBASE3 0x250 -#define SX_IOBASE4 0x260 - -struct specialix_board { - unsigned long flags; - unsigned short base; - unsigned char irq; - //signed char count; - int count; - unsigned char DTR; - int reg; - spinlock_t lock; -}; - -#define SX_BOARD_PRESENT 0x00000001 -#define SX_BOARD_ACTIVE 0x00000002 -#define SX_BOARD_IS_PCI 0x00000004 - - -struct specialix_port { - int magic; - struct tty_port port; - int baud_base; - int flags; - int timeout; - unsigned char * xmit_buf; - int custom_divisor; - int xmit_head; - int xmit_tail; - int xmit_cnt; - short wakeup_chars; - short break_length; - unsigned char mark_mask; - unsigned char IER; - unsigned char MSVR; - unsigned char COR2; - unsigned long overrun; - unsigned long hits[10]; - spinlock_t lock; -}; - -#endif /* __KERNEL__ */ -#endif /* __LINUX_SPECIALIX_H */ - - - - - - - - - diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c deleted file mode 100644 index 4fff5cd3b163..000000000000 --- a/drivers/char/stallion.c +++ /dev/null @@ -1,4651 +0,0 @@ -/*****************************************************************************/ - -/* - * stallion.c -- stallion 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 -#include - -#include - -/*****************************************************************************/ - -/* - * Define different board types. Use the standard Stallion "assigned" - * board numbers. Boards supported in this driver are abbreviated as - * EIO = EasyIO and ECH = EasyConnection 8/32. - */ -#define BRD_EASYIO 20 -#define BRD_ECH 21 -#define BRD_ECHMC 22 -#define BRD_ECHPCI 26 -#define BRD_ECH64PCI 27 -#define BRD_EASYIOPCI 28 - -struct stlconf { - unsigned int brdtype; - int ioaddr1; - int ioaddr2; - unsigned long memaddr; - int irq; - int irqtype; -}; - -static unsigned int stl_nrbrds; - -/*****************************************************************************/ - -/* - * 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 - -/* - * Set the TX buffer size. Bigger is better, but we don't want - * to chew too much memory with buffers! - */ -#define STL_TXBUFLOW 512 -#define STL_TXBUFSIZE 4096 - -/*****************************************************************************/ - -/* - * Define our local driver identity first. Set up stuff to deal with - * all the local structures required by a serial tty driver. - */ -static char *stl_drvtitle = "Stallion Multiport Serial Driver"; -static char *stl_drvname = "stallion"; -static char *stl_drvversion = "5.6.0"; - -static struct tty_driver *stl_serial; - -/* - * 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, 8 data bits, 1 stop bit. - */ -static struct ktermios stl_deftermios = { - .c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL), - .c_cc = INIT_C_CC, - .c_ispeed = 9600, - .c_ospeed = 9600, -}; - -/* - * Define global place to put buffer overflow characters. - */ -static char stl_unwanted[SC26198_RXFIFOSIZE]; - -/*****************************************************************************/ - -static DEFINE_MUTEX(stl_brdslock); -static struct stlbrd *stl_brds[STL_MAXBRDS]; - -static const struct tty_port_operations stl_port_ops; - -/* - * Per board state flags. Used with the state field of the board struct. - * Not really much here! - */ -#define BRD_FOUND 0x1 -#define STL_PROBED 0x2 - - -/* - * Define the port structure istate flags. These set of flags are - * modified at interrupt time - so setting and reseting them needs - * to be atomic. Use the bit clear/setting routines for this. - */ -#define ASYI_TXBUSY 1 -#define ASYI_TXLOW 2 -#define ASYI_TXFLOWED 3 - -/* - * Define an array of board names as printable strings. Handy for - * referencing boards when printing trace and stuff. - */ -static char *stl_brdnames[] = { - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - "EasyIO", - "EC8/32-AT", - "EC8/32-MC", - NULL, - NULL, - NULL, - "EC8/32-PCI", - "EC8/64-PCI", - "EasyIO-PCI", -}; - -/*****************************************************************************/ - -/* - * 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 unsigned int stl_nargs; -static char *board0[4]; -static char *board1[4]; -static char *board2[4]; -static char *board3[4]; - -static char **stl_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. - */ - -static struct { - char *name; - int type; -} stl_brdstr[] = { - { "easyio", BRD_EASYIO }, - { "eio", BRD_EASYIO }, - { "20", BRD_EASYIO }, - { "ec8/32", BRD_ECH }, - { "ec8/32-at", BRD_ECH }, - { "ec8/32-isa", BRD_ECH }, - { "ech", BRD_ECH }, - { "echat", BRD_ECH }, - { "21", BRD_ECH }, - { "ec8/32-mc", BRD_ECHMC }, - { "ec8/32-mca", BRD_ECHMC }, - { "echmc", BRD_ECHMC }, - { "echmca", BRD_ECHMC }, - { "22", BRD_ECHMC }, - { "ec8/32-pc", BRD_ECHPCI }, - { "ec8/32-pci", BRD_ECHPCI }, - { "26", BRD_ECHPCI }, - { "ec8/64-pc", BRD_ECH64PCI }, - { "ec8/64-pci", BRD_ECH64PCI }, - { "ech-pci", BRD_ECH64PCI }, - { "echpci", BRD_ECH64PCI }, - { "echpc", BRD_ECH64PCI }, - { "27", BRD_ECH64PCI }, - { "easyio-pc", BRD_EASYIOPCI }, - { "easyio-pci", BRD_EASYIOPCI }, - { "eio-pci", BRD_EASYIOPCI }, - { "eiopci", BRD_EASYIOPCI }, - { "28", BRD_EASYIOPCI }, -}; - -/* - * Define the module agruments. - */ - -module_param_array(board0, charp, &stl_nargs, 0); -MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,ioaddr2][,irq]]"); -module_param_array(board1, charp, &stl_nargs, 0); -MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,ioaddr2][,irq]]"); -module_param_array(board2, charp, &stl_nargs, 0); -MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,ioaddr2][,irq]]"); -module_param_array(board3, charp, &stl_nargs, 0); -MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,ioaddr2][,irq]]"); - -/*****************************************************************************/ - -/* - * Hardware ID bits for the EasyIO and ECH boards. These defines apply - * to the directly accessible io ports of these boards (not the uarts - - * they are in cd1400.h and sc26198.h). - */ -#define EIO_8PORTRS 0x04 -#define EIO_4PORTRS 0x05 -#define EIO_8PORTDI 0x00 -#define EIO_8PORTM 0x06 -#define EIO_MK3 0x03 -#define EIO_IDBITMASK 0x07 - -#define EIO_BRDMASK 0xf0 -#define ID_BRD4 0x10 -#define ID_BRD8 0x20 -#define ID_BRD16 0x30 - -#define EIO_INTRPEND 0x08 -#define EIO_INTEDGE 0x00 -#define EIO_INTLEVEL 0x08 -#define EIO_0WS 0x10 - -#define ECH_ID 0xa0 -#define ECH_IDBITMASK 0xe0 -#define ECH_BRDENABLE 0x08 -#define ECH_BRDDISABLE 0x00 -#define ECH_INTENABLE 0x01 -#define ECH_INTDISABLE 0x00 -#define ECH_INTLEVEL 0x02 -#define ECH_INTEDGE 0x00 -#define ECH_INTRPEND 0x01 -#define ECH_BRDRESET 0x01 - -#define ECHMC_INTENABLE 0x01 -#define ECHMC_BRDRESET 0x02 - -#define ECH_PNLSTATUS 2 -#define ECH_PNL16PORT 0x20 -#define ECH_PNLIDMASK 0x07 -#define ECH_PNLXPID 0x40 -#define ECH_PNLINTRPEND 0x80 - -#define ECH_ADDR2MASK 0x1e0 - -/* - * Define the vector mapping bits for the programmable interrupt board - * hardware. These bits encode the interrupt for the board to use - it - * is software selectable (except the EIO-8M). - */ -static unsigned char stl_vecmap[] = { - 0xff, 0xff, 0xff, 0x04, 0x06, 0x05, 0xff, 0x07, - 0xff, 0xff, 0x00, 0x02, 0x01, 0xff, 0xff, 0x03 -}; - -/* - * Lock ordering is that you may not take stallion_lock holding - * brd_lock. - */ - -static spinlock_t brd_lock; /* Guard the board mapping */ -static spinlock_t stallion_lock; /* Guard the tty driver */ - -/* - * Set up enable and disable macros for the ECH boards. They require - * the secondary io address space to be activated and deactivated. - * This way all ECH boards can share their secondary io region. - * If this is an ECH-PCI board then also need to set the page pointer - * to point to the correct page. - */ -#define BRDENABLE(brdnr,pagenr) \ - if (stl_brds[(brdnr)]->brdtype == BRD_ECH) \ - outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDENABLE), \ - stl_brds[(brdnr)]->ioctrl); \ - else if (stl_brds[(brdnr)]->brdtype == BRD_ECHPCI) \ - outb((pagenr), stl_brds[(brdnr)]->ioctrl); - -#define BRDDISABLE(brdnr) \ - if (stl_brds[(brdnr)]->brdtype == BRD_ECH) \ - outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE), \ - stl_brds[(brdnr)]->ioctrl); - -#define STL_CD1400MAXBAUD 230400 -#define STL_SC26198MAXBAUD 460800 - -#define STL_BAUDBASE 115200 -#define STL_CLOSEDELAY (5 * HZ / 10) - -/*****************************************************************************/ - -/* - * Define the Stallion PCI vendor and device IDs. - */ -#ifndef PCI_VENDOR_ID_STALLION -#define PCI_VENDOR_ID_STALLION 0x124d -#endif -#ifndef PCI_DEVICE_ID_ECHPCI832 -#define PCI_DEVICE_ID_ECHPCI832 0x0000 -#endif -#ifndef PCI_DEVICE_ID_ECHPCI864 -#define PCI_DEVICE_ID_ECHPCI864 0x0002 -#endif -#ifndef PCI_DEVICE_ID_EIOPCI -#define PCI_DEVICE_ID_EIOPCI 0x0003 -#endif - -/* - * Define structure to hold all Stallion PCI boards. - */ - -static struct pci_device_id stl_pcibrds[] = { - { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI864), - .driver_data = BRD_ECH64PCI }, - { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_EIOPCI), - .driver_data = BRD_EASYIOPCI }, - { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832), - .driver_data = BRD_ECHPCI }, - { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410), - .driver_data = BRD_ECHPCI }, - { } -}; -MODULE_DEVICE_TABLE(pci, stl_pcibrds); - -/*****************************************************************************/ - -/* - * Define macros to extract a brd/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 stl_baudrates[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 -}; - -/*****************************************************************************/ - -/* - * Declare all those functions in this driver! - */ - -static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg); -static int stl_brdinit(struct stlbrd *brdp); -static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp); -static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp); - -/* - * CD1400 uart specific handling functions. - */ -static void stl_cd1400setreg(struct stlport *portp, int regnr, int value); -static int stl_cd1400getreg(struct stlport *portp, int regnr); -static int stl_cd1400updatereg(struct stlport *portp, int regnr, int value); -static int stl_cd1400panelinit(struct stlbrd *brdp, struct stlpanel *panelp); -static void stl_cd1400portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp); -static void stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp); -static int stl_cd1400getsignals(struct stlport *portp); -static void stl_cd1400setsignals(struct stlport *portp, int dtr, int rts); -static void stl_cd1400ccrwait(struct stlport *portp); -static void stl_cd1400enablerxtx(struct stlport *portp, int rx, int tx); -static void stl_cd1400startrxtx(struct stlport *portp, int rx, int tx); -static void stl_cd1400disableintrs(struct stlport *portp); -static void stl_cd1400sendbreak(struct stlport *portp, int len); -static void stl_cd1400flowctrl(struct stlport *portp, int state); -static void stl_cd1400sendflow(struct stlport *portp, int state); -static void stl_cd1400flush(struct stlport *portp); -static int stl_cd1400datastate(struct stlport *portp); -static void stl_cd1400eiointr(struct stlpanel *panelp, unsigned int iobase); -static void stl_cd1400echintr(struct stlpanel *panelp, unsigned int iobase); -static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr); -static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr); -static void stl_cd1400mdmisr(struct stlpanel *panelp, int ioaddr); - -static inline int stl_cd1400breakisr(struct stlport *portp, int ioaddr); - -/* - * SC26198 uart specific handling functions. - */ -static void stl_sc26198setreg(struct stlport *portp, int regnr, int value); -static int stl_sc26198getreg(struct stlport *portp, int regnr); -static int stl_sc26198updatereg(struct stlport *portp, int regnr, int value); -static int stl_sc26198getglobreg(struct stlport *portp, int regnr); -static int stl_sc26198panelinit(struct stlbrd *brdp, struct stlpanel *panelp); -static void stl_sc26198portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp); -static void stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp); -static int stl_sc26198getsignals(struct stlport *portp); -static void stl_sc26198setsignals(struct stlport *portp, int dtr, int rts); -static void stl_sc26198enablerxtx(struct stlport *portp, int rx, int tx); -static void stl_sc26198startrxtx(struct stlport *portp, int rx, int tx); -static void stl_sc26198disableintrs(struct stlport *portp); -static void stl_sc26198sendbreak(struct stlport *portp, int len); -static void stl_sc26198flowctrl(struct stlport *portp, int state); -static void stl_sc26198sendflow(struct stlport *portp, int state); -static void stl_sc26198flush(struct stlport *portp); -static int stl_sc26198datastate(struct stlport *portp); -static void stl_sc26198wait(struct stlport *portp); -static void stl_sc26198txunflow(struct stlport *portp, struct tty_struct *tty); -static void stl_sc26198intr(struct stlpanel *panelp, unsigned int iobase); -static void stl_sc26198txisr(struct stlport *port); -static void stl_sc26198rxisr(struct stlport *port, unsigned int iack); -static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char ch); -static void stl_sc26198rxbadchars(struct stlport *portp); -static void stl_sc26198otherisr(struct stlport *port, unsigned int iack); - -/*****************************************************************************/ - -/* - * Generic UART support structure. - */ -typedef struct uart { - int (*panelinit)(struct stlbrd *brdp, struct stlpanel *panelp); - void (*portinit)(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp); - void (*setport)(struct stlport *portp, struct ktermios *tiosp); - int (*getsignals)(struct stlport *portp); - void (*setsignals)(struct stlport *portp, int dtr, int rts); - void (*enablerxtx)(struct stlport *portp, int rx, int tx); - void (*startrxtx)(struct stlport *portp, int rx, int tx); - void (*disableintrs)(struct stlport *portp); - void (*sendbreak)(struct stlport *portp, int len); - void (*flowctrl)(struct stlport *portp, int state); - void (*sendflow)(struct stlport *portp, int state); - void (*flush)(struct stlport *portp); - int (*datastate)(struct stlport *portp); - void (*intr)(struct stlpanel *panelp, unsigned int iobase); -} uart_t; - -/* - * Define some macros to make calling these functions nice and clean. - */ -#define stl_panelinit (* ((uart_t *) panelp->uartp)->panelinit) -#define stl_portinit (* ((uart_t *) portp->uartp)->portinit) -#define stl_setport (* ((uart_t *) portp->uartp)->setport) -#define stl_getsignals (* ((uart_t *) portp->uartp)->getsignals) -#define stl_setsignals (* ((uart_t *) portp->uartp)->setsignals) -#define stl_enablerxtx (* ((uart_t *) portp->uartp)->enablerxtx) -#define stl_startrxtx (* ((uart_t *) portp->uartp)->startrxtx) -#define stl_disableintrs (* ((uart_t *) portp->uartp)->disableintrs) -#define stl_sendbreak (* ((uart_t *) portp->uartp)->sendbreak) -#define stl_flowctrl (* ((uart_t *) portp->uartp)->flowctrl) -#define stl_sendflow (* ((uart_t *) portp->uartp)->sendflow) -#define stl_flush (* ((uart_t *) portp->uartp)->flush) -#define stl_datastate (* ((uart_t *) portp->uartp)->datastate) - -/*****************************************************************************/ - -/* - * CD1400 UART specific data initialization. - */ -static uart_t stl_cd1400uart = { - stl_cd1400panelinit, - stl_cd1400portinit, - stl_cd1400setport, - stl_cd1400getsignals, - stl_cd1400setsignals, - stl_cd1400enablerxtx, - stl_cd1400startrxtx, - stl_cd1400disableintrs, - stl_cd1400sendbreak, - stl_cd1400flowctrl, - stl_cd1400sendflow, - stl_cd1400flush, - stl_cd1400datastate, - stl_cd1400eiointr -}; - -/* - * Define the offsets within the register bank of a cd1400 based panel. - * These io address offsets are common to the EasyIO board as well. - */ -#define EREG_ADDR 0 -#define EREG_DATA 4 -#define EREG_RXACK 5 -#define EREG_TXACK 6 -#define EREG_MDACK 7 - -#define EREG_BANKSIZE 8 - -#define CD1400_CLK 25000000 -#define CD1400_CLK8M 20000000 - -/* - * Define the cd1400 baud rate clocks. These are used when calculating - * what clock and divisor to use for the required baud rate. Also - * define the maximum baud rate allowed, and the default base baud. - */ -static int stl_cd1400clkdivs[] = { - CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4 -}; - -/*****************************************************************************/ - -/* - * SC26198 UART specific data initization. - */ -static uart_t stl_sc26198uart = { - stl_sc26198panelinit, - stl_sc26198portinit, - stl_sc26198setport, - stl_sc26198getsignals, - stl_sc26198setsignals, - stl_sc26198enablerxtx, - stl_sc26198startrxtx, - stl_sc26198disableintrs, - stl_sc26198sendbreak, - stl_sc26198flowctrl, - stl_sc26198sendflow, - stl_sc26198flush, - stl_sc26198datastate, - stl_sc26198intr -}; - -/* - * Define the offsets within the register bank of a sc26198 based panel. - */ -#define XP_DATA 0 -#define XP_ADDR 1 -#define XP_MODID 2 -#define XP_STATUS 2 -#define XP_IACK 3 - -#define XP_BANKSIZE 4 - -/* - * Define the sc26198 baud rate table. Offsets within the table - * represent the actual baud rate selector of sc26198 registers. - */ -static unsigned int sc26198_baudtable[] = { - 50, 75, 150, 200, 300, 450, 600, 900, 1200, 1800, 2400, 3600, - 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200, - 230400, 460800, 921600 -}; - -#define SC26198_NRBAUDS ARRAY_SIZE(sc26198_baudtable) - -/*****************************************************************************/ - -/* - * Define the driver info for a user level control device. Used mainly - * to get at port stats - only not using the port device itself. - */ -static const struct file_operations stl_fsiomem = { - .owner = THIS_MODULE, - .unlocked_ioctl = stl_memioctl, - .llseek = noop_llseek, -}; - -static struct class *stallion_class; - -static void stl_cd_change(struct stlport *portp) -{ - unsigned int oldsigs = portp->sigs; - struct tty_struct *tty = tty_port_tty_get(&portp->port); - - if (!tty) - return; - - portp->sigs = stl_getsignals(portp); - - if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) - wake_up_interruptible(&portp->port.open_wait); - - if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) - if (portp->port.flags & ASYNC_CHECK_CD) - tty_hangup(tty); - tty_kref_put(tty); -} - -/* - * Check for any arguments passed in on the module load command line. - */ - -/*****************************************************************************/ - -/* - * Parse the supplied argument string, into the board conf struct. - */ - -static int __init stl_parsebrd(struct stlconf *confp, char **argp) -{ - char *sp; - unsigned int i; - - pr_debug("stl_parsebrd(confp=%p,argp=%p)\n", confp, argp); - - if ((argp[0] == 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(stl_brdstr); i++) - if (strcmp(stl_brdstr[i].name, argp[0]) == 0) - break; - - if (i == ARRAY_SIZE(stl_brdstr)) { - printk("STALLION: unknown board name, %s?\n", argp[0]); - return 0; - } - - confp->brdtype = stl_brdstr[i].type; - - i = 1; - if ((argp[i] != NULL) && (*argp[i] != 0)) - confp->ioaddr1 = simple_strtoul(argp[i], NULL, 0); - i++; - if (confp->brdtype == BRD_ECH) { - if ((argp[i] != NULL) && (*argp[i] != 0)) - confp->ioaddr2 = simple_strtoul(argp[i], NULL, 0); - i++; - } - if ((argp[i] != NULL) && (*argp[i] != 0)) - confp->irq = simple_strtoul(argp[i], NULL, 0); - return 1; -} - -/*****************************************************************************/ - -/* - * Allocate a new board structure. Fill out the basic info in it. - */ - -static struct stlbrd *stl_allocbrd(void) -{ - struct stlbrd *brdp; - - brdp = kzalloc(sizeof(struct stlbrd), GFP_KERNEL); - if (!brdp) { - printk("STALLION: failed to allocate memory (size=%Zd)\n", - sizeof(struct stlbrd)); - return NULL; - } - - brdp->magic = STL_BOARDMAGIC; - return brdp; -} - -/*****************************************************************************/ - -static int stl_activate(struct tty_port *port, struct tty_struct *tty) -{ - struct stlport *portp = container_of(port, struct stlport, port); - if (!portp->tx.buf) { - portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL); - if (!portp->tx.buf) - return -ENOMEM; - portp->tx.head = portp->tx.buf; - portp->tx.tail = portp->tx.buf; - } - stl_setport(portp, tty->termios); - portp->sigs = stl_getsignals(portp); - stl_setsignals(portp, 1, 1); - stl_enablerxtx(portp, 1, 1); - stl_startrxtx(portp, 1, 0); - return 0; -} - -static int stl_open(struct tty_struct *tty, struct file *filp) -{ - struct stlport *portp; - struct stlbrd *brdp; - unsigned int minordev, brdnr, panelnr; - int portnr; - - pr_debug("stl_open(tty=%p,filp=%p): device=%s\n", tty, filp, tty->name); - - minordev = tty->index; - brdnr = MINOR2BRD(minordev); - if (brdnr >= stl_nrbrds) - return -ENODEV; - brdp = stl_brds[brdnr]; - if (brdp == NULL) - return -ENODEV; - - minordev = MINOR2PORT(minordev); - for (portnr = -1, panelnr = 0; panelnr < STL_MAXPANELS; panelnr++) { - if (brdp->panels[panelnr] == NULL) - break; - if (minordev < brdp->panels[panelnr]->nrports) { - portnr = minordev; - break; - } - minordev -= brdp->panels[panelnr]->nrports; - } - if (portnr < 0) - return -ENODEV; - - portp = brdp->panels[panelnr]->ports[portnr]; - if (portp == NULL) - return -ENODEV; - - tty->driver_data = portp; - return tty_port_open(&portp->port, tty, filp); - -} - -/*****************************************************************************/ - -static int stl_carrier_raised(struct tty_port *port) -{ - struct stlport *portp = container_of(port, struct stlport, port); - return (portp->sigs & TIOCM_CD) ? 1 : 0; -} - -static void stl_dtr_rts(struct tty_port *port, int on) -{ - struct stlport *portp = container_of(port, struct stlport, port); - /* Takes brd_lock internally */ - stl_setsignals(portp, on, on); -} - -/*****************************************************************************/ - -static void stl_flushbuffer(struct tty_struct *tty) -{ - struct stlport *portp; - - pr_debug("stl_flushbuffer(tty=%p)\n", tty); - - portp = tty->driver_data; - if (portp == NULL) - return; - - stl_flush(portp); - tty_wakeup(tty); -} - -/*****************************************************************************/ - -static void stl_waituntilsent(struct tty_struct *tty, int timeout) -{ - struct stlport *portp; - unsigned long tend; - - pr_debug("stl_waituntilsent(tty=%p,timeout=%d)\n", tty, timeout); - - portp = tty->driver_data; - if (portp == NULL) - return; - - if (timeout == 0) - timeout = HZ; - tend = jiffies + timeout; - - while (stl_datastate(portp)) { - if (signal_pending(current)) - break; - msleep_interruptible(20); - if (time_after_eq(jiffies, tend)) - break; - } -} - -/*****************************************************************************/ - -static void stl_shutdown(struct tty_port *port) -{ - struct stlport *portp = container_of(port, struct stlport, port); - stl_disableintrs(portp); - stl_enablerxtx(portp, 0, 0); - stl_flush(portp); - portp->istate = 0; - if (portp->tx.buf != NULL) { - kfree(portp->tx.buf); - portp->tx.buf = NULL; - portp->tx.head = NULL; - portp->tx.tail = NULL; - } -} - -static void stl_close(struct tty_struct *tty, struct file *filp) -{ - struct stlport*portp; - pr_debug("stl_close(tty=%p,filp=%p)\n", tty, filp); - - portp = tty->driver_data; - if(portp == NULL) - return; - tty_port_close(&portp->port, tty, filp); -} - -/*****************************************************************************/ - -/* - * Write routine. Take data and stuff it in to the TX ring queue. - * If transmit interrupts are not running then start them. - */ - -static int stl_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - struct stlport *portp; - unsigned int len, stlen; - unsigned char *chbuf; - char *head, *tail; - - pr_debug("stl_write(tty=%p,buf=%p,count=%d)\n", tty, buf, count); - - portp = tty->driver_data; - if (portp == NULL) - return 0; - if (portp->tx.buf == NULL) - return 0; - -/* - * If copying direct from user space we must cater for page faults, - * causing us to "sleep" here for a while. To handle this copy in all - * the data we need now, into a local buffer. Then when we got it all - * copy it into the TX buffer. - */ - chbuf = (unsigned char *) buf; - - head = portp->tx.head; - tail = portp->tx.tail; - if (head >= tail) { - len = STL_TXBUFSIZE - (head - tail) - 1; - stlen = STL_TXBUFSIZE - (head - portp->tx.buf); - } else { - len = tail - head - 1; - stlen = len; - } - - len = min(len, (unsigned int)count); - count = 0; - while (len > 0) { - stlen = min(len, stlen); - memcpy(head, chbuf, stlen); - len -= stlen; - chbuf += stlen; - count += stlen; - head += stlen; - if (head >= (portp->tx.buf + STL_TXBUFSIZE)) { - head = portp->tx.buf; - stlen = tail - head; - } - } - portp->tx.head = head; - - clear_bit(ASYI_TXLOW, &portp->istate); - stl_startrxtx(portp, -1, 1); - - return count; -} - -/*****************************************************************************/ - -static int stl_putchar(struct tty_struct *tty, unsigned char ch) -{ - struct stlport *portp; - unsigned int len; - char *head, *tail; - - pr_debug("stl_putchar(tty=%p,ch=%x)\n", tty, ch); - - portp = tty->driver_data; - if (portp == NULL) - return -EINVAL; - if (portp->tx.buf == NULL) - return -EINVAL; - - head = portp->tx.head; - tail = portp->tx.tail; - - len = (head >= tail) ? (STL_TXBUFSIZE - (head - tail)) : (tail - head); - len--; - - if (len > 0) { - *head++ = ch; - if (head >= (portp->tx.buf + STL_TXBUFSIZE)) - head = portp->tx.buf; - } - portp->tx.head = head; - return 0; -} - -/*****************************************************************************/ - -/* - * If there are any characters in the buffer then make sure that TX - * interrupts are on and get'em out. Normally used after the putchar - * routine has been called. - */ - -static void stl_flushchars(struct tty_struct *tty) -{ - struct stlport *portp; - - pr_debug("stl_flushchars(tty=%p)\n", tty); - - portp = tty->driver_data; - if (portp == NULL) - return; - if (portp->tx.buf == NULL) - return; - - stl_startrxtx(portp, -1, 1); -} - -/*****************************************************************************/ - -static int stl_writeroom(struct tty_struct *tty) -{ - struct stlport *portp; - char *head, *tail; - - pr_debug("stl_writeroom(tty=%p)\n", tty); - - portp = tty->driver_data; - if (portp == NULL) - return 0; - if (portp->tx.buf == NULL) - return 0; - - head = portp->tx.head; - tail = portp->tx.tail; - return (head >= tail) ? (STL_TXBUFSIZE - (head - tail) - 1) : (tail - head - 1); -} - -/*****************************************************************************/ - -/* - * Return number of chars in the TX buffer. Normally we would just - * calculate the number of chars in the buffer and return that, but if - * the buffer is empty and TX interrupts are still on then we return - * that the buffer still has 1 char in it. This way whoever called us - * will not think that ALL chars have drained - since the UART still - * must have some chars in it (we are busy after all). - */ - -static int stl_charsinbuffer(struct tty_struct *tty) -{ - struct stlport *portp; - unsigned int size; - char *head, *tail; - - pr_debug("stl_charsinbuffer(tty=%p)\n", tty); - - portp = tty->driver_data; - if (portp == NULL) - return 0; - if (portp->tx.buf == NULL) - return 0; - - head = portp->tx.head; - tail = portp->tx.tail; - size = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); - if ((size == 0) && test_bit(ASYI_TXBUSY, &portp->istate)) - size = 1; - return size; -} - -/*****************************************************************************/ - -/* - * Generate the serial struct info. - */ - -static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp) -{ - struct serial_struct sio; - struct stlbrd *brdp; - - pr_debug("stl_getserial(portp=%p,sp=%p)\n", portp, sp); - - memset(&sio, 0, sizeof(struct serial_struct)); - - mutex_lock(&portp->port.mutex); - sio.line = portp->portnr; - sio.port = portp->ioaddr; - sio.flags = portp->port.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.hub6 = 0; - if (portp->uartp == &stl_cd1400uart) { - sio.type = PORT_CIRRUS; - sio.xmit_fifo_size = CD1400_TXFIFOSIZE; - } else { - sio.type = PORT_UNKNOWN; - sio.xmit_fifo_size = SC26198_TXFIFOSIZE; - } - - brdp = stl_brds[portp->brdnr]; - if (brdp != NULL) - sio.irq = brdp->irq; - mutex_unlock(&portp->port.mutex); - - 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 stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp) -{ - struct stlport * portp = tty->driver_data; - struct serial_struct sio; - - pr_debug("stl_setserial(portp=%p,sp=%p)\n", portp, sp); - - if (copy_from_user(&sio, sp, sizeof(struct serial_struct))) - return -EFAULT; - mutex_lock(&portp->port.mutex); - if (!capable(CAP_SYS_ADMIN)) { - if ((sio.baud_base != portp->baud_base) || - (sio.close_delay != portp->close_delay) || - ((sio.flags & ~ASYNC_USR_MASK) != - (portp->port.flags & ~ASYNC_USR_MASK))) { - mutex_unlock(&portp->port.mutex); - return -EPERM; - } - } - - portp->port.flags = (portp->port.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; - mutex_unlock(&portp->port.mutex); - stl_setport(portp, tty->termios); - return 0; -} - -/*****************************************************************************/ - -static int stl_tiocmget(struct tty_struct *tty) -{ - struct stlport *portp; - - portp = tty->driver_data; - if (portp == NULL) - return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - return stl_getsignals(portp); -} - -static int stl_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct stlport *portp; - int rts = -1, dtr = -1; - - portp = tty->driver_data; - if (portp == NULL) - return -ENODEV; - 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; - - stl_setsignals(portp, dtr, rts); - return 0; -} - -static int stl_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) -{ - struct stlport *portp; - int rc; - void __user *argp = (void __user *)arg; - - pr_debug("stl_ioctl(tty=%p,cmd=%x,arg=%lx)\n", tty, cmd, arg); - - portp = tty->driver_data; - if (portp == NULL) - return -ENODEV; - - 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 TIOCGSERIAL: - rc = stl_getserial(portp, argp); - break; - case TIOCSSERIAL: - rc = stl_setserial(tty, argp); - break; - case COM_GETPORTSTATS: - rc = stl_getportstats(tty, portp, argp); - break; - case COM_CLRPORTSTATS: - rc = stl_clrportstats(portp, argp); - break; - case TIOCSERCONFIG: - case TIOCSERGWILD: - case TIOCSERSWILD: - case TIOCSERGETLSR: - case TIOCSERGSTRUCT: - case TIOCSERGETMULTI: - case TIOCSERSETMULTI: - default: - rc = -ENOIOCTLCMD; - break; - } - return rc; -} - -/*****************************************************************************/ - -/* - * Start the transmitter again. Just turn TX interrupts back on. - */ - -static void stl_start(struct tty_struct *tty) -{ - struct stlport *portp; - - pr_debug("stl_start(tty=%p)\n", tty); - - portp = tty->driver_data; - if (portp == NULL) - return; - stl_startrxtx(portp, -1, 1); -} - -/*****************************************************************************/ - -static void stl_settermios(struct tty_struct *tty, struct ktermios *old) -{ - struct stlport *portp; - struct ktermios *tiosp; - - pr_debug("stl_settermios(tty=%p,old=%p)\n", tty, old); - - portp = tty->driver_data; - if (portp == NULL) - return; - - tiosp = tty->termios; - if ((tiosp->c_cflag == old->c_cflag) && - (tiosp->c_iflag == old->c_iflag)) - return; - - stl_setport(portp, tiosp); - stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), - -1); - if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) { - tty->hw_stopped = 0; - stl_start(tty); - } - if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL)) - wake_up_interruptible(&portp->port.open_wait); -} - -/*****************************************************************************/ - -/* - * Attempt to flow control who ever is sending us data. Based on termios - * settings use software or/and hardware flow control. - */ - -static void stl_throttle(struct tty_struct *tty) -{ - struct stlport *portp; - - pr_debug("stl_throttle(tty=%p)\n", tty); - - portp = tty->driver_data; - if (portp == NULL) - return; - stl_flowctrl(portp, 0); -} - -/*****************************************************************************/ - -/* - * Unflow control the device sending us data... - */ - -static void stl_unthrottle(struct tty_struct *tty) -{ - struct stlport *portp; - - pr_debug("stl_unthrottle(tty=%p)\n", tty); - - portp = tty->driver_data; - if (portp == NULL) - return; - stl_flowctrl(portp, 1); -} - -/*****************************************************************************/ - -/* - * Stop the transmitter. Basically to do this we will just turn TX - * interrupts off. - */ - -static void stl_stop(struct tty_struct *tty) -{ - struct stlport *portp; - - pr_debug("stl_stop(tty=%p)\n", tty); - - portp = tty->driver_data; - if (portp == NULL) - return; - stl_startrxtx(portp, -1, 0); -} - -/*****************************************************************************/ - -/* - * 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. - */ - -static void stl_hangup(struct tty_struct *tty) -{ - struct stlport *portp = tty->driver_data; - pr_debug("stl_hangup(tty=%p)\n", tty); - - if (portp == NULL) - return; - tty_port_hangup(&portp->port); -} - -/*****************************************************************************/ - -static int stl_breakctl(struct tty_struct *tty, int state) -{ - struct stlport *portp; - - pr_debug("stl_breakctl(tty=%p,state=%d)\n", tty, state); - - portp = tty->driver_data; - if (portp == NULL) - return -EINVAL; - - stl_sendbreak(portp, ((state == -1) ? 1 : 2)); - return 0; -} - -/*****************************************************************************/ - -static void stl_sendxchar(struct tty_struct *tty, char ch) -{ - struct stlport *portp; - - pr_debug("stl_sendxchar(tty=%p,ch=%x)\n", tty, ch); - - portp = tty->driver_data; - if (portp == NULL) - return; - - if (ch == STOP_CHAR(tty)) - stl_sendflow(portp, 0); - else if (ch == START_CHAR(tty)) - stl_sendflow(portp, 1); - else - stl_putchar(tty, ch); -} - -static void stl_portinfo(struct seq_file *m, struct stlport *portp, int portnr) -{ - int sigs; - char sep; - - seq_printf(m, "%d: uart:%s tx:%d rx:%d", - portnr, (portp->hwid == 1) ? "SC26198" : "CD1400", - (int) portp->stats.txtotal, (int) portp->stats.rxtotal); - - if (portp->stats.rxframing) - seq_printf(m, " fe:%d", (int) portp->stats.rxframing); - if (portp->stats.rxparity) - seq_printf(m, " pe:%d", (int) portp->stats.rxparity); - if (portp->stats.rxbreaks) - seq_printf(m, " brk:%d", (int) portp->stats.rxbreaks); - if (portp->stats.rxoverrun) - seq_printf(m, " oe:%d", (int) portp->stats.rxoverrun); - - sigs = stl_getsignals(portp); - sep = ' '; - if (sigs & TIOCM_RTS) { - seq_printf(m, "%c%s", sep, "RTS"); - sep = '|'; - } - if (sigs & TIOCM_CTS) { - seq_printf(m, "%c%s", sep, "CTS"); - sep = '|'; - } - if (sigs & TIOCM_DTR) { - seq_printf(m, "%c%s", sep, "DTR"); - sep = '|'; - } - if (sigs & TIOCM_CD) { - seq_printf(m, "%c%s", sep, "DCD"); - sep = '|'; - } - if (sigs & TIOCM_DSR) { - seq_printf(m, "%c%s", sep, "DSR"); - sep = '|'; - } - seq_putc(m, '\n'); -} - -/*****************************************************************************/ - -/* - * Port info, read from the /proc file system. - */ - -static int stl_proc_show(struct seq_file *m, void *v) -{ - struct stlbrd *brdp; - struct stlpanel *panelp; - struct stlport *portp; - unsigned int brdnr, panelnr, portnr; - int totalport; - - totalport = 0; - - seq_printf(m, "%s: version %s\n", stl_drvtitle, stl_drvversion); - -/* - * 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 < stl_nrbrds; brdnr++) { - brdp = stl_brds[brdnr]; - if (brdp == NULL) - continue; - if (brdp->state == 0) - continue; - - totalport = brdnr * STL_MAXPORTS; - for (panelnr = 0; panelnr < brdp->nrpanels; panelnr++) { - panelp = brdp->panels[panelnr]; - if (panelp == NULL) - continue; - - for (portnr = 0; portnr < panelp->nrports; portnr++, - totalport++) { - portp = panelp->ports[portnr]; - if (portp == NULL) - continue; - stl_portinfo(m, portp, totalport); - } - } - } - return 0; -} - -static int stl_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, stl_proc_show, NULL); -} - -static const struct file_operations stl_proc_fops = { - .owner = THIS_MODULE, - .open = stl_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/*****************************************************************************/ - -/* - * All board interrupts are vectored through here first. This code then - * calls off to the approrpriate board interrupt handlers. - */ - -static irqreturn_t stl_intr(int irq, void *dev_id) -{ - struct stlbrd *brdp = dev_id; - - pr_debug("stl_intr(brdp=%p,irq=%d)\n", brdp, brdp->irq); - - return IRQ_RETVAL((* brdp->isr)(brdp)); -} - -/*****************************************************************************/ - -/* - * Interrupt service routine for EasyIO board types. - */ - -static int stl_eiointr(struct stlbrd *brdp) -{ - struct stlpanel *panelp; - unsigned int iobase; - int handled = 0; - - spin_lock(&brd_lock); - panelp = brdp->panels[0]; - iobase = panelp->iobase; - while (inb(brdp->iostatus) & EIO_INTRPEND) { - handled = 1; - (* panelp->isr)(panelp, iobase); - } - spin_unlock(&brd_lock); - return handled; -} - -/*****************************************************************************/ - -/* - * Interrupt service routine for ECH-AT board types. - */ - -static int stl_echatintr(struct stlbrd *brdp) -{ - struct stlpanel *panelp; - unsigned int ioaddr, bnknr; - int handled = 0; - - outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); - - while (inb(brdp->iostatus) & ECH_INTRPEND) { - handled = 1; - for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { - ioaddr = brdp->bnkstataddr[bnknr]; - if (inb(ioaddr) & ECH_PNLINTRPEND) { - panelp = brdp->bnk2panel[bnknr]; - (* panelp->isr)(panelp, (ioaddr & 0xfffc)); - } - } - } - - outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); - - return handled; -} - -/*****************************************************************************/ - -/* - * Interrupt service routine for ECH-MCA board types. - */ - -static int stl_echmcaintr(struct stlbrd *brdp) -{ - struct stlpanel *panelp; - unsigned int ioaddr, bnknr; - int handled = 0; - - while (inb(brdp->iostatus) & ECH_INTRPEND) { - handled = 1; - for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { - ioaddr = brdp->bnkstataddr[bnknr]; - if (inb(ioaddr) & ECH_PNLINTRPEND) { - panelp = brdp->bnk2panel[bnknr]; - (* panelp->isr)(panelp, (ioaddr & 0xfffc)); - } - } - } - return handled; -} - -/*****************************************************************************/ - -/* - * Interrupt service routine for ECH-PCI board types. - */ - -static int stl_echpciintr(struct stlbrd *brdp) -{ - struct stlpanel *panelp; - unsigned int ioaddr, bnknr, recheck; - int handled = 0; - - while (1) { - recheck = 0; - for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { - outb(brdp->bnkpageaddr[bnknr], brdp->ioctrl); - ioaddr = brdp->bnkstataddr[bnknr]; - if (inb(ioaddr) & ECH_PNLINTRPEND) { - panelp = brdp->bnk2panel[bnknr]; - (* panelp->isr)(panelp, (ioaddr & 0xfffc)); - recheck++; - handled = 1; - } - } - if (! recheck) - break; - } - return handled; -} - -/*****************************************************************************/ - -/* - * Interrupt service routine for ECH-8/64-PCI board types. - */ - -static int stl_echpci64intr(struct stlbrd *brdp) -{ - struct stlpanel *panelp; - unsigned int ioaddr, bnknr; - int handled = 0; - - while (inb(brdp->ioctrl) & 0x1) { - handled = 1; - for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { - ioaddr = brdp->bnkstataddr[bnknr]; - if (inb(ioaddr) & ECH_PNLINTRPEND) { - panelp = brdp->bnk2panel[bnknr]; - (* panelp->isr)(panelp, (ioaddr & 0xfffc)); - } - } - } - - return handled; -} - -/*****************************************************************************/ - -/* - * Initialize all the ports on a panel. - */ - -static int __devinit stl_initports(struct stlbrd *brdp, struct stlpanel *panelp) -{ - struct stlport *portp; - unsigned int i; - int chipmask; - - pr_debug("stl_initports(brdp=%p,panelp=%p)\n", brdp, panelp); - - chipmask = stl_panelinit(brdp, panelp); - -/* - * All UART's are initialized (if found!). Now go through and setup - * each ports data structures. - */ - for (i = 0; i < panelp->nrports; i++) { - portp = kzalloc(sizeof(struct stlport), GFP_KERNEL); - if (!portp) { - printk("STALLION: failed to allocate memory " - "(size=%Zd)\n", sizeof(struct stlport)); - break; - } - tty_port_init(&portp->port); - portp->port.ops = &stl_port_ops; - portp->magic = STL_PORTMAGIC; - portp->portnr = i; - portp->brdnr = panelp->brdnr; - portp->panelnr = panelp->panelnr; - portp->uartp = panelp->uartp; - portp->clk = brdp->clk; - portp->baud_base = STL_BAUDBASE; - portp->close_delay = STL_CLOSEDELAY; - portp->closing_wait = 30 * HZ; - init_waitqueue_head(&portp->port.open_wait); - init_waitqueue_head(&portp->port.close_wait); - portp->stats.brd = portp->brdnr; - portp->stats.panel = portp->panelnr; - portp->stats.port = portp->portnr; - panelp->ports[i] = portp; - stl_portinit(brdp, panelp, portp); - } - - return 0; -} - -static void stl_cleanup_panels(struct stlbrd *brdp) -{ - struct stlpanel *panelp; - struct stlport *portp; - unsigned int j, k; - struct tty_struct *tty; - - for (j = 0; j < STL_MAXPANELS; j++) { - panelp = brdp->panels[j]; - if (panelp == NULL) - continue; - for (k = 0; k < STL_PORTSPERPANEL; k++) { - portp = panelp->ports[k]; - if (portp == NULL) - continue; - tty = tty_port_tty_get(&portp->port); - if (tty != NULL) { - stl_hangup(tty); - tty_kref_put(tty); - } - kfree(portp->tx.buf); - kfree(portp); - } - kfree(panelp); - } -} - -/*****************************************************************************/ - -/* - * Try to find and initialize an EasyIO board. - */ - -static int __devinit stl_initeio(struct stlbrd *brdp) -{ - struct stlpanel *panelp; - unsigned int status; - char *name; - int retval; - - pr_debug("stl_initeio(brdp=%p)\n", brdp); - - brdp->ioctrl = brdp->ioaddr1 + 1; - brdp->iostatus = brdp->ioaddr1 + 2; - - status = inb(brdp->iostatus); - if ((status & EIO_IDBITMASK) == EIO_MK3) - brdp->ioctrl++; - -/* - * Handle board specific stuff now. The real difference is PCI - * or not PCI. - */ - if (brdp->brdtype == BRD_EASYIOPCI) { - brdp->iosize1 = 0x80; - brdp->iosize2 = 0x80; - name = "serial(EIO-PCI)"; - outb(0x41, (brdp->ioaddr2 + 0x4c)); - } else { - brdp->iosize1 = 8; - name = "serial(EIO)"; - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", - brdp->irq, brdp->brdnr); - retval = -EINVAL; - goto err; - } - outb((stl_vecmap[brdp->irq] | EIO_0WS | - ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), - brdp->ioctrl); - } - - retval = -EBUSY; - if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) { - printk(KERN_WARNING "STALLION: Warning, board %d I/O address " - "%x conflicts with another device\n", brdp->brdnr, - brdp->ioaddr1); - goto err; - } - - if (brdp->iosize2 > 0) - if (!request_region(brdp->ioaddr2, brdp->iosize2, name)) { - printk(KERN_WARNING "STALLION: Warning, board %d I/O " - "address %x conflicts with another device\n", - brdp->brdnr, brdp->ioaddr2); - printk(KERN_WARNING "STALLION: Warning, also " - "releasing board %d I/O address %x \n", - brdp->brdnr, brdp->ioaddr1); - goto err_rel1; - } - -/* - * Everything looks OK, so let's go ahead and probe for the hardware. - */ - brdp->clk = CD1400_CLK; - brdp->isr = stl_eiointr; - - retval = -ENODEV; - switch (status & EIO_IDBITMASK) { - case EIO_8PORTM: - brdp->clk = CD1400_CLK8M; - /* fall thru */ - case EIO_8PORTRS: - case EIO_8PORTDI: - brdp->nrports = 8; - break; - case EIO_4PORTRS: - brdp->nrports = 4; - break; - case EIO_MK3: - switch (status & EIO_BRDMASK) { - case ID_BRD4: - brdp->nrports = 4; - break; - case ID_BRD8: - brdp->nrports = 8; - break; - case ID_BRD16: - brdp->nrports = 16; - break; - default: - goto err_rel2; - } - break; - default: - goto err_rel2; - } - -/* - * We have verified that the board is actually present, so now we - * can complete the setup. - */ - - panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL); - if (!panelp) { - printk(KERN_WARNING "STALLION: failed to allocate memory " - "(size=%Zd)\n", sizeof(struct stlpanel)); - retval = -ENOMEM; - goto err_rel2; - } - - panelp->magic = STL_PANELMAGIC; - panelp->brdnr = brdp->brdnr; - panelp->panelnr = 0; - panelp->nrports = brdp->nrports; - panelp->iobase = brdp->ioaddr1; - panelp->hwid = status; - if ((status & EIO_IDBITMASK) == EIO_MK3) { - panelp->uartp = &stl_sc26198uart; - panelp->isr = stl_sc26198intr; - } else { - panelp->uartp = &stl_cd1400uart; - panelp->isr = stl_cd1400eiointr; - } - - brdp->panels[0] = panelp; - brdp->nrpanels = 1; - brdp->state |= BRD_FOUND; - brdp->hwid = status; - if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) { - printk("STALLION: failed to register interrupt " - "routine for %s irq=%d\n", name, brdp->irq); - retval = -ENODEV; - goto err_fr; - } - - return 0; -err_fr: - stl_cleanup_panels(brdp); -err_rel2: - if (brdp->iosize2 > 0) - release_region(brdp->ioaddr2, brdp->iosize2); -err_rel1: - release_region(brdp->ioaddr1, brdp->iosize1); -err: - return retval; -} - -/*****************************************************************************/ - -/* - * Try to find an ECH board and initialize it. This code is capable of - * dealing with all types of ECH board. - */ - -static int __devinit stl_initech(struct stlbrd *brdp) -{ - struct stlpanel *panelp; - unsigned int status, nxtid, ioaddr, conflict, panelnr, banknr, i; - int retval; - char *name; - - pr_debug("stl_initech(brdp=%p)\n", brdp); - - status = 0; - conflict = 0; - -/* - * Set up the initial board register contents for boards. This varies a - * bit between the different board types. So we need to handle each - * separately. Also do a check that the supplied IRQ is good. - */ - switch (brdp->brdtype) { - - case BRD_ECH: - brdp->isr = stl_echatintr; - brdp->ioctrl = brdp->ioaddr1 + 1; - brdp->iostatus = brdp->ioaddr1 + 1; - status = inb(brdp->iostatus); - if ((status & ECH_IDBITMASK) != ECH_ID) { - retval = -ENODEV; - goto err; - } - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", - brdp->irq, brdp->brdnr); - retval = -EINVAL; - goto err; - } - status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1); - status |= (stl_vecmap[brdp->irq] << 1); - outb((status | ECH_BRDRESET), brdp->ioaddr1); - brdp->ioctrlval = ECH_INTENABLE | - ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE); - for (i = 0; i < 10; i++) - outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); - brdp->iosize1 = 2; - brdp->iosize2 = 32; - name = "serial(EC8/32)"; - outb(status, brdp->ioaddr1); - break; - - case BRD_ECHMC: - brdp->isr = stl_echmcaintr; - brdp->ioctrl = brdp->ioaddr1 + 0x20; - brdp->iostatus = brdp->ioctrl; - status = inb(brdp->iostatus); - if ((status & ECH_IDBITMASK) != ECH_ID) { - retval = -ENODEV; - goto err; - } - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", - brdp->irq, brdp->brdnr); - retval = -EINVAL; - goto err; - } - outb(ECHMC_BRDRESET, brdp->ioctrl); - outb(ECHMC_INTENABLE, brdp->ioctrl); - brdp->iosize1 = 64; - name = "serial(EC8/32-MC)"; - break; - - case BRD_ECHPCI: - brdp->isr = stl_echpciintr; - brdp->ioctrl = brdp->ioaddr1 + 2; - brdp->iosize1 = 4; - brdp->iosize2 = 8; - name = "serial(EC8/32-PCI)"; - break; - - case BRD_ECH64PCI: - brdp->isr = stl_echpci64intr; - brdp->ioctrl = brdp->ioaddr2 + 0x40; - outb(0x43, (brdp->ioaddr1 + 0x4c)); - brdp->iosize1 = 0x80; - brdp->iosize2 = 0x80; - name = "serial(EC8/64-PCI)"; - break; - - default: - printk("STALLION: unknown board type=%d\n", brdp->brdtype); - retval = -EINVAL; - goto err; - } - -/* - * Check boards for possible IO address conflicts and return fail status - * if an IO conflict found. - */ - retval = -EBUSY; - if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) { - printk(KERN_WARNING "STALLION: Warning, board %d I/O address " - "%x conflicts with another device\n", brdp->brdnr, - brdp->ioaddr1); - goto err; - } - - if (brdp->iosize2 > 0) - if (!request_region(brdp->ioaddr2, brdp->iosize2, name)) { - printk(KERN_WARNING "STALLION: Warning, board %d I/O " - "address %x conflicts with another device\n", - brdp->brdnr, brdp->ioaddr2); - printk(KERN_WARNING "STALLION: Warning, also " - "releasing board %d I/O address %x \n", - brdp->brdnr, brdp->ioaddr1); - goto err_rel1; - } - -/* - * Scan through the secondary io address space looking for panels. - * As we find'em allocate and initialize panel structures for each. - */ - brdp->clk = CD1400_CLK; - brdp->hwid = status; - - ioaddr = brdp->ioaddr2; - banknr = 0; - panelnr = 0; - nxtid = 0; - - for (i = 0; i < STL_MAXPANELS; i++) { - if (brdp->brdtype == BRD_ECHPCI) { - outb(nxtid, brdp->ioctrl); - ioaddr = brdp->ioaddr2; - } - status = inb(ioaddr + ECH_PNLSTATUS); - if ((status & ECH_PNLIDMASK) != nxtid) - break; - panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL); - if (!panelp) { - printk("STALLION: failed to allocate memory " - "(size=%Zd)\n", sizeof(struct stlpanel)); - retval = -ENOMEM; - goto err_fr; - } - panelp->magic = STL_PANELMAGIC; - panelp->brdnr = brdp->brdnr; - panelp->panelnr = panelnr; - panelp->iobase = ioaddr; - panelp->pagenr = nxtid; - panelp->hwid = status; - brdp->bnk2panel[banknr] = panelp; - brdp->bnkpageaddr[banknr] = nxtid; - brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS; - - if (status & ECH_PNLXPID) { - panelp->uartp = &stl_sc26198uart; - panelp->isr = stl_sc26198intr; - if (status & ECH_PNL16PORT) { - panelp->nrports = 16; - brdp->bnk2panel[banknr] = panelp; - brdp->bnkpageaddr[banknr] = nxtid; - brdp->bnkstataddr[banknr++] = ioaddr + 4 + - ECH_PNLSTATUS; - } else - panelp->nrports = 8; - } else { - panelp->uartp = &stl_cd1400uart; - panelp->isr = stl_cd1400echintr; - if (status & ECH_PNL16PORT) { - panelp->nrports = 16; - panelp->ackmask = 0x80; - if (brdp->brdtype != BRD_ECHPCI) - ioaddr += EREG_BANKSIZE; - brdp->bnk2panel[banknr] = panelp; - brdp->bnkpageaddr[banknr] = ++nxtid; - brdp->bnkstataddr[banknr++] = ioaddr + - ECH_PNLSTATUS; - } else { - panelp->nrports = 8; - panelp->ackmask = 0xc0; - } - } - - nxtid++; - ioaddr += EREG_BANKSIZE; - brdp->nrports += panelp->nrports; - brdp->panels[panelnr++] = panelp; - if ((brdp->brdtype != BRD_ECHPCI) && - (ioaddr >= (brdp->ioaddr2 + brdp->iosize2))) { - retval = -EINVAL; - goto err_fr; - } - } - - brdp->nrpanels = panelnr; - brdp->nrbnks = banknr; - if (brdp->brdtype == BRD_ECH) - outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); - - brdp->state |= BRD_FOUND; - if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) { - printk("STALLION: failed to register interrupt " - "routine for %s irq=%d\n", name, brdp->irq); - retval = -ENODEV; - goto err_fr; - } - - return 0; -err_fr: - stl_cleanup_panels(brdp); - if (brdp->iosize2 > 0) - release_region(brdp->ioaddr2, brdp->iosize2); -err_rel1: - release_region(brdp->ioaddr1, brdp->iosize1); -err: - return retval; -} - -/*****************************************************************************/ - -/* - * Initialize and configure the specified board. - * Scan through all the boards in the configuration and see what we - * can find. Handle EIO and the ECH boards a little differently here - * since the initial search and setup is very different. - */ - -static int __devinit stl_brdinit(struct stlbrd *brdp) -{ - int i, retval; - - pr_debug("stl_brdinit(brdp=%p)\n", brdp); - - switch (brdp->brdtype) { - case BRD_EASYIO: - case BRD_EASYIOPCI: - retval = stl_initeio(brdp); - if (retval) - goto err; - break; - case BRD_ECH: - case BRD_ECHMC: - case BRD_ECHPCI: - case BRD_ECH64PCI: - retval = stl_initech(brdp); - if (retval) - goto err; - break; - default: - printk("STALLION: board=%d is unknown board type=%d\n", - brdp->brdnr, brdp->brdtype); - retval = -ENODEV; - goto err; - } - - if ((brdp->state & BRD_FOUND) == 0) { - printk("STALLION: %s board not found, board=%d io=%x irq=%d\n", - stl_brdnames[brdp->brdtype], brdp->brdnr, - brdp->ioaddr1, brdp->irq); - goto err_free; - } - - for (i = 0; i < STL_MAXPANELS; i++) - if (brdp->panels[i] != NULL) - stl_initports(brdp, brdp->panels[i]); - - printk("STALLION: %s found, board=%d io=%x irq=%d " - "nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], - brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, - brdp->nrports); - - return 0; -err_free: - free_irq(brdp->irq, brdp); - - stl_cleanup_panels(brdp); - - release_region(brdp->ioaddr1, brdp->iosize1); - if (brdp->iosize2 > 0) - release_region(brdp->ioaddr2, brdp->iosize2); -err: - return retval; -} - -/*****************************************************************************/ - -/* - * Find the next available board number that is free. - */ - -static int __devinit stl_getbrdnr(void) -{ - unsigned int i; - - for (i = 0; i < STL_MAXBRDS; i++) - if (stl_brds[i] == NULL) { - if (i >= stl_nrbrds) - stl_nrbrds = i + 1; - return i; - } - - return -1; -} - -/*****************************************************************************/ -/* - * We have a Stallion board. Allocate a board structure and - * initialize it. Read its IO and IRQ resources from PCI - * configuration space. - */ - -static int __devinit stl_pciprobe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct stlbrd *brdp; - unsigned int i, brdtype = ent->driver_data; - int brdnr, retval = -ENODEV; - - if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) - goto err; - - retval = pci_enable_device(pdev); - if (retval) - goto err; - brdp = stl_allocbrd(); - if (brdp == NULL) { - retval = -ENOMEM; - goto err; - } - mutex_lock(&stl_brdslock); - brdnr = stl_getbrdnr(); - if (brdnr < 0) { - dev_err(&pdev->dev, "too many boards found, " - "maximum supported %d\n", STL_MAXBRDS); - mutex_unlock(&stl_brdslock); - retval = -ENODEV; - goto err_fr; - } - brdp->brdnr = (unsigned int)brdnr; - stl_brds[brdp->brdnr] = brdp; - mutex_unlock(&stl_brdslock); - - brdp->brdtype = brdtype; - brdp->state |= STL_PROBED; - -/* - * We have all resources from the board, so let's setup the actual - * board structure now. - */ - switch (brdtype) { - case BRD_ECHPCI: - brdp->ioaddr2 = pci_resource_start(pdev, 0); - brdp->ioaddr1 = pci_resource_start(pdev, 1); - break; - case BRD_ECH64PCI: - brdp->ioaddr2 = pci_resource_start(pdev, 2); - brdp->ioaddr1 = pci_resource_start(pdev, 1); - break; - case BRD_EASYIOPCI: - brdp->ioaddr1 = pci_resource_start(pdev, 2); - brdp->ioaddr2 = pci_resource_start(pdev, 1); - break; - default: - dev_err(&pdev->dev, "unknown PCI board type=%u\n", brdtype); - break; - } - - brdp->irq = pdev->irq; - retval = stl_brdinit(brdp); - if (retval) - goto err_null; - - pci_set_drvdata(pdev, brdp); - - for (i = 0; i < brdp->nrports; i++) - tty_register_device(stl_serial, - brdp->brdnr * STL_MAXPORTS + i, &pdev->dev); - - return 0; -err_null: - stl_brds[brdp->brdnr] = NULL; -err_fr: - kfree(brdp); -err: - return retval; -} - -static void __devexit stl_pciremove(struct pci_dev *pdev) -{ - struct stlbrd *brdp = pci_get_drvdata(pdev); - unsigned int i; - - free_irq(brdp->irq, brdp); - - stl_cleanup_panels(brdp); - - release_region(brdp->ioaddr1, brdp->iosize1); - if (brdp->iosize2 > 0) - release_region(brdp->ioaddr2, brdp->iosize2); - - for (i = 0; i < brdp->nrports; i++) - tty_unregister_device(stl_serial, - brdp->brdnr * STL_MAXPORTS + i); - - stl_brds[brdp->brdnr] = NULL; - kfree(brdp); -} - -static struct pci_driver stl_pcidriver = { - .name = "stallion", - .id_table = stl_pcibrds, - .probe = stl_pciprobe, - .remove = __devexit_p(stl_pciremove) -}; - -/*****************************************************************************/ - -/* - * Return the board stats structure to user app. - */ - -static int stl_getbrdstats(combrd_t __user *bp) -{ - combrd_t stl_brdstats; - struct stlbrd *brdp; - struct stlpanel *panelp; - unsigned int i; - - if (copy_from_user(&stl_brdstats, bp, sizeof(combrd_t))) - return -EFAULT; - if (stl_brdstats.brd >= STL_MAXBRDS) - return -ENODEV; - brdp = stl_brds[stl_brdstats.brd]; - if (brdp == NULL) - return -ENODEV; - - memset(&stl_brdstats, 0, sizeof(combrd_t)); - stl_brdstats.brd = brdp->brdnr; - stl_brdstats.type = brdp->brdtype; - stl_brdstats.hwid = brdp->hwid; - stl_brdstats.state = brdp->state; - stl_brdstats.ioaddr = brdp->ioaddr1; - stl_brdstats.ioaddr2 = brdp->ioaddr2; - stl_brdstats.irq = brdp->irq; - stl_brdstats.nrpanels = brdp->nrpanels; - stl_brdstats.nrports = brdp->nrports; - for (i = 0; i < brdp->nrpanels; i++) { - panelp = brdp->panels[i]; - stl_brdstats.panels[i].panel = i; - stl_brdstats.panels[i].hwid = panelp->hwid; - stl_brdstats.panels[i].nrports = panelp->nrports; - } - - return copy_to_user(bp, &stl_brdstats, sizeof(combrd_t)) ? -EFAULT : 0; -} - -/*****************************************************************************/ - -/* - * Resolve the referenced port number into a port struct pointer. - */ - -static struct stlport *stl_getport(int brdnr, int panelnr, int portnr) -{ - struct stlbrd *brdp; - struct stlpanel *panelp; - - if (brdnr < 0 || brdnr >= STL_MAXBRDS) - return NULL; - brdp = stl_brds[brdnr]; - if (brdp == NULL) - return NULL; - if (panelnr < 0 || (unsigned int)panelnr >= brdp->nrpanels) - return NULL; - panelp = brdp->panels[panelnr]; - if (panelp == NULL) - return NULL; - if (portnr < 0 || (unsigned int)portnr >= panelp->nrports) - return NULL; - return panelp->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 stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp) -{ - comstats_t stl_comstats; - unsigned char *head, *tail; - unsigned long flags; - - if (!portp) { - if (copy_from_user(&stl_comstats, cp, sizeof(comstats_t))) - return -EFAULT; - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, - stl_comstats.port); - if (portp == NULL) - return -ENODEV; - } - - mutex_lock(&portp->port.mutex); - portp->stats.state = portp->istate; - portp->stats.flags = portp->port.flags; - portp->stats.hwid = portp->hwid; - - portp->stats.ttystate = 0; - portp->stats.cflags = 0; - portp->stats.iflags = 0; - portp->stats.oflags = 0; - portp->stats.lflags = 0; - portp->stats.rxbuffered = 0; - - spin_lock_irqsave(&stallion_lock, flags); - if (tty != NULL && portp->port.tty == tty) { - portp->stats.ttystate = tty->flags; - /* No longer available as a statistic */ - portp->stats.rxbuffered = 1; /*tty->flip.count; */ - if (tty->termios != NULL) { - portp->stats.cflags = tty->termios->c_cflag; - portp->stats.iflags = tty->termios->c_iflag; - portp->stats.oflags = tty->termios->c_oflag; - portp->stats.lflags = tty->termios->c_lflag; - } - } - spin_unlock_irqrestore(&stallion_lock, flags); - - head = portp->tx.head; - tail = portp->tx.tail; - portp->stats.txbuffered = (head >= tail) ? (head - tail) : - (STL_TXBUFSIZE - (tail - head)); - - portp->stats.signals = (unsigned long) stl_getsignals(portp); - mutex_unlock(&portp->port.mutex); - - return copy_to_user(cp, &portp->stats, - sizeof(comstats_t)) ? -EFAULT : 0; -} - -/*****************************************************************************/ - -/* - * Clear the port stats structure. We also return it zeroed out... - */ - -static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp) -{ - comstats_t stl_comstats; - - if (!portp) { - if (copy_from_user(&stl_comstats, cp, sizeof(comstats_t))) - return -EFAULT; - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, - stl_comstats.port); - if (portp == NULL) - return -ENODEV; - } - - mutex_lock(&portp->port.mutex); - memset(&portp->stats, 0, sizeof(comstats_t)); - portp->stats.brd = portp->brdnr; - portp->stats.panel = portp->panelnr; - portp->stats.port = portp->portnr; - mutex_unlock(&portp->port.mutex); - return copy_to_user(cp, &portp->stats, - sizeof(comstats_t)) ? -EFAULT : 0; -} - -/*****************************************************************************/ - -/* - * Return the entire driver ports structure to a user app. - */ - -static int stl_getportstruct(struct stlport __user *arg) -{ - struct stlport stl_dummyport; - struct stlport *portp; - - if (copy_from_user(&stl_dummyport, arg, sizeof(struct stlport))) - return -EFAULT; - portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr, - stl_dummyport.portnr); - if (!portp) - return -ENODEV; - return copy_to_user(arg, portp, sizeof(struct stlport)) ? -EFAULT : 0; -} - -/*****************************************************************************/ - -/* - * Return the entire driver board structure to a user app. - */ - -static int stl_getbrdstruct(struct stlbrd __user *arg) -{ - struct stlbrd stl_dummybrd; - struct stlbrd *brdp; - - if (copy_from_user(&stl_dummybrd, arg, sizeof(struct stlbrd))) - return -EFAULT; - if (stl_dummybrd.brdnr >= STL_MAXBRDS) - return -ENODEV; - brdp = stl_brds[stl_dummybrd.brdnr]; - if (!brdp) - return -ENODEV; - return copy_to_user(arg, brdp, sizeof(struct stlbrd)) ? -EFAULT : 0; -} - -/*****************************************************************************/ - -/* - * The "staliomem" device is also required to do some special operations - * on the board and/or ports. In this driver it is mostly used for stats - * collection. - */ - -static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg) -{ - int brdnr, rc; - void __user *argp = (void __user *)arg; - - pr_debug("stl_memioctl(fp=%p,cmd=%x,arg=%lx)\n", fp, cmd,arg); - - brdnr = iminor(fp->f_dentry->d_inode); - if (brdnr >= STL_MAXBRDS) - return -ENODEV; - rc = 0; - - switch (cmd) { - case COM_GETPORTSTATS: - rc = stl_getportstats(NULL, NULL, argp); - break; - case COM_CLRPORTSTATS: - rc = stl_clrportstats(NULL, argp); - break; - case COM_GETBRDSTATS: - rc = stl_getbrdstats(argp); - break; - case COM_READPORT: - rc = stl_getportstruct(argp); - break; - case COM_READBOARD: - rc = stl_getbrdstruct(argp); - break; - default: - rc = -ENOIOCTLCMD; - break; - } - return rc; -} - -static const struct tty_operations stl_ops = { - .open = stl_open, - .close = stl_close, - .write = stl_write, - .put_char = stl_putchar, - .flush_chars = stl_flushchars, - .write_room = stl_writeroom, - .chars_in_buffer = stl_charsinbuffer, - .ioctl = stl_ioctl, - .set_termios = stl_settermios, - .throttle = stl_throttle, - .unthrottle = stl_unthrottle, - .stop = stl_stop, - .start = stl_start, - .hangup = stl_hangup, - .flush_buffer = stl_flushbuffer, - .break_ctl = stl_breakctl, - .wait_until_sent = stl_waituntilsent, - .send_xchar = stl_sendxchar, - .tiocmget = stl_tiocmget, - .tiocmset = stl_tiocmset, - .proc_fops = &stl_proc_fops, -}; - -static const struct tty_port_operations stl_port_ops = { - .carrier_raised = stl_carrier_raised, - .dtr_rts = stl_dtr_rts, - .activate = stl_activate, - .shutdown = stl_shutdown, -}; - -/*****************************************************************************/ -/* CD1400 HARDWARE FUNCTIONS */ -/*****************************************************************************/ - -/* - * These functions get/set/update the registers of the cd1400 UARTs. - * Access to the cd1400 registers is via an address/data io port pair. - * (Maybe should make this inline...) - */ - -static int stl_cd1400getreg(struct stlport *portp, int regnr) -{ - outb((regnr + portp->uartaddr), portp->ioaddr); - return inb(portp->ioaddr + EREG_DATA); -} - -static void stl_cd1400setreg(struct stlport *portp, int regnr, int value) -{ - outb(regnr + portp->uartaddr, portp->ioaddr); - outb(value, portp->ioaddr + EREG_DATA); -} - -static int stl_cd1400updatereg(struct stlport *portp, int regnr, int value) -{ - outb(regnr + portp->uartaddr, portp->ioaddr); - if (inb(portp->ioaddr + EREG_DATA) != value) { - outb(value, portp->ioaddr + EREG_DATA); - return 1; - } - return 0; -} - -/*****************************************************************************/ - -/* - * Inbitialize the UARTs in a panel. We don't care what sort of board - * these ports are on - since the port io registers are almost - * identical when dealing with ports. - */ - -static int stl_cd1400panelinit(struct stlbrd *brdp, struct stlpanel *panelp) -{ - unsigned int gfrcr; - int chipmask, i, j; - int nrchips, uartaddr, ioaddr; - unsigned long flags; - - pr_debug("stl_panelinit(brdp=%p,panelp=%p)\n", brdp, panelp); - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(panelp->brdnr, panelp->pagenr); - -/* - * Check that each chip is present and started up OK. - */ - chipmask = 0; - nrchips = panelp->nrports / CD1400_PORTS; - for (i = 0; i < nrchips; i++) { - if (brdp->brdtype == BRD_ECHPCI) { - outb((panelp->pagenr + (i >> 1)), brdp->ioctrl); - ioaddr = panelp->iobase; - } else - ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1)); - uartaddr = (i & 0x01) ? 0x080 : 0; - outb((GFRCR + uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); - outb((CCR + uartaddr), ioaddr); - outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); - outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); - outb((GFRCR + uartaddr), ioaddr); - for (j = 0; j < CCR_MAXWAIT; j++) - if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0) - break; - - if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) { - printk("STALLION: cd1400 not responding, " - "brd=%d panel=%d chip=%d\n", - panelp->brdnr, panelp->panelnr, i); - continue; - } - chipmask |= (0x1 << i); - outb((PPR + uartaddr), ioaddr); - outb(PPR_SCALAR, (ioaddr + EREG_DATA)); - } - - BRDDISABLE(panelp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); - return chipmask; -} - -/*****************************************************************************/ - -/* - * Initialize hardware specific port registers. - */ - -static void stl_cd1400portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp) -{ - unsigned long flags; - pr_debug("stl_cd1400portinit(brdp=%p,panelp=%p,portp=%p)\n", brdp, - panelp, portp); - - if ((brdp == NULL) || (panelp == NULL) || - (portp == NULL)) - return; - - spin_lock_irqsave(&brd_lock, flags); - portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) || - (portp->portnr < 8)) ? 0 : EREG_BANKSIZE); - portp->uartaddr = (portp->portnr & 0x04) << 5; - portp->pagenr = panelp->pagenr + (portp->portnr >> 3); - - BRDENABLE(portp->brdnr, portp->pagenr); - stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - stl_cd1400setreg(portp, LIVR, (portp->portnr << 3)); - portp->hwid = stl_cd1400getreg(portp, GFRCR); - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Wait for the command register to be ready. We will poll this, - * since it won't usually take too long to be ready. - */ - -static void stl_cd1400ccrwait(struct stlport *portp) -{ - int i; - - for (i = 0; i < CCR_MAXWAIT; i++) - if (stl_cd1400getreg(portp, CCR) == 0) - return; - - printk("STALLION: cd1400 not responding, port=%d panel=%d brd=%d\n", - portp->portnr, portp->panelnr, portp->brdnr); -} - -/*****************************************************************************/ - -/* - * Set up the cd1400 registers for a port based on the termios port - * settings. - */ - -static void stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp) -{ - struct stlbrd *brdp; - unsigned long flags; - unsigned int clkdiv, baudrate; - unsigned char cor1, cor2, cor3; - unsigned char cor4, cor5, ccr; - unsigned char srer, sreron, sreroff; - unsigned char mcor1, mcor2, rtpr; - unsigned char clk, div; - - cor1 = 0; - cor2 = 0; - cor3 = 0; - cor4 = 0; - cor5 = 0; - ccr = 0; - rtpr = 0; - clk = 0; - div = 0; - mcor1 = 0; - mcor2 = 0; - sreron = 0; - sreroff = 0; - - brdp = stl_brds[portp->brdnr]; - if (brdp == NULL) - return; - -/* - * Set up the RX char ignore mask with those RX error types we - * can ignore. We can get the cd1400 to help us out a little here, - * it will ignore parity errors and breaks for us. - */ - portp->rxignoremsk = 0; - if (tiosp->c_iflag & IGNPAR) { - portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN); - cor1 |= COR1_PARIGNORE; - } - if (tiosp->c_iflag & IGNBRK) { - portp->rxignoremsk |= ST_BREAK; - cor4 |= COR4_IGNBRK; - } - - portp->rxmarkmsk = ST_OVERRUN; - if (tiosp->c_iflag & (INPCK | PARMRK)) - portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING); - if (tiosp->c_iflag & BRKINT) - portp->rxmarkmsk |= ST_BREAK; - -/* - * Go through the char size, parity and stop bits and set all the - * option register appropriately. - */ - switch (tiosp->c_cflag & CSIZE) { - case CS5: - cor1 |= COR1_CHL5; - break; - case CS6: - cor1 |= COR1_CHL6; - break; - case CS7: - cor1 |= COR1_CHL7; - break; - default: - cor1 |= COR1_CHL8; - break; - } - - if (tiosp->c_cflag & CSTOPB) - cor1 |= COR1_STOP2; - else - cor1 |= COR1_STOP1; - - if (tiosp->c_cflag & PARENB) { - if (tiosp->c_cflag & PARODD) - cor1 |= (COR1_PARENB | COR1_PARODD); - else - cor1 |= (COR1_PARENB | COR1_PAREVEN); - } else { - cor1 |= COR1_PARNONE; - } - -/* - * Set the RX FIFO threshold at 6 chars. This gives a bit of breathing - * space for hardware flow control and the like. This should be set to - * VMIN. Also here we will set the RX data timeout to 10ms - this should - * really be based on VTIME. - */ - cor3 |= FIFO_RXTHRESHOLD; - rtpr = 2; - -/* - * Calculate the baud rate timers. For now we will just assume that - * the input and output baud are the same. Could have used a baud - * table here, but this way we can generate virtually any baud rate - * we like! - */ - baudrate = tiosp->c_cflag & CBAUD; - if (baudrate & CBAUDEX) { - baudrate &= ~CBAUDEX; - if ((baudrate < 1) || (baudrate > 4)) - tiosp->c_cflag &= ~CBAUDEX; - else - baudrate += 15; - } - baudrate = stl_baudrates[baudrate]; - if ((tiosp->c_cflag & CBAUD) == B38400) { - if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - baudrate = 57600; - else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - baudrate = 115200; - else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - baudrate = 230400; - else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - baudrate = 460800; - else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) - baudrate = (portp->baud_base / portp->custom_divisor); - } - if (baudrate > STL_CD1400MAXBAUD) - baudrate = STL_CD1400MAXBAUD; - - if (baudrate > 0) { - for (clk = 0; clk < CD1400_NUMCLKS; clk++) { - clkdiv = (portp->clk / stl_cd1400clkdivs[clk]) / baudrate; - if (clkdiv < 0x100) - break; - } - div = (unsigned char) clkdiv; - } - -/* - * Check what form of modem signaling is required and set it up. - */ - if ((tiosp->c_cflag & CLOCAL) == 0) { - mcor1 |= MCOR1_DCD; - mcor2 |= MCOR2_DCD; - sreron |= SRER_MODEM; - portp->port.flags |= ASYNC_CHECK_CD; - } else - portp->port.flags &= ~ASYNC_CHECK_CD; - -/* - * Setup cd1400 enhanced modes if we can. In particular we want to - * handle as much of the flow control as possible automatically. As - * well as saving a few CPU cycles it will also greatly improve flow - * control reliability. - */ - if (tiosp->c_iflag & IXON) { - cor2 |= COR2_TXIBE; - cor3 |= COR3_SCD12; - if (tiosp->c_iflag & IXANY) - cor2 |= COR2_IXM; - } - - if (tiosp->c_cflag & CRTSCTS) { - cor2 |= COR2_CTSAE; - mcor1 |= FIFO_RTSTHRESHOLD; - } - -/* - * All cd1400 register values calculated so go through and set - * them all up. - */ - - pr_debug("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", - portp->portnr, portp->panelnr, portp->brdnr); - pr_debug(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", - cor1, cor2, cor3, cor4, cor5); - pr_debug(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", - mcor1, mcor2, rtpr, sreron, sreroff); - pr_debug(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div); - pr_debug(" schr1=%x schr2=%x schr3=%x schr4=%x\n", - tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], - tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_cd1400setreg(portp, CAR, (portp->portnr & 0x3)); - srer = stl_cd1400getreg(portp, SRER); - stl_cd1400setreg(portp, SRER, 0); - if (stl_cd1400updatereg(portp, COR1, cor1)) - ccr = 1; - if (stl_cd1400updatereg(portp, COR2, cor2)) - ccr = 1; - if (stl_cd1400updatereg(portp, COR3, cor3)) - ccr = 1; - if (ccr) { - stl_cd1400ccrwait(portp); - stl_cd1400setreg(portp, CCR, CCR_CORCHANGE); - } - stl_cd1400setreg(portp, COR4, cor4); - stl_cd1400setreg(portp, COR5, cor5); - stl_cd1400setreg(portp, MCOR1, mcor1); - stl_cd1400setreg(portp, MCOR2, mcor2); - if (baudrate > 0) { - stl_cd1400setreg(portp, TCOR, clk); - stl_cd1400setreg(portp, TBPR, div); - stl_cd1400setreg(portp, RCOR, clk); - stl_cd1400setreg(portp, RBPR, div); - } - stl_cd1400setreg(portp, SCHR1, tiosp->c_cc[VSTART]); - stl_cd1400setreg(portp, SCHR2, tiosp->c_cc[VSTOP]); - stl_cd1400setreg(portp, SCHR3, tiosp->c_cc[VSTART]); - stl_cd1400setreg(portp, SCHR4, tiosp->c_cc[VSTOP]); - stl_cd1400setreg(portp, RTPR, rtpr); - mcor1 = stl_cd1400getreg(portp, MSVR1); - if (mcor1 & MSVR1_DCD) - portp->sigs |= TIOCM_CD; - else - portp->sigs &= ~TIOCM_CD; - stl_cd1400setreg(portp, SRER, ((srer & ~sreroff) | sreron)); - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Set the state of the DTR and RTS signals. - */ - -static void stl_cd1400setsignals(struct stlport *portp, int dtr, int rts) -{ - unsigned char msvr1, msvr2; - unsigned long flags; - - pr_debug("stl_cd1400setsignals(portp=%p,dtr=%d,rts=%d)\n", - portp, dtr, rts); - - msvr1 = 0; - msvr2 = 0; - if (dtr > 0) - msvr1 = MSVR1_DTR; - if (rts > 0) - msvr2 = MSVR2_RTS; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - if (rts >= 0) - stl_cd1400setreg(portp, MSVR2, msvr2); - if (dtr >= 0) - stl_cd1400setreg(portp, MSVR1, msvr1); - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Return the state of the signals. - */ - -static int stl_cd1400getsignals(struct stlport *portp) -{ - unsigned char msvr1, msvr2; - unsigned long flags; - int sigs; - - pr_debug("stl_cd1400getsignals(portp=%p)\n", portp); - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - msvr1 = stl_cd1400getreg(portp, MSVR1); - msvr2 = stl_cd1400getreg(portp, MSVR2); - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); - - sigs = 0; - sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0; - sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0; - sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0; - sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0; -#if 0 - sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0; - sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0; -#else - sigs |= TIOCM_DSR; -#endif - return sigs; -} - -/*****************************************************************************/ - -/* - * Enable/Disable the Transmitter and/or Receiver. - */ - -static void stl_cd1400enablerxtx(struct stlport *portp, int rx, int tx) -{ - unsigned char ccr; - unsigned long flags; - - pr_debug("stl_cd1400enablerxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx); - - ccr = 0; - - if (tx == 0) - ccr |= CCR_TXDISABLE; - else if (tx > 0) - ccr |= CCR_TXENABLE; - if (rx == 0) - ccr |= CCR_RXDISABLE; - else if (rx > 0) - ccr |= CCR_RXENABLE; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - stl_cd1400ccrwait(portp); - stl_cd1400setreg(portp, CCR, ccr); - stl_cd1400ccrwait(portp); - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Start/stop the Transmitter and/or Receiver. - */ - -static void stl_cd1400startrxtx(struct stlport *portp, int rx, int tx) -{ - unsigned char sreron, sreroff; - unsigned long flags; - - pr_debug("stl_cd1400startrxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx); - - sreron = 0; - sreroff = 0; - if (tx == 0) - sreroff |= (SRER_TXDATA | SRER_TXEMPTY); - else if (tx == 1) - sreron |= SRER_TXDATA; - else if (tx >= 2) - sreron |= SRER_TXEMPTY; - if (rx == 0) - sreroff |= SRER_RXDATA; - else if (rx > 0) - sreron |= SRER_RXDATA; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - stl_cd1400setreg(portp, SRER, - ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron)); - BRDDISABLE(portp->brdnr); - if (tx > 0) - set_bit(ASYI_TXBUSY, &portp->istate); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Disable all interrupts from this port. - */ - -static void stl_cd1400disableintrs(struct stlport *portp) -{ - unsigned long flags; - - pr_debug("stl_cd1400disableintrs(portp=%p)\n", portp); - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - stl_cd1400setreg(portp, SRER, 0); - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -static void stl_cd1400sendbreak(struct stlport *portp, int len) -{ - unsigned long flags; - - pr_debug("stl_cd1400sendbreak(portp=%p,len=%d)\n", portp, len); - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - stl_cd1400setreg(portp, SRER, - ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) | - SRER_TXEMPTY)); - BRDDISABLE(portp->brdnr); - portp->brklen = len; - if (len == 1) - portp->stats.txbreaks++; - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Take flow control actions... - */ - -static void stl_cd1400flowctrl(struct stlport *portp, int state) -{ - struct tty_struct *tty; - unsigned long flags; - - pr_debug("stl_cd1400flowctrl(portp=%p,state=%x)\n", portp, state); - - if (portp == NULL) - return; - tty = tty_port_tty_get(&portp->port); - if (tty == NULL) - return; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - - if (state) { - if (tty->termios->c_iflag & IXOFF) { - stl_cd1400ccrwait(portp); - stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1); - portp->stats.rxxon++; - stl_cd1400ccrwait(portp); - } -/* - * Question: should we return RTS to what it was before? It may - * have been set by an ioctl... Suppose not, since if you have - * hardware flow control set then it is pretty silly to go and - * set the RTS line by hand. - */ - if (tty->termios->c_cflag & CRTSCTS) { - stl_cd1400setreg(portp, MCOR1, - (stl_cd1400getreg(portp, MCOR1) | - FIFO_RTSTHRESHOLD)); - stl_cd1400setreg(portp, MSVR2, MSVR2_RTS); - portp->stats.rxrtson++; - } - } else { - if (tty->termios->c_iflag & IXOFF) { - stl_cd1400ccrwait(portp); - stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2); - portp->stats.rxxoff++; - stl_cd1400ccrwait(portp); - } - if (tty->termios->c_cflag & CRTSCTS) { - stl_cd1400setreg(portp, MCOR1, - (stl_cd1400getreg(portp, MCOR1) & 0xf0)); - stl_cd1400setreg(portp, MSVR2, 0); - portp->stats.rxrtsoff++; - } - } - - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); - tty_kref_put(tty); -} - -/*****************************************************************************/ - -/* - * Send a flow control character... - */ - -static void stl_cd1400sendflow(struct stlport *portp, int state) -{ - struct tty_struct *tty; - unsigned long flags; - - pr_debug("stl_cd1400sendflow(portp=%p,state=%x)\n", portp, state); - - if (portp == NULL) - return; - tty = tty_port_tty_get(&portp->port); - if (tty == NULL) - return; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - if (state) { - stl_cd1400ccrwait(portp); - stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1); - portp->stats.rxxon++; - stl_cd1400ccrwait(portp); - } else { - stl_cd1400ccrwait(portp); - stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2); - portp->stats.rxxoff++; - stl_cd1400ccrwait(portp); - } - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); - tty_kref_put(tty); -} - -/*****************************************************************************/ - -static void stl_cd1400flush(struct stlport *portp) -{ - unsigned long flags; - - pr_debug("stl_cd1400flush(portp=%p)\n", portp); - - if (portp == NULL) - return; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - stl_cd1400ccrwait(portp); - stl_cd1400setreg(portp, CCR, CCR_TXFLUSHFIFO); - stl_cd1400ccrwait(portp); - portp->tx.tail = portp->tx.head; - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Return the current state of data flow on this port. This is only - * really interesting when determining if data has fully completed - * transmission or not... This is easy for the cd1400, it accurately - * maintains the busy port flag. - */ - -static int stl_cd1400datastate(struct stlport *portp) -{ - pr_debug("stl_cd1400datastate(portp=%p)\n", portp); - - if (portp == NULL) - return 0; - - return test_bit(ASYI_TXBUSY, &portp->istate) ? 1 : 0; -} - -/*****************************************************************************/ - -/* - * Interrupt service routine for cd1400 EasyIO boards. - */ - -static void stl_cd1400eiointr(struct stlpanel *panelp, unsigned int iobase) -{ - unsigned char svrtype; - - pr_debug("stl_cd1400eiointr(panelp=%p,iobase=%x)\n", panelp, iobase); - - spin_lock(&brd_lock); - outb(SVRR, iobase); - svrtype = inb(iobase + EREG_DATA); - if (panelp->nrports > 4) { - outb((SVRR + 0x80), iobase); - svrtype |= inb(iobase + EREG_DATA); - } - - if (svrtype & SVRR_RX) - stl_cd1400rxisr(panelp, iobase); - else if (svrtype & SVRR_TX) - stl_cd1400txisr(panelp, iobase); - else if (svrtype & SVRR_MDM) - stl_cd1400mdmisr(panelp, iobase); - - spin_unlock(&brd_lock); -} - -/*****************************************************************************/ - -/* - * Interrupt service routine for cd1400 panels. - */ - -static void stl_cd1400echintr(struct stlpanel *panelp, unsigned int iobase) -{ - unsigned char svrtype; - - pr_debug("stl_cd1400echintr(panelp=%p,iobase=%x)\n", panelp, iobase); - - outb(SVRR, iobase); - svrtype = inb(iobase + EREG_DATA); - outb((SVRR + 0x80), iobase); - svrtype |= inb(iobase + EREG_DATA); - if (svrtype & SVRR_RX) - stl_cd1400rxisr(panelp, iobase); - else if (svrtype & SVRR_TX) - stl_cd1400txisr(panelp, iobase); - else if (svrtype & SVRR_MDM) - stl_cd1400mdmisr(panelp, iobase); -} - - -/*****************************************************************************/ - -/* - * Unfortunately we need to handle breaks in the TX data stream, since - * this is the only way to generate them on the cd1400. - */ - -static int stl_cd1400breakisr(struct stlport *portp, int ioaddr) -{ - if (portp->brklen == 1) { - outb((COR2 + portp->uartaddr), ioaddr); - outb((inb(ioaddr + EREG_DATA) | COR2_ETC), - (ioaddr + EREG_DATA)); - outb((TDR + portp->uartaddr), ioaddr); - outb(ETC_CMD, (ioaddr + EREG_DATA)); - outb(ETC_STARTBREAK, (ioaddr + EREG_DATA)); - outb((SRER + portp->uartaddr), ioaddr); - outb((inb(ioaddr + EREG_DATA) & ~(SRER_TXDATA | SRER_TXEMPTY)), - (ioaddr + EREG_DATA)); - return 1; - } else if (portp->brklen > 1) { - outb((TDR + portp->uartaddr), ioaddr); - outb(ETC_CMD, (ioaddr + EREG_DATA)); - outb(ETC_STOPBREAK, (ioaddr + EREG_DATA)); - portp->brklen = -1; - return 1; - } else { - outb((COR2 + portp->uartaddr), ioaddr); - outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), - (ioaddr + EREG_DATA)); - portp->brklen = 0; - } - return 0; -} - -/*****************************************************************************/ - -/* - * Transmit interrupt handler. This has gotta be fast! Handling TX - * chars is pretty simple, stuff as many as possible from the TX buffer - * into the cd1400 FIFO. Must also handle TX breaks here, since they - * are embedded as commands in the data stream. Oh no, had to use a goto! - * This could be optimized more, will do when I get time... - * In practice it is possible that interrupts are enabled but that the - * port has been hung up. Need to handle not having any TX buffer here, - * this is done by using the side effect that head and tail will also - * be NULL if the buffer has been freed. - */ - -static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr) -{ - struct stlport *portp; - int len, stlen; - char *head, *tail; - unsigned char ioack, srer; - struct tty_struct *tty; - - pr_debug("stl_cd1400txisr(panelp=%p,ioaddr=%x)\n", panelp, ioaddr); - - ioack = inb(ioaddr + EREG_TXACK); - if (((ioack & panelp->ackmask) != 0) || - ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { - printk("STALLION: bad TX interrupt ack value=%x\n", ioack); - return; - } - portp = panelp->ports[(ioack >> 3)]; - -/* - * Unfortunately we need to handle breaks in the data stream, since - * this is the only way to generate them on the cd1400. Do it now if - * a break is to be sent. - */ - if (portp->brklen != 0) - if (stl_cd1400breakisr(portp, ioaddr)) - goto stl_txalldone; - - head = portp->tx.head; - tail = portp->tx.tail; - len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); - if ((len == 0) || ((len < STL_TXBUFLOW) && - (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { - set_bit(ASYI_TXLOW, &portp->istate); - tty = tty_port_tty_get(&portp->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } - } - - if (len == 0) { - outb((SRER + portp->uartaddr), ioaddr); - srer = inb(ioaddr + EREG_DATA); - if (srer & SRER_TXDATA) { - srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY; - } else { - srer &= ~(SRER_TXDATA | SRER_TXEMPTY); - clear_bit(ASYI_TXBUSY, &portp->istate); - } - outb(srer, (ioaddr + EREG_DATA)); - } else { - len = min(len, CD1400_TXFIFOSIZE); - portp->stats.txtotal += len; - stlen = min_t(unsigned int, len, - (portp->tx.buf + STL_TXBUFSIZE) - tail); - outb((TDR + portp->uartaddr), ioaddr); - outsb((ioaddr + EREG_DATA), tail, stlen); - len -= stlen; - tail += stlen; - if (tail >= (portp->tx.buf + STL_TXBUFSIZE)) - tail = portp->tx.buf; - if (len > 0) { - outsb((ioaddr + EREG_DATA), tail, len); - tail += len; - } - portp->tx.tail = tail; - } - -stl_txalldone: - outb((EOSRR + portp->uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); -} - -/*****************************************************************************/ - -/* - * Receive character interrupt handler. Determine if we have good chars - * or bad chars and then process appropriately. Good chars are easy - * just shove the lot into the RX buffer and set all status byte to 0. - * If a bad RX char then process as required. This routine needs to be - * fast! In practice it is possible that we get an interrupt on a port - * that is closed. This can happen on hangups - since they completely - * shutdown a port not in user context. Need to handle this case. - */ - -static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr) -{ - struct stlport *portp; - struct tty_struct *tty; - unsigned int ioack, len, buflen; - unsigned char status; - char ch; - - pr_debug("stl_cd1400rxisr(panelp=%p,ioaddr=%x)\n", panelp, ioaddr); - - ioack = inb(ioaddr + EREG_RXACK); - if ((ioack & panelp->ackmask) != 0) { - printk("STALLION: bad RX interrupt ack value=%x\n", ioack); - return; - } - portp = panelp->ports[(ioack >> 3)]; - tty = tty_port_tty_get(&portp->port); - - if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { - outb((RDCR + portp->uartaddr), ioaddr); - len = inb(ioaddr + EREG_DATA); - if (tty == NULL || (buflen = tty_buffer_request_room(tty, len)) == 0) { - len = min_t(unsigned int, len, sizeof(stl_unwanted)); - outb((RDSR + portp->uartaddr), ioaddr); - insb((ioaddr + EREG_DATA), &stl_unwanted[0], len); - portp->stats.rxlost += len; - portp->stats.rxtotal += len; - } else { - len = min(len, buflen); - if (len > 0) { - unsigned char *ptr; - outb((RDSR + portp->uartaddr), ioaddr); - tty_prepare_flip_string(tty, &ptr, len); - insb((ioaddr + EREG_DATA), ptr, len); - tty_schedule_flip(tty); - portp->stats.rxtotal += len; - } - } - } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) { - outb((RDSR + portp->uartaddr), ioaddr); - status = inb(ioaddr + EREG_DATA); - ch = inb(ioaddr + EREG_DATA); - if (status & ST_PARITY) - portp->stats.rxparity++; - if (status & ST_FRAMING) - portp->stats.rxframing++; - if (status & ST_OVERRUN) - portp->stats.rxoverrun++; - if (status & ST_BREAK) - portp->stats.rxbreaks++; - if (status & ST_SCHARMASK) { - if ((status & ST_SCHARMASK) == ST_SCHAR1) - portp->stats.txxon++; - if ((status & ST_SCHARMASK) == ST_SCHAR2) - portp->stats.txxoff++; - goto stl_rxalldone; - } - if (tty != NULL && (portp->rxignoremsk & status) == 0) { - if (portp->rxmarkmsk & status) { - if (status & ST_BREAK) { - status = TTY_BREAK; - if (portp->port.flags & ASYNC_SAK) { - do_SAK(tty); - BRDENABLE(portp->brdnr, portp->pagenr); - } - } else if (status & ST_PARITY) - status = TTY_PARITY; - else if (status & ST_FRAMING) - status = TTY_FRAME; - else if(status & ST_OVERRUN) - status = TTY_OVERRUN; - else - status = 0; - } else - status = 0; - tty_insert_flip_char(tty, ch, status); - tty_schedule_flip(tty); - } - } else { - printk("STALLION: bad RX interrupt ack value=%x\n", ioack); - tty_kref_put(tty); - return; - } - -stl_rxalldone: - tty_kref_put(tty); - outb((EOSRR + portp->uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); -} - -/*****************************************************************************/ - -/* - * Modem interrupt handler. The is called when the modem signal line - * (DCD) has changed state. Leave most of the work to the off-level - * processing routine. - */ - -static void stl_cd1400mdmisr(struct stlpanel *panelp, int ioaddr) -{ - struct stlport *portp; - unsigned int ioack; - unsigned char misr; - - pr_debug("stl_cd1400mdmisr(panelp=%p)\n", panelp); - - ioack = inb(ioaddr + EREG_MDACK); - if (((ioack & panelp->ackmask) != 0) || - ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { - printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack); - return; - } - portp = panelp->ports[(ioack >> 3)]; - - outb((MISR + portp->uartaddr), ioaddr); - misr = inb(ioaddr + EREG_DATA); - if (misr & MISR_DCD) { - stl_cd_change(portp); - portp->stats.modem++; - } - - outb((EOSRR + portp->uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); -} - -/*****************************************************************************/ -/* SC26198 HARDWARE FUNCTIONS */ -/*****************************************************************************/ - -/* - * These functions get/set/update the registers of the sc26198 UARTs. - * Access to the sc26198 registers is via an address/data io port pair. - * (Maybe should make this inline...) - */ - -static int stl_sc26198getreg(struct stlport *portp, int regnr) -{ - outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); - return inb(portp->ioaddr + XP_DATA); -} - -static void stl_sc26198setreg(struct stlport *portp, int regnr, int value) -{ - outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); - outb(value, (portp->ioaddr + XP_DATA)); -} - -static int stl_sc26198updatereg(struct stlport *portp, int regnr, int value) -{ - outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); - if (inb(portp->ioaddr + XP_DATA) != value) { - outb(value, (portp->ioaddr + XP_DATA)); - return 1; - } - return 0; -} - -/*****************************************************************************/ - -/* - * Functions to get and set the sc26198 global registers. - */ - -static int stl_sc26198getglobreg(struct stlport *portp, int regnr) -{ - outb(regnr, (portp->ioaddr + XP_ADDR)); - return inb(portp->ioaddr + XP_DATA); -} - -#if 0 -static void stl_sc26198setglobreg(struct stlport *portp, int regnr, int value) -{ - outb(regnr, (portp->ioaddr + XP_ADDR)); - outb(value, (portp->ioaddr + XP_DATA)); -} -#endif - -/*****************************************************************************/ - -/* - * Inbitialize the UARTs in a panel. We don't care what sort of board - * these ports are on - since the port io registers are almost - * identical when dealing with ports. - */ - -static int stl_sc26198panelinit(struct stlbrd *brdp, struct stlpanel *panelp) -{ - int chipmask, i; - int nrchips, ioaddr; - - pr_debug("stl_sc26198panelinit(brdp=%p,panelp=%p)\n", brdp, panelp); - - BRDENABLE(panelp->brdnr, panelp->pagenr); - -/* - * Check that each chip is present and started up OK. - */ - chipmask = 0; - nrchips = (panelp->nrports + 4) / SC26198_PORTS; - if (brdp->brdtype == BRD_ECHPCI) - outb(panelp->pagenr, brdp->ioctrl); - - for (i = 0; i < nrchips; i++) { - ioaddr = panelp->iobase + (i * 4); - outb(SCCR, (ioaddr + XP_ADDR)); - outb(CR_RESETALL, (ioaddr + XP_DATA)); - outb(TSTR, (ioaddr + XP_ADDR)); - if (inb(ioaddr + XP_DATA) != 0) { - printk("STALLION: sc26198 not responding, " - "brd=%d panel=%d chip=%d\n", - panelp->brdnr, panelp->panelnr, i); - continue; - } - chipmask |= (0x1 << i); - outb(GCCR, (ioaddr + XP_ADDR)); - outb(GCCR_IVRTYPCHANACK, (ioaddr + XP_DATA)); - outb(WDTRCR, (ioaddr + XP_ADDR)); - outb(0xff, (ioaddr + XP_DATA)); - } - - BRDDISABLE(panelp->brdnr); - return chipmask; -} - -/*****************************************************************************/ - -/* - * Initialize hardware specific port registers. - */ - -static void stl_sc26198portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp) -{ - pr_debug("stl_sc26198portinit(brdp=%p,panelp=%p,portp=%p)\n", brdp, - panelp, portp); - - if ((brdp == NULL) || (panelp == NULL) || - (portp == NULL)) - return; - - portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4); - portp->uartaddr = (portp->portnr & 0x07) << 4; - portp->pagenr = panelp->pagenr; - portp->hwid = 0x1; - - BRDENABLE(portp->brdnr, portp->pagenr); - stl_sc26198setreg(portp, IOPCR, IOPCR_SETSIGS); - BRDDISABLE(portp->brdnr); -} - -/*****************************************************************************/ - -/* - * Set up the sc26198 registers for a port based on the termios port - * settings. - */ - -static void stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp) -{ - struct stlbrd *brdp; - unsigned long flags; - unsigned int baudrate; - unsigned char mr0, mr1, mr2, clk; - unsigned char imron, imroff, iopr, ipr; - - mr0 = 0; - mr1 = 0; - mr2 = 0; - clk = 0; - iopr = 0; - imron = 0; - imroff = 0; - - brdp = stl_brds[portp->brdnr]; - if (brdp == NULL) - return; - -/* - * Set up the RX char ignore mask with those RX error types we - * can ignore. - */ - portp->rxignoremsk = 0; - if (tiosp->c_iflag & IGNPAR) - portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING | - SR_RXOVERRUN); - if (tiosp->c_iflag & IGNBRK) - portp->rxignoremsk |= SR_RXBREAK; - - portp->rxmarkmsk = SR_RXOVERRUN; - if (tiosp->c_iflag & (INPCK | PARMRK)) - portp->rxmarkmsk |= (SR_RXPARITY | SR_RXFRAMING); - if (tiosp->c_iflag & BRKINT) - portp->rxmarkmsk |= SR_RXBREAK; - -/* - * Go through the char size, parity and stop bits and set all the - * option register appropriately. - */ - switch (tiosp->c_cflag & CSIZE) { - case CS5: - mr1 |= MR1_CS5; - break; - case CS6: - mr1 |= MR1_CS6; - break; - case CS7: - mr1 |= MR1_CS7; - break; - default: - mr1 |= MR1_CS8; - break; - } - - if (tiosp->c_cflag & CSTOPB) - mr2 |= MR2_STOP2; - else - mr2 |= MR2_STOP1; - - if (tiosp->c_cflag & PARENB) { - if (tiosp->c_cflag & PARODD) - mr1 |= (MR1_PARENB | MR1_PARODD); - else - mr1 |= (MR1_PARENB | MR1_PAREVEN); - } else - mr1 |= MR1_PARNONE; - - mr1 |= MR1_ERRBLOCK; - -/* - * Set the RX FIFO threshold at 8 chars. This gives a bit of breathing - * space for hardware flow control and the like. This should be set to - * VMIN. - */ - mr2 |= MR2_RXFIFOHALF; - -/* - * Calculate the baud rate timers. For now we will just assume that - * the input and output baud are the same. The sc26198 has a fixed - * baud rate table, so only discrete baud rates possible. - */ - baudrate = tiosp->c_cflag & CBAUD; - if (baudrate & CBAUDEX) { - baudrate &= ~CBAUDEX; - if ((baudrate < 1) || (baudrate > 4)) - tiosp->c_cflag &= ~CBAUDEX; - else - baudrate += 15; - } - baudrate = stl_baudrates[baudrate]; - if ((tiosp->c_cflag & CBAUD) == B38400) { - if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - baudrate = 57600; - else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - baudrate = 115200; - else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - baudrate = 230400; - else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - baudrate = 460800; - else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) - baudrate = (portp->baud_base / portp->custom_divisor); - } - if (baudrate > STL_SC26198MAXBAUD) - baudrate = STL_SC26198MAXBAUD; - - if (baudrate > 0) - for (clk = 0; clk < SC26198_NRBAUDS; clk++) - if (baudrate <= sc26198_baudtable[clk]) - break; - -/* - * Check what form of modem signaling is required and set it up. - */ - if (tiosp->c_cflag & CLOCAL) { - portp->port.flags &= ~ASYNC_CHECK_CD; - } else { - iopr |= IOPR_DCDCOS; - imron |= IR_IOPORT; - portp->port.flags |= ASYNC_CHECK_CD; - } - -/* - * Setup sc26198 enhanced modes if we can. In particular we want to - * handle as much of the flow control as possible automatically. As - * well as saving a few CPU cycles it will also greatly improve flow - * control reliability. - */ - if (tiosp->c_iflag & IXON) { - mr0 |= MR0_SWFTX | MR0_SWFT; - imron |= IR_XONXOFF; - } else - imroff |= IR_XONXOFF; - - if (tiosp->c_iflag & IXOFF) - mr0 |= MR0_SWFRX; - - if (tiosp->c_cflag & CRTSCTS) { - mr2 |= MR2_AUTOCTS; - mr1 |= MR1_AUTORTS; - } - -/* - * All sc26198 register values calculated so go through and set - * them all up. - */ - - pr_debug("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", - portp->portnr, portp->panelnr, portp->brdnr); - pr_debug(" mr0=%x mr1=%x mr2=%x clk=%x\n", mr0, mr1, mr2, clk); - pr_debug(" iopr=%x imron=%x imroff=%x\n", iopr, imron, imroff); - pr_debug(" schr1=%x schr2=%x schr3=%x schr4=%x\n", - tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], - tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_sc26198setreg(portp, IMR, 0); - stl_sc26198updatereg(portp, MR0, mr0); - stl_sc26198updatereg(portp, MR1, mr1); - stl_sc26198setreg(portp, SCCR, CR_RXERRBLOCK); - stl_sc26198updatereg(portp, MR2, mr2); - stl_sc26198updatereg(portp, IOPIOR, - ((stl_sc26198getreg(portp, IOPIOR) & ~IPR_CHANGEMASK) | iopr)); - - if (baudrate > 0) { - stl_sc26198setreg(portp, TXCSR, clk); - stl_sc26198setreg(portp, RXCSR, clk); - } - - stl_sc26198setreg(portp, XONCR, tiosp->c_cc[VSTART]); - stl_sc26198setreg(portp, XOFFCR, tiosp->c_cc[VSTOP]); - - ipr = stl_sc26198getreg(portp, IPR); - if (ipr & IPR_DCD) - portp->sigs &= ~TIOCM_CD; - else - portp->sigs |= TIOCM_CD; - - portp->imr = (portp->imr & ~imroff) | imron; - stl_sc26198setreg(portp, IMR, portp->imr); - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Set the state of the DTR and RTS signals. - */ - -static void stl_sc26198setsignals(struct stlport *portp, int dtr, int rts) -{ - unsigned char iopioron, iopioroff; - unsigned long flags; - - pr_debug("stl_sc26198setsignals(portp=%p,dtr=%d,rts=%d)\n", portp, - dtr, rts); - - iopioron = 0; - iopioroff = 0; - if (dtr == 0) - iopioroff |= IPR_DTR; - else if (dtr > 0) - iopioron |= IPR_DTR; - if (rts == 0) - iopioroff |= IPR_RTS; - else if (rts > 0) - iopioron |= IPR_RTS; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_sc26198setreg(portp, IOPIOR, - ((stl_sc26198getreg(portp, IOPIOR) & ~iopioroff) | iopioron)); - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Return the state of the signals. - */ - -static int stl_sc26198getsignals(struct stlport *portp) -{ - unsigned char ipr; - unsigned long flags; - int sigs; - - pr_debug("stl_sc26198getsignals(portp=%p)\n", portp); - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - ipr = stl_sc26198getreg(portp, IPR); - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); - - sigs = 0; - sigs |= (ipr & IPR_DCD) ? 0 : TIOCM_CD; - sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS; - sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR; - sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS; - sigs |= TIOCM_DSR; - return sigs; -} - -/*****************************************************************************/ - -/* - * Enable/Disable the Transmitter and/or Receiver. - */ - -static void stl_sc26198enablerxtx(struct stlport *portp, int rx, int tx) -{ - unsigned char ccr; - unsigned long flags; - - pr_debug("stl_sc26198enablerxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx,tx); - - ccr = portp->crenable; - if (tx == 0) - ccr &= ~CR_TXENABLE; - else if (tx > 0) - ccr |= CR_TXENABLE; - if (rx == 0) - ccr &= ~CR_RXENABLE; - else if (rx > 0) - ccr |= CR_RXENABLE; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_sc26198setreg(portp, SCCR, ccr); - BRDDISABLE(portp->brdnr); - portp->crenable = ccr; - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Start/stop the Transmitter and/or Receiver. - */ - -static void stl_sc26198startrxtx(struct stlport *portp, int rx, int tx) -{ - unsigned char imr; - unsigned long flags; - - pr_debug("stl_sc26198startrxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx); - - imr = portp->imr; - if (tx == 0) - imr &= ~IR_TXRDY; - else if (tx == 1) - imr |= IR_TXRDY; - if (rx == 0) - imr &= ~(IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG); - else if (rx > 0) - imr |= IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_sc26198setreg(portp, IMR, imr); - BRDDISABLE(portp->brdnr); - portp->imr = imr; - if (tx > 0) - set_bit(ASYI_TXBUSY, &portp->istate); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Disable all interrupts from this port. - */ - -static void stl_sc26198disableintrs(struct stlport *portp) -{ - unsigned long flags; - - pr_debug("stl_sc26198disableintrs(portp=%p)\n", portp); - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - portp->imr = 0; - stl_sc26198setreg(portp, IMR, 0); - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -static void stl_sc26198sendbreak(struct stlport *portp, int len) -{ - unsigned long flags; - - pr_debug("stl_sc26198sendbreak(portp=%p,len=%d)\n", portp, len); - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - if (len == 1) { - stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK); - portp->stats.txbreaks++; - } else - stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK); - - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Take flow control actions... - */ - -static void stl_sc26198flowctrl(struct stlport *portp, int state) -{ - struct tty_struct *tty; - unsigned long flags; - unsigned char mr0; - - pr_debug("stl_sc26198flowctrl(portp=%p,state=%x)\n", portp, state); - - if (portp == NULL) - return; - tty = tty_port_tty_get(&portp->port); - if (tty == NULL) - return; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - - if (state) { - if (tty->termios->c_iflag & IXOFF) { - mr0 = stl_sc26198getreg(portp, MR0); - stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); - stl_sc26198setreg(portp, SCCR, CR_TXSENDXON); - mr0 |= MR0_SWFRX; - portp->stats.rxxon++; - stl_sc26198wait(portp); - stl_sc26198setreg(portp, MR0, mr0); - } -/* - * Question: should we return RTS to what it was before? It may - * have been set by an ioctl... Suppose not, since if you have - * hardware flow control set then it is pretty silly to go and - * set the RTS line by hand. - */ - if (tty->termios->c_cflag & CRTSCTS) { - stl_sc26198setreg(portp, MR1, - (stl_sc26198getreg(portp, MR1) | MR1_AUTORTS)); - stl_sc26198setreg(portp, IOPIOR, - (stl_sc26198getreg(portp, IOPIOR) | IOPR_RTS)); - portp->stats.rxrtson++; - } - } else { - if (tty->termios->c_iflag & IXOFF) { - mr0 = stl_sc26198getreg(portp, MR0); - stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); - stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF); - mr0 &= ~MR0_SWFRX; - portp->stats.rxxoff++; - stl_sc26198wait(portp); - stl_sc26198setreg(portp, MR0, mr0); - } - if (tty->termios->c_cflag & CRTSCTS) { - stl_sc26198setreg(portp, MR1, - (stl_sc26198getreg(portp, MR1) & ~MR1_AUTORTS)); - stl_sc26198setreg(portp, IOPIOR, - (stl_sc26198getreg(portp, IOPIOR) & ~IOPR_RTS)); - portp->stats.rxrtsoff++; - } - } - - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); - tty_kref_put(tty); -} - -/*****************************************************************************/ - -/* - * Send a flow control character. - */ - -static void stl_sc26198sendflow(struct stlport *portp, int state) -{ - struct tty_struct *tty; - unsigned long flags; - unsigned char mr0; - - pr_debug("stl_sc26198sendflow(portp=%p,state=%x)\n", portp, state); - - if (portp == NULL) - return; - tty = tty_port_tty_get(&portp->port); - if (tty == NULL) - return; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - if (state) { - mr0 = stl_sc26198getreg(portp, MR0); - stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); - stl_sc26198setreg(portp, SCCR, CR_TXSENDXON); - mr0 |= MR0_SWFRX; - portp->stats.rxxon++; - stl_sc26198wait(portp); - stl_sc26198setreg(portp, MR0, mr0); - } else { - mr0 = stl_sc26198getreg(portp, MR0); - stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); - stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF); - mr0 &= ~MR0_SWFRX; - portp->stats.rxxoff++; - stl_sc26198wait(portp); - stl_sc26198setreg(portp, MR0, mr0); - } - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); - tty_kref_put(tty); -} - -/*****************************************************************************/ - -static void stl_sc26198flush(struct stlport *portp) -{ - unsigned long flags; - - pr_debug("stl_sc26198flush(portp=%p)\n", portp); - - if (portp == NULL) - return; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_sc26198setreg(portp, SCCR, CR_TXRESET); - stl_sc26198setreg(portp, SCCR, portp->crenable); - BRDDISABLE(portp->brdnr); - portp->tx.tail = portp->tx.head; - spin_unlock_irqrestore(&brd_lock, flags); -} - -/*****************************************************************************/ - -/* - * Return the current state of data flow on this port. This is only - * really interesting when determining if data has fully completed - * transmission or not... The sc26198 interrupt scheme cannot - * determine when all data has actually drained, so we need to - * check the port statusy register to be sure. - */ - -static int stl_sc26198datastate(struct stlport *portp) -{ - unsigned long flags; - unsigned char sr; - - pr_debug("stl_sc26198datastate(portp=%p)\n", portp); - - if (portp == NULL) - return 0; - if (test_bit(ASYI_TXBUSY, &portp->istate)) - return 1; - - spin_lock_irqsave(&brd_lock, flags); - BRDENABLE(portp->brdnr, portp->pagenr); - sr = stl_sc26198getreg(portp, SR); - BRDDISABLE(portp->brdnr); - spin_unlock_irqrestore(&brd_lock, flags); - - return (sr & SR_TXEMPTY) ? 0 : 1; -} - -/*****************************************************************************/ - -/* - * Delay for a small amount of time, to give the sc26198 a chance - * to process a command... - */ - -static void stl_sc26198wait(struct stlport *portp) -{ - int i; - - pr_debug("stl_sc26198wait(portp=%p)\n", portp); - - if (portp == NULL) - return; - - for (i = 0; i < 20; i++) - stl_sc26198getglobreg(portp, TSTR); -} - -/*****************************************************************************/ - -/* - * If we are TX flow controlled and in IXANY mode then we may - * need to unflow control here. We gotta do this because of the - * automatic flow control modes of the sc26198. - */ - -static void stl_sc26198txunflow(struct stlport *portp, struct tty_struct *tty) -{ - unsigned char mr0; - - mr0 = stl_sc26198getreg(portp, MR0); - stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); - stl_sc26198setreg(portp, SCCR, CR_HOSTXON); - stl_sc26198wait(portp); - stl_sc26198setreg(portp, MR0, mr0); - clear_bit(ASYI_TXFLOWED, &portp->istate); -} - -/*****************************************************************************/ - -/* - * Interrupt service routine for sc26198 panels. - */ - -static void stl_sc26198intr(struct stlpanel *panelp, unsigned int iobase) -{ - struct stlport *portp; - unsigned int iack; - - spin_lock(&brd_lock); - -/* - * Work around bug in sc26198 chip... Cannot have A6 address - * line of UART high, else iack will be returned as 0. - */ - outb(0, (iobase + 1)); - - iack = inb(iobase + XP_IACK); - portp = panelp->ports[(iack & IVR_CHANMASK) + ((iobase & 0x4) << 1)]; - - if (iack & IVR_RXDATA) - stl_sc26198rxisr(portp, iack); - else if (iack & IVR_TXDATA) - stl_sc26198txisr(portp); - else - stl_sc26198otherisr(portp, iack); - - spin_unlock(&brd_lock); -} - -/*****************************************************************************/ - -/* - * Transmit interrupt handler. This has gotta be fast! Handling TX - * chars is pretty simple, stuff as many as possible from the TX buffer - * into the sc26198 FIFO. - * In practice it is possible that interrupts are enabled but that the - * port has been hung up. Need to handle not having any TX buffer here, - * this is done by using the side effect that head and tail will also - * be NULL if the buffer has been freed. - */ - -static void stl_sc26198txisr(struct stlport *portp) -{ - struct tty_struct *tty; - unsigned int ioaddr; - unsigned char mr0; - int len, stlen; - char *head, *tail; - - pr_debug("stl_sc26198txisr(portp=%p)\n", portp); - - ioaddr = portp->ioaddr; - head = portp->tx.head; - tail = portp->tx.tail; - len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); - if ((len == 0) || ((len < STL_TXBUFLOW) && - (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { - set_bit(ASYI_TXLOW, &portp->istate); - tty = tty_port_tty_get(&portp->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } - } - - if (len == 0) { - outb((MR0 | portp->uartaddr), (ioaddr + XP_ADDR)); - mr0 = inb(ioaddr + XP_DATA); - if ((mr0 & MR0_TXMASK) == MR0_TXEMPTY) { - portp->imr &= ~IR_TXRDY; - outb((IMR | portp->uartaddr), (ioaddr + XP_ADDR)); - outb(portp->imr, (ioaddr + XP_DATA)); - clear_bit(ASYI_TXBUSY, &portp->istate); - } else { - mr0 |= ((mr0 & ~MR0_TXMASK) | MR0_TXEMPTY); - outb(mr0, (ioaddr + XP_DATA)); - } - } else { - len = min(len, SC26198_TXFIFOSIZE); - portp->stats.txtotal += len; - stlen = min_t(unsigned int, len, - (portp->tx.buf + STL_TXBUFSIZE) - tail); - outb(GTXFIFO, (ioaddr + XP_ADDR)); - outsb((ioaddr + XP_DATA), tail, stlen); - len -= stlen; - tail += stlen; - if (tail >= (portp->tx.buf + STL_TXBUFSIZE)) - tail = portp->tx.buf; - if (len > 0) { - outsb((ioaddr + XP_DATA), tail, len); - tail += len; - } - portp->tx.tail = tail; - } -} - -/*****************************************************************************/ - -/* - * Receive character interrupt handler. Determine if we have good chars - * or bad chars and then process appropriately. Good chars are easy - * just shove the lot into the RX buffer and set all status byte to 0. - * If a bad RX char then process as required. This routine needs to be - * fast! In practice it is possible that we get an interrupt on a port - * that is closed. This can happen on hangups - since they completely - * shutdown a port not in user context. Need to handle this case. - */ - -static void stl_sc26198rxisr(struct stlport *portp, unsigned int iack) -{ - struct tty_struct *tty; - unsigned int len, buflen, ioaddr; - - pr_debug("stl_sc26198rxisr(portp=%p,iack=%x)\n", portp, iack); - - tty = tty_port_tty_get(&portp->port); - ioaddr = portp->ioaddr; - outb(GIBCR, (ioaddr + XP_ADDR)); - len = inb(ioaddr + XP_DATA) + 1; - - if ((iack & IVR_TYPEMASK) == IVR_RXDATA) { - if (tty == NULL || (buflen = tty_buffer_request_room(tty, len)) == 0) { - len = min_t(unsigned int, len, sizeof(stl_unwanted)); - outb(GRXFIFO, (ioaddr + XP_ADDR)); - insb((ioaddr + XP_DATA), &stl_unwanted[0], len); - portp->stats.rxlost += len; - portp->stats.rxtotal += len; - } else { - len = min(len, buflen); - if (len > 0) { - unsigned char *ptr; - outb(GRXFIFO, (ioaddr + XP_ADDR)); - tty_prepare_flip_string(tty, &ptr, len); - insb((ioaddr + XP_DATA), ptr, len); - tty_schedule_flip(tty); - portp->stats.rxtotal += len; - } - } - } else { - stl_sc26198rxbadchars(portp); - } - -/* - * If we are TX flow controlled and in IXANY mode then we may need - * to unflow control here. We gotta do this because of the automatic - * flow control modes of the sc26198. - */ - if (test_bit(ASYI_TXFLOWED, &portp->istate)) { - if ((tty != NULL) && - (tty->termios != NULL) && - (tty->termios->c_iflag & IXANY)) { - stl_sc26198txunflow(portp, tty); - } - } - tty_kref_put(tty); -} - -/*****************************************************************************/ - -/* - * Process an RX bad character. - */ - -static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char ch) -{ - struct tty_struct *tty; - unsigned int ioaddr; - - tty = tty_port_tty_get(&portp->port); - ioaddr = portp->ioaddr; - - if (status & SR_RXPARITY) - portp->stats.rxparity++; - if (status & SR_RXFRAMING) - portp->stats.rxframing++; - if (status & SR_RXOVERRUN) - portp->stats.rxoverrun++; - if (status & SR_RXBREAK) - portp->stats.rxbreaks++; - - if ((tty != NULL) && - ((portp->rxignoremsk & status) == 0)) { - if (portp->rxmarkmsk & status) { - if (status & SR_RXBREAK) { - status = TTY_BREAK; - if (portp->port.flags & ASYNC_SAK) { - do_SAK(tty); - BRDENABLE(portp->brdnr, portp->pagenr); - } - } else if (status & SR_RXPARITY) - status = TTY_PARITY; - else if (status & SR_RXFRAMING) - status = TTY_FRAME; - else if(status & SR_RXOVERRUN) - status = TTY_OVERRUN; - else - status = 0; - } else - status = 0; - - tty_insert_flip_char(tty, ch, status); - tty_schedule_flip(tty); - - if (status == 0) - portp->stats.rxtotal++; - } - tty_kref_put(tty); -} - -/*****************************************************************************/ - -/* - * Process all characters in the RX FIFO of the UART. Check all char - * status bytes as well, and process as required. We need to check - * all bytes in the FIFO, in case some more enter the FIFO while we - * are here. To get the exact character error type we need to switch - * into CHAR error mode (that is why we need to make sure we empty - * the FIFO). - */ - -static void stl_sc26198rxbadchars(struct stlport *portp) -{ - unsigned char status, mr1; - char ch; - -/* - * To get the precise error type for each character we must switch - * back into CHAR error mode. - */ - mr1 = stl_sc26198getreg(portp, MR1); - stl_sc26198setreg(portp, MR1, (mr1 & ~MR1_ERRBLOCK)); - - while ((status = stl_sc26198getreg(portp, SR)) & SR_RXRDY) { - stl_sc26198setreg(portp, SCCR, CR_CLEARRXERR); - ch = stl_sc26198getreg(portp, RXFIFO); - stl_sc26198rxbadch(portp, status, ch); - } - -/* - * To get correct interrupt class we must switch back into BLOCK - * error mode. - */ - stl_sc26198setreg(portp, MR1, mr1); -} - -/*****************************************************************************/ - -/* - * Other interrupt handler. This includes modem signals, flow - * control actions, etc. Most stuff is left to off-level interrupt - * processing time. - */ - -static void stl_sc26198otherisr(struct stlport *portp, unsigned int iack) -{ - unsigned char cir, ipr, xisr; - - pr_debug("stl_sc26198otherisr(portp=%p,iack=%x)\n", portp, iack); - - cir = stl_sc26198getglobreg(portp, CIR); - - switch (cir & CIR_SUBTYPEMASK) { - case CIR_SUBCOS: - ipr = stl_sc26198getreg(portp, IPR); - if (ipr & IPR_DCDCHANGE) { - stl_cd_change(portp); - portp->stats.modem++; - } - break; - case CIR_SUBXONXOFF: - xisr = stl_sc26198getreg(portp, XISR); - if (xisr & XISR_RXXONGOT) { - set_bit(ASYI_TXFLOWED, &portp->istate); - portp->stats.txxoff++; - } - if (xisr & XISR_RXXOFFGOT) { - clear_bit(ASYI_TXFLOWED, &portp->istate); - portp->stats.txxon++; - } - break; - case CIR_SUBBREAK: - stl_sc26198setreg(portp, SCCR, CR_BREAKRESET); - stl_sc26198rxbadchars(portp); - break; - default: - break; - } -} - -static void stl_free_isabrds(void) -{ - struct stlbrd *brdp; - unsigned int i; - - for (i = 0; i < stl_nrbrds; i++) { - if ((brdp = stl_brds[i]) == NULL || (brdp->state & STL_PROBED)) - continue; - - free_irq(brdp->irq, brdp); - - stl_cleanup_panels(brdp); - - release_region(brdp->ioaddr1, brdp->iosize1); - if (brdp->iosize2 > 0) - release_region(brdp->ioaddr2, brdp->iosize2); - - kfree(brdp); - stl_brds[i] = NULL; - } -} - -/* - * Loadable module initialization stuff. - */ -static int __init stallion_module_init(void) -{ - struct stlbrd *brdp; - struct stlconf conf; - unsigned int i, j; - int retval; - - printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion); - - spin_lock_init(&stallion_lock); - spin_lock_init(&brd_lock); - - stl_serial = alloc_tty_driver(STL_MAXBRDS * STL_MAXPORTS); - if (!stl_serial) { - retval = -ENOMEM; - goto err; - } - - stl_serial->owner = THIS_MODULE; - stl_serial->driver_name = stl_drvname; - stl_serial->name = "ttyE"; - stl_serial->major = STL_SERIALMAJOR; - stl_serial->minor_start = 0; - stl_serial->type = TTY_DRIVER_TYPE_SERIAL; - stl_serial->subtype = SERIAL_TYPE_NORMAL; - stl_serial->init_termios = stl_deftermios; - stl_serial->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - tty_set_operations(stl_serial, &stl_ops); - - retval = tty_register_driver(stl_serial); - if (retval) { - printk("STALLION: failed to register serial driver\n"); - goto err_frtty; - } - -/* - * Find any dynamically supported boards. That is via module load - * line options. - */ - for (i = stl_nrbrds; i < stl_nargs; i++) { - memset(&conf, 0, sizeof(conf)); - if (stl_parsebrd(&conf, stl_brdsp[i]) == 0) - continue; - if ((brdp = stl_allocbrd()) == NULL) - continue; - brdp->brdnr = i; - brdp->brdtype = conf.brdtype; - brdp->ioaddr1 = conf.ioaddr1; - brdp->ioaddr2 = conf.ioaddr2; - brdp->irq = conf.irq; - brdp->irqtype = conf.irqtype; - stl_brds[brdp->brdnr] = brdp; - if (stl_brdinit(brdp)) { - stl_brds[brdp->brdnr] = NULL; - kfree(brdp); - } else { - for (j = 0; j < brdp->nrports; j++) - tty_register_device(stl_serial, - brdp->brdnr * STL_MAXPORTS + j, NULL); - stl_nrbrds = i + 1; - } - } - - /* this has to be _after_ isa finding because of locking */ - retval = pci_register_driver(&stl_pcidriver); - if (retval && stl_nrbrds == 0) { - printk(KERN_ERR "STALLION: can't register pci driver\n"); - goto err_unrtty; - } - -/* - * Set up a character driver for per board stuff. This is mainly used - * to do stats ioctls on the ports. - */ - if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem)) - printk("STALLION: failed to register serial board device\n"); - - stallion_class = class_create(THIS_MODULE, "staliomem"); - if (IS_ERR(stallion_class)) - printk("STALLION: failed to create class\n"); - for (i = 0; i < 4; i++) - device_create(stallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i), - NULL, "staliomem%d", i); - - return 0; -err_unrtty: - tty_unregister_driver(stl_serial); -err_frtty: - put_tty_driver(stl_serial); -err: - return retval; -} - -static void __exit stallion_module_exit(void) -{ - struct stlbrd *brdp; - unsigned int i, j; - - pr_debug("cleanup_module()\n"); - - printk(KERN_INFO "Unloading %s: version %s\n", stl_drvtitle, - stl_drvversion); - -/* - * Free up all allocated resources used by the ports. This includes - * memory and interrupts. As part of this process we will also do - * a hangup on every open port - to try to flush out any processes - * hanging onto ports. - */ - for (i = 0; i < stl_nrbrds; i++) { - if ((brdp = stl_brds[i]) == NULL || (brdp->state & STL_PROBED)) - continue; - for (j = 0; j < brdp->nrports; j++) - tty_unregister_device(stl_serial, - brdp->brdnr * STL_MAXPORTS + j); - } - - for (i = 0; i < 4; i++) - device_destroy(stallion_class, MKDEV(STL_SIOMEMMAJOR, i)); - unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"); - class_destroy(stallion_class); - - pci_unregister_driver(&stl_pcidriver); - - stl_free_isabrds(); - - tty_unregister_driver(stl_serial); - put_tty_driver(stl_serial); -} - -module_init(stallion_module_init); -module_exit(stallion_module_exit); - -MODULE_AUTHOR("Greg Ungerer"); -MODULE_DESCRIPTION("Stallion Multiport Serial Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 5c8fcfc42c3e..fb1fc4e5a8cb 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -41,6 +41,8 @@ config STAGING_EXCLUDE_BUILD if !STAGING_EXCLUDE_BUILD +source "drivers/staging/tty/Kconfig" + source "drivers/staging/et131x/Kconfig" source "drivers/staging/slicoss/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index d53886317826..f498e345a01d 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -3,6 +3,7 @@ # fix for build system bug... obj-$(CONFIG_STAGING) += staging.o +obj-y += tty/ obj-$(CONFIG_ET131X) += et131x/ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ diff --git a/drivers/staging/tty/Kconfig b/drivers/staging/tty/Kconfig new file mode 100644 index 000000000000..77103a07abbd --- /dev/null +++ b/drivers/staging/tty/Kconfig @@ -0,0 +1,87 @@ +config STALLION + tristate "Stallion EasyIO or EC8/32 support" + depends on STALDRV && (ISA || EISA || PCI) + help + If you have an EasyIO or EasyConnection 8/32 multiport Stallion + card, then this is for you; say Y. Make sure to read + . + + To compile this driver as a module, choose M here: the + module will be called stallion. + +config ISTALLION + tristate "Stallion EC8/64, ONboard, Brumby support" + depends on STALDRV && (ISA || EISA || PCI) + help + If you have an EasyConnection 8/64, ONboard, Brumby or Stallion + serial multiport card, say Y here. Make sure to read + . + + To compile this driver as a module, choose M here: the + module will be called istallion. + +config DIGIEPCA + tristate "Digiboard Intelligent Async Support" + depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) + ---help--- + This is a driver for Digi International's Xx, Xeve, and Xem series + of cards which provide multiple serial ports. You would need + something like this to connect more than two modems to your Linux + box, for instance in order to become a dial-in server. This driver + supports the original PC (ISA) boards as well as PCI, and EISA. If + you have a card like this, say Y here and read the file + . + + To compile this driver as a module, choose M here: the + module will be called epca. + +config RISCOM8 + tristate "SDL RISCom/8 card support" + depends on SERIAL_NONSTANDARD + help + This is a driver for the SDL Communications RISCom/8 multiport card, + which gives you many serial ports. You would need something like + this to connect more than two modems to your Linux box, for instance + in order to become a dial-in server. If you have a card like that, + say Y here and read the file . + + Also it's possible to say M here and compile this driver as kernel + loadable module; the module will be called riscom8. + +config SPECIALIX + tristate "Specialix IO8+ card support" + depends on SERIAL_NONSTANDARD + help + This is a driver for the Specialix IO8+ multiport card (both the + ISA and the PCI version) which gives you many serial ports. You + would need something like this to connect more than two modems to + your Linux box, for instance in order to become a dial-in server. + + If you have a card like that, say Y here and read the file + . Also it's possible to say + M here and compile this driver as kernel loadable module which will be + called specialix. + +config COMPUTONE + tristate "Computone IntelliPort Plus serial support" + depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) + ---help--- + This driver supports the entire family of Intelliport II/Plus + controllers with the exception of the MicroChannel controllers and + products previous to the Intelliport II. These are multiport cards, + which give you many serial ports. You would need something like this + to connect more than two modems to your Linux box, for instance in + order to become a dial-in server. If you have a card like that, say + Y here and read . + + To compile this driver as module, choose M here: the + module will be called ip2. + +config SERIAL167 + bool "CD2401 support for MVME166/7 serial ports" + depends on MVME16x + help + This is the driver for the serial ports on the Motorola MVME166, + 167, and 172 boards. Everyone using one of these boards should say + Y here. + diff --git a/drivers/staging/tty/Makefile b/drivers/staging/tty/Makefile new file mode 100644 index 000000000000..ac57c105611b --- /dev/null +++ b/drivers/staging/tty/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_STALLION) += stallion.o +obj-$(CONFIG_ISTALLION) += istallion.o +obj-$(CONFIG_DIGIEPCA) += epca.o +obj-$(CONFIG_SERIAL167) += serial167.o +obj-$(CONFIG_SPECIALIX) += specialix.o +obj-$(CONFIG_RISCOM8) += riscom8.o +obj-$(CONFIG_COMPUTONE) += ip2/ diff --git a/drivers/staging/tty/TODO b/drivers/staging/tty/TODO new file mode 100644 index 000000000000..88756453ac6c --- /dev/null +++ b/drivers/staging/tty/TODO @@ -0,0 +1,6 @@ +These are a few tty/serial drivers that either do not build, +or work if they do build, or if they seem to work, are for obsolete +hardware, or are full of unfixable races and no one uses them anymore. + +If no one steps up to adopt any of these drivers, they will be removed +in the 2.6.41 release. diff --git a/drivers/staging/tty/epca.c b/drivers/staging/tty/epca.c new file mode 100644 index 000000000000..7ad3638967ae --- /dev/null +++ b/drivers/staging/tty/epca.c @@ -0,0 +1,2784 @@ +/* + Copyright (C) 1996 Digi International. + + For technical support please email digiLinux@dgii.com or + call Digi tech support at (612) 912-3456 + + ** This driver is no longer supported by Digi ** + + Much of this design and code came from epca.c which was + copyright (C) 1994, 1995 Troy De Jongh, and subsquently + modified by David Nugent, Christoph Lameter, Mike McLagan. + + 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. +*/ +/* See README.epca for change history --DAT*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "digiPCI.h" + + +#include "digi1.h" +#include "digiFep1.h" +#include "epca.h" +#include "epcaconfig.h" + +#define VERSION "1.3.0.1-LK2.6" + +/* This major needs to be submitted to Linux to join the majors list */ +#define DIGIINFOMAJOR 35 /* For Digi specific ioctl */ + + +#define MAXCARDS 7 +#define epcaassert(x, msg) if (!(x)) epca_error(__LINE__, msg) + +#define PFX "epca: " + +static int nbdevs, num_cards, liloconfig; +static int digi_poller_inhibited = 1 ; + +static int setup_error_code; +static int invalid_lilo_config; + +/* + * The ISA boards do window flipping into the same spaces so its only sane with + * a single lock. It's still pretty efficient. This lock guards the hardware + * and the tty_port lock guards the kernel side stuff like use counts. Take + * this lock inside the port lock if you must take both. + */ +static DEFINE_SPINLOCK(epca_lock); + +/* MAXBOARDS is typically 12, but ISA and EISA cards are restricted + to 7 below. */ +static struct board_info boards[MAXBOARDS]; + +static struct tty_driver *pc_driver; +static struct tty_driver *pc_info; + +/* ------------------ Begin Digi specific structures -------------------- */ + +/* + * digi_channels represents an array of structures that keep track of each + * channel of the Digi product. Information such as transmit and receive + * pointers, termio data, and signal definitions (DTR, CTS, etc ...) are stored + * here. This structure is NOT used to overlay the cards physical channel + * structure. + */ +static struct channel digi_channels[MAX_ALLOC]; + +/* + * card_ptr is an array used to hold the address of the first channel structure + * of each card. This array will hold the addresses of various channels located + * in digi_channels. + */ +static struct channel *card_ptr[MAXCARDS]; + +static struct timer_list epca_timer; + +/* + * Begin generic memory functions. These functions will be alias (point at) + * more specific functions dependent on the board being configured. + */ +static void memwinon(struct board_info *b, unsigned int win); +static void memwinoff(struct board_info *b, unsigned int win); +static void globalwinon(struct channel *ch); +static void rxwinon(struct channel *ch); +static void txwinon(struct channel *ch); +static void memoff(struct channel *ch); +static void assertgwinon(struct channel *ch); +static void assertmemoff(struct channel *ch); + +/* ---- Begin more 'specific' memory functions for cx_like products --- */ + +static void pcxem_memwinon(struct board_info *b, unsigned int win); +static void pcxem_memwinoff(struct board_info *b, unsigned int win); +static void pcxem_globalwinon(struct channel *ch); +static void pcxem_rxwinon(struct channel *ch); +static void pcxem_txwinon(struct channel *ch); +static void pcxem_memoff(struct channel *ch); + +/* ------ Begin more 'specific' memory functions for the pcxe ------- */ + +static void pcxe_memwinon(struct board_info *b, unsigned int win); +static void pcxe_memwinoff(struct board_info *b, unsigned int win); +static void pcxe_globalwinon(struct channel *ch); +static void pcxe_rxwinon(struct channel *ch); +static void pcxe_txwinon(struct channel *ch); +static void pcxe_memoff(struct channel *ch); + +/* ---- Begin more 'specific' memory functions for the pc64xe and pcxi ---- */ +/* Note : pc64xe and pcxi share the same windowing routines */ + +static void pcxi_memwinon(struct board_info *b, unsigned int win); +static void pcxi_memwinoff(struct board_info *b, unsigned int win); +static void pcxi_globalwinon(struct channel *ch); +static void pcxi_rxwinon(struct channel *ch); +static void pcxi_txwinon(struct channel *ch); +static void pcxi_memoff(struct channel *ch); + +/* - Begin 'specific' do nothing memory functions needed for some cards - */ + +static void dummy_memwinon(struct board_info *b, unsigned int win); +static void dummy_memwinoff(struct board_info *b, unsigned int win); +static void dummy_globalwinon(struct channel *ch); +static void dummy_rxwinon(struct channel *ch); +static void dummy_txwinon(struct channel *ch); +static void dummy_memoff(struct channel *ch); +static void dummy_assertgwinon(struct channel *ch); +static void dummy_assertmemoff(struct channel *ch); + +static struct channel *verifyChannel(struct tty_struct *); +static void pc_sched_event(struct channel *, int); +static void epca_error(int, char *); +static void pc_close(struct tty_struct *, struct file *); +static void shutdown(struct channel *, struct tty_struct *tty); +static void pc_hangup(struct tty_struct *); +static int pc_write_room(struct tty_struct *); +static int pc_chars_in_buffer(struct tty_struct *); +static void pc_flush_buffer(struct tty_struct *); +static void pc_flush_chars(struct tty_struct *); +static int pc_open(struct tty_struct *, struct file *); +static void post_fep_init(unsigned int crd); +static void epcapoll(unsigned long); +static void doevent(int); +static void fepcmd(struct channel *, int, int, int, int, int); +static unsigned termios2digi_h(struct channel *ch, unsigned); +static unsigned termios2digi_i(struct channel *ch, unsigned); +static unsigned termios2digi_c(struct channel *ch, unsigned); +static void epcaparam(struct tty_struct *, struct channel *); +static void receive_data(struct channel *, struct tty_struct *tty); +static int pc_ioctl(struct tty_struct *, + unsigned int, unsigned long); +static int info_ioctl(struct tty_struct *, + unsigned int, unsigned long); +static void pc_set_termios(struct tty_struct *, struct ktermios *); +static void do_softint(struct work_struct *work); +static void pc_stop(struct tty_struct *); +static void pc_start(struct tty_struct *); +static void pc_throttle(struct tty_struct *tty); +static void pc_unthrottle(struct tty_struct *tty); +static int pc_send_break(struct tty_struct *tty, int msec); +static void setup_empty_event(struct tty_struct *tty, struct channel *ch); + +static int pc_write(struct tty_struct *, const unsigned char *, int); +static int pc_init(void); +static int init_PCI(void); + +/* + * Table of functions for each board to handle memory. Mantaining parallelism + * is a *very* good idea here. The idea is for the runtime code to blindly call + * these functions, not knowing/caring about the underlying hardware. This + * stuff should contain no conditionals; if more functionality is needed a + * different entry should be established. These calls are the interface calls + * and are the only functions that should be accessed. Anyone caught making + * direct calls deserves what they get. + */ +static void memwinon(struct board_info *b, unsigned int win) +{ + b->memwinon(b, win); +} + +static void memwinoff(struct board_info *b, unsigned int win) +{ + b->memwinoff(b, win); +} + +static void globalwinon(struct channel *ch) +{ + ch->board->globalwinon(ch); +} + +static void rxwinon(struct channel *ch) +{ + ch->board->rxwinon(ch); +} + +static void txwinon(struct channel *ch) +{ + ch->board->txwinon(ch); +} + +static void memoff(struct channel *ch) +{ + ch->board->memoff(ch); +} +static void assertgwinon(struct channel *ch) +{ + ch->board->assertgwinon(ch); +} + +static void assertmemoff(struct channel *ch) +{ + ch->board->assertmemoff(ch); +} + +/* PCXEM windowing is the same as that used in the PCXR and CX series cards. */ +static void pcxem_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(FEPWIN | win, b->port + 1); +} + +static void pcxem_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(0, b->port + 1); +} + +static void pcxem_globalwinon(struct channel *ch) +{ + outb_p(FEPWIN, (int)ch->board->port + 1); +} + +static void pcxem_rxwinon(struct channel *ch) +{ + outb_p(ch->rxwin, (int)ch->board->port + 1); +} + +static void pcxem_txwinon(struct channel *ch) +{ + outb_p(ch->txwin, (int)ch->board->port + 1); +} + +static void pcxem_memoff(struct channel *ch) +{ + outb_p(0, (int)ch->board->port + 1); +} + +/* ----------------- Begin pcxe memory window stuff ------------------ */ +static void pcxe_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(FEPWIN | win, b->port + 1); +} + +static void pcxe_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(inb(b->port) & ~FEPMEM, b->port + 1); + outb_p(0, b->port + 1); +} + +static void pcxe_globalwinon(struct channel *ch) +{ + outb_p(FEPWIN, (int)ch->board->port + 1); +} + +static void pcxe_rxwinon(struct channel *ch) +{ + outb_p(ch->rxwin, (int)ch->board->port + 1); +} + +static void pcxe_txwinon(struct channel *ch) +{ + outb_p(ch->txwin, (int)ch->board->port + 1); +} + +static void pcxe_memoff(struct channel *ch) +{ + outb_p(0, (int)ch->board->port); + outb_p(0, (int)ch->board->port + 1); +} + +/* ------------- Begin pc64xe and pcxi memory window stuff -------------- */ +static void pcxi_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(inb(b->port) | FEPMEM, b->port); +} + +static void pcxi_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(inb(b->port) & ~FEPMEM, b->port); +} + +static void pcxi_globalwinon(struct channel *ch) +{ + outb_p(FEPMEM, ch->board->port); +} + +static void pcxi_rxwinon(struct channel *ch) +{ + outb_p(FEPMEM, ch->board->port); +} + +static void pcxi_txwinon(struct channel *ch) +{ + outb_p(FEPMEM, ch->board->port); +} + +static void pcxi_memoff(struct channel *ch) +{ + outb_p(0, ch->board->port); +} + +static void pcxi_assertgwinon(struct channel *ch) +{ + epcaassert(inb(ch->board->port) & FEPMEM, "Global memory off"); +} + +static void pcxi_assertmemoff(struct channel *ch) +{ + epcaassert(!(inb(ch->board->port) & FEPMEM), "Memory on"); +} + +/* + * Not all of the cards need specific memory windowing routines. Some cards + * (Such as PCI) needs no windowing routines at all. We provide these do + * nothing routines so that the same code base can be used. The driver will + * ALWAYS call a windowing routine if it thinks it needs to; regardless of the + * card. However, dependent on the card the routine may or may not do anything. + */ +static void dummy_memwinon(struct board_info *b, unsigned int win) +{ +} + +static void dummy_memwinoff(struct board_info *b, unsigned int win) +{ +} + +static void dummy_globalwinon(struct channel *ch) +{ +} + +static void dummy_rxwinon(struct channel *ch) +{ +} + +static void dummy_txwinon(struct channel *ch) +{ +} + +static void dummy_memoff(struct channel *ch) +{ +} + +static void dummy_assertgwinon(struct channel *ch) +{ +} + +static void dummy_assertmemoff(struct channel *ch) +{ +} + +static struct channel *verifyChannel(struct tty_struct *tty) +{ + /* + * This routine basically provides a sanity check. It insures that the + * channel returned is within the proper range of addresses as well as + * properly initialized. If some bogus info gets passed in + * through tty->driver_data this should catch it. + */ + if (tty) { + struct channel *ch = tty->driver_data; + if (ch >= &digi_channels[0] && ch < &digi_channels[nbdevs]) { + if (ch->magic == EPCA_MAGIC) + return ch; + } + } + return NULL; +} + +static void pc_sched_event(struct channel *ch, int event) +{ + /* + * We call this to schedule interrupt processing on some event. The + * kernel sees our request and calls the related routine in OUR driver. + */ + ch->event |= 1 << event; + schedule_work(&ch->tqueue); +} + +static void epca_error(int line, char *msg) +{ + printk(KERN_ERR "epca_error (Digi): line = %d %s\n", line, msg); +} + +static void pc_close(struct tty_struct *tty, struct file *filp) +{ + struct channel *ch; + struct tty_port *port; + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + if (ch == NULL) + return; + port = &ch->port; + + if (tty_port_close_start(port, tty, filp) == 0) + return; + + pc_flush_buffer(tty); + shutdown(ch, tty); + + tty_port_close_end(port, tty); + ch->event = 0; /* FIXME: review ch->event locking */ + tty_port_tty_set(port, NULL); +} + +static void shutdown(struct channel *ch, struct tty_struct *tty) +{ + unsigned long flags; + struct board_chan __iomem *bc; + struct tty_port *port = &ch->port; + + if (!(port->flags & ASYNC_INITIALIZED)) + return; + + spin_lock_irqsave(&epca_lock, flags); + + globalwinon(ch); + bc = ch->brdchan; + + /* + * In order for an event to be generated on the receipt of data the + * idata flag must be set. Since we are shutting down, this is not + * necessary clear this flag. + */ + if (bc) + writeb(0, &bc->idata); + + /* If we're a modem control device and HUPCL is on, drop RTS & DTR. */ + if (tty->termios->c_cflag & HUPCL) { + ch->omodem &= ~(ch->m_rts | ch->m_dtr); + fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1); + } + memoff(ch); + + /* + * The channel has officialy been closed. The next time it is opened it + * will have to reinitialized. Set a flag to indicate this. + */ + /* Prevent future Digi programmed interrupts from coming active */ + port->flags &= ~ASYNC_INITIALIZED; + spin_unlock_irqrestore(&epca_lock, flags); +} + +static void pc_hangup(struct tty_struct *tty) +{ + struct channel *ch; + + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + if (ch != NULL) { + pc_flush_buffer(tty); + tty_ldisc_flush(tty); + shutdown(ch, tty); + + ch->event = 0; /* FIXME: review locking of ch->event */ + tty_port_hangup(&ch->port); + } +} + +static int pc_write(struct tty_struct *tty, + const unsigned char *buf, int bytesAvailable) +{ + unsigned int head, tail; + int dataLen; + int size; + int amountCopied; + struct channel *ch; + unsigned long flags; + int remain; + struct board_chan __iomem *bc; + + /* + * pc_write is primarily called directly by the kernel routine + * tty_write (Though it can also be called by put_char) found in + * tty_io.c. pc_write is passed a line discipline buffer where the data + * to be written out is stored. The line discipline implementation + * itself is done at the kernel level and is not brought into the + * driver. + */ + + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + if (ch == NULL) + return 0; + + /* Make a pointer to the channel data structure found on the board. */ + bc = ch->brdchan; + size = ch->txbufsize; + amountCopied = 0; + + spin_lock_irqsave(&epca_lock, flags); + globalwinon(ch); + + head = readw(&bc->tin) & (size - 1); + tail = readw(&bc->tout); + + if (tail != readw(&bc->tout)) + tail = readw(&bc->tout); + tail &= (size - 1); + + if (head >= tail) { + /* head has not wrapped */ + /* + * remain (much like dataLen above) represents the total amount + * of space available on the card for data. Here dataLen + * represents the space existing between the head pointer and + * the end of buffer. This is important because a memcpy cannot + * be told to automatically wrap around when it hits the buffer + * end. + */ + dataLen = size - head; + remain = size - (head - tail) - 1; + } else { + /* head has wrapped around */ + remain = tail - head - 1; + dataLen = remain; + } + /* + * Check the space on the card. If we have more data than space; reduce + * the amount of data to fit the space. + */ + bytesAvailable = min(remain, bytesAvailable); + txwinon(ch); + while (bytesAvailable > 0) { + /* there is data to copy onto card */ + + /* + * If head is not wrapped, the below will make sure the first + * data copy fills to the end of card buffer. + */ + dataLen = min(bytesAvailable, dataLen); + memcpy_toio(ch->txptr + head, buf, dataLen); + buf += dataLen; + head += dataLen; + amountCopied += dataLen; + bytesAvailable -= dataLen; + + if (head >= size) { + head = 0; + dataLen = tail; + } + } + ch->statusflags |= TXBUSY; + globalwinon(ch); + writew(head, &bc->tin); + + if ((ch->statusflags & LOWWAIT) == 0) { + ch->statusflags |= LOWWAIT; + writeb(1, &bc->ilow); + } + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + return amountCopied; +} + +static int pc_write_room(struct tty_struct *tty) +{ + int remain = 0; + struct channel *ch; + unsigned long flags; + unsigned int head, tail; + struct board_chan __iomem *bc; + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + if (ch != NULL) { + spin_lock_irqsave(&epca_lock, flags); + globalwinon(ch); + + bc = ch->brdchan; + head = readw(&bc->tin) & (ch->txbufsize - 1); + tail = readw(&bc->tout); + + if (tail != readw(&bc->tout)) + tail = readw(&bc->tout); + /* Wrap tail if necessary */ + tail &= (ch->txbufsize - 1); + remain = tail - head - 1; + if (remain < 0) + remain += ch->txbufsize; + + if (remain && (ch->statusflags & LOWWAIT) == 0) { + ch->statusflags |= LOWWAIT; + writeb(1, &bc->ilow); + } + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + } + /* Return how much room is left on card */ + return remain; +} + +static int pc_chars_in_buffer(struct tty_struct *tty) +{ + int chars; + unsigned int ctail, head, tail; + int remain; + unsigned long flags; + struct channel *ch; + struct board_chan __iomem *bc; + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + if (ch == NULL) + return 0; + + spin_lock_irqsave(&epca_lock, flags); + globalwinon(ch); + + bc = ch->brdchan; + tail = readw(&bc->tout); + head = readw(&bc->tin); + ctail = readw(&ch->mailbox->cout); + + if (tail == head && readw(&ch->mailbox->cin) == ctail && + readb(&bc->tbusy) == 0) + chars = 0; + else { /* Begin if some space on the card has been used */ + head = readw(&bc->tin) & (ch->txbufsize - 1); + tail &= (ch->txbufsize - 1); + /* + * The logic here is basically opposite of the above + * pc_write_room here we are finding the amount of bytes in the + * buffer filled. Not the amount of bytes empty. + */ + remain = tail - head - 1; + if (remain < 0) + remain += ch->txbufsize; + chars = (int)(ch->txbufsize - remain); + /* + * Make it possible to wakeup anything waiting for output in + * tty_ioctl.c, etc. + * + * If not already set. Setup an event to indicate when the + * transmit buffer empties. + */ + if (!(ch->statusflags & EMPTYWAIT)) + setup_empty_event(tty, ch); + } /* End if some space on the card has been used */ + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + /* Return number of characters residing on card. */ + return chars; +} + +static void pc_flush_buffer(struct tty_struct *tty) +{ + unsigned int tail; + unsigned long flags; + struct channel *ch; + struct board_chan __iomem *bc; + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + if (ch == NULL) + return; + + spin_lock_irqsave(&epca_lock, flags); + globalwinon(ch); + bc = ch->brdchan; + tail = readw(&bc->tout); + /* Have FEP move tout pointer; effectively flushing transmit buffer */ + fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0); + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + tty_wakeup(tty); +} + +static void pc_flush_chars(struct tty_struct *tty) +{ + struct channel *ch; + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + if (ch != NULL) { + unsigned long flags; + spin_lock_irqsave(&epca_lock, flags); + /* + * If not already set and the transmitter is busy setup an + * event to indicate when the transmit empties. + */ + if ((ch->statusflags & TXBUSY) && + !(ch->statusflags & EMPTYWAIT)) + setup_empty_event(tty, ch); + spin_unlock_irqrestore(&epca_lock, flags); + } +} + +static int epca_carrier_raised(struct tty_port *port) +{ + struct channel *ch = container_of(port, struct channel, port); + if (ch->imodem & ch->dcd) + return 1; + return 0; +} + +static void epca_dtr_rts(struct tty_port *port, int onoff) +{ +} + +static int pc_open(struct tty_struct *tty, struct file *filp) +{ + struct channel *ch; + struct tty_port *port; + unsigned long flags; + int line, retval, boardnum; + struct board_chan __iomem *bc; + unsigned int head; + + line = tty->index; + if (line < 0 || line >= nbdevs) + return -ENODEV; + + ch = &digi_channels[line]; + port = &ch->port; + boardnum = ch->boardnum; + + /* Check status of board configured in system. */ + + /* + * I check to see if the epca_setup routine detected a user error. It + * might be better to put this in pc_init, but for the moment it goes + * here. + */ + if (invalid_lilo_config) { + if (setup_error_code & INVALID_BOARD_TYPE) + printk(KERN_ERR "epca: pc_open: Invalid board type specified in kernel options.\n"); + if (setup_error_code & INVALID_NUM_PORTS) + printk(KERN_ERR "epca: pc_open: Invalid number of ports specified in kernel options.\n"); + if (setup_error_code & INVALID_MEM_BASE) + printk(KERN_ERR "epca: pc_open: Invalid board memory address specified in kernel options.\n"); + if (setup_error_code & INVALID_PORT_BASE) + printk(KERN_ERR "epca; pc_open: Invalid board port address specified in kernel options.\n"); + if (setup_error_code & INVALID_BOARD_STATUS) + printk(KERN_ERR "epca: pc_open: Invalid board status specified in kernel options.\n"); + if (setup_error_code & INVALID_ALTPIN) + printk(KERN_ERR "epca: pc_open: Invalid board altpin specified in kernel options;\n"); + tty->driver_data = NULL; /* Mark this device as 'down' */ + return -ENODEV; + } + if (boardnum >= num_cards || boards[boardnum].status == DISABLED) { + tty->driver_data = NULL; /* Mark this device as 'down' */ + return(-ENODEV); + } + + bc = ch->brdchan; + if (bc == NULL) { + tty->driver_data = NULL; + return -ENODEV; + } + + spin_lock_irqsave(&port->lock, flags); + /* + * Every time a channel is opened, increment a counter. This is + * necessary because we do not wish to flush and shutdown the channel + * until the last app holding the channel open, closes it. + */ + port->count++; + /* + * Set a kernel structures pointer to our local channel structure. This + * way we can get to it when passed only a tty struct. + */ + tty->driver_data = ch; + port->tty = tty; + /* + * If this is the first time the channel has been opened, initialize + * the tty->termios struct otherwise let pc_close handle it. + */ + spin_lock(&epca_lock); + globalwinon(ch); + ch->statusflags = 0; + + /* Save boards current modem status */ + ch->imodem = readb(&bc->mstat); + + /* + * Set receive head and tail ptrs to each other. This indicates no data + * available to read. + */ + head = readw(&bc->rin); + writew(head, &bc->rout); + + /* Set the channels associated tty structure */ + + /* + * The below routine generally sets up parity, baud, flow control + * issues, etc.... It effect both control flags and input flags. + */ + epcaparam(tty, ch); + memoff(ch); + spin_unlock(&epca_lock); + port->flags |= ASYNC_INITIALIZED; + spin_unlock_irqrestore(&port->lock, flags); + + retval = tty_port_block_til_ready(port, tty, filp); + if (retval) + return retval; + /* + * Set this again in case a hangup set it to zero while this open() was + * waiting for the line... + */ + spin_lock_irqsave(&port->lock, flags); + port->tty = tty; + spin_lock(&epca_lock); + globalwinon(ch); + /* Enable Digi Data events */ + writeb(1, &bc->idata); + memoff(ch); + spin_unlock(&epca_lock); + spin_unlock_irqrestore(&port->lock, flags); + return 0; +} + +static int __init epca_module_init(void) +{ + return pc_init(); +} +module_init(epca_module_init); + +static struct pci_driver epca_driver; + +static void __exit epca_module_exit(void) +{ + int count, crd; + struct board_info *bd; + struct channel *ch; + + del_timer_sync(&epca_timer); + + if (tty_unregister_driver(pc_driver) || + tty_unregister_driver(pc_info)) { + printk(KERN_WARNING "epca: cleanup_module failed to un-register tty driver\n"); + return; + } + put_tty_driver(pc_driver); + put_tty_driver(pc_info); + + for (crd = 0; crd < num_cards; crd++) { + bd = &boards[crd]; + if (!bd) { /* sanity check */ + printk(KERN_ERR " - Digi : cleanup_module failed\n"); + return; + } + ch = card_ptr[crd]; + for (count = 0; count < bd->numports; count++, ch++) { + struct tty_struct *tty = tty_port_tty_get(&ch->port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } + } + } + pci_unregister_driver(&epca_driver); +} +module_exit(epca_module_exit); + +static const struct tty_operations pc_ops = { + .open = pc_open, + .close = pc_close, + .write = pc_write, + .write_room = pc_write_room, + .flush_buffer = pc_flush_buffer, + .chars_in_buffer = pc_chars_in_buffer, + .flush_chars = pc_flush_chars, + .ioctl = pc_ioctl, + .set_termios = pc_set_termios, + .stop = pc_stop, + .start = pc_start, + .throttle = pc_throttle, + .unthrottle = pc_unthrottle, + .hangup = pc_hangup, + .break_ctl = pc_send_break +}; + +static const struct tty_port_operations epca_port_ops = { + .carrier_raised = epca_carrier_raised, + .dtr_rts = epca_dtr_rts, +}; + +static int info_open(struct tty_struct *tty, struct file *filp) +{ + return 0; +} + +static const struct tty_operations info_ops = { + .open = info_open, + .ioctl = info_ioctl, +}; + +static int __init pc_init(void) +{ + int crd; + struct board_info *bd; + unsigned char board_id = 0; + int err = -ENOMEM; + + int pci_boards_found, pci_count; + + pci_count = 0; + + pc_driver = alloc_tty_driver(MAX_ALLOC); + if (!pc_driver) + goto out1; + + pc_info = alloc_tty_driver(MAX_ALLOC); + if (!pc_info) + goto out2; + + /* + * If epca_setup has not been ran by LILO set num_cards to defaults; + * copy board structure defined by digiConfig into drivers board + * structure. Note : If LILO has ran epca_setup then epca_setup will + * handle defining num_cards as well as copying the data into the board + * structure. + */ + if (!liloconfig) { + /* driver has been configured via. epcaconfig */ + nbdevs = NBDEVS; + num_cards = NUMCARDS; + memcpy(&boards, &static_boards, + sizeof(struct board_info) * NUMCARDS); + } + + /* + * Note : If lilo was used to configure the driver and the ignore + * epcaconfig option was choosen (digiepca=2) then nbdevs and num_cards + * will equal 0 at this point. This is okay; PCI cards will still be + * picked up if detected. + */ + + /* + * Set up interrupt, we will worry about memory allocation in + * post_fep_init. + */ + printk(KERN_INFO "DIGI epca driver version %s loaded.\n", VERSION); + + /* + * NOTE : This code assumes that the number of ports found in the + * boards array is correct. This could be wrong if the card in question + * is PCI (And therefore has no ports entry in the boards structure.) + * The rest of the information will be valid for PCI because the + * beginning of pc_init scans for PCI and determines i/o and base + * memory addresses. I am not sure if it is possible to read the number + * of ports supported by the card prior to it being booted (Since that + * is the state it is in when pc_init is run). Because it is not + * possible to query the number of supported ports until after the card + * has booted; we are required to calculate the card_ptrs as the card + * is initialized (Inside post_fep_init). The negative thing about this + * approach is that digiDload's call to GET_INFO will have a bad port + * value. (Since this is called prior to post_fep_init.) + */ + pci_boards_found = 0; + if (num_cards < MAXBOARDS) + pci_boards_found += init_PCI(); + num_cards += pci_boards_found; + + pc_driver->owner = THIS_MODULE; + pc_driver->name = "ttyD"; + pc_driver->major = DIGI_MAJOR; + pc_driver->minor_start = 0; + pc_driver->type = TTY_DRIVER_TYPE_SERIAL; + pc_driver->subtype = SERIAL_TYPE_NORMAL; + pc_driver->init_termios = tty_std_termios; + pc_driver->init_termios.c_iflag = 0; + pc_driver->init_termios.c_oflag = 0; + pc_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; + pc_driver->init_termios.c_lflag = 0; + pc_driver->init_termios.c_ispeed = 9600; + pc_driver->init_termios.c_ospeed = 9600; + pc_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK; + tty_set_operations(pc_driver, &pc_ops); + + pc_info->owner = THIS_MODULE; + pc_info->name = "digi_ctl"; + pc_info->major = DIGIINFOMAJOR; + pc_info->minor_start = 0; + pc_info->type = TTY_DRIVER_TYPE_SERIAL; + pc_info->subtype = SERIAL_TYPE_INFO; + pc_info->init_termios = tty_std_termios; + pc_info->init_termios.c_iflag = 0; + pc_info->init_termios.c_oflag = 0; + pc_info->init_termios.c_lflag = 0; + pc_info->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; + pc_info->init_termios.c_ispeed = 9600; + pc_info->init_termios.c_ospeed = 9600; + pc_info->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(pc_info, &info_ops); + + + for (crd = 0; crd < num_cards; crd++) { + /* + * This is where the appropriate memory handlers for the + * hardware is set. Everything at runtime blindly jumps through + * these vectors. + */ + + /* defined in epcaconfig.h */ + bd = &boards[crd]; + + switch (bd->type) { + case PCXEM: + case EISAXEM: + bd->memwinon = pcxem_memwinon; + bd->memwinoff = pcxem_memwinoff; + bd->globalwinon = pcxem_globalwinon; + bd->txwinon = pcxem_txwinon; + bd->rxwinon = pcxem_rxwinon; + bd->memoff = pcxem_memoff; + bd->assertgwinon = dummy_assertgwinon; + bd->assertmemoff = dummy_assertmemoff; + break; + + case PCIXEM: + case PCIXRJ: + case PCIXR: + bd->memwinon = dummy_memwinon; + bd->memwinoff = dummy_memwinoff; + bd->globalwinon = dummy_globalwinon; + bd->txwinon = dummy_txwinon; + bd->rxwinon = dummy_rxwinon; + bd->memoff = dummy_memoff; + bd->assertgwinon = dummy_assertgwinon; + bd->assertmemoff = dummy_assertmemoff; + break; + + case PCXE: + case PCXEVE: + bd->memwinon = pcxe_memwinon; + bd->memwinoff = pcxe_memwinoff; + bd->globalwinon = pcxe_globalwinon; + bd->txwinon = pcxe_txwinon; + bd->rxwinon = pcxe_rxwinon; + bd->memoff = pcxe_memoff; + bd->assertgwinon = dummy_assertgwinon; + bd->assertmemoff = dummy_assertmemoff; + break; + + case PCXI: + case PC64XE: + bd->memwinon = pcxi_memwinon; + bd->memwinoff = pcxi_memwinoff; + bd->globalwinon = pcxi_globalwinon; + bd->txwinon = pcxi_txwinon; + bd->rxwinon = pcxi_rxwinon; + bd->memoff = pcxi_memoff; + bd->assertgwinon = pcxi_assertgwinon; + bd->assertmemoff = pcxi_assertmemoff; + break; + + default: + break; + } + + /* + * Some cards need a memory segment to be defined for use in + * transmit and receive windowing operations. These boards are + * listed in the below switch. In the case of the XI the amount + * of memory on the board is variable so the memory_seg is also + * variable. This code determines what they segment should be. + */ + switch (bd->type) { + case PCXE: + case PCXEVE: + case PC64XE: + bd->memory_seg = 0xf000; + break; + + case PCXI: + board_id = inb((int)bd->port); + if ((board_id & 0x1) == 0x1) { + /* it's an XI card */ + /* Is it a 64K board */ + if ((board_id & 0x30) == 0) + bd->memory_seg = 0xf000; + + /* Is it a 128K board */ + if ((board_id & 0x30) == 0x10) + bd->memory_seg = 0xe000; + + /* Is is a 256K board */ + if ((board_id & 0x30) == 0x20) + bd->memory_seg = 0xc000; + + /* Is it a 512K board */ + if ((board_id & 0x30) == 0x30) + bd->memory_seg = 0x8000; + } else + printk(KERN_ERR "epca: Board at 0x%x doesn't appear to be an XI\n", (int)bd->port); + break; + } + } + + err = tty_register_driver(pc_driver); + if (err) { + printk(KERN_ERR "Couldn't register Digi PC/ driver"); + goto out3; + } + + err = tty_register_driver(pc_info); + if (err) { + printk(KERN_ERR "Couldn't register Digi PC/ info "); + goto out4; + } + + /* Start up the poller to check for events on all enabled boards */ + init_timer(&epca_timer); + epca_timer.function = epcapoll; + mod_timer(&epca_timer, jiffies + HZ/25); + return 0; + +out4: + tty_unregister_driver(pc_driver); +out3: + put_tty_driver(pc_info); +out2: + put_tty_driver(pc_driver); +out1: + return err; +} + +static void post_fep_init(unsigned int crd) +{ + int i; + void __iomem *memaddr; + struct global_data __iomem *gd; + struct board_info *bd; + struct board_chan __iomem *bc; + struct channel *ch; + int shrinkmem = 0, lowwater; + + /* + * This call is made by the user via. the ioctl call DIGI_INIT. It is + * responsible for setting up all the card specific stuff. + */ + bd = &boards[crd]; + + /* + * If this is a PCI board, get the port info. Remember PCI cards do not + * have entries into the epcaconfig.h file, so we can't get the number + * of ports from it. Unfortunetly, this means that anyone doing a + * DIGI_GETINFO before the board has booted will get an invalid number + * of ports returned (It should return 0). Calls to DIGI_GETINFO after + * DIGI_INIT has been called will return the proper values. + */ + if (bd->type >= PCIXEM) { /* Begin get PCI number of ports */ + /* + * Below we use XEMPORTS as a memory offset regardless of which + * PCI card it is. This is because all of the supported PCI + * cards have the same memory offset for the channel data. This + * will have to be changed if we ever develop a PCI/XE card. + * NOTE : The FEP manual states that the port offset is 0xC22 + * as opposed to 0xC02. This is only true for PC/XE, and PC/XI + * cards; not for the XEM, or CX series. On the PCI cards the + * number of ports is determined by reading a ID PROM located + * in the box attached to the card. The card can then determine + * the index the id to determine the number of ports available. + * (FYI - The id should be located at 0x1ac (And may use up to + * 4 bytes if the box in question is a XEM or CX)). + */ + /* PCI cards are already remapped at this point ISA are not */ + bd->numports = readw(bd->re_map_membase + XEMPORTS); + epcaassert(bd->numports <= 64, "PCI returned a invalid number of ports"); + nbdevs += (bd->numports); + } else { + /* Fix up the mappings for ISA/EISA etc */ + /* FIXME: 64K - can we be smarter ? */ + bd->re_map_membase = ioremap_nocache(bd->membase, 0x10000); + } + + if (crd != 0) + card_ptr[crd] = card_ptr[crd-1] + boards[crd-1].numports; + else + card_ptr[crd] = &digi_channels[crd]; /* <- For card 0 only */ + + ch = card_ptr[crd]; + epcaassert(ch <= &digi_channels[nbdevs - 1], "ch out of range"); + + memaddr = bd->re_map_membase; + + /* + * The below assignment will set bc to point at the BEGINING of the + * cards channel structures. For 1 card there will be between 8 and 64 + * of these structures. + */ + bc = memaddr + CHANSTRUCT; + + /* + * The below assignment will set gd to point at the BEGINING of global + * memory address 0xc00. The first data in that global memory actually + * starts at address 0xc1a. The command in pointer begins at 0xd10. + */ + gd = memaddr + GLOBAL; + + /* + * XEPORTS (address 0xc22) points at the number of channels the card + * supports. (For 64XE, XI, XEM, and XR use 0xc02) + */ + if ((bd->type == PCXEVE || bd->type == PCXE) && + (readw(memaddr + XEPORTS) < 3)) + shrinkmem = 1; + if (bd->type < PCIXEM) + if (!request_region((int)bd->port, 4, board_desc[bd->type])) + return; + memwinon(bd, 0); + + /* + * Remember ch is the main drivers channels structure, while bc is the + * cards channel structure. + */ + for (i = 0; i < bd->numports; i++, ch++, bc++) { + unsigned long flags; + u16 tseg, rseg; + + tty_port_init(&ch->port); + ch->port.ops = &epca_port_ops; + ch->brdchan = bc; + ch->mailbox = gd; + INIT_WORK(&ch->tqueue, do_softint); + ch->board = &boards[crd]; + + spin_lock_irqsave(&epca_lock, flags); + switch (bd->type) { + /* + * Since some of the boards use different bitmaps for + * their control signals we cannot hard code these + * values and retain portability. We virtualize this + * data here. + */ + case EISAXEM: + case PCXEM: + case PCIXEM: + case PCIXRJ: + case PCIXR: + ch->m_rts = 0x02; + ch->m_dcd = 0x80; + ch->m_dsr = 0x20; + ch->m_cts = 0x10; + ch->m_ri = 0x40; + ch->m_dtr = 0x01; + break; + + case PCXE: + case PCXEVE: + case PCXI: + case PC64XE: + ch->m_rts = 0x02; + ch->m_dcd = 0x08; + ch->m_dsr = 0x10; + ch->m_cts = 0x20; + ch->m_ri = 0x40; + ch->m_dtr = 0x80; + break; + } + + if (boards[crd].altpin) { + ch->dsr = ch->m_dcd; + ch->dcd = ch->m_dsr; + ch->digiext.digi_flags |= DIGI_ALTPIN; + } else { + ch->dcd = ch->m_dcd; + ch->dsr = ch->m_dsr; + } + + ch->boardnum = crd; + ch->channelnum = i; + ch->magic = EPCA_MAGIC; + tty_port_tty_set(&ch->port, NULL); + + if (shrinkmem) { + fepcmd(ch, SETBUFFER, 32, 0, 0, 0); + shrinkmem = 0; + } + + tseg = readw(&bc->tseg); + rseg = readw(&bc->rseg); + + switch (bd->type) { + case PCIXEM: + case PCIXRJ: + case PCIXR: + /* Cover all the 2MEG cards */ + ch->txptr = memaddr + ((tseg << 4) & 0x1fffff); + ch->rxptr = memaddr + ((rseg << 4) & 0x1fffff); + ch->txwin = FEPWIN | (tseg >> 11); + ch->rxwin = FEPWIN | (rseg >> 11); + break; + + case PCXEM: + case EISAXEM: + /* Cover all the 32K windowed cards */ + /* Mask equal to window size - 1 */ + ch->txptr = memaddr + ((tseg << 4) & 0x7fff); + ch->rxptr = memaddr + ((rseg << 4) & 0x7fff); + ch->txwin = FEPWIN | (tseg >> 11); + ch->rxwin = FEPWIN | (rseg >> 11); + break; + + case PCXEVE: + case PCXE: + ch->txptr = memaddr + (((tseg - bd->memory_seg) << 4) + & 0x1fff); + ch->txwin = FEPWIN | ((tseg - bd->memory_seg) >> 9); + ch->rxptr = memaddr + (((rseg - bd->memory_seg) << 4) + & 0x1fff); + ch->rxwin = FEPWIN | ((rseg - bd->memory_seg) >> 9); + break; + + case PCXI: + case PC64XE: + ch->txptr = memaddr + ((tseg - bd->memory_seg) << 4); + ch->rxptr = memaddr + ((rseg - bd->memory_seg) << 4); + ch->txwin = ch->rxwin = 0; + break; + } + + ch->txbufhead = 0; + ch->txbufsize = readw(&bc->tmax) + 1; + + ch->rxbufhead = 0; + ch->rxbufsize = readw(&bc->rmax) + 1; + + lowwater = ch->txbufsize >= 2000 ? 1024 : (ch->txbufsize / 2); + + /* Set transmitter low water mark */ + fepcmd(ch, STXLWATER, lowwater, 0, 10, 0); + + /* Set receiver low water mark */ + fepcmd(ch, SRXLWATER, (ch->rxbufsize / 4), 0, 10, 0); + + /* Set receiver high water mark */ + fepcmd(ch, SRXHWATER, (3 * ch->rxbufsize / 4), 0, 10, 0); + + writew(100, &bc->edelay); + writeb(1, &bc->idata); + + ch->startc = readb(&bc->startc); + ch->stopc = readb(&bc->stopc); + ch->startca = readb(&bc->startca); + ch->stopca = readb(&bc->stopca); + + ch->fepcflag = 0; + ch->fepiflag = 0; + ch->fepoflag = 0; + ch->fepstartc = 0; + ch->fepstopc = 0; + ch->fepstartca = 0; + ch->fepstopca = 0; + + ch->port.close_delay = 50; + + spin_unlock_irqrestore(&epca_lock, flags); + } + + printk(KERN_INFO + "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", + VERSION, board_desc[bd->type], (long)bd->port, + (long)bd->membase, bd->numports); + memwinoff(bd, 0); +} + +static void epcapoll(unsigned long ignored) +{ + unsigned long flags; + int crd; + unsigned int head, tail; + struct channel *ch; + struct board_info *bd; + + /* + * This routine is called upon every timer interrupt. Even though the + * Digi series cards are capable of generating interrupts this method + * of non-looping polling is more efficient. This routine checks for + * card generated events (Such as receive data, are transmit buffer + * empty) and acts on those events. + */ + for (crd = 0; crd < num_cards; crd++) { + bd = &boards[crd]; + ch = card_ptr[crd]; + + if ((bd->status == DISABLED) || digi_poller_inhibited) + continue; + + /* + * assertmemoff is not needed here; indeed it is an empty + * subroutine. It is being kept because future boards may need + * this as well as some legacy boards. + */ + spin_lock_irqsave(&epca_lock, flags); + + assertmemoff(ch); + + globalwinon(ch); + + /* + * In this case head and tail actually refer to the event queue + * not the transmit or receive queue. + */ + head = readw(&ch->mailbox->ein); + tail = readw(&ch->mailbox->eout); + + /* If head isn't equal to tail we have an event */ + if (head != tail) + doevent(crd); + memoff(ch); + + spin_unlock_irqrestore(&epca_lock, flags); + } /* End for each card */ + mod_timer(&epca_timer, jiffies + (HZ / 25)); +} + +static void doevent(int crd) +{ + void __iomem *eventbuf; + struct channel *ch, *chan0; + static struct tty_struct *tty; + struct board_info *bd; + struct board_chan __iomem *bc; + unsigned int tail, head; + int event, channel; + int mstat, lstat; + + /* + * This subroutine is called by epcapoll when an event is detected + * in the event queue. This routine responds to those events. + */ + bd = &boards[crd]; + + chan0 = card_ptr[crd]; + epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range"); + assertgwinon(chan0); + while ((tail = readw(&chan0->mailbox->eout)) != + (head = readw(&chan0->mailbox->ein))) { + /* Begin while something in event queue */ + assertgwinon(chan0); + eventbuf = bd->re_map_membase + tail + ISTART; + /* Get the channel the event occurred on */ + channel = readb(eventbuf); + /* Get the actual event code that occurred */ + event = readb(eventbuf + 1); + /* + * The two assignments below get the current modem status + * (mstat) and the previous modem status (lstat). These are + * useful becuase an event could signal a change in modem + * signals itself. + */ + mstat = readb(eventbuf + 2); + lstat = readb(eventbuf + 3); + + ch = chan0 + channel; + if ((unsigned)channel >= bd->numports || !ch) { + if (channel >= bd->numports) + ch = chan0; + bc = ch->brdchan; + goto next; + } + + bc = ch->brdchan; + if (bc == NULL) + goto next; + + tty = tty_port_tty_get(&ch->port); + if (event & DATA_IND) { /* Begin DATA_IND */ + receive_data(ch, tty); + assertgwinon(ch); + } /* End DATA_IND */ + /* else *//* Fix for DCD transition missed bug */ + if (event & MODEMCHG_IND) { + /* A modem signal change has been indicated */ + ch->imodem = mstat; + if (test_bit(ASYNCB_CHECK_CD, &ch->port.flags)) { + /* We are now receiving dcd */ + if (mstat & ch->dcd) + wake_up_interruptible(&ch->port.open_wait); + else /* No dcd; hangup */ + pc_sched_event(ch, EPCA_EVENT_HANGUP); + } + } + if (tty) { + if (event & BREAK_IND) { + /* A break has been indicated */ + tty_insert_flip_char(tty, 0, TTY_BREAK); + tty_schedule_flip(tty); + } else if (event & LOWTX_IND) { + if (ch->statusflags & LOWWAIT) { + ch->statusflags &= ~LOWWAIT; + tty_wakeup(tty); + } + } else if (event & EMPTYTX_IND) { + /* This event is generated by + setup_empty_event */ + ch->statusflags &= ~TXBUSY; + if (ch->statusflags & EMPTYWAIT) { + ch->statusflags &= ~EMPTYWAIT; + tty_wakeup(tty); + } + } + tty_kref_put(tty); + } +next: + globalwinon(ch); + BUG_ON(!bc); + writew(1, &bc->idata); + writew((tail + 4) & (IMAX - ISTART - 4), &chan0->mailbox->eout); + globalwinon(chan0); + } /* End while something in event queue */ +} + +static void fepcmd(struct channel *ch, int cmd, int word_or_byte, + int byte2, int ncmds, int bytecmd) +{ + unchar __iomem *memaddr; + unsigned int head, cmdTail, cmdStart, cmdMax; + long count; + int n; + + /* This is the routine in which commands may be passed to the card. */ + + if (ch->board->status == DISABLED) + return; + assertgwinon(ch); + /* Remember head (As well as max) is just an offset not a base addr */ + head = readw(&ch->mailbox->cin); + /* cmdStart is a base address */ + cmdStart = readw(&ch->mailbox->cstart); + /* + * We do the addition below because we do not want a max pointer + * relative to cmdStart. We want a max pointer that points at the + * physical end of the command queue. + */ + cmdMax = (cmdStart + 4 + readw(&ch->mailbox->cmax)); + memaddr = ch->board->re_map_membase; + + if (head >= (cmdMax - cmdStart) || (head & 03)) { + printk(KERN_ERR "line %d: Out of range, cmd = %x, head = %x\n", + __LINE__, cmd, head); + printk(KERN_ERR "line %d: Out of range, cmdMax = %x, cmdStart = %x\n", + __LINE__, cmdMax, cmdStart); + return; + } + if (bytecmd) { + writeb(cmd, memaddr + head + cmdStart + 0); + writeb(ch->channelnum, memaddr + head + cmdStart + 1); + /* Below word_or_byte is bits to set */ + writeb(word_or_byte, memaddr + head + cmdStart + 2); + /* Below byte2 is bits to reset */ + writeb(byte2, memaddr + head + cmdStart + 3); + } else { + writeb(cmd, memaddr + head + cmdStart + 0); + writeb(ch->channelnum, memaddr + head + cmdStart + 1); + writeb(word_or_byte, memaddr + head + cmdStart + 2); + } + head = (head + 4) & (cmdMax - cmdStart - 4); + writew(head, &ch->mailbox->cin); + count = FEPTIMEOUT; + + for (;;) { + count--; + if (count == 0) { + printk(KERN_ERR " - Fep not responding in fepcmd()\n"); + return; + } + head = readw(&ch->mailbox->cin); + cmdTail = readw(&ch->mailbox->cout); + n = (head - cmdTail) & (cmdMax - cmdStart - 4); + /* + * Basically this will break when the FEP acknowledges the + * command by incrementing cmdTail (Making it equal to head). + */ + if (n <= ncmds * (sizeof(short) * 4)) + break; + } +} + +/* + * Digi products use fields in their channels structures that are very similar + * to the c_cflag and c_iflag fields typically found in UNIX termios + * structures. The below three routines allow mappings between these hardware + * "flags" and their respective Linux flags. + */ +static unsigned termios2digi_h(struct channel *ch, unsigned cflag) +{ + unsigned res = 0; + + if (cflag & CRTSCTS) { + ch->digiext.digi_flags |= (RTSPACE | CTSPACE); + res |= ((ch->m_cts) | (ch->m_rts)); + } + + if (ch->digiext.digi_flags & RTSPACE) + res |= ch->m_rts; + + if (ch->digiext.digi_flags & DTRPACE) + res |= ch->m_dtr; + + if (ch->digiext.digi_flags & CTSPACE) + res |= ch->m_cts; + + if (ch->digiext.digi_flags & DSRPACE) + res |= ch->dsr; + + if (ch->digiext.digi_flags & DCDPACE) + res |= ch->dcd; + + if (res & (ch->m_rts)) + ch->digiext.digi_flags |= RTSPACE; + + if (res & (ch->m_cts)) + ch->digiext.digi_flags |= CTSPACE; + + return res; +} + +static unsigned termios2digi_i(struct channel *ch, unsigned iflag) +{ + unsigned res = iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | + INPCK | ISTRIP | IXON | IXANY | IXOFF); + if (ch->digiext.digi_flags & DIGI_AIXON) + res |= IAIXON; + return res; +} + +static unsigned termios2digi_c(struct channel *ch, unsigned cflag) +{ + unsigned res = 0; + if (cflag & CBAUDEX) { + ch->digiext.digi_flags |= DIGI_FAST; + /* + * HUPCL bit is used by FEP to indicate fast baud table is to + * be used. + */ + res |= FEP_HUPCL; + } else + ch->digiext.digi_flags &= ~DIGI_FAST; + /* + * CBAUD has bit position 0x1000 set these days to indicate Linux + * baud rate remap. Digi hardware can't handle the bit assignment. + * (We use a different bit assignment for high speed.). Clear this + * bit out. + */ + res |= cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | CSTOPB | CSIZE); + /* + * This gets a little confusing. The Digi cards have their own + * representation of c_cflags controlling baud rate. For the most part + * this is identical to the Linux implementation. However; Digi + * supports one rate (76800) that Linux doesn't. This means that the + * c_cflag entry that would normally mean 76800 for Digi actually means + * 115200 under Linux. Without the below mapping, a stty 115200 would + * only drive the board at 76800. Since the rate 230400 is also found + * after 76800, the same problem afflicts us when we choose a rate of + * 230400. Without the below modificiation stty 230400 would actually + * give us 115200. + * + * There are two additional differences. The Linux value for CLOCAL + * (0x800; 0004000) has no meaning to the Digi hardware. Also in later + * releases of Linux; the CBAUD define has CBAUDEX (0x1000; 0010000) + * ored into it (CBAUD = 0x100f as opposed to 0xf). CBAUDEX should be + * checked for a screened out prior to termios2digi_c returning. Since + * CLOCAL isn't used by the board this can be ignored as long as the + * returned value is used only by Digi hardware. + */ + if (cflag & CBAUDEX) { + /* + * The below code is trying to guarantee that only baud rates + * 115200 and 230400 are remapped. We use exclusive or because + * the various baud rates share common bit positions and + * therefore can't be tested for easily. + */ + if ((!((cflag & 0x7) ^ (B115200 & ~CBAUDEX))) || + (!((cflag & 0x7) ^ (B230400 & ~CBAUDEX)))) + res += 1; + } + return res; +} + +/* Caller must hold the locks */ +static void epcaparam(struct tty_struct *tty, struct channel *ch) +{ + unsigned int cmdHead; + struct ktermios *ts; + struct board_chan __iomem *bc; + unsigned mval, hflow, cflag, iflag; + + bc = ch->brdchan; + epcaassert(bc != NULL, "bc out of range"); + + assertgwinon(ch); + ts = tty->termios; + if ((ts->c_cflag & CBAUD) == 0) { /* Begin CBAUD detected */ + cmdHead = readw(&bc->rin); + writew(cmdHead, &bc->rout); + cmdHead = readw(&bc->tin); + /* Changing baud in mid-stream transmission can be wonderful */ + /* + * Flush current transmit buffer by setting cmdTail pointer + * (tout) to cmdHead pointer (tin). Hopefully the transmit + * buffer is empty. + */ + fepcmd(ch, STOUT, (unsigned) cmdHead, 0, 0, 0); + mval = 0; + } else { /* Begin CBAUD not detected */ + /* + * c_cflags have changed but that change had nothing to do with + * BAUD. Propagate the change to the card. + */ + cflag = termios2digi_c(ch, ts->c_cflag); + if (cflag != ch->fepcflag) { + ch->fepcflag = cflag; + /* Set baud rate, char size, stop bits, parity */ + fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0); + } + /* + * If the user has not forced CLOCAL and if the device is not a + * CALLOUT device (Which is always CLOCAL) we set flags such + * that the driver will wait on carrier detect. + */ + if (ts->c_cflag & CLOCAL) + clear_bit(ASYNCB_CHECK_CD, &ch->port.flags); + else + set_bit(ASYNCB_CHECK_CD, &ch->port.flags); + mval = ch->m_dtr | ch->m_rts; + } /* End CBAUD not detected */ + iflag = termios2digi_i(ch, ts->c_iflag); + /* Check input mode flags */ + if (iflag != ch->fepiflag) { + ch->fepiflag = iflag; + /* + * Command sets channels iflag structure on the board. Such + * things as input soft flow control, handling of parity + * errors, and break handling are all set here. + * + * break handling, parity handling, input stripping, + * flow control chars + */ + fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0); + } + /* + * Set the board mint value for this channel. This will cause hardware + * events to be generated each time the DCD signal (Described in mint) + * changes. + */ + writeb(ch->dcd, &bc->mint); + if ((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD)) + if (ch->digiext.digi_flags & DIGI_FORCEDCD) + writeb(0, &bc->mint); + ch->imodem = readb(&bc->mstat); + hflow = termios2digi_h(ch, ts->c_cflag); + if (hflow != ch->hflow) { + ch->hflow = hflow; + /* + * Hard flow control has been selected but the board is not + * using it. Activate hard flow control now. + */ + fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1); + } + mval ^= ch->modemfake & (mval ^ ch->modem); + + if (ch->omodem ^ mval) { + ch->omodem = mval; + /* + * The below command sets the DTR and RTS mstat structure. If + * hard flow control is NOT active these changes will drive the + * output of the actual DTR and RTS lines. If hard flow control + * is active, the changes will be saved in the mstat structure + * and only asserted when hard flow control is turned off. + */ + + /* First reset DTR & RTS; then set them */ + fepcmd(ch, SETMODEM, 0, ((ch->m_dtr)|(ch->m_rts)), 0, 1); + fepcmd(ch, SETMODEM, mval, 0, 0, 1); + } + if (ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc) { + ch->fepstartc = ch->startc; + ch->fepstopc = ch->stopc; + /* + * The XON / XOFF characters have changed; propagate these + * changes to the card. + */ + fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); + } + if (ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca) { + ch->fepstartca = ch->startca; + ch->fepstopca = ch->stopca; + /* + * Similar to the above, this time the auxilarly XON / XOFF + * characters have changed; propagate these changes to the card. + */ + fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); + } +} + +/* Caller holds lock */ +static void receive_data(struct channel *ch, struct tty_struct *tty) +{ + unchar *rptr; + struct ktermios *ts = NULL; + struct board_chan __iomem *bc; + int dataToRead, wrapgap, bytesAvailable; + unsigned int tail, head; + unsigned int wrapmask; + + /* + * This routine is called by doint when a receive data event has taken + * place. + */ + globalwinon(ch); + if (ch->statusflags & RXSTOPPED) + return; + if (tty) + ts = tty->termios; + bc = ch->brdchan; + BUG_ON(!bc); + wrapmask = ch->rxbufsize - 1; + + /* + * Get the head and tail pointers to the receiver queue. Wrap the head + * pointer if it has reached the end of the buffer. + */ + head = readw(&bc->rin); + head &= wrapmask; + tail = readw(&bc->rout) & wrapmask; + + bytesAvailable = (head - tail) & wrapmask; + if (bytesAvailable == 0) + return; + + /* If CREAD bit is off or device not open, set TX tail to head */ + if (!tty || !ts || !(ts->c_cflag & CREAD)) { + writew(head, &bc->rout); + return; + } + + if (tty_buffer_request_room(tty, bytesAvailable + 1) == 0) + return; + + if (readb(&bc->orun)) { + writeb(0, &bc->orun); + printk(KERN_WARNING "epca; overrun! DigiBoard device %s\n", + tty->name); + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + rxwinon(ch); + while (bytesAvailable > 0) { + /* Begin while there is data on the card */ + wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail; + /* + * Even if head has wrapped around only report the amount of + * data to be equal to the size - tail. Remember memcpy can't + * automaticly wrap around the receive buffer. + */ + dataToRead = (wrapgap < bytesAvailable) ? wrapgap + : bytesAvailable; + /* Make sure we don't overflow the buffer */ + dataToRead = tty_prepare_flip_string(tty, &rptr, dataToRead); + if (dataToRead == 0) + break; + /* + * Move data read from our card into the line disciplines + * buffer for translation if necessary. + */ + memcpy_fromio(rptr, ch->rxptr + tail, dataToRead); + tail = (tail + dataToRead) & wrapmask; + bytesAvailable -= dataToRead; + } /* End while there is data on the card */ + globalwinon(ch); + writew(tail, &bc->rout); + /* Must be called with global data */ + tty_schedule_flip(tty); +} + +static int info_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case DIGI_GETINFO: + { + struct digi_info di; + int brd; + + if (get_user(brd, (unsigned int __user *)arg)) + return -EFAULT; + if (brd < 0 || brd >= num_cards || num_cards == 0) + return -ENODEV; + + memset(&di, 0, sizeof(di)); + + di.board = brd; + di.status = boards[brd].status; + di.type = boards[brd].type ; + di.numports = boards[brd].numports ; + /* Legacy fixups - just move along nothing to see */ + di.port = (unsigned char *)boards[brd].port ; + di.membase = (unsigned char *)boards[brd].membase ; + + if (copy_to_user((void __user *)arg, &di, sizeof(di))) + return -EFAULT; + break; + + } + + case DIGI_POLLER: + { + int brd = arg & 0xff000000 >> 16; + unsigned char state = arg & 0xff; + + if (brd < 0 || brd >= num_cards) { + printk(KERN_ERR "epca: DIGI POLLER : brd not valid!\n"); + return -ENODEV; + } + digi_poller_inhibited = state; + break; + } + + case DIGI_INIT: + { + /* + * This call is made by the apps to complete the + * initialization of the board(s). This routine is + * responsible for setting the card to its initial + * state and setting the drivers control fields to the + * sutianle settings for the card in question. + */ + int crd; + for (crd = 0; crd < num_cards; crd++) + post_fep_init(crd); + break; + } + default: + return -ENOTTY; + } + return 0; +} + +static int pc_tiocmget(struct tty_struct *tty) +{ + struct channel *ch = tty->driver_data; + struct board_chan __iomem *bc; + unsigned int mstat, mflag = 0; + unsigned long flags; + + if (ch) + bc = ch->brdchan; + else + return -EINVAL; + + spin_lock_irqsave(&epca_lock, flags); + globalwinon(ch); + mstat = readb(&bc->mstat); + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + + if (mstat & ch->m_dtr) + mflag |= TIOCM_DTR; + if (mstat & ch->m_rts) + mflag |= TIOCM_RTS; + if (mstat & ch->m_cts) + mflag |= TIOCM_CTS; + if (mstat & ch->dsr) + mflag |= TIOCM_DSR; + if (mstat & ch->m_ri) + mflag |= TIOCM_RI; + if (mstat & ch->dcd) + mflag |= TIOCM_CD; + return mflag; +} + +static int pc_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct channel *ch = tty->driver_data; + unsigned long flags; + + if (!ch) + return -EINVAL; + + spin_lock_irqsave(&epca_lock, flags); + /* + * I think this modemfake stuff is broken. It doesn't correctly reflect + * the behaviour desired by the TIOCM* ioctls. Therefore this is + * probably broken. + */ + if (set & TIOCM_RTS) { + ch->modemfake |= ch->m_rts; + ch->modem |= ch->m_rts; + } + if (set & TIOCM_DTR) { + ch->modemfake |= ch->m_dtr; + ch->modem |= ch->m_dtr; + } + if (clear & TIOCM_RTS) { + ch->modemfake |= ch->m_rts; + ch->modem &= ~ch->m_rts; + } + if (clear & TIOCM_DTR) { + ch->modemfake |= ch->m_dtr; + ch->modem &= ~ch->m_dtr; + } + globalwinon(ch); + /* + * The below routine generally sets up parity, baud, flow control + * issues, etc.... It effect both control flags and input flags. + */ + epcaparam(tty, ch); + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + return 0; +} + +static int pc_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + digiflow_t dflow; + unsigned long flags; + unsigned int mflag, mstat; + unsigned char startc, stopc; + struct board_chan __iomem *bc; + struct channel *ch = tty->driver_data; + void __user *argp = (void __user *)arg; + + if (ch) + bc = ch->brdchan; + else + return -EINVAL; + switch (cmd) { + case TIOCMODG: + mflag = pc_tiocmget(tty); + if (put_user(mflag, (unsigned long __user *)argp)) + return -EFAULT; + break; + case TIOCMODS: + if (get_user(mstat, (unsigned __user *)argp)) + return -EFAULT; + return pc_tiocmset(tty, mstat, ~mstat); + case TIOCSDTR: + spin_lock_irqsave(&epca_lock, flags); + ch->omodem |= ch->m_dtr; + globalwinon(ch); + fepcmd(ch, SETMODEM, ch->m_dtr, 0, 10, 1); + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + break; + + case TIOCCDTR: + spin_lock_irqsave(&epca_lock, flags); + ch->omodem &= ~ch->m_dtr; + globalwinon(ch); + fepcmd(ch, SETMODEM, 0, ch->m_dtr, 10, 1); + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + break; + case DIGI_GETA: + if (copy_to_user(argp, &ch->digiext, sizeof(digi_t))) + return -EFAULT; + break; + case DIGI_SETAW: + case DIGI_SETAF: + if (cmd == DIGI_SETAW) { + /* Setup an event to indicate when the transmit + buffer empties */ + spin_lock_irqsave(&epca_lock, flags); + setup_empty_event(tty, ch); + spin_unlock_irqrestore(&epca_lock, flags); + tty_wait_until_sent(tty, 0); + } else { + /* ldisc lock already held in ioctl */ + if (tty->ldisc->ops->flush_buffer) + tty->ldisc->ops->flush_buffer(tty); + } + /* Fall Thru */ + case DIGI_SETA: + if (copy_from_user(&ch->digiext, argp, sizeof(digi_t))) + return -EFAULT; + + if (ch->digiext.digi_flags & DIGI_ALTPIN) { + ch->dcd = ch->m_dsr; + ch->dsr = ch->m_dcd; + } else { + ch->dcd = ch->m_dcd; + ch->dsr = ch->m_dsr; + } + + spin_lock_irqsave(&epca_lock, flags); + globalwinon(ch); + + /* + * The below routine generally sets up parity, baud, flow + * control issues, etc.... It effect both control flags and + * input flags. + */ + epcaparam(tty, ch); + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + break; + + case DIGI_GETFLOW: + case DIGI_GETAFLOW: + spin_lock_irqsave(&epca_lock, flags); + globalwinon(ch); + if (cmd == DIGI_GETFLOW) { + dflow.startc = readb(&bc->startc); + dflow.stopc = readb(&bc->stopc); + } else { + dflow.startc = readb(&bc->startca); + dflow.stopc = readb(&bc->stopca); + } + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + + if (copy_to_user(argp, &dflow, sizeof(dflow))) + return -EFAULT; + break; + + case DIGI_SETAFLOW: + case DIGI_SETFLOW: + if (cmd == DIGI_SETFLOW) { + startc = ch->startc; + stopc = ch->stopc; + } else { + startc = ch->startca; + stopc = ch->stopca; + } + + if (copy_from_user(&dflow, argp, sizeof(dflow))) + return -EFAULT; + + if (dflow.startc != startc || dflow.stopc != stopc) { + /* Begin if setflow toggled */ + spin_lock_irqsave(&epca_lock, flags); + globalwinon(ch); + + if (cmd == DIGI_SETFLOW) { + ch->fepstartc = ch->startc = dflow.startc; + ch->fepstopc = ch->stopc = dflow.stopc; + fepcmd(ch, SONOFFC, ch->fepstartc, + ch->fepstopc, 0, 1); + } else { + ch->fepstartca = ch->startca = dflow.startc; + ch->fepstopca = ch->stopca = dflow.stopc; + fepcmd(ch, SAUXONOFFC, ch->fepstartca, + ch->fepstopca, 0, 1); + } + + if (ch->statusflags & TXSTOPPED) + pc_start(tty); + + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + } /* End if setflow toggled */ + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void pc_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + struct channel *ch; + unsigned long flags; + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + + if (ch != NULL) { /* Begin if channel valid */ + spin_lock_irqsave(&epca_lock, flags); + globalwinon(ch); + epcaparam(tty, ch); + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + + if ((old_termios->c_cflag & CRTSCTS) && + ((tty->termios->c_cflag & CRTSCTS) == 0)) + tty->hw_stopped = 0; + + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&ch->port.open_wait); + + } /* End if channel valid */ +} + +static void do_softint(struct work_struct *work) +{ + struct channel *ch = container_of(work, struct channel, tqueue); + /* Called in response to a modem change event */ + if (ch && ch->magic == EPCA_MAGIC) { + struct tty_struct *tty = tty_port_tty_get(&ch->port); + + if (tty && tty->driver_data) { + if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) { + tty_hangup(tty); + wake_up_interruptible(&ch->port.open_wait); + clear_bit(ASYNCB_NORMAL_ACTIVE, + &ch->port.flags); + } + } + tty_kref_put(tty); + } +} + +/* + * pc_stop and pc_start provide software flow control to the routine and the + * pc_ioctl routine. + */ +static void pc_stop(struct tty_struct *tty) +{ + struct channel *ch; + unsigned long flags; + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + if (ch != NULL) { + spin_lock_irqsave(&epca_lock, flags); + if ((ch->statusflags & TXSTOPPED) == 0) { + /* Begin if transmit stop requested */ + globalwinon(ch); + /* STOP transmitting now !! */ + fepcmd(ch, PAUSETX, 0, 0, 0, 0); + ch->statusflags |= TXSTOPPED; + memoff(ch); + } /* End if transmit stop requested */ + spin_unlock_irqrestore(&epca_lock, flags); + } +} + +static void pc_start(struct tty_struct *tty) +{ + struct channel *ch; + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + if (ch != NULL) { + unsigned long flags; + spin_lock_irqsave(&epca_lock, flags); + /* Just in case output was resumed because of a change + in Digi-flow */ + if (ch->statusflags & TXSTOPPED) { + /* Begin transmit resume requested */ + struct board_chan __iomem *bc; + globalwinon(ch); + bc = ch->brdchan; + if (ch->statusflags & LOWWAIT) + writeb(1, &bc->ilow); + /* Okay, you can start transmitting again... */ + fepcmd(ch, RESUMETX, 0, 0, 0, 0); + ch->statusflags &= ~TXSTOPPED; + memoff(ch); + } /* End transmit resume requested */ + spin_unlock_irqrestore(&epca_lock, flags); + } +} + +/* + * The below routines pc_throttle and pc_unthrottle are used to slow (And + * resume) the receipt of data into the kernels receive buffers. The exact + * occurrence of this depends on the size of the kernels receive buffer and + * what the 'watermarks' are set to for that buffer. See the n_ttys.c file for + * more details. + */ +static void pc_throttle(struct tty_struct *tty) +{ + struct channel *ch; + unsigned long flags; + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + if (ch != NULL) { + spin_lock_irqsave(&epca_lock, flags); + if ((ch->statusflags & RXSTOPPED) == 0) { + globalwinon(ch); + fepcmd(ch, PAUSERX, 0, 0, 0, 0); + ch->statusflags |= RXSTOPPED; + memoff(ch); + } + spin_unlock_irqrestore(&epca_lock, flags); + } +} + +static void pc_unthrottle(struct tty_struct *tty) +{ + struct channel *ch; + unsigned long flags; + /* + * verifyChannel returns the channel from the tty struct if it is + * valid. This serves as a sanity check. + */ + ch = verifyChannel(tty); + if (ch != NULL) { + /* Just in case output was resumed because of a change + in Digi-flow */ + spin_lock_irqsave(&epca_lock, flags); + if (ch->statusflags & RXSTOPPED) { + globalwinon(ch); + fepcmd(ch, RESUMERX, 0, 0, 0, 0); + ch->statusflags &= ~RXSTOPPED; + memoff(ch); + } + spin_unlock_irqrestore(&epca_lock, flags); + } +} + +static int pc_send_break(struct tty_struct *tty, int msec) +{ + struct channel *ch = tty->driver_data; + unsigned long flags; + + if (msec == -1) + msec = 0xFFFF; + else if (msec > 0xFFFE) + msec = 0xFFFE; + else if (msec < 1) + msec = 1; + + spin_lock_irqsave(&epca_lock, flags); + globalwinon(ch); + /* + * Maybe I should send an infinite break here, schedule() for msec + * amount of time, and then stop the break. This way, the user can't + * screw up the FEP by causing digi_send_break() to be called (i.e. via + * an ioctl()) more than once in msec amount of time. + * Try this for now... + */ + fepcmd(ch, SENDBREAK, msec, 0, 10, 0); + memoff(ch); + spin_unlock_irqrestore(&epca_lock, flags); + return 0; +} + +/* Caller MUST hold the lock */ +static void setup_empty_event(struct tty_struct *tty, struct channel *ch) +{ + struct board_chan __iomem *bc = ch->brdchan; + + globalwinon(ch); + ch->statusflags |= EMPTYWAIT; + /* + * When set the iempty flag request a event to be generated when the + * transmit buffer is empty (If there is no BREAK in progress). + */ + writeb(1, &bc->iempty); + memoff(ch); +} + +#ifndef MODULE +static void __init epca_setup(char *str, int *ints) +{ + struct board_info board; + int index, loop, last; + char *temp, *t2; + unsigned len; + + /* + * If this routine looks a little strange it is because it is only + * called if a LILO append command is given to boot the kernel with + * parameters. In this way, we can provide the user a method of + * changing his board configuration without rebuilding the kernel. + */ + if (!liloconfig) + liloconfig = 1; + + memset(&board, 0, sizeof(board)); + + /* Assume the data is int first, later we can change it */ + /* I think that array position 0 of ints holds the number of args */ + for (last = 0, index = 1; index <= ints[0]; index++) + switch (index) { /* Begin parse switch */ + case 1: + board.status = ints[index]; + /* + * We check for 2 (As opposed to 1; because 2 is a flag + * instructing the driver to ignore epcaconfig.) For + * this reason we check for 2. + */ + if (board.status == 2) { + /* Begin ignore epcaconfig as well as lilo cmd line */ + nbdevs = 0; + num_cards = 0; + return; + } /* End ignore epcaconfig as well as lilo cmd line */ + + if (board.status > 2) { + printk(KERN_ERR "epca_setup: Invalid board status 0x%x\n", + board.status); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_STATUS; + return; + } + last = index; + break; + case 2: + board.type = ints[index]; + if (board.type >= PCIXEM) { + printk(KERN_ERR "epca_setup: Invalid board type 0x%x\n", board.type); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_TYPE; + return; + } + last = index; + break; + case 3: + board.altpin = ints[index]; + if (board.altpin > 1) { + printk(KERN_ERR "epca_setup: Invalid board altpin 0x%x\n", board.altpin); + invalid_lilo_config = 1; + setup_error_code |= INVALID_ALTPIN; + return; + } + last = index; + break; + + case 4: + board.numports = ints[index]; + if (board.numports < 2 || board.numports > 256) { + printk(KERN_ERR "epca_setup: Invalid board numports 0x%x\n", board.numports); + invalid_lilo_config = 1; + setup_error_code |= INVALID_NUM_PORTS; + return; + } + nbdevs += board.numports; + last = index; + break; + + case 5: + board.port = ints[index]; + if (ints[index] <= 0) { + printk(KERN_ERR "epca_setup: Invalid io port 0x%x\n", (unsigned int)board.port); + invalid_lilo_config = 1; + setup_error_code |= INVALID_PORT_BASE; + return; + } + last = index; + break; + + case 6: + board.membase = ints[index]; + if (ints[index] <= 0) { + printk(KERN_ERR "epca_setup: Invalid memory base 0x%x\n", + (unsigned int)board.membase); + invalid_lilo_config = 1; + setup_error_code |= INVALID_MEM_BASE; + return; + } + last = index; + break; + + default: + printk(KERN_ERR " - epca_setup: Too many integer parms\n"); + return; + + } /* End parse switch */ + + while (str && *str) { /* Begin while there is a string arg */ + /* find the next comma or terminator */ + temp = str; + /* While string is not null, and a comma hasn't been found */ + while (*temp && (*temp != ',')) + temp++; + if (!*temp) + temp = NULL; + else + *temp++ = 0; + /* Set index to the number of args + 1 */ + index = last + 1; + + switch (index) { + case 1: + len = strlen(str); + if (strncmp("Disable", str, len) == 0) + board.status = 0; + else if (strncmp("Enable", str, len) == 0) + board.status = 1; + else { + printk(KERN_ERR "epca_setup: Invalid status %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_STATUS; + return; + } + last = index; + break; + + case 2: + for (loop = 0; loop < EPCA_NUM_TYPES; loop++) + if (strcmp(board_desc[loop], str) == 0) + break; + /* + * If the index incremented above refers to a + * legitamate board type set it here. + */ + if (index < EPCA_NUM_TYPES) + board.type = loop; + else { + printk(KERN_ERR "epca_setup: Invalid board type: %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_TYPE; + return; + } + last = index; + break; + + case 3: + len = strlen(str); + if (strncmp("Disable", str, len) == 0) + board.altpin = 0; + else if (strncmp("Enable", str, len) == 0) + board.altpin = 1; + else { + printk(KERN_ERR "epca_setup: Invalid altpin %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_ALTPIN; + return; + } + last = index; + break; + + case 4: + t2 = str; + while (isdigit(*t2)) + t2++; + + if (*t2) { + printk(KERN_ERR "epca_setup: Invalid port count %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_NUM_PORTS; + return; + } + + /* + * There is not a man page for simple_strtoul but the + * code can be found in vsprintf.c. The first argument + * is the string to translate (To an unsigned long + * obviously), the second argument can be the address + * of any character variable or a NULL. If a variable + * is given, the end pointer of the string will be + * stored in that variable; if a NULL is given the end + * pointer will not be returned. The last argument is + * the base to use. If a 0 is indicated, the routine + * will attempt to determine the proper base by looking + * at the values prefix (A '0' for octal, a 'x' for + * hex, etc ... If a value is given it will use that + * value as the base. + */ + board.numports = simple_strtoul(str, NULL, 0); + nbdevs += board.numports; + last = index; + break; + + case 5: + t2 = str; + while (isxdigit(*t2)) + t2++; + + if (*t2) { + printk(KERN_ERR "epca_setup: Invalid i/o address %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_PORT_BASE; + return; + } + + board.port = simple_strtoul(str, NULL, 16); + last = index; + break; + + case 6: + t2 = str; + while (isxdigit(*t2)) + t2++; + + if (*t2) { + printk(KERN_ERR "epca_setup: Invalid memory base %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_MEM_BASE; + return; + } + board.membase = simple_strtoul(str, NULL, 16); + last = index; + break; + default: + printk(KERN_ERR "epca: Too many string parms\n"); + return; + } + str = temp; + } /* End while there is a string arg */ + + if (last < 6) { + printk(KERN_ERR "epca: Insufficient parms specified\n"); + return; + } + + /* I should REALLY validate the stuff here */ + /* Copies our local copy of board into boards */ + memcpy((void *)&boards[num_cards], (void *)&board, sizeof(board)); + /* Does this get called once per lilo arg are what ? */ + printk(KERN_INFO "PC/Xx: Added board %i, %s %i ports at 0x%4.4X base 0x%6.6X\n", + num_cards, board_desc[board.type], + board.numports, (int)board.port, (unsigned int) board.membase); + num_cards++; +} + +static int __init epca_real_setup(char *str) +{ + int ints[11]; + + epca_setup(get_options(str, 11, ints), ints); + return 1; +} + +__setup("digiepca", epca_real_setup); +#endif + +enum epic_board_types { + brd_xr = 0, + brd_xem, + brd_cx, + brd_xrj, +}; + +/* indexed directly by epic_board_types enum */ +static struct { + unsigned char board_type; + unsigned bar_idx; /* PCI base address region */ +} epca_info_tbl[] = { + { PCIXR, 0, }, + { PCIXEM, 0, }, + { PCICX, 0, }, + { PCIXRJ, 2, }, +}; + +static int __devinit epca_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + static int board_num = -1; + int board_idx, info_idx = ent->driver_data; + unsigned long addr; + + if (pci_enable_device(pdev)) + return -EIO; + + board_num++; + board_idx = board_num + num_cards; + if (board_idx >= MAXBOARDS) + goto err_out; + + addr = pci_resource_start(pdev, epca_info_tbl[info_idx].bar_idx); + if (!addr) { + printk(KERN_ERR PFX "PCI region #%d not available (size 0)\n", + epca_info_tbl[info_idx].bar_idx); + goto err_out; + } + + boards[board_idx].status = ENABLED; + boards[board_idx].type = epca_info_tbl[info_idx].board_type; + boards[board_idx].numports = 0x0; + boards[board_idx].port = addr + PCI_IO_OFFSET; + boards[board_idx].membase = addr; + + if (!request_mem_region(addr + PCI_IO_OFFSET, 0x200000, "epca")) { + printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n", + 0x200000, addr + PCI_IO_OFFSET); + goto err_out; + } + + boards[board_idx].re_map_port = ioremap_nocache(addr + PCI_IO_OFFSET, + 0x200000); + if (!boards[board_idx].re_map_port) { + printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n", + 0x200000, addr + PCI_IO_OFFSET); + goto err_out_free_pciio; + } + + if (!request_mem_region(addr, 0x200000, "epca")) { + printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n", + 0x200000, addr); + goto err_out_free_iounmap; + } + + boards[board_idx].re_map_membase = ioremap_nocache(addr, 0x200000); + if (!boards[board_idx].re_map_membase) { + printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n", + 0x200000, addr + PCI_IO_OFFSET); + goto err_out_free_memregion; + } + + /* + * I don't know what the below does, but the hardware guys say its + * required on everything except PLX (In this case XRJ). + */ + if (info_idx != brd_xrj) { + pci_write_config_byte(pdev, 0x40, 0); + pci_write_config_byte(pdev, 0x46, 0); + } + + return 0; + +err_out_free_memregion: + release_mem_region(addr, 0x200000); +err_out_free_iounmap: + iounmap(boards[board_idx].re_map_port); +err_out_free_pciio: + release_mem_region(addr + PCI_IO_OFFSET, 0x200000); +err_out: + return -ENODEV; +} + + +static struct pci_device_id epca_pci_tbl[] = { + { PCI_VENDOR_DIGI, PCI_DEVICE_XR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xr }, + { PCI_VENDOR_DIGI, PCI_DEVICE_XEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xem }, + { PCI_VENDOR_DIGI, PCI_DEVICE_CX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_cx }, + { PCI_VENDOR_DIGI, PCI_DEVICE_XRJ, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xrj }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, epca_pci_tbl); + +static int __init init_PCI(void) +{ + memset(&epca_driver, 0, sizeof(epca_driver)); + epca_driver.name = "epca"; + epca_driver.id_table = epca_pci_tbl; + epca_driver.probe = epca_init_one; + + return pci_register_driver(&epca_driver); +} + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/tty/epca.h b/drivers/staging/tty/epca.h new file mode 100644 index 000000000000..d414bf2dbf7c --- /dev/null +++ b/drivers/staging/tty/epca.h @@ -0,0 +1,158 @@ +#define XEMPORTS 0xC02 +#define XEPORTS 0xC22 + +#define MAX_ALLOC 0x100 + +#define MAXBOARDS 12 +#define FEPCODESEG 0x0200L +#define FEPCODE 0x2000L +#define BIOSCODE 0xf800L + +#define MISCGLOBAL 0x0C00L +#define NPORT 0x0C22L +#define MBOX 0x0C40L +#define PORTBASE 0x0C90L + +/* Begin code defines used for epca_setup */ + +#define INVALID_BOARD_TYPE 0x1 +#define INVALID_NUM_PORTS 0x2 +#define INVALID_MEM_BASE 0x4 +#define INVALID_PORT_BASE 0x8 +#define INVALID_BOARD_STATUS 0x10 +#define INVALID_ALTPIN 0x20 + +/* End code defines used for epca_setup */ + + +#define FEPCLR 0x00 +#define FEPMEM 0x02 +#define FEPRST 0x04 +#define FEPINT 0x08 +#define FEPMASK 0x0e +#define FEPWIN 0x80 + +#define PCXE 0 +#define PCXEVE 1 +#define PCXEM 2 +#define EISAXEM 3 +#define PC64XE 4 +#define PCXI 5 +#define PCIXEM 7 +#define PCICX 8 +#define PCIXR 9 +#define PCIXRJ 10 +#define EPCA_NUM_TYPES 6 + + +static char *board_desc[] = +{ + "PC/Xe", + "PC/Xeve", + "PC/Xem", + "EISA/Xem", + "PC/64Xe", + "PC/Xi", + "unknown", + "PCI/Xem", + "PCI/CX", + "PCI/Xr", + "PCI/Xrj", +}; + +#define STARTC 021 +#define STOPC 023 +#define IAIXON 0x2000 + + +#define TXSTOPPED 0x1 +#define LOWWAIT 0x2 +#define EMPTYWAIT 0x4 +#define RXSTOPPED 0x8 +#define TXBUSY 0x10 + +#define DISABLED 0 +#define ENABLED 1 +#define OFF 0 +#define ON 1 + +#define FEPTIMEOUT 200000 +#define SERIAL_TYPE_INFO 3 +#define EPCA_EVENT_HANGUP 1 +#define EPCA_MAGIC 0x5c6df104L + +struct channel +{ + long magic; + struct tty_port port; + unsigned char boardnum; + unsigned char channelnum; + unsigned char omodem; /* FEP output modem status */ + unsigned char imodem; /* FEP input modem status */ + unsigned char modemfake; /* Modem values to be forced */ + unsigned char modem; /* Force values */ + unsigned char hflow; + unsigned char dsr; + unsigned char dcd; + unsigned char m_rts ; /* The bits used in whatever FEP */ + unsigned char m_dcd ; /* is indiginous to this board to */ + unsigned char m_dsr ; /* represent each of the physical */ + unsigned char m_cts ; /* handshake lines */ + unsigned char m_ri ; + unsigned char m_dtr ; + unsigned char stopc; + unsigned char startc; + unsigned char stopca; + unsigned char startca; + unsigned char fepstopc; + unsigned char fepstartc; + unsigned char fepstopca; + unsigned char fepstartca; + unsigned char txwin; + unsigned char rxwin; + unsigned short fepiflag; + unsigned short fepcflag; + unsigned short fepoflag; + unsigned short txbufhead; + unsigned short txbufsize; + unsigned short rxbufhead; + unsigned short rxbufsize; + int close_delay; + unsigned long event; + uint dev; + unsigned long statusflags; + unsigned long c_iflag; + unsigned long c_cflag; + unsigned long c_lflag; + unsigned long c_oflag; + unsigned char __iomem *txptr; + unsigned char __iomem *rxptr; + struct board_info *board; + struct board_chan __iomem *brdchan; + struct digi_struct digiext; + struct work_struct tqueue; + struct global_data __iomem *mailbox; +}; + +struct board_info +{ + unsigned char status; + unsigned char type; + unsigned char altpin; + unsigned short numports; + unsigned long port; + unsigned long membase; + void __iomem *re_map_port; + void __iomem *re_map_membase; + unsigned long memory_seg; + void ( * memwinon ) (struct board_info *, unsigned int) ; + void ( * memwinoff ) (struct board_info *, unsigned int) ; + void ( * globalwinon ) (struct channel *) ; + void ( * txwinon ) (struct channel *) ; + void ( * rxwinon ) (struct channel *) ; + void ( * memoff ) (struct channel *) ; + void ( * assertgwinon ) (struct channel *) ; + void ( * assertmemoff ) (struct channel *) ; + unsigned char poller_inhibited ; +}; + diff --git a/drivers/staging/tty/epcaconfig.h b/drivers/staging/tty/epcaconfig.h new file mode 100644 index 000000000000..55dec067078e --- /dev/null +++ b/drivers/staging/tty/epcaconfig.h @@ -0,0 +1,7 @@ +#define NUMCARDS 0 +#define NBDEVS 0 + +struct board_info static_boards[NUMCARDS]={ +}; + +/* DO NOT HAND EDIT THIS FILE! */ diff --git a/drivers/staging/tty/ip2/Makefile b/drivers/staging/tty/ip2/Makefile new file mode 100644 index 000000000000..7b78e0dfc5b0 --- /dev/null +++ b/drivers/staging/tty/ip2/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the Computone IntelliPort Plus Driver +# + +obj-$(CONFIG_COMPUTONE) += ip2.o + +ip2-y := ip2main.o + diff --git a/drivers/staging/tty/ip2/i2cmd.c b/drivers/staging/tty/ip2/i2cmd.c new file mode 100644 index 000000000000..e7af647800b6 --- /dev/null +++ b/drivers/staging/tty/ip2/i2cmd.c @@ -0,0 +1,210 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definition table for In-line and Bypass commands. Applicable +* only when the standard loadware is active. (This is included +* source code, not a separate compilation module.) +* +*******************************************************************************/ + +//------------------------------------------------------------------------------ +// +// Revision History: +// +// 10 October 1991 MAG First Draft +// 7 November 1991 MAG Reflects additional commands. +// 24 February 1992 MAG Additional commands for 1.4.x loadware +// 11 March 1992 MAG Additional commands +// 30 March 1992 MAG Additional command: CMD_DSS_NOW +// 18 May 1992 MAG Discovered commands 39 & 40 must be at the end of a +// packet: affects implementation. +//------------------------------------------------------------------------------ + +//************ +//* Includes * +//************ + +#include "i2cmd.h" /* To get some bit-defines */ + +//------------------------------------------------------------------------------ +// Here is the table of global arrays which represent each type of command +// supported in the IntelliPort standard loadware. See also i2cmd.h +// for a more complete explanation of what is going on. +//------------------------------------------------------------------------------ + +// Here are the various globals: note that the names are not used except through +// the macros defined in i2cmd.h. Also note that although they are character +// arrays here (for extendability) they are cast to structure pointers in the +// i2cmd.h macros. See i2cmd.h for flags definitions. + +// Length Flags Command +static UCHAR ct02[] = { 1, BTH, 0x02 }; // DTR UP +static UCHAR ct03[] = { 1, BTH, 0x03 }; // DTR DN +static UCHAR ct04[] = { 1, BTH, 0x04 }; // RTS UP +static UCHAR ct05[] = { 1, BTH, 0x05 }; // RTS DN +static UCHAR ct06[] = { 1, BYP, 0x06 }; // START FL +static UCHAR ct07[] = { 2, BTH, 0x07,0 }; // BAUD +static UCHAR ct08[] = { 2, BTH, 0x08,0 }; // BITS +static UCHAR ct09[] = { 2, BTH, 0x09,0 }; // STOP +static UCHAR ct10[] = { 2, BTH, 0x0A,0 }; // PARITY +static UCHAR ct11[] = { 2, BTH, 0x0B,0 }; // XON +static UCHAR ct12[] = { 2, BTH, 0x0C,0 }; // XOFF +static UCHAR ct13[] = { 1, BTH, 0x0D }; // STOP FL +static UCHAR ct14[] = { 1, BYP|VIP, 0x0E }; // ACK HOTK +//static UCHAR ct15[]={ 2, BTH|VIP, 0x0F,0 }; // IRQ SET +static UCHAR ct16[] = { 2, INL, 0x10,0 }; // IXONOPTS +static UCHAR ct17[] = { 2, INL, 0x11,0 }; // OXONOPTS +static UCHAR ct18[] = { 1, INL, 0x12 }; // CTSENAB +static UCHAR ct19[] = { 1, BTH, 0x13 }; // CTSDSAB +static UCHAR ct20[] = { 1, INL, 0x14 }; // DCDENAB +static UCHAR ct21[] = { 1, BTH, 0x15 }; // DCDDSAB +static UCHAR ct22[] = { 1, BTH, 0x16 }; // DSRENAB +static UCHAR ct23[] = { 1, BTH, 0x17 }; // DSRDSAB +static UCHAR ct24[] = { 1, BTH, 0x18 }; // RIENAB +static UCHAR ct25[] = { 1, BTH, 0x19 }; // RIDSAB +static UCHAR ct26[] = { 2, BTH, 0x1A,0 }; // BRKENAB +static UCHAR ct27[] = { 1, BTH, 0x1B }; // BRKDSAB +//static UCHAR ct28[]={ 2, BTH, 0x1C,0 }; // MAXBLOKSIZE +//static UCHAR ct29[]={ 2, 0, 0x1D,0 }; // reserved +static UCHAR ct30[] = { 1, INL, 0x1E }; // CTSFLOWENAB +static UCHAR ct31[] = { 1, INL, 0x1F }; // CTSFLOWDSAB +static UCHAR ct32[] = { 1, INL, 0x20 }; // RTSFLOWENAB +static UCHAR ct33[] = { 1, INL, 0x21 }; // RTSFLOWDSAB +static UCHAR ct34[] = { 2, BTH, 0x22,0 }; // ISTRIPMODE +static UCHAR ct35[] = { 2, BTH|END, 0x23,0 }; // SENDBREAK +static UCHAR ct36[] = { 2, BTH, 0x24,0 }; // SETERRMODE +//static UCHAR ct36a[]={ 3, INL, 0x24,0,0 }; // SET_REPLACE + +// The following is listed for completeness, but should never be sent directly +// by user-level code. It is sent only by library routines in response to data +// movement. +//static UCHAR ct37[]={ 5, BYP|VIP, 0x25,0,0,0,0 }; // FLOW PACKET + +// Back to normal +//static UCHAR ct38[] = {11, BTH|VAR, 0x26,0,0,0,0,0,0,0,0,0,0 }; // DEF KEY SEQ +//static UCHAR ct39[]={ 3, BTH|END, 0x27,0,0 }; // OPOSTON +//static UCHAR ct40[]={ 1, BTH|END, 0x28 }; // OPOSTOFF +static UCHAR ct41[] = { 1, BYP, 0x29 }; // RESUME +//static UCHAR ct42[]={ 2, BTH, 0x2A,0 }; // TXBAUD +//static UCHAR ct43[]={ 2, BTH, 0x2B,0 }; // RXBAUD +//static UCHAR ct44[]={ 2, BTH, 0x2C,0 }; // MS PING +//static UCHAR ct45[]={ 1, BTH, 0x2D }; // HOTENAB +//static UCHAR ct46[]={ 1, BTH, 0x2E }; // HOTDSAB +//static UCHAR ct47[]={ 7, BTH, 0x2F,0,0,0,0,0,0 }; // UNIX FLAGS +//static UCHAR ct48[]={ 1, BTH, 0x30 }; // DSRFLOWENAB +//static UCHAR ct49[]={ 1, BTH, 0x31 }; // DSRFLOWDSAB +//static UCHAR ct50[]={ 1, BTH, 0x32 }; // DTRFLOWENAB +//static UCHAR ct51[]={ 1, BTH, 0x33 }; // DTRFLOWDSAB +//static UCHAR ct52[]={ 1, BTH, 0x34 }; // BAUDTABRESET +//static UCHAR ct53[] = { 3, BTH, 0x35,0,0 }; // BAUDREMAP +static UCHAR ct54[] = { 3, BTH, 0x36,0,0 }; // CUSTOMBAUD1 +static UCHAR ct55[] = { 3, BTH, 0x37,0,0 }; // CUSTOMBAUD2 +static UCHAR ct56[] = { 2, BTH|END, 0x38,0 }; // PAUSE +static UCHAR ct57[] = { 1, BYP, 0x39 }; // SUSPEND +static UCHAR ct58[] = { 1, BYP, 0x3A }; // UNSUSPEND +static UCHAR ct59[] = { 2, BTH, 0x3B,0 }; // PARITYCHK +static UCHAR ct60[] = { 1, INL|VIP, 0x3C }; // BOOKMARKREQ +//static UCHAR ct61[]={ 2, BTH, 0x3D,0 }; // INTERNALLOOP +//static UCHAR ct62[]={ 2, BTH, 0x3E,0 }; // HOTKTIMEOUT +static UCHAR ct63[] = { 2, INL, 0x3F,0 }; // SETTXON +static UCHAR ct64[] = { 2, INL, 0x40,0 }; // SETTXOFF +//static UCHAR ct65[]={ 2, BTH, 0x41,0 }; // SETAUTORTS +//static UCHAR ct66[]={ 2, BTH, 0x42,0 }; // SETHIGHWAT +//static UCHAR ct67[]={ 2, BYP, 0x43,0 }; // STARTSELFL +//static UCHAR ct68[]={ 2, INL, 0x44,0 }; // ENDSELFL +//static UCHAR ct69[]={ 1, BYP, 0x45 }; // HWFLOW_OFF +//static UCHAR ct70[]={ 1, BTH, 0x46 }; // ODSRFL_ENAB +//static UCHAR ct71[]={ 1, BTH, 0x47 }; // ODSRFL_DSAB +//static UCHAR ct72[]={ 1, BTH, 0x48 }; // ODCDFL_ENAB +//static UCHAR ct73[]={ 1, BTH, 0x49 }; // ODCDFL_DSAB +//static UCHAR ct74[]={ 2, BTH, 0x4A,0 }; // LOADLEVEL +//static UCHAR ct75[]={ 2, BTH, 0x4B,0 }; // STATDATA +//static UCHAR ct76[]={ 1, BYP, 0x4C }; // BREAK_ON +//static UCHAR ct77[]={ 1, BYP, 0x4D }; // BREAK_OFF +//static UCHAR ct78[]={ 1, BYP, 0x4E }; // GETFC +static UCHAR ct79[] = { 2, BYP, 0x4F,0 }; // XMIT_NOW +//static UCHAR ct80[]={ 4, BTH, 0x50,0,0,0 }; // DIVISOR_LATCH +//static UCHAR ct81[]={ 1, BYP, 0x51 }; // GET_STATUS +//static UCHAR ct82[]={ 1, BYP, 0x52 }; // GET_TXCNT +//static UCHAR ct83[]={ 1, BYP, 0x53 }; // GET_RXCNT +//static UCHAR ct84[]={ 1, BYP, 0x54 }; // GET_BOXIDS +//static UCHAR ct85[]={10, BYP, 0x55,0,0,0,0,0,0,0,0,0 }; // ENAB_MULT +//static UCHAR ct86[]={ 2, BTH, 0x56,0 }; // RCV_ENABLE +static UCHAR ct87[] = { 1, BYP, 0x57 }; // HW_TEST +//static UCHAR ct88[]={ 3, BTH, 0x58,0,0 }; // RCV_THRESHOLD +//static UCHAR ct90[]={ 3, BYP, 0x5A,0,0 }; // Set SILO +//static UCHAR ct91[]={ 2, BYP, 0x5B,0 }; // timed break + +// Some composite commands as well +//static UCHAR cc01[]={ 2, BTH, 0x02,0x04 }; // DTR & RTS UP +//static UCHAR cc02[]={ 2, BTH, 0x03,0x05 }; // DTR & RTS DN + +//******** +//* Code * +//******** + +//****************************************************************************** +// Function: i2cmdUnixFlags(iflag, cflag, lflag) +// Parameters: Unix tty flags +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of command 47 and returns a pointer to the +// appropriate structure. +//****************************************************************************** +#if 0 +cmdSyntaxPtr +i2cmdUnixFlags(unsigned short iflag,unsigned short cflag,unsigned short lflag) +{ + cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct47; + + pCM->cmd[1] = (unsigned char) iflag; + pCM->cmd[2] = (unsigned char) (iflag >> 8); + pCM->cmd[3] = (unsigned char) cflag; + pCM->cmd[4] = (unsigned char) (cflag >> 8); + pCM->cmd[5] = (unsigned char) lflag; + pCM->cmd[6] = (unsigned char) (lflag >> 8); + return pCM; +} +#endif /* 0 */ + +//****************************************************************************** +// Function: i2cmdBaudDef(which, rate) +// Parameters: ? +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of commands 54 or 55 (according to the +// argument which), and returns a pointer to the appropriate structure. +//****************************************************************************** +static cmdSyntaxPtr +i2cmdBaudDef(int which, unsigned short rate) +{ + cmdSyntaxPtr pCM; + + switch(which) + { + case 1: + pCM = (cmdSyntaxPtr) ct54; + break; + default: + case 2: + pCM = (cmdSyntaxPtr) ct55; + break; + } + pCM->cmd[1] = (unsigned char) rate; + pCM->cmd[2] = (unsigned char) (rate >> 8); + return pCM; +} + diff --git a/drivers/staging/tty/ip2/i2cmd.h b/drivers/staging/tty/ip2/i2cmd.h new file mode 100644 index 000000000000..29277ec6b8ed --- /dev/null +++ b/drivers/staging/tty/ip2/i2cmd.h @@ -0,0 +1,630 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions and support for In-line and Bypass commands. +* Applicable only when the standard loadware is active. +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// Revision History: +// +// 10 October 1991 MAG First Draft +// 7 November 1991 MAG Reflects some new commands +// 20 February 1992 MAG CMD_HOTACK corrected: no argument. +// 24 February 1992 MAG Support added for new commands for 1.4.x loadware. +// 11 March 1992 MAG Additional commands. +// 16 March 1992 MAG Additional commands. +// 30 March 1992 MAG Additional command: CMD_DSS_NOW +// 18 May 1992 MAG Changed CMD_OPOST +// +//------------------------------------------------------------------------------ +#ifndef I2CMD_H // To prevent multiple includes +#define I2CMD_H 1 + +#include "ip2types.h" + +// This module is designed to provide a uniform method of sending commands to +// the board through command packets. The difficulty is, some commands take +// parameters, others do not. Furthermore, it is often useful to send several +// commands to the same channel as part of the same packet. (See also i2pack.h.) +// +// This module is designed so that the caller should not be responsible for +// remembering the exact syntax of each command, or at least so that the +// compiler could check things somewhat. I'll explain as we go... +// +// First, a structure which can embody the syntax of each type of command. +// +typedef struct _cmdSyntax +{ + UCHAR length; // Number of bytes in the command + UCHAR flags; // Information about the command (see below) + + // The command and its parameters, which may be of arbitrary length. Don't + // worry yet how the parameters will be initialized; macros later take care + // of it. Also, don't worry about the arbitrary length issue; this structure + // is never used to allocate space (see i2cmd.c). + UCHAR cmd[2]; +} cmdSyntax, *cmdSyntaxPtr; + +// Bit assignments for flags + +#define INL 1 // Set if suitable for inline commands +#define BYP 2 // Set if suitable for bypass commands +#define BTH (INL|BYP) // suitable for either! +#define END 4 // Set if this must be the last command in a block +#define VIP 8 // Set if this command is special in some way and really + // should only be sent from the library-level and not + // directly from user-level +#define VAR 0x10 // This command is of variable length! + +// Declarations for the global arrays used to bear the commands and their +// arguments. +// +// Note: Since these are globals and the arguments might change, it is important +// that the library routine COPY these into buffers from whence they would be +// sent, rather than merely storing the pointers. In multi-threaded +// environments, important that the copy should obtain before any context switch +// is allowed. Also, for parameterized commands, DO NOT ISSUE THE SAME COMMAND +// MORE THAN ONCE WITH THE SAME PARAMETERS in the same call. +// +static UCHAR ct02[]; +static UCHAR ct03[]; +static UCHAR ct04[]; +static UCHAR ct05[]; +static UCHAR ct06[]; +static UCHAR ct07[]; +static UCHAR ct08[]; +static UCHAR ct09[]; +static UCHAR ct10[]; +static UCHAR ct11[]; +static UCHAR ct12[]; +static UCHAR ct13[]; +static UCHAR ct14[]; +static UCHAR ct15[]; +static UCHAR ct16[]; +static UCHAR ct17[]; +static UCHAR ct18[]; +static UCHAR ct19[]; +static UCHAR ct20[]; +static UCHAR ct21[]; +static UCHAR ct22[]; +static UCHAR ct23[]; +static UCHAR ct24[]; +static UCHAR ct25[]; +static UCHAR ct26[]; +static UCHAR ct27[]; +static UCHAR ct28[]; +static UCHAR ct29[]; +static UCHAR ct30[]; +static UCHAR ct31[]; +static UCHAR ct32[]; +static UCHAR ct33[]; +static UCHAR ct34[]; +static UCHAR ct35[]; +static UCHAR ct36[]; +static UCHAR ct36a[]; +static UCHAR ct41[]; +static UCHAR ct42[]; +static UCHAR ct43[]; +static UCHAR ct44[]; +static UCHAR ct45[]; +static UCHAR ct46[]; +static UCHAR ct48[]; +static UCHAR ct49[]; +static UCHAR ct50[]; +static UCHAR ct51[]; +static UCHAR ct52[]; +static UCHAR ct56[]; +static UCHAR ct57[]; +static UCHAR ct58[]; +static UCHAR ct59[]; +static UCHAR ct60[]; +static UCHAR ct61[]; +static UCHAR ct62[]; +static UCHAR ct63[]; +static UCHAR ct64[]; +static UCHAR ct65[]; +static UCHAR ct66[]; +static UCHAR ct67[]; +static UCHAR ct68[]; +static UCHAR ct69[]; +static UCHAR ct70[]; +static UCHAR ct71[]; +static UCHAR ct72[]; +static UCHAR ct73[]; +static UCHAR ct74[]; +static UCHAR ct75[]; +static UCHAR ct76[]; +static UCHAR ct77[]; +static UCHAR ct78[]; +static UCHAR ct79[]; +static UCHAR ct80[]; +static UCHAR ct81[]; +static UCHAR ct82[]; +static UCHAR ct83[]; +static UCHAR ct84[]; +static UCHAR ct85[]; +static UCHAR ct86[]; +static UCHAR ct87[]; +static UCHAR ct88[]; +static UCHAR ct89[]; +static UCHAR ct90[]; +static UCHAR ct91[]; +static UCHAR cc01[]; +static UCHAR cc02[]; + +// Now, refer to i2cmd.c, and see the character arrays defined there. They are +// cast here to cmdSyntaxPtr. +// +// There are library functions for issuing bypass or inline commands. These +// functions take one or more arguments of the type cmdSyntaxPtr. The routine +// then can figure out how long each command is supposed to be and easily add it +// to the list. +// +// For ease of use, we define manifests which return pointers to appropriate +// cmdSyntaxPtr things. But some commands also take arguments. If a single +// argument is used, we define a macro which performs the single assignment and +// (through the expedient of a comma expression) references the appropriate +// pointer. For commands requiring several arguments, we actually define a +// function to perform the assignments. + +#define CMD_DTRUP (cmdSyntaxPtr)(ct02) // Raise DTR +#define CMD_DTRDN (cmdSyntaxPtr)(ct03) // Lower DTR +#define CMD_RTSUP (cmdSyntaxPtr)(ct04) // Raise RTS +#define CMD_RTSDN (cmdSyntaxPtr)(ct05) // Lower RTS +#define CMD_STARTFL (cmdSyntaxPtr)(ct06) // Start Flushing Data + +#define CMD_DTRRTS_UP (cmdSyntaxPtr)(cc01) // Raise DTR and RTS +#define CMD_DTRRTS_DN (cmdSyntaxPtr)(cc02) // Lower DTR and RTS + +// Set Baud Rate for transmit and receive +#define CMD_SETBAUD(arg) \ + (((cmdSyntaxPtr)(ct07))->cmd[1] = (arg),(cmdSyntaxPtr)(ct07)) + +#define CBR_50 1 +#define CBR_75 2 +#define CBR_110 3 +#define CBR_134 4 +#define CBR_150 5 +#define CBR_200 6 +#define CBR_300 7 +#define CBR_600 8 +#define CBR_1200 9 +#define CBR_1800 10 +#define CBR_2400 11 +#define CBR_4800 12 +#define CBR_9600 13 +#define CBR_19200 14 +#define CBR_38400 15 +#define CBR_2000 16 +#define CBR_3600 17 +#define CBR_7200 18 +#define CBR_56000 19 +#define CBR_57600 20 +#define CBR_64000 21 +#define CBR_76800 22 +#define CBR_115200 23 +#define CBR_C1 24 // Custom baud rate 1 +#define CBR_C2 25 // Custom baud rate 2 +#define CBR_153600 26 +#define CBR_230400 27 +#define CBR_307200 28 +#define CBR_460800 29 +#define CBR_921600 30 + +// Set Character size +// +#define CMD_SETBITS(arg) \ + (((cmdSyntaxPtr)(ct08))->cmd[1] = (arg),(cmdSyntaxPtr)(ct08)) + +#define CSZ_5 0 +#define CSZ_6 1 +#define CSZ_7 2 +#define CSZ_8 3 + +// Set number of stop bits +// +#define CMD_SETSTOP(arg) \ + (((cmdSyntaxPtr)(ct09))->cmd[1] = (arg),(cmdSyntaxPtr)(ct09)) + +#define CST_1 0 +#define CST_15 1 // 1.5 stop bits +#define CST_2 2 + +// Set parity option +// +#define CMD_SETPAR(arg) \ + (((cmdSyntaxPtr)(ct10))->cmd[1] = (arg),(cmdSyntaxPtr)(ct10)) + +#define CSP_NP 0 // no parity +#define CSP_OD 1 // odd parity +#define CSP_EV 2 // Even parity +#define CSP_SP 3 // Space parity +#define CSP_MK 4 // Mark parity + +// Define xon char for transmitter flow control +// +#define CMD_DEF_IXON(arg) \ + (((cmdSyntaxPtr)(ct11))->cmd[1] = (arg),(cmdSyntaxPtr)(ct11)) + +// Define xoff char for transmitter flow control +// +#define CMD_DEF_IXOFF(arg) \ + (((cmdSyntaxPtr)(ct12))->cmd[1] = (arg),(cmdSyntaxPtr)(ct12)) + +#define CMD_STOPFL (cmdSyntaxPtr)(ct13) // Stop Flushing data + +// Acknowledge receipt of hotkey signal +// +#define CMD_HOTACK (cmdSyntaxPtr)(ct14) + +// Define irq level to use. Should actually be sent by library-level code, not +// directly from user... +// +#define CMDVALUE_IRQ 15 // For library use at initialization. Until this command + // is sent, board processing doesn't really start. +#define CMD_SET_IRQ(arg) \ + (((cmdSyntaxPtr)(ct15))->cmd[1] = (arg),(cmdSyntaxPtr)(ct15)) + +#define CIR_POLL 0 // No IRQ - Poll +#define CIR_3 3 // IRQ 3 +#define CIR_4 4 // IRQ 4 +#define CIR_5 5 // IRQ 5 +#define CIR_7 7 // IRQ 7 +#define CIR_10 10 // IRQ 10 +#define CIR_11 11 // IRQ 11 +#define CIR_12 12 // IRQ 12 +#define CIR_15 15 // IRQ 15 + +// Select transmit flow xon/xoff options +// +#define CMD_IXON_OPT(arg) \ + (((cmdSyntaxPtr)(ct16))->cmd[1] = (arg),(cmdSyntaxPtr)(ct16)) + +#define CIX_NONE 0 // Incoming Xon/Xoff characters not special +#define CIX_XON 1 // Xoff disable, Xon enable +#define CIX_XANY 2 // Xoff disable, any key enable + +// Select receive flow xon/xoff options +// +#define CMD_OXON_OPT(arg) \ + (((cmdSyntaxPtr)(ct17))->cmd[1] = (arg),(cmdSyntaxPtr)(ct17)) + +#define COX_NONE 0 // Don't send Xon/Xoff +#define COX_XON 1 // Send xon/xoff to start/stop incoming data + + +#define CMD_CTS_REP (cmdSyntaxPtr)(ct18) // Enable CTS reporting +#define CMD_CTS_NREP (cmdSyntaxPtr)(ct19) // Disable CTS reporting + +#define CMD_DCD_REP (cmdSyntaxPtr)(ct20) // Enable DCD reporting +#define CMD_DCD_NREP (cmdSyntaxPtr)(ct21) // Disable DCD reporting + +#define CMD_DSR_REP (cmdSyntaxPtr)(ct22) // Enable DSR reporting +#define CMD_DSR_NREP (cmdSyntaxPtr)(ct23) // Disable DSR reporting + +#define CMD_RI_REP (cmdSyntaxPtr)(ct24) // Enable RI reporting +#define CMD_RI_NREP (cmdSyntaxPtr)(ct25) // Disable RI reporting + +// Enable break reporting and select style +// +#define CMD_BRK_REP(arg) \ + (((cmdSyntaxPtr)(ct26))->cmd[1] = (arg),(cmdSyntaxPtr)(ct26)) + +#define CBK_STAT 0x00 // Report breaks as a status (exception,irq) +#define CBK_NULL 0x01 // Report breaks as a good null +#define CBK_STAT_SEQ 0x02 // Report breaks as a status AND as in-band character + // sequence FFh, 01h, 10h +#define CBK_SEQ 0x03 // Report breaks as the in-band + //sequence FFh, 01h, 10h ONLY. +#define CBK_FLSH 0x04 // if this bit set also flush input data +#define CBK_POSIX 0x08 // if this bit set report as FF,0,0 sequence +#define CBK_SINGLE 0x10 // if this bit set with CBK_SEQ or CBK_STAT_SEQ + //then reports single null instead of triple + +#define CMD_BRK_NREP (cmdSyntaxPtr)(ct27) // Disable break reporting + +// Specify maximum block size for received data +// +#define CMD_MAX_BLOCK(arg) \ + (((cmdSyntaxPtr)(ct28))->cmd[1] = (arg),(cmdSyntaxPtr)(ct28)) + +// -- COMMAND 29 is reserved -- + +#define CMD_CTSFL_ENAB (cmdSyntaxPtr)(ct30) // Enable CTS flow control +#define CMD_CTSFL_DSAB (cmdSyntaxPtr)(ct31) // Disable CTS flow control +#define CMD_RTSFL_ENAB (cmdSyntaxPtr)(ct32) // Enable RTS flow control +#define CMD_RTSFL_DSAB (cmdSyntaxPtr)(ct33) // Disable RTS flow control + +// Specify istrip option +// +#define CMD_ISTRIP_OPT(arg) \ + (((cmdSyntaxPtr)(ct34))->cmd[1] = (arg),(cmdSyntaxPtr)(ct34)) + +#define CIS_NOSTRIP 0 // Strip characters to character size +#define CIS_STRIP 1 // Strip any 8-bit characters to 7 bits + +// Send a break of arg milliseconds +// +#define CMD_SEND_BRK(arg) \ + (((cmdSyntaxPtr)(ct35))->cmd[1] = (arg),(cmdSyntaxPtr)(ct35)) + +// Set error reporting mode +// +#define CMD_SET_ERROR(arg) \ + (((cmdSyntaxPtr)(ct36))->cmd[1] = (arg),(cmdSyntaxPtr)(ct36)) + +#define CSE_ESTAT 0 // Report error in a status packet +#define CSE_NOREP 1 // Treat character as though it were good +#define CSE_DROP 2 // Discard the character +#define CSE_NULL 3 // Replace with a null +#define CSE_MARK 4 // Replace with a 3-character sequence (as Unix) + +#define CSE_REPLACE 0x8 // Replace the errored character with the + // replacement character defined here + +#define CSE_STAT_REPLACE 0x18 // Replace the errored character with the + // replacement character defined here AND + // report the error as a status packet (as in + // CSE_ESTAT). + + +// COMMAND 37, to send flow control packets, is handled only by low-level +// library code in response to data movement and shouldn't ever be sent by the +// user code. See i2pack.h and the body of i2lib.c for details. + +// Enable on-board post-processing, using options given in oflag argument. +// Formerly, this command was automatically preceded by a CMD_OPOST_OFF command +// because the loadware does not permit sending back-to-back CMD_OPOST_ON +// commands without an intervening CMD_OPOST_OFF. BUT, WE LEARN 18 MAY 92, that +// CMD_OPOST_ON and CMD_OPOST_OFF must each be at the end of a packet (or in a +// solo packet). This means the caller must specify separately CMD_OPOST_OFF, +// CMD_OPOST_ON(parm) when he calls i2QueueCommands(). That function will ensure +// each gets a separate packet. Extra CMD_OPOST_OFF's are always ok. +// +#define CMD_OPOST_ON(oflag) \ + (*(USHORT *)(((cmdSyntaxPtr)(ct39))->cmd[1]) = (oflag), \ + (cmdSyntaxPtr)(ct39)) + +#define CMD_OPOST_OFF (cmdSyntaxPtr)(ct40) // Disable on-board post-proc + +#define CMD_RESUME (cmdSyntaxPtr)(ct41) // Resume: behave as though an XON + // were received; + +// Set Transmit baud rate (see command 7 for arguments) +// +#define CMD_SETBAUD_TX(arg) \ + (((cmdSyntaxPtr)(ct42))->cmd[1] = (arg),(cmdSyntaxPtr)(ct42)) + +// Set Receive baud rate (see command 7 for arguments) +// +#define CMD_SETBAUD_RX(arg) \ + (((cmdSyntaxPtr)(ct43))->cmd[1] = (arg),(cmdSyntaxPtr)(ct43)) + +// Request interrupt from board each arg milliseconds. Interrupt will specify +// "received data", even though there may be no data present. If arg == 0, +// disables any such interrupts. +// +#define CMD_PING_REQ(arg) \ + (((cmdSyntaxPtr)(ct44))->cmd[1] = (arg),(cmdSyntaxPtr)(ct44)) + +#define CMD_HOT_ENAB (cmdSyntaxPtr)(ct45) // Enable Hot-key checking +#define CMD_HOT_DSAB (cmdSyntaxPtr)(ct46) // Disable Hot-key checking + +#if 0 +// COMMAND 47: Send Protocol info via Unix flags: +// iflag = Unix tty t_iflag +// cflag = Unix tty t_cflag +// lflag = Unix tty t_lflag +// See System V Unix/Xenix documentation for the meanings of the bit fields +// within these flags +// +#define CMD_UNIX_FLAGS(iflag,cflag,lflag) i2cmdUnixFlags(iflag,cflag,lflag) +#endif /* 0 */ + +#define CMD_DSRFL_ENAB (cmdSyntaxPtr)(ct48) // Enable DSR receiver ctrl +#define CMD_DSRFL_DSAB (cmdSyntaxPtr)(ct49) // Disable DSR receiver ctrl +#define CMD_DTRFL_ENAB (cmdSyntaxPtr)(ct50) // Enable DTR flow control +#define CMD_DTRFL_DSAB (cmdSyntaxPtr)(ct51) // Disable DTR flow control +#define CMD_BAUD_RESET (cmdSyntaxPtr)(ct52) // Reset baudrate table + +// COMMAND 54: Define custom rate #1 +// rate = (short) 1/10 of the desired baud rate +// +#define CMD_BAUD_DEF1(rate) i2cmdBaudDef(1,rate) + +// COMMAND 55: Define custom rate #2 +// rate = (short) 1/10 of the desired baud rate +// +#define CMD_BAUD_DEF2(rate) i2cmdBaudDef(2,rate) + +// Pause arg hundredths of seconds. (Note, this is NOT milliseconds.) +// +#define CMD_PAUSE(arg) \ + (((cmdSyntaxPtr)(ct56))->cmd[1] = (arg),(cmdSyntaxPtr)(ct56)) + +#define CMD_SUSPEND (cmdSyntaxPtr)(ct57) // Suspend output +#define CMD_UNSUSPEND (cmdSyntaxPtr)(ct58) // Un-Suspend output + +// Set parity-checking options +// +#define CMD_PARCHK(arg) \ + (((cmdSyntaxPtr)(ct59))->cmd[1] = (arg),(cmdSyntaxPtr)(ct59)) + +#define CPK_ENAB 0 // Enable parity checking on input +#define CPK_DSAB 1 // Disable parity checking on input + +#define CMD_BMARK_REQ (cmdSyntaxPtr)(ct60) // Bookmark request + + +// Enable/Disable internal loopback mode +// +#define CMD_INLOOP(arg) \ + (((cmdSyntaxPtr)(ct61))->cmd[1] = (arg),(cmdSyntaxPtr)(ct61)) + +#define CIN_DISABLE 0 // Normal operation (default) +#define CIN_ENABLE 1 // Internal (local) loopback +#define CIN_REMOTE 2 // Remote loopback + +// Specify timeout for hotkeys: Delay will be (arg x 10) milliseconds, arg == 0 +// --> no timeout: wait forever. +// +#define CMD_HOT_TIME(arg) \ + (((cmdSyntaxPtr)(ct62))->cmd[1] = (arg),(cmdSyntaxPtr)(ct62)) + + +// Define (outgoing) xon for receive flow control +// +#define CMD_DEF_OXON(arg) \ + (((cmdSyntaxPtr)(ct63))->cmd[1] = (arg),(cmdSyntaxPtr)(ct63)) + +// Define (outgoing) xoff for receiver flow control +// +#define CMD_DEF_OXOFF(arg) \ + (((cmdSyntaxPtr)(ct64))->cmd[1] = (arg),(cmdSyntaxPtr)(ct64)) + +// Enable/Disable RTS on transmit (1/2 duplex-style) +// +#define CMD_RTS_XMIT(arg) \ + (((cmdSyntaxPtr)(ct65))->cmd[1] = (arg),(cmdSyntaxPtr)(ct65)) + +#define CHD_DISABLE 0 +#define CHD_ENABLE 1 + +// Set high-water-mark level (debugging use only) +// +#define CMD_SETHIGHWAT(arg) \ + (((cmdSyntaxPtr)(ct66))->cmd[1] = (arg),(cmdSyntaxPtr)(ct66)) + +// Start flushing tagged data (tag = 0-14) +// +#define CMD_START_SELFL(tag) \ + (((cmdSyntaxPtr)(ct67))->cmd[1] = (tag),(cmdSyntaxPtr)(ct67)) + +// End flushing tagged data (tag = 0-14) +// +#define CMD_END_SELFL(tag) \ + (((cmdSyntaxPtr)(ct68))->cmd[1] = (tag),(cmdSyntaxPtr)(ct68)) + +#define CMD_HWFLOW_OFF (cmdSyntaxPtr)(ct69) // Disable HW TX flow control +#define CMD_ODSRFL_ENAB (cmdSyntaxPtr)(ct70) // Enable DSR output f/c +#define CMD_ODSRFL_DSAB (cmdSyntaxPtr)(ct71) // Disable DSR output f/c +#define CMD_ODCDFL_ENAB (cmdSyntaxPtr)(ct72) // Enable DCD output f/c +#define CMD_ODCDFL_DSAB (cmdSyntaxPtr)(ct73) // Disable DCD output f/c + +// Set transmit interrupt load level. Count should be an even value 2-12 +// +#define CMD_LOADLEVEL(count) \ + (((cmdSyntaxPtr)(ct74))->cmd[1] = (count),(cmdSyntaxPtr)(ct74)) + +// If reporting DSS changes, map to character sequence FFh, 2, MSR +// +#define CMD_STATDATA(arg) \ + (((cmdSyntaxPtr)(ct75))->cmd[1] = (arg),(cmdSyntaxPtr)(ct75)) + +#define CSTD_DISABLE// Report DSS changes as status packets only (default) +#define CSTD_ENABLE // Report DSS changes as in-band data sequence as well as + // by status packet. + +#define CMD_BREAK_ON (cmdSyntaxPtr)(ct76)// Set break and stop xmit +#define CMD_BREAK_OFF (cmdSyntaxPtr)(ct77)// End break and restart xmit +#define CMD_GETFC (cmdSyntaxPtr)(ct78)// Request for flow control packet + // from board. + +// Transmit this character immediately +// +#define CMD_XMIT_NOW(ch) \ + (((cmdSyntaxPtr)(ct79))->cmd[1] = (ch),(cmdSyntaxPtr)(ct79)) + +// Set baud rate via "divisor latch" +// +#define CMD_DIVISOR_LATCH(which,value) \ + (((cmdSyntaxPtr)(ct80))->cmd[1] = (which), \ + *(USHORT *)(((cmdSyntaxPtr)(ct80))->cmd[2]) = (value), \ + (cmdSyntaxPtr)(ct80)) + +#define CDL_RX 1 // Set receiver rate +#define CDL_TX 2 // Set transmit rate + // (CDL_TX | CDL_RX) Set both rates + +// Request for special diagnostic status pkt from the board. +// +#define CMD_GET_STATUS (cmdSyntaxPtr)(ct81) + +// Request time-stamped transmit character count packet. +// +#define CMD_GET_TXCNT (cmdSyntaxPtr)(ct82) + +// Request time-stamped receive character count packet. +// +#define CMD_GET_RXCNT (cmdSyntaxPtr)(ct83) + +// Request for box/board I.D. packet. +#define CMD_GET_BOXIDS (cmdSyntaxPtr)(ct84) + +// Enable or disable multiple channels according to bit-mapped ushorts box 1-4 +// +#define CMD_ENAB_MULT(enable, box1, box2, box3, box4) \ + (((cmdSytaxPtr)(ct85))->cmd[1] = (enable), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[2]) = (box1), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[4]) = (box2), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[6]) = (box3), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[8]) = (box4), \ + (cmdSyntaxPtr)(ct85)) + +#define CEM_DISABLE 0 +#define CEM_ENABLE 1 + +// Enable or disable receiver or receiver interrupts (default both enabled) +// +#define CMD_RCV_ENABLE(ch) \ + (((cmdSyntaxPtr)(ct86))->cmd[1] = (ch),(cmdSyntaxPtr)(ct86)) + +#define CRE_OFF 0 // Disable the receiver +#define CRE_ON 1 // Enable the receiver +#define CRE_INTOFF 2 // Disable receiver interrupts (to loadware) +#define CRE_INTON 3 // Enable receiver interrupts (to loadware) + +// Starts up a hardware test process, which runs transparently, and sends a +// STAT_HWFAIL packet in case a hardware failure is detected. +// +#define CMD_HW_TEST (cmdSyntaxPtr)(ct87) + +// Change receiver threshold and timeout value: +// Defaults: timeout = 20mS +// threshold count = 8 when DTRflow not in use, +// threshold count = 5 when DTRflow in use. +// +#define CMD_RCV_THRESHOLD(count,ms) \ + (((cmdSyntaxPtr)(ct88))->cmd[1] = (count), \ + ((cmdSyntaxPtr)(ct88))->cmd[2] = (ms), \ + (cmdSyntaxPtr)(ct88)) + +// Makes the loadware report DSS signals for this channel immediately. +// +#define CMD_DSS_NOW (cmdSyntaxPtr)(ct89) + +// Set the receive silo parameters +// timeout is ms idle wait until delivery (~VTIME) +// threshold is max characters cause interrupt (~VMIN) +// +#define CMD_SET_SILO(timeout,threshold) \ + (((cmdSyntaxPtr)(ct90))->cmd[1] = (timeout), \ + ((cmdSyntaxPtr)(ct90))->cmd[2] = (threshold), \ + (cmdSyntaxPtr)(ct90)) + +// Set timed break in decisecond (1/10s) +// +#define CMD_LBREAK(ds) \ + (((cmdSyntaxPtr)(ct91))->cmd[1] = (ds),(cmdSyntaxPtr)(ct66)) + + + +#endif // I2CMD_H diff --git a/drivers/staging/tty/ip2/i2ellis.c b/drivers/staging/tty/ip2/i2ellis.c new file mode 100644 index 000000000000..29db44de399f --- /dev/null +++ b/drivers/staging/tty/ip2/i2ellis.c @@ -0,0 +1,1403 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Low-level interface code for the device driver +* (This is included source code, not a separate compilation +* module.) +* +*******************************************************************************/ +//--------------------------------------------- +// Function declarations private to this module +//--------------------------------------------- +// Functions called only indirectly through i2eBordStr entries. + +static int iiWriteBuf16(i2eBordStrPtr, unsigned char *, int); +static int iiWriteBuf8(i2eBordStrPtr, unsigned char *, int); +static int iiReadBuf16(i2eBordStrPtr, unsigned char *, int); +static int iiReadBuf8(i2eBordStrPtr, unsigned char *, int); + +static unsigned short iiReadWord16(i2eBordStrPtr); +static unsigned short iiReadWord8(i2eBordStrPtr); +static void iiWriteWord16(i2eBordStrPtr, unsigned short); +static void iiWriteWord8(i2eBordStrPtr, unsigned short); + +static int iiWaitForTxEmptyII(i2eBordStrPtr, int); +static int iiWaitForTxEmptyIIEX(i2eBordStrPtr, int); +static int iiTxMailEmptyII(i2eBordStrPtr); +static int iiTxMailEmptyIIEX(i2eBordStrPtr); +static int iiTrySendMailII(i2eBordStrPtr, unsigned char); +static int iiTrySendMailIIEX(i2eBordStrPtr, unsigned char); + +static unsigned short iiGetMailII(i2eBordStrPtr); +static unsigned short iiGetMailIIEX(i2eBordStrPtr); + +static void iiEnableMailIrqII(i2eBordStrPtr); +static void iiEnableMailIrqIIEX(i2eBordStrPtr); +static void iiWriteMaskII(i2eBordStrPtr, unsigned char); +static void iiWriteMaskIIEX(i2eBordStrPtr, unsigned char); + +static void ii2Nop(void); + +//*************** +//* Static Data * +//*************** + +static int ii2Safe; // Safe I/O address for delay routine + +static int iiDelayed; // Set when the iiResetDelay function is + // called. Cleared when ANY board is reset. +static DEFINE_RWLOCK(Dl_spinlock); + +//******** +//* Code * +//******** + +//======================================================= +// Initialization Routines +// +// iiSetAddress +// iiReset +// iiResetDelay +// iiInitialize +//======================================================= + +//****************************************************************************** +// Function: iiSetAddress(pB, address, delay) +// Parameters: pB - pointer to the board structure +// address - the purported I/O address of the board +// delay - pointer to the 1-ms delay function to use +// in this and any future operations to this board +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// This routine (roughly) checks for address validity, sets the i2eValid OK and +// sets the state to II_STATE_COLD which means that we haven't even sent a reset +// yet. +// +//****************************************************************************** +static int +iiSetAddress( i2eBordStrPtr pB, int address, delayFunc_t delay ) +{ + // Should any failure occur before init is finished... + pB->i2eValid = I2E_INCOMPLETE; + + // Cannot check upper limit except extremely: Might be microchannel + // Address must be on an 8-byte boundary + + if ((unsigned int)address <= 0x100 + || (unsigned int)address >= 0xfff8 + || (address & 0x7) + ) + { + I2_COMPLETE(pB, I2EE_BADADDR); + } + + // Initialize accelerators + pB->i2eBase = address; + pB->i2eData = address + FIFO_DATA; + pB->i2eStatus = address + FIFO_STATUS; + pB->i2ePointer = address + FIFO_PTR; + pB->i2eXMail = address + FIFO_MAIL; + pB->i2eXMask = address + FIFO_MASK; + + // Initialize i/o address for ii2DelayIO + ii2Safe = address + FIFO_NOP; + + // Initialize the delay routine + pB->i2eDelay = ((delay != (delayFunc_t)NULL) ? delay : (delayFunc_t)ii2Nop); + + pB->i2eValid = I2E_MAGIC; + pB->i2eState = II_STATE_COLD; + + I2_COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReset(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Attempts to reset the board (see also i2hw.h). Normally, we would use this to +// reset a board immediately after iiSetAddress(), but it is valid to reset a +// board from any state, say, in order to change or re-load loadware. (Under +// such circumstances, no reason to re-run iiSetAddress(), which is why it is a +// separate routine and not included in this routine. +// +//****************************************************************************** +static int +iiReset(i2eBordStrPtr pB) +{ + // Magic number should be set, else even the address is suspect + if (pB->i2eValid != I2E_MAGIC) + { + I2_COMPLETE(pB, I2EE_BADMAGIC); + } + + outb(0, pB->i2eBase + FIFO_RESET); /* Any data will do */ + iiDelay(pB, 50); // Pause between resets + outb(0, pB->i2eBase + FIFO_RESET); /* Second reset */ + + // We must wait before even attempting to read anything from the FIFO: the + // board's P.O.S.T may actually attempt to read and write its end of the + // FIFO in order to check flags, loop back (where supported), etc. On + // completion of this testing it would reset the FIFO, and on completion + // of all // P.O.S.T., write the message. We must not mistake data which + // might have been sent for testing as part of the reset message. To + // better utilize time, say, when resetting several boards, we allow the + // delay to be performed externally; in this way the caller can reset + // several boards, delay a single time, then call the initialization + // routine for all. + + pB->i2eState = II_STATE_RESET; + + iiDelayed = 0; // i.e., the delay routine hasn't been called since the most + // recent reset. + + // Ensure anything which would have been of use to standard loadware is + // blanked out, since board has now forgotten everything!. + + pB->i2eUsingIrq = I2_IRQ_UNDEFINED; /* to not use an interrupt so far */ + pB->i2eWaitingForEmptyFifo = 0; + pB->i2eOutMailWaiting = 0; + pB->i2eChannelPtr = NULL; + pB->i2eChannelCnt = 0; + + pB->i2eLeadoffWord[0] = 0; + pB->i2eFifoInInts = 0; + pB->i2eFifoOutInts = 0; + pB->i2eFatalTrap = NULL; + pB->i2eFatal = 0; + + I2_COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiResetDelay(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Using the delay defined in board structure, waits two seconds (for board to +// reset). +// +//****************************************************************************** +static int +iiResetDelay(i2eBordStrPtr pB) +{ + if (pB->i2eValid != I2E_MAGIC) { + I2_COMPLETE(pB, I2EE_BADMAGIC); + } + if (pB->i2eState != II_STATE_RESET) { + I2_COMPLETE(pB, I2EE_BADSTATE); + } + iiDelay(pB,2000); /* Now we wait for two seconds. */ + iiDelayed = 1; /* Delay has been called: ok to initialize */ + I2_COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiInitialize(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Attempts to read the Power-on reset message. Initializes any remaining fields +// in the pB structure. +// +// This should be called as the third step of a process beginning with +// iiReset(), then iiResetDelay(). This routine checks to see that the structure +// is "valid" and in the reset state, also confirms that the delay routine has +// been called since the latest reset (to any board! overly strong!). +// +//****************************************************************************** +static int +iiInitialize(i2eBordStrPtr pB) +{ + int itemp; + unsigned char c; + unsigned short utemp; + unsigned int ilimit; + + if (pB->i2eValid != I2E_MAGIC) + { + I2_COMPLETE(pB, I2EE_BADMAGIC); + } + + if (pB->i2eState != II_STATE_RESET || !iiDelayed) + { + I2_COMPLETE(pB, I2EE_BADSTATE); + } + + // In case there is a failure short of our completely reading the power-up + // message. + pB->i2eValid = I2E_INCOMPLETE; + + + // Now attempt to read the message. + + for (itemp = 0; itemp < sizeof(porStr); itemp++) + { + // We expect the entire message is ready. + if (!I2_HAS_INPUT(pB)) { + pB->i2ePomSize = itemp; + I2_COMPLETE(pB, I2EE_PORM_SHORT); + } + + pB->i2ePom.c[itemp] = c = inb(pB->i2eData); + + // We check the magic numbers as soon as they are supposed to be read + // (rather than after) to minimize effect of reading something we + // already suspect can't be "us". + if ( (itemp == POR_1_INDEX && c != POR_MAGIC_1) || + (itemp == POR_2_INDEX && c != POR_MAGIC_2)) + { + pB->i2ePomSize = itemp+1; + I2_COMPLETE(pB, I2EE_BADMAGIC); + } + } + + pB->i2ePomSize = itemp; + + // Ensure that this was all the data... + if (I2_HAS_INPUT(pB)) + I2_COMPLETE(pB, I2EE_PORM_LONG); + + // For now, we'll fail to initialize if P.O.S.T reports bad chip mapper: + // Implying we will not be able to download any code either: That's ok: the + // condition is pretty explicit. + if (pB->i2ePom.e.porDiag1 & POR_BAD_MAPPER) + { + I2_COMPLETE(pB, I2EE_POSTERR); + } + + // Determine anything which must be done differently depending on the family + // of boards! + switch (pB->i2ePom.e.porID & POR_ID_FAMILY) + { + case POR_ID_FII: // IntelliPort-II + + pB->i2eFifoStyle = FIFO_II; + pB->i2eFifoSize = 512; // 512 bytes, always + pB->i2eDataWidth16 = false; + + pB->i2eMaxIrq = 15; // Because board cannot tell us it is in an 8-bit + // slot, we do allow it to be done (documentation!) + + pB->i2eGoodMap[1] = + pB->i2eGoodMap[2] = + pB->i2eGoodMap[3] = + pB->i2eChannelMap[1] = + pB->i2eChannelMap[2] = + pB->i2eChannelMap[3] = 0; + + switch (pB->i2ePom.e.porID & POR_ID_SIZE) + { + case POR_ID_II_4: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0x0f; // four-port + + // Since porPorts1 is based on the Hardware ID register, the numbers + // should always be consistent for IntelliPort-II. Ditto below... + if (pB->i2ePom.e.porPorts1 != 4) + { + I2_COMPLETE(pB, I2EE_INCONSIST); + } + break; + + case POR_ID_II_8: + case POR_ID_II_8R: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0xff; // Eight port + if (pB->i2ePom.e.porPorts1 != 8) + { + I2_COMPLETE(pB, I2EE_INCONSIST); + } + break; + + case POR_ID_II_6: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0x3f; // Six Port + if (pB->i2ePom.e.porPorts1 != 6) + { + I2_COMPLETE(pB, I2EE_INCONSIST); + } + break; + } + + // Fix up the "good channel list based on any errors reported. + if (pB->i2ePom.e.porDiag1 & POR_BAD_UART1) + { + pB->i2eGoodMap[0] &= ~0x0f; + } + + if (pB->i2ePom.e.porDiag1 & POR_BAD_UART2) + { + pB->i2eGoodMap[0] &= ~0xf0; + } + + break; // POR_ID_FII case + + case POR_ID_FIIEX: // IntelliPort-IIEX + + pB->i2eFifoStyle = FIFO_IIEX; + + itemp = pB->i2ePom.e.porFifoSize; + + // Implicit assumption that fifo would not grow beyond 32k, + // nor would ever be less than 256. + + if (itemp < 8 || itemp > 15) + { + I2_COMPLETE(pB, I2EE_INCONSIST); + } + pB->i2eFifoSize = (1 << itemp); + + // These are based on what P.O.S.T thinks should be there, based on + // box ID registers + ilimit = pB->i2ePom.e.porNumBoxes; + if (ilimit > ABS_MAX_BOXES) + { + ilimit = ABS_MAX_BOXES; + } + + // For as many boxes as EXIST, gives the type of box. + // Added 8/6/93: check for the ISA-4 (asic) which looks like an + // expandable but for whom "8 or 16?" is not the right question. + + utemp = pB->i2ePom.e.porFlags; + if (utemp & POR_CEX4) + { + pB->i2eChannelMap[0] = 0x000f; + } else { + utemp &= POR_BOXES; + for (itemp = 0; itemp < ilimit; itemp++) + { + pB->i2eChannelMap[itemp] = + ((utemp & POR_BOX_16) ? 0xffff : 0x00ff); + utemp >>= 1; + } + } + + // These are based on what P.O.S.T actually found. + + utemp = (pB->i2ePom.e.porPorts2 << 8) + pB->i2ePom.e.porPorts1; + + for (itemp = 0; itemp < ilimit; itemp++) + { + pB->i2eGoodMap[itemp] = 0; + if (utemp & 1) pB->i2eGoodMap[itemp] |= 0x000f; + if (utemp & 2) pB->i2eGoodMap[itemp] |= 0x00f0; + if (utemp & 4) pB->i2eGoodMap[itemp] |= 0x0f00; + if (utemp & 8) pB->i2eGoodMap[itemp] |= 0xf000; + utemp >>= 4; + } + + // Now determine whether we should transfer in 8 or 16-bit mode. + switch (pB->i2ePom.e.porBus & (POR_BUS_SLOT16 | POR_BUS_DIP16) ) + { + case POR_BUS_SLOT16 | POR_BUS_DIP16: + pB->i2eDataWidth16 = true; + pB->i2eMaxIrq = 15; + break; + + case POR_BUS_SLOT16: + pB->i2eDataWidth16 = false; + pB->i2eMaxIrq = 15; + break; + + case 0: + case POR_BUS_DIP16: // In an 8-bit slot, DIP switch don't care. + default: + pB->i2eDataWidth16 = false; + pB->i2eMaxIrq = 7; + break; + } + break; // POR_ID_FIIEX case + + default: // Unknown type of board + I2_COMPLETE(pB, I2EE_BAD_FAMILY); + break; + } // End the switch based on family + + // Temporarily, claim there is no room in the outbound fifo. + // We will maintain this whenever we check for an empty outbound FIFO. + pB->i2eFifoRemains = 0; + + // Now, based on the bus type, should we expect to be able to re-configure + // interrupts (say, for testing purposes). + switch (pB->i2ePom.e.porBus & POR_BUS_TYPE) + { + case POR_BUS_T_ISA: + case POR_BUS_T_UNK: // If the type of bus is undeclared, assume ok. + case POR_BUS_T_MCA: + case POR_BUS_T_EISA: + break; + default: + I2_COMPLETE(pB, I2EE_BADBUS); + } + + if (pB->i2eDataWidth16) + { + pB->i2eWriteBuf = iiWriteBuf16; + pB->i2eReadBuf = iiReadBuf16; + pB->i2eWriteWord = iiWriteWord16; + pB->i2eReadWord = iiReadWord16; + } else { + pB->i2eWriteBuf = iiWriteBuf8; + pB->i2eReadBuf = iiReadBuf8; + pB->i2eWriteWord = iiWriteWord8; + pB->i2eReadWord = iiReadWord8; + } + + switch(pB->i2eFifoStyle) + { + case FIFO_II: + pB->i2eWaitForTxEmpty = iiWaitForTxEmptyII; + pB->i2eTxMailEmpty = iiTxMailEmptyII; + pB->i2eTrySendMail = iiTrySendMailII; + pB->i2eGetMail = iiGetMailII; + pB->i2eEnableMailIrq = iiEnableMailIrqII; + pB->i2eWriteMask = iiWriteMaskII; + + break; + + case FIFO_IIEX: + pB->i2eWaitForTxEmpty = iiWaitForTxEmptyIIEX; + pB->i2eTxMailEmpty = iiTxMailEmptyIIEX; + pB->i2eTrySendMail = iiTrySendMailIIEX; + pB->i2eGetMail = iiGetMailIIEX; + pB->i2eEnableMailIrq = iiEnableMailIrqIIEX; + pB->i2eWriteMask = iiWriteMaskIIEX; + + break; + + default: + I2_COMPLETE(pB, I2EE_INCONSIST); + } + + // Initialize state information. + pB->i2eState = II_STATE_READY; // Ready to load loadware. + + // Some Final cleanup: + // For some boards, the bootstrap firmware may perform some sort of test + // resulting in a stray character pending in the incoming mailbox. If one is + // there, it should be read and discarded, especially since for the standard + // firmware, it's the mailbox that interrupts the host. + + pB->i2eStartMail = iiGetMail(pB); + + // Throw it away and clear the mailbox structure element + pB->i2eStartMail = NO_MAIL_HERE; + + // Everything is ok now, return with good status/ + + pB->i2eValid = I2E_MAGIC; + I2_COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: ii2DelayTimer(mseconds) +// Parameters: mseconds - number of milliseconds to delay +// +// Returns: Nothing +// +// Description: +// +// This routine delays for approximately mseconds milliseconds and is intended +// to be called indirectly through i2Delay field in i2eBordStr. It uses the +// Linux timer_list mechanism. +// +// The Linux timers use a unit called "jiffies" which are 10mS in the Intel +// architecture. This function rounds the delay period up to the next "jiffy". +// In the Alpha architecture the "jiffy" is 1mS, but this driver is not intended +// for Alpha platforms at this time. +// +//****************************************************************************** +static void +ii2DelayTimer(unsigned int mseconds) +{ + msleep_interruptible(mseconds); +} + +#if 0 +//static void ii2DelayIO(unsigned int); +//****************************************************************************** +// !!! Not Used, this is DOS crap, some of you young folks may be interested in +// in how things were done in the stone age of caculating machines !!! +// Function: ii2DelayIO(mseconds) +// Parameters: mseconds - number of milliseconds to delay +// +// Returns: Nothing +// +// Description: +// +// This routine delays for approximately mseconds milliseconds and is intended +// to be called indirectly through i2Delay field in i2eBordStr. It is intended +// for use where a clock-based function is impossible: for example, DOS drivers. +// +// This function uses the IN instruction to place bounds on the timing and +// assumes that ii2Safe has been set. This is because I/O instructions are not +// subject to caching and will therefore take a certain minimum time. To ensure +// the delay is at least long enough on fast machines, it is based on some +// fastest-case calculations. On slower machines this may cause VERY long +// delays. (3 x fastest case). In the fastest case, everything is cached except +// the I/O instruction itself. +// +// Timing calculations: +// The fastest bus speed for I/O operations is likely to be 10 MHz. The I/O +// operation in question is a byte operation to an odd address. For 8-bit +// operations, the architecture generally enforces two wait states. At 10 MHz, a +// single cycle time is 100nS. A read operation at two wait states takes 6 +// cycles for a total time of 600nS. Therefore approximately 1666 iterations +// would be required to generate a single millisecond delay. The worst +// (reasonable) case would be an 8MHz system with no cacheing. In this case, the +// I/O instruction would take 125nS x 6 cyles = 750 nS. More importantly, code +// fetch of other instructions in the loop would take time (zero wait states, +// however) and would be hard to estimate. This is minimized by using in-line +// assembler for the in inner loop of IN instructions. This consists of just a +// few bytes. So we'll guess about four code fetches per loop. Each code fetch +// should take four cycles, so we have 125nS * 8 = 1000nS. Worst case then is +// that what should have taken 1 mS takes instead 1666 * (1750) = 2.9 mS. +// +// So much for theoretical timings: results using 1666 value on some actual +// machines: +// IBM 286 6MHz 3.15 mS +// Zenith 386 33MHz 2.45 mS +// (brandX) 386 33MHz 1.90 mS (has cache) +// (brandY) 486 33MHz 2.35 mS +// NCR 486 ?? 1.65 mS (microchannel) +// +// For most machines, it is probably safe to scale this number back (remember, +// for robust operation use an actual timed delay if possible), so we are using +// a value of 1190. This yields 1.17 mS for the fastest machine in our sample, +// 1.75 mS for typical 386 machines, and 2.25 mS the absolute slowest machine. +// +// 1/29/93: +// The above timings are too slow. Actual cycle times might be faster. ISA cycle +// times could approach 500 nS, and ... +// The IBM model 77 being microchannel has no wait states for 8-bit reads and +// seems to be accessing the I/O at 440 nS per access (from start of one to +// start of next). This would imply we need 1000/.440 = 2272 iterations to +// guarantee we are fast enough. In actual testing, we see that 2 * 1190 are in +// fact enough. For diagnostics, we keep the level at 1190, but developers note +// this needs tuning. +// +// Safe assumption: 2270 i/o reads = 1 millisecond +// +//****************************************************************************** + + +static int ii2DelValue = 1190; // See timing calculations below + // 1666 for fastest theoretical machine + // 1190 safe for most fast 386 machines + // 1000 for fastest machine tested here + // 540 (sic) for AT286/6Mhz +static void +ii2DelayIO(unsigned int mseconds) +{ + if (!ii2Safe) + return; /* Do nothing if this variable uninitialized */ + + while(mseconds--) { + int i = ii2DelValue; + while ( i-- ) { + inb(ii2Safe); + } + } +} +#endif + +//****************************************************************************** +// Function: ii2Nop() +// Parameters: None +// +// Returns: Nothing +// +// Description: +// +// iiInitialize will set i2eDelay to this if the delay parameter is NULL. This +// saves checking for a NULL pointer at every call. +//****************************************************************************** +static void +ii2Nop(void) +{ + return; // no mystery here +} + +//======================================================= +// Routines which are available in 8/16-bit versions, or +// in different fifo styles. These are ALL called +// indirectly through the board structure. +//======================================================= + +//****************************************************************************** +// Function: iiWriteBuf16(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address of data to write +// count - number of data bytes to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes 'count' bytes from 'address' to the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// sent (identity unknown...). Uses 16-bit (word) operations. Is called +// indirectly through pB->i2eWriteBuf. +// +//****************************************************************************** +static int +iiWriteBuf16(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + I2_COMPLETE(pB, I2EE_INVALID); + + I2_OUTSW(pB->i2eData, address, count); + + I2_COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiWriteBuf8(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address of data to write +// count - number of data bytes to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes 'count' bytes from 'address' to the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// sent (identity unknown...). This is to be consistent with the 16-bit version. +// Uses 8-bit (byte) operations. Is called indirectly through pB->i2eWriteBuf. +// +//****************************************************************************** +static int +iiWriteBuf8(i2eBordStrPtr pB, unsigned char *address, int count) +{ + /* Rudimentary sanity checking here */ + if (pB->i2eValid != I2E_MAGIC) + I2_COMPLETE(pB, I2EE_INVALID); + + I2_OUTSB(pB->i2eData, address, count); + + I2_COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadBuf16(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address to put data read +// count - number of data bytes to read +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Reads 'count' bytes into 'address' from the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// received (identity unknown...). Uses 16-bit (word) operations. Is called +// indirectly through pB->i2eReadBuf. +// +//****************************************************************************** +static int +iiReadBuf16(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + I2_COMPLETE(pB, I2EE_INVALID); + + I2_INSW(pB->i2eData, address, count); + + I2_COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadBuf8(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address to put data read +// count - number of data bytes to read +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Reads 'count' bytes into 'address' from the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// received (identity unknown...). This to match the 16-bit behaviour. Uses +// 8-bit (byte) operations. Is called indirectly through pB->i2eReadBuf. +// +//****************************************************************************** +static int +iiReadBuf8(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + I2_COMPLETE(pB, I2EE_INVALID); + + I2_INSB(pB->i2eData, address, count); + + I2_COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadWord16(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Returns the word read from the data fifo specified by the board-structure +// pointer pB. Uses a 16-bit operation. Is called indirectly through +// pB->i2eReadWord. +// +//****************************************************************************** +static unsigned short +iiReadWord16(i2eBordStrPtr pB) +{ + return inw(pB->i2eData); +} + +//****************************************************************************** +// Function: iiReadWord8(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Returns the word read from the data fifo specified by the board-structure +// pointer pB. Uses two 8-bit operations. Bytes are assumed to be LSB first. Is +// called indirectly through pB->i2eReadWord. +// +//****************************************************************************** +static unsigned short +iiReadWord8(i2eBordStrPtr pB) +{ + unsigned short urs; + + urs = inb(pB->i2eData); + + return (inb(pB->i2eData) << 8) | urs; +} + +//****************************************************************************** +// Function: iiWriteWord16(pB, value) +// Parameters: pB - pointer to board structure +// value - data to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes the word 'value' to the data fifo specified by the board-structure +// pointer pB. Uses 16-bit operation. Is called indirectly through +// pB->i2eWriteWord. +// +//****************************************************************************** +static void +iiWriteWord16(i2eBordStrPtr pB, unsigned short value) +{ + outw((int)value, pB->i2eData); +} + +//****************************************************************************** +// Function: iiWriteWord8(pB, value) +// Parameters: pB - pointer to board structure +// value - data to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes the word 'value' to the data fifo specified by the board-structure +// pointer pB. Uses two 8-bit operations (writes LSB first). Is called +// indirectly through pB->i2eWriteWord. +// +//****************************************************************************** +static void +iiWriteWord8(i2eBordStrPtr pB, unsigned short value) +{ + outb((char)value, pB->i2eData); + outb((char)(value >> 8), pB->i2eData); +} + +//****************************************************************************** +// Function: iiWaitForTxEmptyII(pB, mSdelay) +// Parameters: pB - pointer to board structure +// mSdelay - period to wait before returning +// +// Returns: True if the FIFO is empty. +// False if it not empty in the required time: the pB->i2eError +// field has the error. +// +// Description: +// +// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if +// not empty by the required time, returns false and error in pB->i2eError, +// otherwise returns true. +// +// mSdelay == 0 is taken to mean must be empty on the first test. +// +// This version operates on IntelliPort-II - style FIFO's +// +// Note this routine is organized so that if status is ok there is no delay at +// all called either before or after the test. Is called indirectly through +// pB->i2eWaitForTxEmpty. +// +//****************************************************************************** +static int +iiWaitForTxEmptyII(i2eBordStrPtr pB, int mSdelay) +{ + unsigned long flags; + int itemp; + + for (;;) + { + // This routine hinges on being able to see the "other" status register + // (as seen by the local processor). His incoming fifo is our outgoing + // FIFO. + // + // By the nature of this routine, you would be using this as part of a + // larger atomic context: i.e., you would use this routine to ensure the + // fifo empty, then act on this information. Between these two halves, + // you will generally not want to service interrupts or in any way + // disrupt the assumptions implicit in the larger context. + // + // Even worse, however, this routine "shifts" the status register to + // point to the local status register which is not the usual situation. + // Therefore for extra safety, we force the critical section to be + // completely atomic, and pick up after ourselves before allowing any + // interrupts of any kind. + + + write_lock_irqsave(&Dl_spinlock, flags); + outb(SEL_COMMAND, pB->i2ePointer); + outb(SEL_CMD_SH, pB->i2ePointer); + + itemp = inb(pB->i2eStatus); + + outb(SEL_COMMAND, pB->i2ePointer); + outb(SEL_CMD_UNSH, pB->i2ePointer); + + if (itemp & ST_IN_EMPTY) + { + I2_UPDATE_FIFO_ROOM(pB); + write_unlock_irqrestore(&Dl_spinlock, flags); + I2_COMPLETE(pB, I2EE_GOOD); + } + + write_unlock_irqrestore(&Dl_spinlock, flags); + + if (mSdelay-- == 0) + break; + + iiDelay(pB, 1); /* 1 mS granularity on checking condition */ + } + I2_COMPLETE(pB, I2EE_TXE_TIME); +} + +//****************************************************************************** +// Function: iiWaitForTxEmptyIIEX(pB, mSdelay) +// Parameters: pB - pointer to board structure +// mSdelay - period to wait before returning +// +// Returns: True if the FIFO is empty. +// False if it not empty in the required time: the pB->i2eError +// field has the error. +// +// Description: +// +// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if +// not empty by the required time, returns false and error in pB->i2eError, +// otherwise returns true. +// +// mSdelay == 0 is taken to mean must be empty on the first test. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +// Note this routine is organized so that if status is ok there is no delay at +// all called either before or after the test. Is called indirectly through +// pB->i2eWaitForTxEmpty. +// +//****************************************************************************** +static int +iiWaitForTxEmptyIIEX(i2eBordStrPtr pB, int mSdelay) +{ + unsigned long flags; + + for (;;) + { + // By the nature of this routine, you would be using this as part of a + // larger atomic context: i.e., you would use this routine to ensure the + // fifo empty, then act on this information. Between these two halves, + // you will generally not want to service interrupts or in any way + // disrupt the assumptions implicit in the larger context. + + write_lock_irqsave(&Dl_spinlock, flags); + + if (inb(pB->i2eStatus) & STE_OUT_MT) { + I2_UPDATE_FIFO_ROOM(pB); + write_unlock_irqrestore(&Dl_spinlock, flags); + I2_COMPLETE(pB, I2EE_GOOD); + } + write_unlock_irqrestore(&Dl_spinlock, flags); + + if (mSdelay-- == 0) + break; + + iiDelay(pB, 1); // 1 mS granularity on checking condition + } + I2_COMPLETE(pB, I2EE_TXE_TIME); +} + +//****************************************************************************** +// Function: iiTxMailEmptyII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if the transmit mailbox is empty. +// False if it not empty. +// +// Description: +// +// Returns true or false according to whether the transmit mailbox is empty (and +// therefore able to accept more mail) +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static int +iiTxMailEmptyII(i2eBordStrPtr pB) +{ + int port = pB->i2ePointer; + outb(SEL_OUTMAIL, port); + return inb(port) == 0; +} + +//****************************************************************************** +// Function: iiTxMailEmptyIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if the transmit mailbox is empty. +// False if it not empty. +// +// Description: +// +// Returns true or false according to whether the transmit mailbox is empty (and +// therefore able to accept more mail) +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static int +iiTxMailEmptyIIEX(i2eBordStrPtr pB) +{ + return !(inb(pB->i2eStatus) & STE_OUT_MAIL); +} + +//****************************************************************************** +// Function: iiTrySendMailII(pB,mail) +// Parameters: pB - pointer to board structure +// mail - value to write to mailbox +// +// Returns: True if the transmit mailbox is empty, and mail is sent. +// False if it not empty. +// +// Description: +// +// If outgoing mailbox is empty, sends mail and returns true. If outgoing +// mailbox is not empty, returns false. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static int +iiTrySendMailII(i2eBordStrPtr pB, unsigned char mail) +{ + int port = pB->i2ePointer; + + outb(SEL_OUTMAIL, port); + if (inb(port) == 0) { + outb(SEL_OUTMAIL, port); + outb(mail, port); + return 1; + } + return 0; +} + +//****************************************************************************** +// Function: iiTrySendMailIIEX(pB,mail) +// Parameters: pB - pointer to board structure +// mail - value to write to mailbox +// +// Returns: True if the transmit mailbox is empty, and mail is sent. +// False if it not empty. +// +// Description: +// +// If outgoing mailbox is empty, sends mail and returns true. If outgoing +// mailbox is not empty, returns false. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static int +iiTrySendMailIIEX(i2eBordStrPtr pB, unsigned char mail) +{ + if (inb(pB->i2eStatus) & STE_OUT_MAIL) + return 0; + outb(mail, pB->i2eXMail); + return 1; +} + +//****************************************************************************** +// Function: iiGetMailII(pB,mail) +// Parameters: pB - pointer to board structure +// +// Returns: Mailbox data or NO_MAIL_HERE. +// +// Description: +// +// If no mail available, returns NO_MAIL_HERE otherwise returns the data from +// the mailbox, which is guaranteed != NO_MAIL_HERE. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static unsigned short +iiGetMailII(i2eBordStrPtr pB) +{ + if (I2_HAS_MAIL(pB)) { + outb(SEL_INMAIL, pB->i2ePointer); + return inb(pB->i2ePointer); + } else { + return NO_MAIL_HERE; + } +} + +//****************************************************************************** +// Function: iiGetMailIIEX(pB,mail) +// Parameters: pB - pointer to board structure +// +// Returns: Mailbox data or NO_MAIL_HERE. +// +// Description: +// +// If no mail available, returns NO_MAIL_HERE otherwise returns the data from +// the mailbox, which is guaranteed != NO_MAIL_HERE. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static unsigned short +iiGetMailIIEX(i2eBordStrPtr pB) +{ + if (I2_HAS_MAIL(pB)) + return inb(pB->i2eXMail); + else + return NO_MAIL_HERE; +} + +//****************************************************************************** +// Function: iiEnableMailIrqII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Enables board to interrupt host (only) by writing to host's in-bound mailbox. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static void +iiEnableMailIrqII(i2eBordStrPtr pB) +{ + outb(SEL_MASK, pB->i2ePointer); + outb(ST_IN_MAIL, pB->i2ePointer); +} + +//****************************************************************************** +// Function: iiEnableMailIrqIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Enables board to interrupt host (only) by writing to host's in-bound mailbox. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static void +iiEnableMailIrqIIEX(i2eBordStrPtr pB) +{ + outb(MX_IN_MAIL, pB->i2eXMask); +} + +//****************************************************************************** +// Function: iiWriteMaskII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Writes arbitrary value to the mask register. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static void +iiWriteMaskII(i2eBordStrPtr pB, unsigned char value) +{ + outb(SEL_MASK, pB->i2ePointer); + outb(value, pB->i2ePointer); +} + +//****************************************************************************** +// Function: iiWriteMaskIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Writes arbitrary value to the mask register. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static void +iiWriteMaskIIEX(i2eBordStrPtr pB, unsigned char value) +{ + outb(value, pB->i2eXMask); +} + +//****************************************************************************** +// Function: iiDownloadBlock(pB, pSource, isStandard) +// Parameters: pB - pointer to board structure +// pSource - loadware block to download +// isStandard - True if "standard" loadware, else false. +// +// Returns: Success or Failure +// +// Description: +// +// Downloads a single block (at pSource)to the board referenced by pB. Caller +// sets isStandard to true/false according to whether the "standard" loadware is +// what's being loaded. The normal process, then, is to perform an iiInitialize +// to the board, then perform some number of iiDownloadBlocks using the returned +// state to determine when download is complete. +// +// Possible return values: (see I2ELLIS.H) +// II_DOWN_BADVALID +// II_DOWN_BADFILE +// II_DOWN_CONTINUING +// II_DOWN_GOOD +// II_DOWN_BAD +// II_DOWN_BADSTATE +// II_DOWN_TIMEOUT +// +// Uses the i2eState and i2eToLoad fields (initialized at iiInitialize) to +// determine whether this is the first block, whether to check for magic +// numbers, how many blocks there are to go... +// +//****************************************************************************** +static int +iiDownloadBlock ( i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard) +{ + int itemp; + int loadedFirst; + + if (pB->i2eValid != I2E_MAGIC) return II_DOWN_BADVALID; + + switch(pB->i2eState) + { + case II_STATE_READY: + + // Loading the first block after reset. Must check the magic number of the + // loadfile, store the number of blocks we expect to load. + if (pSource->e.loadMagic != MAGIC_LOADFILE) + { + return II_DOWN_BADFILE; + } + + // Next we store the total number of blocks to load, including this one. + pB->i2eToLoad = 1 + pSource->e.loadBlocksMore; + + // Set the state, store the version numbers. ('Cause this may have come + // from a file - we might want to report these versions and revisions in + // case of an error! + pB->i2eState = II_STATE_LOADING; + pB->i2eLVersion = pSource->e.loadVersion; + pB->i2eLRevision = pSource->e.loadRevision; + pB->i2eLSub = pSource->e.loadSubRevision; + + // The time and date of compilation is also available but don't bother + // storing it for normal purposes. + loadedFirst = 1; + break; + + case II_STATE_LOADING: + loadedFirst = 0; + break; + + default: + return II_DOWN_BADSTATE; + } + + // Now we must be in the II_STATE_LOADING state, and we assume i2eToLoad + // must be positive still, because otherwise we would have cleaned up last + // time and set the state to II_STATE_LOADED. + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) { + return II_DOWN_TIMEOUT; + } + + if (!iiWriteBuf(pB, pSource->c, LOADWARE_BLOCK_SIZE)) { + return II_DOWN_BADVALID; + } + + // If we just loaded the first block, wait for the fifo to empty an extra + // long time to allow for any special startup code in the firmware, like + // sending status messages to the LCD's. + + if (loadedFirst) { + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_START_TIME)) { + return II_DOWN_TIMEOUT; + } + } + + // Determine whether this was our last block! + if (--(pB->i2eToLoad)) { + return II_DOWN_CONTINUING; // more to come... + } + + // It WAS our last block: Clean up operations... + // ...Wait for last buffer to drain from the board... + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) { + return II_DOWN_TIMEOUT; + } + // If there were only a single block written, this would come back + // immediately and be harmless, though not strictly necessary. + itemp = MAX_DLOAD_ACK_TIME/10; + while (--itemp) { + if (I2_HAS_INPUT(pB)) { + switch (inb(pB->i2eData)) { + case LOADWARE_OK: + pB->i2eState = + isStandard ? II_STATE_STDLOADED :II_STATE_LOADED; + + // Some revisions of the bootstrap firmware (e.g. ISA-8 1.0.2) + // will, // if there is a debug port attached, require some + // time to send information to the debug port now. It will do + // this before // executing any of the code we just downloaded. + // It may take up to 700 milliseconds. + if (pB->i2ePom.e.porDiag2 & POR_DEBUG_PORT) { + iiDelay(pB, 700); + } + + return II_DOWN_GOOD; + + case LOADWARE_BAD: + default: + return II_DOWN_BAD; + } + } + + iiDelay(pB, 10); // 10 mS granularity on checking condition + } + + // Drop-through --> timed out waiting for firmware confirmation + + pB->i2eState = II_STATE_BADLOAD; + return II_DOWN_TIMEOUT; +} + +//****************************************************************************** +// Function: iiDownloadAll(pB, pSource, isStandard, size) +// Parameters: pB - pointer to board structure +// pSource - loadware block to download +// isStandard - True if "standard" loadware, else false. +// size - size of data to download (in bytes) +// +// Returns: Success or Failure +// +// Description: +// +// Given a pointer to a board structure, a pointer to the beginning of some +// loadware, whether it is considered the "standard loadware", and the size of +// the array in bytes loads the entire array to the board as loadware. +// +// Assumes the board has been freshly reset and the power-up reset message read. +// (i.e., in II_STATE_READY). Complains if state is bad, or if there seems to be +// too much or too little data to load, or if iiDownloadBlock complains. +//****************************************************************************** +static int +iiDownloadAll(i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard, int size) +{ + int status; + + // We know (from context) board should be ready for the first block of + // download. Complain if not. + if (pB->i2eState != II_STATE_READY) return II_DOWN_BADSTATE; + + while (size > 0) { + size -= LOADWARE_BLOCK_SIZE; // How much data should there be left to + // load after the following operation ? + + // Note we just bump pSource by "one", because its size is actually that + // of an entire block, same as LOADWARE_BLOCK_SIZE. + status = iiDownloadBlock(pB, pSource++, isStandard); + + switch(status) + { + case II_DOWN_GOOD: + return ( (size > 0) ? II_DOWN_OVER : II_DOWN_GOOD); + + case II_DOWN_CONTINUING: + break; + + default: + return status; + } + } + + // We shouldn't drop out: it means "while" caught us with nothing left to + // download, yet the previous DownloadBlock did not return complete. Ergo, + // not enough data to match the size byte in the header. + return II_DOWN_UNDER; +} diff --git a/drivers/staging/tty/ip2/i2ellis.h b/drivers/staging/tty/ip2/i2ellis.h new file mode 100644 index 000000000000..fb6df2456018 --- /dev/null +++ b/drivers/staging/tty/ip2/i2ellis.h @@ -0,0 +1,566 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Mainline code for the device driver +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// i2ellis.h +// +// IntelliPort-II and IntelliPort-IIEX +// +// Extremely +// Low +// Level +// Interface +// Services +// +// Structure Definitions and declarations for "ELLIS" service routines found in +// i2ellis.c +// +// These routines are based on properties of the IntelliPort-II and -IIEX +// hardware and bootstrap firmware, and are not sensitive to particular +// conventions of any particular loadware. +// +// Unlike i2hw.h, which provides IRONCLAD hardware definitions, the material +// here and in i2ellis.c is intended to provice a useful, but not required, +// layer of insulation from the hardware specifics. +//------------------------------------------------------------------------------ +#ifndef I2ELLIS_H /* To prevent multiple includes */ +#define I2ELLIS_H 1 +//------------------------------------------------ +// Revision History: +// +// 30 September 1991 MAG First Draft Started +// 12 October 1991 ...continued... +// +// 20 December 1996 AKM Linux version +//------------------------------------------------- + +//---------------------- +// Mandatory Includes: +//---------------------- +#include "ip2types.h" +#include "i2hw.h" // The hardware definitions + +//------------------------------------------ +// STAT_BOXIDS packets +//------------------------------------------ +#define MAX_BOX 4 + +typedef struct _bidStat +{ + unsigned char bid_value[MAX_BOX]; +} bidStat, *bidStatPtr; + +// This packet is sent in response to a CMD_GET_BOXIDS bypass command. For -IIEX +// boards, reports the hardware-specific "asynchronous resource register" on +// each expansion box. Boxes not present report 0xff. For -II boards, the first +// element contains 0x80 for 8-port, 0x40 for 4-port boards. + +// Box IDs aka ARR or Async Resource Register (more than you want to know) +// 7 6 5 4 3 2 1 0 +// F F N N L S S S +// ============================= +// F F - Product Family Designator +// =====+++++++++++++++++++++++++++++++ +// 0 0 - Intelliport II EX / ISA-8 +// 1 0 - IntelliServer +// 0 1 - SAC - Port Device (Intelliport III ??? ) +// =====+++++++++++++++++++++++++++++++++++++++ +// N N - Number of Ports +// 0 0 - 8 (eight) +// 0 1 - 4 (four) +// 1 0 - 12 (twelve) +// 1 1 - 16 (sixteen) +// =++++++++++++++++++++++++++++++++++ +// L - LCD Display Module Present +// 0 - No +// 1 - LCD module present +// =========+++++++++++++++++++++++++++++++++++++ +// S S S - Async Signals Supported Designator +// 0 0 0 - 8dss, Mod DCE DB25 Female +// 0 0 1 - 6dss, RJ-45 +// 0 1 0 - RS-232/422 dss, DB25 Female +// 0 1 1 - RS-232/422 dss, separate 232/422 DB25 Female +// 1 0 0 - 6dss, 921.6 I/F with ST654's +// 1 0 1 - RS-423/232 8dss, RJ-45 10Pin +// 1 1 0 - 6dss, Mod DCE DB25 Female +// 1 1 1 - NO BOX PRESENT + +#define FF(c) ((c & 0xC0) >> 6) +#define NN(c) ((c & 0x30) >> 4) +#define L(c) ((c & 0x08) >> 3) +#define SSS(c) (c & 0x07) + +#define BID_HAS_654(x) (SSS(x) == 0x04) +#define BID_NO_BOX 0xff /* no box */ +#define BID_8PORT 0x80 /* IP2-8 port */ +#define BID_4PORT 0x81 /* IP2-4 port */ +#define BID_EXP_MASK 0x30 /* IP2-EX */ +#define BID_EXP_8PORT 0x00 /* 8, */ +#define BID_EXP_4PORT 0x10 /* 4, */ +#define BID_EXP_UNDEF 0x20 /* UNDEF, */ +#define BID_EXP_16PORT 0x30 /* 16, */ +#define BID_LCD_CTRL 0x08 /* LCD Controller */ +#define BID_LCD_NONE 0x00 /* - no controller present */ +#define BID_LCD_PRES 0x08 /* - controller present */ +#define BID_CON_MASK 0x07 /* - connector pinouts */ +#define BID_CON_DB25 0x00 /* - DB-25 F */ +#define BID_CON_RJ45 0x01 /* - rj45 */ + +//------------------------------------------------------------------------------ +// i2eBordStr +// +// This structure contains all the information the ELLIS routines require in +// dealing with a particular board. +//------------------------------------------------------------------------------ +// There are some queues here which are guaranteed to never contain the entry +// for a single channel twice. So they must be slightly larger to allow +// unambiguous full/empty management +// +#define CH_QUEUE_SIZE ABS_MOST_PORTS+2 + +typedef struct _i2eBordStr +{ + porStr i2ePom; // Structure containing the power-on message. + + unsigned short i2ePomSize; + // The number of bytes actually read if + // different from sizeof i2ePom, indicates + // there is an error! + + unsigned short i2eStartMail; + // Contains whatever inbound mailbox data + // present at startup. NO_MAIL_HERE indicates + // nothing was present. No special + // significance as of this writing, but may be + // useful for diagnostic reasons. + + unsigned short i2eValid; + // Indicates validity of the structure; if + // i2eValid == I2E_MAGIC, then we can trust + // the other fields. Some (especially + // initialization) functions are good about + // checking for validity. Many functions do + // not, it being assumed that the larger + // context assures we are using a valid + // i2eBordStrPtr. + + unsigned short i2eError; + // Used for returning an error condition from + // several functions which use i2eBordStrPtr + // as an argument. + + // Accelerators to characterize separate features of a board, derived from a + // number of sources. + + unsigned short i2eFifoSize; + // Always, the size of the FIFO. For + // IntelliPort-II, always the same, for -IIEX + // taken from the Power-On reset message. + + volatile + unsigned short i2eFifoRemains; + // Used during normal operation to indicate a + // lower bound on the amount of data which + // might be in the outbound fifo. + + unsigned char i2eFifoStyle; + // Accelerator which tells which style (-II or + // -IIEX) FIFO we are using. + + unsigned char i2eDataWidth16; + // Accelerator which tells whether we should + // do 8 or 16-bit data transfers. + + unsigned char i2eMaxIrq; + // The highest allowable IRQ, based on the + // slot size. + + // Accelerators for various addresses on the board + int i2eBase; // I/O Address of the Board + int i2eData; // From here data transfers happen + int i2eStatus; // From here status reads happen + int i2ePointer; // (IntelliPort-II: pointer/commands) + int i2eXMail; // (IntelliPOrt-IIEX: mailboxes + int i2eXMask; // (IntelliPort-IIEX: mask write + + //------------------------------------------------------- + // Information presented in a common format across boards + // For each box, bit map of the channels present. Box closest to + // the host is box 0. LSB is channel 0. IntelliPort-II (non-expandable) + // is taken to be box 0. These are derived from product i.d. registers. + + unsigned short i2eChannelMap[ABS_MAX_BOXES]; + + // Same as above, except each is derived from firmware attempting to detect + // the uart presence (by reading a valid GFRCR register). If bits are set in + // i2eChannelMap and not in i2eGoodMap, there is a potential problem. + + unsigned short i2eGoodMap[ABS_MAX_BOXES]; + + // --------------------------- + // For indirect function calls + + // Routine to cause an N-millisecond delay: Patched by the ii2Initialize + // function. + + void (*i2eDelay)(unsigned int); + + // Routine to write N bytes to the board through the FIFO. Returns true if + // all copacetic, otherwise returns false and error is in i2eError field. + // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER. + + int (*i2eWriteBuf)(struct _i2eBordStr *, unsigned char *, int); + + // Routine to read N bytes from the board through the FIFO. Returns true if + // copacetic, otherwise returns false and error in i2eError. + // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER. + + int (*i2eReadBuf)(struct _i2eBordStr *, unsigned char *, int); + + // Returns a word from FIFO. Will use 2 byte operations if needed. + + unsigned short (*i2eReadWord)(struct _i2eBordStr *); + + // Writes a word to FIFO. Will use 2 byte operations if needed. + + void (*i2eWriteWord)(struct _i2eBordStr *, unsigned short); + + // Waits specified time for the Transmit FIFO to go empty. Returns true if + // ok, otherwise returns false and error in i2eError. + + int (*i2eWaitForTxEmpty)(struct _i2eBordStr *, int); + + // Returns true or false according to whether the outgoing mailbox is empty. + + int (*i2eTxMailEmpty)(struct _i2eBordStr *); + + // Checks whether outgoing mailbox is empty. If so, sends mail and returns + // true. Otherwise returns false. + + int (*i2eTrySendMail)(struct _i2eBordStr *, unsigned char); + + // If no mail available, returns NO_MAIL_HERE, else returns the value in the + // mailbox (guaranteed can't be NO_MAIL_HERE). + + unsigned short (*i2eGetMail)(struct _i2eBordStr *); + + // Enables the board to interrupt the host when it writes to the mailbox. + // Irqs will not occur, however, until the loadware separately enables + // interrupt generation to the host. The standard loadware does this in + // response to a command packet sent by the host. (Also, disables + // any other potential interrupt sources from the board -- other than the + // inbound mailbox). + + void (*i2eEnableMailIrq)(struct _i2eBordStr *); + + // Writes an arbitrary value to the mask register. + + void (*i2eWriteMask)(struct _i2eBordStr *, unsigned char); + + + // State information + + // During downloading, indicates the number of blocks remaining to download + // to the board. + + short i2eToLoad; + + // State of board (see manifests below) (e.g., whether in reset condition, + // whether standard loadware is installed, etc. + + unsigned char i2eState; + + // These three fields are only valid when there is loadware running on the + // board. (i2eState == II_STATE_LOADED or i2eState == II_STATE_STDLOADED ) + + unsigned char i2eLVersion; // Loadware version + unsigned char i2eLRevision; // Loadware revision + unsigned char i2eLSub; // Loadware subrevision + + // Flags which only have meaning in the context of the standard loadware. + // Somewhat violates the layering concept, but there is so little additional + // needed at the board level (while much additional at the channel level), + // that this beats maintaining two different per-board structures. + + // Indicates which IRQ the board has been initialized (from software) to use + // For MicroChannel boards, any value different from IRQ_UNDEFINED means + // that the software command has been sent to enable interrupts (or specify + // they are disabled). Special value: IRQ_UNDEFINED indicates that the + // software command to select the interrupt has not yet been sent, therefore + // (since the standard loadware insists that it be sent before any other + // packets are sent) no other packets should be sent yet. + + unsigned short i2eUsingIrq; + + // This is set when we hit the MB_OUT_STUFFED mailbox, which prevents us + // putting more in the mailbox until an appropriate mailbox message is + // received. + + unsigned char i2eWaitingForEmptyFifo; + + // Any mailbox bits waiting to be sent to the board are OR'ed in here. + + unsigned char i2eOutMailWaiting; + + // The head of any incoming packet is read into here, is then examined and + // we dispatch accordingly. + + unsigned short i2eLeadoffWord[1]; + + // Running counter of interrupts where the mailbox indicated incoming data. + + unsigned short i2eFifoInInts; + + // Running counter of interrupts where the mailbox indicated outgoing data + // had been stripped. + + unsigned short i2eFifoOutInts; + + // If not void, gives the address of a routine to call if fatal board error + // is found (only applies to standard l/w). + + void (*i2eFatalTrap)(struct _i2eBordStr *); + + // Will point to an array of some sort of channel structures (whose format + // is unknown at this level, being a function of what loadware is + // installed and the code configuration (max sizes of buffers, etc.)). + + void *i2eChannelPtr; + + // Set indicates that the board has gone fatal. + + unsigned short i2eFatal; + + // The number of elements pointed to by i2eChannelPtr. + + unsigned short i2eChannelCnt; + + // Ring-buffers of channel structures whose channels have particular needs. + + rwlock_t Fbuf_spinlock; + volatile + unsigned short i2Fbuf_strip; // Strip index + volatile + unsigned short i2Fbuf_stuff; // Stuff index + void *i2Fbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // flow control packets. + rwlock_t Dbuf_spinlock; + volatile + unsigned short i2Dbuf_strip; // Strip index + volatile + unsigned short i2Dbuf_stuff; // Stuff index + void *i2Dbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // data or in-line command packets. + rwlock_t Bbuf_spinlock; + volatile + unsigned short i2Bbuf_strip; // Strip index + volatile + unsigned short i2Bbuf_stuff; // Stuff index + void *i2Bbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // bypass command packets. + + /* + * A set of flags to indicate that certain events have occurred on at least + * one of the ports on this board. We use this to decide whether to spin + * through the channels looking for breaks, etc. + */ + int got_input; + int status_change; + bidStat channelBtypes; + + /* + * Debugging counters, etc. + */ + unsigned long debugFlowQueued; + unsigned long debugInlineQueued; + unsigned long debugDataQueued; + unsigned long debugBypassQueued; + unsigned long debugFlowCount; + unsigned long debugInlineCount; + unsigned long debugBypassCount; + + rwlock_t read_fifo_spinlock; + rwlock_t write_fifo_spinlock; + +// For queuing interrupt bottom half handlers. /\/\|=mhw=|\/\/ + struct work_struct tqueue_interrupt; + + struct timer_list SendPendingTimer; // Used by iiSendPending + unsigned int SendPendingRetry; +} i2eBordStr, *i2eBordStrPtr; + +//------------------------------------------------------------------- +// Macro Definitions for the indirect calls defined in the i2eBordStr +//------------------------------------------------------------------- +// +#define iiDelay(a,b) (*(a)->i2eDelay)(b) +#define iiWriteBuf(a,b,c) (*(a)->i2eWriteBuf)(a,b,c) +#define iiReadBuf(a,b,c) (*(a)->i2eReadBuf)(a,b,c) + +#define iiWriteWord(a,b) (*(a)->i2eWriteWord)(a,b) +#define iiReadWord(a) (*(a)->i2eReadWord)(a) + +#define iiWaitForTxEmpty(a,b) (*(a)->i2eWaitForTxEmpty)(a,b) + +#define iiTxMailEmpty(a) (*(a)->i2eTxMailEmpty)(a) +#define iiTrySendMail(a,b) (*(a)->i2eTrySendMail)(a,b) + +#define iiGetMail(a) (*(a)->i2eGetMail)(a) +#define iiEnableMailIrq(a) (*(a)->i2eEnableMailIrq)(a) +#define iiDisableMailIrq(a) (*(a)->i2eWriteMask)(a,0) +#define iiWriteMask(a,b) (*(a)->i2eWriteMask)(a,b) + +//------------------------------------------- +// Manifests for i2eBordStr: +//------------------------------------------- + +typedef void (*delayFunc_t)(unsigned int); + +// i2eValid +// +#define I2E_MAGIC 0x4251 // Structure is valid. +#define I2E_INCOMPLETE 0x1122 // Structure failed during init. + + +// i2eError +// +#define I2EE_GOOD 0 // Operation successful +#define I2EE_BADADDR 1 // Address out of range +#define I2EE_BADSTATE 2 // Attempt to perform a function when the board + // structure was in the incorrect state +#define I2EE_BADMAGIC 3 // Bad magic number from Power On test (i2ePomSize + // reflects what was read +#define I2EE_PORM_SHORT 4 // Power On message too short +#define I2EE_PORM_LONG 5 // Power On message too long +#define I2EE_BAD_FAMILY 6 // Un-supported board family type +#define I2EE_INCONSIST 7 // Firmware reports something impossible, + // e.g. unexpected number of ports... Almost no + // excuse other than bad FIFO... +#define I2EE_POSTERR 8 // Power-On self test reported a bad error +#define I2EE_BADBUS 9 // Unknown Bus type declared in message +#define I2EE_TXE_TIME 10 // Timed out waiting for TX Fifo to empty +#define I2EE_INVALID 11 // i2eValid field does not indicate a valid and + // complete board structure (for functions which + // require this be so.) +#define I2EE_BAD_PORT 12 // Discrepancy between channels actually found and + // what the product is supposed to have. Check + // i2eGoodMap vs i2eChannelMap for details. +#define I2EE_BAD_IRQ 13 // Someone specified an unsupported IRQ +#define I2EE_NOCHANNELS 14 // No channel structures have been defined (for + // functions requiring this). + +// i2eFifoStyle +// +#define FIFO_II 0 /* IntelliPort-II style: see also i2hw.h */ +#define FIFO_IIEX 1 /* IntelliPort-IIEX style */ + +// i2eGetMail +// +#define NO_MAIL_HERE 0x1111 // Since mail is unsigned char, cannot possibly + // promote to 0x1111. +// i2eState +// +#define II_STATE_COLD 0 // Addresses have been defined, but board not even + // reset yet. +#define II_STATE_RESET 1 // Board,if it exists, has just been reset +#define II_STATE_READY 2 // Board ready for its first block +#define II_STATE_LOADING 3 // Board continuing load +#define II_STATE_LOADED 4 // Board has finished load: status ok +#define II_STATE_BADLOAD 5 // Board has finished load: failed! +#define II_STATE_STDLOADED 6 // Board has finished load: standard firmware + +// i2eUsingIrq +// +#define I2_IRQ_UNDEFINED 0x1352 /* No valid irq (or polling = 0) can + * ever promote to this! */ +//------------------------------------------ +// Handy Macros for i2ellis.c and others +// Note these are common to -II and -IIEX +//------------------------------------------ + +// Given a pointer to the board structure, does the input FIFO have any data or +// not? +// +#define I2_HAS_INPUT(pB) !(inb(pB->i2eStatus) & ST_IN_EMPTY) + +// Given a pointer to the board structure, is there anything in the incoming +// mailbox? +// +#define I2_HAS_MAIL(pB) (inb(pB->i2eStatus) & ST_IN_MAIL) + +#define I2_UPDATE_FIFO_ROOM(pB) ((pB)->i2eFifoRemains = (pB)->i2eFifoSize) + +//------------------------------------------ +// Function Declarations for i2ellis.c +//------------------------------------------ +// +// Functions called directly +// +// Initialization of a board & structure is in four (five!) parts: +// +// 1) iiSetAddress() - Define the board address & delay function for a board. +// 2) iiReset() - Reset the board (provided it exists) +// -- Note you may do this to several boards -- +// 3) iiResetDelay() - Delay for 2 seconds (once for all boards) +// 4) iiInitialize() - Attempt to read Power-up message; further initialize +// accelerators +// +// Then you may use iiDownloadAll() or iiDownloadFile() (in i2file.c) to write +// loadware. To change loadware, you must begin again with step 2, resetting +// the board again (step 1 not needed). + +static int iiSetAddress(i2eBordStrPtr, int, delayFunc_t ); +static int iiReset(i2eBordStrPtr); +static int iiResetDelay(i2eBordStrPtr); +static int iiInitialize(i2eBordStrPtr); + +// Routine to validate that all channels expected are there. +// +extern int iiValidateChannels(i2eBordStrPtr); + +// Routine used to download a block of loadware. +// +static int iiDownloadBlock(i2eBordStrPtr, loadHdrStrPtr, int); + +// Return values given by iiDownloadBlock, iiDownloadAll, iiDownloadFile: +// +#define II_DOWN_BADVALID 0 // board structure is invalid +#define II_DOWN_CONTINUING 1 // So far, so good, firmware expects more +#define II_DOWN_GOOD 2 // Download complete, CRC good +#define II_DOWN_BAD 3 // Download complete, but CRC bad +#define II_DOWN_BADFILE 4 // Bad magic number in loadware file +#define II_DOWN_BADSTATE 5 // Board is in an inappropriate state for + // downloading loadware. (see i2eState) +#define II_DOWN_TIMEOUT 6 // Timeout waiting for firmware +#define II_DOWN_OVER 7 // Too much data +#define II_DOWN_UNDER 8 // Not enough data +#define II_DOWN_NOFILE 9 // Loadware file not found + +// Routine to download an entire loadware module: Return values are a subset of +// iiDownloadBlock's, excluding, of course, II_DOWN_CONTINUING +// +static int iiDownloadAll(i2eBordStrPtr, loadHdrStrPtr, int, int); + +// Many functions defined here return True if good, False otherwise, with an +// error code in i2eError field. Here is a handy macro for setting the error +// code and returning. +// +#define I2_COMPLETE(pB,code) do { \ + pB->i2eError = code; \ + return (code == I2EE_GOOD);\ + } while (0) + +#endif // I2ELLIS_H diff --git a/drivers/staging/tty/ip2/i2hw.h b/drivers/staging/tty/ip2/i2hw.h new file mode 100644 index 000000000000..c0ba6c05f0cd --- /dev/null +++ b/drivers/staging/tty/ip2/i2hw.h @@ -0,0 +1,652 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions limited to properties of the hardware or the +* bootstrap firmware. As such, they are applicable regardless of +* operating system or loadware (standard or diagnostic). +* +*******************************************************************************/ +#ifndef I2HW_H +#define I2HW_H 1 +//------------------------------------------------------------------------------ +// Revision History: +// +// 23 September 1991 MAG First Draft Started...through... +// 11 October 1991 ... Continuing development... +// 6 August 1993 Added support for ISA-4 (asic) which is architected +// as an ISA-CEX with a single 4-port box. +// +// 20 December 1996 AKM Version for Linux +// +//------------------------------------------------------------------------------ +/*------------------------------------------------------------------------------ + +HARDWARE DESCRIPTION: + +Introduction: + +The IntelliPort-II and IntelliPort-IIEX products occupy a block of eight (8) +addresses in the host's I/O space. + +Some addresses are used to transfer data to/from the board, some to transfer +so-called "mailbox" messages, and some to read bit-mapped status information. +While all the products in the line are functionally similar, some use a 16-bit +data path to transfer data while others use an 8-bit path. Also, the use of +command /status/mailbox registers differs slightly between the II and IIEX +branches of the family. + +The host determines what type of board it is dealing with by reading a string of +sixteen characters from the board. These characters are always placed in the +fifo by the board's local processor whenever the board is reset (either from +power-on or under software control) and are known as the "Power-on Reset +Message." In order that this message can be read from either type of board, the +hardware registers used in reading this message are the same. Once this message +has been read by the host, then it has the information required to operate. + +General Differences between boards: + +The greatest structural difference is between the -II and -IIEX families of +product. The -II boards use the Am4701 dual 512x8 bidirectional fifo to support +the data path, mailbox registers, and status registers. This chip contains some +features which are not used in the IntelliPort-II products; a description of +these is omitted here. Because of these many features, it contains many +registers, too many to access directly within a small address space. They are +accessed by first writing a value to a "pointer" register. This value selects +the register to be accessed. The next read or write to that address accesses +the selected register rather than the pointer register. + +The -IIEX boards use a proprietary design similar to the Am4701 in function. But +because of a simpler, more streamlined design it doesn't require so many +registers. This means they can be accessed directly in single operations rather +than through a pointer register. + +Besides these differences, there are differences in whether 8-bit or 16-bit +transfers are used to move data to the board. + +The -II boards are capable only of 8-bit data transfers, while the -IIEX boards +may be configured for either 8-bit or 16-bit data transfers. If the on-board DIP +switch #8 is ON, and the card has been installed in a 16-bit slot, 16-bit +transfers are supported (and will be expected by the standard loadware). The +on-board firmware can determine the position of the switch, and whether the +board is installed in a 16-bit slot; it supplies this information to the host as +part of the power-up reset message. + +The configuration switch (#8) and slot selection do not directly configure the +hardware. It is up to the on-board loadware and host-based drivers to act +according to the selected options. That is, loadware and drivers could be +written to perform 8-bit transfers regardless of the state of the DIP switch or +slot (and in a diagnostic environment might well do so). Likewise, 16-bit +transfers could be performed as long as the card is in a 16-bit slot. + +Note the slot selection and DIP switch selection are provided separately: a +board running in 8-bit mode in a 16-bit slot has a greater range of possible +interrupts to choose from; information of potential use to the host. + +All 8-bit data transfers are done in the same way, regardless of whether on a +-II board or a -IIEX board. + +The host must consider two things then: 1) whether a -II or -IIEX product is +being used, and 2) whether an 8-bit or 16-bit data path is used. + +A further difference is that -II boards always have a 512-byte fifo operating in +each direction. -IIEX boards may use fifos of varying size; this size is +reported as part of the power-up message. + +I/O Map Of IntelliPort-II and IntelliPort-IIEX boards: +(Relative to the chosen base address) + +Addr R/W IntelliPort-II IntelliPort-IIEX +---- --- -------------- ---------------- +0 R/W Data Port (byte) Data Port (byte or word) +1 R/W (Not used) (MSB of word-wide data written to Data Port) +2 R Status Register Status Register +2 W Pointer Register Interrupt Mask Register +3 R/W (Not used) Mailbox Registers (6 bits: 11111100) +4,5 -- Reserved for future products +6 -- Reserved for future products +7 R Guaranteed to have no effect +7 W Hardware reset of board. + + +Rules: +All data transfers are performed using the even i/o address. If byte-wide data +transfers are being used, do INB/OUTB operations on the data port. If word-wide +transfers are used, do INW/OUTW operations. In some circumstances (such as +reading the power-up message) you will do INB from the data port, but in this +case the MSB of each word read is lost. When accessing all other unreserved +registers, use byte operations only. +------------------------------------------------------------------------------*/ + +//------------------------------------------------ +// Mandatory Includes: +//------------------------------------------------ +// +#include "ip2types.h" + +//------------------------------------------------------------------------- +// Manifests for the I/O map: +//------------------------------------------------------------------------- +// R/W: Data port (byte) for IntelliPort-II, +// R/W: Data port (byte or word) for IntelliPort-IIEX +// Incoming or outgoing data passes through a FIFO, the status of which is +// available in some of the bits in FIFO_STATUS. This (bidirectional) FIFO is +// the primary means of transferring data, commands, flow-control, and status +// information between the host and board. +// +#define FIFO_DATA 0 + +// Another way of passing information between the board and the host is +// through "mailboxes". Unlike a FIFO, a mailbox holds only a single byte of +// data. Writing data to the mailbox causes a status bit to be set, and +// potentially interrupting the intended receiver. The sender has some way to +// determine whether the data has been read yet; as soon as it has, it may send +// more. The mailboxes are handled differently on -II and -IIEX products, as +// suggested below. +//------------------------------------------------------------------------------ +// Read: Status Register for IntelliPort-II or -IIEX +// The presence of any bit set here will cause an interrupt to the host, +// provided the corresponding bit has been unmasked in the interrupt mask +// register. Furthermore, interrupts to the host are disabled globally until the +// loadware selects the irq line to use. With the exception of STN_MR, the bits +// remain set so long as the associated condition is true. +// +#define FIFO_STATUS 2 + +// Bit map of status bits which are identical for -II and -IIEX +// +#define ST_OUT_FULL 0x40 // Outbound FIFO full +#define ST_IN_EMPTY 0x20 // Inbound FIFO empty +#define ST_IN_MAIL 0x04 // Inbound Mailbox full + +// The following exists only on the Intelliport-IIEX, and indicates that the +// board has not read the last outgoing mailbox data yet. In the IntelliPort-II, +// the outgoing mailbox may be read back: a zero indicates the board has read +// the data. +// +#define STE_OUT_MAIL 0x80 // Outbound mailbox full (!) + +// The following bits are defined differently for -II and -IIEX boards. Code +// which relies on these bits will need to be functionally different for the two +// types of boards and should be generally avoided because of the additional +// complexity this creates: + +// Bit map of status bits only on -II + +// Fifo has been RESET (cleared when the status register is read). Note that +// this condition cannot be masked and would always interrupt the host, except +// that the hardware reset also disables interrupts globally from the board +// until re-enabled by loadware. This could also arise from the +// Am4701-supported command to reset the chip, but this command is generally not +// used here. +// +#define STN_MR 0x80 + +// See the AMD Am4701 data sheet for details on the following four bits. They +// are not presently used by Computone drivers. +// +#define STN_OUT_AF 0x10 // Outbound FIFO almost full (programmable) +#define STN_IN_AE 0x08 // Inbound FIFO almost empty (programmable) +#define STN_BD 0x02 // Inbound byte detected +#define STN_PE 0x01 // Parity/Framing condition detected + +// Bit-map of status bits only on -IIEX +// +#define STE_OUT_HF 0x10 // Outbound FIFO half full +#define STE_IN_HF 0x08 // Inbound FIFO half full +#define STE_IN_FULL 0x02 // Inbound FIFO full +#define STE_OUT_MT 0x01 // Outbound FIFO empty + +//------------------------------------------------------------------------------ + +// Intelliport-II -- Write Only: the pointer register. +// Values are written to this register to select the Am4701 internal register to +// be accessed on the next operation. +// +#define FIFO_PTR 0x02 + +// Values for the pointer register +// +#define SEL_COMMAND 0x1 // Selects the Am4701 command register + +// Some possible commands: +// +#define SEL_CMD_MR 0x80 // Am4701 command to reset the chip +#define SEL_CMD_SH 0x40 // Am4701 command to map the "other" port into the + // status register. +#define SEL_CMD_UNSH 0 // Am4701 command to "unshift": port maps into its + // own status register. +#define SEL_MASK 0x2 // Selects the Am4701 interrupt mask register. The + // interrupt mask register is bit-mapped to match + // the status register (FIFO_STATUS) except for + // STN_MR. (See above.) +#define SEL_BYTE_DET 0x3 // Selects the Am4701 byte-detect register. (Not + // normally used except in diagnostics.) +#define SEL_OUTMAIL 0x4 // Selects the outbound mailbox (R/W). Reading back + // a value of zero indicates that the mailbox has + // been read by the board and is available for more + // data./ Writing to the mailbox optionally + // interrupts the board, depending on the loadware's + // setting of its interrupt mask register. +#define SEL_AEAF 0x5 // Selects AE/AF threshold register. +#define SEL_INMAIL 0x6 // Selects the inbound mailbox (Read) + +//------------------------------------------------------------------------------ +// IntelliPort-IIEX -- Write Only: interrupt mask (and misc flags) register: +// Unlike IntelliPort-II, bit assignments do NOT match those of the status +// register. +// +#define FIFO_MASK 0x2 + +// Mailbox readback select: +// If set, reads to FIFO_MAIL will read the OUTBOUND mailbox (host to board). If +// clear (default on reset) reads to FIFO_MAIL will read the INBOUND mailbox. +// This is the normal situation. The clearing of a mailbox is determined on +// -IIEX boards by waiting for the STE_OUT_MAIL bit to clear. Readback +// capability is provided for diagnostic purposes only. +// +#define MX_OUTMAIL_RSEL 0x80 + +#define MX_IN_MAIL 0x40 // Enables interrupts when incoming mailbox goes + // full (ST_IN_MAIL set). +#define MX_IN_FULL 0x20 // Enables interrupts when incoming FIFO goes full + // (STE_IN_FULL). +#define MX_IN_MT 0x08 // Enables interrupts when incoming FIFO goes empty + // (ST_IN_MT). +#define MX_OUT_FULL 0x04 // Enables interrupts when outgoing FIFO goes full + // (ST_OUT_FULL). +#define MX_OUT_MT 0x01 // Enables interrupts when outgoing FIFO goes empty + // (STE_OUT_MT). + +// Any remaining bits are reserved, and should be written to ZERO for +// compatibility with future Computone products. + +//------------------------------------------------------------------------------ +// IntelliPort-IIEX: -- These are only 6-bit mailboxes !!! -- 11111100 (low two +// bits always read back 0). +// Read: One of the mailboxes, usually Inbound. +// Inbound Mailbox (MX_OUTMAIL_RSEL = 0) +// Outbound Mailbox (MX_OUTMAIL_RSEL = 1) +// Write: Outbound Mailbox +// For the IntelliPort-II boards, the outbound mailbox is read back to determine +// whether the board has read the data (0 --> data has been read). For the +// IntelliPort-IIEX, this is done by reading a status register. To determine +// whether mailbox is available for more outbound data, use the STE_OUT_MAIL bit +// in FIFO_STATUS. Moreover, although the Outbound Mailbox can be read back by +// setting MX_OUTMAIL_RSEL, it is NOT cleared when the board reads it, as is the +// case with the -II boards. For this reason, FIFO_MAIL is normally used to read +// the inbound FIFO, and MX_OUTMAIL_RSEL kept clear. (See above for +// MX_OUTMAIL_RSEL description.) +// +#define FIFO_MAIL 0x3 + +//------------------------------------------------------------------------------ +// WRITE ONLY: Resets the board. (Data doesn't matter). +// +#define FIFO_RESET 0x7 + +//------------------------------------------------------------------------------ +// READ ONLY: Will have no effect. (Data is undefined.) +// Actually, there will be an effect, in that the operation is sure to generate +// a bus cycle: viz., an I/O byte Read. This fact can be used to enforce short +// delays when no comparable time constant is available. +// +#define FIFO_NOP 0x7 + +//------------------------------------------------------------------------------ +// RESET & POWER-ON RESET MESSAGE +/*------------------------------------------------------------------------------ +RESET: + +The IntelliPort-II and -IIEX boards are reset in three ways: Power-up, channel +reset, and via a write to the reset register described above. For products using +the ISA bus, these three sources of reset are equvalent. For MCA and EISA buses, +the Power-up and channel reset sources cause additional hardware initialization +which should only occur at system startup time. + +The third type of reset, called a "command reset", is done by writing any data +to the FIFO_RESET address described above. This resets the on-board processor, +FIFO, UARTS, and associated hardware. + +This passes control of the board to the bootstrap firmware, which performs a +Power-On Self Test and which detects its current configuration. For example, +-IIEX products determine the size of FIFO which has been installed, and the +number and type of expansion boxes attached. + +This and other information is then written to the FIFO in a 16-byte data block +to be read by the host. This block is guaranteed to be present within two (2) +seconds of having received the command reset. The firmware is now ready to +receive loadware from the host. + +It is good practice to perform a command reset to the board explicitly as part +of your software initialization. This allows your code to properly restart from +a soft boot. (Many systems do not issue channel reset on soft boot). + +Because of a hardware reset problem on some of the Cirrus Logic 1400's which are +used on the product, it is recommended that you reset the board twice, separated +by an approximately 50 milliseconds delay. (VERY approximately: probably ok to +be off by a factor of five. The important point is that the first command reset +in fact generates a reset pulse on the board. This pulse is guaranteed to last +less than 10 milliseconds. The additional delay ensures the 1400 has had the +chance to respond sufficiently to the first reset. Why not a longer delay? Much +more than 50 milliseconds gets to be noticable, but the board would still work. + +Once all 16 bytes of the Power-on Reset Message have been read, the bootstrap +firmware is ready to receive loadware. + +Note on Power-on Reset Message format: +The various fields have been designed with future expansion in view. +Combinations of bitfields and values have been defined which define products +which may not currently exist. This has been done to allow drivers to anticipate +the possible introduction of products in a systematic fashion. This is not +intended to suggest that each potential product is actually under consideration. +------------------------------------------------------------------------------*/ + +//---------------------------------------- +// Format of Power-on Reset Message +//---------------------------------------- + +typedef union _porStr // "por" stands for Power On Reset +{ + unsigned char c[16]; // array used when considering the message as a + // string of undifferentiated characters + + struct // Elements used when considering values + { + // The first two bytes out of the FIFO are two magic numbers. These are + // intended to establish that there is indeed a member of the + // IntelliPort-II(EX) family present. The remaining bytes may be + // expected // to be valid. When reading the Power-on Reset message, + // if the magic numbers do not match it is probably best to stop + // reading immediately. You are certainly not reading our board (unless + // hardware is faulty), and may in fact be reading some other piece of + // hardware. + + unsigned char porMagic1; // magic number: first byte == POR_MAGIC_1 + unsigned char porMagic2; // magic number: second byte == POR_MAGIC_2 + + // The Version, Revision, and Subrevision are stored as absolute numbers + // and would normally be displayed in the format V.R.S (e.g. 1.0.2) + + unsigned char porVersion; // Bootstrap firmware version number + unsigned char porRevision; // Bootstrap firmware revision number + unsigned char porSubRev; // Bootstrap firmware sub-revision number + + unsigned char porID; // Product ID: Bit-mapped according to + // conventions described below. Among other + // things, this allows us to distinguish + // IntelliPort-II boards from IntelliPort-IIEX + // boards. + + unsigned char porBus; // IntelliPort-II: Unused + // IntelliPort-IIEX: Bus Information: + // Bit-mapped below + + unsigned char porMemory; // On-board DRAM size: in 32k blocks + + // porPorts1 (and porPorts2) are used to determine the ports which are + // available to the board. For non-expandable product, a single number + // is sufficient. For expandable product, the board may be connected + // to as many as four boxes. Each box may be (so far) either a 16-port + // or an 8-port size. Whenever an 8-port box is used, the remaining 8 + // ports leave gaps between existing channels. For that reason, + // expandable products must report a MAP of available channels. Since + // each UART supports four ports, we represent each UART found by a + // single bit. Using two bytes to supply the mapping information we + // report the presense or absense of up to 16 UARTS, or 64 ports in + // steps of 4 ports. For -IIEX products, the ports are numbered + // starting at the box closest to the controller in the "chain". + + // Interpreted Differently for IntelliPort-II and -IIEX. + // -II: Number of ports (Derived actually from product ID). See + // Diag1&2 to indicate if uart was actually detected. + // -IIEX: Bit-map of UARTS found, LSB (see below for MSB of this). This + // bitmap is based on detecting the uarts themselves; + // see porFlags for information from the box i.d's. + unsigned char porPorts1; + + unsigned char porDiag1; // Results of on-board P.O.S.T, 1st byte + unsigned char porDiag2; // Results of on-board P.O.S.T, 2nd byte + unsigned char porSpeed; // Speed of local CPU: given as MHz x10 + // e.g., 16.0 MHz CPU is reported as 160 + unsigned char porFlags; // Misc information (see manifests below) + // Bit-mapped: CPU type, UART's present + + unsigned char porPorts2; // -II: Undefined + // -IIEX: Bit-map of UARTS found, MSB (see + // above for LSB) + + // IntelliPort-II: undefined + // IntelliPort-IIEX: 1 << porFifoSize gives the size, in bytes, of the + // host interface FIFO, in each direction. When running the -IIEX in + // 8-bit mode, fifo capacity is halved. The bootstrap firmware will + // have already accounted for this fact in generating this number. + unsigned char porFifoSize; + + // IntelliPort-II: undefined + // IntelliPort-IIEX: The number of boxes connected. (Presently 1-4) + unsigned char porNumBoxes; + } e; +} porStr, *porStrPtr; + +//-------------------------- +// Values for porStr fields +//-------------------------- + +//--------------------- +// porMagic1, porMagic2 +//---------------------- +// +#define POR_MAGIC_1 0x96 // The only valid value for porMagic1 +#define POR_MAGIC_2 0x35 // The only valid value for porMagic2 +#define POR_1_INDEX 0 // Byte position of POR_MAGIC_1 +#define POR_2_INDEX 1 // Ditto for POR_MAGIC_2 + +//---------------------- +// porID +//---------------------- +// +#define POR_ID_FAMILY 0xc0 // These bits indicate the general family of + // product. +#define POR_ID_FII 0x00 // Family is "IntelliPort-II" +#define POR_ID_FIIEX 0x40 // Family is "IntelliPort-IIEX" + +// These bits are reserved, presently zero. May be used at a later date to +// convey other product information. +// +#define POR_ID_RESERVED 0x3c + +#define POR_ID_SIZE 0x03 // Remaining bits indicate number of ports & + // Connector information. +#define POR_ID_II_8 0x00 // For IntelliPort-II, indicates 8-port using + // standard brick. +#define POR_ID_II_8R 0x01 // For IntelliPort-II, indicates 8-port using + // RJ11's (no CTS) +#define POR_ID_II_6 0x02 // For IntelliPort-II, indicates 6-port using + // RJ45's +#define POR_ID_II_4 0x03 // For IntelliPort-II, indicates 4-port using + // 4xRJ45 connectors +#define POR_ID_EX 0x00 // For IntelliPort-IIEX, indicates standard + // expandable controller (other values reserved) + +//---------------------- +// porBus +//---------------------- + +// IntelliPort-IIEX only: Board is installed in a 16-bit slot +// +#define POR_BUS_SLOT16 0x20 + +// IntelliPort-IIEX only: DIP switch #8 is on, selecting 16-bit host interface +// operation. +// +#define POR_BUS_DIP16 0x10 + +// Bits 0-2 indicate type of bus: This information is stored in the bootstrap +// loadware, different loadware being used on different products for different +// buses. For most situations, the drivers do not need this information; but it +// is handy in a diagnostic environment. For example, on microchannel boards, +// you would not want to try to test several interrupts, only the one for which +// you were configured. +// +#define POR_BUS_TYPE 0x07 + +// Unknown: this product doesn't know what bus it is running in. (e.g. if same +// bootstrap firmware were wanted for two different buses.) +// +#define POR_BUS_T_UNK 0 + +// Note: existing firmware for ISA-8 and MC-8 currently report the POR_BUS_T_UNK +// state, since the same bootstrap firmware is used for each. + +#define POR_BUS_T_MCA 1 // MCA BUS */ +#define POR_BUS_T_EISA 2 // EISA BUS */ +#define POR_BUS_T_ISA 3 // ISA BUS */ + +// Values 4-7 Reserved + +// Remaining bits are reserved + +//---------------------- +// porDiag1 +//---------------------- + +#define POR_BAD_MAPPER 0x80 // HW failure on P.O.S.T: Chip mapper failed + +// These two bits valid only for the IntelliPort-II +// +#define POR_BAD_UART1 0x01 // First 1400 bad +#define POR_BAD_UART2 0x02 // Second 1400 bad + +//---------------------- +// porDiag2 +//---------------------- + +#define POR_DEBUG_PORT 0x80 // debug port was detected by the P.O.S.T +#define POR_DIAG_OK 0x00 // Indicates passage: Failure codes not yet + // available. + // Other bits undefined. +//---------------------- +// porFlags +//---------------------- + +#define POR_CPU 0x03 // These bits indicate supposed CPU type +#define POR_CPU_8 0x01 // Board uses an 80188 (no such thing yet) +#define POR_CPU_6 0x02 // Board uses an 80186 (all existing products) +#define POR_CEX4 0x04 // If set, this is an ISA-CEX/4: An ISA-4 (asic) + // which is architected like an ISA-CEX connected + // to a (hitherto impossible) 4-port box. +#define POR_BOXES 0xf0 // Valid for IntelliPort-IIEX only: Map of Box + // sizes based on box I.D. +#define POR_BOX_16 0x10 // Set indicates 16-port, clear 8-port + +//------------------------------------- +// LOADWARE and DOWNLOADING CODE +//------------------------------------- + +/* +Loadware may be sent to the board in two ways: +1) It may be read from a (binary image) data file block by block as each block + is sent to the board. This is only possible when the initialization is + performed by code which can access your file system. This is most suitable + for diagnostics and appications which use the interface library directly. + +2) It may be hard-coded into your source by including a .h file (typically + supplied by Computone), which declares a data array and initializes every + element. This achieves the same result as if an entire loadware file had + been read into the array. + + This requires more data space in your program, but access to the file system + is not required. This method is more suited to driver code, which typically + is running at a level too low to access the file system directly. + +At present, loadware can only be generated at Computone. + +All Loadware begins with a header area which has a particular format. This +includes a magic number which identifies the file as being (purportedly) +loadware, CRC (for the loader), and version information. +*/ + + +//----------------------------------------------------------------------------- +// Format of loadware block +// +// This is defined as a union so we can pass a pointer to one of these items +// and (if it is the first block) pick out the version information, etc. +// +// Otherwise, to deal with this as a simple character array +//------------------------------------------------------------------------------ + +#define LOADWARE_BLOCK_SIZE 512 // Number of bytes in each block of loadware + +typedef union _loadHdrStr +{ + unsigned char c[LOADWARE_BLOCK_SIZE]; // Valid for every block + + struct // These fields are valid for only the first block of loadware. + { + unsigned char loadMagic; // Magic number: see below + unsigned char loadBlocksMore; // How many more blocks? + unsigned char loadCRC[2]; // Two CRC bytes: used by loader + unsigned char loadVersion; // Version number + unsigned char loadRevision; // Revision number + unsigned char loadSubRevision; // Sub-revision number + unsigned char loadSpares[9]; // Presently unused + unsigned char loadDates[32]; // Null-terminated string which can give + // date and time of compilation + } e; +} loadHdrStr, *loadHdrStrPtr; + +//------------------------------------ +// Defines for downloading code: +//------------------------------------ + +// The loadMagic field in the first block of the loadfile must be this, else the +// file is not valid. +// +#define MAGIC_LOADFILE 0x3c + +// How do we know the load was successful? On completion of the load, the +// bootstrap firmware returns a code to indicate whether it thought the download +// was valid and intends to execute it. These are the only possible valid codes: +// +#define LOADWARE_OK 0xc3 // Download was ok +#define LOADWARE_BAD 0x5a // Download was bad (CRC error) + +// Constants applicable to writing blocks of loadware: +// The first block of loadware might take 600 mS to load, in extreme cases. +// (Expandable board: worst case for sending startup messages to the LCD's). +// The 600mS figure is not really a calculation, but a conservative +// guess/guarantee. Usually this will be within 100 mS, like subsequent blocks. +// +#define MAX_DLOAD_START_TIME 1000 // 1000 mS +#define MAX_DLOAD_READ_TIME 100 // 100 mS + +// Firmware should respond with status (see above) within this long of host +// having sent the final block. +// +#define MAX_DLOAD_ACK_TIME 100 // 100 mS, again! + +//------------------------------------------------------ +// MAXIMUM NUMBER OF PORTS PER BOARD: +// This is fixed for now (with the expandable), but may +// be expanding according to even newer products. +//------------------------------------------------------ +// +#define ABS_MAX_BOXES 4 // Absolute most boxes per board +#define ABS_BIGGEST_BOX 16 // Absolute the most ports per box +#define ABS_MOST_PORTS (ABS_MAX_BOXES * ABS_BIGGEST_BOX) + +#define I2_OUTSW(port, addr, count) outsw((port), (addr), (((count)+1)/2)) +#define I2_OUTSB(port, addr, count) outsb((port), (addr), (((count)+1))&-2) +#define I2_INSW(port, addr, count) insw((port), (addr), (((count)+1)/2)) +#define I2_INSB(port, addr, count) insb((port), (addr), (((count)+1))&-2) + +#endif // I2HW_H + diff --git a/drivers/staging/tty/ip2/i2lib.c b/drivers/staging/tty/ip2/i2lib.c new file mode 100644 index 000000000000..0d10b89218ed --- /dev/null +++ b/drivers/staging/tty/ip2/i2lib.c @@ -0,0 +1,2214 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: High-level interface code for the device driver. Uses the +* Extremely Low Level Interface Support (i2ellis.c). Provides an +* interface to the standard loadware, to support drivers or +* application code. (This is included source code, not a separate +* compilation module.) +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// Note on Strategy: +// Once the board has been initialized, it will interrupt us when: +// 1) It has something in the fifo for us to read (incoming data, flow control +// packets, or whatever). +// 2) It has stripped whatever we have sent last time in the FIFO (and +// consequently is ready for more). +// +// Note also that the buffer sizes declared in i2lib.h are VERY SMALL. This +// worsens performance considerably, but is done so that a great many channels +// might use only a little memory. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Revision History: +// +// 0.00 - 4/16/91 --- First Draft +// 0.01 - 4/29/91 --- 1st beta release +// 0.02 - 6/14/91 --- Changes to allow small model compilation +// 0.03 - 6/17/91 MAG Break reporting protected from interrupts routines with +// in-line asm added for moving data to/from ring buffers, +// replacing a variety of methods used previously. +// 0.04 - 6/21/91 MAG Initial flow-control packets not queued until +// i2_enable_interrupts time. Former versions would enqueue +// them at i2_init_channel time, before we knew how many +// channels were supposed to exist! +// 0.05 - 10/12/91 MAG Major changes: works through the ellis.c routines now; +// supports new 16-bit protocol and expandable boards. +// - 10/24/91 MAG Most changes in place and stable. +// 0.06 - 2/20/92 MAG Format of CMD_HOTACK corrected: the command takes no +// argument. +// 0.07 -- 3/11/92 MAG Support added to store special packet types at interrupt +// level (mostly responses to specific commands.) +// 0.08 -- 3/30/92 MAG Support added for STAT_MODEM packet +// 0.09 -- 6/24/93 MAG i2Link... needed to update number of boards BEFORE +// turning on the interrupt. +// 0.10 -- 6/25/93 MAG To avoid gruesome death from a bad board, we sanity check +// some incoming. +// +// 1.1 - 12/25/96 AKM Linux version. +// - 10/09/98 DMC Revised Linux version. +//------------------------------------------------------------------------------ + +//************ +//* Includes * +//************ + +#include +#include "i2lib.h" + + +//*********************** +//* Function Prototypes * +//*********************** +static void i2QueueNeeds(i2eBordStrPtr, i2ChanStrPtr, int); +static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr, int ); +static void i2StripFifo(i2eBordStrPtr); +static void i2StuffFifoBypass(i2eBordStrPtr); +static void i2StuffFifoFlow(i2eBordStrPtr); +static void i2StuffFifoInline(i2eBordStrPtr); +static int i2RetryFlushOutput(i2ChanStrPtr); + +// Not a documented part of the library routines (careful...) but the Diagnostic +// i2diag.c finds them useful to help the throughput in certain limited +// single-threaded operations. +static void iiSendPendingMail(i2eBordStrPtr); +static void serviceOutgoingFifo(i2eBordStrPtr); + +// Functions defined in ip2.c as part of interrupt handling +static void do_input(struct work_struct *); +static void do_status(struct work_struct *); + +//*************** +//* Debug Data * +//*************** +#ifdef DEBUG_FIFO + +unsigned char DBGBuf[0x4000]; +unsigned short I = 0; + +static void +WriteDBGBuf(char *s, unsigned char *src, unsigned short n ) +{ + char *p = src; + + // XXX: We need a spin lock here if we ever use this again + + while (*s) { // copy label + DBGBuf[I] = *s++; + I = I++ & 0x3fff; + } + while (n--) { // copy data + DBGBuf[I] = *p++; + I = I++ & 0x3fff; + } +} + +static void +fatality(i2eBordStrPtr pB ) +{ + int i; + + for (i=0;i= ' ' && DBGBuf[i] <= '~') { + printk(" %c ",DBGBuf[i]); + } else { + printk(" . "); + } + } + printk("\n"); + printk("Last index %x\n",I); +} +#endif /* DEBUG_FIFO */ + +//******** +//* Code * +//******** + +static inline int +i2Validate ( i2ChanStrPtr pCh ) +{ + //ip2trace(pCh->port_index, ITRC_VERIFY,ITRC_ENTER,2,pCh->validity, + // (CHANNEL_MAGIC | CHANNEL_SUPPORT)); + return ((pCh->validity & (CHANNEL_MAGIC_BITS | CHANNEL_SUPPORT)) + == (CHANNEL_MAGIC | CHANNEL_SUPPORT)); +} + +static void iiSendPendingMail_t(unsigned long data) +{ + i2eBordStrPtr pB = (i2eBordStrPtr)data; + + iiSendPendingMail(pB); +} + +//****************************************************************************** +// Function: iiSendPendingMail(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// If any outgoing mail bits are set and there is outgoing mailbox is empty, +// send the mail and clear the bits. +//****************************************************************************** +static void +iiSendPendingMail(i2eBordStrPtr pB) +{ + if (pB->i2eOutMailWaiting && (!pB->i2eWaitingForEmptyFifo) ) + { + if (iiTrySendMail(pB, pB->i2eOutMailWaiting)) + { + /* If we were already waiting for fifo to empty, + * or just sent MB_OUT_STUFFED, then we are + * still waiting for it to empty, until we should + * receive an MB_IN_STRIPPED from the board. + */ + pB->i2eWaitingForEmptyFifo |= + (pB->i2eOutMailWaiting & MB_OUT_STUFFED); + pB->i2eOutMailWaiting = 0; + pB->SendPendingRetry = 0; + } else { +/* The only time we hit this area is when "iiTrySendMail" has + failed. That only occurs when the outbound mailbox is + still busy with the last message. We take a short breather + to let the board catch up with itself and then try again. + 16 Retries is the limit - then we got a borked board. + /\/\|=mhw=|\/\/ */ + + if( ++pB->SendPendingRetry < 16 ) { + setup_timer(&pB->SendPendingTimer, + iiSendPendingMail_t, (unsigned long)pB); + mod_timer(&pB->SendPendingTimer, jiffies + 1); + } else { + printk( KERN_ERR "IP2: iiSendPendingMail unable to queue outbound mail\n" ); + } + } + } +} + +//****************************************************************************** +// Function: i2InitChannels(pB, nChannels, pCh) +// Parameters: Pointer to Ellis Board structure +// Number of channels to initialize +// Pointer to first element in an array of channel structures +// Returns: Success or failure +// +// Description: +// +// This function patches pointers, back-pointers, and initializes all the +// elements in the channel structure array. +// +// This should be run after the board structure is initialized, through having +// loaded the standard loadware (otherwise it complains). +// +// In any case, it must be done before any serious work begins initializing the +// irq's or sending commands... +// +//****************************************************************************** +static int +i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh) +{ + int index, stuffIndex; + i2ChanStrPtr *ppCh; + + if (pB->i2eValid != I2E_MAGIC) { + I2_COMPLETE(pB, I2EE_BADMAGIC); + } + if (pB->i2eState != II_STATE_STDLOADED) { + I2_COMPLETE(pB, I2EE_BADSTATE); + } + + rwlock_init(&pB->read_fifo_spinlock); + rwlock_init(&pB->write_fifo_spinlock); + rwlock_init(&pB->Dbuf_spinlock); + rwlock_init(&pB->Bbuf_spinlock); + rwlock_init(&pB->Fbuf_spinlock); + + // NO LOCK needed yet - this is init + + pB->i2eChannelPtr = pCh; + pB->i2eChannelCnt = nChannels; + + pB->i2Fbuf_strip = pB->i2Fbuf_stuff = 0; + pB->i2Dbuf_strip = pB->i2Dbuf_stuff = 0; + pB->i2Bbuf_strip = pB->i2Bbuf_stuff = 0; + + pB->SendPendingRetry = 0; + + memset ( pCh, 0, sizeof (i2ChanStr) * nChannels ); + + for (index = stuffIndex = 0, ppCh = (i2ChanStrPtr *)(pB->i2Fbuf); + nChannels && index < ABS_MOST_PORTS; + index++) + { + if ( !(pB->i2eChannelMap[index >> 4] & (1 << (index & 0xf)) ) ) { + continue; + } + rwlock_init(&pCh->Ibuf_spinlock); + rwlock_init(&pCh->Obuf_spinlock); + rwlock_init(&pCh->Cbuf_spinlock); + rwlock_init(&pCh->Pbuf_spinlock); + // NO LOCK needed yet - this is init + // Set up validity flag according to support level + if (pB->i2eGoodMap[index >> 4] & (1 << (index & 0xf)) ) { + pCh->validity = CHANNEL_MAGIC | CHANNEL_SUPPORT; + } else { + pCh->validity = CHANNEL_MAGIC; + } + pCh->pMyBord = pB; /* Back-pointer */ + + // Prepare an outgoing flow-control packet to send as soon as the chance + // occurs. + if ( pCh->validity & CHANNEL_SUPPORT ) { + pCh->infl.hd.i2sChannel = index; + pCh->infl.hd.i2sCount = 5; + pCh->infl.hd.i2sType = PTYPE_BYPASS; + pCh->infl.fcmd = 37; + pCh->infl.asof = 0; + pCh->infl.room = IBUF_SIZE - 1; + + pCh->whenSendFlow = (IBUF_SIZE/5)*4; // when 80% full + + // The following is similar to calling i2QueueNeeds, except that this + // is done in longhand, since we are setting up initial conditions on + // many channels at once. + pCh->channelNeeds = NEED_FLOW; // Since starting from scratch + pCh->sinceLastFlow = 0; // No bytes received since last flow + // control packet was queued + stuffIndex++; + *ppCh++ = pCh; // List this channel as needing + // initial flow control packet sent + } + + // Don't allow anything to be sent until the status packets come in from + // the board. + + pCh->outfl.asof = 0; + pCh->outfl.room = 0; + + // Initialize all the ring buffers + + pCh->Ibuf_stuff = pCh->Ibuf_strip = 0; + pCh->Obuf_stuff = pCh->Obuf_strip = 0; + pCh->Cbuf_stuff = pCh->Cbuf_strip = 0; + + memset( &pCh->icount, 0, sizeof (struct async_icount) ); + pCh->hotKeyIn = HOT_CLEAR; + pCh->channelOptions = 0; + pCh->bookMarks = 0; + init_waitqueue_head(&pCh->pBookmarkWait); + + init_waitqueue_head(&pCh->open_wait); + init_waitqueue_head(&pCh->close_wait); + init_waitqueue_head(&pCh->delta_msr_wait); + + // Set base and divisor so default custom rate is 9600 + pCh->BaudBase = 921600; // MAX for ST654, changed after we get + pCh->BaudDivisor = 96; // the boxids (UART types) later + + pCh->dataSetIn = 0; + pCh->dataSetOut = 0; + + pCh->wopen = 0; + pCh->throttled = 0; + + pCh->speed = CBR_9600; + + pCh->flags = 0; + + pCh->ClosingDelay = 5*HZ/10; + pCh->ClosingWaitTime = 30*HZ; + + // Initialize task queue objects + INIT_WORK(&pCh->tqueue_input, do_input); + INIT_WORK(&pCh->tqueue_status, do_status); + +#ifdef IP2DEBUG_TRACE + pCh->trace = ip2trace; +#endif + + ++pCh; + --nChannels; + } + // No need to check for wrap here; this is initialization. + pB->i2Fbuf_stuff = stuffIndex; + I2_COMPLETE(pB, I2EE_GOOD); + +} + +//****************************************************************************** +// Function: i2DeQueueNeeds(pB, type) +// Parameters: Pointer to a board structure +// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW +// Returns: +// Pointer to a channel structure +// +// Description: Returns pointer struct of next channel that needs service of +// the type specified. Otherwise returns a NULL reference. +// +//****************************************************************************** +static i2ChanStrPtr +i2DeQueueNeeds(i2eBordStrPtr pB, int type) +{ + unsigned short queueIndex; + unsigned long flags; + + i2ChanStrPtr pCh = NULL; + + switch(type) { + + case NEED_INLINE: + + write_lock_irqsave(&pB->Dbuf_spinlock, flags); + if ( pB->i2Dbuf_stuff != pB->i2Dbuf_strip) + { + queueIndex = pB->i2Dbuf_strip; + pCh = pB->i2Dbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Dbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_INLINE; + } + write_unlock_irqrestore(&pB->Dbuf_spinlock, flags); + break; + + case NEED_BYPASS: + + write_lock_irqsave(&pB->Bbuf_spinlock, flags); + if (pB->i2Bbuf_stuff != pB->i2Bbuf_strip) + { + queueIndex = pB->i2Bbuf_strip; + pCh = pB->i2Bbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Bbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_BYPASS; + } + write_unlock_irqrestore(&pB->Bbuf_spinlock, flags); + break; + + case NEED_FLOW: + + write_lock_irqsave(&pB->Fbuf_spinlock, flags); + if (pB->i2Fbuf_stuff != pB->i2Fbuf_strip) + { + queueIndex = pB->i2Fbuf_strip; + pCh = pB->i2Fbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Fbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_FLOW; + } + write_unlock_irqrestore(&pB->Fbuf_spinlock, flags); + break; + default: + printk(KERN_ERR "i2DeQueueNeeds called with bad type:%x\n",type); + break; + } + return pCh; +} + +//****************************************************************************** +// Function: i2QueueNeeds(pB, pCh, type) +// Parameters: Pointer to a board structure +// Pointer to a channel structure +// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW +// Returns: Nothing +// +// Description: +// For each type of need selected, if the given channel is not already in the +// queue, adds it, and sets the flag indicating it is in the queue. +//****************************************************************************** +static void +i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type) +{ + unsigned short queueIndex; + unsigned long flags; + + // We turn off all the interrupts during this brief process, since the + // interrupt-level code might want to put things on the queue as well. + + switch (type) { + + case NEED_INLINE: + + write_lock_irqsave(&pB->Dbuf_spinlock, flags); + if ( !(pCh->channelNeeds & NEED_INLINE) ) + { + pCh->channelNeeds |= NEED_INLINE; + queueIndex = pB->i2Dbuf_stuff; + pB->i2Dbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Dbuf_stuff = queueIndex; + } + write_unlock_irqrestore(&pB->Dbuf_spinlock, flags); + break; + + case NEED_BYPASS: + + write_lock_irqsave(&pB->Bbuf_spinlock, flags); + if ((type & NEED_BYPASS) && !(pCh->channelNeeds & NEED_BYPASS)) + { + pCh->channelNeeds |= NEED_BYPASS; + queueIndex = pB->i2Bbuf_stuff; + pB->i2Bbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Bbuf_stuff = queueIndex; + } + write_unlock_irqrestore(&pB->Bbuf_spinlock, flags); + break; + + case NEED_FLOW: + + write_lock_irqsave(&pB->Fbuf_spinlock, flags); + if ((type & NEED_FLOW) && !(pCh->channelNeeds & NEED_FLOW)) + { + pCh->channelNeeds |= NEED_FLOW; + queueIndex = pB->i2Fbuf_stuff; + pB->i2Fbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Fbuf_stuff = queueIndex; + } + write_unlock_irqrestore(&pB->Fbuf_spinlock, flags); + break; + + case NEED_CREDIT: + pCh->channelNeeds |= NEED_CREDIT; + break; + default: + printk(KERN_ERR "i2QueueNeeds called with bad type:%x\n",type); + break; + } + return; +} + +//****************************************************************************** +// Function: i2QueueCommands(type, pCh, timeout, nCommands, pCs,...) +// Parameters: type - PTYPE_BYPASS or PTYPE_INLINE +// pointer to the channel structure +// maximum period to wait +// number of commands (n) +// n commands +// Returns: Number of commands sent, or -1 for error +// +// get board lock before calling +// +// Description: +// Queues up some commands to be sent to a channel. To send possibly several +// bypass or inline commands to the given channel. The timeout parameter +// indicates how many HUNDREDTHS OF SECONDS to wait until there is room: +// 0 = return immediately if no room, -ive = wait forever, +ive = number of +// 1/100 seconds to wait. Return values: +// -1 Some kind of nasty error: bad channel structure or invalid arguments. +// 0 No room to send all the commands +// (+) Number of commands sent +//****************************************************************************** +static int +i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands, + cmdSyntaxPtr pCs0,...) +{ + int totalsize = 0; + int blocksize; + int lastended; + cmdSyntaxPtr *ppCs; + cmdSyntaxPtr pCs; + int count; + int flag; + i2eBordStrPtr pB; + + unsigned short maxBlock; + unsigned short maxBuff; + short bufroom; + unsigned short stuffIndex; + unsigned char *pBuf; + unsigned char *pInsert; + unsigned char *pDest, *pSource; + unsigned short channel; + int cnt; + unsigned long flags = 0; + rwlock_t *lock_var_p = NULL; + + // Make sure the channel exists, otherwise do nothing + if ( !i2Validate ( pCh ) ) { + return -1; + } + + ip2trace (CHANN, ITRC_QUEUE, ITRC_ENTER, 0 ); + + pB = pCh->pMyBord; + + // Board must also exist, and THE INTERRUPT COMMAND ALREADY SENT + if (pB->i2eValid != I2E_MAGIC || pB->i2eUsingIrq == I2_IRQ_UNDEFINED) + return -2; + // If the board has gone fatal, return bad, and also hit the trap routine if + // it exists. + if (pB->i2eFatal) { + if ( pB->i2eFatalTrap ) { + (*(pB)->i2eFatalTrap)(pB); + } + return -3; + } + // Set up some variables, Which buffers are we using? How big are they? + switch(type) + { + case PTYPE_INLINE: + flag = INL; + maxBlock = MAX_OBUF_BLOCK; + maxBuff = OBUF_SIZE; + pBuf = pCh->Obuf; + break; + case PTYPE_BYPASS: + flag = BYP; + maxBlock = MAX_CBUF_BLOCK; + maxBuff = CBUF_SIZE; + pBuf = pCh->Cbuf; + break; + default: + return -4; + } + // Determine the total size required for all the commands + totalsize = blocksize = sizeof(i2CmdHeader); + lastended = 0; + ppCs = &pCs0; + for ( count = nCommands; count; count--, ppCs++) + { + pCs = *ppCs; + cnt = pCs->length; + // Will a new block be needed for this one? + // Two possible reasons: too + // big or previous command has to be at the end of a packet. + if ((blocksize + cnt > maxBlock) || lastended) { + blocksize = sizeof(i2CmdHeader); + totalsize += sizeof(i2CmdHeader); + } + totalsize += cnt; + blocksize += cnt; + + // If this command had to end a block, then we will make sure to + // account for it should there be any more blocks. + lastended = pCs->flags & END; + } + for (;;) { + // Make sure any pending flush commands go out before we add more data. + if ( !( pCh->flush_flags && i2RetryFlushOutput( pCh ) ) ) { + // How much room (this time through) ? + switch(type) { + case PTYPE_INLINE: + lock_var_p = &pCh->Obuf_spinlock; + write_lock_irqsave(lock_var_p, flags); + stuffIndex = pCh->Obuf_stuff; + bufroom = pCh->Obuf_strip - stuffIndex; + break; + case PTYPE_BYPASS: + lock_var_p = &pCh->Cbuf_spinlock; + write_lock_irqsave(lock_var_p, flags); + stuffIndex = pCh->Cbuf_stuff; + bufroom = pCh->Cbuf_strip - stuffIndex; + break; + default: + return -5; + } + if (--bufroom < 0) { + bufroom += maxBuff; + } + + ip2trace (CHANN, ITRC_QUEUE, 2, 1, bufroom ); + + // Check for overflow + if (totalsize <= bufroom) { + // Normal Expected path - We still hold LOCK + break; /* from for()- Enough room: goto proceed */ + } + ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize); + write_unlock_irqrestore(lock_var_p, flags); + } else + ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize); + + /* Prepare to wait for buffers to empty */ + serviceOutgoingFifo(pB); // Dump what we got + + if (timeout == 0) { + return 0; // Tired of waiting + } + if (timeout > 0) + timeout--; // So negative values == forever + + if (!in_interrupt()) { + schedule_timeout_interruptible(1); // short nap + } else { + // we cannot sched/sleep in interrupt silly + return 0; + } + if (signal_pending(current)) { + return 0; // Wake up! Time to die!!! + } + + ip2trace (CHANN, ITRC_QUEUE, 4, 0 ); + + } // end of for(;;) + + // At this point we have room and the lock - stick them in. + channel = pCh->infl.hd.i2sChannel; + pInsert = &pBuf[stuffIndex]; // Pointer to start of packet + pDest = CMD_OF(pInsert); // Pointer to start of command + + // When we start counting, the block is the size of the header + for (blocksize = sizeof(i2CmdHeader), count = nCommands, + lastended = 0, ppCs = &pCs0; + count; + count--, ppCs++) + { + pCs = *ppCs; // Points to command protocol structure + + // If this is a bookmark request command, post the fact that a bookmark + // request is pending. NOTE THIS TRICK ONLY WORKS BECAUSE CMD_BMARK_REQ + // has no parameters! The more general solution would be to reference + // pCs->cmd[0]. + if (pCs == CMD_BMARK_REQ) { + pCh->bookMarks++; + + ip2trace (CHANN, ITRC_DRAIN, 30, 1, pCh->bookMarks ); + + } + cnt = pCs->length; + + // If this command would put us over the maximum block size or + // if the last command had to be at the end of a block, we end + // the existing block here and start a new one. + if ((blocksize + cnt > maxBlock) || lastended) { + + ip2trace (CHANN, ITRC_QUEUE, 5, 0 ); + + PTYPE_OF(pInsert) = type; + CHANNEL_OF(pInsert) = channel; + // count here does not include the header + CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader); + stuffIndex += blocksize; + if(stuffIndex >= maxBuff) { + stuffIndex = 0; + pInsert = pBuf; + } + pInsert = &pBuf[stuffIndex]; // Pointer to start of next pkt + pDest = CMD_OF(pInsert); + blocksize = sizeof(i2CmdHeader); + } + // Now we know there is room for this one in the current block + + blocksize += cnt; // Total bytes in this command + pSource = pCs->cmd; // Copy the command into the buffer + while (cnt--) { + *pDest++ = *pSource++; + } + // If this command had to end a block, then we will make sure to account + // for it should there be any more blocks. + lastended = pCs->flags & END; + } // end for + // Clean up the final block by writing header, etc + + PTYPE_OF(pInsert) = type; + CHANNEL_OF(pInsert) = channel; + // count here does not include the header + CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader); + stuffIndex += blocksize; + if(stuffIndex >= maxBuff) { + stuffIndex = 0; + pInsert = pBuf; + } + // Updates the index, and post the need for service. When adding these to + // the queue of channels, we turn off the interrupt while doing so, + // because at interrupt level we might want to push a channel back to the + // end of the queue. + switch(type) + { + case PTYPE_INLINE: + pCh->Obuf_stuff = stuffIndex; // Store buffer pointer + write_unlock_irqrestore(&pCh->Obuf_spinlock, flags); + + pB->debugInlineQueued++; + // Add the channel pointer to list of channels needing service (first + // come...), if it's not already there. + i2QueueNeeds(pB, pCh, NEED_INLINE); + break; + + case PTYPE_BYPASS: + pCh->Cbuf_stuff = stuffIndex; // Store buffer pointer + write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags); + + pB->debugBypassQueued++; + // Add the channel pointer to list of channels needing service (first + // come...), if it's not already there. + i2QueueNeeds(pB, pCh, NEED_BYPASS); + break; + } + + ip2trace (CHANN, ITRC_QUEUE, ITRC_RETURN, 1, nCommands ); + + return nCommands; // Good status: number of commands sent +} + +//****************************************************************************** +// Function: i2GetStatus(pCh,resetBits) +// Parameters: Pointer to a channel structure +// Bit map of status bits to clear +// Returns: Bit map of current status bits +// +// Description: +// Returns the state of data set signals, and whether a break has been received, +// (see i2lib.h for bit-mapped result). resetBits is a bit-map of any status +// bits to be cleared: I2_BRK, I2_PAR, I2_FRA, I2_OVR,... These are cleared +// AFTER the condition is passed. If pCh does not point to a valid channel, +// returns -1 (which would be impossible otherwise. +//****************************************************************************** +static int +i2GetStatus(i2ChanStrPtr pCh, int resetBits) +{ + unsigned short status; + i2eBordStrPtr pB; + + ip2trace (CHANN, ITRC_STATUS, ITRC_ENTER, 2, pCh->dataSetIn, resetBits ); + + // Make sure the channel exists, otherwise do nothing */ + if ( !i2Validate ( pCh ) ) + return -1; + + pB = pCh->pMyBord; + + status = pCh->dataSetIn; + + // Clear any specified error bits: but note that only actual error bits can + // be cleared, regardless of the value passed. + if (resetBits) + { + pCh->dataSetIn &= ~(resetBits & (I2_BRK | I2_PAR | I2_FRA | I2_OVR)); + pCh->dataSetIn &= ~(I2_DDCD | I2_DCTS | I2_DDSR | I2_DRI); + } + + ip2trace (CHANN, ITRC_STATUS, ITRC_RETURN, 1, pCh->dataSetIn ); + + return status; +} + +//****************************************************************************** +// Function: i2Input(pChpDest,count) +// Parameters: Pointer to a channel structure +// Pointer to data buffer +// Number of bytes to read +// Returns: Number of bytes read, or -1 for error +// +// Description: +// Strips data from the input buffer and writes it to pDest. If there is a +// collosal blunder, (invalid structure pointers or the like), returns -1. +// Otherwise, returns the number of bytes read. +//****************************************************************************** +static int +i2Input(i2ChanStrPtr pCh) +{ + int amountToMove; + unsigned short stripIndex; + int count; + unsigned long flags = 0; + + ip2trace (CHANN, ITRC_INPUT, ITRC_ENTER, 0); + + // Ensure channel structure seems real + if ( !i2Validate( pCh ) ) { + count = -1; + goto i2Input_exit; + } + write_lock_irqsave(&pCh->Ibuf_spinlock, flags); + + // initialize some accelerators and private copies + stripIndex = pCh->Ibuf_strip; + + count = pCh->Ibuf_stuff - stripIndex; + + // If buffer is empty or requested data count was 0, (trivial case) return + // without any further thought. + if ( count == 0 ) { + write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); + goto i2Input_exit; + } + // Adjust for buffer wrap + if ( count < 0 ) { + count += IBUF_SIZE; + } + // Don't give more than can be taken by the line discipline + amountToMove = pCh->pTTY->receive_room; + if (count > amountToMove) { + count = amountToMove; + } + // How much could we copy without a wrap? + amountToMove = IBUF_SIZE - stripIndex; + + if (amountToMove > count) { + amountToMove = count; + } + // Move the first block + pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY, + &(pCh->Ibuf[stripIndex]), NULL, amountToMove ); + // If we needed to wrap, do the second data move + if (count > amountToMove) { + pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY, + pCh->Ibuf, NULL, count - amountToMove ); + } + // Bump and wrap the stripIndex all at once by the amount of data read. This + // method is good regardless of whether the data was in one or two pieces. + stripIndex += count; + if (stripIndex >= IBUF_SIZE) { + stripIndex -= IBUF_SIZE; + } + pCh->Ibuf_strip = stripIndex; + + // Update our flow control information and possibly queue ourselves to send + // it, depending on how much data has been stripped since the last time a + // packet was sent. + pCh->infl.asof += count; + + if ((pCh->sinceLastFlow += count) >= pCh->whenSendFlow) { + pCh->sinceLastFlow -= pCh->whenSendFlow; + write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); + i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); + } else { + write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); + } + +i2Input_exit: + + ip2trace (CHANN, ITRC_INPUT, ITRC_RETURN, 1, count); + + return count; +} + +//****************************************************************************** +// Function: i2InputFlush(pCh) +// Parameters: Pointer to a channel structure +// Returns: Number of bytes stripped, or -1 for error +// +// Description: +// Strips any data from the input buffer. If there is a collosal blunder, +// (invalid structure pointers or the like), returns -1. Otherwise, returns the +// number of bytes stripped. +//****************************************************************************** +static int +i2InputFlush(i2ChanStrPtr pCh) +{ + int count; + unsigned long flags; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) + return -1; + + ip2trace (CHANN, ITRC_INPUT, 10, 0); + + write_lock_irqsave(&pCh->Ibuf_spinlock, flags); + count = pCh->Ibuf_stuff - pCh->Ibuf_strip; + + // Adjust for buffer wrap + if (count < 0) { + count += IBUF_SIZE; + } + + // Expedient way to zero out the buffer + pCh->Ibuf_strip = pCh->Ibuf_stuff; + + + // Update our flow control information and possibly queue ourselves to send + // it, depending on how much data has been stripped since the last time a + // packet was sent. + + pCh->infl.asof += count; + + if ( (pCh->sinceLastFlow += count) >= pCh->whenSendFlow ) + { + pCh->sinceLastFlow -= pCh->whenSendFlow; + write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); + i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); + } else { + write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); + } + + ip2trace (CHANN, ITRC_INPUT, 19, 1, count); + + return count; +} + +//****************************************************************************** +// Function: i2InputAvailable(pCh) +// Parameters: Pointer to a channel structure +// Returns: Number of bytes available, or -1 for error +// +// Description: +// If there is a collosal blunder, (invalid structure pointers or the like), +// returns -1. Otherwise, returns the number of bytes stripped. Otherwise, +// returns the number of bytes available in the buffer. +//****************************************************************************** +#if 0 +static int +i2InputAvailable(i2ChanStrPtr pCh) +{ + int count; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) return -1; + + + // initialize some accelerators and private copies + read_lock_irqsave(&pCh->Ibuf_spinlock, flags); + count = pCh->Ibuf_stuff - pCh->Ibuf_strip; + read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); + + // Adjust for buffer wrap + if (count < 0) + { + count += IBUF_SIZE; + } + + return count; +} +#endif + +//****************************************************************************** +// Function: i2Output(pCh, pSource, count) +// Parameters: Pointer to channel structure +// Pointer to source data +// Number of bytes to send +// Returns: Number of bytes sent, or -1 for error +// +// Description: +// Queues the data at pSource to be sent as data packets to the board. If there +// is a collosal blunder, (invalid structure pointers or the like), returns -1. +// Otherwise, returns the number of bytes written. What if there is not enough +// room for all the data? If pCh->channelOptions & CO_NBLOCK_WRITE is set, then +// we transfer as many characters as we can now, then return. If this bit is +// clear (default), routine will spin along until all the data is buffered. +// Should this occur, the 1-ms delay routine is called while waiting to avoid +// applications that one cannot break out of. +//****************************************************************************** +static int +i2Output(i2ChanStrPtr pCh, const char *pSource, int count) +{ + i2eBordStrPtr pB; + unsigned char *pInsert; + int amountToMove; + int countOriginal = count; + unsigned short channel; + unsigned short stuffIndex; + unsigned long flags; + + int bailout = 10; + + ip2trace (CHANN, ITRC_OUTPUT, ITRC_ENTER, 2, count, 0 ); + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) + return -1; + + // initialize some accelerators and private copies + pB = pCh->pMyBord; + channel = pCh->infl.hd.i2sChannel; + + // If the board has gone fatal, return bad, and also hit the trap routine if + // it exists. + if (pB->i2eFatal) { + if (pB->i2eFatalTrap) { + (*(pB)->i2eFatalTrap)(pB); + } + return -1; + } + // Proceed as though we would do everything + while ( count > 0 ) { + + // How much room in output buffer is there? + read_lock_irqsave(&pCh->Obuf_spinlock, flags); + amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; + read_unlock_irqrestore(&pCh->Obuf_spinlock, flags); + if (amountToMove < 0) { + amountToMove += OBUF_SIZE; + } + // Subtract off the headers size and see how much room there is for real + // data. If this is negative, we will discover later. + amountToMove -= sizeof (i2DataHeader); + + // Don't move more (now) than can go in a single packet + if ( amountToMove > (int)(MAX_OBUF_BLOCK - sizeof(i2DataHeader)) ) { + amountToMove = MAX_OBUF_BLOCK - sizeof(i2DataHeader); + } + // Don't move more than the count we were given + if (amountToMove > count) { + amountToMove = count; + } + // Now we know how much we must move: NB because the ring buffers have + // an overflow area at the end, we needn't worry about wrapping in the + // middle of a packet. + +// Small WINDOW here with no LOCK but I can't call Flush with LOCK +// We would be flushing (or ending flush) anyway + + ip2trace (CHANN, ITRC_OUTPUT, 10, 1, amountToMove ); + + if ( !(pCh->flush_flags && i2RetryFlushOutput(pCh) ) + && amountToMove > 0 ) + { + write_lock_irqsave(&pCh->Obuf_spinlock, flags); + stuffIndex = pCh->Obuf_stuff; + + // Had room to move some data: don't know whether the block size, + // buffer space, or what was the limiting factor... + pInsert = &(pCh->Obuf[stuffIndex]); + + // Set up the header + CHANNEL_OF(pInsert) = channel; + PTYPE_OF(pInsert) = PTYPE_DATA; + TAG_OF(pInsert) = 0; + ID_OF(pInsert) = ID_ORDINARY_DATA; + DATA_COUNT_OF(pInsert) = amountToMove; + + // Move the data + memcpy( (char*)(DATA_OF(pInsert)), pSource, amountToMove ); + // Adjust pointers and indices + pSource += amountToMove; + pCh->Obuf_char_count += amountToMove; + stuffIndex += amountToMove + sizeof(i2DataHeader); + count -= amountToMove; + + if (stuffIndex >= OBUF_SIZE) { + stuffIndex = 0; + } + pCh->Obuf_stuff = stuffIndex; + + write_unlock_irqrestore(&pCh->Obuf_spinlock, flags); + + ip2trace (CHANN, ITRC_OUTPUT, 13, 1, stuffIndex ); + + } else { + + // Cannot move data + // becuz we need to stuff a flush + // or amount to move is <= 0 + + ip2trace(CHANN, ITRC_OUTPUT, 14, 3, + amountToMove, pB->i2eFifoRemains, + pB->i2eWaitingForEmptyFifo ); + + // Put this channel back on queue + // this ultimatly gets more data or wakes write output + i2QueueNeeds(pB, pCh, NEED_INLINE); + + if ( pB->i2eWaitingForEmptyFifo ) { + + ip2trace (CHANN, ITRC_OUTPUT, 16, 0 ); + + // or schedule + if (!in_interrupt()) { + + ip2trace (CHANN, ITRC_OUTPUT, 61, 0 ); + + schedule_timeout_interruptible(2); + if (signal_pending(current)) { + break; + } + continue; + } else { + + ip2trace (CHANN, ITRC_OUTPUT, 62, 0 ); + + // let interrupt in = WAS restore_flags() + // We hold no lock nor is irq off anymore??? + + break; + } + break; // from while(count) + } + else if ( pB->i2eFifoRemains < 32 && !pB->i2eTxMailEmpty ( pB ) ) + { + ip2trace (CHANN, ITRC_OUTPUT, 19, 2, + pB->i2eFifoRemains, + pB->i2eTxMailEmpty ); + + break; // from while(count) + } else if ( pCh->channelNeeds & NEED_CREDIT ) { + + ip2trace (CHANN, ITRC_OUTPUT, 22, 0 ); + + break; // from while(count) + } else if ( --bailout) { + + // Try to throw more things (maybe not us) in the fifo if we're + // not already waiting for it. + + ip2trace (CHANN, ITRC_OUTPUT, 20, 0 ); + + serviceOutgoingFifo(pB); + //break; CONTINUE; + } else { + ip2trace (CHANN, ITRC_OUTPUT, 21, 3, + pB->i2eFifoRemains, + pB->i2eOutMailWaiting, + pB->i2eWaitingForEmptyFifo ); + + break; // from while(count) + } + } + } // End of while(count) + + i2QueueNeeds(pB, pCh, NEED_INLINE); + + // We drop through either when the count expires, or when there is some + // count left, but there was a non-blocking write. + if (countOriginal > count) { + + ip2trace (CHANN, ITRC_OUTPUT, 17, 2, countOriginal, count ); + + serviceOutgoingFifo( pB ); + } + + ip2trace (CHANN, ITRC_OUTPUT, ITRC_RETURN, 2, countOriginal, count ); + + return countOriginal - count; +} + +//****************************************************************************** +// Function: i2FlushOutput(pCh) +// Parameters: Pointer to a channel structure +// Returns: Nothing +// +// Description: +// Sends bypass command to start flushing (waiting possibly forever until there +// is room), then sends inline command to stop flushing output, (again waiting +// possibly forever). +//****************************************************************************** +static inline void +i2FlushOutput(i2ChanStrPtr pCh) +{ + + ip2trace (CHANN, ITRC_FLUSH, 1, 1, pCh->flush_flags ); + + if (pCh->flush_flags) + return; + + if ( 1 != i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) { + pCh->flush_flags = STARTFL_FLAG; // Failed - flag for later + + ip2trace (CHANN, ITRC_FLUSH, 2, 0 ); + + } else if ( 1 != i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL) ) { + pCh->flush_flags = STOPFL_FLAG; // Failed - flag for later + + ip2trace (CHANN, ITRC_FLUSH, 3, 0 ); + } +} + +static int +i2RetryFlushOutput(i2ChanStrPtr pCh) +{ + int old_flags = pCh->flush_flags; + + ip2trace (CHANN, ITRC_FLUSH, 14, 1, old_flags ); + + pCh->flush_flags = 0; // Clear flag so we can avoid recursion + // and queue the commands + + if ( old_flags & STARTFL_FLAG ) { + if ( 1 == i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) { + old_flags = STOPFL_FLAG; //Success - send stop flush + } else { + old_flags = STARTFL_FLAG; //Failure - Flag for retry later + } + + ip2trace (CHANN, ITRC_FLUSH, 15, 1, old_flags ); + + } + if ( old_flags & STOPFL_FLAG ) { + if (1 == i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL)) { + old_flags = 0; // Success - clear flags + } + + ip2trace (CHANN, ITRC_FLUSH, 16, 1, old_flags ); + } + pCh->flush_flags = old_flags; + + ip2trace (CHANN, ITRC_FLUSH, 17, 1, old_flags ); + + return old_flags; +} + +//****************************************************************************** +// Function: i2DrainOutput(pCh,timeout) +// Parameters: Pointer to a channel structure +// Maximum period to wait +// Returns: ? +// +// Description: +// Uses the bookmark request command to ask the board to send a bookmark back as +// soon as all the data is completely sent. +//****************************************************************************** +static void +i2DrainWakeup(unsigned long d) +{ + i2ChanStrPtr pCh = (i2ChanStrPtr)d; + + ip2trace (CHANN, ITRC_DRAIN, 10, 1, pCh->BookmarkTimer.expires ); + + pCh->BookmarkTimer.expires = 0; + wake_up_interruptible( &pCh->pBookmarkWait ); +} + +static void +i2DrainOutput(i2ChanStrPtr pCh, int timeout) +{ + wait_queue_t wait; + i2eBordStrPtr pB; + + ip2trace (CHANN, ITRC_DRAIN, ITRC_ENTER, 1, pCh->BookmarkTimer.expires); + + pB = pCh->pMyBord; + // If the board has gone fatal, return bad, + // and also hit the trap routine if it exists. + if (pB->i2eFatal) { + if (pB->i2eFatalTrap) { + (*(pB)->i2eFatalTrap)(pB); + } + return; + } + if ((timeout > 0) && (pCh->BookmarkTimer.expires == 0 )) { + // One per customer (channel) + setup_timer(&pCh->BookmarkTimer, i2DrainWakeup, + (unsigned long)pCh); + + ip2trace (CHANN, ITRC_DRAIN, 1, 1, pCh->BookmarkTimer.expires ); + + mod_timer(&pCh->BookmarkTimer, jiffies + timeout); + } + + i2QueueCommands( PTYPE_INLINE, pCh, -1, 1, CMD_BMARK_REQ ); + + init_waitqueue_entry(&wait, current); + add_wait_queue(&(pCh->pBookmarkWait), &wait); + set_current_state( TASK_INTERRUPTIBLE ); + + serviceOutgoingFifo( pB ); + + schedule(); // Now we take our interruptible sleep on + + // Clean up the queue + set_current_state( TASK_RUNNING ); + remove_wait_queue(&(pCh->pBookmarkWait), &wait); + + // if expires == 0 then timer poped, then do not need to del_timer + if ((timeout > 0) && pCh->BookmarkTimer.expires && + time_before(jiffies, pCh->BookmarkTimer.expires)) { + del_timer( &(pCh->BookmarkTimer) ); + pCh->BookmarkTimer.expires = 0; + + ip2trace (CHANN, ITRC_DRAIN, 3, 1, pCh->BookmarkTimer.expires ); + + } + ip2trace (CHANN, ITRC_DRAIN, ITRC_RETURN, 1, pCh->BookmarkTimer.expires ); + return; +} + +//****************************************************************************** +// Function: i2OutputFree(pCh) +// Parameters: Pointer to a channel structure +// Returns: Space in output buffer +// +// Description: +// Returns -1 if very gross error. Otherwise returns the amount of bytes still +// free in the output buffer. +//****************************************************************************** +static int +i2OutputFree(i2ChanStrPtr pCh) +{ + int amountToMove; + unsigned long flags; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) { + return -1; + } + read_lock_irqsave(&pCh->Obuf_spinlock, flags); + amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; + read_unlock_irqrestore(&pCh->Obuf_spinlock, flags); + + if (amountToMove < 0) { + amountToMove += OBUF_SIZE; + } + // If this is negative, we will discover later + amountToMove -= sizeof(i2DataHeader); + + return (amountToMove < 0) ? 0 : amountToMove; +} +static void + +ip2_owake( PTTY tp) +{ + i2ChanStrPtr pCh; + + if (tp == NULL) return; + + pCh = tp->driver_data; + + ip2trace (CHANN, ITRC_SICMD, 10, 2, tp->flags, + (1 << TTY_DO_WRITE_WAKEUP) ); + + tty_wakeup(tp); +} + +static inline void +set_baud_params(i2eBordStrPtr pB) +{ + int i,j; + i2ChanStrPtr *pCh; + + pCh = (i2ChanStrPtr *) pB->i2eChannelPtr; + + for (i = 0; i < ABS_MAX_BOXES; i++) { + if (pB->channelBtypes.bid_value[i]) { + if (BID_HAS_654(pB->channelBtypes.bid_value[i])) { + for (j = 0; j < ABS_BIGGEST_BOX; j++) { + if (pCh[i*16+j] == NULL) + break; + (pCh[i*16+j])->BaudBase = 921600; // MAX for ST654 + (pCh[i*16+j])->BaudDivisor = 96; + } + } else { // has cirrus cd1400 + for (j = 0; j < ABS_BIGGEST_BOX; j++) { + if (pCh[i*16+j] == NULL) + break; + (pCh[i*16+j])->BaudBase = 115200; // MAX for CD1400 + (pCh[i*16+j])->BaudDivisor = 12; + } + } + } + } +} + +//****************************************************************************** +// Function: i2StripFifo(pB) +// Parameters: Pointer to a board structure +// Returns: ? +// +// Description: +// Strips all the available data from the incoming FIFO, identifies the type of +// packet, and either buffers the data or does what needs to be done. +// +// Note there is no overflow checking here: if the board sends more data than it +// ought to, we will not detect it here, but blindly overflow... +//****************************************************************************** + +// A buffer for reading in blocks for unknown channels +static unsigned char junkBuffer[IBUF_SIZE]; + +// A buffer to read in a status packet. Because of the size of the count field +// for these things, the maximum packet size must be less than MAX_CMD_PACK_SIZE +static unsigned char cmdBuffer[MAX_CMD_PACK_SIZE + 4]; + +// This table changes the bit order from MSR order given by STAT_MODEM packet to +// status bits used in our library. +static char xlatDss[16] = { +0 | 0 | 0 | 0 , +0 | 0 | 0 | I2_CTS , +0 | 0 | I2_DSR | 0 , +0 | 0 | I2_DSR | I2_CTS , +0 | I2_RI | 0 | 0 , +0 | I2_RI | 0 | I2_CTS , +0 | I2_RI | I2_DSR | 0 , +0 | I2_RI | I2_DSR | I2_CTS , +I2_DCD | 0 | 0 | 0 , +I2_DCD | 0 | 0 | I2_CTS , +I2_DCD | 0 | I2_DSR | 0 , +I2_DCD | 0 | I2_DSR | I2_CTS , +I2_DCD | I2_RI | 0 | 0 , +I2_DCD | I2_RI | 0 | I2_CTS , +I2_DCD | I2_RI | I2_DSR | 0 , +I2_DCD | I2_RI | I2_DSR | I2_CTS }; + +static inline void +i2StripFifo(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + int channel; + int count; + unsigned short stuffIndex; + int amountToRead; + unsigned char *pc, *pcLimit; + unsigned char uc; + unsigned char dss_change; + unsigned long bflags,cflags; + +// ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_ENTER, 0 ); + + while (I2_HAS_INPUT(pB)) { +// ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 2, 0 ); + + // Process packet from fifo a one atomic unit + write_lock_irqsave(&pB->read_fifo_spinlock, bflags); + + // The first word (or two bytes) will have channel number and type of + // packet, possibly other information + pB->i2eLeadoffWord[0] = iiReadWord(pB); + + switch(PTYPE_OF(pB->i2eLeadoffWord)) + { + case PTYPE_DATA: + pB->got_input = 1; + +// ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 3, 0 ); + + channel = CHANNEL_OF(pB->i2eLeadoffWord); /* Store channel */ + count = iiReadWord(pB); /* Count is in the next word */ + +// NEW: Check the count for sanity! Should the hardware fail, our death +// is more pleasant. While an oversize channel is acceptable (just more +// than the driver supports), an over-length count clearly means we are +// sick! + if ( ((unsigned int)count) > IBUF_SIZE ) { + pB->i2eFatal = 2; + write_unlock_irqrestore(&pB->read_fifo_spinlock, + bflags); + return; /* Bail out ASAP */ + } + // Channel is illegally big ? + if ((channel >= pB->i2eChannelCnt) || + (NULL==(pCh = ((i2ChanStrPtr*)pB->i2eChannelPtr)[channel]))) + { + iiReadBuf(pB, junkBuffer, count); + write_unlock_irqrestore(&pB->read_fifo_spinlock, + bflags); + break; /* From switch: ready for next packet */ + } + + // Channel should be valid, then + + // If this is a hot-key, merely post its receipt for now. These are + // always supposed to be 1-byte packets, so we won't even check the + // count. Also we will post an acknowledgement to the board so that + // more data can be forthcoming. Note that we are not trying to use + // these sequences in this driver, merely to robustly ignore them. + if(ID_OF(pB->i2eLeadoffWord) == ID_HOT_KEY) + { + pCh->hotKeyIn = iiReadWord(pB) & 0xff; + write_unlock_irqrestore(&pB->read_fifo_spinlock, + bflags); + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_HOTACK); + break; /* From the switch: ready for next packet */ + } + + // Normal data! We crudely assume there is room for the data in our + // buffer because the board wouldn't have exceeded his credit limit. + write_lock_irqsave(&pCh->Ibuf_spinlock, cflags); + // We have 2 locks now + stuffIndex = pCh->Ibuf_stuff; + amountToRead = IBUF_SIZE - stuffIndex; + if (amountToRead > count) + amountToRead = count; + + // stuffIndex would have been already adjusted so there would + // always be room for at least one, and count is always at least + // one. + + iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead); + pCh->icount.rx += amountToRead; + + // Update the stuffIndex by the amount of data moved. Note we could + // never ask for more data than would just fit. However, we might + // have read in one more byte than we wanted because the read + // rounds up to even bytes. If this byte is on the end of the + // packet, and is padding, we ignore it. If the byte is part of + // the actual data, we need to move it. + + stuffIndex += amountToRead; + + if (stuffIndex >= IBUF_SIZE) { + if ((amountToRead & 1) && (count > amountToRead)) { + pCh->Ibuf[0] = pCh->Ibuf[IBUF_SIZE]; + amountToRead++; + stuffIndex = 1; + } else { + stuffIndex = 0; + } + } + + // If there is anything left over, read it as well + if (count > amountToRead) { + amountToRead = count - amountToRead; + iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead); + pCh->icount.rx += amountToRead; + stuffIndex += amountToRead; + } + + // Update stuff index + pCh->Ibuf_stuff = stuffIndex; + write_unlock_irqrestore(&pCh->Ibuf_spinlock, cflags); + write_unlock_irqrestore(&pB->read_fifo_spinlock, + bflags); + +#ifdef USE_IQ + schedule_work(&pCh->tqueue_input); +#else + do_input(&pCh->tqueue_input); +#endif + + // Note we do not need to maintain any flow-control credits at this + // time: if we were to increment .asof and decrement .room, there + // would be no net effect. Instead, when we strip data, we will + // increment .asof and leave .room unchanged. + + break; // From switch: ready for next packet + + case PTYPE_STATUS: + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 4, 0 ); + + count = CMD_COUNT_OF(pB->i2eLeadoffWord); + + iiReadBuf(pB, cmdBuffer, count); + // We can release early with buffer grab + write_unlock_irqrestore(&pB->read_fifo_spinlock, + bflags); + + pc = cmdBuffer; + pcLimit = &(cmdBuffer[count]); + + while (pc < pcLimit) { + channel = *pc++; + + ip2trace (channel, ITRC_SFIFO, 7, 2, channel, *pc ); + + /* check for valid channel */ + if (channel < pB->i2eChannelCnt + && + (pCh = (((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])) != NULL + ) + { + dss_change = 0; + + switch (uc = *pc++) + { + /* Breaks and modem signals are easy: just update status */ + case STAT_CTS_UP: + if ( !(pCh->dataSetIn & I2_CTS) ) + { + pCh->dataSetIn |= I2_DCTS; + pCh->icount.cts++; + dss_change = 1; + } + pCh->dataSetIn |= I2_CTS; + break; + + case STAT_CTS_DN: + if ( pCh->dataSetIn & I2_CTS ) + { + pCh->dataSetIn |= I2_DCTS; + pCh->icount.cts++; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_CTS; + break; + + case STAT_DCD_UP: + ip2trace (channel, ITRC_MODEM, 1, 1, pCh->dataSetIn ); + + if ( !(pCh->dataSetIn & I2_DCD) ) + { + ip2trace (CHANN, ITRC_MODEM, 2, 0 ); + pCh->dataSetIn |= I2_DDCD; + pCh->icount.dcd++; + dss_change = 1; + } + pCh->dataSetIn |= I2_DCD; + + ip2trace (channel, ITRC_MODEM, 3, 1, pCh->dataSetIn ); + break; + + case STAT_DCD_DN: + ip2trace (channel, ITRC_MODEM, 4, 1, pCh->dataSetIn ); + if ( pCh->dataSetIn & I2_DCD ) + { + ip2trace (channel, ITRC_MODEM, 5, 0 ); + pCh->dataSetIn |= I2_DDCD; + pCh->icount.dcd++; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_DCD; + + ip2trace (channel, ITRC_MODEM, 6, 1, pCh->dataSetIn ); + break; + + case STAT_DSR_UP: + if ( !(pCh->dataSetIn & I2_DSR) ) + { + pCh->dataSetIn |= I2_DDSR; + pCh->icount.dsr++; + dss_change = 1; + } + pCh->dataSetIn |= I2_DSR; + break; + + case STAT_DSR_DN: + if ( pCh->dataSetIn & I2_DSR ) + { + pCh->dataSetIn |= I2_DDSR; + pCh->icount.dsr++; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_DSR; + break; + + case STAT_RI_UP: + if ( !(pCh->dataSetIn & I2_RI) ) + { + pCh->dataSetIn |= I2_DRI; + pCh->icount.rng++; + dss_change = 1; + } + pCh->dataSetIn |= I2_RI ; + break; + + case STAT_RI_DN: + // to be compat with serial.c + //if ( pCh->dataSetIn & I2_RI ) + //{ + // pCh->dataSetIn |= I2_DRI; + // pCh->icount.rng++; + // dss_change = 1; + //} + pCh->dataSetIn &= ~I2_RI ; + break; + + case STAT_BRK_DET: + pCh->dataSetIn |= I2_BRK; + pCh->icount.brk++; + dss_change = 1; + break; + + // Bookmarks? one less request we're waiting for + case STAT_BMARK: + pCh->bookMarks--; + if (pCh->bookMarks <= 0 ) { + pCh->bookMarks = 0; + wake_up_interruptible( &pCh->pBookmarkWait ); + + ip2trace (channel, ITRC_DRAIN, 20, 1, pCh->BookmarkTimer.expires ); + } + break; + + // Flow control packets? Update the new credits, and if + // someone was waiting for output, queue him up again. + case STAT_FLOW: + pCh->outfl.room = + ((flowStatPtr)pc)->room - + (pCh->outfl.asof - ((flowStatPtr)pc)->asof); + + ip2trace (channel, ITRC_STFLW, 1, 1, pCh->outfl.room ); + + if (pCh->channelNeeds & NEED_CREDIT) + { + ip2trace (channel, ITRC_STFLW, 2, 1, pCh->channelNeeds); + + pCh->channelNeeds &= ~NEED_CREDIT; + i2QueueNeeds(pB, pCh, NEED_INLINE); + if ( pCh->pTTY ) + ip2_owake(pCh->pTTY); + } + + ip2trace (channel, ITRC_STFLW, 3, 1, pCh->channelNeeds); + + pc += sizeof(flowStat); + break; + + /* Special packets: */ + /* Just copy the information into the channel structure */ + + case STAT_STATUS: + + pCh->channelStatus = *((debugStatPtr)pc); + pc += sizeof(debugStat); + break; + + case STAT_TXCNT: + + pCh->channelTcount = *((cntStatPtr)pc); + pc += sizeof(cntStat); + break; + + case STAT_RXCNT: + + pCh->channelRcount = *((cntStatPtr)pc); + pc += sizeof(cntStat); + break; + + case STAT_BOXIDS: + pB->channelBtypes = *((bidStatPtr)pc); + pc += sizeof(bidStat); + set_baud_params(pB); + break; + + case STAT_HWFAIL: + i2QueueCommands (PTYPE_INLINE, pCh, 0, 1, CMD_HW_TEST); + pCh->channelFail = *((failStatPtr)pc); + pc += sizeof(failStat); + break; + + /* No explicit match? then + * Might be an error packet... + */ + default: + switch (uc & STAT_MOD_ERROR) + { + case STAT_ERROR: + if (uc & STAT_E_PARITY) { + pCh->dataSetIn |= I2_PAR; + pCh->icount.parity++; + } + if (uc & STAT_E_FRAMING){ + pCh->dataSetIn |= I2_FRA; + pCh->icount.frame++; + } + if (uc & STAT_E_OVERRUN){ + pCh->dataSetIn |= I2_OVR; + pCh->icount.overrun++; + } + break; + + case STAT_MODEM: + // the answer to DSS_NOW request (not change) + pCh->dataSetIn = (pCh->dataSetIn + & ~(I2_RI | I2_CTS | I2_DCD | I2_DSR) ) + | xlatDss[uc & 0xf]; + wake_up_interruptible ( &pCh->dss_now_wait ); + default: + break; + } + } /* End of switch on status type */ + if (dss_change) { +#ifdef USE_IQ + schedule_work(&pCh->tqueue_status); +#else + do_status(&pCh->tqueue_status); +#endif + } + } + else /* Or else, channel is invalid */ + { + // Even though the channel is invalid, we must test the + // status to see how much additional data it has (to be + // skipped) + switch (*pc++) + { + case STAT_FLOW: + pc += 4; /* Skip the data */ + break; + + default: + break; + } + } + } // End of while (there is still some status packet left) + break; + + default: // Neither packet? should be impossible + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 5, 1, + PTYPE_OF(pB->i2eLeadoffWord) ); + write_unlock_irqrestore(&pB->read_fifo_spinlock, + bflags); + + break; + } // End of switch on type of packets + } /*while(board I2_HAS_INPUT)*/ + + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_RETURN, 0 ); + + // Send acknowledgement to the board even if there was no data! + pB->i2eOutMailWaiting |= MB_IN_STRIPPED; + return; +} + +//****************************************************************************** +// Function: i2Write2Fifo(pB,address,count) +// Parameters: Pointer to a board structure, source address, byte count +// Returns: bytes written +// +// Description: +// Writes count bytes to board io address(implied) from source +// Adjusts count, leaves reserve for next time around bypass cmds +//****************************************************************************** +static int +i2Write2Fifo(i2eBordStrPtr pB, unsigned char *source, int count,int reserve) +{ + int rc = 0; + unsigned long flags; + write_lock_irqsave(&pB->write_fifo_spinlock, flags); + if (!pB->i2eWaitingForEmptyFifo) { + if (pB->i2eFifoRemains > (count+reserve)) { + pB->i2eFifoRemains -= count; + iiWriteBuf(pB, source, count); + pB->i2eOutMailWaiting |= MB_OUT_STUFFED; + rc = count; + } + } + write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); + return rc; +} +//****************************************************************************** +// Function: i2StuffFifoBypass(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as many bypass commands into the fifo as possible. This is simpler +// than stuffing data or inline commands to fifo, since we do not have +// flow-control to deal with. +//****************************************************************************** +static inline void +i2StuffFifoBypass(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned char *pRemove; + unsigned short stripIndex; + unsigned short packetSize; + unsigned short paddedSize; + unsigned short notClogged = 1; + unsigned long flags; + + int bailout = 1000; + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( --bailout && notClogged && + (NULL != (pCh = i2DeQueueNeeds(pB,NEED_BYPASS)))) + { + write_lock_irqsave(&pCh->Cbuf_spinlock, flags); + stripIndex = pCh->Cbuf_strip; + + // as long as there are packets for this channel... + + while (stripIndex != pCh->Cbuf_stuff) { + pRemove = &(pCh->Cbuf[stripIndex]); + packetSize = CMD_COUNT_OF(pRemove) + sizeof(i2CmdHeader); + paddedSize = roundup(packetSize, 2); + + if (paddedSize > 0) { + if ( 0 == i2Write2Fifo(pB, pRemove, paddedSize,0)) { + notClogged = 0; /* fifo full */ + i2QueueNeeds(pB, pCh, NEED_BYPASS); // Put back on queue + break; // Break from the channel + } + } +#ifdef DEBUG_FIFO +WriteDBGBuf("BYPS", pRemove, paddedSize); +#endif /* DEBUG_FIFO */ + pB->debugBypassCount++; + + pRemove += packetSize; + stripIndex += packetSize; + if (stripIndex >= CBUF_SIZE) { + stripIndex = 0; + pRemove = pCh->Cbuf; + } + } + // Done with this channel. Move to next, removing this one from + // the queue of channels if we cleaned it out (i.e., didn't get clogged. + pCh->Cbuf_strip = stripIndex; + write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags); + } // Either clogged or finished all the work + +#ifdef IP2DEBUG_TRACE + if ( !bailout ) { + ip2trace (ITRC_NO_PORT, ITRC_ERROR, 1, 0 ); + } +#endif +} + +//****************************************************************************** +// Function: i2StuffFifoFlow(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as many flow control packets into the fifo as possible. This is easier +// even than doing normal bypass commands, because there is always at most one +// packet, already assembled, for each channel. +//****************************************************************************** +static inline void +i2StuffFifoFlow(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned short paddedSize = roundup(sizeof(flowIn), 2); + + ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_ENTER, 2, + pB->i2eFifoRemains, paddedSize ); + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( (NULL != (pCh = i2DeQueueNeeds(pB,NEED_FLOW)))) { + pB->debugFlowCount++; + + // NO Chan LOCK needed ??? + if ( 0 == i2Write2Fifo(pB,(unsigned char *)&(pCh->infl),paddedSize,0)) { + break; + } +#ifdef DEBUG_FIFO + WriteDBGBuf("FLOW",(unsigned char *) &(pCh->infl), paddedSize); +#endif /* DEBUG_FIFO */ + + } // Either clogged or finished all the work + + ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_RETURN, 0 ); +} + +//****************************************************************************** +// Function: i2StuffFifoInline(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as much data and inline commands into the fifo as possible. This is +// the most complex fifo-stuffing operation, since there if now the channel +// flow-control issue to deal with. +//****************************************************************************** +static inline void +i2StuffFifoInline(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned char *pRemove; + unsigned short stripIndex; + unsigned short packetSize; + unsigned short paddedSize; + unsigned short notClogged = 1; + unsigned short flowsize; + unsigned long flags; + + int bailout = 1000; + int bailout2; + + ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_ENTER, 3, pB->i2eFifoRemains, + pB->i2Dbuf_strip, pB->i2Dbuf_stuff ); + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( --bailout && notClogged && + (NULL != (pCh = i2DeQueueNeeds(pB,NEED_INLINE))) ) + { + write_lock_irqsave(&pCh->Obuf_spinlock, flags); + stripIndex = pCh->Obuf_strip; + + ip2trace (CHANN, ITRC_SICMD, 3, 2, stripIndex, pCh->Obuf_stuff ); + + // as long as there are packets for this channel... + bailout2 = 1000; + while ( --bailout2 && stripIndex != pCh->Obuf_stuff) { + pRemove = &(pCh->Obuf[stripIndex]); + + // Must determine whether this be a data or command packet to + // calculate correctly the header size and the amount of + // flow-control credit this type of packet will use. + if (PTYPE_OF(pRemove) == PTYPE_DATA) { + flowsize = DATA_COUNT_OF(pRemove); + packetSize = flowsize + sizeof(i2DataHeader); + } else { + flowsize = CMD_COUNT_OF(pRemove); + packetSize = flowsize + sizeof(i2CmdHeader); + } + flowsize = CREDIT_USAGE(flowsize); + paddedSize = roundup(packetSize, 2); + + ip2trace (CHANN, ITRC_SICMD, 4, 2, pB->i2eFifoRemains, paddedSize ); + + // If we don't have enough credits from the board to send the data, + // flag the channel that we are waiting for flow control credit, and + // break out. This will clean up this channel and remove us from the + // queue of hot things to do. + + ip2trace (CHANN, ITRC_SICMD, 5, 2, pCh->outfl.room, flowsize ); + + if (pCh->outfl.room <= flowsize) { + // Do Not have the credits to send this packet. + i2QueueNeeds(pB, pCh, NEED_CREDIT); + notClogged = 0; + break; // So to do next channel + } + if ( (paddedSize > 0) + && ( 0 == i2Write2Fifo(pB, pRemove, paddedSize, 128))) { + // Do Not have room in fifo to send this packet. + notClogged = 0; + i2QueueNeeds(pB, pCh, NEED_INLINE); + break; // Break from the channel + } +#ifdef DEBUG_FIFO +WriteDBGBuf("DATA", pRemove, paddedSize); +#endif /* DEBUG_FIFO */ + pB->debugInlineCount++; + + pCh->icount.tx += flowsize; + // Update current credits + pCh->outfl.room -= flowsize; + pCh->outfl.asof += flowsize; + if (PTYPE_OF(pRemove) == PTYPE_DATA) { + pCh->Obuf_char_count -= DATA_COUNT_OF(pRemove); + } + pRemove += packetSize; + stripIndex += packetSize; + + ip2trace (CHANN, ITRC_SICMD, 6, 2, stripIndex, pCh->Obuf_strip); + + if (stripIndex >= OBUF_SIZE) { + stripIndex = 0; + pRemove = pCh->Obuf; + + ip2trace (CHANN, ITRC_SICMD, 7, 1, stripIndex ); + + } + } /* while */ + if ( !bailout2 ) { + ip2trace (CHANN, ITRC_ERROR, 3, 0 ); + } + // Done with this channel. Move to next, removing this one from the + // queue of channels if we cleaned it out (i.e., didn't get clogged. + pCh->Obuf_strip = stripIndex; + write_unlock_irqrestore(&pCh->Obuf_spinlock, flags); + if ( notClogged ) + { + + ip2trace (CHANN, ITRC_SICMD, 8, 0 ); + + if ( pCh->pTTY ) { + ip2_owake(pCh->pTTY); + } + } + } // Either clogged or finished all the work + + if ( !bailout ) { + ip2trace (ITRC_NO_PORT, ITRC_ERROR, 4, 0 ); + } + + ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_RETURN, 1,pB->i2Dbuf_strip); +} + +//****************************************************************************** +// Function: serviceOutgoingFifo(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Helper routine to put data in the outgoing fifo, if we aren't already waiting +// for something to be there. If the fifo has only room for a very little data, +// go head and hit the board with a mailbox hit immediately. Otherwise, it will +// have to happen later in the interrupt processing. Since this routine may be +// called both at interrupt and foreground time, we must turn off interrupts +// during the entire process. +//****************************************************************************** +static void +serviceOutgoingFifo(i2eBordStrPtr pB) +{ + // If we aren't currently waiting for the board to empty our fifo, service + // everything that is pending, in priority order (especially, Bypass before + // Inline). + if ( ! pB->i2eWaitingForEmptyFifo ) + { + i2StuffFifoFlow(pB); + i2StuffFifoBypass(pB); + i2StuffFifoInline(pB); + + iiSendPendingMail(pB); + } +} + +//****************************************************************************** +// Function: i2ServiceBoard(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Normally this is called from interrupt level, but there is deliberately +// nothing in here specific to being called from interrupt level. All the +// hardware-specific, interrupt-specific things happen at the outer levels. +// +// For example, a timer interrupt could drive this routine for some sort of +// polled operation. The only requirement is that the programmer deal with any +// atomiticity/concurrency issues that result. +// +// This routine responds to the board's having sent mailbox information to the +// host (which would normally cause an interrupt). This routine reads the +// incoming mailbox. If there is no data in it, this board did not create the +// interrupt and/or has nothing to be done to it. (Except, if we have been +// waiting to write mailbox data to it, we may do so. +// +// Based on the value in the mailbox, we may take various actions. +// +// No checking here of pB validity: after all, it shouldn't have been called by +// the handler unless pB were on the list. +//****************************************************************************** +static inline int +i2ServiceBoard ( i2eBordStrPtr pB ) +{ + unsigned inmail; + unsigned long flags; + + + /* This should be atomic because of the way we are called... */ + if (NO_MAIL_HERE == ( inmail = pB->i2eStartMail ) ) { + inmail = iiGetMail(pB); + } + pB->i2eStartMail = NO_MAIL_HERE; + + ip2trace (ITRC_NO_PORT, ITRC_INTR, 2, 1, inmail ); + + if (inmail != NO_MAIL_HERE) { + // If the board has gone fatal, nothing to do but hit a bit that will + // alert foreground tasks to protest! + if ( inmail & MB_FATAL_ERROR ) { + pB->i2eFatal = 1; + goto exit_i2ServiceBoard; + } + + /* Assuming no fatal condition, we proceed to do work */ + if ( inmail & MB_IN_STUFFED ) { + pB->i2eFifoInInts++; + i2StripFifo(pB); /* There might be incoming packets */ + } + + if (inmail & MB_OUT_STRIPPED) { + pB->i2eFifoOutInts++; + write_lock_irqsave(&pB->write_fifo_spinlock, flags); + pB->i2eFifoRemains = pB->i2eFifoSize; + pB->i2eWaitingForEmptyFifo = 0; + write_unlock_irqrestore(&pB->write_fifo_spinlock, + flags); + + ip2trace (ITRC_NO_PORT, ITRC_INTR, 30, 1, pB->i2eFifoRemains ); + + } + serviceOutgoingFifo(pB); + } + + ip2trace (ITRC_NO_PORT, ITRC_INTR, 8, 0 ); + +exit_i2ServiceBoard: + + return 0; +} diff --git a/drivers/staging/tty/ip2/i2lib.h b/drivers/staging/tty/ip2/i2lib.h new file mode 100644 index 000000000000..e559e9bac06d --- /dev/null +++ b/drivers/staging/tty/ip2/i2lib.h @@ -0,0 +1,351 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Header file for high level library functions +* +*******************************************************************************/ +#ifndef I2LIB_H +#define I2LIB_H 1 +//------------------------------------------------------------------------------ +// I2LIB.H +// +// IntelliPort-II and IntelliPort-IIEX +// +// Defines, structure definitions, and external declarations for i2lib.c +//------------------------------------------------------------------------------ +//-------------------------------------- +// Mandatory Includes: +//-------------------------------------- +#include "ip2types.h" +#include "i2ellis.h" +#include "i2pack.h" +#include "i2cmd.h" +#include + +//------------------------------------------------------------------------------ +// i2ChanStr -- Channel Structure: +// Used to track per-channel information for the library routines using standard +// loadware. Note also, a pointer to an array of these structures is patched +// into the i2eBordStr (see i2ellis.h) +//------------------------------------------------------------------------------ +// +// If we make some limits on the maximum block sizes, we can avoid dealing with +// buffer wrap. The wrapping of the buffer is based on where the start of the +// packet is. Then there is always room for the packet contiguously. +// +// Maximum total length of an outgoing data or in-line command block. The limit +// of 36 on data is quite arbitrary and based more on DOS memory limitations +// than the board interface. However, for commands, the maximum packet length is +// MAX_CMD_PACK_SIZE, because the field size for the count is only a few bits +// (see I2PACK.H) in such packets. For data packets, the count field size is not +// the limiting factor. As of this writing, MAX_OBUF_BLOCK < MAX_CMD_PACK_SIZE, +// but be careful if wanting to modify either. +// +#define MAX_OBUF_BLOCK 36 + +// Another note on maximum block sizes: we are buffering packets here. Data is +// put into the buffer (if there is room) regardless of the credits from the +// board. The board sends new credits whenever it has removed from his buffers a +// number of characters equal to 80% of total buffer size. (Of course, the total +// buffer size is what is reported when the very first set of flow control +// status packets are received from the board. Therefore, to be robust, you must +// always fill the board to at least 80% of the current credit limit, else you +// might not give it enough to trigger a new report. These conditions are +// obtained here so long as the maximum output block size is less than 20% the +// size of the board's output buffers. This is true at present by "coincidence" +// or "infernal knowledge": the board's output buffers are at least 700 bytes +// long (20% = 140 bytes, at least). The 80% figure is "official", so the safest +// strategy might be to trap the first flow control report and guarantee that +// the effective maxObufBlock is the minimum of MAX_OBUF_BLOCK and 20% of first +// reported buffer credit. +// +#define MAX_CBUF_BLOCK 6 // Maximum total length of a bypass command block + +#define IBUF_SIZE 512 // character capacity of input buffer per channel +#define OBUF_SIZE 1024// character capacity of output buffer per channel +#define CBUF_SIZE 10 // character capacity of output bypass buffer + +typedef struct _i2ChanStr +{ + // First, back-pointers so that given a pointer to this structure, you can + // determine the correct board and channel number to reference, (say, when + // issuing commands, etc. (Note, channel number is in infl.hd.i2sChannel.) + + int port_index; // Index of port in channel structure array attached + // to board structure. + PTTY pTTY; // Pointer to tty structure for port (OS specific) + USHORT validity; // Indicates whether the given channel has been + // initialized, really exists (or is a missing + // channel, e.g. channel 9 on an 8-port box.) + + i2eBordStrPtr pMyBord; // Back-pointer to this channel's board structure + + int wopen; // waiting fer carrier + + int throttled; // Set if upper layer can take no data + + int flags; // Defined in tty.h + + PWAITQ open_wait; // Pointer for OS sleep function. + PWAITQ close_wait; // Pointer for OS sleep function. + PWAITQ delta_msr_wait;// Pointer for OS sleep function. + PWAITQ dss_now_wait; // Pointer for OS sleep function. + + struct timer_list BookmarkTimer; // Used by i2DrainOutput + wait_queue_head_t pBookmarkWait; // Used by i2DrainOutput + + int BaudBase; + int BaudDivisor; + + USHORT ClosingDelay; + USHORT ClosingWaitTime; + + volatile + flowIn infl; // This structure is initialized as a completely + // formed flow-control command packet, and as such + // has the channel number, also the capacity and + // "as-of" data needed continuously. + + USHORT sinceLastFlow; // Counts the number of characters read from input + // buffers, since the last time flow control info + // was sent. + + USHORT whenSendFlow; // Determines when new flow control is to be sent to + // the board. Note unlike earlier manifestations of + // the driver, these packets can be sent from + // in-place. + + USHORT channelNeeds; // Bit map of important things which must be done + // for this channel. (See bits below ) + + volatile + flowStat outfl; // Same type of structure is used to hold current + // flow control information used to control our + // output. "asof" is kept updated as data is sent, + // and "room" never goes to zero. + + // The incoming ring buffer + // Unlike the outgoing buffers, this holds raw data, not packets. The two + // extra bytes are used to hold the byte-padding when there is room for an + // odd number of bytes before we must wrap. + // + UCHAR Ibuf[IBUF_SIZE + 2]; + volatile + USHORT Ibuf_stuff; // Stuffing index + volatile + USHORT Ibuf_strip; // Stripping index + + // The outgoing ring-buffer: Holds Data and command packets. N.B., even + // though these are in the channel structure, the channel is also written + // here, the easier to send it to the fifo when ready. HOWEVER, individual + // packets here are NOT padded to even length: the routines for writing + // blocks to the fifo will pad to even byte counts. + // + UCHAR Obuf[OBUF_SIZE+MAX_OBUF_BLOCK+4]; + volatile + USHORT Obuf_stuff; // Stuffing index + volatile + USHORT Obuf_strip; // Stripping index + int Obuf_char_count; + + // The outgoing bypass-command buffer. Unlike earlier manifestations, the + // flow control packets are sent directly from the structures. As above, the + // channel number is included in the packet, but they are NOT padded to even + // size. + // + UCHAR Cbuf[CBUF_SIZE+MAX_CBUF_BLOCK+2]; + volatile + USHORT Cbuf_stuff; // Stuffing index + volatile + USHORT Cbuf_strip; // Stripping index + + // The temporary buffer for the Linux tty driver PutChar entry. + // + UCHAR Pbuf[MAX_OBUF_BLOCK - sizeof (i2DataHeader)]; + volatile + USHORT Pbuf_stuff; // Stuffing index + + // The state of incoming data-set signals + // + USHORT dataSetIn; // Bit-mapped according to below. Also indicates + // whether a break has been detected since last + // inquiry. + + // The state of outcoming data-set signals (as far as we can tell!) + // + USHORT dataSetOut; // Bit-mapped according to below. + + // Most recent hot-key identifier detected + // + USHORT hotKeyIn; // Hot key as sent by the board, HOT_CLEAR indicates + // no hot key detected since last examined. + + // Counter of outstanding requests for bookmarks + // + short bookMarks; // Number of outstanding bookmark requests, (+ive + // whenever a bookmark request if queued up, -ive + // whenever a bookmark is received). + + // Misc options + // + USHORT channelOptions; // See below + + // To store various incoming special packets + // + debugStat channelStatus; + cntStat channelRcount; + cntStat channelTcount; + failStat channelFail; + + // To store the last values for line characteristics we sent to the board. + // + int speed; + + int flush_flags; + + void (*trace)(unsigned short,unsigned char,unsigned char,unsigned long,...); + + /* + * Kernel counters for the 4 input interrupts + */ + struct async_icount icount; + + /* + * Task queues for processing input packets from the board. + */ + struct work_struct tqueue_input; + struct work_struct tqueue_status; + struct work_struct tqueue_hangup; + + rwlock_t Ibuf_spinlock; + rwlock_t Obuf_spinlock; + rwlock_t Cbuf_spinlock; + rwlock_t Pbuf_spinlock; + +} i2ChanStr, *i2ChanStrPtr; + +//--------------------------------------------------- +// Manifests and bit-maps for elements in i2ChanStr +//--------------------------------------------------- +// +// flush flags +// +#define STARTFL_FLAG 1 +#define STOPFL_FLAG 2 + +// validity +// +#define CHANNEL_MAGIC_BITS 0xff00 +#define CHANNEL_MAGIC 0x5300 // (validity & CHANNEL_MAGIC_BITS) == + // CHANNEL_MAGIC --> structure good + +#define CHANNEL_SUPPORT 0x0001 // Indicates channel is supported, exists, + // and passed P.O.S.T. + +// channelNeeds +// +#define NEED_FLOW 1 // Indicates flow control has been queued +#define NEED_INLINE 2 // Indicates inline commands or data queued +#define NEED_BYPASS 4 // Indicates bypass commands queued +#define NEED_CREDIT 8 // Indicates would be sending except has not sufficient + // credit. The data is still in the channel structure, + // but the channel is not enqueued in the board + // structure again until there is a credit received from + // the board. + +// dataSetIn (Also the bits for i2GetStatus return value) +// +#define I2_DCD 1 +#define I2_CTS 2 +#define I2_DSR 4 +#define I2_RI 8 + +// dataSetOut (Also the bits for i2GetStatus return value) +// +#define I2_DTR 1 +#define I2_RTS 2 + +// i2GetStatus() can optionally clear these bits +// +#define I2_BRK 0x10 // A break was detected +#define I2_PAR 0x20 // A parity error was received +#define I2_FRA 0x40 // A framing error was received +#define I2_OVR 0x80 // An overrun error was received + +// i2GetStatus() automatically clears these bits */ +// +#define I2_DDCD 0x100 // DCD changed from its former value +#define I2_DCTS 0x200 // CTS changed from its former value +#define I2_DDSR 0x400 // DSR changed from its former value +#define I2_DRI 0x800 // RI changed from its former value + +// hotKeyIn +// +#define HOT_CLEAR 0x1322 // Indicates that no hot-key has been detected + +// channelOptions +// +#define CO_NBLOCK_WRITE 1 // Writes don't block waiting for buffer. (Default + // is, they do wait.) + +// fcmodes +// +#define I2_OUTFLOW_CTS 0x0001 +#define I2_INFLOW_RTS 0x0002 +#define I2_INFLOW_DSR 0x0004 +#define I2_INFLOW_DTR 0x0008 +#define I2_OUTFLOW_DSR 0x0010 +#define I2_OUTFLOW_DTR 0x0020 +#define I2_OUTFLOW_XON 0x0040 +#define I2_OUTFLOW_XANY 0x0080 +#define I2_INFLOW_XON 0x0100 + +#define I2_CRTSCTS (I2_OUTFLOW_CTS|I2_INFLOW_RTS) +#define I2_IXANY_MODE (I2_OUTFLOW_XON|I2_OUTFLOW_XANY) + +//------------------------------------------- +// Macros used from user level like functions +//------------------------------------------- + +// Macros to set and clear channel options +// +#define i2SetOption(pCh, option) pCh->channelOptions |= option +#define i2ClrOption(pCh, option) pCh->channelOptions &= ~option + +// Macro to set fatal-error trap +// +#define i2SetFatalTrap(pB, routine) pB->i2eFatalTrap = routine + +//-------------------------------------------- +// Declarations and prototypes for i2lib.c +//-------------------------------------------- +// +static int i2InitChannels(i2eBordStrPtr, int, i2ChanStrPtr); +static int i2QueueCommands(int, i2ChanStrPtr, int, int, cmdSyntaxPtr,...); +static int i2GetStatus(i2ChanStrPtr, int); +static int i2Input(i2ChanStrPtr); +static int i2InputFlush(i2ChanStrPtr); +static int i2Output(i2ChanStrPtr, const char *, int); +static int i2OutputFree(i2ChanStrPtr); +static int i2ServiceBoard(i2eBordStrPtr); +static void i2DrainOutput(i2ChanStrPtr, int); + +#ifdef IP2DEBUG_TRACE +void ip2trace(unsigned short,unsigned char,unsigned char,unsigned long,...); +#else +#define ip2trace(a,b,c,d...) do {} while (0) +#endif + +// Argument to i2QueueCommands +// +#define C_IN_LINE 1 +#define C_BYPASS 0 + +#endif // I2LIB_H diff --git a/drivers/staging/tty/ip2/i2pack.h b/drivers/staging/tty/ip2/i2pack.h new file mode 100644 index 000000000000..00342a677c90 --- /dev/null +++ b/drivers/staging/tty/ip2/i2pack.h @@ -0,0 +1,364 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions of the packets used to transfer data and commands +* Host <--> Board. Information provided here is only applicable +* when the standard loadware is active. +* +*******************************************************************************/ +#ifndef I2PACK_H +#define I2PACK_H 1 + +//----------------------------------------------- +// Revision History: +// +// 10 October 1991 MAG First draft +// 24 February 1992 MAG Additions for 1.4.x loadware +// 11 March 1992 MAG New status packets +// +//----------------------------------------------- + +//------------------------------------------------------------------------------ +// Packet Formats: +// +// Information passes between the host and board through the FIFO in packets. +// These have headers which indicate the type of packet. Because the fifo data +// path may be 16-bits wide, the protocol is constrained such that each packet +// is always padded to an even byte count. (The lower-level interface routines +// -- i2ellis.c -- are designed to do this). +// +// The sender (be it host or board) must place some number of complete packets +// in the fifo, then place a message in the mailbox that packets are available. +// Placing such a message interrupts the "receiver" (be it board or host), who +// reads the mailbox message and determines that there are incoming packets +// ready. Since there are no partial packets, and the length of a packet is +// given in the header, the remainder of the packet can be read without checking +// for FIFO empty condition. The process is repeated, packet by packet, until +// the incoming FIFO is empty. Then the receiver uses the outbound mailbox to +// signal the board that it has read the data. Only then can the sender place +// additional data in the fifo. +//------------------------------------------------------------------------------ +// +//------------------------------------------------ +// Definition of Packet Header Area +//------------------------------------------------ +// +// Caution: these only define header areas. In actual use the data runs off +// beyond the end of these structures. +// +// Since these structures are based on sequences of bytes which go to the board, +// there cannot be ANY padding between the elements. +#pragma pack(1) + +//---------------------------- +// DATA PACKETS +//---------------------------- + +typedef struct _i2DataHeader +{ + unsigned char i2sChannel; /* The channel number: 0-255 */ + + // -- Bitfields are allocated LSB first -- + + // For incoming data, indicates whether this is an ordinary packet or a + // special one (e.g., hot key hit). + unsigned i2sId : 2 __attribute__ ((__packed__)); + + // For tagging data packets. There are flush commands which flush only data + // packets bearing a particular tag. (used in implementing IntelliView and + // IntelliPrint). THE TAG VALUE 0xf is RESERVED and must not be used (it has + // meaning internally to the loadware). + unsigned i2sTag : 4; + + // These two bits determine the type of packet sent/received. + unsigned i2sType : 2; + + // The count of data to follow: does not include the possible additional + // padding byte. MAXIMUM COUNT: 4094. The top four bits must be 0. + unsigned short i2sCount; + +} i2DataHeader, *i2DataHeaderPtr; + +// Structure is immediately followed by the data, proper. + +//---------------------------- +// NON-DATA PACKETS +//---------------------------- + +typedef struct _i2CmdHeader +{ + unsigned char i2sChannel; // The channel number: 0-255 (Except where noted + // - see below + + // Number of bytes of commands, status or whatever to follow + unsigned i2sCount : 6; + + // These two bits determine the type of packet sent/received. + unsigned i2sType : 2; + +} i2CmdHeader, *i2CmdHeaderPtr; + +// Structure is immediately followed by the applicable data. + +//--------------------------------------- +// Flow Control Packets (Outbound) +//--------------------------------------- + +// One type of outbound command packet is so important that the entire structure +// is explicitly defined here. That is the flow-control packet. This is never +// sent by user-level code (as would be the commands to raise/lower DTR, for +// example). These are only sent by the library routines in response to reading +// incoming data into the buffers. +// +// The parameters inside the command block are maintained in place, then the +// block is sent at the appropriate time. + +typedef struct _flowIn +{ + i2CmdHeader hd; // Channel #, count, type (see above) + unsigned char fcmd; // The flow control command (37) + unsigned short asof; // As of byte number "asof" (LSB first!) I have room + // for "room" bytes + unsigned short room; +} flowIn, *flowInPtr; + +//---------------------------------------- +// (Incoming) Status Packets +//---------------------------------------- + +// Incoming packets which are non-data packets are status packets. In this case, +// the channel number in the header is unimportant. What follows are one or more +// sub-packets, the first word of which consists of the channel (first or low +// byte) and the status indicator (second or high byte), followed by possibly +// more data. + +#define STAT_CTS_UP 0 /* CTS raised (no other bytes) */ +#define STAT_CTS_DN 1 /* CTS dropped (no other bytes) */ +#define STAT_DCD_UP 2 /* DCD raised (no other bytes) */ +#define STAT_DCD_DN 3 /* DCD dropped (no other bytes) */ +#define STAT_DSR_UP 4 /* DSR raised (no other bytes) */ +#define STAT_DSR_DN 5 /* DSR dropped (no other bytes) */ +#define STAT_RI_UP 6 /* RI raised (no other bytes) */ +#define STAT_RI_DN 7 /* RI dropped (no other bytes) */ +#define STAT_BRK_DET 8 /* BRK detect (no other bytes) */ +#define STAT_FLOW 9 /* Flow control(-- more: see below */ +#define STAT_BMARK 10 /* Bookmark (no other bytes) + * Bookmark is sent as a response to + * a command 60: request for bookmark + */ +#define STAT_STATUS 11 /* Special packet: see below */ +#define STAT_TXCNT 12 /* Special packet: see below */ +#define STAT_RXCNT 13 /* Special packet: see below */ +#define STAT_BOXIDS 14 /* Special packet: see below */ +#define STAT_HWFAIL 15 /* Special packet: see below */ + +#define STAT_MOD_ERROR 0xc0 +#define STAT_MODEM 0xc0/* If status & STAT_MOD_ERROR: + * == STAT_MODEM, then this is a modem + * status packet, given in response to a + * CMD_DSS_NOW command. + * The low nibble has each data signal: + */ +#define STAT_MOD_DCD 0x8 +#define STAT_MOD_RI 0x4 +#define STAT_MOD_DSR 0x2 +#define STAT_MOD_CTS 0x1 + +#define STAT_ERROR 0x80/* If status & STAT_MOD_ERROR + * == STAT_ERROR, then + * sort of error on the channel. + * The remaining seven bits indicate + * what sort of error it is. + */ +/* The low three bits indicate parity, framing, or overrun errors */ + +#define STAT_E_PARITY 4 /* Parity error */ +#define STAT_E_FRAMING 2 /* Framing error */ +#define STAT_E_OVERRUN 1 /* (uxart) overrun error */ + +//--------------------------------------- +// STAT_FLOW packets +//--------------------------------------- + +typedef struct _flowStat +{ + unsigned short asof; + unsigned short room; +}flowStat, *flowStatPtr; + +// flowStat packets are received from the board to regulate the flow of outgoing +// data. A local copy of this structure is also kept to track the amount of +// credits used and credits remaining. "room" is the amount of space in the +// board's buffers, "as of" having received a certain byte number. When sending +// data to the fifo, you must calculate how much buffer space your packet will +// use. Add this to the current "asof" and subtract it from the current "room". +// +// The calculation for the board's buffer is given by CREDIT_USAGE, where size +// is the un-rounded count of either data characters or command characters. +// (Which is to say, the count rounded up, plus two). + +#define CREDIT_USAGE(size) (((size) + 3) & ~1) + +//--------------------------------------- +// STAT_STATUS packets +//--------------------------------------- + +typedef struct _debugStat +{ + unsigned char d_ccsr; + unsigned char d_txinh; + unsigned char d_stat1; + unsigned char d_stat2; +} debugStat, *debugStatPtr; + +// debugStat packets are sent to the host in response to a CMD_GET_STATUS +// command. Each byte is bit-mapped as described below: + +#define D_CCSR_XON 2 /* Has received XON, ready to transmit */ +#define D_CCSR_XOFF 4 /* Has received XOFF, not transmitting */ +#define D_CCSR_TXENAB 8 /* Transmitter is enabled */ +#define D_CCSR_RXENAB 0x80 /* Receiver is enabled */ + +#define D_TXINH_BREAK 1 /* We are sending a break */ +#define D_TXINH_EMPTY 2 /* No data to send */ +#define D_TXINH_SUSP 4 /* Output suspended via command 57 */ +#define D_TXINH_CMD 8 /* We are processing an in-line command */ +#define D_TXINH_LCD 0x10 /* LCD diagnostics are running */ +#define D_TXINH_PAUSE 0x20 /* We are processing a PAUSE command */ +#define D_TXINH_DCD 0x40 /* DCD is low, preventing transmission */ +#define D_TXINH_DSR 0x80 /* DSR is low, preventing transmission */ + +#define D_STAT1_TXEN 1 /* Transmit INTERRUPTS enabled */ +#define D_STAT1_RXEN 2 /* Receiver INTERRUPTS enabled */ +#define D_STAT1_MDEN 4 /* Modem (data set sigs) interrupts enabled */ +#define D_STAT1_RLM 8 /* Remote loopback mode selected */ +#define D_STAT1_LLM 0x10 /* Local internal loopback mode selected */ +#define D_STAT1_CTS 0x20 /* CTS is low, preventing transmission */ +#define D_STAT1_DTR 0x40 /* DTR is low, to stop remote transmission */ +#define D_STAT1_RTS 0x80 /* RTS is low, to stop remote transmission */ + +#define D_STAT2_TXMT 1 /* Transmit buffers are all empty */ +#define D_STAT2_RXMT 2 /* Receive buffers are all empty */ +#define D_STAT2_RXINH 4 /* Loadware has tried to inhibit remote + * transmission: dropped DTR, sent XOFF, + * whatever... + */ +#define D_STAT2_RXFLO 8 /* Loadware can send no more data to host + * until it receives a flow-control packet + */ +//----------------------------------------- +// STAT_TXCNT and STAT_RXCNT packets +//---------------------------------------- + +typedef struct _cntStat +{ + unsigned short cs_time; // (Assumes host is little-endian!) + unsigned short cs_count; +} cntStat, *cntStatPtr; + +// These packets are sent in response to a CMD_GET_RXCNT or a CMD_GET_TXCNT +// bypass command. cs_time is a running 1 Millisecond counter which acts as a +// time stamp. cs_count is a running counter of data sent or received from the +// uxarts. (Not including data added by the chip itself, as with CRLF +// processing). +//------------------------------------------ +// STAT_HWFAIL packets +//------------------------------------------ + +typedef struct _failStat +{ + unsigned char fs_written; + unsigned char fs_read; + unsigned short fs_address; +} failStat, *failStatPtr; + +// This packet is sent whenever the on-board diagnostic process detects an +// error. At startup, this process is dormant. The host can wake it up by +// issuing the bypass command CMD_HW_TEST. The process runs at low priority and +// performs continuous hardware verification; writing data to certain on-board +// registers, reading it back, and comparing. If it detects an error, this +// packet is sent to the host, and the process goes dormant again until the host +// sends another CMD_HW_TEST. It then continues with the next register to be +// tested. + +//------------------------------------------------------------------------------ +// Macros to deal with the headers more easily! Note that these are defined so +// they may be used as "left" as well as "right" expressions. +//------------------------------------------------------------------------------ + +// Given a pointer to the packet, reference the channel number +// +#define CHANNEL_OF(pP) ((i2DataHeaderPtr)(pP))->i2sChannel + +// Given a pointer to the packet, reference the Packet type +// +#define PTYPE_OF(pP) ((i2DataHeaderPtr)(pP))->i2sType + +// The possible types of packets +// +#define PTYPE_DATA 0 /* Host <--> Board */ +#define PTYPE_BYPASS 1 /* Host ---> Board */ +#define PTYPE_INLINE 2 /* Host ---> Board */ +#define PTYPE_STATUS 2 /* Host <--- Board */ + +// Given a pointer to a Data packet, reference the Tag +// +#define TAG_OF(pP) ((i2DataHeaderPtr)(pP))->i2sTag + +// Given a pointer to a Data packet, reference the data i.d. +// +#define ID_OF(pP) ((i2DataHeaderPtr)(pP))->i2sId + +// The possible types of ID's +// +#define ID_ORDINARY_DATA 0 +#define ID_HOT_KEY 1 + +// Given a pointer to a Data packet, reference the count +// +#define DATA_COUNT_OF(pP) ((i2DataHeaderPtr)(pP))->i2sCount + +// Given a pointer to a Data packet, reference the beginning of data +// +#define DATA_OF(pP) &((unsigned char *)(pP))[4] // 4 = size of header + +// Given a pointer to a Non-Data packet, reference the count +// +#define CMD_COUNT_OF(pP) ((i2CmdHeaderPtr)(pP))->i2sCount + +#define MAX_CMD_PACK_SIZE 62 // Maximum size of such a count + +// Given a pointer to a Non-Data packet, reference the beginning of data +// +#define CMD_OF(pP) &((unsigned char *)(pP))[2] // 2 = size of header + +//-------------------------------- +// MailBox Bits: +//-------------------------------- + +//-------------------------- +// Outgoing (host to board) +//-------------------------- +// +#define MB_OUT_STUFFED 0x80 // Host has placed output in fifo +#define MB_IN_STRIPPED 0x40 // Host has read in all input from fifo + +//-------------------------- +// Incoming (board to host) +//-------------------------- +// +#define MB_IN_STUFFED 0x80 // Board has placed input in fifo +#define MB_OUT_STRIPPED 0x40 // Board has read all output from fifo +#define MB_FATAL_ERROR 0x20 // Board has encountered a fatal error + +#pragma pack() // Reset padding to command-line default + +#endif // I2PACK_H + diff --git a/drivers/staging/tty/ip2/ip2.h b/drivers/staging/tty/ip2/ip2.h new file mode 100644 index 000000000000..936ccc533949 --- /dev/null +++ b/drivers/staging/tty/ip2/ip2.h @@ -0,0 +1,107 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants for configuration and tuning +* +* NOTES: +* +*******************************************************************************/ +#ifndef IP2_H +#define IP2_H + +#include "ip2types.h" +#include "i2cmd.h" + +/*************/ +/* Constants */ +/*************/ + +/* Device major numbers - since version 2.0.26. */ +#define IP2_TTY_MAJOR 71 +#define IP2_CALLOUT_MAJOR 72 +#define IP2_IPL_MAJOR 73 + +/* Board configuration array. + * This array defines the hardware irq and address for up to IP2_MAX_BOARDS + * (4 supported per ip2_types.h) ISA board addresses and irqs MUST be specified, + * PCI and EISA boards are probed for and automagicly configed + * iff the addresses are set to 1 and 2 respectivily. + * 0x0100 - 0x03f0 == ISA + * 1 == PCI + * 2 == EISA + * 0 == (skip this board) + * This array defines the hardware addresses for them. Special + * addresses are EISA and PCI which go sniffing for boards. + + * In a multiboard system the position in the array determines which port + * devices are assigned to each board: + * board 0 is assigned ttyF0.. to ttyF63, + * board 1 is assigned ttyF64 to ttyF127, + * board 2 is assigned ttyF128 to ttyF191, + * board 3 is assigned ttyF192 to ttyF255. + * + * In PCI and EISA bus systems each range is mapped to card in + * monotonically increasing slot number order, ISA position is as specified + * here. + + * If the irqs are ALL set to 0,0,0,0 all boards operate in + * polled mode. For interrupt operation ISA boards require that the IRQ be + * specified, while PCI and EISA boards any nonzero entry + * will enable interrupts using the BIOS configured irq for the board. + * An invalid irq entry will default to polled mode for that card and print + * console warning. + + * When the driver is loaded as a module these setting can be overridden on the + * modprobe command line or on an option line in /etc/modprobe.conf. + * If the driver is built-in the configuration must be + * set here for ISA cards and address set to 1 and 2 for PCI and EISA. + * + * Here is an example that shows most if not all possibe combinations: + + *static ip2config_t ip2config = + *{ + * {11,1,0,0}, // irqs + * { // Addresses + * 0x0308, // Board 0, ttyF0 - ttyF63// ISA card at io=0x308, irq=11 + * 0x0001, // Board 1, ttyF64 - ttyF127//PCI card configured by BIOS + * 0x0000, // Board 2, ttyF128 - ttyF191// Slot skipped + * 0x0002 // Board 3, ttyF192 - ttyF255//EISA card configured by BIOS + * // but polled not irq driven + * } + *}; + */ + + /* this structure is zeroed out because the suggested method is to configure + * the driver as a module, set up the parameters with an options line in + * /etc/modprobe.conf and load with modprobe or kmod, the kernel + * module loader + */ + + /* This structure is NOW always initialized when the driver is initialized. + * Compiled in defaults MUST be added to the io and irq arrays in + * ip2.c. Those values are configurable from insmod parameters in the + * case of modules or from command line parameters (ip2=io,irq) when + * compiled in. + */ + +static ip2config_t ip2config = +{ + {0,0,0,0}, // irqs + { // Addresses + /* Do NOT set compile time defaults HERE! Use the arrays in + ip2.c! These WILL be overwritten! =mhw= */ + 0x0000, // Board 0, ttyF0 - ttyF63 + 0x0000, // Board 1, ttyF64 - ttyF127 + 0x0000, // Board 2, ttyF128 - ttyF191 + 0x0000 // Board 3, ttyF192 - ttyF255 + } +}; + +#endif diff --git a/drivers/staging/tty/ip2/ip2ioctl.h b/drivers/staging/tty/ip2/ip2ioctl.h new file mode 100644 index 000000000000..aa0a9da85e05 --- /dev/null +++ b/drivers/staging/tty/ip2/ip2ioctl.h @@ -0,0 +1,35 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants for configuration and tuning +* +* NOTES: +* +*******************************************************************************/ + +#ifndef IP2IOCTL_H +#define IP2IOCTL_H + +//************* +//* Constants * +//************* + +// High baud rates (if not defined elsewhere. +#ifndef B153600 +# define B153600 0010005 +#endif +#ifndef B307200 +# define B307200 0010006 +#endif +#ifndef B921600 +# define B921600 0010007 +#endif + +#endif diff --git a/drivers/staging/tty/ip2/ip2main.c b/drivers/staging/tty/ip2/ip2main.c new file mode 100644 index 000000000000..ea7a8fb08283 --- /dev/null +++ b/drivers/staging/tty/ip2/ip2main.c @@ -0,0 +1,3234 @@ +/* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Mainline code for the device driver +* +*******************************************************************************/ +// ToDo: +// +// Fix the immediate DSS_NOW problem. +// Work over the channel stats return logic in ip2_ipl_ioctl so they +// make sense for all 256 possible channels and so the user space +// utilities will compile and work properly. +// +// Done: +// +// 1.2.14 /\/\|=mhw=|\/\/ +// Added bounds checking to ip2_ipl_ioctl to avoid potential terroristic acts. +// Changed the definition of ip2trace to be more consistent with kernel style +// Thanks to Andreas Dilger for these updates +// +// 1.2.13 /\/\|=mhw=|\/\/ +// DEVFS: Renamed ttf/{n} to tts/F{n} and cuf/{n} to cua/F{n} to conform +// to agreed devfs serial device naming convention. +// +// 1.2.12 /\/\|=mhw=|\/\/ +// Cleaned up some remove queue cut and paste errors +// +// 1.2.11 /\/\|=mhw=|\/\/ +// Clean up potential NULL pointer dereferences +// Clean up devfs registration +// Add kernel command line parsing for io and irq +// Compile defaults for io and irq are now set in ip2.c not ip2.h! +// Reworked poll_only hack for explicit parameter setting +// You must now EXPLICITLY set poll_only = 1 or set all irqs to 0 +// Merged ip2_loadmain and old_ip2_init +// Converted all instances of interruptible_sleep_on into queue calls +// Most of these had no race conditions but better to clean up now +// +// 1.2.10 /\/\|=mhw=|\/\/ +// Fixed the bottom half interrupt handler and enabled USE_IQI +// to split the interrupt handler into a formal top-half / bottom-half +// Fixed timing window on high speed processors that queued messages to +// the outbound mail fifo faster than the board could handle. +// +// 1.2.9 +// Four box EX was barfing on >128k kmalloc, made structure smaller by +// reducing output buffer size +// +// 1.2.8 +// Device file system support (MHW) +// +// 1.2.7 +// Fixed +// Reload of ip2 without unloading ip2main hangs system on cat of /proc/modules +// +// 1.2.6 +//Fixes DCD problems +// DCD was not reported when CLOCAL was set on call to TIOCMGET +// +//Enhancements: +// TIOCMGET requests and waits for status return +// No DSS interrupts enabled except for DCD when needed +// +// For internal use only +// +//#define IP2DEBUG_INIT +//#define IP2DEBUG_OPEN +//#define IP2DEBUG_WRITE +//#define IP2DEBUG_READ +//#define IP2DEBUG_IOCTL +//#define IP2DEBUG_IPL + +//#define IP2DEBUG_TRACE +//#define DEBUG_FIFO + +/************/ +/* Includes */ +/************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include "ip2types.h" +#include "ip2trace.h" +#include "ip2ioctl.h" +#include "ip2.h" +#include "i2ellis.h" +#include "i2lib.h" + +/***************** + * /proc/ip2mem * + *****************/ + +#include +#include + +static DEFINE_MUTEX(ip2_mutex); +static const struct file_operations ip2mem_proc_fops; +static const struct file_operations ip2_proc_fops; + +/********************/ +/* Type Definitions */ +/********************/ + +/*************/ +/* Constants */ +/*************/ + +/* String constants to identify ourselves */ +static const char pcName[] = "Computone IntelliPort Plus multiport driver"; +static const char pcVersion[] = "1.2.14"; + +/* String constants for port names */ +static const char pcDriver_name[] = "ip2"; +static const char pcIpl[] = "ip2ipl"; + +/***********************/ +/* Function Prototypes */ +/***********************/ + +/* Global module entry functions */ + +/* Private (static) functions */ +static int ip2_open(PTTY, struct file *); +static void ip2_close(PTTY, struct file *); +static int ip2_write(PTTY, const unsigned char *, int); +static int ip2_putchar(PTTY, unsigned char); +static void ip2_flush_chars(PTTY); +static int ip2_write_room(PTTY); +static int ip2_chars_in_buf(PTTY); +static void ip2_flush_buffer(PTTY); +static int ip2_ioctl(PTTY, UINT, ULONG); +static void ip2_set_termios(PTTY, struct ktermios *); +static void ip2_set_line_discipline(PTTY); +static void ip2_throttle(PTTY); +static void ip2_unthrottle(PTTY); +static void ip2_stop(PTTY); +static void ip2_start(PTTY); +static void ip2_hangup(PTTY); +static int ip2_tiocmget(struct tty_struct *tty); +static int ip2_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear); +static int ip2_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount); + +static void set_irq(int, int); +static void ip2_interrupt_bh(struct work_struct *work); +static irqreturn_t ip2_interrupt(int irq, void *dev_id); +static void ip2_poll(unsigned long arg); +static inline void service_all_boards(void); +static void do_input(struct work_struct *); +static void do_status(struct work_struct *); + +static void ip2_wait_until_sent(PTTY,int); + +static void set_params (i2ChanStrPtr, struct ktermios *); +static int get_serial_info(i2ChanStrPtr, struct serial_struct __user *); +static int set_serial_info(i2ChanStrPtr, struct serial_struct __user *); + +static ssize_t ip2_ipl_read(struct file *, char __user *, size_t, loff_t *); +static ssize_t ip2_ipl_write(struct file *, const char __user *, size_t, loff_t *); +static long ip2_ipl_ioctl(struct file *, UINT, ULONG); +static int ip2_ipl_open(struct inode *, struct file *); + +static int DumpTraceBuffer(char __user *, int); +static int DumpFifoBuffer( char __user *, int); + +static void ip2_init_board(int, const struct firmware *); +static unsigned short find_eisa_board(int); +static int ip2_setup(char *str); + +/***************/ +/* Static Data */ +/***************/ + +static struct tty_driver *ip2_tty_driver; + +/* Here, then is a table of board pointers which the interrupt routine should + * scan through to determine who it must service. + */ +static unsigned short i2nBoards; // Number of boards here + +static i2eBordStrPtr i2BoardPtrTable[IP2_MAX_BOARDS]; + +static i2ChanStrPtr DevTable[IP2_MAX_PORTS]; +//DevTableMem just used to save addresses for kfree +static void *DevTableMem[IP2_MAX_BOARDS]; + +/* This is the driver descriptor for the ip2ipl device, which is used to + * download the loadware to the boards. + */ +static const struct file_operations ip2_ipl = { + .owner = THIS_MODULE, + .read = ip2_ipl_read, + .write = ip2_ipl_write, + .unlocked_ioctl = ip2_ipl_ioctl, + .open = ip2_ipl_open, + .llseek = noop_llseek, +}; + +static unsigned long irq_counter; +static unsigned long bh_counter; + +// Use immediate queue to service interrupts +#define USE_IQI +//#define USE_IQ // PCI&2.2 needs work + +/* The timer_list entry for our poll routine. If interrupt operation is not + * selected, the board is serviced periodically to see if anything needs doing. + */ +#define POLL_TIMEOUT (jiffies + 1) +static DEFINE_TIMER(PollTimer, ip2_poll, 0, 0); + +#ifdef IP2DEBUG_TRACE +/* Trace (debug) buffer data */ +#define TRACEMAX 1000 +static unsigned long tracebuf[TRACEMAX]; +static int tracestuff; +static int tracestrip; +static int tracewrap; +#endif + +/**********/ +/* Macros */ +/**********/ + +#ifdef IP2DEBUG_OPEN +#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] ttyc=%d, modc=%x -> %s\n", \ + tty->name,(pCh->flags), \ + tty->count,/*GET_USE_COUNT(module)*/0,s) +#else +#define DBG_CNT(s) +#endif + +/********/ +/* Code */ +/********/ + +#include "i2ellis.c" /* Extremely low-level interface services */ +#include "i2cmd.c" /* Standard loadware command definitions */ +#include "i2lib.c" /* High level interface services */ + +/* Configuration area for modprobe */ + +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +MODULE_LICENSE("GPL"); + +#define MAX_CMD_STR 50 + +static int poll_only; +static char cmd[MAX_CMD_STR]; + +static int Eisa_irq; +static int Eisa_slot; + +static int iindx; +static char rirqs[IP2_MAX_BOARDS]; +static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0}; + +/* Note: Add compiled in defaults to these arrays, not to the structure + in ip2.h any longer. That structure WILL get overridden + by these values, or command line values, or insmod values!!! =mhw= +*/ +static int io[IP2_MAX_BOARDS]; +static int irq[IP2_MAX_BOARDS] = { -1, -1, -1, -1 }; + +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +module_param_array(irq, int, NULL, 0); +MODULE_PARM_DESC(irq, "Interrupts for IntelliPort Cards"); +module_param_array(io, int, NULL, 0); +MODULE_PARM_DESC(io, "I/O ports for IntelliPort Cards"); +module_param(poll_only, bool, 0); +MODULE_PARM_DESC(poll_only, "Do not use card interrupts"); +module_param_string(ip2, cmd, MAX_CMD_STR, 0); +MODULE_PARM_DESC(ip2, "Contains module parameter passed with 'ip2='"); + +/* for sysfs class support */ +static struct class *ip2_class; + +/* Some functions to keep track of what irqs we have */ + +static int __init is_valid_irq(int irq) +{ + int *i = Valid_Irqs; + + while (*i != 0 && *i != irq) + i++; + + return *i; +} + +static void __init mark_requested_irq(char irq) +{ + rirqs[iindx++] = irq; +} + +static int __exit clear_requested_irq(char irq) +{ + int i; + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + if (rirqs[i] == irq) { + rirqs[i] = 0; + return 1; + } + } + return 0; +} + +static int have_requested_irq(char irq) +{ + /* array init to zeros so 0 irq will not be requested as a side + * effect */ + int i; + for (i = 0; i < IP2_MAX_BOARDS; ++i) + if (rirqs[i] == irq) + return 1; + return 0; +} + +/******************************************************************************/ +/* Function: cleanup_module() */ +/* Parameters: None */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This is a required entry point for an installable module. It has to return */ +/* the device and the driver to a passive state. It should not be necessary */ +/* to reset the board fully, especially as the loadware is downloaded */ +/* externally rather than in the driver. We just want to disable the board */ +/* and clear the loadware to a reset state. To allow this there has to be a */ +/* way to detect whether the board has the loadware running at init time to */ +/* handle subsequent installations of the driver. All memory allocated by the */ +/* driver should be returned since it may be unloaded from memory. */ +/******************************************************************************/ +static void __exit ip2_cleanup_module(void) +{ + int err; + int i; + + del_timer_sync(&PollTimer); + + /* Reset the boards we have. */ + for (i = 0; i < IP2_MAX_BOARDS; i++) + if (i2BoardPtrTable[i]) + iiReset(i2BoardPtrTable[i]); + + /* The following is done at most once, if any boards were installed. */ + for (i = 0; i < IP2_MAX_BOARDS; i++) { + if (i2BoardPtrTable[i]) { + iiResetDelay(i2BoardPtrTable[i]); + /* free io addresses and Tibet */ + release_region(ip2config.addr[i], 8); + device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i)); + device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, + 4 * i + 1)); + } + /* Disable and remove interrupt handler. */ + if (ip2config.irq[i] > 0 && + have_requested_irq(ip2config.irq[i])) { + free_irq(ip2config.irq[i], (void *)&pcName); + clear_requested_irq(ip2config.irq[i]); + } + } + class_destroy(ip2_class); + err = tty_unregister_driver(ip2_tty_driver); + if (err) + printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", + err); + put_tty_driver(ip2_tty_driver); + unregister_chrdev(IP2_IPL_MAJOR, pcIpl); + remove_proc_entry("ip2mem", NULL); + + /* free memory */ + for (i = 0; i < IP2_MAX_BOARDS; i++) { + void *pB; +#ifdef CONFIG_PCI + if (ip2config.type[i] == PCI && ip2config.pci_dev[i]) { + pci_disable_device(ip2config.pci_dev[i]); + pci_dev_put(ip2config.pci_dev[i]); + ip2config.pci_dev[i] = NULL; + } +#endif + pB = i2BoardPtrTable[i]; + if (pB != NULL) { + kfree(pB); + i2BoardPtrTable[i] = NULL; + } + if (DevTableMem[i] != NULL) { + kfree(DevTableMem[i]); + DevTableMem[i] = NULL; + } + } +} +module_exit(ip2_cleanup_module); + +static const struct tty_operations ip2_ops = { + .open = ip2_open, + .close = ip2_close, + .write = ip2_write, + .put_char = ip2_putchar, + .flush_chars = ip2_flush_chars, + .write_room = ip2_write_room, + .chars_in_buffer = ip2_chars_in_buf, + .flush_buffer = ip2_flush_buffer, + .ioctl = ip2_ioctl, + .throttle = ip2_throttle, + .unthrottle = ip2_unthrottle, + .set_termios = ip2_set_termios, + .set_ldisc = ip2_set_line_discipline, + .stop = ip2_stop, + .start = ip2_start, + .hangup = ip2_hangup, + .tiocmget = ip2_tiocmget, + .tiocmset = ip2_tiocmset, + .get_icount = ip2_get_icount, + .proc_fops = &ip2_proc_fops, +}; + +/******************************************************************************/ +/* Function: ip2_loadmain() */ +/* Parameters: irq, io from command line of insmod et. al. */ +/* pointer to fip firmware and firmware size for boards */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This was the required entry point for all drivers (now in ip2.c) */ +/* It performs all */ +/* initialisation of the devices and driver structures, and registers itself */ +/* with the relevant kernel modules. */ +/******************************************************************************/ +/* IRQF_DISABLED - if set blocks all interrupts else only this line */ +/* IRQF_SHARED - for shared irq PCI or maybe EISA only */ +/* SA_RANDOM - can be source for cert. random number generators */ +#define IP2_SA_FLAGS 0 + + +static const struct firmware *ip2_request_firmware(void) +{ + struct platform_device *pdev; + const struct firmware *fw; + + pdev = platform_device_register_simple("ip2", 0, NULL, 0); + if (IS_ERR(pdev)) { + printk(KERN_ERR "Failed to register platform device for ip2\n"); + return NULL; + } + if (request_firmware(&fw, "intelliport2.bin", &pdev->dev)) { + printk(KERN_ERR "Failed to load firmware 'intelliport2.bin'\n"); + fw = NULL; + } + platform_device_unregister(pdev); + return fw; +} + +/****************************************************************************** + * ip2_setup: + * str: kernel command line string + * + * Can't autoprobe the boards so user must specify configuration on + * kernel command line. Sane people build it modular but the others + * come here. + * + * Alternating pairs of io,irq for up to 4 boards. + * ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3 + * + * io=0 => No board + * io=1 => PCI + * io=2 => EISA + * else => ISA I/O address + * + * irq=0 or invalid for ISA will revert to polling mode + * + * Any value = -1, do not overwrite compiled in value. + * + ******************************************************************************/ +static int __init ip2_setup(char *str) +{ + int j, ints[10]; /* 4 boards, 2 parameters + 2 */ + unsigned int i; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + for (i = 0, j = 1; i < 4; i++) { + if (j > ints[0]) + break; + if (ints[j] >= 0) + io[i] = ints[j]; + j++; + if (j > ints[0]) + break; + if (ints[j] >= 0) + irq[i] = ints[j]; + j++; + } + return 1; +} +__setup("ip2=", ip2_setup); + +static int __init ip2_loadmain(void) +{ + int i, j, box; + int err = 0; + i2eBordStrPtr pB = NULL; + int rc = -1; + const struct firmware *fw = NULL; + char *str; + + str = cmd; + + if (poll_only) { + /* Hard lock the interrupts to zero */ + irq[0] = irq[1] = irq[2] = irq[3] = poll_only = 0; + } + + /* Check module parameter with 'ip2=' has been passed or not */ + if (!poll_only && (!strncmp(str, "ip2=", 4))) + ip2_setup(str); + + ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0); + + /* process command line arguments to modprobe or + insmod i.e. iop & irqp */ + /* irqp and iop should ALWAYS be specified now... But we check + them individually just to be sure, anyways... */ + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + ip2config.addr[i] = io[i]; + if (irq[i] >= 0) + ip2config.irq[i] = irq[i]; + else + ip2config.irq[i] = 0; + /* This is a little bit of a hack. If poll_only=1 on command + line back in ip2.c OR all IRQs on all specified boards are + explicitly set to 0, then drop to poll only mode and override + PCI or EISA interrupts. This superceeds the old hack of + triggering if all interrupts were zero (like da default). + Still a hack but less prone to random acts of terrorism. + + What we really should do, now that the IRQ default is set + to -1, is to use 0 as a hard coded, do not probe. + + /\/\|=mhw=|\/\/ + */ + poll_only |= irq[i]; + } + poll_only = !poll_only; + + /* Announce our presence */ + printk(KERN_INFO "%s version %s\n", pcName, pcVersion); + + ip2_tty_driver = alloc_tty_driver(IP2_MAX_PORTS); + if (!ip2_tty_driver) + return -ENOMEM; + + /* Initialise all the boards we can find (up to the maximum). */ + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + switch (ip2config.addr[i]) { + case 0: /* skip this slot even if card is present */ + break; + default: /* ISA */ + /* ISA address must be specified */ + if (ip2config.addr[i] < 0x100 || + ip2config.addr[i] > 0x3f8) { + printk(KERN_ERR "IP2: Bad ISA board %d " + "address %x\n", i, + ip2config.addr[i]); + ip2config.addr[i] = 0; + break; + } + ip2config.type[i] = ISA; + + /* Check for valid irq argument, set for polling if + * invalid */ + if (ip2config.irq[i] && + !is_valid_irq(ip2config.irq[i])) { + printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n", + ip2config.irq[i]); + /* 0 is polling and is valid in that sense */ + ip2config.irq[i] = 0; + } + break; + case PCI: +#ifdef CONFIG_PCI + { + struct pci_dev *pdev = NULL; + u32 addr; + int status; + + pdev = pci_get_device(PCI_VENDOR_ID_COMPUTONE, + PCI_DEVICE_ID_COMPUTONE_IP2EX, pdev); + if (pdev == NULL) { + ip2config.addr[i] = 0; + printk(KERN_ERR "IP2: PCI board %d not " + "found\n", i); + break; + } + + if (pci_enable_device(pdev)) { + dev_err(&pdev->dev, "can't enable device\n"); + goto out; + } + ip2config.type[i] = PCI; + ip2config.pci_dev[i] = pci_dev_get(pdev); + status = pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1, + &addr); + if (addr & 1) + ip2config.addr[i] = (USHORT)(addr & 0xfffe); + else + dev_err(&pdev->dev, "I/O address error\n"); + + ip2config.irq[i] = pdev->irq; +out: + pci_dev_put(pdev); + } +#else + printk(KERN_ERR "IP2: PCI card specified but PCI " + "support not enabled.\n"); + printk(KERN_ERR "IP2: Recompile kernel with CONFIG_PCI " + "defined!\n"); +#endif /* CONFIG_PCI */ + break; + case EISA: + ip2config.addr[i] = find_eisa_board(Eisa_slot + 1); + if (ip2config.addr[i] != 0) { + /* Eisa_irq set as side effect, boo */ + ip2config.type[i] = EISA; + } + ip2config.irq[i] = Eisa_irq; + break; + } /* switch */ + } /* for */ + + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + if (ip2config.addr[i]) { + pB = kzalloc(sizeof(i2eBordStr), GFP_KERNEL); + if (pB) { + i2BoardPtrTable[i] = pB; + iiSetAddress(pB, ip2config.addr[i], + ii2DelayTimer); + iiReset(pB); + } else + printk(KERN_ERR "IP2: board memory allocation " + "error\n"); + } + } + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + pB = i2BoardPtrTable[i]; + if (pB != NULL) { + iiResetDelay(pB); + break; + } + } + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + /* We don't want to request the firmware unless we have at + least one board */ + if (i2BoardPtrTable[i] != NULL) { + if (!fw) + fw = ip2_request_firmware(); + if (!fw) + break; + ip2_init_board(i, fw); + } + } + if (fw) + release_firmware(fw); + + ip2trace(ITRC_NO_PORT, ITRC_INIT, 2, 0); + + ip2_tty_driver->owner = THIS_MODULE; + ip2_tty_driver->name = "ttyF"; + ip2_tty_driver->driver_name = pcDriver_name; + ip2_tty_driver->major = IP2_TTY_MAJOR; + ip2_tty_driver->minor_start = 0; + ip2_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + ip2_tty_driver->subtype = SERIAL_TYPE_NORMAL; + ip2_tty_driver->init_termios = tty_std_termios; + ip2_tty_driver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; + ip2_tty_driver->flags = TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(ip2_tty_driver, &ip2_ops); + + ip2trace(ITRC_NO_PORT, ITRC_INIT, 3, 0); + + err = tty_register_driver(ip2_tty_driver); + if (err) { + printk(KERN_ERR "IP2: failed to register tty driver\n"); + put_tty_driver(ip2_tty_driver); + return err; /* leaking resources */ + } + + err = register_chrdev(IP2_IPL_MAJOR, pcIpl, &ip2_ipl); + if (err) { + printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", + err); + } else { + /* create the sysfs class */ + ip2_class = class_create(THIS_MODULE, "ip2"); + if (IS_ERR(ip2_class)) { + err = PTR_ERR(ip2_class); + goto out_chrdev; + } + } + /* Register the read_procmem thing */ + if (!proc_create("ip2mem",0,NULL,&ip2mem_proc_fops)) { + printk(KERN_ERR "IP2: failed to register read_procmem\n"); + return -EIO; /* leaking resources */ + } + + ip2trace(ITRC_NO_PORT, ITRC_INIT, 4, 0); + /* Register the interrupt handler or poll handler, depending upon the + * specified interrupt. + */ + + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + if (ip2config.addr[i] == 0) + continue; + + pB = i2BoardPtrTable[i]; + if (pB != NULL) { + device_create(ip2_class, NULL, + MKDEV(IP2_IPL_MAJOR, 4 * i), + NULL, "ipl%d", i); + device_create(ip2_class, NULL, + MKDEV(IP2_IPL_MAJOR, 4 * i + 1), + NULL, "stat%d", i); + + for (box = 0; box < ABS_MAX_BOXES; box++) + for (j = 0; j < ABS_BIGGEST_BOX; j++) + if (pB->i2eChannelMap[box] & (1 << j)) + tty_register_device( + ip2_tty_driver, + j + ABS_BIGGEST_BOX * + (box+i*ABS_MAX_BOXES), + NULL); + } + + if (poll_only) { + /* Poll only forces driver to only use polling and + to ignore the probed PCI or EISA interrupts. */ + ip2config.irq[i] = CIR_POLL; + } + if (ip2config.irq[i] == CIR_POLL) { +retry: + if (!timer_pending(&PollTimer)) { + mod_timer(&PollTimer, POLL_TIMEOUT); + printk(KERN_INFO "IP2: polling\n"); + } + } else { + if (have_requested_irq(ip2config.irq[i])) + continue; + rc = request_irq(ip2config.irq[i], ip2_interrupt, + IP2_SA_FLAGS | + (ip2config.type[i] == PCI ? IRQF_SHARED : 0), + pcName, i2BoardPtrTable[i]); + if (rc) { + printk(KERN_ERR "IP2: request_irq failed: " + "error %d\n", rc); + ip2config.irq[i] = CIR_POLL; + printk(KERN_INFO "IP2: Polling %ld/sec.\n", + (POLL_TIMEOUT - jiffies)); + goto retry; + } + mark_requested_irq(ip2config.irq[i]); + /* Initialise the interrupt handler bottom half + * (aka slih). */ + } + } + + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + if (i2BoardPtrTable[i]) { + /* set and enable board interrupt */ + set_irq(i, ip2config.irq[i]); + } + } + + ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0); + + return 0; + +out_chrdev: + unregister_chrdev(IP2_IPL_MAJOR, "ip2"); + /* unregister and put tty here */ + return err; +} +module_init(ip2_loadmain); + +/******************************************************************************/ +/* Function: ip2_init_board() */ +/* Parameters: Index of board in configuration structure */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This function initializes the specified board. The loadware is copied to */ +/* the board, the channel structures are initialized, and the board details */ +/* are reported on the console. */ +/******************************************************************************/ +static void +ip2_init_board(int boardnum, const struct firmware *fw) +{ + int i; + int nports = 0, nboxes = 0; + i2ChanStrPtr pCh; + i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; + + if ( !iiInitialize ( pB ) ) { + printk ( KERN_ERR "IP2: Failed to initialize board at 0x%x, error %d\n", + pB->i2eBase, pB->i2eError ); + goto err_initialize; + } + printk(KERN_INFO "IP2: Board %d: addr=0x%x irq=%d\n", boardnum + 1, + ip2config.addr[boardnum], ip2config.irq[boardnum] ); + + if (!request_region( ip2config.addr[boardnum], 8, pcName )) { + printk(KERN_ERR "IP2: bad addr=0x%x\n", ip2config.addr[boardnum]); + goto err_initialize; + } + + if ( iiDownloadAll ( pB, (loadHdrStrPtr)fw->data, 1, fw->size ) + != II_DOWN_GOOD ) { + printk ( KERN_ERR "IP2: failed to download loadware\n" ); + goto err_release_region; + } else { + printk ( KERN_INFO "IP2: fv=%d.%d.%d lv=%d.%d.%d\n", + pB->i2ePom.e.porVersion, + pB->i2ePom.e.porRevision, + pB->i2ePom.e.porSubRev, pB->i2eLVersion, + pB->i2eLRevision, pB->i2eLSub ); + } + + switch ( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) { + + default: + printk( KERN_ERR "IP2: Unknown board type, ID = %x\n", + pB->i2ePom.e.porID ); + nports = 0; + goto err_release_region; + break; + + case POR_ID_II_4: /* IntelliPort-II, ISA-4 (4xRJ45) */ + printk ( KERN_INFO "IP2: ISA-4\n" ); + nports = 4; + break; + + case POR_ID_II_8: /* IntelliPort-II, 8-port using standard brick. */ + printk ( KERN_INFO "IP2: ISA-8 std\n" ); + nports = 8; + break; + + case POR_ID_II_8R: /* IntelliPort-II, 8-port using RJ11's (no CTS) */ + printk ( KERN_INFO "IP2: ISA-8 RJ11\n" ); + nports = 8; + break; + + case POR_ID_FIIEX: /* IntelliPort IIEX */ + { + int portnum = IP2_PORTS_PER_BOARD * boardnum; + int box; + + for( box = 0; box < ABS_MAX_BOXES; ++box ) { + if ( pB->i2eChannelMap[box] != 0 ) { + ++nboxes; + } + for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { + if ( pB->i2eChannelMap[box] & 1<< i ) { + ++nports; + } + } + } + DevTableMem[boardnum] = pCh = + kmalloc( sizeof(i2ChanStr) * nports, GFP_KERNEL ); + if ( !pCh ) { + printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n"); + goto err_release_region; + } + if ( !i2InitChannels( pB, nports, pCh ) ) { + printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError); + kfree ( pCh ); + goto err_release_region; + } + pB->i2eChannelPtr = &DevTable[portnum]; + pB->i2eChannelCnt = ABS_MOST_PORTS; + + for( box = 0; box < ABS_MAX_BOXES; ++box, portnum += ABS_BIGGEST_BOX ) { + for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { + if ( pB->i2eChannelMap[box] & (1 << i) ) { + DevTable[portnum + i] = pCh; + pCh->port_index = portnum + i; + pCh++; + } + } + } + printk(KERN_INFO "IP2: EX box=%d ports=%d %d bit\n", + nboxes, nports, pB->i2eDataWidth16 ? 16 : 8 ); + } + goto ex_exit; + } + DevTableMem[boardnum] = pCh = + kmalloc ( sizeof (i2ChanStr) * nports, GFP_KERNEL ); + if ( !pCh ) { + printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n"); + goto err_release_region; + } + pB->i2eChannelPtr = pCh; + pB->i2eChannelCnt = nports; + if ( !i2InitChannels( pB, nports, pCh ) ) { + printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError); + kfree ( pCh ); + goto err_release_region; + } + pB->i2eChannelPtr = &DevTable[IP2_PORTS_PER_BOARD * boardnum]; + + for( i = 0; i < pB->i2eChannelCnt; ++i ) { + DevTable[IP2_PORTS_PER_BOARD * boardnum + i] = pCh; + pCh->port_index = (IP2_PORTS_PER_BOARD * boardnum) + i; + pCh++; + } +ex_exit: + INIT_WORK(&pB->tqueue_interrupt, ip2_interrupt_bh); + return; + +err_release_region: + release_region(ip2config.addr[boardnum], 8); +err_initialize: + kfree ( pB ); + i2BoardPtrTable[boardnum] = NULL; + return; +} + +/******************************************************************************/ +/* Function: find_eisa_board ( int start_slot ) */ +/* Parameters: First slot to check */ +/* Returns: Address of EISA IntelliPort II controller */ +/* */ +/* Description: */ +/* This function searches for an EISA IntelliPort controller, starting */ +/* from the specified slot number. If the motherboard is not identified as an */ +/* EISA motherboard, or no valid board ID is selected it returns 0. Otherwise */ +/* it returns the base address of the controller. */ +/******************************************************************************/ +static unsigned short +find_eisa_board( int start_slot ) +{ + int i, j; + unsigned int idm = 0; + unsigned int idp = 0; + unsigned int base = 0; + unsigned int value; + int setup_address; + int setup_irq; + int ismine = 0; + + /* + * First a check for an EISA motherboard, which we do by comparing the + * EISA ID registers for the system board and the first couple of slots. + * No slot ID should match the system board ID, but on an ISA or PCI + * machine the odds are that an empty bus will return similar values for + * each slot. + */ + i = 0x0c80; + value = (inb(i) << 24) + (inb(i+1) << 16) + (inb(i+2) << 8) + inb(i+3); + for( i = 0x1c80; i <= 0x4c80; i += 0x1000 ) { + j = (inb(i)<<24)+(inb(i+1)<<16)+(inb(i+2)<<8)+inb(i+3); + if ( value == j ) + return 0; + } + + /* + * OK, so we are inclined to believe that this is an EISA machine. Find + * an IntelliPort controller. + */ + for( i = start_slot; i < 16; i++ ) { + base = i << 12; + idm = (inb(base + 0xc80) << 8) | (inb(base + 0xc81) & 0xff); + idp = (inb(base + 0xc82) << 8) | (inb(base + 0xc83) & 0xff); + ismine = 0; + if ( idm == 0x0e8e ) { + if ( idp == 0x0281 || idp == 0x0218 ) { + ismine = 1; + } else if ( idp == 0x0282 || idp == 0x0283 ) { + ismine = 3; /* Can do edge-trigger */ + } + if ( ismine ) { + Eisa_slot = i; + break; + } + } + } + if ( !ismine ) + return 0; + + /* It's some sort of EISA card, but at what address is it configured? */ + + setup_address = base + 0xc88; + value = inb(base + 0xc86); + setup_irq = (value & 8) ? Valid_Irqs[value & 7] : 0; + + if ( (ismine & 2) && !(value & 0x10) ) { + ismine = 1; /* Could be edging, but not */ + } + + if ( Eisa_irq == 0 ) { + Eisa_irq = setup_irq; + } else if ( Eisa_irq != setup_irq ) { + printk ( KERN_ERR "IP2: EISA irq mismatch between EISA controllers\n" ); + } + +#ifdef IP2DEBUG_INIT +printk(KERN_DEBUG "Computone EISA board in slot %d, I.D. 0x%x%x, Address 0x%x", + base >> 12, idm, idp, setup_address); + if ( Eisa_irq ) { + printk(KERN_DEBUG ", Interrupt %d %s\n", + setup_irq, (ismine & 2) ? "(edge)" : "(level)"); + } else { + printk(KERN_DEBUG ", (polled)\n"); + } +#endif + return setup_address; +} + +/******************************************************************************/ +/* Function: set_irq() */ +/* Parameters: index to board in board table */ +/* IRQ to use */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/******************************************************************************/ +static void +set_irq( int boardnum, int boardIrq ) +{ + unsigned char tempCommand[16]; + i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; + unsigned long flags; + + /* + * Notify the boards they may generate interrupts. This is done by + * sending an in-line command to channel 0 on each board. This is why + * the channels have to be defined already. For each board, if the + * interrupt has never been defined, we must do so NOW, directly, since + * board will not send flow control or even give an interrupt until this + * is done. If polling we must send 0 as the interrupt parameter. + */ + + // We will get an interrupt here at the end of this function + + iiDisableMailIrq(pB); + + /* We build up the entire packet header. */ + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_INLINE; + CMD_COUNT_OF(tempCommand) = 2; + (CMD_OF(tempCommand))[0] = CMDVALUE_IRQ; + (CMD_OF(tempCommand))[1] = boardIrq; + /* + * Write to FIFO; don't bother to adjust fifo capacity for this, since + * board will respond almost immediately after SendMail hit. + */ + write_lock_irqsave(&pB->write_fifo_spinlock, flags); + iiWriteBuf(pB, tempCommand, 4); + write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); + pB->i2eUsingIrq = boardIrq; + pB->i2eOutMailWaiting |= MB_OUT_STUFFED; + + /* Need to update number of boards before you enable mailbox int */ + ++i2nBoards; + + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 6; + (CMD_OF(tempCommand))[0] = 88; // SILO + (CMD_OF(tempCommand))[1] = 64; // chars + (CMD_OF(tempCommand))[2] = 32; // ms + + (CMD_OF(tempCommand))[3] = 28; // MAX_BLOCK + (CMD_OF(tempCommand))[4] = 64; // chars + + (CMD_OF(tempCommand))[5] = 87; // HW_TEST + write_lock_irqsave(&pB->write_fifo_spinlock, flags); + iiWriteBuf(pB, tempCommand, 8); + write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); + + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 1; + (CMD_OF(tempCommand))[0] = 84; /* get BOX_IDS */ + iiWriteBuf(pB, tempCommand, 3); + +#ifdef XXX + // enable heartbeat for test porpoises + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 2; + (CMD_OF(tempCommand))[0] = 44; /* get ping */ + (CMD_OF(tempCommand))[1] = 200; /* 200 ms */ + write_lock_irqsave(&pB->write_fifo_spinlock, flags); + iiWriteBuf(pB, tempCommand, 4); + write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); +#endif + + iiEnableMailIrq(pB); + iiSendPendingMail(pB); +} + +/******************************************************************************/ +/* Interrupt Handler Section */ +/******************************************************************************/ + +static inline void +service_all_boards(void) +{ + int i; + i2eBordStrPtr pB; + + /* Service every board on the list */ + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + pB = i2BoardPtrTable[i]; + if ( pB ) { + i2ServiceBoard( pB ); + } + } +} + + +/******************************************************************************/ +/* Function: ip2_interrupt_bh(work) */ +/* Parameters: work - pointer to the board structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* Service the board in a bottom half interrupt handler and then */ +/* reenable the board's interrupts if it has an IRQ number */ +/* */ +/******************************************************************************/ +static void +ip2_interrupt_bh(struct work_struct *work) +{ + i2eBordStrPtr pB = container_of(work, i2eBordStr, tqueue_interrupt); +// pB better well be set or we have a problem! We can only get +// here from the IMMEDIATE queue. Here, we process the boards. +// Checking pB doesn't cost much and it saves us from the sanity checkers. + + bh_counter++; + + if ( pB ) { + i2ServiceBoard( pB ); + if( pB->i2eUsingIrq ) { +// Re-enable his interrupts + iiEnableMailIrq(pB); + } + } +} + + +/******************************************************************************/ +/* Function: ip2_interrupt(int irq, void *dev_id) */ +/* Parameters: irq - interrupt number */ +/* pointer to optional device ID structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* Our task here is simply to identify each board which needs servicing. */ +/* If we are queuing then, queue it to be serviced, and disable its irq */ +/* mask otherwise process the board directly. */ +/* */ +/* We could queue by IRQ but that just complicates things on both ends */ +/* with very little gain in performance (how many instructions does */ +/* it take to iterate on the immediate queue). */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_irq_work(i2eBordStrPtr pB) +{ +#ifdef USE_IQI + if (NO_MAIL_HERE != ( pB->i2eStartMail = iiGetMail(pB))) { +// Disable his interrupt (will be enabled when serviced) +// This is mostly to protect from reentrancy. + iiDisableMailIrq(pB); + +// Park the board on the immediate queue for processing. + schedule_work(&pB->tqueue_interrupt); + +// Make sure the immediate queue is flagged to fire. + } +#else + +// We are using immediate servicing here. This sucks and can +// cause all sorts of havoc with ppp and others. The failsafe +// check on iiSendPendingMail could also throw a hairball. + + i2ServiceBoard( pB ); + +#endif /* USE_IQI */ +} + +static void +ip2_polled_interrupt(void) +{ + int i; + i2eBordStrPtr pB; + + ip2trace(ITRC_NO_PORT, ITRC_INTR, 99, 1, 0); + + /* Service just the boards on the list using this irq */ + for( i = 0; i < i2nBoards; ++i ) { + pB = i2BoardPtrTable[i]; + +// Only process those boards which match our IRQ. +// IRQ = 0 for polled boards, we won't poll "IRQ" boards + + if (pB && pB->i2eUsingIrq == 0) + ip2_irq_work(pB); + } + + ++irq_counter; + + ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); +} + +static irqreturn_t +ip2_interrupt(int irq, void *dev_id) +{ + i2eBordStrPtr pB = dev_id; + + ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, pB->i2eUsingIrq ); + + ip2_irq_work(pB); + + ++irq_counter; + + ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); + return IRQ_HANDLED; +} + +/******************************************************************************/ +/* Function: ip2_poll(unsigned long arg) */ +/* Parameters: ? */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function calls the library routine i2ServiceBoard for each board in */ +/* the board table. This is used instead of the interrupt routine when polled */ +/* mode is specified. */ +/******************************************************************************/ +static void +ip2_poll(unsigned long arg) +{ + ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 ); + + // Just polled boards, IRQ = 0 will hit all non-interrupt boards. + // It will NOT poll boards handled by hard interrupts. + // The issue of queued BH interrupts is handled in ip2_interrupt(). + ip2_polled_interrupt(); + + mod_timer(&PollTimer, POLL_TIMEOUT); + + ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); +} + +static void do_input(struct work_struct *work) +{ + i2ChanStrPtr pCh = container_of(work, i2ChanStr, tqueue_input); + unsigned long flags; + + ip2trace(CHANN, ITRC_INPUT, 21, 0 ); + + // Data input + if ( pCh->pTTY != NULL ) { + read_lock_irqsave(&pCh->Ibuf_spinlock, flags); + if (!pCh->throttled && (pCh->Ibuf_stuff != pCh->Ibuf_strip)) { + read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); + i2Input( pCh ); + } else + read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); + } else { + ip2trace(CHANN, ITRC_INPUT, 22, 0 ); + + i2InputFlush( pCh ); + } +} + +// code duplicated from n_tty (ldisc) +static inline void isig(int sig, struct tty_struct *tty, int flush) +{ + /* FIXME: This is completely bogus */ + if (tty->pgrp) + kill_pgrp(tty->pgrp, sig, 1); + if (flush || !L_NOFLSH(tty)) { + if ( tty->ldisc->ops->flush_buffer ) + tty->ldisc->ops->flush_buffer(tty); + i2InputFlush( tty->driver_data ); + } +} + +static void do_status(struct work_struct *work) +{ + i2ChanStrPtr pCh = container_of(work, i2ChanStr, tqueue_status); + int status; + + status = i2GetStatus( pCh, (I2_BRK|I2_PAR|I2_FRA|I2_OVR) ); + + ip2trace (CHANN, ITRC_STATUS, 21, 1, status ); + + if (pCh->pTTY && (status & (I2_BRK|I2_PAR|I2_FRA|I2_OVR)) ) { + if ( (status & I2_BRK) ) { + // code duplicated from n_tty (ldisc) + if (I_IGNBRK(pCh->pTTY)) + goto skip_this; + if (I_BRKINT(pCh->pTTY)) { + isig(SIGINT, pCh->pTTY, 1); + goto skip_this; + } + wake_up_interruptible(&pCh->pTTY->read_wait); + } +#ifdef NEVER_HAPPENS_AS_SETUP_XXX + // and can't work because we don't know the_char + // as the_char is reported on a separate path + // The intelligent board does this stuff as setup + { + char brkf = TTY_NORMAL; + unsigned char brkc = '\0'; + unsigned char tmp; + if ( (status & I2_BRK) ) { + brkf = TTY_BREAK; + brkc = '\0'; + } + else if (status & I2_PAR) { + brkf = TTY_PARITY; + brkc = the_char; + } else if (status & I2_FRA) { + brkf = TTY_FRAME; + brkc = the_char; + } else if (status & I2_OVR) { + brkf = TTY_OVERRUN; + brkc = the_char; + } + tmp = pCh->pTTY->real_raw; + pCh->pTTY->real_raw = 0; + pCh->pTTY->ldisc->ops.receive_buf( pCh->pTTY, &brkc, &brkf, 1 ); + pCh->pTTY->real_raw = tmp; + } +#endif /* NEVER_HAPPENS_AS_SETUP_XXX */ + } +skip_this: + + if ( status & (I2_DDCD | I2_DDSR | I2_DCTS | I2_DRI) ) { + wake_up_interruptible(&pCh->delta_msr_wait); + + if ( (pCh->flags & ASYNC_CHECK_CD) && (status & I2_DDCD) ) { + if ( status & I2_DCD ) { + if ( pCh->wopen ) { + wake_up_interruptible ( &pCh->open_wait ); + } + } else { + if (pCh->pTTY && (!(pCh->pTTY->termios->c_cflag & CLOCAL)) ) { + tty_hangup( pCh->pTTY ); + } + } + } + } + + ip2trace (CHANN, ITRC_STATUS, 26, 0 ); +} + +/******************************************************************************/ +/* Device Open/Close/Ioctl Entry Point Section */ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: open_sanity_check() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* Verifies the structure magic numbers and cross links. */ +/******************************************************************************/ +#ifdef IP2DEBUG_OPEN +static void +open_sanity_check( i2ChanStrPtr pCh, i2eBordStrPtr pBrd ) +{ + if ( pBrd->i2eValid != I2E_MAGIC ) { + printk(KERN_ERR "IP2: invalid board structure\n" ); + } else if ( pBrd != pCh->pMyBord ) { + printk(KERN_ERR "IP2: board structure pointer mismatch (%p)\n", + pCh->pMyBord ); + } else if ( pBrd->i2eChannelCnt < pCh->port_index ) { + printk(KERN_ERR "IP2: bad device index (%d)\n", pCh->port_index ); + } else if (&((i2ChanStrPtr)pBrd->i2eChannelPtr)[pCh->port_index] != pCh) { + } else { + printk(KERN_INFO "IP2: all pointers check out!\n" ); + } +} +#endif + + +/******************************************************************************/ +/* Function: ip2_open() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: (MANDATORY) */ +/* A successful device open has to run a gauntlet of checks before it */ +/* completes. After some sanity checking and pointer setup, the function */ +/* blocks until all conditions are satisfied. It then initialises the port to */ +/* the default characteristics and returns. */ +/******************************************************************************/ +static int +ip2_open( PTTY tty, struct file *pFile ) +{ + wait_queue_t wait; + int rc = 0; + int do_clocal = 0; + i2ChanStrPtr pCh = DevTable[tty->index]; + + ip2trace (tty->index, ITRC_OPEN, ITRC_ENTER, 0 ); + + if ( pCh == NULL ) { + return -ENODEV; + } + /* Setup pointer links in device and tty structures */ + pCh->pTTY = tty; + tty->driver_data = pCh; + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG \ + "IP2:open(tty=%p,pFile=%p):dev=%s,ch=%d,idx=%d\n", + tty, pFile, tty->name, pCh->infl.hd.i2sChannel, pCh->port_index); + open_sanity_check ( pCh, pCh->pMyBord ); +#endif + + i2QueueCommands(PTYPE_INLINE, pCh, 100, 3, CMD_DTRUP,CMD_RTSUP,CMD_DCD_REP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + serviceOutgoingFifo( pCh->pMyBord ); + + /* Block here until the port is ready (per serial and istallion) */ + /* + * 1. If the port is in the middle of closing wait for the completion + * and then return the appropriate error. + */ + init_waitqueue_entry(&wait, current); + add_wait_queue(&pCh->close_wait, &wait); + set_current_state( TASK_INTERRUPTIBLE ); + + if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) { + if ( pCh->flags & ASYNC_CLOSING ) { + tty_unlock(); + schedule(); + tty_lock(); + } + if ( tty_hung_up_p(pFile) ) { + set_current_state( TASK_RUNNING ); + remove_wait_queue(&pCh->close_wait, &wait); + return( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS; + } + } + set_current_state( TASK_RUNNING ); + remove_wait_queue(&pCh->close_wait, &wait); + + /* + * 3. Handle a non-blocking open of a normal port. + */ + if ( (pFile->f_flags & O_NONBLOCK) || (tty->flags & (1<flags |= ASYNC_NORMAL_ACTIVE; + goto noblock; + } + /* + * 4. Now loop waiting for the port to be free and carrier present + * (if required). + */ + if ( tty->termios->c_cflag & CLOCAL ) + do_clocal = 1; + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "OpenBlock: do_clocal = %d\n", do_clocal); +#endif + + ++pCh->wopen; + + init_waitqueue_entry(&wait, current); + add_wait_queue(&pCh->open_wait, &wait); + + for(;;) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + set_current_state( TASK_INTERRUPTIBLE ); + serviceOutgoingFifo( pCh->pMyBord ); + if ( tty_hung_up_p(pFile) ) { + set_current_state( TASK_RUNNING ); + remove_wait_queue(&pCh->open_wait, &wait); + return ( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EBUSY : -ERESTARTSYS; + } + if (!(pCh->flags & ASYNC_CLOSING) && + (do_clocal || (pCh->dataSetIn & I2_DCD) )) { + rc = 0; + break; + } + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "ASYNC_CLOSING = %s\n", + (pCh->flags & ASYNC_CLOSING)?"True":"False"); + printk(KERN_DEBUG "OpenBlock: waiting for CD or signal\n"); +#endif + ip2trace (CHANN, ITRC_OPEN, 3, 2, 0, + (pCh->flags & ASYNC_CLOSING) ); + /* check for signal */ + if (signal_pending(current)) { + rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS); + break; + } + tty_unlock(); + schedule(); + tty_lock(); + } + set_current_state( TASK_RUNNING ); + remove_wait_queue(&pCh->open_wait, &wait); + + --pCh->wopen; //why count? + + ip2trace (CHANN, ITRC_OPEN, 4, 0 ); + + if (rc != 0 ) { + return rc; + } + pCh->flags |= ASYNC_NORMAL_ACTIVE; + +noblock: + + /* first open - Assign termios structure to port */ + if ( tty->count == 1 ) { + i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + /* Now we must send the termios settings to the loadware */ + set_params( pCh, NULL ); + } + + /* + * Now set any i2lib options. These may go away if the i2lib code ends + * up rolled into the mainline. + */ + pCh->channelOptions |= CO_NBLOCK_WRITE; + +#ifdef IP2DEBUG_OPEN + printk (KERN_DEBUG "IP2: open completed\n" ); +#endif + serviceOutgoingFifo( pCh->pMyBord ); + + ip2trace (CHANN, ITRC_OPEN, ITRC_RETURN, 0 ); + + return 0; +} + +/******************************************************************************/ +/* Function: ip2_close() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_close( PTTY tty, struct file *pFile ) +{ + i2ChanStrPtr pCh = tty->driver_data; + + if ( !pCh ) { + return; + } + + ip2trace (CHANN, ITRC_CLOSE, ITRC_ENTER, 0 ); + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "IP2:close %s:\n",tty->name); +#endif + + if ( tty_hung_up_p ( pFile ) ) { + + ip2trace (CHANN, ITRC_CLOSE, 2, 1, 2 ); + + return; + } + if ( tty->count > 1 ) { /* not the last close */ + + ip2trace (CHANN, ITRC_CLOSE, 2, 1, 3 ); + + return; + } + pCh->flags |= ASYNC_CLOSING; // last close actually + + tty->closing = 1; + + if (pCh->ClosingWaitTime != ASYNC_CLOSING_WAIT_NONE) { + /* + * Before we drop DTR, make sure the transmitter has completely drained. + * This uses an timeout, after which the close + * completes. + */ + ip2_wait_until_sent(tty, pCh->ClosingWaitTime ); + } + /* + * At this point we stop accepting input. Here we flush the channel + * input buffer which will allow the board to send up more data. Any + * additional input is tossed at interrupt/poll time. + */ + i2InputFlush( pCh ); + + /* disable DSS reporting */ + i2QueueCommands(PTYPE_INLINE, pCh, 100, 4, + CMD_DCD_NREP, CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); + if (tty->termios->c_cflag & HUPCL) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + } + + serviceOutgoingFifo ( pCh->pMyBord ); + + tty_ldisc_flush(tty); + tty_driver_flush_buffer(tty); + tty->closing = 0; + + pCh->pTTY = NULL; + + if (pCh->wopen) { + if (pCh->ClosingDelay) { + msleep_interruptible(jiffies_to_msecs(pCh->ClosingDelay)); + } + wake_up_interruptible(&pCh->open_wait); + } + + pCh->flags &=~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&pCh->close_wait); + +#ifdef IP2DEBUG_OPEN + DBG_CNT("ip2_close: after wakeups--"); +#endif + + + ip2trace (CHANN, ITRC_CLOSE, ITRC_RETURN, 1, 1 ); + + return; +} + +/******************************************************************************/ +/* Function: ip2_hangup() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_hangup ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + + if( !pCh ) { + return; + } + + ip2trace (CHANN, ITRC_HANGUP, ITRC_ENTER, 0 ); + + ip2_flush_buffer(tty); + + /* disable DSS reporting */ + + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_DCD_NREP); + i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + if ( (tty->termios->c_cflag & HUPCL) ) { + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + } + i2QueueCommands(PTYPE_INLINE, pCh, 1, 3, + CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); + serviceOutgoingFifo ( pCh->pMyBord ); + + wake_up_interruptible ( &pCh->delta_msr_wait ); + + pCh->flags &= ~ASYNC_NORMAL_ACTIVE; + pCh->pTTY = NULL; + wake_up_interruptible ( &pCh->open_wait ); + + ip2trace (CHANN, ITRC_HANGUP, ITRC_RETURN, 0 ); +} + +/******************************************************************************/ +/******************************************************************************/ +/* Device Output Section */ +/******************************************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_write() */ +/* Parameters: Pointer to tty structure */ +/* Flag denoting data is in user (1) or kernel (0) space */ +/* Pointer to data */ +/* Number of bytes to write */ +/* Returns: Number of bytes actually written */ +/* */ +/* Description: (MANDATORY) */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_write( PTTY tty, const unsigned char *pData, int count) +{ + i2ChanStrPtr pCh = tty->driver_data; + int bytesSent = 0; + unsigned long flags; + + ip2trace (CHANN, ITRC_WRITE, ITRC_ENTER, 2, count, -1 ); + + /* Flush out any buffered data left over from ip2_putchar() calls. */ + ip2_flush_chars( tty ); + + /* This is the actual move bit. Make sure it does what we need!!!!! */ + write_lock_irqsave(&pCh->Pbuf_spinlock, flags); + bytesSent = i2Output( pCh, pData, count); + write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); + + ip2trace (CHANN, ITRC_WRITE, ITRC_RETURN, 1, bytesSent ); + + return bytesSent > 0 ? bytesSent : 0; +} + +/******************************************************************************/ +/* Function: ip2_putchar() */ +/* Parameters: Pointer to tty structure */ +/* Character to write */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_putchar( PTTY tty, unsigned char ch ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + +// ip2trace (CHANN, ITRC_PUTC, ITRC_ENTER, 1, ch ); + + write_lock_irqsave(&pCh->Pbuf_spinlock, flags); + pCh->Pbuf[pCh->Pbuf_stuff++] = ch; + if ( pCh->Pbuf_stuff == sizeof pCh->Pbuf ) { + write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); + ip2_flush_chars( tty ); + } else + write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); + return 1; + +// ip2trace (CHANN, ITRC_PUTC, ITRC_RETURN, 1, ch ); +} + +/******************************************************************************/ +/* Function: ip2_flush_chars() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/******************************************************************************/ +static void +ip2_flush_chars( PTTY tty ) +{ + int strip; + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + + write_lock_irqsave(&pCh->Pbuf_spinlock, flags); + if ( pCh->Pbuf_stuff ) { + +// ip2trace (CHANN, ITRC_PUTC, 10, 1, strip ); + + // + // We may need to restart i2Output if it does not fullfill this request + // + strip = i2Output( pCh, pCh->Pbuf, pCh->Pbuf_stuff); + if ( strip != pCh->Pbuf_stuff ) { + memmove( pCh->Pbuf, &pCh->Pbuf[strip], pCh->Pbuf_stuff - strip ); + } + pCh->Pbuf_stuff -= strip; + } + write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); +} + +/******************************************************************************/ +/* Function: ip2_write_room() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Number of bytes that the driver can accept */ +/* */ +/* Description: */ +/* */ +/******************************************************************************/ +static int +ip2_write_room ( PTTY tty ) +{ + int bytesFree; + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + + read_lock_irqsave(&pCh->Pbuf_spinlock, flags); + bytesFree = i2OutputFree( pCh ) - pCh->Pbuf_stuff; + read_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); + + ip2trace (CHANN, ITRC_WRITE, 11, 1, bytesFree ); + + return ((bytesFree > 0) ? bytesFree : 0); +} + +/******************************************************************************/ +/* Function: ip2_chars_in_buf() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Number of bytes queued for transmission */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_chars_in_buf ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + int rc; + unsigned long flags; + + ip2trace (CHANN, ITRC_WRITE, 12, 1, pCh->Obuf_char_count + pCh->Pbuf_stuff ); + +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: chars in buffer = %d (%d,%d)\n", + pCh->Obuf_char_count + pCh->Pbuf_stuff, + pCh->Obuf_char_count, pCh->Pbuf_stuff ); +#endif + read_lock_irqsave(&pCh->Obuf_spinlock, flags); + rc = pCh->Obuf_char_count; + read_unlock_irqrestore(&pCh->Obuf_spinlock, flags); + read_lock_irqsave(&pCh->Pbuf_spinlock, flags); + rc += pCh->Pbuf_stuff; + read_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); + return rc; +} + +/******************************************************************************/ +/* Function: ip2_flush_buffer() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_flush_buffer( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + + ip2trace (CHANN, ITRC_FLUSH, ITRC_ENTER, 0 ); + +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: flush buffer\n" ); +#endif + write_lock_irqsave(&pCh->Pbuf_spinlock, flags); + pCh->Pbuf_stuff = 0; + write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); + i2FlushOutput( pCh ); + ip2_owake(tty); + + ip2trace (CHANN, ITRC_FLUSH, ITRC_RETURN, 0 ); + +} + +/******************************************************************************/ +/* Function: ip2_wait_until_sent() */ +/* Parameters: Pointer to tty structure */ +/* Timeout for wait. */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function is used in place of the normal tty_wait_until_sent, which */ +/* only waits for the driver buffers to be empty (or rather, those buffers */ +/* reported by chars_in_buffer) which doesn't work for IP2 due to the */ +/* indeterminate number of bytes buffered on the board. */ +/******************************************************************************/ +static void +ip2_wait_until_sent ( PTTY tty, int timeout ) +{ + int i = jiffies; + i2ChanStrPtr pCh = tty->driver_data; + + tty_wait_until_sent(tty, timeout ); + if ( (i = timeout - (jiffies -i)) > 0) + i2DrainOutput( pCh, i ); +} + +/******************************************************************************/ +/******************************************************************************/ +/* Device Input Section */ +/******************************************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_throttle() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_throttle ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "IP2: throttle\n" ); +#endif + /* + * Signal the poll/interrupt handlers not to forward incoming data to + * the line discipline. This will cause the buffers to fill up in the + * library and thus cause the library routines to send the flow control + * stuff. + */ + pCh->throttled = 1; +} + +/******************************************************************************/ +/* Function: ip2_unthrottle() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_unthrottle ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "IP2: unthrottle\n" ); +#endif + + /* Pass incoming data up to the line discipline again. */ + pCh->throttled = 0; + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME); + serviceOutgoingFifo( pCh->pMyBord ); + read_lock_irqsave(&pCh->Ibuf_spinlock, flags); + if ( pCh->Ibuf_stuff != pCh->Ibuf_strip ) { + read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "i2Input called from unthrottle\n" ); +#endif + i2Input( pCh ); + } else + read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); +} + +static void +ip2_start ( PTTY tty ) +{ + i2ChanStrPtr pCh = DevTable[tty->index]; + + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME); + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_UNSUSPEND); + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_RESUME); +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: start tx\n" ); +#endif +} + +static void +ip2_stop ( PTTY tty ) +{ + i2ChanStrPtr pCh = DevTable[tty->index]; + + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_SUSPEND); +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: stop tx\n" ); +#endif +} + +/******************************************************************************/ +/* Device Ioctl Section */ +/******************************************************************************/ + +static int ip2_tiocmget(struct tty_struct *tty) +{ + i2ChanStrPtr pCh = DevTable[tty->index]; +#ifdef ENABLE_DSSNOW + wait_queue_t wait; +#endif + + if (pCh == NULL) + return -ENODEV; + +/* + FIXME - the following code is causing a NULL pointer dereference in + 2.3.51 in an interrupt handler. It's suppose to prompt the board + to return the DSS signal status immediately. Why doesn't it do + the same thing in 2.2.14? +*/ + +/* This thing is still busted in the 1.2.12 driver on 2.4.x + and even hoses the serial console so the oops can be trapped. + /\/\|=mhw=|\/\/ */ + +#ifdef ENABLE_DSSNOW + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DSS_NOW); + + init_waitqueue_entry(&wait, current); + add_wait_queue(&pCh->dss_now_wait, &wait); + set_current_state( TASK_INTERRUPTIBLE ); + + serviceOutgoingFifo( pCh->pMyBord ); + + schedule(); + + set_current_state( TASK_RUNNING ); + remove_wait_queue(&pCh->dss_now_wait, &wait); + + if (signal_pending(current)) { + return -EINTR; + } +#endif + return ((pCh->dataSetOut & I2_RTS) ? TIOCM_RTS : 0) + | ((pCh->dataSetOut & I2_DTR) ? TIOCM_DTR : 0) + | ((pCh->dataSetIn & I2_DCD) ? TIOCM_CAR : 0) + | ((pCh->dataSetIn & I2_RI) ? TIOCM_RNG : 0) + | ((pCh->dataSetIn & I2_DSR) ? TIOCM_DSR : 0) + | ((pCh->dataSetIn & I2_CTS) ? TIOCM_CTS : 0); +} + +static int ip2_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + i2ChanStrPtr pCh = DevTable[tty->index]; + + if (pCh == NULL) + return -ENODEV; + + if (set & TIOCM_RTS) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP); + pCh->dataSetOut |= I2_RTS; + } + if (set & TIOCM_DTR) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP); + pCh->dataSetOut |= I2_DTR; + } + + if (clear & TIOCM_RTS) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN); + pCh->dataSetOut &= ~I2_RTS; + } + if (clear & TIOCM_DTR) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN); + pCh->dataSetOut &= ~I2_DTR; + } + serviceOutgoingFifo( pCh->pMyBord ); + return 0; +} + +/******************************************************************************/ +/* Function: ip2_ioctl() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Command */ +/* Argument */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_ioctl ( PTTY tty, UINT cmd, ULONG arg ) +{ + wait_queue_t wait; + i2ChanStrPtr pCh = DevTable[tty->index]; + i2eBordStrPtr pB; + struct async_icount cprev, cnow; /* kernel counter temps */ + int rc = 0; + unsigned long flags; + void __user *argp = (void __user *)arg; + + if ( pCh == NULL ) + return -ENODEV; + + pB = pCh->pMyBord; + + ip2trace (CHANN, ITRC_IOCTL, ITRC_ENTER, 2, cmd, arg ); + +#ifdef IP2DEBUG_IOCTL + printk(KERN_DEBUG "IP2: ioctl cmd (%x), arg (%lx)\n", cmd, arg ); +#endif + + switch(cmd) { + case TIOCGSERIAL: + + ip2trace (CHANN, ITRC_IOCTL, 2, 1, rc ); + + rc = get_serial_info(pCh, argp); + if (rc) + return rc; + break; + + case TIOCSSERIAL: + + ip2trace (CHANN, ITRC_IOCTL, 3, 1, rc ); + + rc = set_serial_info(pCh, argp); + if (rc) + return rc; + break; + + case TCXONC: + rc = tty_check_change(tty); + if (rc) + return rc; + switch (arg) { + case TCOOFF: + //return -ENOIOCTLCMD; + break; + case TCOON: + //return -ENOIOCTLCMD; + break; + case TCIOFF: + if (STOP_CHAR(tty) != __DISABLED_CHAR) { + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1, + CMD_XMIT_NOW(STOP_CHAR(tty))); + } + break; + case TCION: + if (START_CHAR(tty) != __DISABLED_CHAR) { + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1, + CMD_XMIT_NOW(START_CHAR(tty))); + } + break; + default: + return -EINVAL; + } + return 0; + + case TCSBRK: /* SVID version: non-zero arg --> no break */ + rc = tty_check_change(tty); + + ip2trace (CHANN, ITRC_IOCTL, 4, 1, rc ); + + if (!rc) { + ip2_wait_until_sent(tty,0); + if (!arg) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SEND_BRK(250)); + serviceOutgoingFifo( pCh->pMyBord ); + } + } + break; + + case TCSBRKP: /* support for POSIX tcsendbreak() */ + rc = tty_check_change(tty); + + ip2trace (CHANN, ITRC_IOCTL, 5, 1, rc ); + + if (!rc) { + ip2_wait_until_sent(tty,0); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_SEND_BRK(arg ? arg*100 : 250)); + serviceOutgoingFifo ( pCh->pMyBord ); + } + break; + + case TIOCGSOFTCAR: + + ip2trace (CHANN, ITRC_IOCTL, 6, 1, rc ); + + rc = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *)argp); + if (rc) + return rc; + break; + + case TIOCSSOFTCAR: + + ip2trace (CHANN, ITRC_IOCTL, 7, 1, rc ); + + rc = get_user(arg,(unsigned long __user *) argp); + if (rc) + return rc; + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) + | (arg ? CLOCAL : 0)); + + break; + + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - mask + * passed in arg for lines of interest (use |'ed TIOCM_RNG/DSR/CD/CTS + * for masking). Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + write_lock_irqsave(&pB->read_fifo_spinlock, flags); + cprev = pCh->icount; /* note the counters on entry */ + write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 4, + CMD_DCD_REP, CMD_CTS_REP, CMD_DSR_REP, CMD_RI_REP); + init_waitqueue_entry(&wait, current); + add_wait_queue(&pCh->delta_msr_wait, &wait); + set_current_state( TASK_INTERRUPTIBLE ); + + serviceOutgoingFifo( pCh->pMyBord ); + for(;;) { + ip2trace (CHANN, ITRC_IOCTL, 10, 0 ); + + schedule(); + + ip2trace (CHANN, ITRC_IOCTL, 11, 0 ); + + /* see if a signal did it */ + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + write_lock_irqsave(&pB->read_fifo_spinlock, flags); + cnow = pCh->icount; /* atomic copy */ + write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { + rc = -EIO; /* no change => rc */ + break; + } + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + rc = 0; + break; + } + cprev = cnow; + } + set_current_state( TASK_RUNNING ); + remove_wait_queue(&pCh->delta_msr_wait, &wait); + + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 3, + CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); + if ( ! (pCh->flags & ASYNC_CHECK_CD)) { + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DCD_NREP); + } + serviceOutgoingFifo( pCh->pMyBord ); + return rc; + break; + + /* + * The rest are not supported by this driver. By returning -ENOIOCTLCMD they + * will be passed to the line discipline for it to handle. + */ + case TIOCSERCONFIG: + case TIOCSERGWILD: + case TIOCSERGETLSR: + case TIOCSERSWILD: + case TIOCSERGSTRUCT: + case TIOCSERGETMULTI: + case TIOCSERSETMULTI: + + default: + ip2trace (CHANN, ITRC_IOCTL, 12, 0 ); + + rc = -ENOIOCTLCMD; + break; + } + + ip2trace (CHANN, ITRC_IOCTL, ITRC_RETURN, 0 ); + + return rc; +} + +static int ip2_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + i2ChanStrPtr pCh = DevTable[tty->index]; + i2eBordStrPtr pB; + struct async_icount cnow; /* kernel counter temp */ + unsigned long flags; + + if ( pCh == NULL ) + return -ENODEV; + + pB = pCh->pMyBord; + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for RI where + * only 0->1 is counted. The controller is quite capable of counting + * both, but this done to preserve compatibility with the standard + * serial driver. + */ + + write_lock_irqsave(&pB->read_fifo_spinlock, flags); + cnow = pCh->icount; + write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); + + icount->cts = cnow.cts; + icount->dsr = cnow.dsr; + icount->rng = cnow.rng; + icount->dcd = cnow.dcd; + icount->rx = cnow.rx; + icount->tx = cnow.tx; + icount->frame = cnow.frame; + icount->overrun = cnow.overrun; + icount->parity = cnow.parity; + icount->brk = cnow.brk; + icount->buf_overrun = cnow.buf_overrun; + return 0; +} + +/******************************************************************************/ +/* Function: GetSerialInfo() */ +/* Parameters: Pointer to channel structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This is to support the setserial command, and requires processing of the */ +/* standard Linux serial structure. */ +/******************************************************************************/ +static int +get_serial_info ( i2ChanStrPtr pCh, struct serial_struct __user *retinfo ) +{ + struct serial_struct tmp; + + memset ( &tmp, 0, sizeof(tmp) ); + tmp.type = pCh->pMyBord->channelBtypes.bid_value[(pCh->port_index & (IP2_PORTS_PER_BOARD-1))/16]; + if (BID_HAS_654(tmp.type)) { + tmp.type = PORT_16650; + } else { + tmp.type = PORT_CIRRUS; + } + tmp.line = pCh->port_index; + tmp.port = pCh->pMyBord->i2eBase; + tmp.irq = ip2config.irq[pCh->port_index/64]; + tmp.flags = pCh->flags; + tmp.baud_base = pCh->BaudBase; + tmp.close_delay = pCh->ClosingDelay; + tmp.closing_wait = pCh->ClosingWaitTime; + tmp.custom_divisor = pCh->BaudDivisor; + return copy_to_user(retinfo,&tmp,sizeof(*retinfo)); +} + +/******************************************************************************/ +/* Function: SetSerialInfo() */ +/* Parameters: Pointer to channel structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function provides support for setserial, which uses the TIOCSSERIAL */ +/* ioctl. Not all setserial parameters are relevant. If the user attempts to */ +/* change the IRQ, address or type of the port the ioctl fails. */ +/******************************************************************************/ +static int +set_serial_info( i2ChanStrPtr pCh, struct serial_struct __user *new_info ) +{ + struct serial_struct ns; + int old_flags, old_baud_divisor; + + if (copy_from_user(&ns, new_info, sizeof (ns))) + return -EFAULT; + + /* + * We don't allow setserial to change IRQ, board address, type or baud + * base. Also line nunber as such is meaningless but we use it for our + * array index so it is fixed also. + */ + if ( (ns.irq != ip2config.irq[pCh->port_index]) + || ((int) ns.port != ((int) (pCh->pMyBord->i2eBase))) + || (ns.baud_base != pCh->BaudBase) + || (ns.line != pCh->port_index) ) { + return -EINVAL; + } + + old_flags = pCh->flags; + old_baud_divisor = pCh->BaudDivisor; + + if ( !capable(CAP_SYS_ADMIN) ) { + if ( ( ns.close_delay != pCh->ClosingDelay ) || + ( (ns.flags & ~ASYNC_USR_MASK) != + (pCh->flags & ~ASYNC_USR_MASK) ) ) { + return -EPERM; + } + + pCh->flags = (pCh->flags & ~ASYNC_USR_MASK) | + (ns.flags & ASYNC_USR_MASK); + pCh->BaudDivisor = ns.custom_divisor; + } else { + pCh->flags = (pCh->flags & ~ASYNC_FLAGS) | + (ns.flags & ASYNC_FLAGS); + pCh->BaudDivisor = ns.custom_divisor; + pCh->ClosingDelay = ns.close_delay * HZ/100; + pCh->ClosingWaitTime = ns.closing_wait * HZ/100; + } + + if ( ( (old_flags & ASYNC_SPD_MASK) != (pCh->flags & ASYNC_SPD_MASK) ) + || (old_baud_divisor != pCh->BaudDivisor) ) { + // Invalidate speed and reset parameters + set_params( pCh, NULL ); + } + + return 0; +} + +/******************************************************************************/ +/* Function: ip2_set_termios() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_set_termios( PTTY tty, struct ktermios *old_termios ) +{ + i2ChanStrPtr pCh = (i2ChanStrPtr)tty->driver_data; + +#ifdef IP2DEBUG_IOCTL + printk (KERN_DEBUG "IP2: set termios %p\n", old_termios ); +#endif + + set_params( pCh, old_termios ); +} + +/******************************************************************************/ +/* Function: ip2_set_line_discipline() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: Does nothing */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_set_line_discipline ( PTTY tty ) +{ +#ifdef IP2DEBUG_IOCTL + printk (KERN_DEBUG "IP2: set line discipline\n" ); +#endif + + ip2trace (((i2ChanStrPtr)tty->driver_data)->port_index, ITRC_IOCTL, 16, 0 ); + +} + +/******************************************************************************/ +/* Function: SetLine Characteristics() */ +/* Parameters: Pointer to channel structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This routine is called to update the channel structure with the new line */ +/* characteristics, and send the appropriate commands to the board when they */ +/* change. */ +/******************************************************************************/ +static void +set_params( i2ChanStrPtr pCh, struct ktermios *o_tios ) +{ + tcflag_t cflag, iflag, lflag; + char stop_char, start_char; + struct ktermios dummy; + + lflag = pCh->pTTY->termios->c_lflag; + cflag = pCh->pTTY->termios->c_cflag; + iflag = pCh->pTTY->termios->c_iflag; + + if (o_tios == NULL) { + dummy.c_lflag = ~lflag; + dummy.c_cflag = ~cflag; + dummy.c_iflag = ~iflag; + o_tios = &dummy; + } + + { + switch ( cflag & CBAUD ) { + case B0: + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + pCh->pTTY->termios->c_cflag |= (CBAUD & o_tios->c_cflag); + goto service_it; + break; + case B38400: + /* + * This is the speed that is overloaded with all the other high + * speeds, depending upon the flag settings. + */ + if ( ( pCh->flags & ASYNC_SPD_MASK ) == ASYNC_SPD_HI ) { + pCh->speed = CBR_57600; + } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI ) { + pCh->speed = CBR_115200; + } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST ) { + pCh->speed = CBR_C1; + } else { + pCh->speed = CBR_38400; + } + break; + case B50: pCh->speed = CBR_50; break; + case B75: pCh->speed = CBR_75; break; + case B110: pCh->speed = CBR_110; break; + case B134: pCh->speed = CBR_134; break; + case B150: pCh->speed = CBR_150; break; + case B200: pCh->speed = CBR_200; break; + case B300: pCh->speed = CBR_300; break; + case B600: pCh->speed = CBR_600; break; + case B1200: pCh->speed = CBR_1200; break; + case B1800: pCh->speed = CBR_1800; break; + case B2400: pCh->speed = CBR_2400; break; + case B4800: pCh->speed = CBR_4800; break; + case B9600: pCh->speed = CBR_9600; break; + case B19200: pCh->speed = CBR_19200; break; + case B57600: pCh->speed = CBR_57600; break; + case B115200: pCh->speed = CBR_115200; break; + case B153600: pCh->speed = CBR_153600; break; + case B230400: pCh->speed = CBR_230400; break; + case B307200: pCh->speed = CBR_307200; break; + case B460800: pCh->speed = CBR_460800; break; + case B921600: pCh->speed = CBR_921600; break; + default: pCh->speed = CBR_9600; break; + } + if ( pCh->speed == CBR_C1 ) { + // Process the custom speed parameters. + int bps = pCh->BaudBase / pCh->BaudDivisor; + if ( bps == 921600 ) { + pCh->speed = CBR_921600; + } else { + bps = bps/10; + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_BAUD_DEF1(bps) ); + } + } + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_SETBAUD(pCh->speed)); + + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + } + if ( (CSTOPB & cflag) ^ (CSTOPB & o_tios->c_cflag)) + { + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, + CMD_SETSTOP( ( cflag & CSTOPB ) ? CST_2 : CST_1)); + } + if (((PARENB|PARODD) & cflag) ^ ((PARENB|PARODD) & o_tios->c_cflag)) + { + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, + CMD_SETPAR( + (cflag & PARENB ? (cflag & PARODD ? CSP_OD : CSP_EV) : CSP_NP) + ) + ); + } + /* byte size and parity */ + if ( (CSIZE & cflag)^(CSIZE & o_tios->c_cflag)) + { + int datasize; + switch ( cflag & CSIZE ) { + case CS5: datasize = CSZ_5; break; + case CS6: datasize = CSZ_6; break; + case CS7: datasize = CSZ_7; break; + case CS8: datasize = CSZ_8; break; + default: datasize = CSZ_5; break; /* as per serial.c */ + } + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, CMD_SETBITS(datasize) ); + } + /* Process CTS flow control flag setting */ + if ( (cflag & CRTSCTS) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, + 2, CMD_CTSFL_ENAB, CMD_RTSFL_ENAB); + } else { + i2QueueCommands(PTYPE_INLINE, pCh, 100, + 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + } + // + // Process XON/XOFF flow control flags settings + // + stop_char = STOP_CHAR(pCh->pTTY); + start_char = START_CHAR(pCh->pTTY); + + //////////// can't be \000 + if (stop_char == __DISABLED_CHAR ) + { + stop_char = ~__DISABLED_CHAR; + } + if (start_char == __DISABLED_CHAR ) + { + start_char = ~__DISABLED_CHAR; + } + ///////////////////////////////// + + if ( o_tios->c_cc[VSTART] != start_char ) + { + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXON(start_char)); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXON(start_char)); + } + if ( o_tios->c_cc[VSTOP] != stop_char ) + { + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXOFF(stop_char)); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXOFF(stop_char)); + } + if (stop_char == __DISABLED_CHAR ) + { + stop_char = ~__DISABLED_CHAR; //TEST123 + goto no_xoff; + } + if ((iflag & (IXOFF))^(o_tios->c_iflag & (IXOFF))) + { + if ( iflag & IXOFF ) { // Enable XOFF output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_XON)); + } else { // Disable XOFF output flow control +no_xoff: + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_NONE)); + } + } + if (start_char == __DISABLED_CHAR ) + { + goto no_xon; + } + if ((iflag & (IXON|IXANY)) ^ (o_tios->c_iflag & (IXON|IXANY))) + { + if ( iflag & IXON ) { + if ( iflag & IXANY ) { // Enable XON/XANY output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XANY)); + } else { // Enable XON output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XON)); + } + } else { // Disable XON output flow control +no_xon: + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_NONE)); + } + } + if ( (iflag & ISTRIP) ^ ( o_tios->c_iflag & (ISTRIP)) ) + { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_ISTRIP_OPT((iflag & ISTRIP ? 1 : 0))); + } + if ( (iflag & INPCK) ^ ( o_tios->c_iflag & (INPCK)) ) + { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_PARCHK((iflag & INPCK) ? CPK_ENAB : CPK_DSAB)); + } + + if ( (iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) + ^ ( o_tios->c_iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) ) + { + char brkrpt = 0; + char parrpt = 0; + + if ( iflag & IGNBRK ) { /* Ignore breaks altogether */ + /* Ignore breaks altogether */ + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_NREP); + } else { + if ( iflag & BRKINT ) { + if ( iflag & PARMRK ) { + brkrpt = 0x0a; // exception an inline triple + } else { + brkrpt = 0x1a; // exception and NULL + } + brkrpt |= 0x04; // flush input + } else { + if ( iflag & PARMRK ) { + brkrpt = 0x0b; //POSIX triple \0377 \0 \0 + } else { + brkrpt = 0x01; // Null only + } + } + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_REP(brkrpt)); + } + + if (iflag & IGNPAR) { + parrpt = 0x20; + /* would be 2 for not cirrus bug */ + /* would be 0x20 cept for cirrus bug */ + } else { + if ( iflag & PARMRK ) { + /* + * Replace error characters with 3-byte sequence (\0377,\0,char) + */ + parrpt = 0x04 ; + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_ISTRIP_OPT((char)0)); + } else { + parrpt = 0x03; + } + } + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SET_ERROR(parrpt)); + } + if (cflag & CLOCAL) { + // Status reporting fails for DCD if this is off + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_NREP); + pCh->flags &= ~ASYNC_CHECK_CD; + } else { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_REP); + pCh->flags |= ASYNC_CHECK_CD; + } + +service_it: + i2DrainOutput( pCh, 100 ); +} + +/******************************************************************************/ +/* IPL Device Section */ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_ipl_read() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Pointer to data */ +/* Number of bytes to read */ +/* Returns: Success or failure */ +/* */ +/* Description: Ugly */ +/* */ +/* */ +/******************************************************************************/ + +static +ssize_t +ip2_ipl_read(struct file *pFile, char __user *pData, size_t count, loff_t *off ) +{ + unsigned int minor = iminor(pFile->f_path.dentry->d_inode); + int rc = 0; + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: read %p, %d bytes\n", pData, count ); +#endif + + switch( minor ) { + case 0: // IPL device + rc = -EINVAL; + break; + case 1: // Status dump + rc = -EINVAL; + break; + case 2: // Ping device + rc = -EINVAL; + break; + case 3: // Trace device + rc = DumpTraceBuffer ( pData, count ); + break; + case 4: // Trace device + rc = DumpFifoBuffer ( pData, count ); + break; + default: + rc = -ENODEV; + break; + } + return rc; +} + +static int +DumpFifoBuffer ( char __user *pData, int count ) +{ +#ifdef DEBUG_FIFO + int rc; + rc = copy_to_user(pData, DBGBuf, count); + + printk(KERN_DEBUG "Last index %d\n", I ); + + return count; +#endif /* DEBUG_FIFO */ + return 0; +} + +static int +DumpTraceBuffer ( char __user *pData, int count ) +{ +#ifdef IP2DEBUG_TRACE + int rc; + int dumpcount; + int chunk; + int *pIndex = (int __user *)pData; + + if ( count < (sizeof(int) * 6) ) { + return -EIO; + } + rc = put_user(tracewrap, pIndex ); + rc = put_user(TRACEMAX, ++pIndex ); + rc = put_user(tracestrip, ++pIndex ); + rc = put_user(tracestuff, ++pIndex ); + pData += sizeof(int) * 6; + count -= sizeof(int) * 6; + + dumpcount = tracestuff - tracestrip; + if ( dumpcount < 0 ) { + dumpcount += TRACEMAX; + } + if ( dumpcount > count ) { + dumpcount = count; + } + chunk = TRACEMAX - tracestrip; + if ( dumpcount > chunk ) { + rc = copy_to_user(pData, &tracebuf[tracestrip], + chunk * sizeof(tracebuf[0]) ); + pData += chunk * sizeof(tracebuf[0]); + tracestrip = 0; + chunk = dumpcount - chunk; + } else { + chunk = dumpcount; + } + rc = copy_to_user(pData, &tracebuf[tracestrip], + chunk * sizeof(tracebuf[0]) ); + tracestrip += chunk; + tracewrap = 0; + + rc = put_user(tracestrip, ++pIndex ); + rc = put_user(tracestuff, ++pIndex ); + + return dumpcount; +#else + return 0; +#endif +} + +/******************************************************************************/ +/* Function: ip2_ipl_write() */ +/* Parameters: */ +/* Pointer to file structure */ +/* Pointer to data */ +/* Number of bytes to write */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static ssize_t +ip2_ipl_write(struct file *pFile, const char __user *pData, size_t count, loff_t *off) +{ +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: write %p, %d bytes\n", pData, count ); +#endif + return 0; +} + +/******************************************************************************/ +/* Function: ip2_ipl_ioctl() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Command */ +/* Argument */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static long +ip2_ipl_ioctl (struct file *pFile, UINT cmd, ULONG arg ) +{ + unsigned int iplminor = iminor(pFile->f_path.dentry->d_inode); + int rc = 0; + void __user *argp = (void __user *)arg; + ULONG __user *pIndex = argp; + i2eBordStrPtr pB = i2BoardPtrTable[iplminor / 4]; + i2ChanStrPtr pCh; + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: ioctl cmd %d, arg %ld\n", cmd, arg ); +#endif + + mutex_lock(&ip2_mutex); + + switch ( iplminor ) { + case 0: // IPL device + rc = -EINVAL; + break; + case 1: // Status dump + case 5: + case 9: + case 13: + switch ( cmd ) { + case 64: /* Driver - ip2stat */ + rc = put_user(-1, pIndex++ ); + rc = put_user(irq_counter, pIndex++ ); + rc = put_user(bh_counter, pIndex++ ); + break; + + case 65: /* Board - ip2stat */ + if ( pB ) { + rc = copy_to_user(argp, pB, sizeof(i2eBordStr)); + rc = put_user(inb(pB->i2eStatus), + (ULONG __user *)(arg + (ULONG)(&pB->i2eStatus) - (ULONG)pB ) ); + } else { + rc = -ENODEV; + } + break; + + default: + if (cmd < IP2_MAX_PORTS) { + pCh = DevTable[cmd]; + if ( pCh ) + { + rc = copy_to_user(argp, pCh, sizeof(i2ChanStr)); + if (rc) + rc = -EFAULT; + } else { + rc = -ENODEV; + } + } else { + rc = -EINVAL; + } + } + break; + + case 2: // Ping device + rc = -EINVAL; + break; + case 3: // Trace device + /* + * akpm: This used to write a whole bunch of function addresses + * to userspace, which generated lots of put_user() warnings. + * I killed it all. Just return "success" and don't do + * anything. + */ + if (cmd == 1) + rc = 0; + else + rc = -EINVAL; + break; + + default: + rc = -ENODEV; + break; + } + mutex_unlock(&ip2_mutex); + return rc; +} + +/******************************************************************************/ +/* Function: ip2_ipl_open() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_ipl_open( struct inode *pInode, struct file *pFile ) +{ + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: open\n" ); +#endif + return 0; +} + +static int +proc_ip2mem_show(struct seq_file *m, void *v) +{ + i2eBordStrPtr pB; + i2ChanStrPtr pCh; + PTTY tty; + int i; + +#define FMTLINE "%3d: 0x%08x 0x%08x 0%011o 0%011o\n" +#define FMTLIN2 " 0x%04x 0x%04x tx flow 0x%x\n" +#define FMTLIN3 " 0x%04x 0x%04x rc flow\n" + + seq_printf(m,"\n"); + + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + pB = i2BoardPtrTable[i]; + if ( pB ) { + seq_printf(m,"board %d:\n",i); + seq_printf(m,"\tFifo rem: %d mty: %x outM %x\n", + pB->i2eFifoRemains,pB->i2eWaitingForEmptyFifo,pB->i2eOutMailWaiting); + } + } + + seq_printf(m,"#: tty flags, port flags, cflags, iflags\n"); + for (i=0; i < IP2_MAX_PORTS; i++) { + pCh = DevTable[i]; + if (pCh) { + tty = pCh->pTTY; + if (tty && tty->count) { + seq_printf(m,FMTLINE,i,(int)tty->flags,pCh->flags, + tty->termios->c_cflag,tty->termios->c_iflag); + + seq_printf(m,FMTLIN2, + pCh->outfl.asof,pCh->outfl.room,pCh->channelNeeds); + seq_printf(m,FMTLIN3,pCh->infl.asof,pCh->infl.room); + } + } + } + return 0; +} + +static int proc_ip2mem_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_ip2mem_show, NULL); +} + +static const struct file_operations ip2mem_proc_fops = { + .owner = THIS_MODULE, + .open = proc_ip2mem_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* + * This is the handler for /proc/tty/driver/ip2 + * + * This stretch of code has been largely plagerized from at least three + * different sources including ip2mkdev.c and a couple of other drivers. + * The bugs are all mine. :-) =mhw= + */ +static int ip2_proc_show(struct seq_file *m, void *v) +{ + int i, j, box; + int boxes = 0; + int ports = 0; + int tports = 0; + i2eBordStrPtr pB; + char *sep; + + seq_printf(m, "ip2info: 1.0 driver: %s\n", pcVersion); + seq_printf(m, "Driver: SMajor=%d CMajor=%d IMajor=%d MaxBoards=%d MaxBoxes=%d MaxPorts=%d\n", + IP2_TTY_MAJOR, IP2_CALLOUT_MAJOR, IP2_IPL_MAJOR, + IP2_MAX_BOARDS, ABS_MAX_BOXES, ABS_BIGGEST_BOX); + + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + /* This need to be reset for a board by board count... */ + boxes = 0; + pB = i2BoardPtrTable[i]; + if( pB ) { + switch( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) + { + case POR_ID_FIIEX: + seq_printf(m, "Board %d: EX ports=", i); + sep = ""; + for( box = 0; box < ABS_MAX_BOXES; ++box ) + { + ports = 0; + + if( pB->i2eChannelMap[box] != 0 ) ++boxes; + for( j = 0; j < ABS_BIGGEST_BOX; ++j ) + { + if( pB->i2eChannelMap[box] & 1<< j ) { + ++ports; + } + } + seq_printf(m, "%s%d", sep, ports); + sep = ","; + tports += ports; + } + seq_printf(m, " boxes=%d width=%d", boxes, pB->i2eDataWidth16 ? 16 : 8); + break; + + case POR_ID_II_4: + seq_printf(m, "Board %d: ISA-4 ports=4 boxes=1", i); + tports = ports = 4; + break; + + case POR_ID_II_8: + seq_printf(m, "Board %d: ISA-8-std ports=8 boxes=1", i); + tports = ports = 8; + break; + + case POR_ID_II_8R: + seq_printf(m, "Board %d: ISA-8-RJ11 ports=8 boxes=1", i); + tports = ports = 8; + break; + + default: + seq_printf(m, "Board %d: unknown", i); + /* Don't try and probe for minor numbers */ + tports = ports = 0; + } + + } else { + /* Don't try and probe for minor numbers */ + seq_printf(m, "Board %d: vacant", i); + tports = ports = 0; + } + + if( tports ) { + seq_puts(m, " minors="); + sep = ""; + for ( box = 0; box < ABS_MAX_BOXES; ++box ) + { + for ( j = 0; j < ABS_BIGGEST_BOX; ++j ) + { + if ( pB->i2eChannelMap[box] & (1 << j) ) + { + seq_printf(m, "%s%d", sep, + j + ABS_BIGGEST_BOX * + (box+i*ABS_MAX_BOXES)); + sep = ","; + } + } + } + } + seq_putc(m, '\n'); + } + return 0; + } + +static int ip2_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, ip2_proc_show, NULL); +} + +static const struct file_operations ip2_proc_fops = { + .owner = THIS_MODULE, + .open = ip2_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/******************************************************************************/ +/* Function: ip2trace() */ +/* Parameters: Value to add to trace buffer */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +#ifdef IP2DEBUG_TRACE +void +ip2trace (unsigned short pn, unsigned char cat, unsigned char label, unsigned long codes, ...) +{ + long flags; + unsigned long *pCode = &codes; + union ip2breadcrumb bc; + i2ChanStrPtr pCh; + + + tracebuf[tracestuff++] = jiffies; + if ( tracestuff == TRACEMAX ) { + tracestuff = 0; + } + if ( tracestuff == tracestrip ) { + if ( ++tracestrip == TRACEMAX ) { + tracestrip = 0; + } + ++tracewrap; + } + + bc.hdr.port = 0xff & pn; + bc.hdr.cat = cat; + bc.hdr.codes = (unsigned char)( codes & 0xff ); + bc.hdr.label = label; + tracebuf[tracestuff++] = bc.value; + + for (;;) { + if ( tracestuff == TRACEMAX ) { + tracestuff = 0; + } + if ( tracestuff == tracestrip ) { + if ( ++tracestrip == TRACEMAX ) { + tracestrip = 0; + } + ++tracewrap; + } + + if ( !codes-- ) + break; + + tracebuf[tracestuff++] = *++pCode; + } +} +#endif + + +MODULE_LICENSE("GPL"); + +static struct pci_device_id ip2main_pci_tbl[] __devinitdata __used = { + { PCI_DEVICE(PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_IP2EX) }, + { } +}; + +MODULE_DEVICE_TABLE(pci, ip2main_pci_tbl); + +MODULE_FIRMWARE("intelliport2.bin"); diff --git a/drivers/staging/tty/ip2/ip2trace.h b/drivers/staging/tty/ip2/ip2trace.h new file mode 100644 index 000000000000..da20435dc8a6 --- /dev/null +++ b/drivers/staging/tty/ip2/ip2trace.h @@ -0,0 +1,42 @@ + +// +union ip2breadcrumb +{ + struct { + unsigned char port, cat, codes, label; + } __attribute__ ((packed)) hdr; + unsigned long value; +}; + +#define ITRC_NO_PORT 0xFF +#define CHANN (pCh->port_index) + +#define ITRC_ERROR '!' +#define ITRC_INIT 'A' +#define ITRC_OPEN 'B' +#define ITRC_CLOSE 'C' +#define ITRC_DRAIN 'D' +#define ITRC_IOCTL 'E' +#define ITRC_FLUSH 'F' +#define ITRC_STATUS 'G' +#define ITRC_HANGUP 'H' +#define ITRC_INTR 'I' +#define ITRC_SFLOW 'J' +#define ITRC_SBCMD 'K' +#define ITRC_SICMD 'L' +#define ITRC_MODEM 'M' +#define ITRC_INPUT 'N' +#define ITRC_OUTPUT 'O' +#define ITRC_PUTC 'P' +#define ITRC_QUEUE 'Q' +#define ITRC_STFLW 'R' +#define ITRC_SFIFO 'S' +#define ITRC_VERIFY 'V' +#define ITRC_WRITE 'W' + +#define ITRC_ENTER 0x00 +#define ITRC_RETURN 0xFF + +#define ITRC_QUEUE_ROOM 2 +#define ITRC_QUEUE_CMD 6 + diff --git a/drivers/staging/tty/ip2/ip2types.h b/drivers/staging/tty/ip2/ip2types.h new file mode 100644 index 000000000000..9d67b260b2f6 --- /dev/null +++ b/drivers/staging/tty/ip2/ip2types.h @@ -0,0 +1,57 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants and type definitions. +* +* NOTES: +* +*******************************************************************************/ +#ifndef IP2TYPES_H +#define IP2TYPES_H + +//************* +//* Constants * +//************* + +// Define some limits for this driver. Ports per board is a hardware limitation +// that will not change. Current hardware limits this to 64 ports per board. +// Boards per driver is a self-imposed limit. +// +#define IP2_MAX_BOARDS 4 +#define IP2_PORTS_PER_BOARD ABS_MOST_PORTS +#define IP2_MAX_PORTS (IP2_MAX_BOARDS*IP2_PORTS_PER_BOARD) + +#define ISA 0 +#define PCI 1 +#define EISA 2 + +//******************** +//* Type Definitions * +//******************** + +typedef struct tty_struct * PTTY; +typedef wait_queue_head_t PWAITQ; + +typedef unsigned char UCHAR; +typedef unsigned int UINT; +typedef unsigned short USHORT; +typedef unsigned long ULONG; + +typedef struct +{ + short irq[IP2_MAX_BOARDS]; + unsigned short addr[IP2_MAX_BOARDS]; + int type[IP2_MAX_BOARDS]; +#ifdef CONFIG_PCI + struct pci_dev *pci_dev[IP2_MAX_BOARDS]; +#endif +} ip2config_t; + +#endif diff --git a/drivers/staging/tty/istallion.c b/drivers/staging/tty/istallion.c new file mode 100644 index 000000000000..0b266272cccd --- /dev/null +++ b/drivers/staging/tty/istallion.c @@ -0,0 +1,4507 @@ +/*****************************************************************************/ + +/* + * 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. + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/*****************************************************************************/ + +/* + * 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_ONBOARDE 7 +#define BRD_ECP 23 +#define BRD_ECPE 24 +#define BRD_ECPMC 25 +#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. + */ + +struct stlconf { + int brdtype; + int ioaddr1; + int ioaddr2; + unsigned long memaddr; + int irq; + int irqtype; +}; + +static unsigned int stli_nrbrds; + +/* stli_lock must NOT be taken holding brd_lock */ +static spinlock_t stli_lock; /* TTY logic lock */ +static spinlock_t brd_lock; /* Board logic lock */ + +/* + * 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; +static const struct tty_port_operations stli_port_ops; + +#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 ktermios stli_deftermios = { + .c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL), + .c_cc = INIT_C_CC, + .c_ispeed = 9600, + .c_ospeed = 9600, +}; + +/* + * 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 struct asystats stli_cdkstats; + +/*****************************************************************************/ + +static DEFINE_MUTEX(stli_brdslock); +static struct stlibrd *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 0 +#define BST_STARTED 1 +#define BST_PROBED 2 + +/* + * 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_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", + NULL, + "ONboard", + "ONboard-MC", + "ONboard-MC", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 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", +}; + +/*****************************************************************************/ + +/* + * 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. + */ + +static struct stlibrdtype { + char *name; + int type; +} 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]"); + +#if STLI_EISAPROBE != 0 +/* + * 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); +#endif + +/* + * Define the Stallion PCI vendor and device IDs. + */ +#ifndef PCI_DEVICE_ID_ECRA +#define PCI_DEVICE_ID_ECRA 0x0004 +#endif + +static struct pci_device_id istallion_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECRA), }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, istallion_pci_tbl); + +static struct pci_driver stli_pcidriver; + +/*****************************************************************************/ + +/* + * 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) + +/*****************************************************************************/ + +/* + * Prototype all functions in this driver! + */ + +static int stli_parsebrd(struct stlconf *confp, char **argp); +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 int 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, unsigned int cmd, unsigned long arg); +static void stli_settermios(struct tty_struct *tty, struct ktermios *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 int 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_brdinit(struct stlibrd *brdp); +static int stli_startbrd(struct stlibrd *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 long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg); +static void stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp); +static void stli_poll(unsigned long arg); +static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp); +static int stli_initopen(struct tty_struct *tty, struct stlibrd *brdp, struct stliport *portp); +static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); +static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); +static int stli_setport(struct tty_struct *tty); +static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); +static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); +static void __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); +static void stli_dodelaycmd(struct stliport *portp, cdkctrl_t __iomem *cp); +static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp, asyport_t *pp, struct ktermios *tiosp); +static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts); +static long stli_mktiocm(unsigned long sigvalue); +static void stli_read(struct stlibrd *brdp, struct stliport *portp); +static int stli_getserial(struct stliport *portp, struct serial_struct __user *sp); +static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp); +static int stli_getbrdstats(combrd_t __user *bp); +static int stli_getportstats(struct tty_struct *tty, struct stliport *portp, comstats_t __user *cp); +static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp); +static int stli_clrportstats(struct stliport *portp, comstats_t __user *cp); +static int stli_getportstruct(struct stliport __user *arg); +static int stli_getbrdstruct(struct stlibrd __user *arg); +static struct stlibrd *stli_allocbrd(void); + +static void stli_ecpinit(struct stlibrd *brdp); +static void stli_ecpenable(struct stlibrd *brdp); +static void stli_ecpdisable(struct stlibrd *brdp); +static void __iomem *stli_ecpgetmemptr(struct stlibrd *brdp, unsigned long offset, int line); +static void stli_ecpreset(struct stlibrd *brdp); +static void stli_ecpintr(struct stlibrd *brdp); +static void stli_ecpeiinit(struct stlibrd *brdp); +static void stli_ecpeienable(struct stlibrd *brdp); +static void stli_ecpeidisable(struct stlibrd *brdp); +static void __iomem *stli_ecpeigetmemptr(struct stlibrd *brdp, unsigned long offset, int line); +static void stli_ecpeireset(struct stlibrd *brdp); +static void stli_ecpmcenable(struct stlibrd *brdp); +static void stli_ecpmcdisable(struct stlibrd *brdp); +static void __iomem *stli_ecpmcgetmemptr(struct stlibrd *brdp, unsigned long offset, int line); +static void stli_ecpmcreset(struct stlibrd *brdp); +static void stli_ecppciinit(struct stlibrd *brdp); +static void __iomem *stli_ecppcigetmemptr(struct stlibrd *brdp, unsigned long offset, int line); +static void stli_ecppcireset(struct stlibrd *brdp); + +static void stli_onbinit(struct stlibrd *brdp); +static void stli_onbenable(struct stlibrd *brdp); +static void stli_onbdisable(struct stlibrd *brdp); +static void __iomem *stli_onbgetmemptr(struct stlibrd *brdp, unsigned long offset, int line); +static void stli_onbreset(struct stlibrd *brdp); +static void stli_onbeinit(struct stlibrd *brdp); +static void stli_onbeenable(struct stlibrd *brdp); +static void stli_onbedisable(struct stlibrd *brdp); +static void __iomem *stli_onbegetmemptr(struct stlibrd *brdp, unsigned long offset, int line); +static void stli_onbereset(struct stlibrd *brdp); +static void stli_bbyinit(struct stlibrd *brdp); +static void __iomem *stli_bbygetmemptr(struct stlibrd *brdp, unsigned long offset, int line); +static void stli_bbyreset(struct stlibrd *brdp); +static void stli_stalinit(struct stlibrd *brdp); +static void __iomem *stli_stalgetmemptr(struct stlibrd *brdp, unsigned long offset, int line); +static void stli_stalreset(struct stlibrd *brdp); + +static struct stliport *stli_getport(unsigned int brdnr, unsigned int panelnr, unsigned int portnr); + +static int stli_initecp(struct stlibrd *brdp); +static int stli_initonb(struct stlibrd *brdp); +#if STLI_EISAPROBE != 0 +static int stli_eisamemprobe(struct stlibrd *brdp); +#endif +static int stli_initports(struct stlibrd *brdp); + +/*****************************************************************************/ + +/* + * 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 const struct file_operations stli_fsiomem = { + .owner = THIS_MODULE, + .read = stli_memread, + .write = stli_memwrite, + .unlocked_ioctl = stli_memioctl, + .llseek = default_llseek, +}; + +/*****************************************************************************/ + +/* + * 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; + +static void stli_cleanup_ports(struct stlibrd *brdp) +{ + struct stliport *portp; + unsigned int j; + struct tty_struct *tty; + + for (j = 0; j < STL_MAXPORTS; j++) { + portp = brdp->ports[j]; + if (portp != NULL) { + tty = tty_port_tty_get(&portp->port); + if (tty != NULL) { + tty_hangup(tty); + tty_kref_put(tty); + } + kfree(portp); + } + } +} + +/*****************************************************************************/ + +/* + * Parse the supplied argument string, into the board conf struct. + */ + +static int stli_parsebrd(struct stlconf *confp, char **argp) +{ + unsigned int i; + char *sp; + + if (argp[0] == 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(KERN_WARNING "istallion: unknown board name, %s?\n", argp[0]); + return 0; + } + + confp->brdtype = stli_brdstr[i].type; + if (argp[1] != NULL && *argp[1] != 0) + confp->ioaddr1 = simple_strtoul(argp[1], NULL, 0); + if (argp[2] != NULL && *argp[2] != 0) + confp->memaddr = simple_strtoul(argp[2], NULL, 0); + return(1); +} + +/*****************************************************************************/ + +/* + * 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. + * + * Locking: protected by the port mutex. + */ + +static int stli_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct stliport *portp = container_of(port, struct stliport, port); + struct stlibrd *brdp = stli_brds[portp->brdnr]; + int rc; + + if ((rc = stli_initopen(tty, brdp, portp)) >= 0) + clear_bit(TTY_IO_ERROR, &tty->flags); + wake_up_interruptible(&portp->raw_wait); + return rc; +} + +static int stli_open(struct tty_struct *tty, struct file *filp) +{ + struct stlibrd *brdp; + struct stliport *portp; + unsigned int minordev, brdnr, portnr; + + minordev = tty->index; + brdnr = MINOR2BRD(minordev); + if (brdnr >= stli_nrbrds) + return -ENODEV; + brdp = stli_brds[brdnr]; + if (brdp == NULL) + return -ENODEV; + if (!test_bit(BST_STARTED, &brdp->state)) + return -ENODEV; + portnr = MINOR2PORT(minordev); + if (portnr > brdp->nrports) + return -ENODEV; + + portp = brdp->ports[portnr]; + if (portp == NULL) + return -ENODEV; + if (portp->devnr < 1) + return -ENODEV; + + tty->driver_data = portp; + return tty_port_open(&portp->port, tty, filp); +} + + +/*****************************************************************************/ + +static void stli_shutdown(struct tty_port *port) +{ + struct stlibrd *brdp; + unsigned long ftype; + unsigned long flags; + struct stliport *portp = container_of(port, struct stliport, port); + + if (portp->brdnr >= stli_nrbrds) + return; + brdp = stli_brds[portp->brdnr]; + if (brdp == NULL) + return; + + /* + * 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 (!test_bit(ST_CLOSING, &portp->state)) + stli_rawclose(brdp, portp, 0, 0); + + spin_lock_irqsave(&stli_lock, flags); + clear_bit(ST_TXBUSY, &portp->state); + clear_bit(ST_RXSTOP, &portp->state); + spin_unlock_irqrestore(&stli_lock, flags); + + ftype = FLUSHTX | FLUSHRX; + stli_cmdwait(brdp, portp, A_FLUSH, &ftype, sizeof(u32), 0); +} + +static void stli_close(struct tty_struct *tty, struct file *filp) +{ + struct stliport *portp = tty->driver_data; + unsigned long flags; + if (portp == NULL) + return; + spin_lock_irqsave(&stli_lock, flags); + /* Flush any internal buffering out first */ + if (tty == stli_txcooktty) + stli_flushchars(tty); + spin_unlock_irqrestore(&stli_lock, flags); + tty_port_close(&portp->port, tty, filp); +} + +/*****************************************************************************/ + +/* + * 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(struct tty_struct *tty, + struct stlibrd *brdp, struct stliport *portp) +{ + asynotify_t nt; + asyport_t aport; + int rc; + + 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; + + stli_mkasyport(tty, 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(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait) +{ + cdkhdr_t __iomem *hdrp; + cdkctrl_t __iomem *cp; + unsigned char __iomem *bits; + unsigned long flags; + int rc; + +/* + * Send a message to the slave to open this port. + */ + +/* + * 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_tty(portp->raw_wait, + !test_bit(ST_CLOSING, &portp->state)); + if (signal_pending(current)) { + 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. + */ + spin_lock_irqsave(&brd_lock, flags); + EBRDENABLE(brdp); + cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; + writel(arg, &cp->openarg); + writeb(1, &cp->open); + hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); + bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset + + portp->portidx; + writeb(readb(bits) | portp->portbit, bits); + EBRDDISABLE(brdp); + + if (wait == 0) { + spin_unlock_irqrestore(&brd_lock, 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); + spin_unlock_irqrestore(&brd_lock, flags); + + wait_event_interruptible_tty(portp->raw_wait, + !test_bit(ST_OPENING, &portp->state)); + if (signal_pending(current)) + rc = -ERESTARTSYS; + + 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(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait) +{ + cdkhdr_t __iomem *hdrp; + cdkctrl_t __iomem *cp; + unsigned char __iomem *bits; + unsigned long flags; + int rc; + +/* + * Slave is already closing this port. This can happen if a hangup + * occurs on this port. + */ + if (wait) { + wait_event_interruptible_tty(portp->raw_wait, + !test_bit(ST_CLOSING, &portp->state)); + if (signal_pending(current)) { + return -ERESTARTSYS; + } + } + +/* + * Write the close command into shared memory. + */ + spin_lock_irqsave(&brd_lock, flags); + EBRDENABLE(brdp); + cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; + writel(arg, &cp->closearg); + writeb(1, &cp->close); + hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); + bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset + + portp->portidx; + writeb(readb(bits) |portp->portbit, bits); + EBRDDISABLE(brdp); + + set_bit(ST_CLOSING, &portp->state); + spin_unlock_irqrestore(&brd_lock, flags); + + if (wait == 0) + return 0; + +/* + * Slave is in action, so now we must wait for the open acknowledgment + * to come back. + */ + rc = 0; + wait_event_interruptible_tty(portp->raw_wait, + !test_bit(ST_CLOSING, &portp->state)); + if (signal_pending(current)) + rc = -ERESTARTSYS; + + 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(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback) +{ + /* + * no need for wait_event_tty because clearing ST_CMDING cannot block + * on BTM + */ + wait_event_interruptible(portp->raw_wait, + !test_bit(ST_CMDING, &portp->state)); + if (signal_pending(current)) + 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)) + return -ERESTARTSYS; + + 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(struct tty_struct *tty) +{ + struct stliport *portp = tty->driver_data; + struct stlibrd *brdp; + asyport_t aport; + + if (portp == NULL) + return -ENODEV; + if (portp->brdnr >= stli_nrbrds) + return -ENODEV; + brdp = stli_brds[portp->brdnr]; + if (brdp == NULL) + return -ENODEV; + + stli_mkasyport(tty, portp, &aport, tty->termios); + return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)); +} + +/*****************************************************************************/ + +static int stli_carrier_raised(struct tty_port *port) +{ + struct stliport *portp = container_of(port, struct stliport, port); + return (portp->sigs & TIOCM_CD) ? 1 : 0; +} + +static void stli_dtr_rts(struct tty_port *port, int on) +{ + struct stliport *portp = container_of(port, struct stliport, port); + struct stlibrd *brdp = stli_brds[portp->brdnr]; + stli_mkasysigs(&portp->asig, on, on); + if (stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, + sizeof(asysigs_t), 0) < 0) + printk(KERN_WARNING "istallion: dtr set failed.\n"); +} + + +/*****************************************************************************/ + +/* + * 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) +{ + cdkasy_t __iomem *ap; + cdkhdr_t __iomem *hdrp; + unsigned char __iomem *bits; + unsigned char __iomem *shbuf; + unsigned char *chbuf; + struct stliport *portp; + struct stlibrd *brdp; + unsigned int len, stlen, head, tail, size; + unsigned long flags; + + if (tty == stli_txcooktty) + stli_flushchars(tty); + portp = tty->driver_data; + if (portp == NULL) + return 0; + if (portp->brdnr >= stli_nrbrds) + return 0; + brdp = stli_brds[portp->brdnr]; + if (brdp == NULL) + return 0; + chbuf = (unsigned char *) buf; + +/* + * All data is now local, shove as much as possible into shared memory. + */ + spin_lock_irqsave(&brd_lock, flags); + EBRDENABLE(brdp); + ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr); + head = (unsigned int) readw(&ap->txq.head); + tail = (unsigned int) readw(&ap->txq.tail); + if (tail != ((unsigned int) readw(&ap->txq.tail))) + tail = (unsigned int) readw(&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, (unsigned int)count); + count = 0; + shbuf = (char __iomem *) EBRDGETMEMPTR(brdp, portp->txoffset); + + while (len > 0) { + stlen = min(len, stlen); + memcpy_toio(shbuf + head, chbuf, stlen); + chbuf += stlen; + len -= stlen; + count += stlen; + head += stlen; + if (head >= size) { + head = 0; + stlen = tail; + } + } + + ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr); + writew(head, &ap->txq.head); + if (test_bit(ST_TXBUSY, &portp->state)) { + if (readl(&ap->changed.data) & DT_TXEMPTY) + writel(readl(&ap->changed.data) & ~DT_TXEMPTY, &ap->changed.data); + } + hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); + bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset + + portp->portidx; + writeb(readb(bits) | portp->portbit, bits); + set_bit(ST_TXBUSY, &portp->state); + EBRDDISABLE(brdp); + spin_unlock_irqrestore(&brd_lock, 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 int stli_putchar(struct tty_struct *tty, unsigned char ch) +{ + if (tty != stli_txcooktty) { + if (stli_txcooktty != NULL) + stli_flushchars(stli_txcooktty); + stli_txcooktty = tty; + } + + stli_txcookbuf[stli_txcooksize++] = ch; + return 0; +} + +/*****************************************************************************/ + +/* + * 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) +{ + cdkhdr_t __iomem *hdrp; + unsigned char __iomem *bits; + cdkasy_t __iomem *ap; + struct tty_struct *cooktty; + struct stliport *portp; + struct stlibrd *brdp; + unsigned int len, stlen, head, tail, size, count, cooksize; + unsigned char *buf; + unsigned char __iomem *shbuf; + unsigned long flags; + + cooksize = stli_txcooksize; + cooktty = stli_txcooktty; + stli_txcooksize = 0; + stli_txcookrealsize = 0; + stli_txcooktty = NULL; + + if (cooktty == NULL) + return; + if (tty != cooktty) + tty = cooktty; + if (cooksize == 0) + return; + + portp = tty->driver_data; + if (portp == NULL) + return; + if (portp->brdnr >= stli_nrbrds) + return; + brdp = stli_brds[portp->brdnr]; + if (brdp == NULL) + return; + + spin_lock_irqsave(&brd_lock, flags); + EBRDENABLE(brdp); + + ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr); + head = (unsigned int) readw(&ap->txq.head); + tail = (unsigned int) readw(&ap->txq.tail); + if (tail != ((unsigned int) readw(&ap->txq.tail))) + tail = (unsigned int) readw(&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 = EBRDGETMEMPTR(brdp, portp->txoffset); + buf = stli_txcookbuf; + + while (len > 0) { + stlen = min(len, stlen); + memcpy_toio(shbuf + head, buf, stlen); + buf += stlen; + len -= stlen; + count += stlen; + head += stlen; + if (head >= size) { + head = 0; + stlen = tail; + } + } + + ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr); + writew(head, &ap->txq.head); + + if (test_bit(ST_TXBUSY, &portp->state)) { + if (readl(&ap->changed.data) & DT_TXEMPTY) + writel(readl(&ap->changed.data) & ~DT_TXEMPTY, &ap->changed.data); + } + hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); + bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset + + portp->portidx; + writeb(readb(bits) | portp->portbit, bits); + set_bit(ST_TXBUSY, &portp->state); + + EBRDDISABLE(brdp); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +static int stli_writeroom(struct tty_struct *tty) +{ + cdkasyrq_t __iomem *rp; + struct stliport *portp; + struct stlibrd *brdp; + unsigned int head, tail, len; + unsigned long flags; + + if (tty == stli_txcooktty) { + if (stli_txcookrealsize != 0) { + len = stli_txcookrealsize - stli_txcooksize; + return len; + } + } + + portp = tty->driver_data; + if (portp == NULL) + return 0; + if (portp->brdnr >= stli_nrbrds) + return 0; + brdp = stli_brds[portp->brdnr]; + if (brdp == NULL) + return 0; + + spin_lock_irqsave(&brd_lock, flags); + EBRDENABLE(brdp); + rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->txq; + head = (unsigned int) readw(&rp->head); + tail = (unsigned int) readw(&rp->tail); + if (tail != ((unsigned int) readw(&rp->tail))) + tail = (unsigned int) readw(&rp->tail); + len = (head >= tail) ? (portp->txsize - (head - tail)) : (tail - head); + len--; + EBRDDISABLE(brdp); + spin_unlock_irqrestore(&brd_lock, 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) +{ + cdkasyrq_t __iomem *rp; + struct stliport *portp; + struct stlibrd *brdp; + unsigned int head, tail, len; + unsigned long flags; + + if (tty == stli_txcooktty) + stli_flushchars(tty); + portp = tty->driver_data; + if (portp == NULL) + return 0; + if (portp->brdnr >= stli_nrbrds) + return 0; + brdp = stli_brds[portp->brdnr]; + if (brdp == NULL) + return 0; + + spin_lock_irqsave(&brd_lock, flags); + EBRDENABLE(brdp); + rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->txq; + head = (unsigned int) readw(&rp->head); + tail = (unsigned int) readw(&rp->tail); + if (tail != ((unsigned int) readw(&rp->tail))) + tail = (unsigned int) readw(&rp->tail); + len = (head >= tail) ? (head - tail) : (portp->txsize - (tail - head)); + if ((len == 0) && test_bit(ST_TXBUSY, &portp->state)) + len = 1; + EBRDDISABLE(brdp); + spin_unlock_irqrestore(&brd_lock, flags); + + return len; +} + +/*****************************************************************************/ + +/* + * Generate the serial struct info. + */ + +static int stli_getserial(struct stliport *portp, struct serial_struct __user *sp) +{ + struct serial_struct sio; + struct stlibrd *brdp; + + memset(&sio, 0, sizeof(struct serial_struct)); + sio.type = PORT_UNKNOWN; + sio.line = portp->portnr; + sio.irq = 0; + sio.flags = portp->port.flags; + sio.baud_base = portp->baud_base; + sio.close_delay = portp->port.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 != 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(struct tty_struct *tty, struct serial_struct __user *sp) +{ + struct serial_struct sio; + int rc; + struct stliport *portp = tty->driver_data; + + 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->port.close_delay) || + ((sio.flags & ~ASYNC_USR_MASK) != + (portp->port.flags & ~ASYNC_USR_MASK))) + return -EPERM; + } + + portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) | + (sio.flags & ASYNC_USR_MASK); + portp->baud_base = sio.baud_base; + portp->port.close_delay = sio.close_delay; + portp->closing_wait = sio.closing_wait; + portp->custom_divisor = sio.custom_divisor; + + if ((rc = stli_setport(tty)) < 0) + return rc; + return 0; +} + +/*****************************************************************************/ + +static int stli_tiocmget(struct tty_struct *tty) +{ + struct stliport *portp = tty->driver_data; + struct stlibrd *brdp; + int rc; + + if (portp == NULL) + return -ENODEV; + if (portp->brdnr >= stli_nrbrds) + return 0; + brdp = stli_brds[portp->brdnr]; + if (brdp == 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, + unsigned int set, unsigned int clear) +{ + struct stliport *portp = tty->driver_data; + struct stlibrd *brdp; + int rts = -1, dtr = -1; + + if (portp == NULL) + return -ENODEV; + if (portp->brdnr >= stli_nrbrds) + return 0; + brdp = stli_brds[portp->brdnr]; + if (brdp == 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, unsigned int cmd, unsigned long arg) +{ + struct stliport *portp; + struct stlibrd *brdp; + int rc; + void __user *argp = (void __user *)arg; + + portp = tty->driver_data; + if (portp == NULL) + return -ENODEV; + if (portp->brdnr >= stli_nrbrds) + return 0; + brdp = stli_brds[portp->brdnr]; + if (brdp == 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 TIOCGSERIAL: + rc = stli_getserial(portp, argp); + break; + case TIOCSSERIAL: + rc = stli_setserial(tty, 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(tty); + break; + case COM_GETPORTSTATS: + rc = stli_getportstats(tty, 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 ktermios *old) +{ + struct stliport *portp; + struct stlibrd *brdp; + struct ktermios *tiosp; + asyport_t aport; + + portp = tty->driver_data; + if (portp == NULL) + return; + if (portp->brdnr >= stli_nrbrds) + return; + brdp = stli_brds[portp->brdnr]; + if (brdp == NULL) + return; + + tiosp = tty->termios; + + stli_mkasyport(tty, 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->port.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) +{ + struct stliport *portp = tty->driver_data; + if (portp == 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) +{ + struct stliport *portp = tty->driver_data; + if (portp == NULL) + return; + clear_bit(ST_RXSTOP, &portp->state); +} + +/*****************************************************************************/ + +/* + * Stop the transmitter. + */ + +static void stli_stop(struct tty_struct *tty) +{ +} + +/*****************************************************************************/ + +/* + * Start the transmitter again. + */ + +static void stli_start(struct tty_struct *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) +{ + struct stliport *portp = tty->driver_data; + tty_port_hangup(&portp->port); +} + +/*****************************************************************************/ + +/* + * 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) +{ + struct stliport *portp; + struct stlibrd *brdp; + unsigned long ftype, flags; + + portp = tty->driver_data; + if (portp == NULL) + return; + if (portp->brdnr >= stli_nrbrds) + return; + brdp = stli_brds[portp->brdnr]; + if (brdp == NULL) + return; + + spin_lock_irqsave(&brd_lock, flags); + if (tty == stli_txcooktty) { + stli_txcooktty = 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(u32), 0); + } + spin_unlock_irqrestore(&brd_lock, flags); + tty_wakeup(tty); +} + +/*****************************************************************************/ + +static int stli_breakctl(struct tty_struct *tty, int state) +{ + struct stlibrd *brdp; + struct stliport *portp; + long arg; + + portp = tty->driver_data; + if (portp == NULL) + return -EINVAL; + if (portp->brdnr >= stli_nrbrds) + return -EINVAL; + brdp = stli_brds[portp->brdnr]; + if (brdp == NULL) + return -EINVAL; + + arg = (state == -1) ? BREAKON : BREAKOFF; + stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0); + return 0; +} + +/*****************************************************************************/ + +static void stli_waituntilsent(struct tty_struct *tty, int timeout) +{ + struct stliport *portp; + unsigned long tend; + + portp = tty->driver_data; + if (portp == 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) +{ + struct stlibrd *brdp; + struct stliport *portp; + asyctrl_t actrl; + + portp = tty->driver_data; + if (portp == NULL) + return; + if (portp->brdnr >= stli_nrbrds) + return; + brdp = stli_brds[portp->brdnr]; + if (brdp == 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); +} + +static void stli_portinfo(struct seq_file *m, struct stlibrd *brdp, struct stliport *portp, int portnr) +{ + char *uart; + int rc; + + rc = stli_portcmdstats(NULL, portp); + + uart = "UNKNOWN"; + if (test_bit(BST_STARTED, &brdp->state)) { + switch (stli_comstats.hwid) { + case 0: uart = "2681"; break; + case 1: uart = "SC26198"; break; + default:uart = "CD1400"; break; + } + } + seq_printf(m, "%d: uart:%s ", portnr, uart); + + if (test_bit(BST_STARTED, &brdp->state) && rc >= 0) { + char sep; + + seq_printf(m, "tx:%d rx:%d", (int) stli_comstats.txtotal, + (int) stli_comstats.rxtotal); + + if (stli_comstats.rxframing) + seq_printf(m, " fe:%d", + (int) stli_comstats.rxframing); + if (stli_comstats.rxparity) + seq_printf(m, " pe:%d", + (int) stli_comstats.rxparity); + if (stli_comstats.rxbreaks) + seq_printf(m, " brk:%d", + (int) stli_comstats.rxbreaks); + if (stli_comstats.rxoverrun) + seq_printf(m, " oe:%d", + (int) stli_comstats.rxoverrun); + + sep = ' '; + if (stli_comstats.signals & TIOCM_RTS) { + seq_printf(m, "%c%s", sep, "RTS"); + sep = '|'; + } + if (stli_comstats.signals & TIOCM_CTS) { + seq_printf(m, "%c%s", sep, "CTS"); + sep = '|'; + } + if (stli_comstats.signals & TIOCM_DTR) { + seq_printf(m, "%c%s", sep, "DTR"); + sep = '|'; + } + if (stli_comstats.signals & TIOCM_CD) { + seq_printf(m, "%c%s", sep, "DCD"); + sep = '|'; + } + if (stli_comstats.signals & TIOCM_DSR) { + seq_printf(m, "%c%s", sep, "DSR"); + sep = '|'; + } + } + seq_putc(m, '\n'); +} + +/*****************************************************************************/ + +/* + * Port info, read from the /proc file system. + */ + +static int stli_proc_show(struct seq_file *m, void *v) +{ + struct stlibrd *brdp; + struct stliport *portp; + unsigned int brdnr, portnr, totalport; + + totalport = 0; + + seq_printf(m, "%s: version %s\n", stli_drvtitle, stli_drvversion); + +/* + * 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 == NULL) + continue; + if (brdp->state == 0) + continue; + + totalport = brdnr * STL_MAXPORTS; + for (portnr = 0; (portnr < brdp->nrports); portnr++, + totalport++) { + portp = brdp->ports[portnr]; + if (portp == NULL) + continue; + stli_portinfo(m, brdp, portp, totalport); + } + } + return 0; +} + +static int stli_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, stli_proc_show, NULL); +} + +static const struct file_operations stli_proc_fops = { + .owner = THIS_MODULE, + .open = stli_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/*****************************************************************************/ + +/* + * 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. + * + * The caller must hold the brd_lock (see also stli_sendcmd the usual + * entry point) + */ + +static void __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback) +{ + cdkhdr_t __iomem *hdrp; + cdkctrl_t __iomem *cp; + unsigned char __iomem *bits; + + if (test_bit(ST_CMDING, &portp->state)) { + printk(KERN_ERR "istallion: command already busy, cmd=%x!\n", + (int) cmd); + return; + } + + EBRDENABLE(brdp); + cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; + if (size > 0) { + memcpy_toio((void __iomem *) &(cp->args[0]), arg, size); + if (copyback) { + portp->argp = arg; + portp->argsize = size; + } + } + writel(0, &cp->status); + writel(cmd, &cp->cmd); + hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); + bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset + + portp->portidx; + writeb(readb(bits) | portp->portbit, bits); + set_bit(ST_CMDING, &portp->state); + EBRDDISABLE(brdp); +} + +static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback) +{ + unsigned long flags; + + spin_lock_irqsave(&brd_lock, flags); + __stli_sendcmd(brdp, portp, cmd, arg, size, copyback); + spin_unlock_irqrestore(&brd_lock, 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(struct stlibrd *brdp, struct stliport *portp) +{ + cdkasyrq_t __iomem *rp; + char __iomem *shbuf; + struct tty_struct *tty; + unsigned int head, tail, size; + unsigned int len, stlen; + + if (test_bit(ST_RXSTOP, &portp->state)) + return; + tty = tty_port_tty_get(&portp->port); + if (tty == NULL) + return; + + rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->rxq; + head = (unsigned int) readw(&rp->head); + if (head != ((unsigned int) readw(&rp->head))) + head = (unsigned int) readw(&rp->head); + tail = (unsigned int) readw(&rp->tail); + size = portp->rxsize; + if (head >= tail) { + len = head - tail; + stlen = len; + } else { + len = size - (tail - head); + stlen = size - tail; + } + + len = tty_buffer_request_room(tty, len); + + shbuf = (char __iomem *) EBRDGETMEMPTR(brdp, portp->rxoffset); + + while (len > 0) { + unsigned char *cptr; + + stlen = min(len, stlen); + tty_prepare_flip_string(tty, &cptr, stlen); + memcpy_fromio(cptr, shbuf + tail, stlen); + len -= stlen; + tail += stlen; + if (tail >= size) { + tail = 0; + stlen = head; + } + } + rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->rxq; + writew(tail, &rp->tail); + + if (head != tail) + set_bit(ST_RXING, &portp->state); + + tty_schedule_flip(tty); + tty_kref_put(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(struct stliport *portp, cdkctrl_t __iomem *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_toio((void __iomem *) &(cp->args[0]), (void *) &portp->asig, + sizeof(asysigs_t)); + writel(0, &cp->status); + writel(cmd, &cp->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_toio((void __iomem *) &(cp->args[0]), (void *) &cmd, sizeof(int)); + writel(0, &cp->status); + writel(A_FLUSH, &cp->cmd); + 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(struct stlibrd *brdp, struct stliport *portp) +{ + cdkasy_t __iomem *ap; + cdkctrl_t __iomem *cp; + struct tty_struct *tty; + asynotify_t nt; + unsigned long oldsigs; + int rc, donerx; + + ap = (cdkasy_t __iomem *) 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 = readl(&cp->openarg); + if (readb(&cp->open) == 0 && rc != 0) { + if (rc > 0) + rc--; + writel(0, &cp->openarg); + 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) readl(&cp->closearg); + if (readb(&cp->close) == 0 && rc != 0) { + if (rc > 0) + rc--; + writel(0, &cp->closearg); + 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 = readl(&cp->status); + if (readl(&cp->cmd) == 0 && rc != 0) { + if (rc > 0) + rc--; + if (portp->argp != NULL) { + memcpy_fromio(portp->argp, (void __iomem *) &(cp->args[0]), + portp->argsize); + portp->argp = NULL; + } + writel(0, &cp->status); + 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 = tty_port_tty_get(&portp->port); + + 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->port.open_wait); + if ((oldsigs & TIOCM_CD) && + ((portp->sigs & TIOCM_CD) == 0)) { + if (portp->port.flags & ASYNC_CHECK_CD) { + if (tty) + tty_hangup(tty); + } + } + } + + if (nt.data & DT_TXEMPTY) + clear_bit(ST_TXBUSY, &portp->state); + if (nt.data & (DT_TXEMPTY | DT_TXLOW)) { + if (tty != NULL) { + tty_wakeup(tty); + EBRDENABLE(brdp); + } + } + + if ((nt.data & DT_RXBREAK) && (portp->rxmarkmsk & BRKINT)) { + if (tty != NULL) { + tty_insert_flip_char(tty, 0, TTY_BREAK); + if (portp->port.flags & ASYNC_SAK) { + do_SAK(tty); + EBRDENABLE(brdp); + } + tty_schedule_flip(tty); + } + } + tty_kref_put(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(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp) +{ + struct stliport *portp; + unsigned char hostbits[(STL_MAXCHANS / 8) + 1]; + unsigned char slavebits[(STL_MAXCHANS / 8) + 1]; + unsigned char __iomem *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_fromio(&hostbits[0], (((unsigned char __iomem *) 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 = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); + slavep = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset; + for (bitpos = 0; (bitpos < bitsize); bitpos++) { + if (readb(slavebits + bitpos)) + writeb(readb(slavep + bitpos) & ~slavebits[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) +{ + cdkhdr_t __iomem *hdrp; + struct stlibrd *brdp; + unsigned int brdnr; + + mod_timer(&stli_timerlist, STLI_TIMEOUT); + +/* + * Check each board and do any servicing required. + */ + for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) { + brdp = stli_brds[brdnr]; + if (brdp == NULL) + continue; + if (!test_bit(BST_STARTED, &brdp->state)) + continue; + + spin_lock(&brd_lock); + EBRDENABLE(brdp); + hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); + if (readb(&hdrp->hostreq)) + stli_brdpoll(brdp, hdrp); + EBRDDISABLE(brdp); + spin_unlock(&brd_lock); + } +} + +/*****************************************************************************/ + +/* + * Translate the termios settings into the port setting structure of + * the slave. + */ + +static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp, + asyport_t *pp, struct ktermios *tiosp) +{ + memset(pp, 0, sizeof(asyport_t)); + +/* + * Start of by setting the baud, char size, parity and stop bit info. + */ + pp->baudout = tty_get_baud_rate(tty); + if ((tiosp->c_cflag & CBAUD) == B38400) { + if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + pp->baudout = 57600; + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + pp->baudout = 115200; + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + pp->baudout = 230400; + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + pp->baudout = 460800; + else if ((portp->port.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->port.flags &= ~ASYNC_CHECK_CD; + else + portp->port.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) +{ + 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 = 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(struct stlibrd *brdp) +{ + struct stliport *portp; + unsigned int i, panelnr, panelport; + + for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) { + portp = kzalloc(sizeof(struct stliport), GFP_KERNEL); + if (!portp) { + printk(KERN_WARNING "istallion: failed to allocate port structure\n"); + continue; + } + tty_port_init(&portp->port); + portp->port.ops = &stli_port_ops; + portp->magic = STLI_PORTMAGIC; + portp->portnr = i; + portp->brdnr = brdp->brdnr; + portp->panelnr = panelnr; + portp->baud_base = STL_BAUDBASE; + portp->port.close_delay = STL_CLOSEDELAY; + portp->closing_wait = 30 * HZ; + init_waitqueue_head(&portp->port.open_wait); + init_waitqueue_head(&portp->port.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(struct stlibrd *brdp) +{ + unsigned long memconf; + + 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(struct stlibrd *brdp) +{ + outb(ECP_ATENABLE, (brdp->iobase + ECP_ATCONFR)); +} + +/*****************************************************************************/ + +static void stli_ecpdisable(struct stlibrd *brdp) +{ + outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR)); +} + +/*****************************************************************************/ + +static void __iomem *stli_ecpgetmemptr(struct stlibrd *brdp, unsigned long offset, int line) +{ + void __iomem *ptr; + unsigned char val; + + if (offset > brdp->memsize) { + printk(KERN_ERR "istallion: 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(struct stlibrd *brdp) +{ + outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR)); + udelay(10); + outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR)); + udelay(500); +} + +/*****************************************************************************/ + +static void stli_ecpintr(struct stlibrd *brdp) +{ + outb(0x1, brdp->iobase); +} + +/*****************************************************************************/ + +/* + * The following set of functions act on ECP EISA boards. + */ + +static void stli_ecpeiinit(struct stlibrd *brdp) +{ + unsigned long memconf; + + 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(struct stlibrd *brdp) +{ + outb(ECP_EIENABLE, (brdp->iobase + ECP_EICONFR)); +} + +/*****************************************************************************/ + +static void stli_ecpeidisable(struct stlibrd *brdp) +{ + outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR)); +} + +/*****************************************************************************/ + +static void __iomem *stli_ecpeigetmemptr(struct stlibrd *brdp, unsigned long offset, int line) +{ + void __iomem *ptr; + unsigned char val; + + if (offset > brdp->memsize) { + printk(KERN_ERR "istallion: 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(struct stlibrd *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(struct stlibrd *brdp) +{ + outb(ECP_MCENABLE, (brdp->iobase + ECP_MCCONFR)); +} + +/*****************************************************************************/ + +static void stli_ecpmcdisable(struct stlibrd *brdp) +{ + outb(ECP_MCDISABLE, (brdp->iobase + ECP_MCCONFR)); +} + +/*****************************************************************************/ + +static void __iomem *stli_ecpmcgetmemptr(struct stlibrd *brdp, unsigned long offset, int line) +{ + void __iomem *ptr; + unsigned char val; + + if (offset > brdp->memsize) { + printk(KERN_ERR "istallion: 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(struct stlibrd *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(struct stlibrd *brdp) +{ + outb(ECP_PCISTOP, (brdp->iobase + ECP_PCICONFR)); + udelay(10); + outb(0, (brdp->iobase + ECP_PCICONFR)); + udelay(500); +} + +/*****************************************************************************/ + +static void __iomem *stli_ecppcigetmemptr(struct stlibrd *brdp, unsigned long offset, int line) +{ + void __iomem *ptr; + unsigned char val; + + if (offset > brdp->memsize) { + printk(KERN_ERR "istallion: 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(struct stlibrd *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(struct stlibrd *brdp) +{ + unsigned long memconf; + + 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(struct stlibrd *brdp) +{ + outb((brdp->enabval | ONB_ATENABLE), (brdp->iobase + ONB_ATCONFR)); +} + +/*****************************************************************************/ + +static void stli_onbdisable(struct stlibrd *brdp) +{ + outb((brdp->enabval | ONB_ATDISABLE), (brdp->iobase + ONB_ATCONFR)); +} + +/*****************************************************************************/ + +static void __iomem *stli_onbgetmemptr(struct stlibrd *brdp, unsigned long offset, int line) +{ + void __iomem *ptr; + + if (offset > brdp->memsize) { + printk(KERN_ERR "istallion: 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(struct stlibrd *brdp) +{ + 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(struct stlibrd *brdp) +{ + unsigned long memconf; + + 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(struct stlibrd *brdp) +{ + outb(ONB_EIENABLE, (brdp->iobase + ONB_EICONFR)); +} + +/*****************************************************************************/ + +static void stli_onbedisable(struct stlibrd *brdp) +{ + outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR)); +} + +/*****************************************************************************/ + +static void __iomem *stli_onbegetmemptr(struct stlibrd *brdp, unsigned long offset, int line) +{ + void __iomem *ptr; + unsigned char val; + + if (offset > brdp->memsize) { + printk(KERN_ERR "istallion: 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(struct stlibrd *brdp) +{ + 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(struct stlibrd *brdp) +{ + outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR)); + udelay(10); + outb(0, (brdp->iobase + BBY_ATCONFR)); + mdelay(1000); + outb(0x1, brdp->iobase); + mdelay(1); +} + +/*****************************************************************************/ + +static void __iomem *stli_bbygetmemptr(struct stlibrd *brdp, unsigned long offset, int line) +{ + void __iomem *ptr; + unsigned char val; + + BUG_ON(offset > brdp->memsize); + + ptr = brdp->membase + (offset % BBY_PAGESIZE); + val = (unsigned char) (offset / BBY_PAGESIZE); + outb(val, (brdp->iobase + BBY_ATCONFR)); + return(ptr); +} + +/*****************************************************************************/ + +static void stli_bbyreset(struct stlibrd *brdp) +{ + 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(struct stlibrd *brdp) +{ + outb(0x1, brdp->iobase); + mdelay(1000); +} + +/*****************************************************************************/ + +static void __iomem *stli_stalgetmemptr(struct stlibrd *brdp, unsigned long offset, int line) +{ + BUG_ON(offset > brdp->memsize); + return brdp->membase + (offset % STAL_PAGESIZE); +} + +/*****************************************************************************/ + +static void stli_stalreset(struct stlibrd *brdp) +{ + u32 __iomem *vecp; + + vecp = (u32 __iomem *) (brdp->membase + 0x30); + writel(0xffff0000, vecp); + 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(struct stlibrd *brdp) +{ + cdkecpsig_t sig; + cdkecpsig_t __iomem *sigsp; + unsigned int status, nxtid; + char *name; + int retval, panelnr, nrports; + + if ((brdp->iobase == 0) || (brdp->memaddr == 0)) { + retval = -ENODEV; + goto err; + } + + brdp->iosize = ECP_IOSIZE; + + if (!request_region(brdp->iobase, brdp->iosize, "istallion")) { + retval = -EIO; + goto err; + } + +/* + * Based on the specific board type setup the common vars to access + * and enable shared memory. Set all board specific information now + * as well. + */ + switch (brdp->brdtype) { + case BRD_ECP: + brdp->memsize = ECP_MEMSIZE; + brdp->pagesize = ECP_ATPAGESIZE; + brdp->init = stli_ecpinit; + brdp->enable = stli_ecpenable; + brdp->reenable = stli_ecpenable; + brdp->disable = stli_ecpdisable; + brdp->getmemptr = stli_ecpgetmemptr; + brdp->intr = stli_ecpintr; + brdp->reset = stli_ecpreset; + name = "serial(EC8/64)"; + break; + + case BRD_ECPE: + brdp->memsize = ECP_MEMSIZE; + brdp->pagesize = ECP_EIPAGESIZE; + brdp->init = stli_ecpeiinit; + brdp->enable = stli_ecpeienable; + brdp->reenable = stli_ecpeienable; + brdp->disable = stli_ecpeidisable; + brdp->getmemptr = stli_ecpeigetmemptr; + brdp->intr = stli_ecpintr; + brdp->reset = stli_ecpeireset; + name = "serial(EC8/64-EI)"; + break; + + case BRD_ECPMC: + brdp->memsize = ECP_MEMSIZE; + brdp->pagesize = ECP_MCPAGESIZE; + brdp->init = NULL; + brdp->enable = stli_ecpmcenable; + brdp->reenable = stli_ecpmcenable; + brdp->disable = stli_ecpmcdisable; + brdp->getmemptr = stli_ecpmcgetmemptr; + brdp->intr = stli_ecpintr; + brdp->reset = stli_ecpmcreset; + name = "serial(EC8/64-MCA)"; + break; + + case BRD_ECPPCI: + 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: + retval = -EINVAL; + goto err_reg; + } + +/* + * 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_nocache(brdp->memaddr, brdp->memsize); + if (brdp->membase == NULL) { + retval = -ENOMEM; + goto err_reg; + } + +/* + * 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 __iomem *) EBRDGETMEMPTR(brdp, CDK_SIGADDR); + memcpy_fromio(&sig, sigsp, sizeof(cdkecpsig_t)); + EBRDDISABLE(brdp); + + if (sig.magic != cpu_to_le32(ECP_MAGIC)) { + retval = -ENODEV; + goto err_unmap; + } + +/* + * 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++; + } + + + set_bit(BST_FOUND, &brdp->state); + return 0; +err_unmap: + iounmap(brdp->membase); + brdp->membase = NULL; +err_reg: + release_region(brdp->iobase, brdp->iosize); +err: + return retval; +} + +/*****************************************************************************/ + +/* + * Try to find an ONboard, Brumby or Stallion board and initialize it. + * This handles only these board types. + */ + +static int stli_initonb(struct stlibrd *brdp) +{ + cdkonbsig_t sig; + cdkonbsig_t __iomem *sigsp; + char *name; + int i, retval; + +/* + * Do a basic sanity check on the IO and memory addresses. + */ + if (brdp->iobase == 0 || brdp->memaddr == 0) { + retval = -ENODEV; + goto err; + } + + brdp->iosize = ONB_IOSIZE; + + if (!request_region(brdp->iobase, brdp->iosize, "istallion")) { + retval = -EIO; + goto err; + } + +/* + * 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_ONBOARD2: + 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->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: + 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->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: + retval = -EINVAL; + goto err_reg; + } + +/* + * 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_nocache(brdp->memaddr, brdp->memsize); + if (brdp->membase == NULL) { + retval = -ENOMEM; + goto err_reg; + } + +/* + * 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 __iomem *) EBRDGETMEMPTR(brdp, CDK_SIGADDR); + memcpy_fromio(&sig, sigsp, sizeof(cdkonbsig_t)); + EBRDDISABLE(brdp); + + if (sig.magic0 != cpu_to_le16(ONB_MAGIC0) || + sig.magic1 != cpu_to_le16(ONB_MAGIC1) || + sig.magic2 != cpu_to_le16(ONB_MAGIC2) || + sig.magic3 != cpu_to_le16(ONB_MAGIC3)) { + retval = -ENODEV; + goto err_unmap; + } + +/* + * 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; + + + set_bit(BST_FOUND, &brdp->state); + return 0; +err_unmap: + iounmap(brdp->membase); + brdp->membase = NULL; +err_reg: + release_region(brdp->iobase, brdp->iosize); +err: + return retval; +} + +/*****************************************************************************/ + +/* + * 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(struct stlibrd *brdp) +{ + cdkhdr_t __iomem *hdrp; + cdkmem_t __iomem *memp; + cdkasy_t __iomem *ap; + unsigned long flags; + unsigned int portnr, nrdevs, i; + struct stliport *portp; + int rc = 0; + u32 memoff; + + spin_lock_irqsave(&brd_lock, flags); + EBRDENABLE(brdp); + hdrp = (cdkhdr_t __iomem *) 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__, readb(&hdrp->ver_release), readb(&hdrp->ver_modification), + readb(&hdrp->ver_fix), nrdevs, (int) readl(&hdrp->memp), readl(&hdrp->hostp), + readl(&hdrp->slavep)); +#endif + + if (nrdevs < (brdp->nrports + 1)) { + printk(KERN_ERR "istallion: 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; + memoff = readl(&hdrp->memp); + if (memoff > brdp->memsize) { + printk(KERN_ERR "istallion: corrupted shared memory region?\n"); + rc = -EIO; + goto stli_donestartup; + } + memp = (cdkmem_t __iomem *) EBRDGETMEMPTR(brdp, memoff); + if (readw(&memp->dtype) != TYP_ASYNCTRL) { + printk(KERN_ERR "istallion: 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 (readw(&memp->dtype) != TYP_ASYNC) + break; + portp = brdp->ports[portnr]; + if (portp == NULL) + break; + portp->devnr = i; + portp->addr = readl(&memp->offset); + portp->reqbit = (unsigned char) (0x1 << (i * 8 / nrdevs)); + portp->portidx = (unsigned char) (i / 8); + portp->portbit = (unsigned char) (0x1 << (i % 8)); + } + + writeb(0xff, &hdrp->slavereq); + +/* + * 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 == NULL) + break; + if (portp->addr == 0) + break; + ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr); + if (ap != NULL) { + portp->rxsize = readw(&ap->rxq.size); + portp->txsize = readw(&ap->txq.size); + portp->rxoffset = readl(&ap->rxq.offset); + portp->txoffset = readl(&ap->txq.offset); + } + } + +stli_donestartup: + EBRDDISABLE(brdp); + spin_unlock_irqrestore(&brd_lock, flags); + + if (rc == 0) + set_bit(BST_STARTED, &brdp->state); + + if (! stli_timeron) { + stli_timeron++; + mod_timer(&stli_timerlist, STLI_TIMEOUT); + } + + return rc; +} + +/*****************************************************************************/ + +/* + * Probe and initialize the specified board. + */ + +static int __devinit stli_brdinit(struct stlibrd *brdp) +{ + int retval; + + switch (brdp->brdtype) { + case BRD_ECP: + case BRD_ECPE: + case BRD_ECPMC: + case BRD_ECPPCI: + retval = stli_initecp(brdp); + break; + case BRD_ONBOARD: + case BRD_ONBOARDE: + case BRD_ONBOARD2: + case BRD_BRUMBY4: + case BRD_STALLION: + retval = stli_initonb(brdp); + break; + default: + printk(KERN_ERR "istallion: board=%d is unknown board " + "type=%d\n", brdp->brdnr, brdp->brdtype); + retval = -ENODEV; + } + + if (retval) + return retval; + + stli_initports(brdp); + printk(KERN_INFO "istallion: %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; +} + +#if STLI_EISAPROBE != 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(struct stlibrd *brdp) +{ + cdkecpsig_t ecpsig, __iomem *ecpsigp; + cdkonbsig_t onbsig, __iomem *onbsigp; + int i, foundit; + +/* + * 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 = ioremap_nocache(brdp->memaddr, brdp->memsize); + if (brdp->membase == NULL) + continue; + + if (brdp->brdtype == BRD_ECPE) { + ecpsigp = stli_ecpeigetmemptr(brdp, + CDK_SIGADDR, __LINE__); + memcpy_fromio(&ecpsig, ecpsigp, sizeof(cdkecpsig_t)); + if (ecpsig.magic == cpu_to_le32(ECP_MAGIC)) + foundit = 1; + } else { + onbsigp = (cdkonbsig_t __iomem *) stli_onbegetmemptr(brdp, + CDK_SIGADDR, __LINE__); + memcpy_fromio(&onbsig, onbsigp, sizeof(cdkonbsig_t)); + if ((onbsig.magic0 == cpu_to_le16(ONB_MAGIC0)) && + (onbsig.magic1 == cpu_to_le16(ONB_MAGIC1)) && + (onbsig.magic2 == cpu_to_le16(ONB_MAGIC2)) && + (onbsig.magic3 == cpu_to_le16(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 "istallion: failed to probe shared memory " + "region for %s in EISA slot=%d\n", + stli_brdnames[brdp->brdtype], (brdp->iobase >> 12)); + return -ENODEV; + } + return 0; +} +#endif + +static int stli_getbrdnr(void) +{ + unsigned 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; +} + +#if STLI_EISAPROBE != 0 +/*****************************************************************************/ + +/* + * 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 __init stli_findeisabrds(void) +{ + struct stlibrd *brdp; + unsigned int iobase, eid, i; + int brdnr, found = 0; + +/* + * Firstly check if this is an EISA system. If this is not an EISA system then + * don't bother going any further! + */ + if (EISA_bus) + 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 == 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()) == NULL) + return found ? : -ENOMEM; + brdnr = stli_getbrdnr(); + if (brdnr < 0) + return found ? : -ENOMEM; + brdp->brdnr = (unsigned int)brdnr; + 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)); + if (stli_brdinit(brdp) < 0) { + kfree(brdp); + continue; + } + + stli_brds[brdp->brdnr] = brdp; + found++; + + for (i = 0; i < brdp->nrports; i++) + tty_register_device(stli_serial, + brdp->brdnr * STL_MAXPORTS + i, NULL); + } + + return found; +} +#else +static inline int stli_findeisabrds(void) { return 0; } +#endif + +/*****************************************************************************/ + +/* + * Find the next available board number that is free. + */ + +/*****************************************************************************/ + +/* + * We have a Stallion board. Allocate a board structure and + * initialize it. Read its IO and MEMORY resources from PCI + * configuration space. + */ + +static int __devinit stli_pciprobe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct stlibrd *brdp; + unsigned int i; + int brdnr, retval = -EIO; + + retval = pci_enable_device(pdev); + if (retval) + goto err; + brdp = stli_allocbrd(); + if (brdp == NULL) { + retval = -ENOMEM; + goto err; + } + mutex_lock(&stli_brdslock); + brdnr = stli_getbrdnr(); + if (brdnr < 0) { + printk(KERN_INFO "istallion: too many boards found, " + "maximum supported %d\n", STL_MAXBRDS); + mutex_unlock(&stli_brdslock); + retval = -EIO; + goto err_fr; + } + brdp->brdnr = (unsigned int)brdnr; + stli_brds[brdp->brdnr] = brdp; + mutex_unlock(&stli_brdslock); + brdp->brdtype = BRD_ECPPCI; +/* + * We have all resources from the board, so lets setup the actual + * board structure now. + */ + brdp->iobase = pci_resource_start(pdev, 3); + brdp->memaddr = pci_resource_start(pdev, 2); + retval = stli_brdinit(brdp); + if (retval) + goto err_null; + + set_bit(BST_PROBED, &brdp->state); + pci_set_drvdata(pdev, brdp); + + EBRDENABLE(brdp); + brdp->enable = NULL; + brdp->disable = NULL; + + for (i = 0; i < brdp->nrports; i++) + tty_register_device(stli_serial, brdp->brdnr * STL_MAXPORTS + i, + &pdev->dev); + + return 0; +err_null: + stli_brds[brdp->brdnr] = NULL; +err_fr: + kfree(brdp); +err: + return retval; +} + +static void __devexit stli_pciremove(struct pci_dev *pdev) +{ + struct stlibrd *brdp = pci_get_drvdata(pdev); + + stli_cleanup_ports(brdp); + + iounmap(brdp->membase); + if (brdp->iosize > 0) + release_region(brdp->iobase, brdp->iosize); + + stli_brds[brdp->brdnr] = NULL; + kfree(brdp); +} + +static struct pci_driver stli_pcidriver = { + .name = "istallion", + .id_table = istallion_pci_tbl, + .probe = stli_pciprobe, + .remove = __devexit_p(stli_pciremove) +}; +/*****************************************************************************/ + +/* + * Allocate a new board structure. Fill out the basic info in it. + */ + +static struct stlibrd *stli_allocbrd(void) +{ + struct stlibrd *brdp; + + brdp = kzalloc(sizeof(struct stlibrd), GFP_KERNEL); + if (!brdp) { + printk(KERN_ERR "istallion: failed to allocate memory " + "(size=%Zd)\n", sizeof(struct stlibrd)); + return NULL; + } + brdp->magic = STLI_BOARDMAGIC; + return brdp; +} + +/*****************************************************************************/ + +/* + * Scan through all the boards in the configuration and see what we + * can find. + */ + +static int __init stli_initbrds(void) +{ + struct stlibrd *brdp, *nxtbrdp; + struct stlconf conf; + unsigned int i, j, found = 0; + int retval; + + for (stli_nrbrds = 0; stli_nrbrds < ARRAY_SIZE(stli_brdsp); + stli_nrbrds++) { + memset(&conf, 0, sizeof(conf)); + if (stli_parsebrd(&conf, stli_brdsp[stli_nrbrds]) == 0) + continue; + if ((brdp = stli_allocbrd()) == NULL) + continue; + brdp->brdnr = stli_nrbrds; + brdp->brdtype = conf.brdtype; + brdp->iobase = conf.ioaddr1; + brdp->memaddr = conf.memaddr; + if (stli_brdinit(brdp) < 0) { + kfree(brdp); + continue; + } + stli_brds[brdp->brdnr] = brdp; + found++; + + for (i = 0; i < brdp->nrports; i++) + tty_register_device(stli_serial, + brdp->brdnr * STL_MAXPORTS + i, NULL); + } + + retval = stli_findeisabrds(); + if (retval > 0) + found += retval; + +/* + * 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 == NULL) + continue; + for (j = i + 1; (j < stli_nrbrds); j++) { + nxtbrdp = stli_brds[j]; + if (nxtbrdp == 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 == NULL) + continue; + if (test_bit(BST_FOUND, &brdp->state)) { + EBRDENABLE(brdp); + brdp->enable = NULL; + brdp->disable = NULL; + } + } + } + + retval = pci_register_driver(&stli_pcidriver); + if (retval && found == 0) { + printk(KERN_ERR "Neither isa nor eisa cards found nor pci " + "driver can be registered!\n"); + goto err; + } + + return 0; +err: + return retval; +} + +/*****************************************************************************/ + +/* + * 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 __iomem *memptr; + struct stlibrd *brdp; + unsigned int brdnr; + int size, n; + void *p; + loff_t off = *offp; + + brdnr = iminor(fp->f_path.dentry->d_inode); + if (brdnr >= stli_nrbrds) + return -ENODEV; + brdp = stli_brds[brdnr]; + if (brdp == NULL) + return -ENODEV; + if (brdp->state == 0) + return -ENODEV; + if (off >= brdp->memsize || off + count < off) + return 0; + + size = min(count, (size_t)(brdp->memsize - off)); + + /* + * Copy the data a page at a time + */ + + p = (void *)__get_free_page(GFP_KERNEL); + if(p == NULL) + return -ENOMEM; + + while (size > 0) { + spin_lock_irqsave(&brd_lock, flags); + EBRDENABLE(brdp); + memptr = EBRDGETMEMPTR(brdp, off); + n = min(size, (int)(brdp->pagesize - (((unsigned long) off) % brdp->pagesize))); + n = min(n, (int)PAGE_SIZE); + memcpy_fromio(p, memptr, n); + EBRDDISABLE(brdp); + spin_unlock_irqrestore(&brd_lock, flags); + if (copy_to_user(buf, p, n)) { + count = -EFAULT; + goto out; + } + off += n; + buf += n; + size -= n; + } +out: + *offp = off; + free_page((unsigned long)p); + 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 :-) + * + * FIXME: copy under lock + */ + +static ssize_t stli_memwrite(struct file *fp, const char __user *buf, size_t count, loff_t *offp) +{ + unsigned long flags; + void __iomem *memptr; + struct stlibrd *brdp; + char __user *chbuf; + unsigned int brdnr; + int size, n; + void *p; + loff_t off = *offp; + + brdnr = iminor(fp->f_path.dentry->d_inode); + + if (brdnr >= stli_nrbrds) + return -ENODEV; + brdp = stli_brds[brdnr]; + if (brdp == NULL) + return -ENODEV; + if (brdp->state == 0) + return -ENODEV; + if (off >= brdp->memsize || off + count < off) + return 0; + + chbuf = (char __user *) buf; + size = min(count, (size_t)(brdp->memsize - off)); + + /* + * Copy the data a page at a time + */ + + p = (void *)__get_free_page(GFP_KERNEL); + if(p == NULL) + return -ENOMEM; + + while (size > 0) { + n = min(size, (int)(brdp->pagesize - (((unsigned long) off) % brdp->pagesize))); + n = min(n, (int)PAGE_SIZE); + if (copy_from_user(p, chbuf, n)) { + if (count == 0) + count = -EFAULT; + goto out; + } + spin_lock_irqsave(&brd_lock, flags); + EBRDENABLE(brdp); + memptr = EBRDGETMEMPTR(brdp, off); + memcpy_toio(memptr, p, n); + EBRDDISABLE(brdp); + spin_unlock_irqrestore(&brd_lock, flags); + off += n; + chbuf += n; + size -= n; + } +out: + free_page((unsigned long) p); + *offp = off; + return count; +} + +/*****************************************************************************/ + +/* + * Return the board stats structure to user app. + */ + +static int stli_getbrdstats(combrd_t __user *bp) +{ + struct stlibrd *brdp; + unsigned 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 == 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 struct stliport *stli_getport(unsigned int brdnr, unsigned int panelnr, + unsigned int portnr) +{ + struct stlibrd *brdp; + unsigned int i; + + if (brdnr >= STL_MAXBRDS) + return NULL; + brdp = stli_brds[brdnr]; + if (brdp == NULL) + return NULL; + for (i = 0; (i < panelnr); i++) + portnr += brdp->panels[i]; + if (portnr >= brdp->nrports) + return 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(struct tty_struct *tty, struct stliport *portp) +{ + unsigned long flags; + struct stlibrd *brdp; + int rc; + + memset(&stli_comstats, 0, sizeof(comstats_t)); + + if (portp == NULL) + return -ENODEV; + brdp = stli_brds[portp->brdnr]; + if (brdp == NULL) + return -ENODEV; + + mutex_lock(&portp->port.mutex); + if (test_bit(BST_STARTED, &brdp->state)) { + if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, + &stli_cdkstats, sizeof(asystats_t), 1)) < 0) { + mutex_unlock(&portp->port.mutex); + 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->port.flags; + + spin_lock_irqsave(&brd_lock, flags); + if (tty != NULL) { + if (portp->port.tty == tty) { + stli_comstats.ttystate = tty->flags; + stli_comstats.rxbuffered = -1; + if (tty->termios != NULL) { + stli_comstats.cflags = tty->termios->c_cflag; + stli_comstats.iflags = tty->termios->c_iflag; + stli_comstats.oflags = tty->termios->c_oflag; + stli_comstats.lflags = tty->termios->c_lflag; + } + } + } + spin_unlock_irqrestore(&brd_lock, 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); + mutex_unlock(&portp->port.mutex); + + 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(struct tty_struct *tty, struct stliport *portp, + comstats_t __user *cp) +{ + struct stlibrd *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(tty, 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(struct stliport *portp, comstats_t __user *cp) +{ + struct stlibrd *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; + + mutex_lock(&portp->port.mutex); + + if (test_bit(BST_STARTED, &brdp->state)) { + if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, NULL, 0, 0)) < 0) { + mutex_unlock(&portp->port.mutex); + return rc; + } + } + + memset(&stli_comstats, 0, sizeof(comstats_t)); + stli_comstats.brd = portp->brdnr; + stli_comstats.panel = portp->panelnr; + stli_comstats.port = portp->portnr; + mutex_unlock(&portp->port.mutex); + + 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(struct stliport __user *arg) +{ + struct stliport stli_dummyport; + struct stliport *portp; + + if (copy_from_user(&stli_dummyport, arg, sizeof(struct stliport))) + 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(struct stliport))) + return -EFAULT; + return 0; +} + +/*****************************************************************************/ + +/* + * Return the entire driver board structure to a user app. + */ + +static int stli_getbrdstruct(struct stlibrd __user *arg) +{ + struct stlibrd stli_dummybrd; + struct stlibrd *brdp; + + if (copy_from_user(&stli_dummybrd, arg, sizeof(struct stlibrd))) + return -EFAULT; + if (stli_dummybrd.brdnr >= STL_MAXBRDS) + return -ENODEV; + brdp = stli_brds[stli_dummybrd.brdnr]; + if (!brdp) + return -ENODEV; + if (copy_to_user(arg, brdp, sizeof(struct stlibrd))) + 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 long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct stlibrd *brdp; + int brdnr, rc, done; + void __user *argp = (void __user *)arg; + +/* + * First up handle the board independent ioctls. + */ + done = 0; + rc = 0; + + switch (cmd) { + case COM_GETPORTSTATS: + rc = stli_getportstats(NULL, 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(fp->f_dentry->d_inode); + 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: + clear_bit(BST_STARTED, &brdp->state); + break; + case STL_BRESET: + clear_bit(BST_STARTED, &brdp->state); + EBRDRESET(brdp); + if (stli_shared == 0) { + if (brdp->reenable != NULL) + (* brdp->reenable)(brdp); + } + break; + default: + rc = -ENOIOCTLCMD; + break; + } + return rc; +} + +static const 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, + .tiocmget = stli_tiocmget, + .tiocmset = stli_tiocmset, + .proc_fops = &stli_proc_fops, +}; + +static const struct tty_port_operations stli_port_ops = { + .carrier_raised = stli_carrier_raised, + .dtr_rts = stli_dtr_rts, + .activate = stli_activate, + .shutdown = stli_shutdown, +}; + +/*****************************************************************************/ +/* + * Loadable module initialization stuff. + */ + +static void istallion_cleanup_isa(void) +{ + struct stlibrd *brdp; + unsigned int j; + + for (j = 0; (j < stli_nrbrds); j++) { + if ((brdp = stli_brds[j]) == NULL || + test_bit(BST_PROBED, &brdp->state)) + continue; + + stli_cleanup_ports(brdp); + + iounmap(brdp->membase); + if (brdp->iosize > 0) + release_region(brdp->iobase, brdp->iosize); + kfree(brdp); + stli_brds[j] = NULL; + } +} + +static int __init istallion_module_init(void) +{ + unsigned int i; + int retval; + + printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion); + + spin_lock_init(&stli_lock); + spin_lock_init(&brd_lock); + + stli_txcookbuf = kmalloc(STLI_TXBUFSIZE, GFP_KERNEL); + if (!stli_txcookbuf) { + printk(KERN_ERR "istallion: failed to allocate memory " + "(size=%d)\n", STLI_TXBUFSIZE); + retval = -ENOMEM; + goto err; + } + + stli_serial = alloc_tty_driver(STL_MAXBRDS * STL_MAXPORTS); + if (!stli_serial) { + retval = -ENOMEM; + goto err_free; + } + + 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_DRIVER_DYNAMIC_DEV; + tty_set_operations(stli_serial, &stli_ops); + + retval = tty_register_driver(stli_serial); + if (retval) { + printk(KERN_ERR "istallion: failed to register serial driver\n"); + goto err_ttyput; + } + + retval = stli_initbrds(); + if (retval) + goto err_ttyunr; + +/* + * 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. + */ + retval = register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem); + if (retval) { + printk(KERN_ERR "istallion: failed to register serial memory " + "device\n"); + goto err_deinit; + } + + istallion_class = class_create(THIS_MODULE, "staliomem"); + for (i = 0; i < 4; i++) + device_create(istallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i), + NULL, "staliomem%d", i); + + return 0; +err_deinit: + pci_unregister_driver(&stli_pcidriver); + istallion_cleanup_isa(); +err_ttyunr: + tty_unregister_driver(stli_serial); +err_ttyput: + put_tty_driver(stli_serial); +err_free: + kfree(stli_txcookbuf); +err: + return retval; +} + +/*****************************************************************************/ + +static void __exit istallion_module_exit(void) +{ + unsigned int j; + + printk(KERN_INFO "Unloading %s: version %s\n", stli_drvtitle, + stli_drvversion); + + if (stli_timeron) { + stli_timeron = 0; + del_timer_sync(&stli_timerlist); + } + + unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"); + + for (j = 0; j < 4; j++) + device_destroy(istallion_class, MKDEV(STL_SIOMEMMAJOR, j)); + class_destroy(istallion_class); + + pci_unregister_driver(&stli_pcidriver); + istallion_cleanup_isa(); + + tty_unregister_driver(stli_serial); + put_tty_driver(stli_serial); + + kfree(stli_txcookbuf); +} + +module_init(istallion_module_init); +module_exit(istallion_module_exit); diff --git a/drivers/staging/tty/riscom8.c b/drivers/staging/tty/riscom8.c new file mode 100644 index 000000000000..602643a40b4b --- /dev/null +++ b/drivers/staging/tty/riscom8.c @@ -0,0 +1,1560 @@ +/* + * linux/drivers/char/riscom.c -- RISCom/8 multiport serial driver. + * + * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) + * + * This code is loosely based on the Linux serial driver, written by + * Linus Torvalds, Theodore T'so and others. The RISCom/8 card + * programming info was obtained from various drivers for other OSes + * (FreeBSD, ISC, etc), but no source code from those drivers were + * directly included in this driver. + * + * + * 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. + * + * Revision 1.1 + * + * ChangeLog: + * Arnaldo Carvalho de Melo - 27-Jun-2001 + * - get rid of check_region and several cleanups + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "riscom8.h" +#include "riscom8_reg.h" + +/* Am I paranoid or not ? ;-) */ +#define RISCOM_PARANOIA_CHECK + +/* + * Crazy InteliCom/8 boards sometimes have swapped CTS & DSR signals. + * You can slightly speed up things by #undefing the following option, + * if you are REALLY sure that your board is correct one. + */ + +#define RISCOM_BRAIN_DAMAGED_CTS + +/* + * The following defines are mostly for testing purposes. But if you need + * some nice reporting in your syslog, you can define them also. + */ +#undef RC_REPORT_FIFO +#undef RC_REPORT_OVERRUN + + +#define RISCOM_LEGAL_FLAGS \ + (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \ + ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ + ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) + +static struct tty_driver *riscom_driver; + +static DEFINE_SPINLOCK(riscom_lock); + +static struct riscom_board rc_board[RC_NBOARD] = { + { + .base = RC_IOBASE1, + }, + { + .base = RC_IOBASE2, + }, + { + .base = RC_IOBASE3, + }, + { + .base = RC_IOBASE4, + }, +}; + +static struct riscom_port rc_port[RC_NBOARD * RC_NPORT]; + +/* RISCom/8 I/O ports addresses (without address translation) */ +static unsigned short rc_ioport[] = { +#if 1 + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c, +#else + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c, 0x10, + 0x11, 0x12, 0x18, 0x28, 0x31, 0x32, 0x39, 0x3a, 0x40, 0x41, 0x61, 0x62, + 0x63, 0x64, 0x6b, 0x70, 0x71, 0x78, 0x7a, 0x7b, 0x7f, 0x100, 0x101 +#endif +}; +#define RC_NIOPORT ARRAY_SIZE(rc_ioport) + + +static int rc_paranoia_check(struct riscom_port const *port, + char *name, const char *routine) +{ +#ifdef RISCOM_PARANOIA_CHECK + static const char badmagic[] = KERN_INFO + "rc: Warning: bad riscom port magic number for device %s in %s\n"; + static const char badinfo[] = KERN_INFO + "rc: Warning: null riscom port for device %s in %s\n"; + + if (!port) { + printk(badinfo, name, routine); + return 1; + } + if (port->magic != RISCOM8_MAGIC) { + printk(badmagic, name, routine); + return 1; + } +#endif + return 0; +} + +/* + * + * Service functions for RISCom/8 driver. + * + */ + +/* Get board number from pointer */ +static inline int board_No(struct riscom_board const *bp) +{ + return bp - rc_board; +} + +/* Get port number from pointer */ +static inline int port_No(struct riscom_port const *port) +{ + return RC_PORT(port - rc_port); +} + +/* Get pointer to board from pointer to port */ +static inline struct riscom_board *port_Board(struct riscom_port const *port) +{ + return &rc_board[RC_BOARD(port - rc_port)]; +} + +/* Input Byte from CL CD180 register */ +static inline unsigned char rc_in(struct riscom_board const *bp, + unsigned short reg) +{ + return inb(bp->base + RC_TO_ISA(reg)); +} + +/* Output Byte to CL CD180 register */ +static inline void rc_out(struct riscom_board const *bp, unsigned short reg, + unsigned char val) +{ + outb(val, bp->base + RC_TO_ISA(reg)); +} + +/* Wait for Channel Command Register ready */ +static void rc_wait_CCR(struct riscom_board const *bp) +{ + unsigned long delay; + + /* FIXME: need something more descriptive then 100000 :) */ + for (delay = 100000; delay; delay--) + if (!rc_in(bp, CD180_CCR)) + return; + + printk(KERN_INFO "rc%d: Timeout waiting for CCR.\n", board_No(bp)); +} + +/* + * RISCom/8 probe functions. + */ + +static int rc_request_io_range(struct riscom_board * const bp) +{ + int i; + + for (i = 0; i < RC_NIOPORT; i++) + if (!request_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1, + "RISCom/8")) { + goto out_release; + } + return 0; +out_release: + printk(KERN_INFO "rc%d: Skipping probe at 0x%03x. IO address in use.\n", + board_No(bp), bp->base); + while (--i >= 0) + release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1); + return 1; +} + +static void rc_release_io_range(struct riscom_board * const bp) +{ + int i; + + for (i = 0; i < RC_NIOPORT; i++) + release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1); +} + +/* Reset and setup CD180 chip */ +static void __init rc_init_CD180(struct riscom_board const *bp) +{ + unsigned long flags; + + spin_lock_irqsave(&riscom_lock, flags); + + rc_out(bp, RC_CTOUT, 0); /* Clear timeout */ + rc_wait_CCR(bp); /* Wait for CCR ready */ + rc_out(bp, CD180_CCR, CCR_HARDRESET); /* Reset CD180 chip */ + spin_unlock_irqrestore(&riscom_lock, flags); + msleep(50); /* Delay 0.05 sec */ + spin_lock_irqsave(&riscom_lock, flags); + rc_out(bp, CD180_GIVR, RC_ID); /* Set ID for this chip */ + rc_out(bp, CD180_GICR, 0); /* Clear all bits */ + rc_out(bp, CD180_PILR1, RC_ACK_MINT); /* Prio for modem intr */ + rc_out(bp, CD180_PILR2, RC_ACK_TINT); /* Prio for tx intr */ + rc_out(bp, CD180_PILR3, RC_ACK_RINT); /* Prio for rx intr */ + + /* Setting up prescaler. We need 4 ticks per 1 ms */ + rc_out(bp, CD180_PPRH, (RC_OSCFREQ/(1000000/RISCOM_TPS)) >> 8); + rc_out(bp, CD180_PPRL, (RC_OSCFREQ/(1000000/RISCOM_TPS)) & 0xff); + + spin_unlock_irqrestore(&riscom_lock, flags); +} + +/* Main probing routine, also sets irq. */ +static int __init rc_probe(struct riscom_board *bp) +{ + unsigned char val1, val2; + int irqs = 0; + int retries; + + bp->irq = 0; + + if (rc_request_io_range(bp)) + return 1; + + /* Are the I/O ports here ? */ + rc_out(bp, CD180_PPRL, 0x5a); + outb(0xff, 0x80); + val1 = rc_in(bp, CD180_PPRL); + rc_out(bp, CD180_PPRL, 0xa5); + outb(0x00, 0x80); + val2 = rc_in(bp, CD180_PPRL); + + if ((val1 != 0x5a) || (val2 != 0xa5)) { + printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not found.\n", + board_No(bp), bp->base); + goto out_release; + } + + /* It's time to find IRQ for this board */ + for (retries = 0; retries < 5 && irqs <= 0; retries++) { + irqs = probe_irq_on(); + rc_init_CD180(bp); /* Reset CD180 chip */ + rc_out(bp, CD180_CAR, 2); /* Select port 2 */ + rc_wait_CCR(bp); + rc_out(bp, CD180_CCR, CCR_TXEN); /* Enable transmitter */ + rc_out(bp, CD180_IER, IER_TXRDY);/* Enable tx empty intr */ + msleep(50); + irqs = probe_irq_off(irqs); + val1 = rc_in(bp, RC_BSR); /* Get Board Status reg */ + val2 = rc_in(bp, RC_ACK_TINT); /* ACK interrupt */ + rc_init_CD180(bp); /* Reset CD180 again */ + + if ((val1 & RC_BSR_TINT) || (val2 != (RC_ID | GIVR_IT_TX))) { + printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not " + "found.\n", board_No(bp), bp->base); + goto out_release; + } + } + + if (irqs <= 0) { + printk(KERN_ERR "rc%d: Can't find IRQ for RISCom/8 board " + "at 0x%03x.\n", board_No(bp), bp->base); + goto out_release; + } + bp->irq = irqs; + bp->flags |= RC_BOARD_PRESENT; + + printk(KERN_INFO "rc%d: RISCom/8 Rev. %c board detected at " + "0x%03x, IRQ %d.\n", + board_No(bp), + (rc_in(bp, CD180_GFRCR) & 0x0f) + 'A', /* Board revision */ + bp->base, bp->irq); + + return 0; +out_release: + rc_release_io_range(bp); + return 1; +} + +/* + * + * Interrupt processing routines. + * + */ + +static struct riscom_port *rc_get_port(struct riscom_board const *bp, + unsigned char const *what) +{ + unsigned char channel; + struct riscom_port *port; + + channel = rc_in(bp, CD180_GICR) >> GICR_CHAN_OFF; + if (channel < CD180_NCH) { + port = &rc_port[board_No(bp) * RC_NPORT + channel]; + if (port->port.flags & ASYNC_INITIALIZED) + return port; + } + printk(KERN_ERR "rc%d: %s interrupt from invalid port %d\n", + board_No(bp), what, channel); + return NULL; +} + +static void rc_receive_exc(struct riscom_board const *bp) +{ + struct riscom_port *port; + struct tty_struct *tty; + unsigned char status; + unsigned char ch, flag; + + port = rc_get_port(bp, "Receive"); + if (port == NULL) + return; + + tty = tty_port_tty_get(&port->port); + +#ifdef RC_REPORT_OVERRUN + status = rc_in(bp, CD180_RCSR); + if (status & RCSR_OE) + port->overrun++; + status &= port->mark_mask; +#else + status = rc_in(bp, CD180_RCSR) & port->mark_mask; +#endif + ch = rc_in(bp, CD180_RDR); + if (!status) + goto out; + if (status & RCSR_TOUT) { + printk(KERN_WARNING "rc%d: port %d: Receiver timeout. " + "Hardware problems ?\n", + board_No(bp), port_No(port)); + goto out; + + } else if (status & RCSR_BREAK) { + printk(KERN_INFO "rc%d: port %d: Handling break...\n", + board_No(bp), port_No(port)); + flag = TTY_BREAK; + if (tty && (port->port.flags & ASYNC_SAK)) + do_SAK(tty); + + } else if (status & RCSR_PE) + flag = TTY_PARITY; + + else if (status & RCSR_FE) + flag = TTY_FRAME; + + else if (status & RCSR_OE) + flag = TTY_OVERRUN; + else + flag = TTY_NORMAL; + + if (tty) { + tty_insert_flip_char(tty, ch, flag); + tty_flip_buffer_push(tty); + } +out: + tty_kref_put(tty); +} + +static void rc_receive(struct riscom_board const *bp) +{ + struct riscom_port *port; + struct tty_struct *tty; + unsigned char count; + + port = rc_get_port(bp, "Receive"); + if (port == NULL) + return; + + tty = tty_port_tty_get(&port->port); + + count = rc_in(bp, CD180_RDCR); + +#ifdef RC_REPORT_FIFO + port->hits[count > 8 ? 9 : count]++; +#endif + + while (count--) { + u8 ch = rc_in(bp, CD180_RDR); + if (tty) + tty_insert_flip_char(tty, ch, TTY_NORMAL); + } + if (tty) { + tty_flip_buffer_push(tty); + tty_kref_put(tty); + } +} + +static void rc_transmit(struct riscom_board const *bp) +{ + struct riscom_port *port; + struct tty_struct *tty; + unsigned char count; + + port = rc_get_port(bp, "Transmit"); + if (port == NULL) + return; + + tty = tty_port_tty_get(&port->port); + + if (port->IER & IER_TXEMPTY) { + /* FIFO drained */ + rc_out(bp, CD180_CAR, port_No(port)); + port->IER &= ~IER_TXEMPTY; + rc_out(bp, CD180_IER, port->IER); + goto out; + } + + if ((port->xmit_cnt <= 0 && !port->break_length) + || (tty && (tty->stopped || tty->hw_stopped))) { + rc_out(bp, CD180_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + rc_out(bp, CD180_IER, port->IER); + goto out; + } + + if (port->break_length) { + if (port->break_length > 0) { + if (port->COR2 & COR2_ETC) { + rc_out(bp, CD180_TDR, CD180_C_ESC); + rc_out(bp, CD180_TDR, CD180_C_SBRK); + port->COR2 &= ~COR2_ETC; + } + count = min_t(int, port->break_length, 0xff); + rc_out(bp, CD180_TDR, CD180_C_ESC); + rc_out(bp, CD180_TDR, CD180_C_DELAY); + rc_out(bp, CD180_TDR, count); + port->break_length -= count; + if (port->break_length == 0) + port->break_length--; + } else { + rc_out(bp, CD180_TDR, CD180_C_ESC); + rc_out(bp, CD180_TDR, CD180_C_EBRK); + rc_out(bp, CD180_COR2, port->COR2); + rc_wait_CCR(bp); + rc_out(bp, CD180_CCR, CCR_CORCHG2); + port->break_length = 0; + } + goto out; + } + + count = CD180_NFIFO; + do { + rc_out(bp, CD180_TDR, port->port.xmit_buf[port->xmit_tail++]); + port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); + if (--port->xmit_cnt <= 0) + break; + } while (--count > 0); + + if (port->xmit_cnt <= 0) { + rc_out(bp, CD180_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + rc_out(bp, CD180_IER, port->IER); + } + if (tty && port->xmit_cnt <= port->wakeup_chars) + tty_wakeup(tty); +out: + tty_kref_put(tty); +} + +static void rc_check_modem(struct riscom_board const *bp) +{ + struct riscom_port *port; + struct tty_struct *tty; + unsigned char mcr; + + port = rc_get_port(bp, "Modem"); + if (port == NULL) + return; + + tty = tty_port_tty_get(&port->port); + + mcr = rc_in(bp, CD180_MCR); + if (mcr & MCR_CDCHG) { + if (rc_in(bp, CD180_MSVR) & MSVR_CD) + wake_up_interruptible(&port->port.open_wait); + else if (tty) + tty_hangup(tty); + } + +#ifdef RISCOM_BRAIN_DAMAGED_CTS + if (mcr & MCR_CTSCHG) { + if (rc_in(bp, CD180_MSVR) & MSVR_CTS) { + port->IER |= IER_TXRDY; + if (tty) { + tty->hw_stopped = 0; + if (port->xmit_cnt <= port->wakeup_chars) + tty_wakeup(tty); + } + } else { + if (tty) + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + rc_out(bp, CD180_IER, port->IER); + } + if (mcr & MCR_DSRCHG) { + if (rc_in(bp, CD180_MSVR) & MSVR_DSR) { + port->IER |= IER_TXRDY; + if (tty) { + tty->hw_stopped = 0; + if (port->xmit_cnt <= port->wakeup_chars) + tty_wakeup(tty); + } + } else { + if (tty) + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + rc_out(bp, CD180_IER, port->IER); + } +#endif /* RISCOM_BRAIN_DAMAGED_CTS */ + + /* Clear change bits */ + rc_out(bp, CD180_MCR, 0); + tty_kref_put(tty); +} + +/* The main interrupt processing routine */ +static irqreturn_t rc_interrupt(int dummy, void *dev_id) +{ + unsigned char status; + unsigned char ack; + struct riscom_board *bp = dev_id; + unsigned long loop = 0; + int handled = 0; + + if (!(bp->flags & RC_BOARD_ACTIVE)) + return IRQ_NONE; + + while ((++loop < 16) && ((status = ~(rc_in(bp, RC_BSR))) & + (RC_BSR_TOUT | RC_BSR_TINT | + RC_BSR_MINT | RC_BSR_RINT))) { + handled = 1; + if (status & RC_BSR_TOUT) + printk(KERN_WARNING "rc%d: Got timeout. Hardware " + "error?\n", board_No(bp)); + else if (status & RC_BSR_RINT) { + ack = rc_in(bp, RC_ACK_RINT); + if (ack == (RC_ID | GIVR_IT_RCV)) + rc_receive(bp); + else if (ack == (RC_ID | GIVR_IT_REXC)) + rc_receive_exc(bp); + else + printk(KERN_WARNING "rc%d: Bad receive ack " + "0x%02x.\n", + board_No(bp), ack); + } else if (status & RC_BSR_TINT) { + ack = rc_in(bp, RC_ACK_TINT); + if (ack == (RC_ID | GIVR_IT_TX)) + rc_transmit(bp); + else + printk(KERN_WARNING "rc%d: Bad transmit ack " + "0x%02x.\n", + board_No(bp), ack); + } else /* if (status & RC_BSR_MINT) */ { + ack = rc_in(bp, RC_ACK_MINT); + if (ack == (RC_ID | GIVR_IT_MODEM)) + rc_check_modem(bp); + else + printk(KERN_WARNING "rc%d: Bad modem ack " + "0x%02x.\n", + board_No(bp), ack); + } + rc_out(bp, CD180_EOIR, 0); /* Mark end of interrupt */ + rc_out(bp, RC_CTOUT, 0); /* Clear timeout flag */ + } + return IRQ_RETVAL(handled); +} + +/* + * Routines for open & close processing. + */ + +/* Called with disabled interrupts */ +static int rc_setup_board(struct riscom_board *bp) +{ + int error; + + if (bp->flags & RC_BOARD_ACTIVE) + return 0; + + error = request_irq(bp->irq, rc_interrupt, IRQF_DISABLED, + "RISCom/8", bp); + if (error) + return error; + + rc_out(bp, RC_CTOUT, 0); /* Just in case */ + bp->DTR = ~0; + rc_out(bp, RC_DTR, bp->DTR); /* Drop DTR on all ports */ + + bp->flags |= RC_BOARD_ACTIVE; + + return 0; +} + +/* Called with disabled interrupts */ +static void rc_shutdown_board(struct riscom_board *bp) +{ + if (!(bp->flags & RC_BOARD_ACTIVE)) + return; + + bp->flags &= ~RC_BOARD_ACTIVE; + + free_irq(bp->irq, NULL); + + bp->DTR = ~0; + rc_out(bp, RC_DTR, bp->DTR); /* Drop DTR on all ports */ + +} + +/* + * Setting up port characteristics. + * Must be called with disabled interrupts + */ +static void rc_change_speed(struct tty_struct *tty, struct riscom_board *bp, + struct riscom_port *port) +{ + unsigned long baud; + long tmp; + unsigned char cor1 = 0, cor3 = 0; + unsigned char mcor1 = 0, mcor2 = 0; + + port->IER = 0; + port->COR2 = 0; + port->MSVR = MSVR_RTS; + + baud = tty_get_baud_rate(tty); + + /* Select port on the board */ + rc_out(bp, CD180_CAR, port_No(port)); + + if (!baud) { + /* Drop DTR & exit */ + bp->DTR |= (1u << port_No(port)); + rc_out(bp, RC_DTR, bp->DTR); + return; + } else { + /* Set DTR on */ + bp->DTR &= ~(1u << port_No(port)); + rc_out(bp, RC_DTR, bp->DTR); + } + + /* + * Now we must calculate some speed depended things + */ + + /* Set baud rate for port */ + tmp = (((RC_OSCFREQ + baud/2) / baud + + CD180_TPC/2) / CD180_TPC); + + rc_out(bp, CD180_RBPRH, (tmp >> 8) & 0xff); + rc_out(bp, CD180_TBPRH, (tmp >> 8) & 0xff); + rc_out(bp, CD180_RBPRL, tmp & 0xff); + rc_out(bp, CD180_TBPRL, tmp & 0xff); + + baud = (baud + 5) / 10; /* Estimated CPS */ + + /* Two timer ticks seems enough to wakeup something like SLIP driver */ + tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO; + port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? + SERIAL_XMIT_SIZE - 1 : tmp); + + /* Receiver timeout will be transmission time for 1.5 chars */ + tmp = (RISCOM_TPS + RISCOM_TPS/2 + baud/2) / baud; + tmp = (tmp > 0xff) ? 0xff : tmp; + rc_out(bp, CD180_RTPR, tmp); + + switch (C_CSIZE(tty)) { + case CS5: + cor1 |= COR1_5BITS; + break; + case CS6: + cor1 |= COR1_6BITS; + break; + case CS7: + cor1 |= COR1_7BITS; + break; + case CS8: + cor1 |= COR1_8BITS; + break; + } + if (C_CSTOPB(tty)) + cor1 |= COR1_2SB; + + cor1 |= COR1_IGNORE; + if (C_PARENB(tty)) { + cor1 |= COR1_NORMPAR; + if (C_PARODD(tty)) + cor1 |= COR1_ODDP; + if (I_INPCK(tty)) + cor1 &= ~COR1_IGNORE; + } + /* Set marking of some errors */ + port->mark_mask = RCSR_OE | RCSR_TOUT; + if (I_INPCK(tty)) + port->mark_mask |= RCSR_FE | RCSR_PE; + if (I_BRKINT(tty) || I_PARMRK(tty)) + port->mark_mask |= RCSR_BREAK; + if (I_IGNPAR(tty)) + port->mark_mask &= ~(RCSR_FE | RCSR_PE); + if (I_IGNBRK(tty)) { + port->mark_mask &= ~RCSR_BREAK; + if (I_IGNPAR(tty)) + /* Real raw mode. Ignore all */ + port->mark_mask &= ~RCSR_OE; + } + /* Enable Hardware Flow Control */ + if (C_CRTSCTS(tty)) { +#ifdef RISCOM_BRAIN_DAMAGED_CTS + port->IER |= IER_DSR | IER_CTS; + mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; + mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; + tty->hw_stopped = !(rc_in(bp, CD180_MSVR) & + (MSVR_CTS|MSVR_DSR)); +#else + port->COR2 |= COR2_CTSAE; +#endif + } + /* Enable Software Flow Control. FIXME: I'm not sure about this */ + /* Some people reported that it works, but I still doubt */ + if (I_IXON(tty)) { + port->COR2 |= COR2_TXIBE; + cor3 |= (COR3_FCT | COR3_SCDE); + if (I_IXANY(tty)) + port->COR2 |= COR2_IXM; + rc_out(bp, CD180_SCHR1, START_CHAR(tty)); + rc_out(bp, CD180_SCHR2, STOP_CHAR(tty)); + rc_out(bp, CD180_SCHR3, START_CHAR(tty)); + rc_out(bp, CD180_SCHR4, STOP_CHAR(tty)); + } + if (!C_CLOCAL(tty)) { + /* Enable CD check */ + port->IER |= IER_CD; + mcor1 |= MCOR1_CDZD; + mcor2 |= MCOR2_CDOD; + } + + if (C_CREAD(tty)) + /* Enable receiver */ + port->IER |= IER_RXD; + + /* Set input FIFO size (1-8 bytes) */ + cor3 |= RISCOM_RXFIFO; + /* Setting up CD180 channel registers */ + rc_out(bp, CD180_COR1, cor1); + rc_out(bp, CD180_COR2, port->COR2); + rc_out(bp, CD180_COR3, cor3); + /* Make CD180 know about registers change */ + rc_wait_CCR(bp); + rc_out(bp, CD180_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); + /* Setting up modem option registers */ + rc_out(bp, CD180_MCOR1, mcor1); + rc_out(bp, CD180_MCOR2, mcor2); + /* Enable CD180 transmitter & receiver */ + rc_wait_CCR(bp); + rc_out(bp, CD180_CCR, CCR_TXEN | CCR_RXEN); + /* Enable interrupts */ + rc_out(bp, CD180_IER, port->IER); + /* And finally set RTS on */ + rc_out(bp, CD180_MSVR, port->MSVR); +} + +/* Must be called with interrupts enabled */ +static int rc_activate_port(struct tty_port *port, struct tty_struct *tty) +{ + struct riscom_port *rp = container_of(port, struct riscom_port, port); + struct riscom_board *bp = port_Board(rp); + unsigned long flags; + + if (tty_port_alloc_xmit_buf(port) < 0) + return -ENOMEM; + + spin_lock_irqsave(&riscom_lock, flags); + + clear_bit(TTY_IO_ERROR, &tty->flags); + bp->count++; + rp->xmit_cnt = rp->xmit_head = rp->xmit_tail = 0; + rc_change_speed(tty, bp, rp); + spin_unlock_irqrestore(&riscom_lock, flags); + return 0; +} + +/* Must be called with interrupts disabled */ +static void rc_shutdown_port(struct tty_struct *tty, + struct riscom_board *bp, struct riscom_port *port) +{ +#ifdef RC_REPORT_OVERRUN + printk(KERN_INFO "rc%d: port %d: Total %ld overruns were detected.\n", + board_No(bp), port_No(port), port->overrun); +#endif +#ifdef RC_REPORT_FIFO + { + int i; + + printk(KERN_INFO "rc%d: port %d: FIFO hits [ ", + board_No(bp), port_No(port)); + for (i = 0; i < 10; i++) + printk("%ld ", port->hits[i]); + printk("].\n"); + } +#endif + tty_port_free_xmit_buf(&port->port); + + /* Select port */ + rc_out(bp, CD180_CAR, port_No(port)); + /* Reset port */ + rc_wait_CCR(bp); + rc_out(bp, CD180_CCR, CCR_SOFTRESET); + /* Disable all interrupts from this port */ + port->IER = 0; + rc_out(bp, CD180_IER, port->IER); + + set_bit(TTY_IO_ERROR, &tty->flags); + + if (--bp->count < 0) { + printk(KERN_INFO "rc%d: rc_shutdown_port: " + "bad board count: %d\n", + board_No(bp), bp->count); + bp->count = 0; + } + /* + * If this is the last opened port on the board + * shutdown whole board + */ + if (!bp->count) + rc_shutdown_board(bp); +} + +static int carrier_raised(struct tty_port *port) +{ + struct riscom_port *p = container_of(port, struct riscom_port, port); + struct riscom_board *bp = port_Board(p); + unsigned long flags; + int CD; + + spin_lock_irqsave(&riscom_lock, flags); + rc_out(bp, CD180_CAR, port_No(p)); + CD = rc_in(bp, CD180_MSVR) & MSVR_CD; + rc_out(bp, CD180_MSVR, MSVR_RTS); + bp->DTR &= ~(1u << port_No(p)); + rc_out(bp, RC_DTR, bp->DTR); + spin_unlock_irqrestore(&riscom_lock, flags); + return CD; +} + +static void dtr_rts(struct tty_port *port, int onoff) +{ + struct riscom_port *p = container_of(port, struct riscom_port, port); + struct riscom_board *bp = port_Board(p); + unsigned long flags; + + spin_lock_irqsave(&riscom_lock, flags); + bp->DTR &= ~(1u << port_No(p)); + if (onoff == 0) + bp->DTR |= (1u << port_No(p)); + rc_out(bp, RC_DTR, bp->DTR); + spin_unlock_irqrestore(&riscom_lock, flags); +} + +static int rc_open(struct tty_struct *tty, struct file *filp) +{ + int board; + int error; + struct riscom_port *port; + struct riscom_board *bp; + + board = RC_BOARD(tty->index); + if (board >= RC_NBOARD || !(rc_board[board].flags & RC_BOARD_PRESENT)) + return -ENODEV; + + bp = &rc_board[board]; + port = rc_port + board * RC_NPORT + RC_PORT(tty->index); + if (rc_paranoia_check(port, tty->name, "rc_open")) + return -ENODEV; + + error = rc_setup_board(bp); + if (error) + return error; + + tty->driver_data = port; + return tty_port_open(&port->port, tty, filp); +} + +static void rc_flush_buffer(struct tty_struct *tty) +{ + struct riscom_port *port = tty->driver_data; + unsigned long flags; + + if (rc_paranoia_check(port, tty->name, "rc_flush_buffer")) + return; + + spin_lock_irqsave(&riscom_lock, flags); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + spin_unlock_irqrestore(&riscom_lock, flags); + + tty_wakeup(tty); +} + +static void rc_close_port(struct tty_port *port) +{ + unsigned long flags; + struct riscom_port *rp = container_of(port, struct riscom_port, port); + struct riscom_board *bp = port_Board(rp); + unsigned long timeout; + + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + + spin_lock_irqsave(&riscom_lock, flags); + rp->IER &= ~IER_RXD; + + rp->IER &= ~IER_TXRDY; + rp->IER |= IER_TXEMPTY; + rc_out(bp, CD180_CAR, port_No(rp)); + rc_out(bp, CD180_IER, rp->IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies + HZ; + while (rp->IER & IER_TXEMPTY) { + spin_unlock_irqrestore(&riscom_lock, flags); + msleep_interruptible(jiffies_to_msecs(rp->timeout)); + spin_lock_irqsave(&riscom_lock, flags); + if (time_after(jiffies, timeout)) + break; + } + rc_shutdown_port(port->tty, bp, rp); + spin_unlock_irqrestore(&riscom_lock, flags); +} + +static void rc_close(struct tty_struct *tty, struct file *filp) +{ + struct riscom_port *port = tty->driver_data; + + if (!port || rc_paranoia_check(port, tty->name, "close")) + return; + tty_port_close(&port->port, tty, filp); +} + +static int rc_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct riscom_port *port = tty->driver_data; + struct riscom_board *bp; + int c, total = 0; + unsigned long flags; + + if (rc_paranoia_check(port, tty->name, "rc_write")) + return 0; + + bp = port_Board(port); + + while (1) { + spin_lock_irqsave(&riscom_lock, flags); + + c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + if (c <= 0) + break; /* lock continues to be held */ + + memcpy(port->port.xmit_buf + port->xmit_head, buf, c); + port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + port->xmit_cnt += c; + + spin_unlock_irqrestore(&riscom_lock, flags); + + buf += c; + count -= c; + total += c; + } + + if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && + !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + rc_out(bp, CD180_CAR, port_No(port)); + rc_out(bp, CD180_IER, port->IER); + } + + spin_unlock_irqrestore(&riscom_lock, flags); + + return total; +} + +static int rc_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct riscom_port *port = tty->driver_data; + unsigned long flags; + int ret = 0; + + if (rc_paranoia_check(port, tty->name, "rc_put_char")) + return 0; + + spin_lock_irqsave(&riscom_lock, flags); + + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) + goto out; + + port->port.xmit_buf[port->xmit_head++] = ch; + port->xmit_head &= SERIAL_XMIT_SIZE - 1; + port->xmit_cnt++; + ret = 1; + +out: + spin_unlock_irqrestore(&riscom_lock, flags); + return ret; +} + +static void rc_flush_chars(struct tty_struct *tty) +{ + struct riscom_port *port = tty->driver_data; + unsigned long flags; + + if (rc_paranoia_check(port, tty->name, "rc_flush_chars")) + return; + + if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped) + return; + + spin_lock_irqsave(&riscom_lock, flags); + + port->IER |= IER_TXRDY; + rc_out(port_Board(port), CD180_CAR, port_No(port)); + rc_out(port_Board(port), CD180_IER, port->IER); + + spin_unlock_irqrestore(&riscom_lock, flags); +} + +static int rc_write_room(struct tty_struct *tty) +{ + struct riscom_port *port = tty->driver_data; + int ret; + + if (rc_paranoia_check(port, tty->name, "rc_write_room")) + return 0; + + ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} + +static int rc_chars_in_buffer(struct tty_struct *tty) +{ + struct riscom_port *port = tty->driver_data; + + if (rc_paranoia_check(port, tty->name, "rc_chars_in_buffer")) + return 0; + + return port->xmit_cnt; +} + +static int rc_tiocmget(struct tty_struct *tty) +{ + struct riscom_port *port = tty->driver_data; + struct riscom_board *bp; + unsigned char status; + unsigned int result; + unsigned long flags; + + if (rc_paranoia_check(port, tty->name, __func__)) + return -ENODEV; + + bp = port_Board(port); + + spin_lock_irqsave(&riscom_lock, flags); + + rc_out(bp, CD180_CAR, port_No(port)); + status = rc_in(bp, CD180_MSVR); + result = rc_in(bp, RC_RI) & (1u << port_No(port)) ? 0 : TIOCM_RNG; + + spin_unlock_irqrestore(&riscom_lock, flags); + + result |= ((status & MSVR_RTS) ? TIOCM_RTS : 0) + | ((status & MSVR_DTR) ? TIOCM_DTR : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + | ((status & MSVR_DSR) ? TIOCM_DSR : 0) + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + return result; +} + +static int rc_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct riscom_port *port = tty->driver_data; + unsigned long flags; + struct riscom_board *bp; + + if (rc_paranoia_check(port, tty->name, __func__)) + return -ENODEV; + + bp = port_Board(port); + + spin_lock_irqsave(&riscom_lock, flags); + + if (set & TIOCM_RTS) + port->MSVR |= MSVR_RTS; + if (set & TIOCM_DTR) + bp->DTR &= ~(1u << port_No(port)); + + if (clear & TIOCM_RTS) + port->MSVR &= ~MSVR_RTS; + if (clear & TIOCM_DTR) + bp->DTR |= (1u << port_No(port)); + + rc_out(bp, CD180_CAR, port_No(port)); + rc_out(bp, CD180_MSVR, port->MSVR); + rc_out(bp, RC_DTR, bp->DTR); + + spin_unlock_irqrestore(&riscom_lock, flags); + + return 0; +} + +static int rc_send_break(struct tty_struct *tty, int length) +{ + struct riscom_port *port = tty->driver_data; + struct riscom_board *bp = port_Board(port); + unsigned long flags; + + if (length == 0 || length == -1) + return -EOPNOTSUPP; + + spin_lock_irqsave(&riscom_lock, flags); + + port->break_length = RISCOM_TPS / HZ * length; + port->COR2 |= COR2_ETC; + port->IER |= IER_TXRDY; + rc_out(bp, CD180_CAR, port_No(port)); + rc_out(bp, CD180_COR2, port->COR2); + rc_out(bp, CD180_IER, port->IER); + rc_wait_CCR(bp); + rc_out(bp, CD180_CCR, CCR_CORCHG2); + rc_wait_CCR(bp); + + spin_unlock_irqrestore(&riscom_lock, flags); + return 0; +} + +static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port, + struct serial_struct __user *newinfo) +{ + struct serial_struct tmp; + struct riscom_board *bp = port_Board(port); + int change_speed; + + if (copy_from_user(&tmp, newinfo, sizeof(tmp))) + return -EFAULT; + + mutex_lock(&port->port.mutex); + change_speed = ((port->port.flags & ASYNC_SPD_MASK) != + (tmp.flags & ASYNC_SPD_MASK)); + + if (!capable(CAP_SYS_ADMIN)) { + if ((tmp.close_delay != port->port.close_delay) || + (tmp.closing_wait != port->port.closing_wait) || + ((tmp.flags & ~ASYNC_USR_MASK) != + (port->port.flags & ~ASYNC_USR_MASK))) { + mutex_unlock(&port->port.mutex); + return -EPERM; + } + port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | + (tmp.flags & ASYNC_USR_MASK)); + } else { + port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | + (tmp.flags & ASYNC_FLAGS)); + port->port.close_delay = tmp.close_delay; + port->port.closing_wait = tmp.closing_wait; + } + if (change_speed) { + unsigned long flags; + + spin_lock_irqsave(&riscom_lock, flags); + rc_change_speed(tty, bp, port); + spin_unlock_irqrestore(&riscom_lock, flags); + } + mutex_unlock(&port->port.mutex); + return 0; +} + +static int rc_get_serial_info(struct riscom_port *port, + struct serial_struct __user *retinfo) +{ + struct serial_struct tmp; + struct riscom_board *bp = port_Board(port); + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = PORT_CIRRUS; + tmp.line = port - rc_port; + + mutex_lock(&port->port.mutex); + tmp.port = bp->base; + tmp.irq = bp->irq; + tmp.flags = port->port.flags; + tmp.baud_base = (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC; + tmp.close_delay = port->port.close_delay * HZ/100; + tmp.closing_wait = port->port.closing_wait * HZ/100; + mutex_unlock(&port->port.mutex); + tmp.xmit_fifo_size = CD180_NFIFO; + return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0; +} + +static int rc_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct riscom_port *port = tty->driver_data; + void __user *argp = (void __user *)arg; + int retval; + + if (rc_paranoia_check(port, tty->name, "rc_ioctl")) + return -ENODEV; + + switch (cmd) { + case TIOCGSERIAL: + retval = rc_get_serial_info(port, argp); + break; + case TIOCSSERIAL: + retval = rc_set_serial_info(tty, port, argp); + break; + default: + retval = -ENOIOCTLCMD; + } + return retval; +} + +static void rc_throttle(struct tty_struct *tty) +{ + struct riscom_port *port = tty->driver_data; + struct riscom_board *bp; + unsigned long flags; + + if (rc_paranoia_check(port, tty->name, "rc_throttle")) + return; + bp = port_Board(port); + + spin_lock_irqsave(&riscom_lock, flags); + port->MSVR &= ~MSVR_RTS; + rc_out(bp, CD180_CAR, port_No(port)); + if (I_IXOFF(tty)) { + rc_wait_CCR(bp); + rc_out(bp, CD180_CCR, CCR_SSCH2); + rc_wait_CCR(bp); + } + rc_out(bp, CD180_MSVR, port->MSVR); + spin_unlock_irqrestore(&riscom_lock, flags); +} + +static void rc_unthrottle(struct tty_struct *tty) +{ + struct riscom_port *port = tty->driver_data; + struct riscom_board *bp; + unsigned long flags; + + if (rc_paranoia_check(port, tty->name, "rc_unthrottle")) + return; + bp = port_Board(port); + + spin_lock_irqsave(&riscom_lock, flags); + port->MSVR |= MSVR_RTS; + rc_out(bp, CD180_CAR, port_No(port)); + if (I_IXOFF(tty)) { + rc_wait_CCR(bp); + rc_out(bp, CD180_CCR, CCR_SSCH1); + rc_wait_CCR(bp); + } + rc_out(bp, CD180_MSVR, port->MSVR); + spin_unlock_irqrestore(&riscom_lock, flags); +} + +static void rc_stop(struct tty_struct *tty) +{ + struct riscom_port *port = tty->driver_data; + struct riscom_board *bp; + unsigned long flags; + + if (rc_paranoia_check(port, tty->name, "rc_stop")) + return; + + bp = port_Board(port); + + spin_lock_irqsave(&riscom_lock, flags); + port->IER &= ~IER_TXRDY; + rc_out(bp, CD180_CAR, port_No(port)); + rc_out(bp, CD180_IER, port->IER); + spin_unlock_irqrestore(&riscom_lock, flags); +} + +static void rc_start(struct tty_struct *tty) +{ + struct riscom_port *port = tty->driver_data; + struct riscom_board *bp; + unsigned long flags; + + if (rc_paranoia_check(port, tty->name, "rc_start")) + return; + + bp = port_Board(port); + + spin_lock_irqsave(&riscom_lock, flags); + + if (port->xmit_cnt && port->port.xmit_buf && !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + rc_out(bp, CD180_CAR, port_No(port)); + rc_out(bp, CD180_IER, port->IER); + } + spin_unlock_irqrestore(&riscom_lock, flags); +} + +static void rc_hangup(struct tty_struct *tty) +{ + struct riscom_port *port = tty->driver_data; + + if (rc_paranoia_check(port, tty->name, "rc_hangup")) + return; + + tty_port_hangup(&port->port); +} + +static void rc_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + struct riscom_port *port = tty->driver_data; + unsigned long flags; + + if (rc_paranoia_check(port, tty->name, "rc_set_termios")) + return; + + spin_lock_irqsave(&riscom_lock, flags); + rc_change_speed(tty, port_Board(port), port); + spin_unlock_irqrestore(&riscom_lock, flags); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rc_start(tty); + } +} + +static const struct tty_operations riscom_ops = { + .open = rc_open, + .close = rc_close, + .write = rc_write, + .put_char = rc_put_char, + .flush_chars = rc_flush_chars, + .write_room = rc_write_room, + .chars_in_buffer = rc_chars_in_buffer, + .flush_buffer = rc_flush_buffer, + .ioctl = rc_ioctl, + .throttle = rc_throttle, + .unthrottle = rc_unthrottle, + .set_termios = rc_set_termios, + .stop = rc_stop, + .start = rc_start, + .hangup = rc_hangup, + .tiocmget = rc_tiocmget, + .tiocmset = rc_tiocmset, + .break_ctl = rc_send_break, +}; + +static const struct tty_port_operations riscom_port_ops = { + .carrier_raised = carrier_raised, + .dtr_rts = dtr_rts, + .shutdown = rc_close_port, + .activate = rc_activate_port, +}; + + +static int __init rc_init_drivers(void) +{ + int error; + int i; + + riscom_driver = alloc_tty_driver(RC_NBOARD * RC_NPORT); + if (!riscom_driver) + return -ENOMEM; + + riscom_driver->owner = THIS_MODULE; + riscom_driver->name = "ttyL"; + riscom_driver->major = RISCOM8_NORMAL_MAJOR; + riscom_driver->type = TTY_DRIVER_TYPE_SERIAL; + riscom_driver->subtype = SERIAL_TYPE_NORMAL; + riscom_driver->init_termios = tty_std_termios; + riscom_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + riscom_driver->init_termios.c_ispeed = 9600; + riscom_driver->init_termios.c_ospeed = 9600; + riscom_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK; + tty_set_operations(riscom_driver, &riscom_ops); + error = tty_register_driver(riscom_driver); + if (error != 0) { + put_tty_driver(riscom_driver); + printk(KERN_ERR "rc: Couldn't register RISCom/8 driver, " + "error = %d\n", error); + return 1; + } + memset(rc_port, 0, sizeof(rc_port)); + for (i = 0; i < RC_NPORT * RC_NBOARD; i++) { + tty_port_init(&rc_port[i].port); + rc_port[i].port.ops = &riscom_port_ops; + rc_port[i].magic = RISCOM8_MAGIC; + } + return 0; +} + +static void rc_release_drivers(void) +{ + tty_unregister_driver(riscom_driver); + put_tty_driver(riscom_driver); +} + +#ifndef MODULE +/* + * Called at boot time. + * + * You can specify IO base for up to RC_NBOARD cards, + * using line "riscom8=0xiobase1,0xiobase2,.." at LILO prompt. + * Note that there will be no probing at default + * addresses in this case. + * + */ +static int __init riscom8_setup(char *str) +{ + int ints[RC_NBOARD]; + int i; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + for (i = 0; i < RC_NBOARD; i++) { + if (i < ints[0]) + rc_board[i].base = ints[i+1]; + else + rc_board[i].base = 0; + } + return 1; +} + +__setup("riscom8=", riscom8_setup); +#endif + +static char banner[] __initdata = + KERN_INFO "rc: SDL RISCom/8 card driver v1.1, (c) D.Gorodchanin " + "1994-1996.\n"; +static char no_boards_msg[] __initdata = + KERN_INFO "rc: No RISCom/8 boards detected.\n"; + +/* + * This routine must be called by kernel at boot time + */ +static int __init riscom8_init(void) +{ + int i; + int found = 0; + + printk(banner); + + if (rc_init_drivers()) + return -EIO; + + for (i = 0; i < RC_NBOARD; i++) + if (rc_board[i].base && !rc_probe(&rc_board[i])) + found++; + if (!found) { + rc_release_drivers(); + printk(no_boards_msg); + return -EIO; + } + return 0; +} + +#ifdef MODULE +static int iobase; +static int iobase1; +static int iobase2; +static int iobase3; +module_param(iobase, int, 0); +module_param(iobase1, int, 0); +module_param(iobase2, int, 0); +module_param(iobase3, int, 0); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(RISCOM8_NORMAL_MAJOR); +#endif /* MODULE */ + +/* + * You can setup up to 4 boards (current value of RC_NBOARD) + * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter. + * + */ +static int __init riscom8_init_module(void) +{ +#ifdef MODULE + int i; + + if (iobase || iobase1 || iobase2 || iobase3) { + for (i = 0; i < RC_NBOARD; i++) + rc_board[i].base = 0; + } + + if (iobase) + rc_board[0].base = iobase; + if (iobase1) + rc_board[1].base = iobase1; + if (iobase2) + rc_board[2].base = iobase2; + if (iobase3) + rc_board[3].base = iobase3; +#endif /* MODULE */ + + return riscom8_init(); +} + +static void __exit riscom8_exit_module(void) +{ + int i; + + rc_release_drivers(); + for (i = 0; i < RC_NBOARD; i++) + if (rc_board[i].flags & RC_BOARD_PRESENT) + rc_release_io_range(&rc_board[i]); + +} + +module_init(riscom8_init_module); +module_exit(riscom8_exit_module); diff --git a/drivers/staging/tty/riscom8.h b/drivers/staging/tty/riscom8.h new file mode 100644 index 000000000000..c9876b3f9714 --- /dev/null +++ b/drivers/staging/tty/riscom8.h @@ -0,0 +1,91 @@ +/* + * linux/drivers/char/riscom8.h -- RISCom/8 multiport serial driver. + * + * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) + * + * This code is loosely based on the Linux serial driver, written by + * Linus Torvalds, Theodore T'so and others. The RISCom/8 card + * programming info was obtained from various drivers for other OSes + * (FreeBSD, ISC, etc), but no source code from those drivers were + * directly included in this driver. + * + * + * 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. + */ + +#ifndef __LINUX_RISCOM8_H +#define __LINUX_RISCOM8_H + +#include + +#ifdef __KERNEL__ + +#define RC_NBOARD 4 +/* NOTE: RISCom decoder recognizes 16 addresses... */ +#define RC_NPORT 8 +#define RC_BOARD(line) (((line) >> 3) & 0x07) +#define RC_PORT(line) ((line) & (RC_NPORT - 1)) + +/* Ticks per sec. Used for setting receiver timeout and break length */ +#define RISCOM_TPS 4000 + +/* Yeah, after heavy testing I decided it must be 6. + * Sure, You can change it if needed. + */ +#define RISCOM_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ + +#define RISCOM8_MAGIC 0x0907 + +#define RC_IOBASE1 0x220 +#define RC_IOBASE2 0x240 +#define RC_IOBASE3 0x250 +#define RC_IOBASE4 0x260 + +struct riscom_board { + unsigned long flags; + unsigned short base; + unsigned char irq; + signed char count; + unsigned char DTR; +}; + +#define RC_BOARD_PRESENT 0x00000001 +#define RC_BOARD_ACTIVE 0x00000002 + +struct riscom_port { + int magic; + struct tty_port port; + int baud_base; + int timeout; + int custom_divisor; + int xmit_head; + int xmit_tail; + int xmit_cnt; + short wakeup_chars; + short break_length; + unsigned char mark_mask; + unsigned char IER; + unsigned char MSVR; + unsigned char COR2; +#ifdef RC_REPORT_OVERRUN + unsigned long overrun; +#endif +#ifdef RC_REPORT_FIFO + unsigned long hits[10]; +#endif +}; + +#endif /* __KERNEL__ */ +#endif /* __LINUX_RISCOM8_H */ diff --git a/drivers/staging/tty/riscom8_reg.h b/drivers/staging/tty/riscom8_reg.h new file mode 100644 index 000000000000..a32475ed0d18 --- /dev/null +++ b/drivers/staging/tty/riscom8_reg.h @@ -0,0 +1,254 @@ +/* + * linux/drivers/char/riscom8_reg.h -- RISCom/8 multiport serial driver. + */ + +/* + * Definitions for RISCom/8 Async Mux card by SDL Communications, Inc. + */ + +/* + * Address mapping between Cirrus Logic CD180 chip internal registers + * and ISA port addresses: + * + * CL-CD180 A6 A5 A4 A3 A2 A1 A0 + * ISA A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0 + */ +#define RC_TO_ISA(r) ((((r)&0x07)<<1) | (((r)&~0x07)<<7)) + + +/* RISCom/8 On-Board Registers (assuming address translation) */ + +#define RC_RI 0x100 /* Ring Indicator Register (R/O) */ +#define RC_DTR 0x100 /* DTR Register (W/O) */ +#define RC_BSR 0x101 /* Board Status Register (R/O) */ +#define RC_CTOUT 0x101 /* Clear Timeout (W/O) */ + + +/* Board Status Register */ + +#define RC_BSR_TOUT 0x08 /* Hardware Timeout */ +#define RC_BSR_RINT 0x04 /* Receiver Interrupt */ +#define RC_BSR_TINT 0x02 /* Transmitter Interrupt */ +#define RC_BSR_MINT 0x01 /* Modem Ctl Interrupt */ + + +/* On-board oscillator frequency (in Hz) */ +#define RC_OSCFREQ 9830400 + +/* Values of choice for Interrupt ACKs */ +#define RC_ACK_MINT 0x81 /* goes to PILR1 */ +#define RC_ACK_RINT 0x82 /* goes to PILR3 */ +#define RC_ACK_TINT 0x84 /* goes to PILR2 */ + +/* Chip ID (sorry, only one chip now) */ +#define RC_ID 0x10 + +/* Definitions for Cirrus Logic CL-CD180 8-port async mux chip */ + +#define CD180_NCH 8 /* Total number of channels */ +#define CD180_TPC 16 /* Ticks per character */ +#define CD180_NFIFO 8 /* TX FIFO size */ + + +/* Global registers */ + +#define CD180_GIVR 0x40 /* Global Interrupt Vector Register */ +#define CD180_GICR 0x41 /* Global Interrupting Channel Register */ +#define CD180_PILR1 0x61 /* Priority Interrupt Level Register 1 */ +#define CD180_PILR2 0x62 /* Priority Interrupt Level Register 2 */ +#define CD180_PILR3 0x63 /* Priority Interrupt Level Register 3 */ +#define CD180_CAR 0x64 /* Channel Access Register */ +#define CD180_GFRCR 0x6b /* Global Firmware Revision Code Register */ +#define CD180_PPRH 0x70 /* Prescaler Period Register High */ +#define CD180_PPRL 0x71 /* Prescaler Period Register Low */ +#define CD180_RDR 0x78 /* Receiver Data Register */ +#define CD180_RCSR 0x7a /* Receiver Character Status Register */ +#define CD180_TDR 0x7b /* Transmit Data Register */ +#define CD180_EOIR 0x7f /* End of Interrupt Register */ + + +/* Channel Registers */ + +#define CD180_CCR 0x01 /* Channel Command Register */ +#define CD180_IER 0x02 /* Interrupt Enable Register */ +#define CD180_COR1 0x03 /* Channel Option Register 1 */ +#define CD180_COR2 0x04 /* Channel Option Register 2 */ +#define CD180_COR3 0x05 /* Channel Option Register 3 */ +#define CD180_CCSR 0x06 /* Channel Control Status Register */ +#define CD180_RDCR 0x07 /* Receive Data Count Register */ +#define CD180_SCHR1 0x09 /* Special Character Register 1 */ +#define CD180_SCHR2 0x0a /* Special Character Register 2 */ +#define CD180_SCHR3 0x0b /* Special Character Register 3 */ +#define CD180_SCHR4 0x0c /* Special Character Register 4 */ +#define CD180_MCOR1 0x10 /* Modem Change Option 1 Register */ +#define CD180_MCOR2 0x11 /* Modem Change Option 2 Register */ +#define CD180_MCR 0x12 /* Modem Change Register */ +#define CD180_RTPR 0x18 /* Receive Timeout Period Register */ +#define CD180_MSVR 0x28 /* Modem Signal Value Register */ +#define CD180_RBPRH 0x31 /* Receive Baud Rate Period Register High */ +#define CD180_RBPRL 0x32 /* Receive Baud Rate Period Register Low */ +#define CD180_TBPRH 0x39 /* Transmit Baud Rate Period Register High */ +#define CD180_TBPRL 0x3a /* Transmit Baud Rate Period Register Low */ + + +/* Global Interrupt Vector Register (R/W) */ + +#define GIVR_ITMASK 0x07 /* Interrupt type mask */ +#define GIVR_IT_MODEM 0x01 /* Modem Signal Change Interrupt */ +#define GIVR_IT_TX 0x02 /* Transmit Data Interrupt */ +#define GIVR_IT_RCV 0x03 /* Receive Good Data Interrupt */ +#define GIVR_IT_REXC 0x07 /* Receive Exception Interrupt */ + + +/* Global Interrupt Channel Register (R/W) */ + +#define GICR_CHAN 0x1c /* Channel Number Mask */ +#define GICR_CHAN_OFF 2 /* Channel Number Offset */ + + +/* Channel Address Register (R/W) */ + +#define CAR_CHAN 0x07 /* Channel Number Mask */ +#define CAR_A7 0x08 /* A7 Address Extension (unused) */ + + +/* Receive Character Status Register (R/O) */ + +#define RCSR_TOUT 0x80 /* Rx Timeout */ +#define RCSR_SCDET 0x70 /* Special Character Detected Mask */ +#define RCSR_NO_SC 0x00 /* No Special Characters Detected */ +#define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */ +#define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */ +#define RCSR_SC_3 0x30 /* Special Char 3 Detected */ +#define RCSR_SC_4 0x40 /* Special Char 4 Detected */ +#define RCSR_BREAK 0x08 /* Break has been detected */ +#define RCSR_PE 0x04 /* Parity Error */ +#define RCSR_FE 0x02 /* Frame Error */ +#define RCSR_OE 0x01 /* Overrun Error */ + + +/* Channel Command Register (R/W) (commands in groups can be OR-ed) */ + +#define CCR_HARDRESET 0x81 /* Reset the chip */ + +#define CCR_SOFTRESET 0x80 /* Soft Channel Reset */ + +#define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */ +#define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */ +#define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */ + +#define CCR_SSCH1 0x21 /* Send Special Character 1 */ + +#define CCR_SSCH2 0x22 /* Send Special Character 2 */ + +#define CCR_SSCH3 0x23 /* Send Special Character 3 */ + +#define CCR_SSCH4 0x24 /* Send Special Character 4 */ + +#define CCR_TXEN 0x18 /* Enable Transmitter */ +#define CCR_RXEN 0x12 /* Enable Receiver */ + +#define CCR_TXDIS 0x14 /* Disable Transmitter */ +#define CCR_RXDIS 0x11 /* Disable Receiver */ + + +/* Interrupt Enable Register (R/W) */ + +#define IER_DSR 0x80 /* Enable interrupt on DSR change */ +#define IER_CD 0x40 /* Enable interrupt on CD change */ +#define IER_CTS 0x20 /* Enable interrupt on CTS change */ +#define IER_RXD 0x10 /* Enable interrupt on Receive Data */ +#define IER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */ +#define IER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */ +#define IER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */ +#define IER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */ + + +/* Channel Option Register 1 (R/W) */ + +#define COR1_ODDP 0x80 /* Odd Parity */ +#define COR1_PARMODE 0x60 /* Parity Mode mask */ +#define COR1_NOPAR 0x00 /* No Parity */ +#define COR1_FORCEPAR 0x20 /* Force Parity */ +#define COR1_NORMPAR 0x40 /* Normal Parity */ +#define COR1_IGNORE 0x10 /* Ignore Parity on RX */ +#define COR1_STOPBITS 0x0c /* Number of Stop Bits */ +#define COR1_1SB 0x00 /* 1 Stop Bit */ +#define COR1_15SB 0x04 /* 1.5 Stop Bits */ +#define COR1_2SB 0x08 /* 2 Stop Bits */ +#define COR1_CHARLEN 0x03 /* Character Length */ +#define COR1_5BITS 0x00 /* 5 bits */ +#define COR1_6BITS 0x01 /* 6 bits */ +#define COR1_7BITS 0x02 /* 7 bits */ +#define COR1_8BITS 0x03 /* 8 bits */ + + +/* Channel Option Register 2 (R/W) */ + +#define COR2_IXM 0x80 /* Implied XON mode */ +#define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */ +#define COR2_ETC 0x20 /* Embedded Tx Commands Enable */ +#define COR2_LLM 0x10 /* Local Loopback Mode */ +#define COR2_RLM 0x08 /* Remote Loopback Mode */ +#define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */ +#define COR2_CTSAE 0x02 /* CTS Automatic Enable */ +#define COR2_DSRAE 0x01 /* DSR Automatic Enable */ + + +/* Channel Option Register 3 (R/W) */ + +#define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */ +#define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */ +#define COR3_FCT 0x20 /* Flow-Control Transparency Mode */ +#define COR3_SCDE 0x10 /* Special Character Detection Enable */ +#define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */ + + +/* Channel Control Status Register (R/O) */ + +#define CCSR_RXEN 0x80 /* Receiver Enabled */ +#define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */ +#define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */ +#define CCSR_TXEN 0x08 /* Transmitter Enabled */ +#define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */ +#define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */ + + +/* Modem Change Option Register 1 (R/W) */ + +#define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */ +#define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */ +#define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */ +#define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */ +#define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */ + + +/* Modem Change Option Register 2 (R/W) */ + +#define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */ +#define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */ +#define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */ + + +/* Modem Change Register (R/W) */ + +#define MCR_DSRCHG 0x80 /* DSR Changed */ +#define MCR_CDCHG 0x40 /* CD Changed */ +#define MCR_CTSCHG 0x20 /* CTS Changed */ + + +/* Modem Signal Value Register (R/W) */ + +#define MSVR_DSR 0x80 /* Current state of DSR input */ +#define MSVR_CD 0x40 /* Current state of CD input */ +#define MSVR_CTS 0x20 /* Current state of CTS input */ +#define MSVR_DTR 0x02 /* Current state of DTR output */ +#define MSVR_RTS 0x01 /* Current state of RTS output */ + + +/* Escape characters */ + +#define CD180_C_ESC 0x00 /* Escape character */ +#define CD180_C_SBRK 0x81 /* Start sending BREAK */ +#define CD180_C_DELAY 0x82 /* Delay output */ +#define CD180_C_EBRK 0x83 /* Stop sending BREAK */ diff --git a/drivers/staging/tty/serial167.c b/drivers/staging/tty/serial167.c new file mode 100644 index 000000000000..674af6933978 --- /dev/null +++ b/drivers/staging/tty/serial167.c @@ -0,0 +1,2489 @@ +/* + * linux/drivers/char/serial167.c + * + * Driver for MVME166/7 board serial ports, which are via a CD2401. + * Based very much on cyclades.c. + * + * MVME166/7 work by Richard Hirst [richard@sleepie.demon.co.uk] + * + * ============================================================== + * + * static char rcsid[] = + * "$Revision: 1.36.1.4 $$Date: 1995/03/29 06:14:14 $"; + * + * linux/kernel/cyclades.c + * + * Maintained by Marcio Saito (cyclades@netcom.com) and + * Randolph Bentson (bentson@grieg.seaslug.org) + * + * Much of the design and some of the code came from serial.c + * which was copyright (C) 1991, 1992 Linus Torvalds. It was + * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92, + * and then fixed as suggested by Michael K. Johnson 12/12/92. + * + * This version does not support shared irq's. + * + * $Log: cyclades.c,v $ + * Revision 1.36.1.4 1995/03/29 06:14:14 bentson + * disambiguate between Cyclom-16Y and Cyclom-32Ye; + * + * Changes: + * + * 200 lines of changes record removed - RGH 11-10-95, starting work on + * converting this to drive serial ports on mvme166 (cd2401). + * + * Arnaldo Carvalho de Melo - 2000/08/25 + * - get rid of verify_area + * - use get_user to access memory from userspace in set_threshold, + * set_default_threshold and set_timeout + * - don't use the panic function in serial167_init + * - do resource release on failure on serial167_init + * - include missing restore_flags in mvme167_serial_console_setup + * + * Kars de Jong - 2004/09/06 + * - replace bottom half handler with task queue handler + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define SERIAL_PARANOIA_CHECK +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_THROTTLE +#undef SERIAL_DEBUG_OTHER +#undef SERIAL_DEBUG_IO +#undef SERIAL_DEBUG_COUNT +#undef SERIAL_DEBUG_DTR +#undef CYCLOM_16Y_HACK +#define CYCLOM_ENABLE_MONITORING + +#define WAKEUP_CHARS 256 + +#define STD_COM_FLAGS (0) + +static struct tty_driver *cy_serial_driver; +extern int serial_console; +static struct cyclades_port *serial_console_info = NULL; +static unsigned int serial_console_cflag = 0; +u_char initial_console_speed; + +/* Base address of cd2401 chip on mvme166/7 */ + +#define BASE_ADDR (0xfff45000) +#define pcc2chip ((volatile u_char *)0xfff42000) +#define PccSCCMICR 0x1d +#define PccSCCTICR 0x1e +#define PccSCCRICR 0x1f +#define PccTPIACKR 0x25 +#define PccRPIACKR 0x27 +#define PccIMLR 0x3f + +/* This is the per-port data structure */ +struct cyclades_port cy_port[] = { + /* CARD# */ + {-1}, /* ttyS0 */ + {-1}, /* ttyS1 */ + {-1}, /* ttyS2 */ + {-1}, /* ttyS3 */ +}; + +#define NR_PORTS ARRAY_SIZE(cy_port) + +/* + * This is used to look up the divisor speeds and the timeouts + * We're normally limited to 15 distinct baud rates. The extra + * are accessed via settings in info->flags. + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + * HI VHI + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, + 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000, + 0 +}; + +#if 0 +static char baud_co[] = { /* 25 MHz clock option table */ + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static char baud_bpr[] = { /* 25 MHz baud rate period table */ + 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, + 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15 +}; +#endif + +/* I think 166 brd clocks 2401 at 20MHz.... */ + +/* These values are written directly to tcor, and >> 5 for writing to rcor */ +static u_char baud_co[] = { /* 20 MHz clock option table */ + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x60, 0x60, 0x40, + 0x40, 0x40, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* These values written directly to tbpr/rbpr */ +static u_char baud_bpr[] = { /* 20 MHz baud rate period table */ + 0x00, 0xc0, 0x80, 0x58, 0x6c, 0x40, 0xc0, 0x81, 0x40, 0x81, + 0x57, 0x40, 0x81, 0x40, 0x81, 0x40, 0x2b, 0x20, 0x15, 0x10 +}; + +static u_char baud_cor4[] = { /* receive threshold */ + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07 +}; + +static void shutdown(struct cyclades_port *); +static int startup(struct cyclades_port *); +static void cy_throttle(struct tty_struct *); +static void cy_unthrottle(struct tty_struct *); +static void config_setup(struct cyclades_port *); +#ifdef CYCLOM_SHOW_STATUS +static void show_status(int); +#endif + +/* + * I have my own version of udelay(), as it is needed when initialising + * the chip, before the delay loop has been calibrated. Should probably + * reference one of the vmechip2 or pccchip2 counter for an accurate + * delay, but this wild guess will do for now. + */ + +void my_udelay(long us) +{ + u_char x; + volatile u_char *p = &x; + int i; + + while (us--) + for (i = 100; i; i--) + x |= *p; +} + +static inline int serial_paranoia_check(struct cyclades_port *info, char *name, + const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + if (!info) { + printk("Warning: null cyclades_port for (%s) in %s\n", name, + routine); + return 1; + } + + if (info < &cy_port[0] || info >= &cy_port[NR_PORTS]) { + printk("Warning: cyclades_port out of range for (%s) in %s\n", + name, routine); + return 1; + } + + if (info->magic != CYCLADES_MAGIC) { + printk("Warning: bad magic number for serial struct (%s) in " + "%s\n", name, routine); + return 1; + } +#endif + return 0; +} /* serial_paranoia_check */ + +#if 0 +/* The following diagnostic routines allow the driver to spew + information on the screen, even (especially!) during interrupts. + */ +void SP(char *data) +{ + unsigned long flags; + local_irq_save(flags); + printk(KERN_EMERG "%s", data); + local_irq_restore(flags); +} + +char scrn[2]; +void CP(char data) +{ + unsigned long flags; + local_irq_save(flags); + scrn[0] = data; + printk(KERN_EMERG "%c", scrn); + local_irq_restore(flags); +} /* CP */ + +void CP1(int data) +{ + (data < 10) ? CP(data + '0') : CP(data + 'A' - 10); +} /* CP1 */ +void CP2(int data) +{ + CP1((data >> 4) & 0x0f); + CP1(data & 0x0f); +} /* CP2 */ +void CP4(int data) +{ + CP2((data >> 8) & 0xff); + CP2(data & 0xff); +} /* CP4 */ +void CP8(long data) +{ + CP4((data >> 16) & 0xffff); + CP4(data & 0xffff); +} /* CP8 */ +#endif + +/* This routine waits up to 1000 micro-seconds for the previous + command to the Cirrus chip to complete and then issues the + new command. An error is returned if the previous command + didn't finish within the time limit. + */ +u_short write_cy_cmd(volatile u_char * base_addr, u_char cmd) +{ + unsigned long flags; + volatile int i; + + local_irq_save(flags); + /* Check to see that the previous command has completed */ + for (i = 0; i < 100; i++) { + if (base_addr[CyCCR] == 0) { + break; + } + my_udelay(10L); + } + /* if the CCR never cleared, the previous command + didn't finish within the "reasonable time" */ + if (i == 10) { + local_irq_restore(flags); + return (-1); + } + + /* Issue the new command */ + base_addr[CyCCR] = cmd; + local_irq_restore(flags); + return (0); +} /* write_cy_cmd */ + +/* cy_start and cy_stop provide software output flow control as a + function of XON/XOFF, software CTS, and other such stuff. */ + +static void cy_stop(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + unsigned long flags; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_stop %s\n", tty->name); /* */ +#endif + + if (serial_paranoia_check(info, tty->name, "cy_stop")) + return; + + channel = info->line; + + local_irq_save(flags); + base_addr[CyCAR] = (u_char) (channel); /* index channel */ + base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); + local_irq_restore(flags); +} /* cy_stop */ + +static void cy_start(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + unsigned long flags; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_start %s\n", tty->name); /* */ +#endif + + if (serial_paranoia_check(info, tty->name, "cy_start")) + return; + + channel = info->line; + + local_irq_save(flags); + base_addr[CyCAR] = (u_char) (channel); + base_addr[CyIER] |= CyTxMpty; + local_irq_restore(flags); +} /* cy_start */ + +/* The real interrupt service routines are called + whenever the card wants its hand held--chars + received, out buffer empty, modem change, etc. + */ +static irqreturn_t cd2401_rxerr_interrupt(int irq, void *dev_id) +{ + struct tty_struct *tty; + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + unsigned char err, rfoc; + int channel; + char data; + + /* determine the channel and change to that context */ + channel = (u_short) (base_addr[CyLICR] >> 2); + info = &cy_port[channel]; + info->last_active = jiffies; + + if ((err = base_addr[CyRISR]) & CyTIMEOUT) { + /* This is a receive timeout interrupt, ignore it */ + base_addr[CyREOIR] = CyNOTRANS; + return IRQ_HANDLED; + } + + /* Read a byte of data if there is any - assume the error + * is associated with this character */ + + if ((rfoc = base_addr[CyRFOC]) != 0) + data = base_addr[CyRDR]; + else + data = 0; + + /* if there is nowhere to put the data, discard it */ + if (info->tty == 0) { + base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; + return IRQ_HANDLED; + } else { /* there is an open port for this data */ + tty = info->tty; + if (err & info->ignore_status_mask) { + base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; + return IRQ_HANDLED; + } + if (tty_buffer_request_room(tty, 1) != 0) { + if (err & info->read_status_mask) { + if (err & CyBREAK) { + tty_insert_flip_char(tty, data, + TTY_BREAK); + if (info->flags & ASYNC_SAK) { + do_SAK(tty); + } + } else if (err & CyFRAME) { + tty_insert_flip_char(tty, data, + TTY_FRAME); + } else if (err & CyPARITY) { + tty_insert_flip_char(tty, data, + TTY_PARITY); + } else if (err & CyOVERRUN) { + tty_insert_flip_char(tty, 0, + TTY_OVERRUN); + /* + If the flip buffer itself is + overflowing, we still lose + the next incoming character. + */ + if (tty_buffer_request_room(tty, 1) != + 0) { + tty_insert_flip_char(tty, data, + TTY_FRAME); + } + /* These two conditions may imply */ + /* a normal read should be done. */ + /* else if(data & CyTIMEOUT) */ + /* else if(data & CySPECHAR) */ + } else { + tty_insert_flip_char(tty, 0, + TTY_NORMAL); + } + } else { + tty_insert_flip_char(tty, data, TTY_NORMAL); + } + } else { + /* there was a software buffer overrun + and nothing could be done about it!!! */ + } + } + tty_schedule_flip(tty); + /* end of service */ + base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; + return IRQ_HANDLED; +} /* cy_rxerr_interrupt */ + +static irqreturn_t cd2401_modem_interrupt(int irq, void *dev_id) +{ + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + int mdm_change; + int mdm_status; + + /* determine the channel and change to that context */ + channel = (u_short) (base_addr[CyLICR] >> 2); + info = &cy_port[channel]; + info->last_active = jiffies; + + mdm_change = base_addr[CyMISR]; + mdm_status = base_addr[CyMSVR1]; + + if (info->tty == 0) { /* nowhere to put the data, ignore it */ + ; + } else { + if ((mdm_change & CyDCD) + && (info->flags & ASYNC_CHECK_CD)) { + if (mdm_status & CyDCD) { +/* CP('!'); */ + wake_up_interruptible(&info->open_wait); + } else { +/* CP('@'); */ + tty_hangup(info->tty); + wake_up_interruptible(&info->open_wait); + info->flags &= ~ASYNC_NORMAL_ACTIVE; + } + } + if ((mdm_change & CyCTS) + && (info->flags & ASYNC_CTS_FLOW)) { + if (info->tty->stopped) { + if (mdm_status & CyCTS) { + /* !!! cy_start isn't used because... */ + info->tty->stopped = 0; + base_addr[CyIER] |= CyTxMpty; + tty_wakeup(info->tty); + } + } else { + if (!(mdm_status & CyCTS)) { + /* !!! cy_stop isn't used because... */ + info->tty->stopped = 1; + base_addr[CyIER] &= + ~(CyTxMpty | CyTxRdy); + } + } + } + if (mdm_status & CyDSR) { + } + } + base_addr[CyMEOIR] = 0; + return IRQ_HANDLED; +} /* cy_modem_interrupt */ + +static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id) +{ + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + int char_count, saved_cnt; + int outch; + + /* determine the channel and change to that context */ + channel = (u_short) (base_addr[CyLICR] >> 2); + + /* validate the port number (as configured and open) */ + if ((channel < 0) || (NR_PORTS <= channel)) { + base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); + base_addr[CyTEOIR] = CyNOTRANS; + return IRQ_HANDLED; + } + info = &cy_port[channel]; + info->last_active = jiffies; + if (info->tty == 0) { + base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); + base_addr[CyTEOIR] = CyNOTRANS; + return IRQ_HANDLED; + } + + /* load the on-chip space available for outbound data */ + saved_cnt = char_count = base_addr[CyTFTC]; + + if (info->x_char) { /* send special char */ + outch = info->x_char; + base_addr[CyTDR] = outch; + char_count--; + info->x_char = 0; + } + + if (info->x_break) { + /* The Cirrus chip requires the "Embedded Transmit + Commands" of start break, delay, and end break + sequences to be sent. The duration of the + break is given in TICs, which runs at HZ + (typically 100) and the PPR runs at 200 Hz, + so the delay is duration * 200/HZ, and thus a + break can run from 1/100 sec to about 5/4 sec. + Need to check these values - RGH 141095. + */ + base_addr[CyTDR] = 0; /* start break */ + base_addr[CyTDR] = 0x81; + base_addr[CyTDR] = 0; /* delay a bit */ + base_addr[CyTDR] = 0x82; + base_addr[CyTDR] = info->x_break * 200 / HZ; + base_addr[CyTDR] = 0; /* terminate break */ + base_addr[CyTDR] = 0x83; + char_count -= 7; + info->x_break = 0; + } + + while (char_count > 0) { + if (!info->xmit_cnt) { + base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); + break; + } + if (info->xmit_buf == 0) { + base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); + break; + } + if (info->tty->stopped || info->tty->hw_stopped) { + base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); + break; + } + /* Because the Embedded Transmit Commands have been + enabled, we must check to see if the escape + character, NULL, is being sent. If it is, we + must ensure that there is room for it to be + doubled in the output stream. Therefore we + no longer advance the pointer when the character + is fetched, but rather wait until after the check + for a NULL output character. (This is necessary + because there may not be room for the two chars + needed to send a NULL. + */ + outch = info->xmit_buf[info->xmit_tail]; + if (outch) { + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR] = outch; + char_count--; + } else { + if (char_count > 1) { + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR] = outch; + base_addr[CyTDR] = 0; + char_count--; + char_count--; + } else { + break; + } + } + } + + if (info->xmit_cnt < WAKEUP_CHARS) + tty_wakeup(info->tty); + + base_addr[CyTEOIR] = (char_count != saved_cnt) ? 0 : CyNOTRANS; + return IRQ_HANDLED; +} /* cy_tx_interrupt */ + +static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id) +{ + struct tty_struct *tty; + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + char data; + int char_count; + int save_cnt; + + /* determine the channel and change to that context */ + channel = (u_short) (base_addr[CyLICR] >> 2); + info = &cy_port[channel]; + info->last_active = jiffies; + save_cnt = char_count = base_addr[CyRFOC]; + + /* if there is nowhere to put the data, discard it */ + if (info->tty == 0) { + while (char_count--) { + data = base_addr[CyRDR]; + } + } else { /* there is an open port for this data */ + tty = info->tty; + /* load # characters available from the chip */ + +#ifdef CYCLOM_ENABLE_MONITORING + ++info->mon.int_count; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; +#endif + while (char_count--) { + data = base_addr[CyRDR]; + tty_insert_flip_char(tty, data, TTY_NORMAL); +#ifdef CYCLOM_16Y_HACK + udelay(10L); +#endif + } + tty_schedule_flip(tty); + } + /* end of service */ + base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS; + return IRQ_HANDLED; +} /* cy_rx_interrupt */ + +/* This is called whenever a port becomes active; + interrupts are enabled and DTR & RTS are turned on. + */ +static int startup(struct cyclades_port *info) +{ + unsigned long flags; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + + if (info->flags & ASYNC_INITIALIZED) { + return 0; + } + + if (!info->type) { + if (info->tty) { + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + return 0; + } + if (!info->xmit_buf) { + info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); + if (!info->xmit_buf) { + return -ENOMEM; + } + } + + config_setup(info); + + channel = info->line; + +#ifdef SERIAL_DEBUG_OPEN + printk("startup channel %d\n", channel); +#endif + + local_irq_save(flags); + base_addr[CyCAR] = (u_char) channel; + write_cy_cmd(base_addr, CyENB_RCVR | CyENB_XMTR); + + base_addr[CyCAR] = (u_char) channel; /* !!! Is this needed? */ + base_addr[CyMSVR1] = CyRTS; +/* CP('S');CP('1'); */ + base_addr[CyMSVR2] = CyDTR; + +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], + base_addr[CyMSVR2]); +#endif + + base_addr[CyIER] |= CyRxData; + info->flags |= ASYNC_INITIALIZED; + + if (info->tty) { + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + local_irq_restore(flags); + +#ifdef SERIAL_DEBUG_OPEN + printk(" done\n"); +#endif + return 0; +} /* startup */ + +void start_xmit(struct cyclades_port *info) +{ + unsigned long flags; + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + int channel; + + channel = info->line; + local_irq_save(flags); + base_addr[CyCAR] = channel; + base_addr[CyIER] |= CyTxMpty; + local_irq_restore(flags); +} /* start_xmit */ + +/* + * This routine shuts down a serial port; interrupts are disabled, + * and DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(struct cyclades_port *info) +{ + unsigned long flags; + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + int channel; + + if (!(info->flags & ASYNC_INITIALIZED)) { +/* CP('$'); */ + return; + } + + channel = info->line; + +#ifdef SERIAL_DEBUG_OPEN + printk("shutdown channel %d\n", channel); +#endif + + /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE + SENT BEFORE DROPPING THE LINE !!! (Perhaps + set some flag that is read when XMTY happens.) + Other choices are to delay some fixed interval + or schedule some later processing. + */ + local_irq_save(flags); + if (info->xmit_buf) { + free_page((unsigned long)info->xmit_buf); + info->xmit_buf = NULL; + } + + base_addr[CyCAR] = (u_char) channel; + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + base_addr[CyMSVR1] = 0; +/* CP('C');CP('1'); */ + base_addr[CyMSVR2] = 0; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: dropping DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], + base_addr[CyMSVR2]); +#endif + } + write_cy_cmd(base_addr, CyDIS_RCVR); + /* it may be appropriate to clear _XMIT at + some later date (after testing)!!! */ + + if (info->tty) { + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->flags &= ~ASYNC_INITIALIZED; + local_irq_restore(flags); + +#ifdef SERIAL_DEBUG_OPEN + printk(" done\n"); +#endif +} /* shutdown */ + +/* + * This routine finds or computes the various line characteristics. + */ +static void config_setup(struct cyclades_port *info) +{ + unsigned long flags; + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + int channel; + unsigned cflag; + int i; + unsigned char ti, need_init_chan = 0; + + if (!info->tty || !info->tty->termios) { + return; + } + if (info->line == -1) { + return; + } + cflag = info->tty->termios->c_cflag; + + /* baud rate */ + i = cflag & CBAUD; +#ifdef CBAUDEX +/* Starting with kernel 1.1.65, there is direct support for + higher baud rates. The following code supports those + changes. The conditional aspect allows this driver to be + used for earlier as well as later kernel versions. (The + mapping is slightly different from serial.c because there + is still the possibility of supporting 75 kbit/sec with + the Cyclades board.) + */ + if (i & CBAUDEX) { + if (i == B57600) + i = 16; + else if (i == B115200) + i = 18; +#ifdef B78600 + else if (i == B78600) + i = 17; +#endif + else + info->tty->termios->c_cflag &= ~CBAUDEX; + } +#endif + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i += 1; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i += 3; + } + /* Don't ever change the speed of the console port. It will + * run at the speed specified in bootinfo, or at 19.2K */ + /* Actually, it should run at whatever speed 166Bug was using */ + /* Note info->timeout isn't used at present */ + if (info != serial_console_info) { + info->tbpr = baud_bpr[i]; /* Tx BPR */ + info->tco = baud_co[i]; /* Tx CO */ + info->rbpr = baud_bpr[i]; /* Rx BPR */ + info->rco = baud_co[i] >> 5; /* Rx CO */ + if (baud_table[i] == 134) { + info->timeout = + (info->xmit_fifo_size * HZ * 30 / 269) + 2; + /* get it right for 134.5 baud */ + } else if (baud_table[i]) { + info->timeout = + (info->xmit_fifo_size * HZ * 15 / baud_table[i]) + + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + } + /* By tradition (is it a standard?) a baud rate of zero + implies the line should be/has been closed. A bit + later in this routine such a test is performed. */ + + /* byte size and parity */ + info->cor7 = 0; + info->cor6 = 0; + info->cor5 = 0; + info->cor4 = (info->default_threshold ? info->default_threshold : baud_cor4[i]); /* receive threshold */ + /* Following two lines added 101295, RGH. */ + /* It is obviously wrong to access CyCORx, and not info->corx here, + * try and remember to fix it later! */ + channel = info->line; + base_addr[CyCAR] = (u_char) channel; + if (C_CLOCAL(info->tty)) { + if (base_addr[CyIER] & CyMdmCh) + base_addr[CyIER] &= ~CyMdmCh; /* without modem intr */ + /* ignore 1->0 modem transitions */ + if (base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD)) + base_addr[CyCOR4] &= ~(CyDSR | CyCTS | CyDCD); + /* ignore 0->1 modem transitions */ + if (base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD)) + base_addr[CyCOR5] &= ~(CyDSR | CyCTS | CyDCD); + } else { + if ((base_addr[CyIER] & CyMdmCh) != CyMdmCh) + base_addr[CyIER] |= CyMdmCh; /* with modem intr */ + /* act on 1->0 modem transitions */ + if ((base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD)) != + (CyDSR | CyCTS | CyDCD)) + base_addr[CyCOR4] |= CyDSR | CyCTS | CyDCD; + /* act on 0->1 modem transitions */ + if ((base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD)) != + (CyDSR | CyCTS | CyDCD)) + base_addr[CyCOR5] |= CyDSR | CyCTS | CyDCD; + } + info->cor3 = (cflag & CSTOPB) ? Cy_2_STOP : Cy_1_STOP; + info->cor2 = CyETC; + switch (cflag & CSIZE) { + case CS5: + info->cor1 = Cy_5_BITS; + break; + case CS6: + info->cor1 = Cy_6_BITS; + break; + case CS7: + info->cor1 = Cy_7_BITS; + break; + case CS8: + info->cor1 = Cy_8_BITS; + break; + } + if (cflag & PARENB) { + if (cflag & PARODD) { + info->cor1 |= CyPARITY_O; + } else { + info->cor1 |= CyPARITY_E; + } + } else { + info->cor1 |= CyPARITY_NONE; + } + + /* CTS flow control flag */ +#if 0 + /* Don't complcate matters for now! RGH 141095 */ + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->cor2 |= CyCtsAE; + } else { + info->flags &= ~ASYNC_CTS_FLOW; + info->cor2 &= ~CyCtsAE; + } +#endif + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + /*********************************************** + The hardware option, CyRtsAO, presents RTS when + the chip has characters to send. Since most modems + use RTS as reverse (inbound) flow control, this + option is not used. If inbound flow control is + necessary, DTR can be programmed to provide the + appropriate signals for use with a non-standard + cable. Contact Marcio Saito for details. + ***********************************************/ + + channel = info->line; + + local_irq_save(flags); + base_addr[CyCAR] = (u_char) channel; + + /* CyCMR set once only in mvme167_init_serial() */ + if (base_addr[CyLICR] != channel << 2) + base_addr[CyLICR] = channel << 2; + if (base_addr[CyLIVR] != 0x5c) + base_addr[CyLIVR] = 0x5c; + + /* tx and rx baud rate */ + + if (base_addr[CyCOR1] != info->cor1) + need_init_chan = 1; + if (base_addr[CyTCOR] != info->tco) + base_addr[CyTCOR] = info->tco; + if (base_addr[CyTBPR] != info->tbpr) + base_addr[CyTBPR] = info->tbpr; + if (base_addr[CyRCOR] != info->rco) + base_addr[CyRCOR] = info->rco; + if (base_addr[CyRBPR] != info->rbpr) + base_addr[CyRBPR] = info->rbpr; + + /* set line characteristics according configuration */ + + if (base_addr[CySCHR1] != START_CHAR(info->tty)) + base_addr[CySCHR1] = START_CHAR(info->tty); + if (base_addr[CySCHR2] != STOP_CHAR(info->tty)) + base_addr[CySCHR2] = STOP_CHAR(info->tty); + if (base_addr[CySCRL] != START_CHAR(info->tty)) + base_addr[CySCRL] = START_CHAR(info->tty); + if (base_addr[CySCRH] != START_CHAR(info->tty)) + base_addr[CySCRH] = START_CHAR(info->tty); + if (base_addr[CyCOR1] != info->cor1) + base_addr[CyCOR1] = info->cor1; + if (base_addr[CyCOR2] != info->cor2) + base_addr[CyCOR2] = info->cor2; + if (base_addr[CyCOR3] != info->cor3) + base_addr[CyCOR3] = info->cor3; + if (base_addr[CyCOR4] != info->cor4) + base_addr[CyCOR4] = info->cor4; + if (base_addr[CyCOR5] != info->cor5) + base_addr[CyCOR5] = info->cor5; + if (base_addr[CyCOR6] != info->cor6) + base_addr[CyCOR6] = info->cor6; + if (base_addr[CyCOR7] != info->cor7) + base_addr[CyCOR7] = info->cor7; + + if (need_init_chan) + write_cy_cmd(base_addr, CyINIT_CHAN); + + base_addr[CyCAR] = (u_char) channel; /* !!! Is this needed? */ + + /* 2ms default rx timeout */ + ti = info->default_timeout ? info->default_timeout : 0x02; + if (base_addr[CyRTPRL] != ti) + base_addr[CyRTPRL] = ti; + if (base_addr[CyRTPRH] != 0) + base_addr[CyRTPRH] = 0; + + /* Set up RTS here also ????? RGH 141095 */ + if (i == 0) { /* baud rate is zero, turn off line */ + if ((base_addr[CyMSVR2] & CyDTR) == CyDTR) + base_addr[CyMSVR2] = 0; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: dropping DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], + base_addr[CyMSVR2]); +#endif + } else { + if ((base_addr[CyMSVR2] & CyDTR) != CyDTR) + base_addr[CyMSVR2] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], + base_addr[CyMSVR2]); +#endif + } + + if (info->tty) { + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + local_irq_restore(flags); + +} /* config_setup */ + +static int cy_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct cyclades_port *info = tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_IO + printk("cy_put_char %s(0x%02x)\n", tty->name, ch); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_put_char")) + return 0; + + if (!info->xmit_buf) + return 0; + + local_irq_save(flags); + if (info->xmit_cnt >= PAGE_SIZE - 1) { + local_irq_restore(flags); + return 0; + } + + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= PAGE_SIZE - 1; + info->xmit_cnt++; + local_irq_restore(flags); + return 1; +} /* cy_put_char */ + +static void cy_flush_chars(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + unsigned long flags; + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + int channel; + +#ifdef SERIAL_DEBUG_IO + printk("cy_flush_chars %s\n", tty->name); /* */ +#endif + + if (serial_paranoia_check(info, tty->name, "cy_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped + || tty->hw_stopped || !info->xmit_buf) + return; + + channel = info->line; + + local_irq_save(flags); + base_addr[CyCAR] = channel; + base_addr[CyIER] |= CyTxMpty; + local_irq_restore(flags); +} /* cy_flush_chars */ + +/* This routine gets called when tty_write has put something into + the write_queue. If the port is not already transmitting stuff, + start it off by enabling interrupts. The interrupt service + routine will then ensure that the characters are sent. If the + port is already active, there is no need to kick it. + */ +static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + struct cyclades_port *info = tty->driver_data; + unsigned long flags; + int c, total = 0; + +#ifdef SERIAL_DEBUG_IO + printk("cy_write %s\n", tty->name); /* */ +#endif + + if (serial_paranoia_check(info, tty->name, "cy_write")) { + return 0; + } + + if (!info->xmit_buf) { + return 0; + } + + while (1) { + local_irq_save(flags); + c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) { + local_irq_restore(flags); + break; + } + + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = + (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1); + info->xmit_cnt += c; + local_irq_restore(flags); + + buf += c; + count -= c; + total += c; + } + + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { + start_xmit(info); + } + return total; +} /* cy_write */ + +static int cy_write_room(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + int ret; + +#ifdef SERIAL_DEBUG_IO + printk("cy_write_room %s\n", tty->name); /* */ +#endif + + if (serial_paranoia_check(info, tty->name, "cy_write_room")) + return 0; + ret = PAGE_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} /* cy_write_room */ + +static int cy_chars_in_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + +#ifdef SERIAL_DEBUG_IO + printk("cy_chars_in_buffer %s %d\n", tty->name, info->xmit_cnt); /* */ +#endif + + if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer")) + return 0; + + return info->xmit_cnt; +} /* cy_chars_in_buffer */ + +static void cy_flush_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_IO + printk("cy_flush_buffer %s\n", tty->name); /* */ +#endif + + if (serial_paranoia_check(info, tty->name, "cy_flush_buffer")) + return; + local_irq_save(flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + local_irq_restore(flags); + tty_wakeup(tty); +} /* cy_flush_buffer */ + +/* This routine is called by the upper-layer tty layer to signal + that incoming characters should be throttled or that the + throttle should be released. + */ +static void cy_throttle(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + unsigned long flags; + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + int channel; + +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); + printk("cy_throttle %s\n", tty->name); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_nthrottle")) { + return; + } + + if (I_IXOFF(tty)) { + info->x_char = STOP_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ + } + + channel = info->line; + + local_irq_save(flags); + base_addr[CyCAR] = (u_char) channel; + base_addr[CyMSVR1] = 0; + local_irq_restore(flags); +} /* cy_throttle */ + +static void cy_unthrottle(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + unsigned long flags; + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + int channel; + +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); + printk("cy_unthrottle %s\n", tty->name); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_nthrottle")) { + return; + } + + if (I_IXOFF(tty)) { + info->x_char = START_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ + } + + channel = info->line; + + local_irq_save(flags); + base_addr[CyCAR] = (u_char) channel; + base_addr[CyMSVR1] = CyRTS; + local_irq_restore(flags); +} /* cy_unthrottle */ + +static int +get_serial_info(struct cyclades_port *info, + struct serial_struct __user * retinfo) +{ + struct serial_struct tmp; + +/* CP('g'); */ + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->line; + tmp.port = info->line; + tmp.irq = 0; + tmp.flags = info->flags; + tmp.baud_base = 0; /*!!! */ + tmp.close_delay = info->close_delay; + tmp.custom_divisor = 0; /*!!! */ + tmp.hub6 = 0; /*!!! */ + return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; +} /* get_serial_info */ + +static int +set_serial_info(struct cyclades_port *info, + struct serial_struct __user * new_info) +{ + struct serial_struct new_serial; + struct cyclades_port old_info; + +/* CP('s'); */ + if (!new_info) + return -EFAULT; + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) + return -EFAULT; + old_info = *info; + + if (!capable(CAP_SYS_ADMIN)) { + if ((new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != + (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) + return -EPERM; + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + goto check_and_exit; + } + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + info->flags = ((info->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->close_delay = new_serial.close_delay; + +check_and_exit: + if (info->flags & ASYNC_INITIALIZED) { + config_setup(info); + return 0; + } + return startup(info); +} /* set_serial_info */ + +static int cy_tiocmget(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + int channel; + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + unsigned long flags; + unsigned char status; + + channel = info->line; + + local_irq_save(flags); + base_addr[CyCAR] = (u_char) channel; + status = base_addr[CyMSVR1] | base_addr[CyMSVR2]; + local_irq_restore(flags); + + return ((status & CyRTS) ? TIOCM_RTS : 0) + | ((status & CyDTR) ? TIOCM_DTR : 0) + | ((status & CyDCD) ? TIOCM_CAR : 0) + | ((status & CyDSR) ? TIOCM_DSR : 0) + | ((status & CyCTS) ? TIOCM_CTS : 0); +} /* cy_tiocmget */ + +static int +cy_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) +{ + struct cyclades_port *info = tty->driver_data; + int channel; + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + unsigned long flags; + + channel = info->line; + + if (set & TIOCM_RTS) { + local_irq_save(flags); + base_addr[CyCAR] = (u_char) channel; + base_addr[CyMSVR1] = CyRTS; + local_irq_restore(flags); + } + if (set & TIOCM_DTR) { + local_irq_save(flags); + base_addr[CyCAR] = (u_char) channel; +/* CP('S');CP('2'); */ + base_addr[CyMSVR2] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], + base_addr[CyMSVR2]); +#endif + local_irq_restore(flags); + } + + if (clear & TIOCM_RTS) { + local_irq_save(flags); + base_addr[CyCAR] = (u_char) channel; + base_addr[CyMSVR1] = 0; + local_irq_restore(flags); + } + if (clear & TIOCM_DTR) { + local_irq_save(flags); + base_addr[CyCAR] = (u_char) channel; +/* CP('C');CP('2'); */ + base_addr[CyMSVR2] = 0; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: dropping DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], + base_addr[CyMSVR2]); +#endif + local_irq_restore(flags); + } + + return 0; +} /* set_modem_info */ + +static void send_break(struct cyclades_port *info, int duration) +{ /* Let the transmit ISR take care of this (since it + requires stuffing characters into the output stream). + */ + info->x_break = duration; + if (!info->xmit_cnt) { + start_xmit(info); + } +} /* send_break */ + +static int +get_mon_info(struct cyclades_port *info, struct cyclades_monitor __user * mon) +{ + + if (copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor))) + return -EFAULT; + info->mon.int_count = 0; + info->mon.char_count = 0; + info->mon.char_max = 0; + info->mon.char_last = 0; + return 0; +} + +static int set_threshold(struct cyclades_port *info, unsigned long __user * arg) +{ + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + unsigned long value; + int channel; + + if (get_user(value, arg)) + return -EFAULT; + + channel = info->line; + info->cor4 &= ~CyREC_FIFO; + info->cor4 |= value & CyREC_FIFO; + base_addr[CyCOR4] = info->cor4; + return 0; +} + +static int +get_threshold(struct cyclades_port *info, unsigned long __user * value) +{ + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + int channel; + unsigned long tmp; + + channel = info->line; + + tmp = base_addr[CyCOR4] & CyREC_FIFO; + return put_user(tmp, value); +} + +static int +set_default_threshold(struct cyclades_port *info, unsigned long __user * arg) +{ + unsigned long value; + + if (get_user(value, arg)) + return -EFAULT; + + info->default_threshold = value & 0x0f; + return 0; +} + +static int +get_default_threshold(struct cyclades_port *info, unsigned long __user * value) +{ + return put_user(info->default_threshold, value); +} + +static int set_timeout(struct cyclades_port *info, unsigned long __user * arg) +{ + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + int channel; + unsigned long value; + + if (get_user(value, arg)) + return -EFAULT; + + channel = info->line; + + base_addr[CyRTPRL] = value & 0xff; + base_addr[CyRTPRH] = (value >> 8) & 0xff; + return 0; +} + +static int get_timeout(struct cyclades_port *info, unsigned long __user * value) +{ + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + int channel; + unsigned long tmp; + + channel = info->line; + + tmp = base_addr[CyRTPRL]; + return put_user(tmp, value); +} + +static int set_default_timeout(struct cyclades_port *info, unsigned long value) +{ + info->default_timeout = value & 0xff; + return 0; +} + +static int +get_default_timeout(struct cyclades_port *info, unsigned long __user * value) +{ + return put_user(info->default_timeout, value); +} + +static int +cy_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct cyclades_port *info = tty->driver_data; + int ret_val = 0; + void __user *argp = (void __user *)arg; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg); /* */ +#endif + + tty_lock(); + + switch (cmd) { + case CYGETMON: + ret_val = get_mon_info(info, argp); + break; + case CYGETTHRESH: + ret_val = get_threshold(info, argp); + break; + case CYSETTHRESH: + ret_val = set_threshold(info, argp); + break; + case CYGETDEFTHRESH: + ret_val = get_default_threshold(info, argp); + break; + case CYSETDEFTHRESH: + ret_val = set_default_threshold(info, argp); + break; + case CYGETTIMEOUT: + ret_val = get_timeout(info, argp); + break; + case CYSETTIMEOUT: + ret_val = set_timeout(info, argp); + break; + case CYGETDEFTIMEOUT: + ret_val = get_default_timeout(info, argp); + break; + case CYSETDEFTIMEOUT: + ret_val = set_default_timeout(info, (unsigned long)arg); + break; + case TCSBRK: /* SVID version: non-zero arg --> no break */ + ret_val = tty_check_change(tty); + if (ret_val) + break; + tty_wait_until_sent(tty, 0); + if (!arg) + send_break(info, HZ / 4); /* 1/4 second */ + break; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + ret_val = tty_check_change(tty); + if (ret_val) + break; + tty_wait_until_sent(tty, 0); + send_break(info, arg ? arg * (HZ / 10) : HZ / 4); + break; + +/* The following commands are incompletely implemented!!! */ + case TIOCGSERIAL: + ret_val = get_serial_info(info, argp); + break; + case TIOCSSERIAL: + ret_val = set_serial_info(info, argp); + break; + default: + ret_val = -ENOIOCTLCMD; + } + tty_unlock(); + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_ioctl done\n"); +#endif + + return ret_val; +} /* cy_ioctl */ + +static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + struct cyclades_port *info = tty->driver_data; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_set_termios %s\n", tty->name); +#endif + + if (tty->termios->c_cflag == old_termios->c_cflag) + return; + config_setup(info); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->stopped = 0; + cy_start(tty); + } +#ifdef tytso_patch_94Nov25_1726 + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif +} /* cy_set_termios */ + +static void cy_close(struct tty_struct *tty, struct file *filp) +{ + struct cyclades_port *info = tty->driver_data; + +/* CP('C'); */ +#ifdef SERIAL_DEBUG_OTHER + printk("cy_close %s\n", tty->name); +#endif + + if (!info || serial_paranoia_check(info, tty->name, "cy_close")) { + return; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cy_close %s, count = %d\n", tty->name, info->count); +#endif + + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("cy_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: decrementing count to %d\n", __LINE__, + info->count - 1); +#endif + if (--info->count < 0) { + printk("cy_close: bad serial port count for ttys%d: %d\n", + info->line, info->count); +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: setting count to 0\n", __LINE__); +#endif + info->count = 0; + } + if (info->count) + return; + info->flags |= ASYNC_CLOSING; + if (info->flags & ASYNC_INITIALIZED) + tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ + shutdown(info); + cy_flush_buffer(tty); + tty_ldisc_flush(tty); + info->tty = NULL; + if (info->blocked_open) { + if (info->close_delay) { + msleep_interruptible(jiffies_to_msecs + (info->close_delay)); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_close done\n"); +#endif +} /* cy_close */ + +/* + * cy_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +void cy_hangup(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_hangup %s\n", tty->name); /* */ +#endif + + if (serial_paranoia_check(info, tty->name, "cy_hangup")) + return; + + shutdown(info); +#if 0 + info->event = 0; + info->count = 0; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: setting count to 0\n", __LINE__); +#endif + info->tty = 0; +#endif + info->flags &= ~ASYNC_NORMAL_ACTIVE; + wake_up_interruptible(&info->open_wait); +} /* cy_hangup */ + +/* + * ------------------------------------------------------------ + * cy_open() and friends + * ------------------------------------------------------------ + */ + +static int +block_til_ready(struct tty_struct *tty, struct file *filp, + struct cyclades_port *info) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int channel; + int retval; + volatile u_char *base_addr = (u_char *) BASE_ADDR; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (info->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&info->close_wait); + if (info->flags & ASYNC_HUP_NOTIFY) { + return -EAGAIN; + } else { + return -ERESTARTSYS; + } + } + + /* + * If non-blocking mode is set, then make the check up front + * and then exit. + */ + if (filp->f_flags & O_NONBLOCK) { + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * cy_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: %s, count = %d\n", + tty->name, info->count); + /**/ +#endif + info->count--; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count); +#endif + info->blocked_open++; + + channel = info->line; + + while (1) { + local_irq_save(flags); + base_addr[CyCAR] = (u_char) channel; + base_addr[CyMSVR1] = CyRTS; +/* CP('S');CP('4'); */ + base_addr[CyMSVR2] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], + base_addr[CyMSVR2]); +#endif + local_irq_restore(flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) + || !(info->flags & ASYNC_INITIALIZED)) { + if (info->flags & ASYNC_HUP_NOTIFY) { + retval = -EAGAIN; + } else { + retval = -ERESTARTSYS; + } + break; + } + local_irq_save(flags); + base_addr[CyCAR] = (u_char) channel; +/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */ + if (!(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (base_addr[CyMSVR1] & CyDCD))) { + local_irq_restore(flags); + break; + } + local_irq_restore(flags); + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: %s, count = %d\n", + tty->name, info->count); + /**/ +#endif + tty_unlock(); + schedule(); + tty_lock(); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) { + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: incrementing count to %d\n", __LINE__, + info->count); +#endif + } + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: %s, count = %d\n", + tty->name, info->count); + /**/ +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} /* block_til_ready */ + +/* + * This routine is called whenever a serial port is opened. It + * performs the serial-specific initialization for the tty structure. + */ +int cy_open(struct tty_struct *tty, struct file *filp) +{ + struct cyclades_port *info; + int retval, line; + +/* CP('O'); */ + line = tty->index; + if ((line < 0) || (NR_PORTS <= line)) { + return -ENODEV; + } + info = &cy_port[line]; + if (info->line < 0) { + return -ENODEV; + } +#ifdef SERIAL_DEBUG_OTHER + printk("cy_open %s\n", tty->name); /* */ +#endif + if (serial_paranoia_check(info, tty->name, "cy_open")) { + return -ENODEV; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cy_open %s, count = %d\n", tty->name, info->count); + /**/ +#endif + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count); +#endif + tty->driver_data = info; + info->tty = tty; + + /* + * Start up serial port + */ + retval = startup(info); + if (retval) { + return retval; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("cy_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cy_open done\n"); + /**/ +#endif + return 0; +} /* cy_open */ + +/* + * --------------------------------------------------------------------- + * serial167_init() and friends + * + * serial167_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + +/* + * This routine prints out the appropriate serial driver version + * number, and identifies which options were configured into this + * driver. + */ +static void show_version(void) +{ + printk("MVME166/167 cd2401 driver\n"); +} /* show_version */ + +/* initialize chips on card -- return number of valid + chips (which is number of ports/4) */ + +/* + * This initialises the hardware to a reasonable state. It should + * probe the chip first so as to copy 166-Bug setup as a default for + * port 0. It initialises CMR to CyASYNC; that is never done again, so + * as to limit the number of CyINIT_CHAN commands in normal running. + * + * ... I wonder what I should do if this fails ... + */ + +void mvme167_serial_console_setup(int cflag) +{ + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + int ch; + u_char spd; + u_char rcor, rbpr, badspeed = 0; + unsigned long flags; + + local_irq_save(flags); + + /* + * First probe channel zero of the chip, to see what speed has + * been selected. + */ + + base_addr[CyCAR] = 0; + + rcor = base_addr[CyRCOR] << 5; + rbpr = base_addr[CyRBPR]; + + for (spd = 0; spd < sizeof(baud_bpr); spd++) + if (rbpr == baud_bpr[spd] && rcor == baud_co[spd]) + break; + if (spd >= sizeof(baud_bpr)) { + spd = 14; /* 19200 */ + badspeed = 1; /* Failed to identify speed */ + } + initial_console_speed = spd; + + /* OK, we have chosen a speed, now reset and reinitialise */ + + my_udelay(20000L); /* Allow time for any active o/p to complete */ + if (base_addr[CyCCR] != 0x00) { + local_irq_restore(flags); + /* printk(" chip is never idle (CCR != 0)\n"); */ + return; + } + + base_addr[CyCCR] = CyCHIP_RESET; /* Reset the chip */ + my_udelay(1000L); + + if (base_addr[CyGFRCR] == 0x00) { + local_irq_restore(flags); + /* printk(" chip is not responding (GFRCR stayed 0)\n"); */ + return; + } + + /* + * System clock is 20Mhz, divided by 2048, so divide by 10 for a 1.0ms + * tick + */ + + base_addr[CyTPR] = 10; + + base_addr[CyPILR1] = 0x01; /* Interrupt level for modem change */ + base_addr[CyPILR2] = 0x02; /* Interrupt level for tx ints */ + base_addr[CyPILR3] = 0x03; /* Interrupt level for rx ints */ + + /* + * Attempt to set up all channels to something reasonable, and + * bang out a INIT_CHAN command. We should then be able to limit + * the amount of fiddling we have to do in normal running. + */ + + for (ch = 3; ch >= 0; ch--) { + base_addr[CyCAR] = (u_char) ch; + base_addr[CyIER] = 0; + base_addr[CyCMR] = CyASYNC; + base_addr[CyLICR] = (u_char) ch << 2; + base_addr[CyLIVR] = 0x5c; + base_addr[CyTCOR] = baud_co[spd]; + base_addr[CyTBPR] = baud_bpr[spd]; + base_addr[CyRCOR] = baud_co[spd] >> 5; + base_addr[CyRBPR] = baud_bpr[spd]; + base_addr[CySCHR1] = 'Q' & 0x1f; + base_addr[CySCHR2] = 'X' & 0x1f; + base_addr[CySCRL] = 0; + base_addr[CySCRH] = 0; + base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE; + base_addr[CyCOR2] = 0; + base_addr[CyCOR3] = Cy_1_STOP; + base_addr[CyCOR4] = baud_cor4[spd]; + base_addr[CyCOR5] = 0; + base_addr[CyCOR6] = 0; + base_addr[CyCOR7] = 0; + base_addr[CyRTPRL] = 2; + base_addr[CyRTPRH] = 0; + base_addr[CyMSVR1] = 0; + base_addr[CyMSVR2] = 0; + write_cy_cmd(base_addr, CyINIT_CHAN | CyDIS_RCVR | CyDIS_XMTR); + } + + /* + * Now do specials for channel zero.... + */ + + base_addr[CyMSVR1] = CyRTS; + base_addr[CyMSVR2] = CyDTR; + base_addr[CyIER] = CyRxData; + write_cy_cmd(base_addr, CyENB_RCVR | CyENB_XMTR); + + local_irq_restore(flags); + + my_udelay(20000L); /* Let it all settle down */ + + printk("CD2401 initialised, chip is rev 0x%02x\n", base_addr[CyGFRCR]); + if (badspeed) + printk + (" WARNING: Failed to identify line speed, rcor=%02x,rbpr=%02x\n", + rcor >> 5, rbpr); +} /* serial_console_init */ + +static const struct tty_operations cy_ops = { + .open = cy_open, + .close = cy_close, + .write = cy_write, + .put_char = cy_put_char, + .flush_chars = cy_flush_chars, + .write_room = cy_write_room, + .chars_in_buffer = cy_chars_in_buffer, + .flush_buffer = cy_flush_buffer, + .ioctl = cy_ioctl, + .throttle = cy_throttle, + .unthrottle = cy_unthrottle, + .set_termios = cy_set_termios, + .stop = cy_stop, + .start = cy_start, + .hangup = cy_hangup, + .tiocmget = cy_tiocmget, + .tiocmset = cy_tiocmset, +}; + +/* The serial driver boot-time initialization code! + Hardware I/O ports are mapped to character special devices on a + first found, first allocated manner. That is, this code searches + for Cyclom cards in the system. As each is found, it is probed + to discover how many chips (and thus how many ports) are present. + These ports are mapped to the tty ports 64 and upward in monotonic + fashion. If an 8-port card is replaced with a 16-port card, the + port mapping on a following card will shift. + + This approach is different from what is used in the other serial + device driver because the Cyclom is more properly a multiplexer, + not just an aggregation of serial ports on one card. + + If there are more cards with more ports than have been statically + allocated above, a warning is printed and the extra ports are ignored. + */ +static int __init serial167_init(void) +{ + struct cyclades_port *info; + int ret = 0; + int good_ports = 0; + int port_num = 0; + int index; + int DefSpeed; +#ifdef notyet + struct sigaction sa; +#endif + + if (!(mvme16x_config & MVME16x_CONFIG_GOT_CD2401)) + return 0; + + cy_serial_driver = alloc_tty_driver(NR_PORTS); + if (!cy_serial_driver) + return -ENOMEM; + +#if 0 + scrn[1] = '\0'; +#endif + + show_version(); + + /* Has "console=0,9600n8" been used in bootinfo to change speed? */ + if (serial_console_cflag) + DefSpeed = serial_console_cflag & 0017; + else { + DefSpeed = initial_console_speed; + serial_console_info = &cy_port[0]; + serial_console_cflag = DefSpeed | CS8; +#if 0 + serial_console = 64; /*callout_driver.minor_start */ +#endif + } + + /* Initialize the tty_driver structure */ + + cy_serial_driver->owner = THIS_MODULE; + cy_serial_driver->name = "ttyS"; + cy_serial_driver->major = TTY_MAJOR; + cy_serial_driver->minor_start = 64; + cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL; + cy_serial_driver->subtype = SERIAL_TYPE_NORMAL; + cy_serial_driver->init_termios = tty_std_termios; + cy_serial_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + cy_serial_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(cy_serial_driver, &cy_ops); + + ret = tty_register_driver(cy_serial_driver); + if (ret) { + printk(KERN_ERR "Couldn't register MVME166/7 serial driver\n"); + put_tty_driver(cy_serial_driver); + return ret; + } + + port_num = 0; + info = cy_port; + for (index = 0; index < 1; index++) { + + good_ports = 4; + + if (port_num < NR_PORTS) { + while (good_ports-- && port_num < NR_PORTS) { + /*** initialize port ***/ + info->magic = CYCLADES_MAGIC; + info->type = PORT_CIRRUS; + info->card = index; + info->line = port_num; + info->flags = STD_COM_FLAGS; + info->tty = NULL; + info->xmit_fifo_size = 12; + info->cor1 = CyPARITY_NONE | Cy_8_BITS; + info->cor2 = CyETC; + info->cor3 = Cy_1_STOP; + info->cor4 = 0x08; /* _very_ small receive threshold */ + info->cor5 = 0; + info->cor6 = 0; + info->cor7 = 0; + info->tbpr = baud_bpr[DefSpeed]; /* Tx BPR */ + info->tco = baud_co[DefSpeed]; /* Tx CO */ + info->rbpr = baud_bpr[DefSpeed]; /* Rx BPR */ + info->rco = baud_co[DefSpeed] >> 5; /* Rx CO */ + info->close_delay = 0; + info->x_char = 0; + info->count = 0; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: setting count to 0\n", + __LINE__); +#endif + info->blocked_open = 0; + info->default_threshold = 0; + info->default_timeout = 0; + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + /* info->session */ + /* info->pgrp */ +/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/ + info->read_status_mask = + CyTIMEOUT | CySPECHAR | CyBREAK | CyPARITY | + CyFRAME | CyOVERRUN; + /* info->timeout */ + + printk("ttyS%d ", info->line); + port_num++; + info++; + if (!(port_num & 7)) { + printk("\n "); + } + } + } + printk("\n"); + } + while (port_num < NR_PORTS) { + info->line = -1; + port_num++; + info++; + } + + ret = request_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0, + "cd2401_errors", cd2401_rxerr_interrupt); + if (ret) { + printk(KERN_ERR "Could't get cd2401_errors IRQ"); + goto cleanup_serial_driver; + } + + ret = request_irq(MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt, 0, + "cd2401_modem", cd2401_modem_interrupt); + if (ret) { + printk(KERN_ERR "Could't get cd2401_modem IRQ"); + goto cleanup_irq_cd2401_errors; + } + + ret = request_irq(MVME167_IRQ_SER_TX, cd2401_tx_interrupt, 0, + "cd2401_txints", cd2401_tx_interrupt); + if (ret) { + printk(KERN_ERR "Could't get cd2401_txints IRQ"); + goto cleanup_irq_cd2401_modem; + } + + ret = request_irq(MVME167_IRQ_SER_RX, cd2401_rx_interrupt, 0, + "cd2401_rxints", cd2401_rx_interrupt); + if (ret) { + printk(KERN_ERR "Could't get cd2401_rxints IRQ"); + goto cleanup_irq_cd2401_txints; + } + + /* Now we have registered the interrupt handlers, allow the interrupts */ + + pcc2chip[PccSCCMICR] = 0x15; /* Serial ints are level 5 */ + pcc2chip[PccSCCTICR] = 0x15; + pcc2chip[PccSCCRICR] = 0x15; + + pcc2chip[PccIMLR] = 3; /* Allow PCC2 ints above 3!? */ + + return 0; +cleanup_irq_cd2401_txints: + free_irq(MVME167_IRQ_SER_TX, cd2401_tx_interrupt); +cleanup_irq_cd2401_modem: + free_irq(MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt); +cleanup_irq_cd2401_errors: + free_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt); +cleanup_serial_driver: + if (tty_unregister_driver(cy_serial_driver)) + printk(KERN_ERR + "Couldn't unregister MVME166/7 serial driver\n"); + put_tty_driver(cy_serial_driver); + return ret; +} /* serial167_init */ + +module_init(serial167_init); + +#ifdef CYCLOM_SHOW_STATUS +static void show_status(int line_num) +{ + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + int channel; + struct cyclades_port *info; + unsigned long flags; + + info = &cy_port[line_num]; + channel = info->line; + printk(" channel %d\n", channel); + /**/ printk(" cy_port\n"); + printk(" card line flags = %d %d %x\n", + info->card, info->line, info->flags); + printk + (" *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n", + (long)info->tty, info->read_status_mask, info->timeout, + info->xmit_fifo_size); + printk(" cor1,cor2,cor3,cor4,cor5,cor6,cor7 = %x %x %x %x %x %x %x\n", + info->cor1, info->cor2, info->cor3, info->cor4, info->cor5, + info->cor6, info->cor7); + printk(" tbpr,tco,rbpr,rco = %d %d %d %d\n", info->tbpr, info->tco, + info->rbpr, info->rco); + printk(" close_delay event count = %d %d %d\n", info->close_delay, + info->event, info->count); + printk(" x_char blocked_open = %x %x\n", info->x_char, + info->blocked_open); + printk(" open_wait = %lx %lx %lx\n", (long)info->open_wait); + + local_irq_save(flags); + +/* Global Registers */ + + printk(" CyGFRCR %x\n", base_addr[CyGFRCR]); + printk(" CyCAR %x\n", base_addr[CyCAR]); + printk(" CyRISR %x\n", base_addr[CyRISR]); + printk(" CyTISR %x\n", base_addr[CyTISR]); + printk(" CyMISR %x\n", base_addr[CyMISR]); + printk(" CyRIR %x\n", base_addr[CyRIR]); + printk(" CyTIR %x\n", base_addr[CyTIR]); + printk(" CyMIR %x\n", base_addr[CyMIR]); + printk(" CyTPR %x\n", base_addr[CyTPR]); + + base_addr[CyCAR] = (u_char) channel; + +/* Virtual Registers */ + +#if 0 + printk(" CyRIVR %x\n", base_addr[CyRIVR]); + printk(" CyTIVR %x\n", base_addr[CyTIVR]); + printk(" CyMIVR %x\n", base_addr[CyMIVR]); + printk(" CyMISR %x\n", base_addr[CyMISR]); +#endif + +/* Channel Registers */ + + printk(" CyCCR %x\n", base_addr[CyCCR]); + printk(" CyIER %x\n", base_addr[CyIER]); + printk(" CyCOR1 %x\n", base_addr[CyCOR1]); + printk(" CyCOR2 %x\n", base_addr[CyCOR2]); + printk(" CyCOR3 %x\n", base_addr[CyCOR3]); + printk(" CyCOR4 %x\n", base_addr[CyCOR4]); + printk(" CyCOR5 %x\n", base_addr[CyCOR5]); +#if 0 + printk(" CyCCSR %x\n", base_addr[CyCCSR]); + printk(" CyRDCR %x\n", base_addr[CyRDCR]); +#endif + printk(" CySCHR1 %x\n", base_addr[CySCHR1]); + printk(" CySCHR2 %x\n", base_addr[CySCHR2]); +#if 0 + printk(" CySCHR3 %x\n", base_addr[CySCHR3]); + printk(" CySCHR4 %x\n", base_addr[CySCHR4]); + printk(" CySCRL %x\n", base_addr[CySCRL]); + printk(" CySCRH %x\n", base_addr[CySCRH]); + printk(" CyLNC %x\n", base_addr[CyLNC]); + printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]); + printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]); +#endif + printk(" CyRTPRL %x\n", base_addr[CyRTPRL]); + printk(" CyRTPRH %x\n", base_addr[CyRTPRH]); + printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]); + printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]); + printk(" CyRBPR %x\n", base_addr[CyRBPR]); + printk(" CyRCOR %x\n", base_addr[CyRCOR]); + printk(" CyTBPR %x\n", base_addr[CyTBPR]); + printk(" CyTCOR %x\n", base_addr[CyTCOR]); + + local_irq_restore(flags); +} /* show_status */ +#endif + +#if 0 +/* Dummy routine in mvme16x/config.c for now */ + +/* Serial console setup. Called from linux/init/main.c */ + +void console_setup(char *str, int *ints) +{ + char *s; + int baud, bits, parity; + int cflag = 0; + + /* Sanity check. */ + if (ints[0] > 3 || ints[1] > 3) + return; + + /* Get baud, bits and parity */ + baud = 2400; + bits = 8; + parity = 'n'; + if (ints[2]) + baud = ints[2]; + if ((s = strchr(str, ','))) { + do { + s++; + } while (*s >= '0' && *s <= '9'); + if (*s) + parity = *s++; + if (*s) + bits = *s - '0'; + } + + /* Now construct a cflag setting. */ + switch (baud) { + case 1200: + cflag |= B1200; + break; + case 9600: + cflag |= B9600; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 2400: + default: + cflag |= B2400; + break; + } + switch (bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch (parity) { + case 'o': + case 'O': + cflag |= PARODD; + break; + case 'e': + case 'E': + cflag |= PARENB; + break; + } + + serial_console_info = &cy_port[ints[1]]; + serial_console_cflag = cflag; + serial_console = ints[1] + 64; /*callout_driver.minor_start */ +} +#endif + +/* + * The following is probably out of date for 2.1.x serial console stuff. + * + * The console is registered early on from arch/m68k/kernel/setup.c, and + * it therefore relies on the chip being setup correctly by 166-Bug. This + * seems reasonable, as the serial port has been used to invoke the system + * boot. It also means that this function must not rely on any data + * initialisation performed by serial167_init() etc. + * + * Of course, once the console has been registered, we had better ensure + * that serial167_init() doesn't leave the chip non-functional. + * + * The console must be locked when we get here. + */ + +void serial167_console_write(struct console *co, const char *str, + unsigned count) +{ + volatile unsigned char *base_addr = (u_char *) BASE_ADDR; + unsigned long flags; + volatile u_char sink; + u_char ier; + int port; + u_char do_lf = 0; + int i = 0; + + local_irq_save(flags); + + /* Ensure transmitter is enabled! */ + + port = 0; + base_addr[CyCAR] = (u_char) port; + while (base_addr[CyCCR]) + ; + base_addr[CyCCR] = CyENB_XMTR; + + ier = base_addr[CyIER]; + base_addr[CyIER] = CyTxMpty; + + while (1) { + if (pcc2chip[PccSCCTICR] & 0x20) { + /* We have a Tx int. Acknowledge it */ + sink = pcc2chip[PccTPIACKR]; + if ((base_addr[CyLICR] >> 2) == port) { + if (i == count) { + /* Last char of string is now output */ + base_addr[CyTEOIR] = CyNOTRANS; + break; + } + if (do_lf) { + base_addr[CyTDR] = '\n'; + str++; + i++; + do_lf = 0; + } else if (*str == '\n') { + base_addr[CyTDR] = '\r'; + do_lf = 1; + } else { + base_addr[CyTDR] = *str++; + i++; + } + base_addr[CyTEOIR] = 0; + } else + base_addr[CyTEOIR] = CyNOTRANS; + } + } + + base_addr[CyIER] = ier; + + local_irq_restore(flags); +} + +static struct tty_driver *serial167_console_device(struct console *c, + int *index) +{ + *index = c->index; + return cy_serial_driver; +} + +static struct console sercons = { + .name = "ttyS", + .write = serial167_console_write, + .device = serial167_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +static int __init serial167_console_init(void) +{ + if (vme_brdtype == VME_TYPE_MVME166 || + vme_brdtype == VME_TYPE_MVME167 || + vme_brdtype == VME_TYPE_MVME177) { + mvme167_serial_console_setup(0); + register_console(&sercons); + } + return 0; +} + +console_initcall(serial167_console_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/tty/specialix.c b/drivers/staging/tty/specialix.c new file mode 100644 index 000000000000..47e5753f732a --- /dev/null +++ b/drivers/staging/tty/specialix.c @@ -0,0 +1,2368 @@ +/* + * specialix.c -- specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) + * + * Specialix pays for the development and support of this driver. + * Please DO contact io8-linux@specialix.co.uk if you require + * support. But please read the documentation (specialix.txt) + * first. + * + * This driver was developped in the BitWizard linux device + * driver service. If you require a linux device driver for your + * product, please contact devices@BitWizard.nl for a quote. + * + * This code is firmly based on the riscom/8 serial driver, + * written by Dmitry Gorodchanin. The specialix IO8+ card + * programming information was obtained from the CL-CD1865 Data + * Book, and Specialix document number 6200059: IO8+ Hardware + * Functional Specification. + * + * 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. + * + * Revision history: + * + * Revision 1.0: April 1st 1997. + * Initial release for alpha testing. + * Revision 1.1: April 14th 1997. + * Incorporated Richard Hudsons suggestions, + * removed some debugging printk's. + * Revision 1.2: April 15th 1997. + * Ported to 2.1.x kernels. + * Revision 1.3: April 17th 1997 + * Backported to 2.0. (Compatibility macros). + * Revision 1.4: April 18th 1997 + * Fixed DTR/RTS bug that caused the card to indicate + * "don't send data" to a modem after the password prompt. + * Fixed bug for premature (fake) interrupts. + * Revision 1.5: April 19th 1997 + * fixed a minor typo in the header file, cleanup a little. + * performance warnings are now MAXed at once per minute. + * Revision 1.6: May 23 1997 + * Changed the specialix=... format to include interrupt. + * Revision 1.7: May 27 1997 + * Made many more debug printk's a compile time option. + * Revision 1.8: Jul 1 1997 + * port to linux-2.1.43 kernel. + * Revision 1.9: Oct 9 1998 + * Added stuff for the IO8+/PCI version. + * Revision 1.10: Oct 22 1999 / Jan 21 2000. + * Added stuff for setserial. + * Nicolas Mailhot (Nicolas.Mailhot@email.enst.fr) + * + */ + +#define VERSION "1.11" + + +/* + * There is a bunch of documentation about the card, jumpers, config + * settings, restrictions, cables, device names and numbers in + * Documentation/serial/specialix.txt + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "specialix_io8.h" +#include "cd1865.h" + + +/* + This driver can spew a whole lot of debugging output at you. If you + need maximum performance, you should disable the DEBUG define. To + aid in debugging in the field, I'm leaving the compile-time debug + features enabled, and disable them "runtime". That allows me to + instruct people with problems to enable debugging without requiring + them to recompile... +*/ +#define DEBUG + +static int sx_debug; +static int sx_rxfifo = SPECIALIX_RXFIFO; +static int sx_rtscts; + +#ifdef DEBUG +#define dprintk(f, str...) if (sx_debug & f) printk(str) +#else +#define dprintk(f, str...) /* nothing */ +#endif + +#define SX_DEBUG_FLOW 0x0001 +#define SX_DEBUG_DATA 0x0002 +#define SX_DEBUG_PROBE 0x0004 +#define SX_DEBUG_CHAN 0x0008 +#define SX_DEBUG_INIT 0x0010 +#define SX_DEBUG_RX 0x0020 +#define SX_DEBUG_TX 0x0040 +#define SX_DEBUG_IRQ 0x0080 +#define SX_DEBUG_OPEN 0x0100 +#define SX_DEBUG_TERMIOS 0x0200 +#define SX_DEBUG_SIGNALS 0x0400 +#define SX_DEBUG_FIFO 0x0800 + + +#define func_enter() dprintk(SX_DEBUG_FLOW, "io8: enter %s\n", __func__) +#define func_exit() dprintk(SX_DEBUG_FLOW, "io8: exit %s\n", __func__) + + +/* Configurable options: */ + +/* Am I paranoid or not ? ;-) */ +#define SPECIALIX_PARANOIA_CHECK + +/* + * The following defines are mostly for testing purposes. But if you need + * some nice reporting in your syslog, you can define them also. + */ +#undef SX_REPORT_FIFO +#undef SX_REPORT_OVERRUN + + + + +#define SPECIALIX_LEGAL_FLAGS \ + (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \ + ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ + ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) + +static struct tty_driver *specialix_driver; + +static struct specialix_board sx_board[SX_NBOARD] = { + { 0, SX_IOBASE1, 9, }, + { 0, SX_IOBASE2, 11, }, + { 0, SX_IOBASE3, 12, }, + { 0, SX_IOBASE4, 15, }, +}; + +static struct specialix_port sx_port[SX_NBOARD * SX_NPORT]; + + +static int sx_paranoia_check(struct specialix_port const *port, + char *name, const char *routine) +{ +#ifdef SPECIALIX_PARANOIA_CHECK + static const char *badmagic = KERN_ERR + "sx: Warning: bad specialix port magic number for device %s in %s\n"; + static const char *badinfo = KERN_ERR + "sx: Warning: null specialix port for device %s in %s\n"; + + if (!port) { + printk(badinfo, name, routine); + return 1; + } + if (port->magic != SPECIALIX_MAGIC) { + printk(badmagic, name, routine); + return 1; + } +#endif + return 0; +} + + +/* + * + * Service functions for specialix IO8+ driver. + * + */ + +/* Get board number from pointer */ +static inline int board_No(struct specialix_board *bp) +{ + return bp - sx_board; +} + + +/* Get port number from pointer */ +static inline int port_No(struct specialix_port const *port) +{ + return SX_PORT(port - sx_port); +} + + +/* Get pointer to board from pointer to port */ +static inline struct specialix_board *port_Board( + struct specialix_port const *port) +{ + return &sx_board[SX_BOARD(port - sx_port)]; +} + + +/* Input Byte from CL CD186x register */ +static inline unsigned char sx_in(struct specialix_board *bp, + unsigned short reg) +{ + bp->reg = reg | 0x80; + outb(reg | 0x80, bp->base + SX_ADDR_REG); + return inb(bp->base + SX_DATA_REG); +} + + +/* Output Byte to CL CD186x register */ +static inline void sx_out(struct specialix_board *bp, unsigned short reg, + unsigned char val) +{ + bp->reg = reg | 0x80; + outb(reg | 0x80, bp->base + SX_ADDR_REG); + outb(val, bp->base + SX_DATA_REG); +} + + +/* Input Byte from CL CD186x register */ +static inline unsigned char sx_in_off(struct specialix_board *bp, + unsigned short reg) +{ + bp->reg = reg; + outb(reg, bp->base + SX_ADDR_REG); + return inb(bp->base + SX_DATA_REG); +} + + +/* Output Byte to CL CD186x register */ +static inline void sx_out_off(struct specialix_board *bp, + unsigned short reg, unsigned char val) +{ + bp->reg = reg; + outb(reg, bp->base + SX_ADDR_REG); + outb(val, bp->base + SX_DATA_REG); +} + + +/* Wait for Channel Command Register ready */ +static void sx_wait_CCR(struct specialix_board *bp) +{ + unsigned long delay, flags; + unsigned char ccr; + + for (delay = SX_CCR_TIMEOUT; delay; delay--) { + spin_lock_irqsave(&bp->lock, flags); + ccr = sx_in(bp, CD186x_CCR); + spin_unlock_irqrestore(&bp->lock, flags); + if (!ccr) + return; + udelay(1); + } + + printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); +} + + +/* Wait for Channel Command Register ready */ +static void sx_wait_CCR_off(struct specialix_board *bp) +{ + unsigned long delay; + unsigned char crr; + unsigned long flags; + + for (delay = SX_CCR_TIMEOUT; delay; delay--) { + spin_lock_irqsave(&bp->lock, flags); + crr = sx_in_off(bp, CD186x_CCR); + spin_unlock_irqrestore(&bp->lock, flags); + if (!crr) + return; + udelay(1); + } + + printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); +} + + +/* + * specialix IO8+ IO range functions. + */ + +static int sx_request_io_range(struct specialix_board *bp) +{ + return request_region(bp->base, + bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE, + "specialix IO8+") == NULL; +} + + +static void sx_release_io_range(struct specialix_board *bp) +{ + release_region(bp->base, bp->flags & SX_BOARD_IS_PCI ? + SX_PCI_IO_SPACE : SX_IO_SPACE); +} + + +/* Set the IRQ using the RTS lines that run to the PAL on the board.... */ +static int sx_set_irq(struct specialix_board *bp) +{ + int virq; + int i; + unsigned long flags; + + if (bp->flags & SX_BOARD_IS_PCI) + return 1; + switch (bp->irq) { + /* In the same order as in the docs... */ + case 15: + virq = 0; + break; + case 12: + virq = 1; + break; + case 11: + virq = 2; + break; + case 9: + virq = 3; + break; + default:printk(KERN_ERR + "Speclialix: cannot set irq to %d.\n", bp->irq); + return 0; + } + spin_lock_irqsave(&bp->lock, flags); + for (i = 0; i < 2; i++) { + sx_out(bp, CD186x_CAR, i); + sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0); + } + spin_unlock_irqrestore(&bp->lock, flags); + return 1; +} + + +/* Reset and setup CD186x chip */ +static int sx_init_CD186x(struct specialix_board *bp) +{ + unsigned long flags; + int scaler; + int rv = 1; + + func_enter(); + sx_wait_CCR_off(bp); /* Wait for CCR ready */ + spin_lock_irqsave(&bp->lock, flags); + sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */ + spin_unlock_irqrestore(&bp->lock, flags); + msleep(50); /* Delay 0.05 sec */ + spin_lock_irqsave(&bp->lock, flags); + sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */ + sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */ + sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */ + sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */ + sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */ + /* Set RegAckEn */ + sx_out_off(bp, CD186x_SRCR, sx_in(bp, CD186x_SRCR) | SRCR_REGACKEN); + + /* Setting up prescaler. We need 4 ticks per 1 ms */ + scaler = SX_OSCFREQ/SPECIALIX_TPS; + + sx_out_off(bp, CD186x_PPRH, scaler >> 8); + sx_out_off(bp, CD186x_PPRL, scaler & 0xff); + spin_unlock_irqrestore(&bp->lock, flags); + + if (!sx_set_irq(bp)) { + /* Figure out how to pass this along... */ + printk(KERN_ERR "Cannot set irq to %d.\n", bp->irq); + rv = 0; + } + + func_exit(); + return rv; +} + + +static int read_cross_byte(struct specialix_board *bp, int reg, int bit) +{ + int i; + int t; + unsigned long flags; + + spin_lock_irqsave(&bp->lock, flags); + for (i = 0, t = 0; i < 8; i++) { + sx_out_off(bp, CD186x_CAR, i); + if (sx_in_off(bp, reg) & bit) + t |= 1 << i; + } + spin_unlock_irqrestore(&bp->lock, flags); + + return t; +} + + +/* Main probing routine, also sets irq. */ +static int sx_probe(struct specialix_board *bp) +{ + unsigned char val1, val2; + int rev; + int chip; + + func_enter(); + + if (sx_request_io_range(bp)) { + func_exit(); + return 1; + } + + /* Are the I/O ports here ? */ + sx_out_off(bp, CD186x_PPRL, 0x5a); + udelay(1); + val1 = sx_in_off(bp, CD186x_PPRL); + + sx_out_off(bp, CD186x_PPRL, 0xa5); + udelay(1); + val2 = sx_in_off(bp, CD186x_PPRL); + + + if (val1 != 0x5a || val2 != 0xa5) { + printk(KERN_INFO + "sx%d: specialix IO8+ Board at 0x%03x not found.\n", + board_No(bp), bp->base); + sx_release_io_range(bp); + func_exit(); + return 1; + } + + /* Check the DSR lines that Specialix uses as board + identification */ + val1 = read_cross_byte(bp, CD186x_MSVR, MSVR_DSR); + val2 = read_cross_byte(bp, CD186x_MSVR, MSVR_RTS); + dprintk(SX_DEBUG_INIT, + "sx%d: DSR lines are: %02x, rts lines are: %02x\n", + board_No(bp), val1, val2); + + /* They managed to switch the bit order between the docs and + the IO8+ card. The new PCI card now conforms to old docs. + They changed the PCI docs to reflect the situation on the + old card. */ + val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2; + if (val1 != val2) { + printk(KERN_INFO + "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n", + board_No(bp), val2, bp->base, val1); + sx_release_io_range(bp); + func_exit(); + return 1; + } + + + /* Reset CD186x again */ + if (!sx_init_CD186x(bp)) { + sx_release_io_range(bp); + func_exit(); + return 1; + } + + sx_request_io_range(bp); + bp->flags |= SX_BOARD_PRESENT; + + /* Chip revcode pkgtype + GFRCR SRCR bit 7 + CD180 rev B 0x81 0 + CD180 rev C 0x82 0 + CD1864 rev A 0x82 1 + CD1865 rev A 0x83 1 -- Do not use!!! Does not work. + CD1865 rev B 0x84 1 + -- Thanks to Gwen Wang, Cirrus Logic. + */ + + switch (sx_in_off(bp, CD186x_GFRCR)) { + case 0x82: + chip = 1864; + rev = 'A'; + break; + case 0x83: + chip = 1865; + rev = 'A'; + break; + case 0x84: + chip = 1865; + rev = 'B'; + break; + case 0x85: + chip = 1865; + rev = 'C'; + break; /* Does not exist at this time */ + default: + chip = -1; + rev = 'x'; + } + + dprintk(SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR)); + + printk(KERN_INFO + "sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n", + board_No(bp), bp->base, bp->irq, chip, rev); + + func_exit(); + return 0; +} + +/* + * + * Interrupt processing routines. + * */ + +static struct specialix_port *sx_get_port(struct specialix_board *bp, + unsigned char const *what) +{ + unsigned char channel; + struct specialix_port *port = NULL; + + channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF; + dprintk(SX_DEBUG_CHAN, "channel: %d\n", channel); + if (channel < CD186x_NCH) { + port = &sx_port[board_No(bp) * SX_NPORT + channel]; + dprintk(SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n", + board_No(bp) * SX_NPORT + channel, port, + port->port.flags & ASYNC_INITIALIZED); + + if (port->port.flags & ASYNC_INITIALIZED) { + dprintk(SX_DEBUG_CHAN, "port: %d %p\n", channel, port); + func_exit(); + return port; + } + } + printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", + board_No(bp), what, channel); + return NULL; +} + + +static void sx_receive_exc(struct specialix_board *bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char status; + unsigned char ch, flag; + + func_enter(); + + port = sx_get_port(bp, "Receive"); + if (!port) { + dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n"); + func_exit(); + return; + } + tty = port->port.tty; + + status = sx_in(bp, CD186x_RCSR); + + dprintk(SX_DEBUG_RX, "status: 0x%x\n", status); + if (status & RCSR_OE) { + port->overrun++; + dprintk(SX_DEBUG_FIFO, + "sx%d: port %d: Overrun. Total %ld overruns.\n", + board_No(bp), port_No(port), port->overrun); + } + status &= port->mark_mask; + + /* This flip buffer check needs to be below the reading of the + status register to reset the chip's IRQ.... */ + if (tty_buffer_request_room(tty, 1) == 0) { + dprintk(SX_DEBUG_FIFO, + "sx%d: port %d: Working around flip buffer overflow.\n", + board_No(bp), port_No(port)); + func_exit(); + return; + } + + ch = sx_in(bp, CD186x_RDR); + if (!status) { + func_exit(); + return; + } + if (status & RCSR_TOUT) { + printk(KERN_INFO + "sx%d: port %d: Receiver timeout. Hardware problems ?\n", + board_No(bp), port_No(port)); + func_exit(); + return; + + } else if (status & RCSR_BREAK) { + dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n", + board_No(bp), port_No(port)); + flag = TTY_BREAK; + if (port->port.flags & ASYNC_SAK) + do_SAK(tty); + + } else if (status & RCSR_PE) + flag = TTY_PARITY; + + else if (status & RCSR_FE) + flag = TTY_FRAME; + + else if (status & RCSR_OE) + flag = TTY_OVERRUN; + + else + flag = TTY_NORMAL; + + if (tty_insert_flip_char(tty, ch, flag)) + tty_flip_buffer_push(tty); + func_exit(); +} + + +static void sx_receive(struct specialix_board *bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char count; + + func_enter(); + + port = sx_get_port(bp, "Receive"); + if (port == NULL) { + dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n"); + func_exit(); + return; + } + tty = port->port.tty; + + count = sx_in(bp, CD186x_RDCR); + dprintk(SX_DEBUG_RX, "port: %p: count: %d\n", port, count); + port->hits[count > 8 ? 9 : count]++; + + while (count--) + tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL); + tty_flip_buffer_push(tty); + func_exit(); +} + + +static void sx_transmit(struct specialix_board *bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char count; + + func_enter(); + port = sx_get_port(bp, "Transmit"); + if (port == NULL) { + func_exit(); + return; + } + dprintk(SX_DEBUG_TX, "port: %p\n", port); + tty = port->port.tty; + + if (port->IER & IER_TXEMPTY) { + /* FIFO drained */ + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXEMPTY; + sx_out(bp, CD186x_IER, port->IER); + func_exit(); + return; + } + + if ((port->xmit_cnt <= 0 && !port->break_length) + || tty->stopped || tty->hw_stopped) { + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_IER, port->IER); + func_exit(); + return; + } + + if (port->break_length) { + if (port->break_length > 0) { + if (port->COR2 & COR2_ETC) { + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_SBRK); + port->COR2 &= ~COR2_ETC; + } + count = min_t(int, port->break_length, 0xff); + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_DELAY); + sx_out(bp, CD186x_TDR, count); + port->break_length -= count; + if (port->break_length == 0) + port->break_length--; + } else { + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_EBRK); + sx_out(bp, CD186x_COR2, port->COR2); + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG2); + port->break_length = 0; + } + + func_exit(); + return; + } + + count = CD186x_NFIFO; + do { + sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]); + port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); + if (--port->xmit_cnt <= 0) + break; + } while (--count > 0); + + if (port->xmit_cnt <= 0) { + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_IER, port->IER); + } + if (port->xmit_cnt <= port->wakeup_chars) + tty_wakeup(tty); + + func_exit(); +} + + +static void sx_check_modem(struct specialix_board *bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char mcr; + int msvr_cd; + + dprintk(SX_DEBUG_SIGNALS, "Modem intr. "); + port = sx_get_port(bp, "Modem"); + if (port == NULL) + return; + + tty = port->port.tty; + + mcr = sx_in(bp, CD186x_MCR); + + if ((mcr & MCR_CDCHG)) { + dprintk(SX_DEBUG_SIGNALS, "CD just changed... "); + msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD; + if (msvr_cd) { + dprintk(SX_DEBUG_SIGNALS, "Waking up guys in open.\n"); + wake_up_interruptible(&port->port.open_wait); + } else { + dprintk(SX_DEBUG_SIGNALS, "Sending HUP.\n"); + tty_hangup(tty); + } + } + +#ifdef SPECIALIX_BRAIN_DAMAGED_CTS + if (mcr & MCR_CTSCHG) { + if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + tty_wakeup(tty); + } else { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + sx_out(bp, CD186x_IER, port->IER); + } + if (mcr & MCR_DSSXHG) { + if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + tty_wakeup(tty); + } else { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + sx_out(bp, CD186x_IER, port->IER); + } +#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */ + + /* Clear change bits */ + sx_out(bp, CD186x_MCR, 0); +} + + +/* The main interrupt processing routine */ +static irqreturn_t sx_interrupt(int dummy, void *dev_id) +{ + unsigned char status; + unsigned char ack; + struct specialix_board *bp = dev_id; + unsigned long loop = 0; + int saved_reg; + unsigned long flags; + + func_enter(); + + spin_lock_irqsave(&bp->lock, flags); + + dprintk(SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__, + port_No(sx_get_port(bp, "INT")), + SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1); + if (!(bp->flags & SX_BOARD_ACTIVE)) { + dprintk(SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", + bp->irq); + spin_unlock_irqrestore(&bp->lock, flags); + func_exit(); + return IRQ_NONE; + } + + saved_reg = bp->reg; + + while (++loop < 16) { + status = sx_in(bp, CD186x_SRSR) & + (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint); + if (status == 0) + break; + if (status & SRSR_RREQint) { + ack = sx_in(bp, CD186x_RRAR); + + if (ack == (SX_ID | GIVR_IT_RCV)) + sx_receive(bp); + else if (ack == (SX_ID | GIVR_IT_REXC)) + sx_receive_exc(bp); + else + printk(KERN_ERR + "sx%d: status: 0x%x Bad receive ack 0x%02x.\n", + board_No(bp), status, ack); + + } else if (status & SRSR_TREQint) { + ack = sx_in(bp, CD186x_TRAR); + + if (ack == (SX_ID | GIVR_IT_TX)) + sx_transmit(bp); + else + printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n", + board_No(bp), status, ack, + port_No(sx_get_port(bp, "Int"))); + } else if (status & SRSR_MREQint) { + ack = sx_in(bp, CD186x_MRAR); + + if (ack == (SX_ID | GIVR_IT_MODEM)) + sx_check_modem(bp); + else + printk(KERN_ERR + "sx%d: status: 0x%x Bad modem ack 0x%02x.\n", + board_No(bp), status, ack); + + } + + sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */ + } + bp->reg = saved_reg; + outb(bp->reg, bp->base + SX_ADDR_REG); + spin_unlock_irqrestore(&bp->lock, flags); + func_exit(); + return IRQ_HANDLED; +} + + +/* + * Routines for open & close processing. + */ + +static void turn_ints_off(struct specialix_board *bp) +{ + unsigned long flags; + + func_enter(); + spin_lock_irqsave(&bp->lock, flags); + (void) sx_in_off(bp, 0); /* Turn off interrupts. */ + spin_unlock_irqrestore(&bp->lock, flags); + + func_exit(); +} + +static void turn_ints_on(struct specialix_board *bp) +{ + unsigned long flags; + + func_enter(); + + spin_lock_irqsave(&bp->lock, flags); + (void) sx_in(bp, 0); /* Turn ON interrupts. */ + spin_unlock_irqrestore(&bp->lock, flags); + + func_exit(); +} + + +/* Called with disabled interrupts */ +static int sx_setup_board(struct specialix_board *bp) +{ + int error; + + if (bp->flags & SX_BOARD_ACTIVE) + return 0; + + if (bp->flags & SX_BOARD_IS_PCI) + error = request_irq(bp->irq, sx_interrupt, + IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp); + else + error = request_irq(bp->irq, sx_interrupt, + IRQF_DISABLED, "specialix IO8+", bp); + + if (error) + return error; + + turn_ints_on(bp); + bp->flags |= SX_BOARD_ACTIVE; + + return 0; +} + + +/* Called with disabled interrupts */ +static void sx_shutdown_board(struct specialix_board *bp) +{ + func_enter(); + + if (!(bp->flags & SX_BOARD_ACTIVE)) { + func_exit(); + return; + } + + bp->flags &= ~SX_BOARD_ACTIVE; + + dprintk(SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n", + bp->irq, board_No(bp)); + free_irq(bp->irq, bp); + turn_ints_off(bp); + func_exit(); +} + +static unsigned int sx_crtscts(struct tty_struct *tty) +{ + if (sx_rtscts) + return C_CRTSCTS(tty); + return 1; +} + +/* + * Setting up port characteristics. + * Must be called with disabled interrupts + */ +static void sx_change_speed(struct specialix_board *bp, + struct specialix_port *port) +{ + struct tty_struct *tty; + unsigned long baud; + long tmp; + unsigned char cor1 = 0, cor3 = 0; + unsigned char mcor1 = 0, mcor2 = 0; + static unsigned long again; + unsigned long flags; + + func_enter(); + + tty = port->port.tty; + if (!tty || !tty->termios) { + func_exit(); + return; + } + + port->IER = 0; + port->COR2 = 0; + /* Select port on the board */ + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CAR, port_No(port)); + + /* The Specialix board doens't implement the RTS lines. + They are used to set the IRQ level. Don't touch them. */ + if (sx_crtscts(tty)) + port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS); + else + port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS); + spin_unlock_irqrestore(&bp->lock, flags); + dprintk(SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR); + baud = tty_get_baud_rate(tty); + + if (baud == 38400) { + if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baud = 57600; + if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baud = 115200; + } + + if (!baud) { + /* Drop DTR & exit */ + dprintk(SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n"); + if (!sx_crtscts(tty)) { + port->MSVR &= ~MSVR_DTR; + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_MSVR, port->MSVR); + spin_unlock_irqrestore(&bp->lock, flags); + } else + dprintk(SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n"); + return; + } else { + /* Set DTR on */ + if (!sx_crtscts(tty)) + port->MSVR |= MSVR_DTR; + } + + /* + * Now we must calculate some speed depended things + */ + + /* Set baud rate for port */ + tmp = port->custom_divisor ; + if (tmp) + printk(KERN_INFO + "sx%d: Using custom baud rate divisor %ld. \n" + "This is an untested option, please be careful.\n", + port_No(port), tmp); + else + tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) / + CD186x_TPC); + + if (tmp < 0x10 && time_before(again, jiffies)) { + again = jiffies + HZ * 60; + /* Page 48 of version 2.0 of the CL-CD1865 databook */ + if (tmp >= 12) { + printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Performance degradation is possible.\n" + "Read specialix.txt for more info.\n", + port_No(port), tmp); + } else { + printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Warning: overstressing Cirrus chip. This might not work.\n" + "Read specialix.txt for more info.\n", port_No(port), tmp); + } + } + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); + sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); + sx_out(bp, CD186x_RBPRL, tmp & 0xff); + sx_out(bp, CD186x_TBPRL, tmp & 0xff); + spin_unlock_irqrestore(&bp->lock, flags); + if (port->custom_divisor) + baud = (SX_OSCFREQ + port->custom_divisor/2) / + port->custom_divisor; + baud = (baud + 5) / 10; /* Estimated CPS */ + + /* Two timer ticks seems enough to wakeup something like SLIP driver */ + tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO; + port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? + SERIAL_XMIT_SIZE - 1 : tmp); + + /* Receiver timeout will be transmission time for 1.5 chars */ + tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud; + tmp = (tmp > 0xff) ? 0xff : tmp; + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_RTPR, tmp); + spin_unlock_irqrestore(&bp->lock, flags); + switch (C_CSIZE(tty)) { + case CS5: + cor1 |= COR1_5BITS; + break; + case CS6: + cor1 |= COR1_6BITS; + break; + case CS7: + cor1 |= COR1_7BITS; + break; + case CS8: + cor1 |= COR1_8BITS; + break; + } + + if (C_CSTOPB(tty)) + cor1 |= COR1_2SB; + + cor1 |= COR1_IGNORE; + if (C_PARENB(tty)) { + cor1 |= COR1_NORMPAR; + if (C_PARODD(tty)) + cor1 |= COR1_ODDP; + if (I_INPCK(tty)) + cor1 &= ~COR1_IGNORE; + } + /* Set marking of some errors */ + port->mark_mask = RCSR_OE | RCSR_TOUT; + if (I_INPCK(tty)) + port->mark_mask |= RCSR_FE | RCSR_PE; + if (I_BRKINT(tty) || I_PARMRK(tty)) + port->mark_mask |= RCSR_BREAK; + if (I_IGNPAR(tty)) + port->mark_mask &= ~(RCSR_FE | RCSR_PE); + if (I_IGNBRK(tty)) { + port->mark_mask &= ~RCSR_BREAK; + if (I_IGNPAR(tty)) + /* Real raw mode. Ignore all */ + port->mark_mask &= ~RCSR_OE; + } + /* Enable Hardware Flow Control */ + if (C_CRTSCTS(tty)) { +#ifdef SPECIALIX_BRAIN_DAMAGED_CTS + port->IER |= IER_DSR | IER_CTS; + mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; + mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; + spin_lock_irqsave(&bp->lock, flags); + tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & + (MSVR_CTS|MSVR_DSR)); + spin_unlock_irqrestore(&bp->lock, flags); +#else + port->COR2 |= COR2_CTSAE; +#endif + } + /* Enable Software Flow Control. FIXME: I'm not sure about this */ + /* Some people reported that it works, but I still doubt it */ + if (I_IXON(tty)) { + port->COR2 |= COR2_TXIBE; + cor3 |= (COR3_FCT | COR3_SCDE); + if (I_IXANY(tty)) + port->COR2 |= COR2_IXM; + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_SCHR1, START_CHAR(tty)); + sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty)); + sx_out(bp, CD186x_SCHR3, START_CHAR(tty)); + sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty)); + spin_unlock_irqrestore(&bp->lock, flags); + } + if (!C_CLOCAL(tty)) { + /* Enable CD check */ + port->IER |= IER_CD; + mcor1 |= MCOR1_CDZD; + mcor2 |= MCOR2_CDOD; + } + + if (C_CREAD(tty)) + /* Enable receiver */ + port->IER |= IER_RXD; + + /* Set input FIFO size (1-8 bytes) */ + cor3 |= sx_rxfifo; + /* Setting up CD186x channel registers */ + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_COR1, cor1); + sx_out(bp, CD186x_COR2, port->COR2); + sx_out(bp, CD186x_COR3, cor3); + spin_unlock_irqrestore(&bp->lock, flags); + /* Make CD186x know about registers change */ + sx_wait_CCR(bp); + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); + /* Setting up modem option registers */ + dprintk(SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", + mcor1, mcor2); + sx_out(bp, CD186x_MCOR1, mcor1); + sx_out(bp, CD186x_MCOR2, mcor2); + spin_unlock_irqrestore(&bp->lock, flags); + /* Enable CD186x transmitter & receiver */ + sx_wait_CCR(bp); + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN); + /* Enable interrupts */ + sx_out(bp, CD186x_IER, port->IER); + /* And finally set the modem lines... */ + sx_out(bp, CD186x_MSVR, port->MSVR); + spin_unlock_irqrestore(&bp->lock, flags); + + func_exit(); +} + + +/* Must be called with interrupts enabled */ +static int sx_setup_port(struct specialix_board *bp, + struct specialix_port *port) +{ + unsigned long flags; + + func_enter(); + + if (port->port.flags & ASYNC_INITIALIZED) { + func_exit(); + return 0; + } + + if (!port->xmit_buf) { + /* We may sleep in get_zeroed_page() */ + unsigned long tmp; + + tmp = get_zeroed_page(GFP_KERNEL); + if (tmp == 0L) { + func_exit(); + return -ENOMEM; + } + + if (port->xmit_buf) { + free_page(tmp); + func_exit(); + return -ERESTARTSYS; + } + port->xmit_buf = (unsigned char *) tmp; + } + + spin_lock_irqsave(&port->lock, flags); + + if (port->port.tty) + clear_bit(TTY_IO_ERROR, &port->port.tty->flags); + + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + sx_change_speed(bp, port); + port->port.flags |= ASYNC_INITIALIZED; + + spin_unlock_irqrestore(&port->lock, flags); + + + func_exit(); + return 0; +} + + +/* Must be called with interrupts disabled */ +static void sx_shutdown_port(struct specialix_board *bp, + struct specialix_port *port) +{ + struct tty_struct *tty; + int i; + unsigned long flags; + + func_enter(); + + if (!(port->port.flags & ASYNC_INITIALIZED)) { + func_exit(); + return; + } + + if (sx_debug & SX_DEBUG_FIFO) { + dprintk(SX_DEBUG_FIFO, + "sx%d: port %d: %ld overruns, FIFO hits [ ", + board_No(bp), port_No(port), port->overrun); + for (i = 0; i < 10; i++) + dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]); + dprintk(SX_DEBUG_FIFO, "].\n"); + } + + if (port->xmit_buf) { + free_page((unsigned long) port->xmit_buf); + port->xmit_buf = NULL; + } + + /* Select port */ + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CAR, port_No(port)); + + tty = port->port.tty; + if (tty == NULL || C_HUPCL(tty)) { + /* Drop DTR */ + sx_out(bp, CD186x_MSVDTR, 0); + } + spin_unlock_irqrestore(&bp->lock, flags); + /* Reset port */ + sx_wait_CCR(bp); + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CCR, CCR_SOFTRESET); + /* Disable all interrupts from this port */ + port->IER = 0; + sx_out(bp, CD186x_IER, port->IER); + spin_unlock_irqrestore(&bp->lock, flags); + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); + port->port.flags &= ~ASYNC_INITIALIZED; + + if (!bp->count) + sx_shutdown_board(bp); + func_exit(); +} + + +static int block_til_ready(struct tty_struct *tty, struct file *filp, + struct specialix_port *port) +{ + DECLARE_WAITQUEUE(wait, current); + struct specialix_board *bp = port_Board(port); + int retval; + int do_clocal = 0; + int CD; + unsigned long flags; + + func_enter(); + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->port.close_wait); + if (port->port.flags & ASYNC_HUP_NOTIFY) { + func_exit(); + return -EAGAIN; + } else { + func_exit(); + return -ERESTARTSYS; + } + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + port->port.flags |= ASYNC_NORMAL_ACTIVE; + func_exit(); + return 0; + } + + if (C_CLOCAL(tty)) + do_clocal = 1; + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&port->port.open_wait, &wait); + spin_lock_irqsave(&port->lock, flags); + if (!tty_hung_up_p(filp)) + port->port.count--; + spin_unlock_irqrestore(&port->lock, flags); + port->port.blocked_open++; + while (1) { + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CAR, port_No(port)); + CD = sx_in(bp, CD186x_MSVR) & MSVR_CD; + if (sx_crtscts(tty)) { + /* Activate RTS */ + port->MSVR |= MSVR_DTR; /* WTF? */ + sx_out(bp, CD186x_MSVR, port->MSVR); + } else { + /* Activate DTR */ + port->MSVR |= MSVR_DTR; + sx_out(bp, CD186x_MSVR, port->MSVR); + } + spin_unlock_irqrestore(&bp->lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(port->port.flags & ASYNC_INITIALIZED)) { + if (port->port.flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + if (!(port->port.flags & ASYNC_CLOSING) && + (do_clocal || CD)) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + tty_unlock(); + schedule(); + tty_lock(); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->port.open_wait, &wait); + spin_lock_irqsave(&port->lock, flags); + if (!tty_hung_up_p(filp)) + port->port.count++; + port->port.blocked_open--; + spin_unlock_irqrestore(&port->lock, flags); + if (retval) { + func_exit(); + return retval; + } + + port->port.flags |= ASYNC_NORMAL_ACTIVE; + func_exit(); + return 0; +} + + +static int sx_open(struct tty_struct *tty, struct file *filp) +{ + int board; + int error; + struct specialix_port *port; + struct specialix_board *bp; + int i; + unsigned long flags; + + func_enter(); + + board = SX_BOARD(tty->index); + + if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) { + func_exit(); + return -ENODEV; + } + + bp = &sx_board[board]; + port = sx_port + board * SX_NPORT + SX_PORT(tty->index); + port->overrun = 0; + for (i = 0; i < 10; i++) + port->hits[i] = 0; + + dprintk(SX_DEBUG_OPEN, + "Board = %d, bp = %p, port = %p, portno = %d.\n", + board, bp, port, SX_PORT(tty->index)); + + if (sx_paranoia_check(port, tty->name, "sx_open")) { + func_enter(); + return -ENODEV; + } + + error = sx_setup_board(bp); + if (error) { + func_exit(); + return error; + } + + spin_lock_irqsave(&bp->lock, flags); + port->port.count++; + bp->count++; + tty->driver_data = port; + port->port.tty = tty; + spin_unlock_irqrestore(&bp->lock, flags); + + error = sx_setup_port(bp, port); + if (error) { + func_enter(); + return error; + } + + error = block_til_ready(tty, filp, port); + if (error) { + func_enter(); + return error; + } + + func_exit(); + return 0; +} + +static void sx_flush_buffer(struct tty_struct *tty) +{ + struct specialix_port *port = tty->driver_data; + unsigned long flags; + struct specialix_board *bp; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) { + func_exit(); + return; + } + + bp = port_Board(port); + spin_lock_irqsave(&port->lock, flags); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + spin_unlock_irqrestore(&port->lock, flags); + tty_wakeup(tty); + + func_exit(); +} + +static void sx_close(struct tty_struct *tty, struct file *filp) +{ + struct specialix_port *port = tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + unsigned long timeout; + + func_enter(); + if (!port || sx_paranoia_check(port, tty->name, "close")) { + func_exit(); + return; + } + spin_lock_irqsave(&port->lock, flags); + + if (tty_hung_up_p(filp)) { + spin_unlock_irqrestore(&port->lock, flags); + func_exit(); + return; + } + + bp = port_Board(port); + if (tty->count == 1 && port->port.count != 1) { + printk(KERN_ERR "sx%d: sx_close: bad port count;" + " tty->count is 1, port count is %d\n", + board_No(bp), port->port.count); + port->port.count = 1; + } + + if (port->port.count > 1) { + port->port.count--; + bp->count--; + + spin_unlock_irqrestore(&port->lock, flags); + + func_exit(); + return; + } + port->port.flags |= ASYNC_CLOSING; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + spin_unlock_irqrestore(&port->lock, flags); + dprintk(SX_DEBUG_OPEN, "Closing\n"); + if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->port.closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + dprintk(SX_DEBUG_OPEN, "Closed\n"); + port->IER &= ~IER_RXD; + if (port->port.flags & ASYNC_INITIALIZED) { + port->IER &= ~IER_TXRDY; + port->IER |= IER_TXEMPTY; + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + spin_unlock_irqrestore(&bp->lock, flags); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies+HZ; + while (port->IER & IER_TXEMPTY) { + set_current_state(TASK_INTERRUPTIBLE); + msleep_interruptible(jiffies_to_msecs(port->timeout)); + if (time_after(jiffies, timeout)) { + printk(KERN_INFO "Timeout waiting for close\n"); + break; + } + } + + } + + if (--bp->count < 0) { + printk(KERN_ERR + "sx%d: sx_shutdown_port: bad board count: %d port: %d\n", + board_No(bp), bp->count, tty->index); + bp->count = 0; + } + if (--port->port.count < 0) { + printk(KERN_ERR + "sx%d: sx_close: bad port count for tty%d: %d\n", + board_No(bp), port_No(port), port->port.count); + port->port.count = 0; + } + + sx_shutdown_port(bp, port); + sx_flush_buffer(tty); + tty_ldisc_flush(tty); + spin_lock_irqsave(&port->lock, flags); + tty->closing = 0; + port->port.tty = NULL; + spin_unlock_irqrestore(&port->lock, flags); + if (port->port.blocked_open) { + if (port->port.close_delay) + msleep_interruptible( + jiffies_to_msecs(port->port.close_delay)); + wake_up_interruptible(&port->port.open_wait); + } + port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&port->port.close_wait); + + func_exit(); +} + + +static int sx_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct specialix_port *port = tty->driver_data; + struct specialix_board *bp; + int c, total = 0; + unsigned long flags; + + func_enter(); + if (sx_paranoia_check(port, tty->name, "sx_write")) { + func_exit(); + return 0; + } + + bp = port_Board(port); + + if (!port->xmit_buf) { + func_exit(); + return 0; + } + + while (1) { + spin_lock_irqsave(&port->lock, flags); + c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + if (c <= 0) { + spin_unlock_irqrestore(&port->lock, flags); + break; + } + memcpy(port->xmit_buf + port->xmit_head, buf, c); + port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + port->xmit_cnt += c; + spin_unlock_irqrestore(&port->lock, flags); + + buf += c; + count -= c; + total += c; + } + + spin_lock_irqsave(&bp->lock, flags); + if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && + !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + } + spin_unlock_irqrestore(&bp->lock, flags); + func_exit(); + + return total; +} + + +static int sx_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct specialix_port *port = tty->driver_data; + unsigned long flags; + struct specialix_board *bp; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_put_char")) { + func_exit(); + return 0; + } + dprintk(SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf); + if (!port->xmit_buf) { + func_exit(); + return 0; + } + bp = port_Board(port); + spin_lock_irqsave(&port->lock, flags); + + dprintk(SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", + port->xmit_cnt, port->xmit_buf); + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1 || !port->xmit_buf) { + spin_unlock_irqrestore(&port->lock, flags); + dprintk(SX_DEBUG_TX, "Exit size\n"); + func_exit(); + return 0; + } + dprintk(SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf); + port->xmit_buf[port->xmit_head++] = ch; + port->xmit_head &= SERIAL_XMIT_SIZE - 1; + port->xmit_cnt++; + spin_unlock_irqrestore(&port->lock, flags); + + func_exit(); + return 1; +} + + +static void sx_flush_chars(struct tty_struct *tty) +{ + struct specialix_port *port = tty->driver_data; + unsigned long flags; + struct specialix_board *bp = port_Board(port); + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) { + func_exit(); + return; + } + if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !port->xmit_buf) { + func_exit(); + return; + } + spin_lock_irqsave(&bp->lock, flags); + port->IER |= IER_TXRDY; + sx_out(port_Board(port), CD186x_CAR, port_No(port)); + sx_out(port_Board(port), CD186x_IER, port->IER); + spin_unlock_irqrestore(&bp->lock, flags); + + func_exit(); +} + + +static int sx_write_room(struct tty_struct *tty) +{ + struct specialix_port *port = tty->driver_data; + int ret; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_write_room")) { + func_exit(); + return 0; + } + + ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; + if (ret < 0) + ret = 0; + + func_exit(); + return ret; +} + + +static int sx_chars_in_buffer(struct tty_struct *tty) +{ + struct specialix_port *port = tty->driver_data; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) { + func_exit(); + return 0; + } + func_exit(); + return port->xmit_cnt; +} + +static int sx_tiocmget(struct tty_struct *tty) +{ + struct specialix_port *port = tty->driver_data; + struct specialix_board *bp; + unsigned char status; + unsigned int result; + unsigned long flags; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, __func__)) { + func_exit(); + return -ENODEV; + } + + bp = port_Board(port); + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CAR, port_No(port)); + status = sx_in(bp, CD186x_MSVR); + spin_unlock_irqrestore(&bp->lock, flags); + dprintk(SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n", + port_No(port), status, sx_in(bp, CD186x_CAR)); + dprintk(SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port); + if (sx_crtscts(port->port.tty)) { + result = TIOCM_DTR | TIOCM_DSR + | ((status & MSVR_DTR) ? TIOCM_RTS : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } else { + result = TIOCM_RTS | TIOCM_DSR + | ((status & MSVR_DTR) ? TIOCM_DTR : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } + + func_exit(); + + return result; +} + + +static int sx_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct specialix_port *port = tty->driver_data; + unsigned long flags; + struct specialix_board *bp; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, __func__)) { + func_exit(); + return -ENODEV; + } + + bp = port_Board(port); + + spin_lock_irqsave(&port->lock, flags); + if (sx_crtscts(port->port.tty)) { + if (set & TIOCM_RTS) + port->MSVR |= MSVR_DTR; + } else { + if (set & TIOCM_DTR) + port->MSVR |= MSVR_DTR; + } + if (sx_crtscts(port->port.tty)) { + if (clear & TIOCM_RTS) + port->MSVR &= ~MSVR_DTR; + } else { + if (clear & TIOCM_DTR) + port->MSVR &= ~MSVR_DTR; + } + spin_lock(&bp->lock); + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_MSVR, port->MSVR); + spin_unlock(&bp->lock); + spin_unlock_irqrestore(&port->lock, flags); + func_exit(); + return 0; +} + + +static int sx_send_break(struct tty_struct *tty, int length) +{ + struct specialix_port *port = tty->driver_data; + struct specialix_board *bp = port_Board(port); + unsigned long flags; + + func_enter(); + if (length == 0 || length == -1) + return -EOPNOTSUPP; + + spin_lock_irqsave(&port->lock, flags); + port->break_length = SPECIALIX_TPS / HZ * length; + port->COR2 |= COR2_ETC; + port->IER |= IER_TXRDY; + spin_lock(&bp->lock); + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_COR2, port->COR2); + sx_out(bp, CD186x_IER, port->IER); + spin_unlock(&bp->lock); + spin_unlock_irqrestore(&port->lock, flags); + sx_wait_CCR(bp); + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CCR, CCR_CORCHG2); + spin_unlock_irqrestore(&bp->lock, flags); + sx_wait_CCR(bp); + + func_exit(); + return 0; +} + + +static int sx_set_serial_info(struct specialix_port *port, + struct serial_struct __user *newinfo) +{ + struct serial_struct tmp; + struct specialix_board *bp = port_Board(port); + int change_speed; + + func_enter(); + + if (copy_from_user(&tmp, newinfo, sizeof(tmp))) { + func_enter(); + return -EFAULT; + } + + mutex_lock(&port->port.mutex); + change_speed = ((port->port.flags & ASYNC_SPD_MASK) != + (tmp.flags & ASYNC_SPD_MASK)); + change_speed |= (tmp.custom_divisor != port->custom_divisor); + + if (!capable(CAP_SYS_ADMIN)) { + if ((tmp.close_delay != port->port.close_delay) || + (tmp.closing_wait != port->port.closing_wait) || + ((tmp.flags & ~ASYNC_USR_MASK) != + (port->port.flags & ~ASYNC_USR_MASK))) { + func_exit(); + mutex_unlock(&port->port.mutex); + return -EPERM; + } + port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | + (tmp.flags & ASYNC_USR_MASK)); + port->custom_divisor = tmp.custom_divisor; + } else { + port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | + (tmp.flags & ASYNC_FLAGS)); + port->port.close_delay = tmp.close_delay; + port->port.closing_wait = tmp.closing_wait; + port->custom_divisor = tmp.custom_divisor; + } + if (change_speed) + sx_change_speed(bp, port); + + func_exit(); + mutex_unlock(&port->port.mutex); + return 0; +} + + +static int sx_get_serial_info(struct specialix_port *port, + struct serial_struct __user *retinfo) +{ + struct serial_struct tmp; + struct specialix_board *bp = port_Board(port); + + func_enter(); + + memset(&tmp, 0, sizeof(tmp)); + mutex_lock(&port->port.mutex); + tmp.type = PORT_CIRRUS; + tmp.line = port - sx_port; + tmp.port = bp->base; + tmp.irq = bp->irq; + tmp.flags = port->port.flags; + tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC; + tmp.close_delay = port->port.close_delay * HZ/100; + tmp.closing_wait = port->port.closing_wait * HZ/100; + tmp.custom_divisor = port->custom_divisor; + tmp.xmit_fifo_size = CD186x_NFIFO; + mutex_unlock(&port->port.mutex); + if (copy_to_user(retinfo, &tmp, sizeof(tmp))) { + func_exit(); + return -EFAULT; + } + + func_exit(); + return 0; +} + + +static int sx_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct specialix_port *port = tty->driver_data; + void __user *argp = (void __user *)arg; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_ioctl")) { + func_exit(); + return -ENODEV; + } + + switch (cmd) { + case TIOCGSERIAL: + func_exit(); + return sx_get_serial_info(port, argp); + case TIOCSSERIAL: + func_exit(); + return sx_set_serial_info(port, argp); + default: + func_exit(); + return -ENOIOCTLCMD; + } + func_exit(); + return 0; +} + + +static void sx_throttle(struct tty_struct *tty) +{ + struct specialix_port *port = tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_throttle")) { + func_exit(); + return; + } + + bp = port_Board(port); + + /* Use DTR instead of RTS ! */ + if (sx_crtscts(tty)) + port->MSVR &= ~MSVR_DTR; + else { + /* Auch!!! I think the system shouldn't call this then. */ + /* Or maybe we're supposed (allowed?) to do our side of hw + handshake anyway, even when hardware handshake is off. + When you see this in your logs, please report.... */ + printk(KERN_ERR + "sx%d: Need to throttle, but can't (hardware hs is off)\n", + port_No(port)); + } + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CAR, port_No(port)); + spin_unlock_irqrestore(&bp->lock, flags); + if (I_IXOFF(tty)) { + sx_wait_CCR(bp); + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CCR, CCR_SSCH2); + spin_unlock_irqrestore(&bp->lock, flags); + sx_wait_CCR(bp); + } + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_MSVR, port->MSVR); + spin_unlock_irqrestore(&bp->lock, flags); + + func_exit(); +} + + +static void sx_unthrottle(struct tty_struct *tty) +{ + struct specialix_port *port = tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) { + func_exit(); + return; + } + + bp = port_Board(port); + + spin_lock_irqsave(&port->lock, flags); + /* XXXX Use DTR INSTEAD???? */ + if (sx_crtscts(tty)) + port->MSVR |= MSVR_DTR; + /* Else clause: see remark in "sx_throttle"... */ + spin_lock(&bp->lock); + sx_out(bp, CD186x_CAR, port_No(port)); + spin_unlock(&bp->lock); + if (I_IXOFF(tty)) { + spin_unlock_irqrestore(&port->lock, flags); + sx_wait_CCR(bp); + spin_lock_irqsave(&bp->lock, flags); + sx_out(bp, CD186x_CCR, CCR_SSCH1); + spin_unlock_irqrestore(&bp->lock, flags); + sx_wait_CCR(bp); + spin_lock_irqsave(&port->lock, flags); + } + spin_lock(&bp->lock); + sx_out(bp, CD186x_MSVR, port->MSVR); + spin_unlock(&bp->lock); + spin_unlock_irqrestore(&port->lock, flags); + + func_exit(); +} + + +static void sx_stop(struct tty_struct *tty) +{ + struct specialix_port *port = tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_stop")) { + func_exit(); + return; + } + + bp = port_Board(port); + + spin_lock_irqsave(&port->lock, flags); + port->IER &= ~IER_TXRDY; + spin_lock(&bp->lock); + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + spin_unlock(&bp->lock); + spin_unlock_irqrestore(&port->lock, flags); + + func_exit(); +} + + +static void sx_start(struct tty_struct *tty) +{ + struct specialix_port *port = tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_start")) { + func_exit(); + return; + } + + bp = port_Board(port); + + spin_lock_irqsave(&port->lock, flags); + if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + spin_lock(&bp->lock); + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + spin_unlock(&bp->lock); + } + spin_unlock_irqrestore(&port->lock, flags); + + func_exit(); +} + +static void sx_hangup(struct tty_struct *tty) +{ + struct specialix_port *port = tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_hangup")) { + func_exit(); + return; + } + + bp = port_Board(port); + + sx_shutdown_port(bp, port); + spin_lock_irqsave(&port->lock, flags); + bp->count -= port->port.count; + if (bp->count < 0) { + printk(KERN_ERR + "sx%d: sx_hangup: bad board count: %d port: %d\n", + board_No(bp), bp->count, tty->index); + bp->count = 0; + } + port->port.count = 0; + port->port.flags &= ~ASYNC_NORMAL_ACTIVE; + port->port.tty = NULL; + spin_unlock_irqrestore(&port->lock, flags); + wake_up_interruptible(&port->port.open_wait); + + func_exit(); +} + + +static void sx_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + struct specialix_port *port = tty->driver_data; + unsigned long flags; + struct specialix_board *bp; + + if (sx_paranoia_check(port, tty->name, "sx_set_termios")) + return; + + bp = port_Board(port); + spin_lock_irqsave(&port->lock, flags); + sx_change_speed(port_Board(port), port); + spin_unlock_irqrestore(&port->lock, flags); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + sx_start(tty); + } +} + +static const struct tty_operations sx_ops = { + .open = sx_open, + .close = sx_close, + .write = sx_write, + .put_char = sx_put_char, + .flush_chars = sx_flush_chars, + .write_room = sx_write_room, + .chars_in_buffer = sx_chars_in_buffer, + .flush_buffer = sx_flush_buffer, + .ioctl = sx_ioctl, + .throttle = sx_throttle, + .unthrottle = sx_unthrottle, + .set_termios = sx_set_termios, + .stop = sx_stop, + .start = sx_start, + .hangup = sx_hangup, + .tiocmget = sx_tiocmget, + .tiocmset = sx_tiocmset, + .break_ctl = sx_send_break, +}; + +static int sx_init_drivers(void) +{ + int error; + int i; + + func_enter(); + + specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT); + if (!specialix_driver) { + printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n"); + func_exit(); + return 1; + } + + specialix_driver->owner = THIS_MODULE; + specialix_driver->name = "ttyW"; + specialix_driver->major = SPECIALIX_NORMAL_MAJOR; + specialix_driver->type = TTY_DRIVER_TYPE_SERIAL; + specialix_driver->subtype = SERIAL_TYPE_NORMAL; + specialix_driver->init_termios = tty_std_termios; + specialix_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + specialix_driver->init_termios.c_ispeed = 9600; + specialix_driver->init_termios.c_ospeed = 9600; + specialix_driver->flags = TTY_DRIVER_REAL_RAW | + TTY_DRIVER_HARDWARE_BREAK; + tty_set_operations(specialix_driver, &sx_ops); + + error = tty_register_driver(specialix_driver); + if (error) { + put_tty_driver(specialix_driver); + printk(KERN_ERR + "sx: Couldn't register specialix IO8+ driver, error = %d\n", + error); + func_exit(); + return 1; + } + memset(sx_port, 0, sizeof(sx_port)); + for (i = 0; i < SX_NPORT * SX_NBOARD; i++) { + sx_port[i].magic = SPECIALIX_MAGIC; + tty_port_init(&sx_port[i].port); + spin_lock_init(&sx_port[i].lock); + } + + func_exit(); + return 0; +} + +static void sx_release_drivers(void) +{ + func_enter(); + + tty_unregister_driver(specialix_driver); + put_tty_driver(specialix_driver); + func_exit(); +} + +/* + * This routine must be called by kernel at boot time + */ +static int __init specialix_init(void) +{ + int i; + int found = 0; + + func_enter(); + + printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n"); + printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n"); + if (sx_rtscts) + printk(KERN_INFO + "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n"); + else + printk(KERN_INFO "sx: DTR/RTS pin is always RTS.\n"); + + for (i = 0; i < SX_NBOARD; i++) + spin_lock_init(&sx_board[i].lock); + + if (sx_init_drivers()) { + func_exit(); + return -EIO; + } + + for (i = 0; i < SX_NBOARD; i++) + if (sx_board[i].base && !sx_probe(&sx_board[i])) + found++; + +#ifdef CONFIG_PCI + { + struct pci_dev *pdev = NULL; + + i = 0; + while (i < SX_NBOARD) { + if (sx_board[i].flags & SX_BOARD_PRESENT) { + i++; + continue; + } + pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX, + PCI_DEVICE_ID_SPECIALIX_IO8, pdev); + if (!pdev) + break; + + if (pci_enable_device(pdev)) + continue; + + sx_board[i].irq = pdev->irq; + + sx_board[i].base = pci_resource_start(pdev, 2); + + sx_board[i].flags |= SX_BOARD_IS_PCI; + if (!sx_probe(&sx_board[i])) + found++; + } + /* May exit pci_get sequence early with lots of boards */ + if (pdev != NULL) + pci_dev_put(pdev); + } +#endif + + if (!found) { + sx_release_drivers(); + printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n"); + func_exit(); + return -EIO; + } + + func_exit(); + return 0; +} + +static int iobase[SX_NBOARD] = {0,}; +static int irq[SX_NBOARD] = {0,}; + +module_param_array(iobase, int, NULL, 0); +module_param_array(irq, int, NULL, 0); +module_param(sx_debug, int, 0); +module_param(sx_rtscts, int, 0); +module_param(sx_rxfifo, int, 0); + +/* + * You can setup up to 4 boards. + * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter. + * You should specify the IRQs too in that case "irq=....,...". + * + * More than 4 boards in one computer is not possible, as the card can + * only use 4 different interrupts. + * + */ +static int __init specialix_init_module(void) +{ + int i; + + func_enter(); + + if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) { + for (i = 0; i < SX_NBOARD; i++) { + sx_board[i].base = iobase[i]; + sx_board[i].irq = irq[i]; + sx_board[i].count = 0; + } + } + + func_exit(); + + return specialix_init(); +} + +static void __exit specialix_exit_module(void) +{ + int i; + + func_enter(); + + sx_release_drivers(); + for (i = 0; i < SX_NBOARD; i++) + if (sx_board[i].flags & SX_BOARD_PRESENT) + sx_release_io_range(&sx_board[i]); + func_exit(); +} + +static struct pci_device_id specialx_pci_tbl[] __devinitdata __used = { + { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) }, + { } +}; +MODULE_DEVICE_TABLE(pci, specialx_pci_tbl); + +module_init(specialix_init_module); +module_exit(specialix_exit_module); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(SPECIALIX_NORMAL_MAJOR); diff --git a/drivers/staging/tty/specialix_io8.h b/drivers/staging/tty/specialix_io8.h new file mode 100644 index 000000000000..c63005274d9b --- /dev/null +++ b/drivers/staging/tty/specialix_io8.h @@ -0,0 +1,140 @@ +/* + * linux/drivers/char/specialix_io8.h -- + * Specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) + * + * + * Specialix pays for the development and support of this driver. + * Please DO contact io8-linux@specialix.co.uk if you require + * support. + * + * This driver was developped in the BitWizard linux device + * driver service. If you require a linux device driver for your + * product, please contact devices@BitWizard.nl for a quote. + * + * This code is firmly based on the riscom/8 serial driver, + * written by Dmitry Gorodchanin. The specialix IO8+ card + * programming information was obtained from the CL-CD1865 Data + * Book, and Specialix document number 6200059: IO8+ Hardware + * Functional Specification. + * + * 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. + * */ + +#ifndef __LINUX_SPECIALIX_H +#define __LINUX_SPECIALIX_H + +#include + +#ifdef __KERNEL__ + +/* You can have max 4 ISA cards in one PC, and I recommend not much +more than a few PCI versions of the card. */ + +#define SX_NBOARD 8 + +/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */ +#define SX_IO_SPACE 4 +/* The PCI version decodes 8 addresses, but still only 2 are used. */ +#define SX_PCI_IO_SPACE 8 + +/* eight ports per board. */ +#define SX_NPORT 8 +#define SX_BOARD(line) ((line) / SX_NPORT) +#define SX_PORT(line) ((line) & (SX_NPORT - 1)) + + +#define SX_DATA_REG 0 /* Base+0 : Data register */ +#define SX_ADDR_REG 1 /* base+1 : Address register. */ + +#define MHz *1000000 /* I'm ashamed of myself. */ + +/* On-board oscillator frequency */ +#define SX_OSCFREQ (25 MHz/2) +/* There is a 25MHz crystal on the board, but the chip is in /2 mode */ + + +/* Ticks per sec. Used for setting receiver timeout and break length */ +#define SPECIALIX_TPS 4000 + +/* Yeah, after heavy testing I decided it must be 6. + * Sure, You can change it if needed. + */ +#define SPECIALIX_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ + +#define SPECIALIX_MAGIC 0x0907 + +#define SX_CCR_TIMEOUT 10000 /* CCR timeout. You may need to wait upto + 10 milliseconds before the internal + processor is available again after + you give it a command */ + +#define SX_IOBASE1 0x100 +#define SX_IOBASE2 0x180 +#define SX_IOBASE3 0x250 +#define SX_IOBASE4 0x260 + +struct specialix_board { + unsigned long flags; + unsigned short base; + unsigned char irq; + //signed char count; + int count; + unsigned char DTR; + int reg; + spinlock_t lock; +}; + +#define SX_BOARD_PRESENT 0x00000001 +#define SX_BOARD_ACTIVE 0x00000002 +#define SX_BOARD_IS_PCI 0x00000004 + + +struct specialix_port { + int magic; + struct tty_port port; + int baud_base; + int flags; + int timeout; + unsigned char * xmit_buf; + int custom_divisor; + int xmit_head; + int xmit_tail; + int xmit_cnt; + short wakeup_chars; + short break_length; + unsigned char mark_mask; + unsigned char IER; + unsigned char MSVR; + unsigned char COR2; + unsigned long overrun; + unsigned long hits[10]; + spinlock_t lock; +}; + +#endif /* __KERNEL__ */ +#endif /* __LINUX_SPECIALIX_H */ + + + + + + + + + diff --git a/drivers/staging/tty/stallion.c b/drivers/staging/tty/stallion.c new file mode 100644 index 000000000000..4fff5cd3b163 --- /dev/null +++ b/drivers/staging/tty/stallion.c @@ -0,0 +1,4651 @@ +/*****************************************************************************/ + +/* + * stallion.c -- stallion 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 +#include + +#include + +/*****************************************************************************/ + +/* + * Define different board types. Use the standard Stallion "assigned" + * board numbers. Boards supported in this driver are abbreviated as + * EIO = EasyIO and ECH = EasyConnection 8/32. + */ +#define BRD_EASYIO 20 +#define BRD_ECH 21 +#define BRD_ECHMC 22 +#define BRD_ECHPCI 26 +#define BRD_ECH64PCI 27 +#define BRD_EASYIOPCI 28 + +struct stlconf { + unsigned int brdtype; + int ioaddr1; + int ioaddr2; + unsigned long memaddr; + int irq; + int irqtype; +}; + +static unsigned int stl_nrbrds; + +/*****************************************************************************/ + +/* + * 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 + +/* + * Set the TX buffer size. Bigger is better, but we don't want + * to chew too much memory with buffers! + */ +#define STL_TXBUFLOW 512 +#define STL_TXBUFSIZE 4096 + +/*****************************************************************************/ + +/* + * Define our local driver identity first. Set up stuff to deal with + * all the local structures required by a serial tty driver. + */ +static char *stl_drvtitle = "Stallion Multiport Serial Driver"; +static char *stl_drvname = "stallion"; +static char *stl_drvversion = "5.6.0"; + +static struct tty_driver *stl_serial; + +/* + * 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, 8 data bits, 1 stop bit. + */ +static struct ktermios stl_deftermios = { + .c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL), + .c_cc = INIT_C_CC, + .c_ispeed = 9600, + .c_ospeed = 9600, +}; + +/* + * Define global place to put buffer overflow characters. + */ +static char stl_unwanted[SC26198_RXFIFOSIZE]; + +/*****************************************************************************/ + +static DEFINE_MUTEX(stl_brdslock); +static struct stlbrd *stl_brds[STL_MAXBRDS]; + +static const struct tty_port_operations stl_port_ops; + +/* + * Per board state flags. Used with the state field of the board struct. + * Not really much here! + */ +#define BRD_FOUND 0x1 +#define STL_PROBED 0x2 + + +/* + * Define the port structure istate flags. These set of flags are + * modified at interrupt time - so setting and reseting them needs + * to be atomic. Use the bit clear/setting routines for this. + */ +#define ASYI_TXBUSY 1 +#define ASYI_TXLOW 2 +#define ASYI_TXFLOWED 3 + +/* + * Define an array of board names as printable strings. Handy for + * referencing boards when printing trace and stuff. + */ +static char *stl_brdnames[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "EasyIO", + "EC8/32-AT", + "EC8/32-MC", + NULL, + NULL, + NULL, + "EC8/32-PCI", + "EC8/64-PCI", + "EasyIO-PCI", +}; + +/*****************************************************************************/ + +/* + * 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 unsigned int stl_nargs; +static char *board0[4]; +static char *board1[4]; +static char *board2[4]; +static char *board3[4]; + +static char **stl_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. + */ + +static struct { + char *name; + int type; +} stl_brdstr[] = { + { "easyio", BRD_EASYIO }, + { "eio", BRD_EASYIO }, + { "20", BRD_EASYIO }, + { "ec8/32", BRD_ECH }, + { "ec8/32-at", BRD_ECH }, + { "ec8/32-isa", BRD_ECH }, + { "ech", BRD_ECH }, + { "echat", BRD_ECH }, + { "21", BRD_ECH }, + { "ec8/32-mc", BRD_ECHMC }, + { "ec8/32-mca", BRD_ECHMC }, + { "echmc", BRD_ECHMC }, + { "echmca", BRD_ECHMC }, + { "22", BRD_ECHMC }, + { "ec8/32-pc", BRD_ECHPCI }, + { "ec8/32-pci", BRD_ECHPCI }, + { "26", BRD_ECHPCI }, + { "ec8/64-pc", BRD_ECH64PCI }, + { "ec8/64-pci", BRD_ECH64PCI }, + { "ech-pci", BRD_ECH64PCI }, + { "echpci", BRD_ECH64PCI }, + { "echpc", BRD_ECH64PCI }, + { "27", BRD_ECH64PCI }, + { "easyio-pc", BRD_EASYIOPCI }, + { "easyio-pci", BRD_EASYIOPCI }, + { "eio-pci", BRD_EASYIOPCI }, + { "eiopci", BRD_EASYIOPCI }, + { "28", BRD_EASYIOPCI }, +}; + +/* + * Define the module agruments. + */ + +module_param_array(board0, charp, &stl_nargs, 0); +MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,ioaddr2][,irq]]"); +module_param_array(board1, charp, &stl_nargs, 0); +MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,ioaddr2][,irq]]"); +module_param_array(board2, charp, &stl_nargs, 0); +MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,ioaddr2][,irq]]"); +module_param_array(board3, charp, &stl_nargs, 0); +MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,ioaddr2][,irq]]"); + +/*****************************************************************************/ + +/* + * Hardware ID bits for the EasyIO and ECH boards. These defines apply + * to the directly accessible io ports of these boards (not the uarts - + * they are in cd1400.h and sc26198.h). + */ +#define EIO_8PORTRS 0x04 +#define EIO_4PORTRS 0x05 +#define EIO_8PORTDI 0x00 +#define EIO_8PORTM 0x06 +#define EIO_MK3 0x03 +#define EIO_IDBITMASK 0x07 + +#define EIO_BRDMASK 0xf0 +#define ID_BRD4 0x10 +#define ID_BRD8 0x20 +#define ID_BRD16 0x30 + +#define EIO_INTRPEND 0x08 +#define EIO_INTEDGE 0x00 +#define EIO_INTLEVEL 0x08 +#define EIO_0WS 0x10 + +#define ECH_ID 0xa0 +#define ECH_IDBITMASK 0xe0 +#define ECH_BRDENABLE 0x08 +#define ECH_BRDDISABLE 0x00 +#define ECH_INTENABLE 0x01 +#define ECH_INTDISABLE 0x00 +#define ECH_INTLEVEL 0x02 +#define ECH_INTEDGE 0x00 +#define ECH_INTRPEND 0x01 +#define ECH_BRDRESET 0x01 + +#define ECHMC_INTENABLE 0x01 +#define ECHMC_BRDRESET 0x02 + +#define ECH_PNLSTATUS 2 +#define ECH_PNL16PORT 0x20 +#define ECH_PNLIDMASK 0x07 +#define ECH_PNLXPID 0x40 +#define ECH_PNLINTRPEND 0x80 + +#define ECH_ADDR2MASK 0x1e0 + +/* + * Define the vector mapping bits for the programmable interrupt board + * hardware. These bits encode the interrupt for the board to use - it + * is software selectable (except the EIO-8M). + */ +static unsigned char stl_vecmap[] = { + 0xff, 0xff, 0xff, 0x04, 0x06, 0x05, 0xff, 0x07, + 0xff, 0xff, 0x00, 0x02, 0x01, 0xff, 0xff, 0x03 +}; + +/* + * Lock ordering is that you may not take stallion_lock holding + * brd_lock. + */ + +static spinlock_t brd_lock; /* Guard the board mapping */ +static spinlock_t stallion_lock; /* Guard the tty driver */ + +/* + * Set up enable and disable macros for the ECH boards. They require + * the secondary io address space to be activated and deactivated. + * This way all ECH boards can share their secondary io region. + * If this is an ECH-PCI board then also need to set the page pointer + * to point to the correct page. + */ +#define BRDENABLE(brdnr,pagenr) \ + if (stl_brds[(brdnr)]->brdtype == BRD_ECH) \ + outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDENABLE), \ + stl_brds[(brdnr)]->ioctrl); \ + else if (stl_brds[(brdnr)]->brdtype == BRD_ECHPCI) \ + outb((pagenr), stl_brds[(brdnr)]->ioctrl); + +#define BRDDISABLE(brdnr) \ + if (stl_brds[(brdnr)]->brdtype == BRD_ECH) \ + outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE), \ + stl_brds[(brdnr)]->ioctrl); + +#define STL_CD1400MAXBAUD 230400 +#define STL_SC26198MAXBAUD 460800 + +#define STL_BAUDBASE 115200 +#define STL_CLOSEDELAY (5 * HZ / 10) + +/*****************************************************************************/ + +/* + * Define the Stallion PCI vendor and device IDs. + */ +#ifndef PCI_VENDOR_ID_STALLION +#define PCI_VENDOR_ID_STALLION 0x124d +#endif +#ifndef PCI_DEVICE_ID_ECHPCI832 +#define PCI_DEVICE_ID_ECHPCI832 0x0000 +#endif +#ifndef PCI_DEVICE_ID_ECHPCI864 +#define PCI_DEVICE_ID_ECHPCI864 0x0002 +#endif +#ifndef PCI_DEVICE_ID_EIOPCI +#define PCI_DEVICE_ID_EIOPCI 0x0003 +#endif + +/* + * Define structure to hold all Stallion PCI boards. + */ + +static struct pci_device_id stl_pcibrds[] = { + { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI864), + .driver_data = BRD_ECH64PCI }, + { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_EIOPCI), + .driver_data = BRD_EASYIOPCI }, + { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832), + .driver_data = BRD_ECHPCI }, + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410), + .driver_data = BRD_ECHPCI }, + { } +}; +MODULE_DEVICE_TABLE(pci, stl_pcibrds); + +/*****************************************************************************/ + +/* + * Define macros to extract a brd/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 stl_baudrates[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 +}; + +/*****************************************************************************/ + +/* + * Declare all those functions in this driver! + */ + +static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg); +static int stl_brdinit(struct stlbrd *brdp); +static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp); +static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp); + +/* + * CD1400 uart specific handling functions. + */ +static void stl_cd1400setreg(struct stlport *portp, int regnr, int value); +static int stl_cd1400getreg(struct stlport *portp, int regnr); +static int stl_cd1400updatereg(struct stlport *portp, int regnr, int value); +static int stl_cd1400panelinit(struct stlbrd *brdp, struct stlpanel *panelp); +static void stl_cd1400portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp); +static void stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp); +static int stl_cd1400getsignals(struct stlport *portp); +static void stl_cd1400setsignals(struct stlport *portp, int dtr, int rts); +static void stl_cd1400ccrwait(struct stlport *portp); +static void stl_cd1400enablerxtx(struct stlport *portp, int rx, int tx); +static void stl_cd1400startrxtx(struct stlport *portp, int rx, int tx); +static void stl_cd1400disableintrs(struct stlport *portp); +static void stl_cd1400sendbreak(struct stlport *portp, int len); +static void stl_cd1400flowctrl(struct stlport *portp, int state); +static void stl_cd1400sendflow(struct stlport *portp, int state); +static void stl_cd1400flush(struct stlport *portp); +static int stl_cd1400datastate(struct stlport *portp); +static void stl_cd1400eiointr(struct stlpanel *panelp, unsigned int iobase); +static void stl_cd1400echintr(struct stlpanel *panelp, unsigned int iobase); +static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr); +static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr); +static void stl_cd1400mdmisr(struct stlpanel *panelp, int ioaddr); + +static inline int stl_cd1400breakisr(struct stlport *portp, int ioaddr); + +/* + * SC26198 uart specific handling functions. + */ +static void stl_sc26198setreg(struct stlport *portp, int regnr, int value); +static int stl_sc26198getreg(struct stlport *portp, int regnr); +static int stl_sc26198updatereg(struct stlport *portp, int regnr, int value); +static int stl_sc26198getglobreg(struct stlport *portp, int regnr); +static int stl_sc26198panelinit(struct stlbrd *brdp, struct stlpanel *panelp); +static void stl_sc26198portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp); +static void stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp); +static int stl_sc26198getsignals(struct stlport *portp); +static void stl_sc26198setsignals(struct stlport *portp, int dtr, int rts); +static void stl_sc26198enablerxtx(struct stlport *portp, int rx, int tx); +static void stl_sc26198startrxtx(struct stlport *portp, int rx, int tx); +static void stl_sc26198disableintrs(struct stlport *portp); +static void stl_sc26198sendbreak(struct stlport *portp, int len); +static void stl_sc26198flowctrl(struct stlport *portp, int state); +static void stl_sc26198sendflow(struct stlport *portp, int state); +static void stl_sc26198flush(struct stlport *portp); +static int stl_sc26198datastate(struct stlport *portp); +static void stl_sc26198wait(struct stlport *portp); +static void stl_sc26198txunflow(struct stlport *portp, struct tty_struct *tty); +static void stl_sc26198intr(struct stlpanel *panelp, unsigned int iobase); +static void stl_sc26198txisr(struct stlport *port); +static void stl_sc26198rxisr(struct stlport *port, unsigned int iack); +static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char ch); +static void stl_sc26198rxbadchars(struct stlport *portp); +static void stl_sc26198otherisr(struct stlport *port, unsigned int iack); + +/*****************************************************************************/ + +/* + * Generic UART support structure. + */ +typedef struct uart { + int (*panelinit)(struct stlbrd *brdp, struct stlpanel *panelp); + void (*portinit)(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp); + void (*setport)(struct stlport *portp, struct ktermios *tiosp); + int (*getsignals)(struct stlport *portp); + void (*setsignals)(struct stlport *portp, int dtr, int rts); + void (*enablerxtx)(struct stlport *portp, int rx, int tx); + void (*startrxtx)(struct stlport *portp, int rx, int tx); + void (*disableintrs)(struct stlport *portp); + void (*sendbreak)(struct stlport *portp, int len); + void (*flowctrl)(struct stlport *portp, int state); + void (*sendflow)(struct stlport *portp, int state); + void (*flush)(struct stlport *portp); + int (*datastate)(struct stlport *portp); + void (*intr)(struct stlpanel *panelp, unsigned int iobase); +} uart_t; + +/* + * Define some macros to make calling these functions nice and clean. + */ +#define stl_panelinit (* ((uart_t *) panelp->uartp)->panelinit) +#define stl_portinit (* ((uart_t *) portp->uartp)->portinit) +#define stl_setport (* ((uart_t *) portp->uartp)->setport) +#define stl_getsignals (* ((uart_t *) portp->uartp)->getsignals) +#define stl_setsignals (* ((uart_t *) portp->uartp)->setsignals) +#define stl_enablerxtx (* ((uart_t *) portp->uartp)->enablerxtx) +#define stl_startrxtx (* ((uart_t *) portp->uartp)->startrxtx) +#define stl_disableintrs (* ((uart_t *) portp->uartp)->disableintrs) +#define stl_sendbreak (* ((uart_t *) portp->uartp)->sendbreak) +#define stl_flowctrl (* ((uart_t *) portp->uartp)->flowctrl) +#define stl_sendflow (* ((uart_t *) portp->uartp)->sendflow) +#define stl_flush (* ((uart_t *) portp->uartp)->flush) +#define stl_datastate (* ((uart_t *) portp->uartp)->datastate) + +/*****************************************************************************/ + +/* + * CD1400 UART specific data initialization. + */ +static uart_t stl_cd1400uart = { + stl_cd1400panelinit, + stl_cd1400portinit, + stl_cd1400setport, + stl_cd1400getsignals, + stl_cd1400setsignals, + stl_cd1400enablerxtx, + stl_cd1400startrxtx, + stl_cd1400disableintrs, + stl_cd1400sendbreak, + stl_cd1400flowctrl, + stl_cd1400sendflow, + stl_cd1400flush, + stl_cd1400datastate, + stl_cd1400eiointr +}; + +/* + * Define the offsets within the register bank of a cd1400 based panel. + * These io address offsets are common to the EasyIO board as well. + */ +#define EREG_ADDR 0 +#define EREG_DATA 4 +#define EREG_RXACK 5 +#define EREG_TXACK 6 +#define EREG_MDACK 7 + +#define EREG_BANKSIZE 8 + +#define CD1400_CLK 25000000 +#define CD1400_CLK8M 20000000 + +/* + * Define the cd1400 baud rate clocks. These are used when calculating + * what clock and divisor to use for the required baud rate. Also + * define the maximum baud rate allowed, and the default base baud. + */ +static int stl_cd1400clkdivs[] = { + CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4 +}; + +/*****************************************************************************/ + +/* + * SC26198 UART specific data initization. + */ +static uart_t stl_sc26198uart = { + stl_sc26198panelinit, + stl_sc26198portinit, + stl_sc26198setport, + stl_sc26198getsignals, + stl_sc26198setsignals, + stl_sc26198enablerxtx, + stl_sc26198startrxtx, + stl_sc26198disableintrs, + stl_sc26198sendbreak, + stl_sc26198flowctrl, + stl_sc26198sendflow, + stl_sc26198flush, + stl_sc26198datastate, + stl_sc26198intr +}; + +/* + * Define the offsets within the register bank of a sc26198 based panel. + */ +#define XP_DATA 0 +#define XP_ADDR 1 +#define XP_MODID 2 +#define XP_STATUS 2 +#define XP_IACK 3 + +#define XP_BANKSIZE 4 + +/* + * Define the sc26198 baud rate table. Offsets within the table + * represent the actual baud rate selector of sc26198 registers. + */ +static unsigned int sc26198_baudtable[] = { + 50, 75, 150, 200, 300, 450, 600, 900, 1200, 1800, 2400, 3600, + 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200, + 230400, 460800, 921600 +}; + +#define SC26198_NRBAUDS ARRAY_SIZE(sc26198_baudtable) + +/*****************************************************************************/ + +/* + * Define the driver info for a user level control device. Used mainly + * to get at port stats - only not using the port device itself. + */ +static const struct file_operations stl_fsiomem = { + .owner = THIS_MODULE, + .unlocked_ioctl = stl_memioctl, + .llseek = noop_llseek, +}; + +static struct class *stallion_class; + +static void stl_cd_change(struct stlport *portp) +{ + unsigned int oldsigs = portp->sigs; + struct tty_struct *tty = tty_port_tty_get(&portp->port); + + if (!tty) + return; + + portp->sigs = stl_getsignals(portp); + + if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) + wake_up_interruptible(&portp->port.open_wait); + + if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) + if (portp->port.flags & ASYNC_CHECK_CD) + tty_hangup(tty); + tty_kref_put(tty); +} + +/* + * Check for any arguments passed in on the module load command line. + */ + +/*****************************************************************************/ + +/* + * Parse the supplied argument string, into the board conf struct. + */ + +static int __init stl_parsebrd(struct stlconf *confp, char **argp) +{ + char *sp; + unsigned int i; + + pr_debug("stl_parsebrd(confp=%p,argp=%p)\n", confp, argp); + + if ((argp[0] == 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(stl_brdstr); i++) + if (strcmp(stl_brdstr[i].name, argp[0]) == 0) + break; + + if (i == ARRAY_SIZE(stl_brdstr)) { + printk("STALLION: unknown board name, %s?\n", argp[0]); + return 0; + } + + confp->brdtype = stl_brdstr[i].type; + + i = 1; + if ((argp[i] != NULL) && (*argp[i] != 0)) + confp->ioaddr1 = simple_strtoul(argp[i], NULL, 0); + i++; + if (confp->brdtype == BRD_ECH) { + if ((argp[i] != NULL) && (*argp[i] != 0)) + confp->ioaddr2 = simple_strtoul(argp[i], NULL, 0); + i++; + } + if ((argp[i] != NULL) && (*argp[i] != 0)) + confp->irq = simple_strtoul(argp[i], NULL, 0); + return 1; +} + +/*****************************************************************************/ + +/* + * Allocate a new board structure. Fill out the basic info in it. + */ + +static struct stlbrd *stl_allocbrd(void) +{ + struct stlbrd *brdp; + + brdp = kzalloc(sizeof(struct stlbrd), GFP_KERNEL); + if (!brdp) { + printk("STALLION: failed to allocate memory (size=%Zd)\n", + sizeof(struct stlbrd)); + return NULL; + } + + brdp->magic = STL_BOARDMAGIC; + return brdp; +} + +/*****************************************************************************/ + +static int stl_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct stlport *portp = container_of(port, struct stlport, port); + if (!portp->tx.buf) { + portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL); + if (!portp->tx.buf) + return -ENOMEM; + portp->tx.head = portp->tx.buf; + portp->tx.tail = portp->tx.buf; + } + stl_setport(portp, tty->termios); + portp->sigs = stl_getsignals(portp); + stl_setsignals(portp, 1, 1); + stl_enablerxtx(portp, 1, 1); + stl_startrxtx(portp, 1, 0); + return 0; +} + +static int stl_open(struct tty_struct *tty, struct file *filp) +{ + struct stlport *portp; + struct stlbrd *brdp; + unsigned int minordev, brdnr, panelnr; + int portnr; + + pr_debug("stl_open(tty=%p,filp=%p): device=%s\n", tty, filp, tty->name); + + minordev = tty->index; + brdnr = MINOR2BRD(minordev); + if (brdnr >= stl_nrbrds) + return -ENODEV; + brdp = stl_brds[brdnr]; + if (brdp == NULL) + return -ENODEV; + + minordev = MINOR2PORT(minordev); + for (portnr = -1, panelnr = 0; panelnr < STL_MAXPANELS; panelnr++) { + if (brdp->panels[panelnr] == NULL) + break; + if (minordev < brdp->panels[panelnr]->nrports) { + portnr = minordev; + break; + } + minordev -= brdp->panels[panelnr]->nrports; + } + if (portnr < 0) + return -ENODEV; + + portp = brdp->panels[panelnr]->ports[portnr]; + if (portp == NULL) + return -ENODEV; + + tty->driver_data = portp; + return tty_port_open(&portp->port, tty, filp); + +} + +/*****************************************************************************/ + +static int stl_carrier_raised(struct tty_port *port) +{ + struct stlport *portp = container_of(port, struct stlport, port); + return (portp->sigs & TIOCM_CD) ? 1 : 0; +} + +static void stl_dtr_rts(struct tty_port *port, int on) +{ + struct stlport *portp = container_of(port, struct stlport, port); + /* Takes brd_lock internally */ + stl_setsignals(portp, on, on); +} + +/*****************************************************************************/ + +static void stl_flushbuffer(struct tty_struct *tty) +{ + struct stlport *portp; + + pr_debug("stl_flushbuffer(tty=%p)\n", tty); + + portp = tty->driver_data; + if (portp == NULL) + return; + + stl_flush(portp); + tty_wakeup(tty); +} + +/*****************************************************************************/ + +static void stl_waituntilsent(struct tty_struct *tty, int timeout) +{ + struct stlport *portp; + unsigned long tend; + + pr_debug("stl_waituntilsent(tty=%p,timeout=%d)\n", tty, timeout); + + portp = tty->driver_data; + if (portp == NULL) + return; + + if (timeout == 0) + timeout = HZ; + tend = jiffies + timeout; + + while (stl_datastate(portp)) { + if (signal_pending(current)) + break; + msleep_interruptible(20); + if (time_after_eq(jiffies, tend)) + break; + } +} + +/*****************************************************************************/ + +static void stl_shutdown(struct tty_port *port) +{ + struct stlport *portp = container_of(port, struct stlport, port); + stl_disableintrs(portp); + stl_enablerxtx(portp, 0, 0); + stl_flush(portp); + portp->istate = 0; + if (portp->tx.buf != NULL) { + kfree(portp->tx.buf); + portp->tx.buf = NULL; + portp->tx.head = NULL; + portp->tx.tail = NULL; + } +} + +static void stl_close(struct tty_struct *tty, struct file *filp) +{ + struct stlport*portp; + pr_debug("stl_close(tty=%p,filp=%p)\n", tty, filp); + + portp = tty->driver_data; + if(portp == NULL) + return; + tty_port_close(&portp->port, tty, filp); +} + +/*****************************************************************************/ + +/* + * Write routine. Take data and stuff it in to the TX ring queue. + * If transmit interrupts are not running then start them. + */ + +static int stl_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + struct stlport *portp; + unsigned int len, stlen; + unsigned char *chbuf; + char *head, *tail; + + pr_debug("stl_write(tty=%p,buf=%p,count=%d)\n", tty, buf, count); + + portp = tty->driver_data; + if (portp == NULL) + return 0; + if (portp->tx.buf == NULL) + return 0; + +/* + * If copying direct from user space we must cater for page faults, + * causing us to "sleep" here for a while. To handle this copy in all + * the data we need now, into a local buffer. Then when we got it all + * copy it into the TX buffer. + */ + chbuf = (unsigned char *) buf; + + head = portp->tx.head; + tail = portp->tx.tail; + if (head >= tail) { + len = STL_TXBUFSIZE - (head - tail) - 1; + stlen = STL_TXBUFSIZE - (head - portp->tx.buf); + } else { + len = tail - head - 1; + stlen = len; + } + + len = min(len, (unsigned int)count); + count = 0; + while (len > 0) { + stlen = min(len, stlen); + memcpy(head, chbuf, stlen); + len -= stlen; + chbuf += stlen; + count += stlen; + head += stlen; + if (head >= (portp->tx.buf + STL_TXBUFSIZE)) { + head = portp->tx.buf; + stlen = tail - head; + } + } + portp->tx.head = head; + + clear_bit(ASYI_TXLOW, &portp->istate); + stl_startrxtx(portp, -1, 1); + + return count; +} + +/*****************************************************************************/ + +static int stl_putchar(struct tty_struct *tty, unsigned char ch) +{ + struct stlport *portp; + unsigned int len; + char *head, *tail; + + pr_debug("stl_putchar(tty=%p,ch=%x)\n", tty, ch); + + portp = tty->driver_data; + if (portp == NULL) + return -EINVAL; + if (portp->tx.buf == NULL) + return -EINVAL; + + head = portp->tx.head; + tail = portp->tx.tail; + + len = (head >= tail) ? (STL_TXBUFSIZE - (head - tail)) : (tail - head); + len--; + + if (len > 0) { + *head++ = ch; + if (head >= (portp->tx.buf + STL_TXBUFSIZE)) + head = portp->tx.buf; + } + portp->tx.head = head; + return 0; +} + +/*****************************************************************************/ + +/* + * If there are any characters in the buffer then make sure that TX + * interrupts are on and get'em out. Normally used after the putchar + * routine has been called. + */ + +static void stl_flushchars(struct tty_struct *tty) +{ + struct stlport *portp; + + pr_debug("stl_flushchars(tty=%p)\n", tty); + + portp = tty->driver_data; + if (portp == NULL) + return; + if (portp->tx.buf == NULL) + return; + + stl_startrxtx(portp, -1, 1); +} + +/*****************************************************************************/ + +static int stl_writeroom(struct tty_struct *tty) +{ + struct stlport *portp; + char *head, *tail; + + pr_debug("stl_writeroom(tty=%p)\n", tty); + + portp = tty->driver_data; + if (portp == NULL) + return 0; + if (portp->tx.buf == NULL) + return 0; + + head = portp->tx.head; + tail = portp->tx.tail; + return (head >= tail) ? (STL_TXBUFSIZE - (head - tail) - 1) : (tail - head - 1); +} + +/*****************************************************************************/ + +/* + * Return number of chars in the TX buffer. Normally we would just + * calculate the number of chars in the buffer and return that, but if + * the buffer is empty and TX interrupts are still on then we return + * that the buffer still has 1 char in it. This way whoever called us + * will not think that ALL chars have drained - since the UART still + * must have some chars in it (we are busy after all). + */ + +static int stl_charsinbuffer(struct tty_struct *tty) +{ + struct stlport *portp; + unsigned int size; + char *head, *tail; + + pr_debug("stl_charsinbuffer(tty=%p)\n", tty); + + portp = tty->driver_data; + if (portp == NULL) + return 0; + if (portp->tx.buf == NULL) + return 0; + + head = portp->tx.head; + tail = portp->tx.tail; + size = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); + if ((size == 0) && test_bit(ASYI_TXBUSY, &portp->istate)) + size = 1; + return size; +} + +/*****************************************************************************/ + +/* + * Generate the serial struct info. + */ + +static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp) +{ + struct serial_struct sio; + struct stlbrd *brdp; + + pr_debug("stl_getserial(portp=%p,sp=%p)\n", portp, sp); + + memset(&sio, 0, sizeof(struct serial_struct)); + + mutex_lock(&portp->port.mutex); + sio.line = portp->portnr; + sio.port = portp->ioaddr; + sio.flags = portp->port.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.hub6 = 0; + if (portp->uartp == &stl_cd1400uart) { + sio.type = PORT_CIRRUS; + sio.xmit_fifo_size = CD1400_TXFIFOSIZE; + } else { + sio.type = PORT_UNKNOWN; + sio.xmit_fifo_size = SC26198_TXFIFOSIZE; + } + + brdp = stl_brds[portp->brdnr]; + if (brdp != NULL) + sio.irq = brdp->irq; + mutex_unlock(&portp->port.mutex); + + 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 stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp) +{ + struct stlport * portp = tty->driver_data; + struct serial_struct sio; + + pr_debug("stl_setserial(portp=%p,sp=%p)\n", portp, sp); + + if (copy_from_user(&sio, sp, sizeof(struct serial_struct))) + return -EFAULT; + mutex_lock(&portp->port.mutex); + if (!capable(CAP_SYS_ADMIN)) { + if ((sio.baud_base != portp->baud_base) || + (sio.close_delay != portp->close_delay) || + ((sio.flags & ~ASYNC_USR_MASK) != + (portp->port.flags & ~ASYNC_USR_MASK))) { + mutex_unlock(&portp->port.mutex); + return -EPERM; + } + } + + portp->port.flags = (portp->port.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; + mutex_unlock(&portp->port.mutex); + stl_setport(portp, tty->termios); + return 0; +} + +/*****************************************************************************/ + +static int stl_tiocmget(struct tty_struct *tty) +{ + struct stlport *portp; + + portp = tty->driver_data; + if (portp == NULL) + return -ENODEV; + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + + return stl_getsignals(portp); +} + +static int stl_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct stlport *portp; + int rts = -1, dtr = -1; + + portp = tty->driver_data; + if (portp == NULL) + return -ENODEV; + 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; + + stl_setsignals(portp, dtr, rts); + return 0; +} + +static int stl_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) +{ + struct stlport *portp; + int rc; + void __user *argp = (void __user *)arg; + + pr_debug("stl_ioctl(tty=%p,cmd=%x,arg=%lx)\n", tty, cmd, arg); + + portp = tty->driver_data; + if (portp == NULL) + return -ENODEV; + + 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 TIOCGSERIAL: + rc = stl_getserial(portp, argp); + break; + case TIOCSSERIAL: + rc = stl_setserial(tty, argp); + break; + case COM_GETPORTSTATS: + rc = stl_getportstats(tty, portp, argp); + break; + case COM_CLRPORTSTATS: + rc = stl_clrportstats(portp, argp); + break; + case TIOCSERCONFIG: + case TIOCSERGWILD: + case TIOCSERSWILD: + case TIOCSERGETLSR: + case TIOCSERGSTRUCT: + case TIOCSERGETMULTI: + case TIOCSERSETMULTI: + default: + rc = -ENOIOCTLCMD; + break; + } + return rc; +} + +/*****************************************************************************/ + +/* + * Start the transmitter again. Just turn TX interrupts back on. + */ + +static void stl_start(struct tty_struct *tty) +{ + struct stlport *portp; + + pr_debug("stl_start(tty=%p)\n", tty); + + portp = tty->driver_data; + if (portp == NULL) + return; + stl_startrxtx(portp, -1, 1); +} + +/*****************************************************************************/ + +static void stl_settermios(struct tty_struct *tty, struct ktermios *old) +{ + struct stlport *portp; + struct ktermios *tiosp; + + pr_debug("stl_settermios(tty=%p,old=%p)\n", tty, old); + + portp = tty->driver_data; + if (portp == NULL) + return; + + tiosp = tty->termios; + if ((tiosp->c_cflag == old->c_cflag) && + (tiosp->c_iflag == old->c_iflag)) + return; + + stl_setport(portp, tiosp); + stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), + -1); + if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) { + tty->hw_stopped = 0; + stl_start(tty); + } + if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL)) + wake_up_interruptible(&portp->port.open_wait); +} + +/*****************************************************************************/ + +/* + * Attempt to flow control who ever is sending us data. Based on termios + * settings use software or/and hardware flow control. + */ + +static void stl_throttle(struct tty_struct *tty) +{ + struct stlport *portp; + + pr_debug("stl_throttle(tty=%p)\n", tty); + + portp = tty->driver_data; + if (portp == NULL) + return; + stl_flowctrl(portp, 0); +} + +/*****************************************************************************/ + +/* + * Unflow control the device sending us data... + */ + +static void stl_unthrottle(struct tty_struct *tty) +{ + struct stlport *portp; + + pr_debug("stl_unthrottle(tty=%p)\n", tty); + + portp = tty->driver_data; + if (portp == NULL) + return; + stl_flowctrl(portp, 1); +} + +/*****************************************************************************/ + +/* + * Stop the transmitter. Basically to do this we will just turn TX + * interrupts off. + */ + +static void stl_stop(struct tty_struct *tty) +{ + struct stlport *portp; + + pr_debug("stl_stop(tty=%p)\n", tty); + + portp = tty->driver_data; + if (portp == NULL) + return; + stl_startrxtx(portp, -1, 0); +} + +/*****************************************************************************/ + +/* + * 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. + */ + +static void stl_hangup(struct tty_struct *tty) +{ + struct stlport *portp = tty->driver_data; + pr_debug("stl_hangup(tty=%p)\n", tty); + + if (portp == NULL) + return; + tty_port_hangup(&portp->port); +} + +/*****************************************************************************/ + +static int stl_breakctl(struct tty_struct *tty, int state) +{ + struct stlport *portp; + + pr_debug("stl_breakctl(tty=%p,state=%d)\n", tty, state); + + portp = tty->driver_data; + if (portp == NULL) + return -EINVAL; + + stl_sendbreak(portp, ((state == -1) ? 1 : 2)); + return 0; +} + +/*****************************************************************************/ + +static void stl_sendxchar(struct tty_struct *tty, char ch) +{ + struct stlport *portp; + + pr_debug("stl_sendxchar(tty=%p,ch=%x)\n", tty, ch); + + portp = tty->driver_data; + if (portp == NULL) + return; + + if (ch == STOP_CHAR(tty)) + stl_sendflow(portp, 0); + else if (ch == START_CHAR(tty)) + stl_sendflow(portp, 1); + else + stl_putchar(tty, ch); +} + +static void stl_portinfo(struct seq_file *m, struct stlport *portp, int portnr) +{ + int sigs; + char sep; + + seq_printf(m, "%d: uart:%s tx:%d rx:%d", + portnr, (portp->hwid == 1) ? "SC26198" : "CD1400", + (int) portp->stats.txtotal, (int) portp->stats.rxtotal); + + if (portp->stats.rxframing) + seq_printf(m, " fe:%d", (int) portp->stats.rxframing); + if (portp->stats.rxparity) + seq_printf(m, " pe:%d", (int) portp->stats.rxparity); + if (portp->stats.rxbreaks) + seq_printf(m, " brk:%d", (int) portp->stats.rxbreaks); + if (portp->stats.rxoverrun) + seq_printf(m, " oe:%d", (int) portp->stats.rxoverrun); + + sigs = stl_getsignals(portp); + sep = ' '; + if (sigs & TIOCM_RTS) { + seq_printf(m, "%c%s", sep, "RTS"); + sep = '|'; + } + if (sigs & TIOCM_CTS) { + seq_printf(m, "%c%s", sep, "CTS"); + sep = '|'; + } + if (sigs & TIOCM_DTR) { + seq_printf(m, "%c%s", sep, "DTR"); + sep = '|'; + } + if (sigs & TIOCM_CD) { + seq_printf(m, "%c%s", sep, "DCD"); + sep = '|'; + } + if (sigs & TIOCM_DSR) { + seq_printf(m, "%c%s", sep, "DSR"); + sep = '|'; + } + seq_putc(m, '\n'); +} + +/*****************************************************************************/ + +/* + * Port info, read from the /proc file system. + */ + +static int stl_proc_show(struct seq_file *m, void *v) +{ + struct stlbrd *brdp; + struct stlpanel *panelp; + struct stlport *portp; + unsigned int brdnr, panelnr, portnr; + int totalport; + + totalport = 0; + + seq_printf(m, "%s: version %s\n", stl_drvtitle, stl_drvversion); + +/* + * 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 < stl_nrbrds; brdnr++) { + brdp = stl_brds[brdnr]; + if (brdp == NULL) + continue; + if (brdp->state == 0) + continue; + + totalport = brdnr * STL_MAXPORTS; + for (panelnr = 0; panelnr < brdp->nrpanels; panelnr++) { + panelp = brdp->panels[panelnr]; + if (panelp == NULL) + continue; + + for (portnr = 0; portnr < panelp->nrports; portnr++, + totalport++) { + portp = panelp->ports[portnr]; + if (portp == NULL) + continue; + stl_portinfo(m, portp, totalport); + } + } + } + return 0; +} + +static int stl_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, stl_proc_show, NULL); +} + +static const struct file_operations stl_proc_fops = { + .owner = THIS_MODULE, + .open = stl_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/*****************************************************************************/ + +/* + * All board interrupts are vectored through here first. This code then + * calls off to the approrpriate board interrupt handlers. + */ + +static irqreturn_t stl_intr(int irq, void *dev_id) +{ + struct stlbrd *brdp = dev_id; + + pr_debug("stl_intr(brdp=%p,irq=%d)\n", brdp, brdp->irq); + + return IRQ_RETVAL((* brdp->isr)(brdp)); +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for EasyIO board types. + */ + +static int stl_eiointr(struct stlbrd *brdp) +{ + struct stlpanel *panelp; + unsigned int iobase; + int handled = 0; + + spin_lock(&brd_lock); + panelp = brdp->panels[0]; + iobase = panelp->iobase; + while (inb(brdp->iostatus) & EIO_INTRPEND) { + handled = 1; + (* panelp->isr)(panelp, iobase); + } + spin_unlock(&brd_lock); + return handled; +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for ECH-AT board types. + */ + +static int stl_echatintr(struct stlbrd *brdp) +{ + struct stlpanel *panelp; + unsigned int ioaddr, bnknr; + int handled = 0; + + outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); + + while (inb(brdp->iostatus) & ECH_INTRPEND) { + handled = 1; + for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); + } + } + } + + outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); + + return handled; +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for ECH-MCA board types. + */ + +static int stl_echmcaintr(struct stlbrd *brdp) +{ + struct stlpanel *panelp; + unsigned int ioaddr, bnknr; + int handled = 0; + + while (inb(brdp->iostatus) & ECH_INTRPEND) { + handled = 1; + for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); + } + } + } + return handled; +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for ECH-PCI board types. + */ + +static int stl_echpciintr(struct stlbrd *brdp) +{ + struct stlpanel *panelp; + unsigned int ioaddr, bnknr, recheck; + int handled = 0; + + while (1) { + recheck = 0; + for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { + outb(brdp->bnkpageaddr[bnknr], brdp->ioctrl); + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); + recheck++; + handled = 1; + } + } + if (! recheck) + break; + } + return handled; +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for ECH-8/64-PCI board types. + */ + +static int stl_echpci64intr(struct stlbrd *brdp) +{ + struct stlpanel *panelp; + unsigned int ioaddr, bnknr; + int handled = 0; + + while (inb(brdp->ioctrl) & 0x1) { + handled = 1; + for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); + } + } + } + + return handled; +} + +/*****************************************************************************/ + +/* + * Initialize all the ports on a panel. + */ + +static int __devinit stl_initports(struct stlbrd *brdp, struct stlpanel *panelp) +{ + struct stlport *portp; + unsigned int i; + int chipmask; + + pr_debug("stl_initports(brdp=%p,panelp=%p)\n", brdp, panelp); + + chipmask = stl_panelinit(brdp, panelp); + +/* + * All UART's are initialized (if found!). Now go through and setup + * each ports data structures. + */ + for (i = 0; i < panelp->nrports; i++) { + portp = kzalloc(sizeof(struct stlport), GFP_KERNEL); + if (!portp) { + printk("STALLION: failed to allocate memory " + "(size=%Zd)\n", sizeof(struct stlport)); + break; + } + tty_port_init(&portp->port); + portp->port.ops = &stl_port_ops; + portp->magic = STL_PORTMAGIC; + portp->portnr = i; + portp->brdnr = panelp->brdnr; + portp->panelnr = panelp->panelnr; + portp->uartp = panelp->uartp; + portp->clk = brdp->clk; + portp->baud_base = STL_BAUDBASE; + portp->close_delay = STL_CLOSEDELAY; + portp->closing_wait = 30 * HZ; + init_waitqueue_head(&portp->port.open_wait); + init_waitqueue_head(&portp->port.close_wait); + portp->stats.brd = portp->brdnr; + portp->stats.panel = portp->panelnr; + portp->stats.port = portp->portnr; + panelp->ports[i] = portp; + stl_portinit(brdp, panelp, portp); + } + + return 0; +} + +static void stl_cleanup_panels(struct stlbrd *brdp) +{ + struct stlpanel *panelp; + struct stlport *portp; + unsigned int j, k; + struct tty_struct *tty; + + for (j = 0; j < STL_MAXPANELS; j++) { + panelp = brdp->panels[j]; + if (panelp == NULL) + continue; + for (k = 0; k < STL_PORTSPERPANEL; k++) { + portp = panelp->ports[k]; + if (portp == NULL) + continue; + tty = tty_port_tty_get(&portp->port); + if (tty != NULL) { + stl_hangup(tty); + tty_kref_put(tty); + } + kfree(portp->tx.buf); + kfree(portp); + } + kfree(panelp); + } +} + +/*****************************************************************************/ + +/* + * Try to find and initialize an EasyIO board. + */ + +static int __devinit stl_initeio(struct stlbrd *brdp) +{ + struct stlpanel *panelp; + unsigned int status; + char *name; + int retval; + + pr_debug("stl_initeio(brdp=%p)\n", brdp); + + brdp->ioctrl = brdp->ioaddr1 + 1; + brdp->iostatus = brdp->ioaddr1 + 2; + + status = inb(brdp->iostatus); + if ((status & EIO_IDBITMASK) == EIO_MK3) + brdp->ioctrl++; + +/* + * Handle board specific stuff now. The real difference is PCI + * or not PCI. + */ + if (brdp->brdtype == BRD_EASYIOPCI) { + brdp->iosize1 = 0x80; + brdp->iosize2 = 0x80; + name = "serial(EIO-PCI)"; + outb(0x41, (brdp->ioaddr2 + 0x4c)); + } else { + brdp->iosize1 = 8; + name = "serial(EIO)"; + if ((brdp->irq < 0) || (brdp->irq > 15) || + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); + retval = -EINVAL; + goto err; + } + outb((stl_vecmap[brdp->irq] | EIO_0WS | + ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), + brdp->ioctrl); + } + + retval = -EBUSY; + if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) { + printk(KERN_WARNING "STALLION: Warning, board %d I/O address " + "%x conflicts with another device\n", brdp->brdnr, + brdp->ioaddr1); + goto err; + } + + if (brdp->iosize2 > 0) + if (!request_region(brdp->ioaddr2, brdp->iosize2, name)) { + printk(KERN_WARNING "STALLION: Warning, board %d I/O " + "address %x conflicts with another device\n", + brdp->brdnr, brdp->ioaddr2); + printk(KERN_WARNING "STALLION: Warning, also " + "releasing board %d I/O address %x \n", + brdp->brdnr, brdp->ioaddr1); + goto err_rel1; + } + +/* + * Everything looks OK, so let's go ahead and probe for the hardware. + */ + brdp->clk = CD1400_CLK; + brdp->isr = stl_eiointr; + + retval = -ENODEV; + switch (status & EIO_IDBITMASK) { + case EIO_8PORTM: + brdp->clk = CD1400_CLK8M; + /* fall thru */ + case EIO_8PORTRS: + case EIO_8PORTDI: + brdp->nrports = 8; + break; + case EIO_4PORTRS: + brdp->nrports = 4; + break; + case EIO_MK3: + switch (status & EIO_BRDMASK) { + case ID_BRD4: + brdp->nrports = 4; + break; + case ID_BRD8: + brdp->nrports = 8; + break; + case ID_BRD16: + brdp->nrports = 16; + break; + default: + goto err_rel2; + } + break; + default: + goto err_rel2; + } + +/* + * We have verified that the board is actually present, so now we + * can complete the setup. + */ + + panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL); + if (!panelp) { + printk(KERN_WARNING "STALLION: failed to allocate memory " + "(size=%Zd)\n", sizeof(struct stlpanel)); + retval = -ENOMEM; + goto err_rel2; + } + + panelp->magic = STL_PANELMAGIC; + panelp->brdnr = brdp->brdnr; + panelp->panelnr = 0; + panelp->nrports = brdp->nrports; + panelp->iobase = brdp->ioaddr1; + panelp->hwid = status; + if ((status & EIO_IDBITMASK) == EIO_MK3) { + panelp->uartp = &stl_sc26198uart; + panelp->isr = stl_sc26198intr; + } else { + panelp->uartp = &stl_cd1400uart; + panelp->isr = stl_cd1400eiointr; + } + + brdp->panels[0] = panelp; + brdp->nrpanels = 1; + brdp->state |= BRD_FOUND; + brdp->hwid = status; + if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) { + printk("STALLION: failed to register interrupt " + "routine for %s irq=%d\n", name, brdp->irq); + retval = -ENODEV; + goto err_fr; + } + + return 0; +err_fr: + stl_cleanup_panels(brdp); +err_rel2: + if (brdp->iosize2 > 0) + release_region(brdp->ioaddr2, brdp->iosize2); +err_rel1: + release_region(brdp->ioaddr1, brdp->iosize1); +err: + return retval; +} + +/*****************************************************************************/ + +/* + * Try to find an ECH board and initialize it. This code is capable of + * dealing with all types of ECH board. + */ + +static int __devinit stl_initech(struct stlbrd *brdp) +{ + struct stlpanel *panelp; + unsigned int status, nxtid, ioaddr, conflict, panelnr, banknr, i; + int retval; + char *name; + + pr_debug("stl_initech(brdp=%p)\n", brdp); + + status = 0; + conflict = 0; + +/* + * Set up the initial board register contents for boards. This varies a + * bit between the different board types. So we need to handle each + * separately. Also do a check that the supplied IRQ is good. + */ + switch (brdp->brdtype) { + + case BRD_ECH: + brdp->isr = stl_echatintr; + brdp->ioctrl = brdp->ioaddr1 + 1; + brdp->iostatus = brdp->ioaddr1 + 1; + status = inb(brdp->iostatus); + if ((status & ECH_IDBITMASK) != ECH_ID) { + retval = -ENODEV; + goto err; + } + if ((brdp->irq < 0) || (brdp->irq > 15) || + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); + retval = -EINVAL; + goto err; + } + status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1); + status |= (stl_vecmap[brdp->irq] << 1); + outb((status | ECH_BRDRESET), brdp->ioaddr1); + brdp->ioctrlval = ECH_INTENABLE | + ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE); + for (i = 0; i < 10; i++) + outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); + brdp->iosize1 = 2; + brdp->iosize2 = 32; + name = "serial(EC8/32)"; + outb(status, brdp->ioaddr1); + break; + + case BRD_ECHMC: + brdp->isr = stl_echmcaintr; + brdp->ioctrl = brdp->ioaddr1 + 0x20; + brdp->iostatus = brdp->ioctrl; + status = inb(brdp->iostatus); + if ((status & ECH_IDBITMASK) != ECH_ID) { + retval = -ENODEV; + goto err; + } + if ((brdp->irq < 0) || (brdp->irq > 15) || + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); + retval = -EINVAL; + goto err; + } + outb(ECHMC_BRDRESET, brdp->ioctrl); + outb(ECHMC_INTENABLE, brdp->ioctrl); + brdp->iosize1 = 64; + name = "serial(EC8/32-MC)"; + break; + + case BRD_ECHPCI: + brdp->isr = stl_echpciintr; + brdp->ioctrl = brdp->ioaddr1 + 2; + brdp->iosize1 = 4; + brdp->iosize2 = 8; + name = "serial(EC8/32-PCI)"; + break; + + case BRD_ECH64PCI: + brdp->isr = stl_echpci64intr; + brdp->ioctrl = brdp->ioaddr2 + 0x40; + outb(0x43, (brdp->ioaddr1 + 0x4c)); + brdp->iosize1 = 0x80; + brdp->iosize2 = 0x80; + name = "serial(EC8/64-PCI)"; + break; + + default: + printk("STALLION: unknown board type=%d\n", brdp->brdtype); + retval = -EINVAL; + goto err; + } + +/* + * Check boards for possible IO address conflicts and return fail status + * if an IO conflict found. + */ + retval = -EBUSY; + if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) { + printk(KERN_WARNING "STALLION: Warning, board %d I/O address " + "%x conflicts with another device\n", brdp->brdnr, + brdp->ioaddr1); + goto err; + } + + if (brdp->iosize2 > 0) + if (!request_region(brdp->ioaddr2, brdp->iosize2, name)) { + printk(KERN_WARNING "STALLION: Warning, board %d I/O " + "address %x conflicts with another device\n", + brdp->brdnr, brdp->ioaddr2); + printk(KERN_WARNING "STALLION: Warning, also " + "releasing board %d I/O address %x \n", + brdp->brdnr, brdp->ioaddr1); + goto err_rel1; + } + +/* + * Scan through the secondary io address space looking for panels. + * As we find'em allocate and initialize panel structures for each. + */ + brdp->clk = CD1400_CLK; + brdp->hwid = status; + + ioaddr = brdp->ioaddr2; + banknr = 0; + panelnr = 0; + nxtid = 0; + + for (i = 0; i < STL_MAXPANELS; i++) { + if (brdp->brdtype == BRD_ECHPCI) { + outb(nxtid, brdp->ioctrl); + ioaddr = brdp->ioaddr2; + } + status = inb(ioaddr + ECH_PNLSTATUS); + if ((status & ECH_PNLIDMASK) != nxtid) + break; + panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL); + if (!panelp) { + printk("STALLION: failed to allocate memory " + "(size=%Zd)\n", sizeof(struct stlpanel)); + retval = -ENOMEM; + goto err_fr; + } + panelp->magic = STL_PANELMAGIC; + panelp->brdnr = brdp->brdnr; + panelp->panelnr = panelnr; + panelp->iobase = ioaddr; + panelp->pagenr = nxtid; + panelp->hwid = status; + brdp->bnk2panel[banknr] = panelp; + brdp->bnkpageaddr[banknr] = nxtid; + brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS; + + if (status & ECH_PNLXPID) { + panelp->uartp = &stl_sc26198uart; + panelp->isr = stl_sc26198intr; + if (status & ECH_PNL16PORT) { + panelp->nrports = 16; + brdp->bnk2panel[banknr] = panelp; + brdp->bnkpageaddr[banknr] = nxtid; + brdp->bnkstataddr[banknr++] = ioaddr + 4 + + ECH_PNLSTATUS; + } else + panelp->nrports = 8; + } else { + panelp->uartp = &stl_cd1400uart; + panelp->isr = stl_cd1400echintr; + if (status & ECH_PNL16PORT) { + panelp->nrports = 16; + panelp->ackmask = 0x80; + if (brdp->brdtype != BRD_ECHPCI) + ioaddr += EREG_BANKSIZE; + brdp->bnk2panel[banknr] = panelp; + brdp->bnkpageaddr[banknr] = ++nxtid; + brdp->bnkstataddr[banknr++] = ioaddr + + ECH_PNLSTATUS; + } else { + panelp->nrports = 8; + panelp->ackmask = 0xc0; + } + } + + nxtid++; + ioaddr += EREG_BANKSIZE; + brdp->nrports += panelp->nrports; + brdp->panels[panelnr++] = panelp; + if ((brdp->brdtype != BRD_ECHPCI) && + (ioaddr >= (brdp->ioaddr2 + brdp->iosize2))) { + retval = -EINVAL; + goto err_fr; + } + } + + brdp->nrpanels = panelnr; + brdp->nrbnks = banknr; + if (brdp->brdtype == BRD_ECH) + outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); + + brdp->state |= BRD_FOUND; + if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) { + printk("STALLION: failed to register interrupt " + "routine for %s irq=%d\n", name, brdp->irq); + retval = -ENODEV; + goto err_fr; + } + + return 0; +err_fr: + stl_cleanup_panels(brdp); + if (brdp->iosize2 > 0) + release_region(brdp->ioaddr2, brdp->iosize2); +err_rel1: + release_region(brdp->ioaddr1, brdp->iosize1); +err: + return retval; +} + +/*****************************************************************************/ + +/* + * Initialize and configure the specified board. + * Scan through all the boards in the configuration and see what we + * can find. Handle EIO and the ECH boards a little differently here + * since the initial search and setup is very different. + */ + +static int __devinit stl_brdinit(struct stlbrd *brdp) +{ + int i, retval; + + pr_debug("stl_brdinit(brdp=%p)\n", brdp); + + switch (brdp->brdtype) { + case BRD_EASYIO: + case BRD_EASYIOPCI: + retval = stl_initeio(brdp); + if (retval) + goto err; + break; + case BRD_ECH: + case BRD_ECHMC: + case BRD_ECHPCI: + case BRD_ECH64PCI: + retval = stl_initech(brdp); + if (retval) + goto err; + break; + default: + printk("STALLION: board=%d is unknown board type=%d\n", + brdp->brdnr, brdp->brdtype); + retval = -ENODEV; + goto err; + } + + if ((brdp->state & BRD_FOUND) == 0) { + printk("STALLION: %s board not found, board=%d io=%x irq=%d\n", + stl_brdnames[brdp->brdtype], brdp->brdnr, + brdp->ioaddr1, brdp->irq); + goto err_free; + } + + for (i = 0; i < STL_MAXPANELS; i++) + if (brdp->panels[i] != NULL) + stl_initports(brdp, brdp->panels[i]); + + printk("STALLION: %s found, board=%d io=%x irq=%d " + "nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], + brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, + brdp->nrports); + + return 0; +err_free: + free_irq(brdp->irq, brdp); + + stl_cleanup_panels(brdp); + + release_region(brdp->ioaddr1, brdp->iosize1); + if (brdp->iosize2 > 0) + release_region(brdp->ioaddr2, brdp->iosize2); +err: + return retval; +} + +/*****************************************************************************/ + +/* + * Find the next available board number that is free. + */ + +static int __devinit stl_getbrdnr(void) +{ + unsigned int i; + + for (i = 0; i < STL_MAXBRDS; i++) + if (stl_brds[i] == NULL) { + if (i >= stl_nrbrds) + stl_nrbrds = i + 1; + return i; + } + + return -1; +} + +/*****************************************************************************/ +/* + * We have a Stallion board. Allocate a board structure and + * initialize it. Read its IO and IRQ resources from PCI + * configuration space. + */ + +static int __devinit stl_pciprobe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct stlbrd *brdp; + unsigned int i, brdtype = ent->driver_data; + int brdnr, retval = -ENODEV; + + if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) + goto err; + + retval = pci_enable_device(pdev); + if (retval) + goto err; + brdp = stl_allocbrd(); + if (brdp == NULL) { + retval = -ENOMEM; + goto err; + } + mutex_lock(&stl_brdslock); + brdnr = stl_getbrdnr(); + if (brdnr < 0) { + dev_err(&pdev->dev, "too many boards found, " + "maximum supported %d\n", STL_MAXBRDS); + mutex_unlock(&stl_brdslock); + retval = -ENODEV; + goto err_fr; + } + brdp->brdnr = (unsigned int)brdnr; + stl_brds[brdp->brdnr] = brdp; + mutex_unlock(&stl_brdslock); + + brdp->brdtype = brdtype; + brdp->state |= STL_PROBED; + +/* + * We have all resources from the board, so let's setup the actual + * board structure now. + */ + switch (brdtype) { + case BRD_ECHPCI: + brdp->ioaddr2 = pci_resource_start(pdev, 0); + brdp->ioaddr1 = pci_resource_start(pdev, 1); + break; + case BRD_ECH64PCI: + brdp->ioaddr2 = pci_resource_start(pdev, 2); + brdp->ioaddr1 = pci_resource_start(pdev, 1); + break; + case BRD_EASYIOPCI: + brdp->ioaddr1 = pci_resource_start(pdev, 2); + brdp->ioaddr2 = pci_resource_start(pdev, 1); + break; + default: + dev_err(&pdev->dev, "unknown PCI board type=%u\n", brdtype); + break; + } + + brdp->irq = pdev->irq; + retval = stl_brdinit(brdp); + if (retval) + goto err_null; + + pci_set_drvdata(pdev, brdp); + + for (i = 0; i < brdp->nrports; i++) + tty_register_device(stl_serial, + brdp->brdnr * STL_MAXPORTS + i, &pdev->dev); + + return 0; +err_null: + stl_brds[brdp->brdnr] = NULL; +err_fr: + kfree(brdp); +err: + return retval; +} + +static void __devexit stl_pciremove(struct pci_dev *pdev) +{ + struct stlbrd *brdp = pci_get_drvdata(pdev); + unsigned int i; + + free_irq(brdp->irq, brdp); + + stl_cleanup_panels(brdp); + + release_region(brdp->ioaddr1, brdp->iosize1); + if (brdp->iosize2 > 0) + release_region(brdp->ioaddr2, brdp->iosize2); + + for (i = 0; i < brdp->nrports; i++) + tty_unregister_device(stl_serial, + brdp->brdnr * STL_MAXPORTS + i); + + stl_brds[brdp->brdnr] = NULL; + kfree(brdp); +} + +static struct pci_driver stl_pcidriver = { + .name = "stallion", + .id_table = stl_pcibrds, + .probe = stl_pciprobe, + .remove = __devexit_p(stl_pciremove) +}; + +/*****************************************************************************/ + +/* + * Return the board stats structure to user app. + */ + +static int stl_getbrdstats(combrd_t __user *bp) +{ + combrd_t stl_brdstats; + struct stlbrd *brdp; + struct stlpanel *panelp; + unsigned int i; + + if (copy_from_user(&stl_brdstats, bp, sizeof(combrd_t))) + return -EFAULT; + if (stl_brdstats.brd >= STL_MAXBRDS) + return -ENODEV; + brdp = stl_brds[stl_brdstats.brd]; + if (brdp == NULL) + return -ENODEV; + + memset(&stl_brdstats, 0, sizeof(combrd_t)); + stl_brdstats.brd = brdp->brdnr; + stl_brdstats.type = brdp->brdtype; + stl_brdstats.hwid = brdp->hwid; + stl_brdstats.state = brdp->state; + stl_brdstats.ioaddr = brdp->ioaddr1; + stl_brdstats.ioaddr2 = brdp->ioaddr2; + stl_brdstats.irq = brdp->irq; + stl_brdstats.nrpanels = brdp->nrpanels; + stl_brdstats.nrports = brdp->nrports; + for (i = 0; i < brdp->nrpanels; i++) { + panelp = brdp->panels[i]; + stl_brdstats.panels[i].panel = i; + stl_brdstats.panels[i].hwid = panelp->hwid; + stl_brdstats.panels[i].nrports = panelp->nrports; + } + + return copy_to_user(bp, &stl_brdstats, sizeof(combrd_t)) ? -EFAULT : 0; +} + +/*****************************************************************************/ + +/* + * Resolve the referenced port number into a port struct pointer. + */ + +static struct stlport *stl_getport(int brdnr, int panelnr, int portnr) +{ + struct stlbrd *brdp; + struct stlpanel *panelp; + + if (brdnr < 0 || brdnr >= STL_MAXBRDS) + return NULL; + brdp = stl_brds[brdnr]; + if (brdp == NULL) + return NULL; + if (panelnr < 0 || (unsigned int)panelnr >= brdp->nrpanels) + return NULL; + panelp = brdp->panels[panelnr]; + if (panelp == NULL) + return NULL; + if (portnr < 0 || (unsigned int)portnr >= panelp->nrports) + return NULL; + return panelp->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 stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp) +{ + comstats_t stl_comstats; + unsigned char *head, *tail; + unsigned long flags; + + if (!portp) { + if (copy_from_user(&stl_comstats, cp, sizeof(comstats_t))) + return -EFAULT; + portp = stl_getport(stl_comstats.brd, stl_comstats.panel, + stl_comstats.port); + if (portp == NULL) + return -ENODEV; + } + + mutex_lock(&portp->port.mutex); + portp->stats.state = portp->istate; + portp->stats.flags = portp->port.flags; + portp->stats.hwid = portp->hwid; + + portp->stats.ttystate = 0; + portp->stats.cflags = 0; + portp->stats.iflags = 0; + portp->stats.oflags = 0; + portp->stats.lflags = 0; + portp->stats.rxbuffered = 0; + + spin_lock_irqsave(&stallion_lock, flags); + if (tty != NULL && portp->port.tty == tty) { + portp->stats.ttystate = tty->flags; + /* No longer available as a statistic */ + portp->stats.rxbuffered = 1; /*tty->flip.count; */ + if (tty->termios != NULL) { + portp->stats.cflags = tty->termios->c_cflag; + portp->stats.iflags = tty->termios->c_iflag; + portp->stats.oflags = tty->termios->c_oflag; + portp->stats.lflags = tty->termios->c_lflag; + } + } + spin_unlock_irqrestore(&stallion_lock, flags); + + head = portp->tx.head; + tail = portp->tx.tail; + portp->stats.txbuffered = (head >= tail) ? (head - tail) : + (STL_TXBUFSIZE - (tail - head)); + + portp->stats.signals = (unsigned long) stl_getsignals(portp); + mutex_unlock(&portp->port.mutex); + + return copy_to_user(cp, &portp->stats, + sizeof(comstats_t)) ? -EFAULT : 0; +} + +/*****************************************************************************/ + +/* + * Clear the port stats structure. We also return it zeroed out... + */ + +static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp) +{ + comstats_t stl_comstats; + + if (!portp) { + if (copy_from_user(&stl_comstats, cp, sizeof(comstats_t))) + return -EFAULT; + portp = stl_getport(stl_comstats.brd, stl_comstats.panel, + stl_comstats.port); + if (portp == NULL) + return -ENODEV; + } + + mutex_lock(&portp->port.mutex); + memset(&portp->stats, 0, sizeof(comstats_t)); + portp->stats.brd = portp->brdnr; + portp->stats.panel = portp->panelnr; + portp->stats.port = portp->portnr; + mutex_unlock(&portp->port.mutex); + return copy_to_user(cp, &portp->stats, + sizeof(comstats_t)) ? -EFAULT : 0; +} + +/*****************************************************************************/ + +/* + * Return the entire driver ports structure to a user app. + */ + +static int stl_getportstruct(struct stlport __user *arg) +{ + struct stlport stl_dummyport; + struct stlport *portp; + + if (copy_from_user(&stl_dummyport, arg, sizeof(struct stlport))) + return -EFAULT; + portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr, + stl_dummyport.portnr); + if (!portp) + return -ENODEV; + return copy_to_user(arg, portp, sizeof(struct stlport)) ? -EFAULT : 0; +} + +/*****************************************************************************/ + +/* + * Return the entire driver board structure to a user app. + */ + +static int stl_getbrdstruct(struct stlbrd __user *arg) +{ + struct stlbrd stl_dummybrd; + struct stlbrd *brdp; + + if (copy_from_user(&stl_dummybrd, arg, sizeof(struct stlbrd))) + return -EFAULT; + if (stl_dummybrd.brdnr >= STL_MAXBRDS) + return -ENODEV; + brdp = stl_brds[stl_dummybrd.brdnr]; + if (!brdp) + return -ENODEV; + return copy_to_user(arg, brdp, sizeof(struct stlbrd)) ? -EFAULT : 0; +} + +/*****************************************************************************/ + +/* + * The "staliomem" device is also required to do some special operations + * on the board and/or ports. In this driver it is mostly used for stats + * collection. + */ + +static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + int brdnr, rc; + void __user *argp = (void __user *)arg; + + pr_debug("stl_memioctl(fp=%p,cmd=%x,arg=%lx)\n", fp, cmd,arg); + + brdnr = iminor(fp->f_dentry->d_inode); + if (brdnr >= STL_MAXBRDS) + return -ENODEV; + rc = 0; + + switch (cmd) { + case COM_GETPORTSTATS: + rc = stl_getportstats(NULL, NULL, argp); + break; + case COM_CLRPORTSTATS: + rc = stl_clrportstats(NULL, argp); + break; + case COM_GETBRDSTATS: + rc = stl_getbrdstats(argp); + break; + case COM_READPORT: + rc = stl_getportstruct(argp); + break; + case COM_READBOARD: + rc = stl_getbrdstruct(argp); + break; + default: + rc = -ENOIOCTLCMD; + break; + } + return rc; +} + +static const struct tty_operations stl_ops = { + .open = stl_open, + .close = stl_close, + .write = stl_write, + .put_char = stl_putchar, + .flush_chars = stl_flushchars, + .write_room = stl_writeroom, + .chars_in_buffer = stl_charsinbuffer, + .ioctl = stl_ioctl, + .set_termios = stl_settermios, + .throttle = stl_throttle, + .unthrottle = stl_unthrottle, + .stop = stl_stop, + .start = stl_start, + .hangup = stl_hangup, + .flush_buffer = stl_flushbuffer, + .break_ctl = stl_breakctl, + .wait_until_sent = stl_waituntilsent, + .send_xchar = stl_sendxchar, + .tiocmget = stl_tiocmget, + .tiocmset = stl_tiocmset, + .proc_fops = &stl_proc_fops, +}; + +static const struct tty_port_operations stl_port_ops = { + .carrier_raised = stl_carrier_raised, + .dtr_rts = stl_dtr_rts, + .activate = stl_activate, + .shutdown = stl_shutdown, +}; + +/*****************************************************************************/ +/* CD1400 HARDWARE FUNCTIONS */ +/*****************************************************************************/ + +/* + * These functions get/set/update the registers of the cd1400 UARTs. + * Access to the cd1400 registers is via an address/data io port pair. + * (Maybe should make this inline...) + */ + +static int stl_cd1400getreg(struct stlport *portp, int regnr) +{ + outb((regnr + portp->uartaddr), portp->ioaddr); + return inb(portp->ioaddr + EREG_DATA); +} + +static void stl_cd1400setreg(struct stlport *portp, int regnr, int value) +{ + outb(regnr + portp->uartaddr, portp->ioaddr); + outb(value, portp->ioaddr + EREG_DATA); +} + +static int stl_cd1400updatereg(struct stlport *portp, int regnr, int value) +{ + outb(regnr + portp->uartaddr, portp->ioaddr); + if (inb(portp->ioaddr + EREG_DATA) != value) { + outb(value, portp->ioaddr + EREG_DATA); + return 1; + } + return 0; +} + +/*****************************************************************************/ + +/* + * Inbitialize the UARTs in a panel. We don't care what sort of board + * these ports are on - since the port io registers are almost + * identical when dealing with ports. + */ + +static int stl_cd1400panelinit(struct stlbrd *brdp, struct stlpanel *panelp) +{ + unsigned int gfrcr; + int chipmask, i, j; + int nrchips, uartaddr, ioaddr; + unsigned long flags; + + pr_debug("stl_panelinit(brdp=%p,panelp=%p)\n", brdp, panelp); + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(panelp->brdnr, panelp->pagenr); + +/* + * Check that each chip is present and started up OK. + */ + chipmask = 0; + nrchips = panelp->nrports / CD1400_PORTS; + for (i = 0; i < nrchips; i++) { + if (brdp->brdtype == BRD_ECHPCI) { + outb((panelp->pagenr + (i >> 1)), brdp->ioctrl); + ioaddr = panelp->iobase; + } else + ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1)); + uartaddr = (i & 0x01) ? 0x080 : 0; + outb((GFRCR + uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); + outb((CCR + uartaddr), ioaddr); + outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); + outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); + outb((GFRCR + uartaddr), ioaddr); + for (j = 0; j < CCR_MAXWAIT; j++) + if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0) + break; + + if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) { + printk("STALLION: cd1400 not responding, " + "brd=%d panel=%d chip=%d\n", + panelp->brdnr, panelp->panelnr, i); + continue; + } + chipmask |= (0x1 << i); + outb((PPR + uartaddr), ioaddr); + outb(PPR_SCALAR, (ioaddr + EREG_DATA)); + } + + BRDDISABLE(panelp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); + return chipmask; +} + +/*****************************************************************************/ + +/* + * Initialize hardware specific port registers. + */ + +static void stl_cd1400portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp) +{ + unsigned long flags; + pr_debug("stl_cd1400portinit(brdp=%p,panelp=%p,portp=%p)\n", brdp, + panelp, portp); + + if ((brdp == NULL) || (panelp == NULL) || + (portp == NULL)) + return; + + spin_lock_irqsave(&brd_lock, flags); + portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) || + (portp->portnr < 8)) ? 0 : EREG_BANKSIZE); + portp->uartaddr = (portp->portnr & 0x04) << 5; + portp->pagenr = panelp->pagenr + (portp->portnr >> 3); + + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, LIVR, (portp->portnr << 3)); + portp->hwid = stl_cd1400getreg(portp, GFRCR); + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Wait for the command register to be ready. We will poll this, + * since it won't usually take too long to be ready. + */ + +static void stl_cd1400ccrwait(struct stlport *portp) +{ + int i; + + for (i = 0; i < CCR_MAXWAIT; i++) + if (stl_cd1400getreg(portp, CCR) == 0) + return; + + printk("STALLION: cd1400 not responding, port=%d panel=%d brd=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); +} + +/*****************************************************************************/ + +/* + * Set up the cd1400 registers for a port based on the termios port + * settings. + */ + +static void stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp) +{ + struct stlbrd *brdp; + unsigned long flags; + unsigned int clkdiv, baudrate; + unsigned char cor1, cor2, cor3; + unsigned char cor4, cor5, ccr; + unsigned char srer, sreron, sreroff; + unsigned char mcor1, mcor2, rtpr; + unsigned char clk, div; + + cor1 = 0; + cor2 = 0; + cor3 = 0; + cor4 = 0; + cor5 = 0; + ccr = 0; + rtpr = 0; + clk = 0; + div = 0; + mcor1 = 0; + mcor2 = 0; + sreron = 0; + sreroff = 0; + + brdp = stl_brds[portp->brdnr]; + if (brdp == NULL) + return; + +/* + * Set up the RX char ignore mask with those RX error types we + * can ignore. We can get the cd1400 to help us out a little here, + * it will ignore parity errors and breaks for us. + */ + portp->rxignoremsk = 0; + if (tiosp->c_iflag & IGNPAR) { + portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN); + cor1 |= COR1_PARIGNORE; + } + if (tiosp->c_iflag & IGNBRK) { + portp->rxignoremsk |= ST_BREAK; + cor4 |= COR4_IGNBRK; + } + + portp->rxmarkmsk = ST_OVERRUN; + if (tiosp->c_iflag & (INPCK | PARMRK)) + portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING); + if (tiosp->c_iflag & BRKINT) + portp->rxmarkmsk |= ST_BREAK; + +/* + * Go through the char size, parity and stop bits and set all the + * option register appropriately. + */ + switch (tiosp->c_cflag & CSIZE) { + case CS5: + cor1 |= COR1_CHL5; + break; + case CS6: + cor1 |= COR1_CHL6; + break; + case CS7: + cor1 |= COR1_CHL7; + break; + default: + cor1 |= COR1_CHL8; + break; + } + + if (tiosp->c_cflag & CSTOPB) + cor1 |= COR1_STOP2; + else + cor1 |= COR1_STOP1; + + if (tiosp->c_cflag & PARENB) { + if (tiosp->c_cflag & PARODD) + cor1 |= (COR1_PARENB | COR1_PARODD); + else + cor1 |= (COR1_PARENB | COR1_PAREVEN); + } else { + cor1 |= COR1_PARNONE; + } + +/* + * Set the RX FIFO threshold at 6 chars. This gives a bit of breathing + * space for hardware flow control and the like. This should be set to + * VMIN. Also here we will set the RX data timeout to 10ms - this should + * really be based on VTIME. + */ + cor3 |= FIFO_RXTHRESHOLD; + rtpr = 2; + +/* + * Calculate the baud rate timers. For now we will just assume that + * the input and output baud are the same. Could have used a baud + * table here, but this way we can generate virtually any baud rate + * we like! + */ + baudrate = tiosp->c_cflag & CBAUD; + if (baudrate & CBAUDEX) { + baudrate &= ~CBAUDEX; + if ((baudrate < 1) || (baudrate > 4)) + tiosp->c_cflag &= ~CBAUDEX; + else + baudrate += 15; + } + baudrate = stl_baudrates[baudrate]; + if ((tiosp->c_cflag & CBAUD) == B38400) { + if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baudrate = 57600; + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baudrate = 115200; + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + baudrate = 230400; + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + baudrate = 460800; + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + baudrate = (portp->baud_base / portp->custom_divisor); + } + if (baudrate > STL_CD1400MAXBAUD) + baudrate = STL_CD1400MAXBAUD; + + if (baudrate > 0) { + for (clk = 0; clk < CD1400_NUMCLKS; clk++) { + clkdiv = (portp->clk / stl_cd1400clkdivs[clk]) / baudrate; + if (clkdiv < 0x100) + break; + } + div = (unsigned char) clkdiv; + } + +/* + * Check what form of modem signaling is required and set it up. + */ + if ((tiosp->c_cflag & CLOCAL) == 0) { + mcor1 |= MCOR1_DCD; + mcor2 |= MCOR2_DCD; + sreron |= SRER_MODEM; + portp->port.flags |= ASYNC_CHECK_CD; + } else + portp->port.flags &= ~ASYNC_CHECK_CD; + +/* + * Setup cd1400 enhanced modes if we can. In particular we want to + * handle as much of the flow control as possible automatically. As + * well as saving a few CPU cycles it will also greatly improve flow + * control reliability. + */ + if (tiosp->c_iflag & IXON) { + cor2 |= COR2_TXIBE; + cor3 |= COR3_SCD12; + if (tiosp->c_iflag & IXANY) + cor2 |= COR2_IXM; + } + + if (tiosp->c_cflag & CRTSCTS) { + cor2 |= COR2_CTSAE; + mcor1 |= FIFO_RTSTHRESHOLD; + } + +/* + * All cd1400 register values calculated so go through and set + * them all up. + */ + + pr_debug("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); + pr_debug(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", + cor1, cor2, cor3, cor4, cor5); + pr_debug(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", + mcor1, mcor2, rtpr, sreron, sreroff); + pr_debug(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div); + pr_debug(" schr1=%x schr2=%x schr3=%x schr4=%x\n", + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x3)); + srer = stl_cd1400getreg(portp, SRER); + stl_cd1400setreg(portp, SRER, 0); + if (stl_cd1400updatereg(portp, COR1, cor1)) + ccr = 1; + if (stl_cd1400updatereg(portp, COR2, cor2)) + ccr = 1; + if (stl_cd1400updatereg(portp, COR3, cor3)) + ccr = 1; + if (ccr) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_CORCHANGE); + } + stl_cd1400setreg(portp, COR4, cor4); + stl_cd1400setreg(portp, COR5, cor5); + stl_cd1400setreg(portp, MCOR1, mcor1); + stl_cd1400setreg(portp, MCOR2, mcor2); + if (baudrate > 0) { + stl_cd1400setreg(portp, TCOR, clk); + stl_cd1400setreg(portp, TBPR, div); + stl_cd1400setreg(portp, RCOR, clk); + stl_cd1400setreg(portp, RBPR, div); + } + stl_cd1400setreg(portp, SCHR1, tiosp->c_cc[VSTART]); + stl_cd1400setreg(portp, SCHR2, tiosp->c_cc[VSTOP]); + stl_cd1400setreg(portp, SCHR3, tiosp->c_cc[VSTART]); + stl_cd1400setreg(portp, SCHR4, tiosp->c_cc[VSTOP]); + stl_cd1400setreg(portp, RTPR, rtpr); + mcor1 = stl_cd1400getreg(portp, MSVR1); + if (mcor1 & MSVR1_DCD) + portp->sigs |= TIOCM_CD; + else + portp->sigs &= ~TIOCM_CD; + stl_cd1400setreg(portp, SRER, ((srer & ~sreroff) | sreron)); + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Set the state of the DTR and RTS signals. + */ + +static void stl_cd1400setsignals(struct stlport *portp, int dtr, int rts) +{ + unsigned char msvr1, msvr2; + unsigned long flags; + + pr_debug("stl_cd1400setsignals(portp=%p,dtr=%d,rts=%d)\n", + portp, dtr, rts); + + msvr1 = 0; + msvr2 = 0; + if (dtr > 0) + msvr1 = MSVR1_DTR; + if (rts > 0) + msvr2 = MSVR2_RTS; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + if (rts >= 0) + stl_cd1400setreg(portp, MSVR2, msvr2); + if (dtr >= 0) + stl_cd1400setreg(portp, MSVR1, msvr1); + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Return the state of the signals. + */ + +static int stl_cd1400getsignals(struct stlport *portp) +{ + unsigned char msvr1, msvr2; + unsigned long flags; + int sigs; + + pr_debug("stl_cd1400getsignals(portp=%p)\n", portp); + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + msvr1 = stl_cd1400getreg(portp, MSVR1); + msvr2 = stl_cd1400getreg(portp, MSVR2); + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); + + sigs = 0; + sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0; + sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0; + sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0; + sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0; +#if 0 + sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0; + sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0; +#else + sigs |= TIOCM_DSR; +#endif + return sigs; +} + +/*****************************************************************************/ + +/* + * Enable/Disable the Transmitter and/or Receiver. + */ + +static void stl_cd1400enablerxtx(struct stlport *portp, int rx, int tx) +{ + unsigned char ccr; + unsigned long flags; + + pr_debug("stl_cd1400enablerxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx); + + ccr = 0; + + if (tx == 0) + ccr |= CCR_TXDISABLE; + else if (tx > 0) + ccr |= CCR_TXENABLE; + if (rx == 0) + ccr |= CCR_RXDISABLE; + else if (rx > 0) + ccr |= CCR_RXENABLE; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, ccr); + stl_cd1400ccrwait(portp); + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Start/stop the Transmitter and/or Receiver. + */ + +static void stl_cd1400startrxtx(struct stlport *portp, int rx, int tx) +{ + unsigned char sreron, sreroff; + unsigned long flags; + + pr_debug("stl_cd1400startrxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx); + + sreron = 0; + sreroff = 0; + if (tx == 0) + sreroff |= (SRER_TXDATA | SRER_TXEMPTY); + else if (tx == 1) + sreron |= SRER_TXDATA; + else if (tx >= 2) + sreron |= SRER_TXEMPTY; + if (rx == 0) + sreroff |= SRER_RXDATA; + else if (rx > 0) + sreron |= SRER_RXDATA; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, SRER, + ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron)); + BRDDISABLE(portp->brdnr); + if (tx > 0) + set_bit(ASYI_TXBUSY, &portp->istate); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Disable all interrupts from this port. + */ + +static void stl_cd1400disableintrs(struct stlport *portp) +{ + unsigned long flags; + + pr_debug("stl_cd1400disableintrs(portp=%p)\n", portp); + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, SRER, 0); + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +static void stl_cd1400sendbreak(struct stlport *portp, int len) +{ + unsigned long flags; + + pr_debug("stl_cd1400sendbreak(portp=%p,len=%d)\n", portp, len); + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, SRER, + ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) | + SRER_TXEMPTY)); + BRDDISABLE(portp->brdnr); + portp->brklen = len; + if (len == 1) + portp->stats.txbreaks++; + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Take flow control actions... + */ + +static void stl_cd1400flowctrl(struct stlport *portp, int state) +{ + struct tty_struct *tty; + unsigned long flags; + + pr_debug("stl_cd1400flowctrl(portp=%p,state=%x)\n", portp, state); + + if (portp == NULL) + return; + tty = tty_port_tty_get(&portp->port); + if (tty == NULL) + return; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + + if (state) { + if (tty->termios->c_iflag & IXOFF) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1); + portp->stats.rxxon++; + stl_cd1400ccrwait(portp); + } +/* + * Question: should we return RTS to what it was before? It may + * have been set by an ioctl... Suppose not, since if you have + * hardware flow control set then it is pretty silly to go and + * set the RTS line by hand. + */ + if (tty->termios->c_cflag & CRTSCTS) { + stl_cd1400setreg(portp, MCOR1, + (stl_cd1400getreg(portp, MCOR1) | + FIFO_RTSTHRESHOLD)); + stl_cd1400setreg(portp, MSVR2, MSVR2_RTS); + portp->stats.rxrtson++; + } + } else { + if (tty->termios->c_iflag & IXOFF) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2); + portp->stats.rxxoff++; + stl_cd1400ccrwait(portp); + } + if (tty->termios->c_cflag & CRTSCTS) { + stl_cd1400setreg(portp, MCOR1, + (stl_cd1400getreg(portp, MCOR1) & 0xf0)); + stl_cd1400setreg(portp, MSVR2, 0); + portp->stats.rxrtsoff++; + } + } + + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); +} + +/*****************************************************************************/ + +/* + * Send a flow control character... + */ + +static void stl_cd1400sendflow(struct stlport *portp, int state) +{ + struct tty_struct *tty; + unsigned long flags; + + pr_debug("stl_cd1400sendflow(portp=%p,state=%x)\n", portp, state); + + if (portp == NULL) + return; + tty = tty_port_tty_get(&portp->port); + if (tty == NULL) + return; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + if (state) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1); + portp->stats.rxxon++; + stl_cd1400ccrwait(portp); + } else { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2); + portp->stats.rxxoff++; + stl_cd1400ccrwait(portp); + } + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); +} + +/*****************************************************************************/ + +static void stl_cd1400flush(struct stlport *portp) +{ + unsigned long flags; + + pr_debug("stl_cd1400flush(portp=%p)\n", portp); + + if (portp == NULL) + return; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_TXFLUSHFIFO); + stl_cd1400ccrwait(portp); + portp->tx.tail = portp->tx.head; + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Return the current state of data flow on this port. This is only + * really interesting when determining if data has fully completed + * transmission or not... This is easy for the cd1400, it accurately + * maintains the busy port flag. + */ + +static int stl_cd1400datastate(struct stlport *portp) +{ + pr_debug("stl_cd1400datastate(portp=%p)\n", portp); + + if (portp == NULL) + return 0; + + return test_bit(ASYI_TXBUSY, &portp->istate) ? 1 : 0; +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for cd1400 EasyIO boards. + */ + +static void stl_cd1400eiointr(struct stlpanel *panelp, unsigned int iobase) +{ + unsigned char svrtype; + + pr_debug("stl_cd1400eiointr(panelp=%p,iobase=%x)\n", panelp, iobase); + + spin_lock(&brd_lock); + outb(SVRR, iobase); + svrtype = inb(iobase + EREG_DATA); + if (panelp->nrports > 4) { + outb((SVRR + 0x80), iobase); + svrtype |= inb(iobase + EREG_DATA); + } + + if (svrtype & SVRR_RX) + stl_cd1400rxisr(panelp, iobase); + else if (svrtype & SVRR_TX) + stl_cd1400txisr(panelp, iobase); + else if (svrtype & SVRR_MDM) + stl_cd1400mdmisr(panelp, iobase); + + spin_unlock(&brd_lock); +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for cd1400 panels. + */ + +static void stl_cd1400echintr(struct stlpanel *panelp, unsigned int iobase) +{ + unsigned char svrtype; + + pr_debug("stl_cd1400echintr(panelp=%p,iobase=%x)\n", panelp, iobase); + + outb(SVRR, iobase); + svrtype = inb(iobase + EREG_DATA); + outb((SVRR + 0x80), iobase); + svrtype |= inb(iobase + EREG_DATA); + if (svrtype & SVRR_RX) + stl_cd1400rxisr(panelp, iobase); + else if (svrtype & SVRR_TX) + stl_cd1400txisr(panelp, iobase); + else if (svrtype & SVRR_MDM) + stl_cd1400mdmisr(panelp, iobase); +} + + +/*****************************************************************************/ + +/* + * Unfortunately we need to handle breaks in the TX data stream, since + * this is the only way to generate them on the cd1400. + */ + +static int stl_cd1400breakisr(struct stlport *portp, int ioaddr) +{ + if (portp->brklen == 1) { + outb((COR2 + portp->uartaddr), ioaddr); + outb((inb(ioaddr + EREG_DATA) | COR2_ETC), + (ioaddr + EREG_DATA)); + outb((TDR + portp->uartaddr), ioaddr); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_STARTBREAK, (ioaddr + EREG_DATA)); + outb((SRER + portp->uartaddr), ioaddr); + outb((inb(ioaddr + EREG_DATA) & ~(SRER_TXDATA | SRER_TXEMPTY)), + (ioaddr + EREG_DATA)); + return 1; + } else if (portp->brklen > 1) { + outb((TDR + portp->uartaddr), ioaddr); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_STOPBREAK, (ioaddr + EREG_DATA)); + portp->brklen = -1; + return 1; + } else { + outb((COR2 + portp->uartaddr), ioaddr); + outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), + (ioaddr + EREG_DATA)); + portp->brklen = 0; + } + return 0; +} + +/*****************************************************************************/ + +/* + * Transmit interrupt handler. This has gotta be fast! Handling TX + * chars is pretty simple, stuff as many as possible from the TX buffer + * into the cd1400 FIFO. Must also handle TX breaks here, since they + * are embedded as commands in the data stream. Oh no, had to use a goto! + * This could be optimized more, will do when I get time... + * In practice it is possible that interrupts are enabled but that the + * port has been hung up. Need to handle not having any TX buffer here, + * this is done by using the side effect that head and tail will also + * be NULL if the buffer has been freed. + */ + +static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr) +{ + struct stlport *portp; + int len, stlen; + char *head, *tail; + unsigned char ioack, srer; + struct tty_struct *tty; + + pr_debug("stl_cd1400txisr(panelp=%p,ioaddr=%x)\n", panelp, ioaddr); + + ioack = inb(ioaddr + EREG_TXACK); + if (((ioack & panelp->ackmask) != 0) || + ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { + printk("STALLION: bad TX interrupt ack value=%x\n", ioack); + return; + } + portp = panelp->ports[(ioack >> 3)]; + +/* + * Unfortunately we need to handle breaks in the data stream, since + * this is the only way to generate them on the cd1400. Do it now if + * a break is to be sent. + */ + if (portp->brklen != 0) + if (stl_cd1400breakisr(portp, ioaddr)) + goto stl_txalldone; + + head = portp->tx.head; + tail = portp->tx.tail; + len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); + if ((len == 0) || ((len < STL_TXBUFLOW) && + (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { + set_bit(ASYI_TXLOW, &portp->istate); + tty = tty_port_tty_get(&portp->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } + } + + if (len == 0) { + outb((SRER + portp->uartaddr), ioaddr); + srer = inb(ioaddr + EREG_DATA); + if (srer & SRER_TXDATA) { + srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY; + } else { + srer &= ~(SRER_TXDATA | SRER_TXEMPTY); + clear_bit(ASYI_TXBUSY, &portp->istate); + } + outb(srer, (ioaddr + EREG_DATA)); + } else { + len = min(len, CD1400_TXFIFOSIZE); + portp->stats.txtotal += len; + stlen = min_t(unsigned int, len, + (portp->tx.buf + STL_TXBUFSIZE) - tail); + outb((TDR + portp->uartaddr), ioaddr); + outsb((ioaddr + EREG_DATA), tail, stlen); + len -= stlen; + tail += stlen; + if (tail >= (portp->tx.buf + STL_TXBUFSIZE)) + tail = portp->tx.buf; + if (len > 0) { + outsb((ioaddr + EREG_DATA), tail, len); + tail += len; + } + portp->tx.tail = tail; + } + +stl_txalldone: + outb((EOSRR + portp->uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); +} + +/*****************************************************************************/ + +/* + * Receive character interrupt handler. Determine if we have good chars + * or bad chars and then process appropriately. Good chars are easy + * just shove the lot into the RX buffer and set all status byte to 0. + * If a bad RX char then process as required. This routine needs to be + * fast! In practice it is possible that we get an interrupt on a port + * that is closed. This can happen on hangups - since they completely + * shutdown a port not in user context. Need to handle this case. + */ + +static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr) +{ + struct stlport *portp; + struct tty_struct *tty; + unsigned int ioack, len, buflen; + unsigned char status; + char ch; + + pr_debug("stl_cd1400rxisr(panelp=%p,ioaddr=%x)\n", panelp, ioaddr); + + ioack = inb(ioaddr + EREG_RXACK); + if ((ioack & panelp->ackmask) != 0) { + printk("STALLION: bad RX interrupt ack value=%x\n", ioack); + return; + } + portp = panelp->ports[(ioack >> 3)]; + tty = tty_port_tty_get(&portp->port); + + if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { + outb((RDCR + portp->uartaddr), ioaddr); + len = inb(ioaddr + EREG_DATA); + if (tty == NULL || (buflen = tty_buffer_request_room(tty, len)) == 0) { + len = min_t(unsigned int, len, sizeof(stl_unwanted)); + outb((RDSR + portp->uartaddr), ioaddr); + insb((ioaddr + EREG_DATA), &stl_unwanted[0], len); + portp->stats.rxlost += len; + portp->stats.rxtotal += len; + } else { + len = min(len, buflen); + if (len > 0) { + unsigned char *ptr; + outb((RDSR + portp->uartaddr), ioaddr); + tty_prepare_flip_string(tty, &ptr, len); + insb((ioaddr + EREG_DATA), ptr, len); + tty_schedule_flip(tty); + portp->stats.rxtotal += len; + } + } + } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) { + outb((RDSR + portp->uartaddr), ioaddr); + status = inb(ioaddr + EREG_DATA); + ch = inb(ioaddr + EREG_DATA); + if (status & ST_PARITY) + portp->stats.rxparity++; + if (status & ST_FRAMING) + portp->stats.rxframing++; + if (status & ST_OVERRUN) + portp->stats.rxoverrun++; + if (status & ST_BREAK) + portp->stats.rxbreaks++; + if (status & ST_SCHARMASK) { + if ((status & ST_SCHARMASK) == ST_SCHAR1) + portp->stats.txxon++; + if ((status & ST_SCHARMASK) == ST_SCHAR2) + portp->stats.txxoff++; + goto stl_rxalldone; + } + if (tty != NULL && (portp->rxignoremsk & status) == 0) { + if (portp->rxmarkmsk & status) { + if (status & ST_BREAK) { + status = TTY_BREAK; + if (portp->port.flags & ASYNC_SAK) { + do_SAK(tty); + BRDENABLE(portp->brdnr, portp->pagenr); + } + } else if (status & ST_PARITY) + status = TTY_PARITY; + else if (status & ST_FRAMING) + status = TTY_FRAME; + else if(status & ST_OVERRUN) + status = TTY_OVERRUN; + else + status = 0; + } else + status = 0; + tty_insert_flip_char(tty, ch, status); + tty_schedule_flip(tty); + } + } else { + printk("STALLION: bad RX interrupt ack value=%x\n", ioack); + tty_kref_put(tty); + return; + } + +stl_rxalldone: + tty_kref_put(tty); + outb((EOSRR + portp->uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); +} + +/*****************************************************************************/ + +/* + * Modem interrupt handler. The is called when the modem signal line + * (DCD) has changed state. Leave most of the work to the off-level + * processing routine. + */ + +static void stl_cd1400mdmisr(struct stlpanel *panelp, int ioaddr) +{ + struct stlport *portp; + unsigned int ioack; + unsigned char misr; + + pr_debug("stl_cd1400mdmisr(panelp=%p)\n", panelp); + + ioack = inb(ioaddr + EREG_MDACK); + if (((ioack & panelp->ackmask) != 0) || + ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { + printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack); + return; + } + portp = panelp->ports[(ioack >> 3)]; + + outb((MISR + portp->uartaddr), ioaddr); + misr = inb(ioaddr + EREG_DATA); + if (misr & MISR_DCD) { + stl_cd_change(portp); + portp->stats.modem++; + } + + outb((EOSRR + portp->uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); +} + +/*****************************************************************************/ +/* SC26198 HARDWARE FUNCTIONS */ +/*****************************************************************************/ + +/* + * These functions get/set/update the registers of the sc26198 UARTs. + * Access to the sc26198 registers is via an address/data io port pair. + * (Maybe should make this inline...) + */ + +static int stl_sc26198getreg(struct stlport *portp, int regnr) +{ + outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); + return inb(portp->ioaddr + XP_DATA); +} + +static void stl_sc26198setreg(struct stlport *portp, int regnr, int value) +{ + outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); + outb(value, (portp->ioaddr + XP_DATA)); +} + +static int stl_sc26198updatereg(struct stlport *portp, int regnr, int value) +{ + outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); + if (inb(portp->ioaddr + XP_DATA) != value) { + outb(value, (portp->ioaddr + XP_DATA)); + return 1; + } + return 0; +} + +/*****************************************************************************/ + +/* + * Functions to get and set the sc26198 global registers. + */ + +static int stl_sc26198getglobreg(struct stlport *portp, int regnr) +{ + outb(regnr, (portp->ioaddr + XP_ADDR)); + return inb(portp->ioaddr + XP_DATA); +} + +#if 0 +static void stl_sc26198setglobreg(struct stlport *portp, int regnr, int value) +{ + outb(regnr, (portp->ioaddr + XP_ADDR)); + outb(value, (portp->ioaddr + XP_DATA)); +} +#endif + +/*****************************************************************************/ + +/* + * Inbitialize the UARTs in a panel. We don't care what sort of board + * these ports are on - since the port io registers are almost + * identical when dealing with ports. + */ + +static int stl_sc26198panelinit(struct stlbrd *brdp, struct stlpanel *panelp) +{ + int chipmask, i; + int nrchips, ioaddr; + + pr_debug("stl_sc26198panelinit(brdp=%p,panelp=%p)\n", brdp, panelp); + + BRDENABLE(panelp->brdnr, panelp->pagenr); + +/* + * Check that each chip is present and started up OK. + */ + chipmask = 0; + nrchips = (panelp->nrports + 4) / SC26198_PORTS; + if (brdp->brdtype == BRD_ECHPCI) + outb(panelp->pagenr, brdp->ioctrl); + + for (i = 0; i < nrchips; i++) { + ioaddr = panelp->iobase + (i * 4); + outb(SCCR, (ioaddr + XP_ADDR)); + outb(CR_RESETALL, (ioaddr + XP_DATA)); + outb(TSTR, (ioaddr + XP_ADDR)); + if (inb(ioaddr + XP_DATA) != 0) { + printk("STALLION: sc26198 not responding, " + "brd=%d panel=%d chip=%d\n", + panelp->brdnr, panelp->panelnr, i); + continue; + } + chipmask |= (0x1 << i); + outb(GCCR, (ioaddr + XP_ADDR)); + outb(GCCR_IVRTYPCHANACK, (ioaddr + XP_DATA)); + outb(WDTRCR, (ioaddr + XP_ADDR)); + outb(0xff, (ioaddr + XP_DATA)); + } + + BRDDISABLE(panelp->brdnr); + return chipmask; +} + +/*****************************************************************************/ + +/* + * Initialize hardware specific port registers. + */ + +static void stl_sc26198portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp) +{ + pr_debug("stl_sc26198portinit(brdp=%p,panelp=%p,portp=%p)\n", brdp, + panelp, portp); + + if ((brdp == NULL) || (panelp == NULL) || + (portp == NULL)) + return; + + portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4); + portp->uartaddr = (portp->portnr & 0x07) << 4; + portp->pagenr = panelp->pagenr; + portp->hwid = 0x1; + + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IOPCR, IOPCR_SETSIGS); + BRDDISABLE(portp->brdnr); +} + +/*****************************************************************************/ + +/* + * Set up the sc26198 registers for a port based on the termios port + * settings. + */ + +static void stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp) +{ + struct stlbrd *brdp; + unsigned long flags; + unsigned int baudrate; + unsigned char mr0, mr1, mr2, clk; + unsigned char imron, imroff, iopr, ipr; + + mr0 = 0; + mr1 = 0; + mr2 = 0; + clk = 0; + iopr = 0; + imron = 0; + imroff = 0; + + brdp = stl_brds[portp->brdnr]; + if (brdp == NULL) + return; + +/* + * Set up the RX char ignore mask with those RX error types we + * can ignore. + */ + portp->rxignoremsk = 0; + if (tiosp->c_iflag & IGNPAR) + portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING | + SR_RXOVERRUN); + if (tiosp->c_iflag & IGNBRK) + portp->rxignoremsk |= SR_RXBREAK; + + portp->rxmarkmsk = SR_RXOVERRUN; + if (tiosp->c_iflag & (INPCK | PARMRK)) + portp->rxmarkmsk |= (SR_RXPARITY | SR_RXFRAMING); + if (tiosp->c_iflag & BRKINT) + portp->rxmarkmsk |= SR_RXBREAK; + +/* + * Go through the char size, parity and stop bits and set all the + * option register appropriately. + */ + switch (tiosp->c_cflag & CSIZE) { + case CS5: + mr1 |= MR1_CS5; + break; + case CS6: + mr1 |= MR1_CS6; + break; + case CS7: + mr1 |= MR1_CS7; + break; + default: + mr1 |= MR1_CS8; + break; + } + + if (tiosp->c_cflag & CSTOPB) + mr2 |= MR2_STOP2; + else + mr2 |= MR2_STOP1; + + if (tiosp->c_cflag & PARENB) { + if (tiosp->c_cflag & PARODD) + mr1 |= (MR1_PARENB | MR1_PARODD); + else + mr1 |= (MR1_PARENB | MR1_PAREVEN); + } else + mr1 |= MR1_PARNONE; + + mr1 |= MR1_ERRBLOCK; + +/* + * Set the RX FIFO threshold at 8 chars. This gives a bit of breathing + * space for hardware flow control and the like. This should be set to + * VMIN. + */ + mr2 |= MR2_RXFIFOHALF; + +/* + * Calculate the baud rate timers. For now we will just assume that + * the input and output baud are the same. The sc26198 has a fixed + * baud rate table, so only discrete baud rates possible. + */ + baudrate = tiosp->c_cflag & CBAUD; + if (baudrate & CBAUDEX) { + baudrate &= ~CBAUDEX; + if ((baudrate < 1) || (baudrate > 4)) + tiosp->c_cflag &= ~CBAUDEX; + else + baudrate += 15; + } + baudrate = stl_baudrates[baudrate]; + if ((tiosp->c_cflag & CBAUD) == B38400) { + if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baudrate = 57600; + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baudrate = 115200; + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + baudrate = 230400; + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + baudrate = 460800; + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + baudrate = (portp->baud_base / portp->custom_divisor); + } + if (baudrate > STL_SC26198MAXBAUD) + baudrate = STL_SC26198MAXBAUD; + + if (baudrate > 0) + for (clk = 0; clk < SC26198_NRBAUDS; clk++) + if (baudrate <= sc26198_baudtable[clk]) + break; + +/* + * Check what form of modem signaling is required and set it up. + */ + if (tiosp->c_cflag & CLOCAL) { + portp->port.flags &= ~ASYNC_CHECK_CD; + } else { + iopr |= IOPR_DCDCOS; + imron |= IR_IOPORT; + portp->port.flags |= ASYNC_CHECK_CD; + } + +/* + * Setup sc26198 enhanced modes if we can. In particular we want to + * handle as much of the flow control as possible automatically. As + * well as saving a few CPU cycles it will also greatly improve flow + * control reliability. + */ + if (tiosp->c_iflag & IXON) { + mr0 |= MR0_SWFTX | MR0_SWFT; + imron |= IR_XONXOFF; + } else + imroff |= IR_XONXOFF; + + if (tiosp->c_iflag & IXOFF) + mr0 |= MR0_SWFRX; + + if (tiosp->c_cflag & CRTSCTS) { + mr2 |= MR2_AUTOCTS; + mr1 |= MR1_AUTORTS; + } + +/* + * All sc26198 register values calculated so go through and set + * them all up. + */ + + pr_debug("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); + pr_debug(" mr0=%x mr1=%x mr2=%x clk=%x\n", mr0, mr1, mr2, clk); + pr_debug(" iopr=%x imron=%x imroff=%x\n", iopr, imron, imroff); + pr_debug(" schr1=%x schr2=%x schr3=%x schr4=%x\n", + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IMR, 0); + stl_sc26198updatereg(portp, MR0, mr0); + stl_sc26198updatereg(portp, MR1, mr1); + stl_sc26198setreg(portp, SCCR, CR_RXERRBLOCK); + stl_sc26198updatereg(portp, MR2, mr2); + stl_sc26198updatereg(portp, IOPIOR, + ((stl_sc26198getreg(portp, IOPIOR) & ~IPR_CHANGEMASK) | iopr)); + + if (baudrate > 0) { + stl_sc26198setreg(portp, TXCSR, clk); + stl_sc26198setreg(portp, RXCSR, clk); + } + + stl_sc26198setreg(portp, XONCR, tiosp->c_cc[VSTART]); + stl_sc26198setreg(portp, XOFFCR, tiosp->c_cc[VSTOP]); + + ipr = stl_sc26198getreg(portp, IPR); + if (ipr & IPR_DCD) + portp->sigs &= ~TIOCM_CD; + else + portp->sigs |= TIOCM_CD; + + portp->imr = (portp->imr & ~imroff) | imron; + stl_sc26198setreg(portp, IMR, portp->imr); + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Set the state of the DTR and RTS signals. + */ + +static void stl_sc26198setsignals(struct stlport *portp, int dtr, int rts) +{ + unsigned char iopioron, iopioroff; + unsigned long flags; + + pr_debug("stl_sc26198setsignals(portp=%p,dtr=%d,rts=%d)\n", portp, + dtr, rts); + + iopioron = 0; + iopioroff = 0; + if (dtr == 0) + iopioroff |= IPR_DTR; + else if (dtr > 0) + iopioron |= IPR_DTR; + if (rts == 0) + iopioroff |= IPR_RTS; + else if (rts > 0) + iopioron |= IPR_RTS; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IOPIOR, + ((stl_sc26198getreg(portp, IOPIOR) & ~iopioroff) | iopioron)); + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Return the state of the signals. + */ + +static int stl_sc26198getsignals(struct stlport *portp) +{ + unsigned char ipr; + unsigned long flags; + int sigs; + + pr_debug("stl_sc26198getsignals(portp=%p)\n", portp); + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + ipr = stl_sc26198getreg(portp, IPR); + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); + + sigs = 0; + sigs |= (ipr & IPR_DCD) ? 0 : TIOCM_CD; + sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS; + sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR; + sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS; + sigs |= TIOCM_DSR; + return sigs; +} + +/*****************************************************************************/ + +/* + * Enable/Disable the Transmitter and/or Receiver. + */ + +static void stl_sc26198enablerxtx(struct stlport *portp, int rx, int tx) +{ + unsigned char ccr; + unsigned long flags; + + pr_debug("stl_sc26198enablerxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx,tx); + + ccr = portp->crenable; + if (tx == 0) + ccr &= ~CR_TXENABLE; + else if (tx > 0) + ccr |= CR_TXENABLE; + if (rx == 0) + ccr &= ~CR_RXENABLE; + else if (rx > 0) + ccr |= CR_RXENABLE; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, SCCR, ccr); + BRDDISABLE(portp->brdnr); + portp->crenable = ccr; + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Start/stop the Transmitter and/or Receiver. + */ + +static void stl_sc26198startrxtx(struct stlport *portp, int rx, int tx) +{ + unsigned char imr; + unsigned long flags; + + pr_debug("stl_sc26198startrxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx); + + imr = portp->imr; + if (tx == 0) + imr &= ~IR_TXRDY; + else if (tx == 1) + imr |= IR_TXRDY; + if (rx == 0) + imr &= ~(IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG); + else if (rx > 0) + imr |= IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IMR, imr); + BRDDISABLE(portp->brdnr); + portp->imr = imr; + if (tx > 0) + set_bit(ASYI_TXBUSY, &portp->istate); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Disable all interrupts from this port. + */ + +static void stl_sc26198disableintrs(struct stlport *portp) +{ + unsigned long flags; + + pr_debug("stl_sc26198disableintrs(portp=%p)\n", portp); + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + portp->imr = 0; + stl_sc26198setreg(portp, IMR, 0); + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +static void stl_sc26198sendbreak(struct stlport *portp, int len) +{ + unsigned long flags; + + pr_debug("stl_sc26198sendbreak(portp=%p,len=%d)\n", portp, len); + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + if (len == 1) { + stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK); + portp->stats.txbreaks++; + } else + stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK); + + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Take flow control actions... + */ + +static void stl_sc26198flowctrl(struct stlport *portp, int state) +{ + struct tty_struct *tty; + unsigned long flags; + unsigned char mr0; + + pr_debug("stl_sc26198flowctrl(portp=%p,state=%x)\n", portp, state); + + if (portp == NULL) + return; + tty = tty_port_tty_get(&portp->port); + if (tty == NULL) + return; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + + if (state) { + if (tty->termios->c_iflag & IXOFF) { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXON); + mr0 |= MR0_SWFRX; + portp->stats.rxxon++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } +/* + * Question: should we return RTS to what it was before? It may + * have been set by an ioctl... Suppose not, since if you have + * hardware flow control set then it is pretty silly to go and + * set the RTS line by hand. + */ + if (tty->termios->c_cflag & CRTSCTS) { + stl_sc26198setreg(portp, MR1, + (stl_sc26198getreg(portp, MR1) | MR1_AUTORTS)); + stl_sc26198setreg(portp, IOPIOR, + (stl_sc26198getreg(portp, IOPIOR) | IOPR_RTS)); + portp->stats.rxrtson++; + } + } else { + if (tty->termios->c_iflag & IXOFF) { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF); + mr0 &= ~MR0_SWFRX; + portp->stats.rxxoff++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } + if (tty->termios->c_cflag & CRTSCTS) { + stl_sc26198setreg(portp, MR1, + (stl_sc26198getreg(portp, MR1) & ~MR1_AUTORTS)); + stl_sc26198setreg(portp, IOPIOR, + (stl_sc26198getreg(portp, IOPIOR) & ~IOPR_RTS)); + portp->stats.rxrtsoff++; + } + } + + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); +} + +/*****************************************************************************/ + +/* + * Send a flow control character. + */ + +static void stl_sc26198sendflow(struct stlport *portp, int state) +{ + struct tty_struct *tty; + unsigned long flags; + unsigned char mr0; + + pr_debug("stl_sc26198sendflow(portp=%p,state=%x)\n", portp, state); + + if (portp == NULL) + return; + tty = tty_port_tty_get(&portp->port); + if (tty == NULL) + return; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + if (state) { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXON); + mr0 |= MR0_SWFRX; + portp->stats.rxxon++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } else { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF); + mr0 &= ~MR0_SWFRX; + portp->stats.rxxoff++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); +} + +/*****************************************************************************/ + +static void stl_sc26198flush(struct stlport *portp) +{ + unsigned long flags; + + pr_debug("stl_sc26198flush(portp=%p)\n", portp); + + if (portp == NULL) + return; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, SCCR, CR_TXRESET); + stl_sc26198setreg(portp, SCCR, portp->crenable); + BRDDISABLE(portp->brdnr); + portp->tx.tail = portp->tx.head; + spin_unlock_irqrestore(&brd_lock, flags); +} + +/*****************************************************************************/ + +/* + * Return the current state of data flow on this port. This is only + * really interesting when determining if data has fully completed + * transmission or not... The sc26198 interrupt scheme cannot + * determine when all data has actually drained, so we need to + * check the port statusy register to be sure. + */ + +static int stl_sc26198datastate(struct stlport *portp) +{ + unsigned long flags; + unsigned char sr; + + pr_debug("stl_sc26198datastate(portp=%p)\n", portp); + + if (portp == NULL) + return 0; + if (test_bit(ASYI_TXBUSY, &portp->istate)) + return 1; + + spin_lock_irqsave(&brd_lock, flags); + BRDENABLE(portp->brdnr, portp->pagenr); + sr = stl_sc26198getreg(portp, SR); + BRDDISABLE(portp->brdnr); + spin_unlock_irqrestore(&brd_lock, flags); + + return (sr & SR_TXEMPTY) ? 0 : 1; +} + +/*****************************************************************************/ + +/* + * Delay for a small amount of time, to give the sc26198 a chance + * to process a command... + */ + +static void stl_sc26198wait(struct stlport *portp) +{ + int i; + + pr_debug("stl_sc26198wait(portp=%p)\n", portp); + + if (portp == NULL) + return; + + for (i = 0; i < 20; i++) + stl_sc26198getglobreg(portp, TSTR); +} + +/*****************************************************************************/ + +/* + * If we are TX flow controlled and in IXANY mode then we may + * need to unflow control here. We gotta do this because of the + * automatic flow control modes of the sc26198. + */ + +static void stl_sc26198txunflow(struct stlport *portp, struct tty_struct *tty) +{ + unsigned char mr0; + + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_HOSTXON); + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + clear_bit(ASYI_TXFLOWED, &portp->istate); +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for sc26198 panels. + */ + +static void stl_sc26198intr(struct stlpanel *panelp, unsigned int iobase) +{ + struct stlport *portp; + unsigned int iack; + + spin_lock(&brd_lock); + +/* + * Work around bug in sc26198 chip... Cannot have A6 address + * line of UART high, else iack will be returned as 0. + */ + outb(0, (iobase + 1)); + + iack = inb(iobase + XP_IACK); + portp = panelp->ports[(iack & IVR_CHANMASK) + ((iobase & 0x4) << 1)]; + + if (iack & IVR_RXDATA) + stl_sc26198rxisr(portp, iack); + else if (iack & IVR_TXDATA) + stl_sc26198txisr(portp); + else + stl_sc26198otherisr(portp, iack); + + spin_unlock(&brd_lock); +} + +/*****************************************************************************/ + +/* + * Transmit interrupt handler. This has gotta be fast! Handling TX + * chars is pretty simple, stuff as many as possible from the TX buffer + * into the sc26198 FIFO. + * In practice it is possible that interrupts are enabled but that the + * port has been hung up. Need to handle not having any TX buffer here, + * this is done by using the side effect that head and tail will also + * be NULL if the buffer has been freed. + */ + +static void stl_sc26198txisr(struct stlport *portp) +{ + struct tty_struct *tty; + unsigned int ioaddr; + unsigned char mr0; + int len, stlen; + char *head, *tail; + + pr_debug("stl_sc26198txisr(portp=%p)\n", portp); + + ioaddr = portp->ioaddr; + head = portp->tx.head; + tail = portp->tx.tail; + len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); + if ((len == 0) || ((len < STL_TXBUFLOW) && + (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { + set_bit(ASYI_TXLOW, &portp->istate); + tty = tty_port_tty_get(&portp->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } + } + + if (len == 0) { + outb((MR0 | portp->uartaddr), (ioaddr + XP_ADDR)); + mr0 = inb(ioaddr + XP_DATA); + if ((mr0 & MR0_TXMASK) == MR0_TXEMPTY) { + portp->imr &= ~IR_TXRDY; + outb((IMR | portp->uartaddr), (ioaddr + XP_ADDR)); + outb(portp->imr, (ioaddr + XP_DATA)); + clear_bit(ASYI_TXBUSY, &portp->istate); + } else { + mr0 |= ((mr0 & ~MR0_TXMASK) | MR0_TXEMPTY); + outb(mr0, (ioaddr + XP_DATA)); + } + } else { + len = min(len, SC26198_TXFIFOSIZE); + portp->stats.txtotal += len; + stlen = min_t(unsigned int, len, + (portp->tx.buf + STL_TXBUFSIZE) - tail); + outb(GTXFIFO, (ioaddr + XP_ADDR)); + outsb((ioaddr + XP_DATA), tail, stlen); + len -= stlen; + tail += stlen; + if (tail >= (portp->tx.buf + STL_TXBUFSIZE)) + tail = portp->tx.buf; + if (len > 0) { + outsb((ioaddr + XP_DATA), tail, len); + tail += len; + } + portp->tx.tail = tail; + } +} + +/*****************************************************************************/ + +/* + * Receive character interrupt handler. Determine if we have good chars + * or bad chars and then process appropriately. Good chars are easy + * just shove the lot into the RX buffer and set all status byte to 0. + * If a bad RX char then process as required. This routine needs to be + * fast! In practice it is possible that we get an interrupt on a port + * that is closed. This can happen on hangups - since they completely + * shutdown a port not in user context. Need to handle this case. + */ + +static void stl_sc26198rxisr(struct stlport *portp, unsigned int iack) +{ + struct tty_struct *tty; + unsigned int len, buflen, ioaddr; + + pr_debug("stl_sc26198rxisr(portp=%p,iack=%x)\n", portp, iack); + + tty = tty_port_tty_get(&portp->port); + ioaddr = portp->ioaddr; + outb(GIBCR, (ioaddr + XP_ADDR)); + len = inb(ioaddr + XP_DATA) + 1; + + if ((iack & IVR_TYPEMASK) == IVR_RXDATA) { + if (tty == NULL || (buflen = tty_buffer_request_room(tty, len)) == 0) { + len = min_t(unsigned int, len, sizeof(stl_unwanted)); + outb(GRXFIFO, (ioaddr + XP_ADDR)); + insb((ioaddr + XP_DATA), &stl_unwanted[0], len); + portp->stats.rxlost += len; + portp->stats.rxtotal += len; + } else { + len = min(len, buflen); + if (len > 0) { + unsigned char *ptr; + outb(GRXFIFO, (ioaddr + XP_ADDR)); + tty_prepare_flip_string(tty, &ptr, len); + insb((ioaddr + XP_DATA), ptr, len); + tty_schedule_flip(tty); + portp->stats.rxtotal += len; + } + } + } else { + stl_sc26198rxbadchars(portp); + } + +/* + * If we are TX flow controlled and in IXANY mode then we may need + * to unflow control here. We gotta do this because of the automatic + * flow control modes of the sc26198. + */ + if (test_bit(ASYI_TXFLOWED, &portp->istate)) { + if ((tty != NULL) && + (tty->termios != NULL) && + (tty->termios->c_iflag & IXANY)) { + stl_sc26198txunflow(portp, tty); + } + } + tty_kref_put(tty); +} + +/*****************************************************************************/ + +/* + * Process an RX bad character. + */ + +static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char ch) +{ + struct tty_struct *tty; + unsigned int ioaddr; + + tty = tty_port_tty_get(&portp->port); + ioaddr = portp->ioaddr; + + if (status & SR_RXPARITY) + portp->stats.rxparity++; + if (status & SR_RXFRAMING) + portp->stats.rxframing++; + if (status & SR_RXOVERRUN) + portp->stats.rxoverrun++; + if (status & SR_RXBREAK) + portp->stats.rxbreaks++; + + if ((tty != NULL) && + ((portp->rxignoremsk & status) == 0)) { + if (portp->rxmarkmsk & status) { + if (status & SR_RXBREAK) { + status = TTY_BREAK; + if (portp->port.flags & ASYNC_SAK) { + do_SAK(tty); + BRDENABLE(portp->brdnr, portp->pagenr); + } + } else if (status & SR_RXPARITY) + status = TTY_PARITY; + else if (status & SR_RXFRAMING) + status = TTY_FRAME; + else if(status & SR_RXOVERRUN) + status = TTY_OVERRUN; + else + status = 0; + } else + status = 0; + + tty_insert_flip_char(tty, ch, status); + tty_schedule_flip(tty); + + if (status == 0) + portp->stats.rxtotal++; + } + tty_kref_put(tty); +} + +/*****************************************************************************/ + +/* + * Process all characters in the RX FIFO of the UART. Check all char + * status bytes as well, and process as required. We need to check + * all bytes in the FIFO, in case some more enter the FIFO while we + * are here. To get the exact character error type we need to switch + * into CHAR error mode (that is why we need to make sure we empty + * the FIFO). + */ + +static void stl_sc26198rxbadchars(struct stlport *portp) +{ + unsigned char status, mr1; + char ch; + +/* + * To get the precise error type for each character we must switch + * back into CHAR error mode. + */ + mr1 = stl_sc26198getreg(portp, MR1); + stl_sc26198setreg(portp, MR1, (mr1 & ~MR1_ERRBLOCK)); + + while ((status = stl_sc26198getreg(portp, SR)) & SR_RXRDY) { + stl_sc26198setreg(portp, SCCR, CR_CLEARRXERR); + ch = stl_sc26198getreg(portp, RXFIFO); + stl_sc26198rxbadch(portp, status, ch); + } + +/* + * To get correct interrupt class we must switch back into BLOCK + * error mode. + */ + stl_sc26198setreg(portp, MR1, mr1); +} + +/*****************************************************************************/ + +/* + * Other interrupt handler. This includes modem signals, flow + * control actions, etc. Most stuff is left to off-level interrupt + * processing time. + */ + +static void stl_sc26198otherisr(struct stlport *portp, unsigned int iack) +{ + unsigned char cir, ipr, xisr; + + pr_debug("stl_sc26198otherisr(portp=%p,iack=%x)\n", portp, iack); + + cir = stl_sc26198getglobreg(portp, CIR); + + switch (cir & CIR_SUBTYPEMASK) { + case CIR_SUBCOS: + ipr = stl_sc26198getreg(portp, IPR); + if (ipr & IPR_DCDCHANGE) { + stl_cd_change(portp); + portp->stats.modem++; + } + break; + case CIR_SUBXONXOFF: + xisr = stl_sc26198getreg(portp, XISR); + if (xisr & XISR_RXXONGOT) { + set_bit(ASYI_TXFLOWED, &portp->istate); + portp->stats.txxoff++; + } + if (xisr & XISR_RXXOFFGOT) { + clear_bit(ASYI_TXFLOWED, &portp->istate); + portp->stats.txxon++; + } + break; + case CIR_SUBBREAK: + stl_sc26198setreg(portp, SCCR, CR_BREAKRESET); + stl_sc26198rxbadchars(portp); + break; + default: + break; + } +} + +static void stl_free_isabrds(void) +{ + struct stlbrd *brdp; + unsigned int i; + + for (i = 0; i < stl_nrbrds; i++) { + if ((brdp = stl_brds[i]) == NULL || (brdp->state & STL_PROBED)) + continue; + + free_irq(brdp->irq, brdp); + + stl_cleanup_panels(brdp); + + release_region(brdp->ioaddr1, brdp->iosize1); + if (brdp->iosize2 > 0) + release_region(brdp->ioaddr2, brdp->iosize2); + + kfree(brdp); + stl_brds[i] = NULL; + } +} + +/* + * Loadable module initialization stuff. + */ +static int __init stallion_module_init(void) +{ + struct stlbrd *brdp; + struct stlconf conf; + unsigned int i, j; + int retval; + + printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion); + + spin_lock_init(&stallion_lock); + spin_lock_init(&brd_lock); + + stl_serial = alloc_tty_driver(STL_MAXBRDS * STL_MAXPORTS); + if (!stl_serial) { + retval = -ENOMEM; + goto err; + } + + stl_serial->owner = THIS_MODULE; + stl_serial->driver_name = stl_drvname; + stl_serial->name = "ttyE"; + stl_serial->major = STL_SERIALMAJOR; + stl_serial->minor_start = 0; + stl_serial->type = TTY_DRIVER_TYPE_SERIAL; + stl_serial->subtype = SERIAL_TYPE_NORMAL; + stl_serial->init_termios = stl_deftermios; + stl_serial->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(stl_serial, &stl_ops); + + retval = tty_register_driver(stl_serial); + if (retval) { + printk("STALLION: failed to register serial driver\n"); + goto err_frtty; + } + +/* + * Find any dynamically supported boards. That is via module load + * line options. + */ + for (i = stl_nrbrds; i < stl_nargs; i++) { + memset(&conf, 0, sizeof(conf)); + if (stl_parsebrd(&conf, stl_brdsp[i]) == 0) + continue; + if ((brdp = stl_allocbrd()) == NULL) + continue; + brdp->brdnr = i; + brdp->brdtype = conf.brdtype; + brdp->ioaddr1 = conf.ioaddr1; + brdp->ioaddr2 = conf.ioaddr2; + brdp->irq = conf.irq; + brdp->irqtype = conf.irqtype; + stl_brds[brdp->brdnr] = brdp; + if (stl_brdinit(brdp)) { + stl_brds[brdp->brdnr] = NULL; + kfree(brdp); + } else { + for (j = 0; j < brdp->nrports; j++) + tty_register_device(stl_serial, + brdp->brdnr * STL_MAXPORTS + j, NULL); + stl_nrbrds = i + 1; + } + } + + /* this has to be _after_ isa finding because of locking */ + retval = pci_register_driver(&stl_pcidriver); + if (retval && stl_nrbrds == 0) { + printk(KERN_ERR "STALLION: can't register pci driver\n"); + goto err_unrtty; + } + +/* + * Set up a character driver for per board stuff. This is mainly used + * to do stats ioctls on the ports. + */ + if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem)) + printk("STALLION: failed to register serial board device\n"); + + stallion_class = class_create(THIS_MODULE, "staliomem"); + if (IS_ERR(stallion_class)) + printk("STALLION: failed to create class\n"); + for (i = 0; i < 4; i++) + device_create(stallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i), + NULL, "staliomem%d", i); + + return 0; +err_unrtty: + tty_unregister_driver(stl_serial); +err_frtty: + put_tty_driver(stl_serial); +err: + return retval; +} + +static void __exit stallion_module_exit(void) +{ + struct stlbrd *brdp; + unsigned int i, j; + + pr_debug("cleanup_module()\n"); + + printk(KERN_INFO "Unloading %s: version %s\n", stl_drvtitle, + stl_drvversion); + +/* + * Free up all allocated resources used by the ports. This includes + * memory and interrupts. As part of this process we will also do + * a hangup on every open port - to try to flush out any processes + * hanging onto ports. + */ + for (i = 0; i < stl_nrbrds; i++) { + if ((brdp = stl_brds[i]) == NULL || (brdp->state & STL_PROBED)) + continue; + for (j = 0; j < brdp->nrports; j++) + tty_unregister_device(stl_serial, + brdp->brdnr * STL_MAXPORTS + j); + } + + for (i = 0; i < 4; i++) + device_destroy(stallion_class, MKDEV(STL_SIOMEMMAJOR, i)); + unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"); + class_destroy(stallion_class); + + pci_unregister_driver(&stl_pcidriver); + + stl_free_isabrds(); + + tty_unregister_driver(stl_serial); + put_tty_driver(stl_serial); +} + +module_init(stallion_module_init); +module_exit(stallion_module_exit); + +MODULE_AUTHOR("Greg Ungerer"); +MODULE_DESCRIPTION("Stallion Multiport Serial Driver"); +MODULE_LICENSE("GPL");