diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/epca.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/epca.c')
-rw-r--r-- | drivers/char/epca.c | 3789 |
1 files changed, 3789 insertions, 0 deletions
diff --git a/drivers/char/epca.c b/drivers/char/epca.c new file mode 100644 index 00000000000..6025e1866c7 --- /dev/null +++ b/drivers/char/epca.c @@ -0,0 +1,3789 @@ +/* + + + Copyright (C) 1996 Digi International. + + For technical support please email digiLinux@dgii.com or + call Digi tech support at (612) 912-3456 + + 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 <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <asm/uaccess.h> +#include <asm/io.h> + +#ifdef CONFIG_PCI +#define ENABLE_PCI +#endif /* CONFIG_PCI */ + +#define putUser(arg1, arg2) put_user(arg1, (unsigned long __user *)arg2) +#define getUser(arg1, arg2) get_user(arg1, (unsigned __user *)arg2) + +#ifdef ENABLE_PCI +#include <linux/pci.h> +#include "digiPCI.h" +#endif /* ENABLE_PCI */ + +#include "digi1.h" +#include "digiFep1.h" +#include "epca.h" +#include "epcaconfig.h" + +#if BITS_PER_LONG != 32 +# error FIXME: this driver only works on 32-bit platforms +#endif + +/* ---------------------- Begin defines ------------------------ */ + +#define VERSION "1.3.0.1-LK" + +/* 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: " + +/* ----------------- Begin global definitions ------------------- */ + +static char mesg[100]; +static int nbdevs, num_cards, liloconfig; +static int digi_poller_inhibited = 1 ; + +static int setup_error_code; +static int invalid_lilo_config; + +/* ----------------------------------------------------------------------- + MAXBOARDS is typically 12, but ISA and EISA cards are restricted to + 7 below. +--------------------------------------------------------------------------*/ +static struct board_info boards[MAXBOARDS]; + + +/* ------------- Begin structures used for driver registeration ---------- */ + +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 function prototypes --------------------- */ + +/* ---------------------------------------------------------------------- + Begin generic memory functions. These functions will be alias + (point at) more specific functions dependent on the board being + configured. +----------------------------------------------------------------------- */ + +static inline void memwinon(struct board_info *b, unsigned int win); +static inline void memwinoff(struct board_info *b, unsigned int win); +static inline void globalwinon(struct channel *ch); +static inline void rxwinon(struct channel *ch); +static inline void txwinon(struct channel *ch); +static inline void memoff(struct channel *ch); +static inline void assertgwinon(struct channel *ch); +static inline void assertmemoff(struct channel *ch); + +/* ---- Begin more 'specific' memory functions for cx_like products --- */ + +static inline void pcxem_memwinon(struct board_info *b, unsigned int win); +static inline void pcxem_memwinoff(struct board_info *b, unsigned int win); +static inline void pcxem_globalwinon(struct channel *ch); +static inline void pcxem_rxwinon(struct channel *ch); +static inline void pcxem_txwinon(struct channel *ch); +static inline void pcxem_memoff(struct channel *ch); + +/* ------ Begin more 'specific' memory functions for the pcxe ------- */ + +static inline void pcxe_memwinon(struct board_info *b, unsigned int win); +static inline void pcxe_memwinoff(struct board_info *b, unsigned int win); +static inline void pcxe_globalwinon(struct channel *ch); +static inline void pcxe_rxwinon(struct channel *ch); +static inline void pcxe_txwinon(struct channel *ch); +static inline 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 inline void pcxi_memwinon(struct board_info *b, unsigned int win); +static inline void pcxi_memwinoff(struct board_info *b, unsigned int win); +static inline void pcxi_globalwinon(struct channel *ch); +static inline void pcxi_rxwinon(struct channel *ch); +static inline void pcxi_txwinon(struct channel *ch); +static inline void pcxi_memoff(struct channel *ch); + +/* - Begin 'specific' do nothing memory functions needed for some cards - */ + +static inline void dummy_memwinon(struct board_info *b, unsigned int win); +static inline void dummy_memwinoff(struct board_info *b, unsigned int win); +static inline void dummy_globalwinon(struct channel *ch); +static inline void dummy_rxwinon(struct channel *ch); +static inline void dummy_txwinon(struct channel *ch); +static inline void dummy_memoff(struct channel *ch); +static inline void dummy_assertgwinon(struct channel *ch); +static inline void dummy_assertmemoff(struct channel *ch); + +/* ------------------- Begin declare functions ----------------------- */ + +static inline struct channel *verifyChannel(register struct tty_struct *); +static inline 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 *); +static void pc_hangup(struct tty_struct *); +static void pc_put_char(struct tty_struct *, unsigned char); +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 block_til_ready(struct tty_struct *, struct file *, + struct channel *); +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 *); +static int pc_ioctl(struct tty_struct *, struct file *, + unsigned int, unsigned long); +static int info_ioctl(struct tty_struct *, struct file *, + unsigned int, unsigned long); +static void pc_set_termios(struct tty_struct *, struct termios *); +static void do_softint(void *); +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 void digi_send_break(struct channel *ch, int msec); +static void setup_empty_event(struct tty_struct *tty, struct channel *ch); +void epca_setup(char *, int *); +void console_print(const char *); + +static int get_termio(struct tty_struct *, struct termio __user *); +static int pc_write(struct tty_struct *, const unsigned char *, int); +int pc_init(void); + +#ifdef ENABLE_PCI +static int init_PCI(void); +#endif /* ENABLE_PCI */ + + +/* ------------------------------------------------------------------ + 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 inline void memwinon(struct board_info *b, unsigned int win) +{ + (b->memwinon)(b, win); +} + +static inline void memwinoff(struct board_info *b, unsigned int win) +{ + (b->memwinoff)(b, win); +} + +static inline void globalwinon(struct channel *ch) +{ + (ch->board->globalwinon)(ch); +} + +static inline void rxwinon(struct channel *ch) +{ + (ch->board->rxwinon)(ch); +} + +static inline void txwinon(struct channel *ch) +{ + (ch->board->txwinon)(ch); +} + +static inline void memoff(struct channel *ch) +{ + (ch->board->memoff)(ch); +} +static inline void assertgwinon(struct channel *ch) +{ + (ch->board->assertgwinon)(ch); +} + +static inline 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 inline void pcxem_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(FEPWIN|win, (int)b->port + 1); +} + +static inline void pcxem_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(0, (int)b->port + 1); +} + +static inline void pcxem_globalwinon(struct channel *ch) +{ + outb_p( FEPWIN, (int)ch->board->port + 1); +} + +static inline void pcxem_rxwinon(struct channel *ch) +{ + outb_p(ch->rxwin, (int)ch->board->port + 1); +} + +static inline void pcxem_txwinon(struct channel *ch) +{ + outb_p(ch->txwin, (int)ch->board->port + 1); +} + +static inline void pcxem_memoff(struct channel *ch) +{ + outb_p(0, (int)ch->board->port + 1); +} + +/* ----------------- Begin pcxe memory window stuff ------------------ */ + +static inline void pcxe_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(FEPWIN | win, (int)b->port + 1); +} + +static inline void pcxe_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(inb((int)b->port) & ~FEPMEM, + (int)b->port + 1); + outb_p(0, (int)b->port + 1); +} + +static inline void pcxe_globalwinon(struct channel *ch) +{ + outb_p( FEPWIN, (int)ch->board->port + 1); +} + +static inline void pcxe_rxwinon(struct channel *ch) +{ + outb_p(ch->rxwin, (int)ch->board->port + 1); +} + +static inline void pcxe_txwinon(struct channel *ch) +{ + outb_p(ch->txwin, (int)ch->board->port + 1); +} + +static inline 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 inline void pcxi_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(inb((int)b->port) | FEPMEM, (int)b->port); +} + +static inline void pcxi_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(inb((int)b->port) & ~FEPMEM, (int)b->port); +} + +static inline void pcxi_globalwinon(struct channel *ch) +{ + outb_p(FEPMEM, (int)ch->board->port); +} + +static inline void pcxi_rxwinon(struct channel *ch) +{ + outb_p(FEPMEM, (int)ch->board->port); +} + +static inline void pcxi_txwinon(struct channel *ch) +{ + outb_p(FEPMEM, (int)ch->board->port); +} + +static inline void pcxi_memoff(struct channel *ch) +{ + outb_p(0, (int)ch->board->port); +} + +static inline void pcxi_assertgwinon(struct channel *ch) +{ + epcaassert(inb((int)ch->board->port) & FEPMEM, "Global memory off"); +} + +static inline void pcxi_assertmemoff(struct channel *ch) +{ + epcaassert(!(inb((int)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 inline void dummy_memwinon(struct board_info *b, unsigned int win) +{ +} + +static inline void dummy_memwinoff(struct board_info *b, unsigned int win) +{ +} + +static inline void dummy_globalwinon(struct channel *ch) +{ +} + +static inline void dummy_rxwinon(struct channel *ch) +{ +} + +static inline void dummy_txwinon(struct channel *ch) +{ +} + +static inline void dummy_memoff(struct channel *ch) +{ +} + +static inline void dummy_assertgwinon(struct channel *ch) +{ +} + +static inline void dummy_assertmemoff(struct channel *ch) +{ +} + +/* ----------------- Begin verifyChannel function ----------------------- */ +static inline struct channel *verifyChannel(register struct tty_struct *tty) +{ /* Begin verifyChannel */ + + /* -------------------------------------------------------------------- + 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) + { /* Begin if tty */ + + register struct channel *ch = (struct channel *)tty->driver_data; + + if ((ch >= &digi_channels[0]) && (ch < &digi_channels[nbdevs])) + { + if (ch->magic == EPCA_MAGIC) + return ch; + } + + } /* End if tty */ + + /* Else return a NULL for invalid */ + return NULL; + +} /* End verifyChannel */ + +/* ------------------ Begin pc_sched_event ------------------------- */ + +static inline void pc_sched_event(struct channel *ch, int event) +{ /* Begin pc_sched_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); + + +} /* End pc_sched_event */ + +/* ------------------ Begin epca_error ------------------------- */ + +static void epca_error(int line, char *msg) +{ /* Begin epca_error */ + + printk(KERN_ERR "epca_error (Digi): line = %d %s\n",line,msg); + return; + +} /* End epca_error */ + +/* ------------------ Begin pc_close ------------------------- */ +static void pc_close(struct tty_struct * tty, struct file * filp) +{ /* Begin pc_close */ + + struct channel *ch; + unsigned long flags; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if ch != NULL */ + + save_flags(flags); + cli(); + + if (tty_hung_up_p(filp)) + { + restore_flags(flags); + return; + } + + /* Check to see if the channel is open more than once */ + if (ch->count-- > 1) + { /* Begin channel is open more than once */ + + /* ------------------------------------------------------------- + Return without doing anything. Someone might still be using + the channel. + ---------------------------------------------------------------- */ + + restore_flags(flags); + return; + } /* End channel is open more than once */ + + /* Port open only once go ahead with shutdown & reset */ + + if (ch->count < 0) + { + ch->count = 0; + } + + /* --------------------------------------------------------------- + Let the rest of the driver know the channel is being closed. + This becomes important if an open is attempted before close + is finished. + ------------------------------------------------------------------ */ + + ch->asyncflags |= ASYNC_CLOSING; + + tty->closing = 1; + + if (ch->asyncflags & ASYNC_INITIALIZED) + { + /* Setup an event to indicate when the transmit buffer empties */ + setup_empty_event(tty, ch); + tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ + } + + if (tty->driver->flush_buffer) + tty->driver->flush_buffer(tty); + + tty_ldisc_flush(tty); + shutdown(ch); + tty->closing = 0; + ch->event = 0; + ch->tty = NULL; + + if (ch->blocked_open) + { /* Begin if blocked_open */ + + if (ch->close_delay) + { + msleep_interruptible(jiffies_to_msecs(ch->close_delay)); + } + + wake_up_interruptible(&ch->open_wait); + + } /* End if blocked_open */ + + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | + ASYNC_CLOSING); + wake_up_interruptible(&ch->close_wait); + + + restore_flags(flags); + + } /* End if ch != NULL */ + +} /* End pc_close */ + +/* ------------------ Begin shutdown ------------------------- */ + +static void shutdown(struct channel *ch) +{ /* Begin shutdown */ + + unsigned long flags; + struct tty_struct *tty; + volatile struct board_chan *bc; + + if (!(ch->asyncflags & ASYNC_INITIALIZED)) + return; + + save_flags(flags); + cli(); + 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) + bc->idata = 0; + + tty = ch->tty; + + /* ---------------------------------------------------------------- + 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 */ + + ch->asyncflags &= ~ASYNC_INITIALIZED; + restore_flags(flags); + +} /* End shutdown */ + +/* ------------------ Begin pc_hangup ------------------------- */ + +static void pc_hangup(struct tty_struct *tty) +{ /* Begin pc_hangup */ + + struct channel *ch; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if ch != NULL */ + + unsigned long flags; + + save_flags(flags); + cli(); + if (tty->driver->flush_buffer) + tty->driver->flush_buffer(tty); + tty_ldisc_flush(tty); + shutdown(ch); + + ch->tty = NULL; + ch->event = 0; + ch->count = 0; + restore_flags(flags); + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED); + wake_up_interruptible(&ch->open_wait); + + } /* End if ch != NULL */ + +} /* End pc_hangup */ + +/* ------------------ Begin pc_write ------------------------- */ + +static int pc_write(struct tty_struct * tty, + const unsigned char *buf, int bytesAvailable) +{ /* Begin pc_write */ + + register unsigned int head, tail; + register int dataLen; + register int size; + register int amountCopied; + + + struct channel *ch; + unsigned long flags; + int remain; + volatile struct board_chan *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. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) == NULL) + return 0; + + /* Make a pointer to the channel data structure found on the board. */ + + bc = ch->brdchan; + size = ch->txbufsize; + + amountCopied = 0; + save_flags(flags); + cli(); + + globalwinon(ch); + + head = bc->tin & (size - 1); + tail = bc->tout; + + if (tail != bc->tout) + tail = bc->tout; + tail &= (size - 1); + + /* If head >= tail, head has not wrapped around. */ + if (head >= tail) + { /* Begin 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; + + } /* End head has not wrapped */ + else + { /* Begin head has wrapped around */ + + remain = tail - head - 1; + dataLen = remain; + + } /* End head has wrapped around */ + + /* ------------------------------------------------------------------- + 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) + { /* Begin while 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(ch->txptr + head, buf, dataLen); + buf += dataLen; + head += dataLen; + amountCopied += dataLen; + bytesAvailable -= dataLen; + + if (head >= size) + { + head = 0; + dataLen = tail; + } + + } /* End while there is data to copy onto card */ + + ch->statusflags |= TXBUSY; + globalwinon(ch); + bc->tin = head; + + if ((ch->statusflags & LOWWAIT) == 0) + { + ch->statusflags |= LOWWAIT; + bc->ilow = 1; + } + memoff(ch); + restore_flags(flags); + + return(amountCopied); + +} /* End pc_write */ + +/* ------------------ Begin pc_put_char ------------------------- */ + +static void pc_put_char(struct tty_struct *tty, unsigned char c) +{ /* Begin pc_put_char */ + + + pc_write(tty, &c, 1); + return; + +} /* End pc_put_char */ + +/* ------------------ Begin pc_write_room ------------------------- */ + +static int pc_write_room(struct tty_struct *tty) +{ /* Begin pc_write_room */ + + int remain; + struct channel *ch; + unsigned long flags; + unsigned int head, tail; + volatile struct board_chan *bc; + + remain = 0; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { + save_flags(flags); + cli(); + globalwinon(ch); + + bc = ch->brdchan; + head = bc->tin & (ch->txbufsize - 1); + tail = bc->tout; + + if (tail != bc->tout) + tail = bc->tout; + /* Wrap tail if necessary */ + tail &= (ch->txbufsize - 1); + + if ((remain = tail - head - 1) < 0 ) + remain += ch->txbufsize; + + if (remain && (ch->statusflags & LOWWAIT) == 0) + { + ch->statusflags |= LOWWAIT; + bc->ilow = 1; + } + memoff(ch); + restore_flags(flags); + } + + /* Return how much room is left on card */ + return remain; + +} /* End pc_write_room */ + +/* ------------------ Begin pc_chars_in_buffer ---------------------- */ + +static int pc_chars_in_buffer(struct tty_struct *tty) +{ /* Begin pc_chars_in_buffer */ + + int chars; + unsigned int ctail, head, tail; + int remain; + unsigned long flags; + struct channel *ch; + volatile struct board_chan *bc; + + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) == NULL) + return(0); + + save_flags(flags); + cli(); + globalwinon(ch); + + bc = ch->brdchan; + tail = bc->tout; + head = bc->tin; + ctail = ch->mailbox->cout; + + if (tail == head && ch->mailbox->cin == ctail && bc->tbusy == 0) + chars = 0; + else + { /* Begin if some space on the card has been used */ + + head = 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. + ------------------------------------------------------------------- */ + + if ((remain = tail - head - 1) < 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); + restore_flags(flags); + + /* Return number of characters residing on card. */ + return(chars); + +} /* End pc_chars_in_buffer */ + +/* ------------------ Begin pc_flush_buffer ---------------------- */ + +static void pc_flush_buffer(struct tty_struct *tty) +{ /* Begin pc_flush_buffer */ + + unsigned int tail; + unsigned long flags; + struct channel *ch; + volatile struct board_chan *bc; + + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) == NULL) + return; + + save_flags(flags); + cli(); + + globalwinon(ch); + + bc = ch->brdchan; + tail = bc->tout; + + /* Have FEP move tout pointer; effectively flushing transmit buffer */ + + fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0); + + memoff(ch); + restore_flags(flags); + + wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); + +} /* End pc_flush_buffer */ + +/* ------------------ Begin pc_flush_chars ---------------------- */ + +static void pc_flush_chars(struct tty_struct *tty) +{ /* Begin pc_flush_chars */ + + struct channel * ch; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { + unsigned long flags; + + save_flags(flags); + cli(); + + /* ---------------------------------------------------------------- + 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); + + restore_flags(flags); + } + +} /* End pc_flush_chars */ + +/* ------------------ Begin block_til_ready ---------------------- */ + +static int block_til_ready(struct tty_struct *tty, + struct file *filp, struct channel *ch) +{ /* Begin block_til_ready */ + + DECLARE_WAITQUEUE(wait,current); + int retval, do_clocal = 0; + unsigned long flags; + + + if (tty_hung_up_p(filp)) + { + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + return(retval); + } + + /* ----------------------------------------------------------------- + If the device is in the middle of being closed, then block + until it's done, and then try again. + -------------------------------------------------------------------- */ + if (ch->asyncflags & ASYNC_CLOSING) + { + interruptible_sleep_on(&ch->close_wait); + + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + + if (filp->f_flags & O_NONBLOCK) + { + /* ----------------------------------------------------------------- + If non-blocking mode is set, then make the check up front + and then exit. + -------------------------------------------------------------------- */ + + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + + return 0; + } + + + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + + /* Block waiting for the carrier detect and the line to become free */ + + retval = 0; + add_wait_queue(&ch->open_wait, &wait); + save_flags(flags); + cli(); + + + /* We dec count so that pc_close will know when to free things */ + if (!tty_hung_up_p(filp)) + ch->count--; + + restore_flags(flags); + + ch->blocked_open++; + + while(1) + { /* Begin forever while */ + + set_current_state(TASK_INTERRUPTIBLE); + + if (tty_hung_up_p(filp) || + !(ch->asyncflags & ASYNC_INITIALIZED)) + { + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + + if (!(ch->asyncflags & ASYNC_CLOSING) && + (do_clocal || (ch->imodem & ch->dcd))) + break; + + if (signal_pending(current)) + { + retval = -ERESTARTSYS; + break; + } + + /* --------------------------------------------------------------- + Allow someone else to be scheduled. We will occasionally go + through this loop until one of the above conditions change. + The below schedule call will allow other processes to enter and + prevent this loop from hogging the cpu. + ------------------------------------------------------------------ */ + schedule(); + + } /* End forever while */ + + current->state = TASK_RUNNING; + remove_wait_queue(&ch->open_wait, &wait); + cli(); + if (!tty_hung_up_p(filp)) + ch->count++; + restore_flags(flags); + + ch->blocked_open--; + + if (retval) + return retval; + + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + + return 0; + +} /* End block_til_ready */ + +/* ------------------ Begin pc_open ---------------------- */ + +static int pc_open(struct tty_struct *tty, struct file * filp) +{ /* Begin pc_open */ + + struct channel *ch; + unsigned long flags; + int line, retval, boardnum;< |