From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from silver.osuosl.org (silver.osuosl.org [140.211.166.136]) by ash.osuosl.org (Postfix) with ESMTP id 8C0D71CE9AF for ; Wed, 19 Feb 2014 18:12:55 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id 792822506A for ; Wed, 19 Feb 2014 18:12:55 +0000 (UTC) Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id OkN9ZrqEX69Y for ; Wed, 19 Feb 2014 18:12:32 +0000 (UTC) Received: from mx2.compro.net (admin.comprocs.com [12.186.155.30]) by silver.osuosl.org (Postfix) with SMTP id 8C97932E54 for ; Wed, 19 Feb 2014 18:12:31 +0000 (UTC) From: Mark Hounschell Subject: [PATCH 03/19] staging: dgap: Merge dgap_tty.c into dgap_driver.c Date: Wed, 19 Feb 2014 13:11:59 -0500 Message-Id: <1392833535-25652-4-git-send-email-markh@compro.net> In-Reply-To: <1392833535-25652-1-git-send-email-markh@compro.net> References: <1392833535-25652-1-git-send-email-markh@compro.net> List-Id: Linux Driver Project Developer List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: driverdev-devel-bounces@linuxdriverproject.org Sender: driverdev-devel-bounces@linuxdriverproject.org To: driverdev-devel@linuxdriverproject.org Cc: Greg Kroah-Hartman There is a lot of cleanup work to do on these digi drivers and merging as much as is possible will make it easier. I also notice that many merged drivers are single source and header. Signed-off-by: Mark Hounschell Cc: Greg Kroah-Hartman --- drivers/staging/dgap/Makefile | 2 +- drivers/staging/dgap/dgap_driver.c | 3495 +++++++++++++++++++++++++++++++++++ drivers/staging/dgap/dgap_tty.c | 3555 ------------------------------------ 3 files changed, 3496 insertions(+), 3556 deletions(-) delete mode 100644 drivers/staging/dgap/dgap_tty.c diff --git a/drivers/staging/dgap/Makefile b/drivers/staging/dgap/Makefile index 3abe8d2..b80cad5 100644 --- a/drivers/staging/dgap/Makefile +++ b/drivers/staging/dgap/Makefile @@ -3,5 +3,5 @@ obj-$(CONFIG_DGAP) += dgap.o dgap-objs := dgap_driver.o dgap_fep5.o \ dgap_parse.o dgap_trace.o \ - dgap_tty.o dgap_sysfs.o + dgap_sysfs.o diff --git a/drivers/staging/dgap/dgap_driver.c b/drivers/staging/dgap/dgap_driver.c index 0f5fb12..b49f698 100644 --- a/drivers/staging/dgap/dgap_driver.c +++ b/drivers/staging/dgap/dgap_driver.c @@ -38,6 +38,13 @@ #include /* For copy_from_user/copy_to_user */ #include +#include /* For tasklet and interrupt structs/defines */ +#include +#include +#include +#include +#include /* For read[bwl]/write[bwl] */ + #include "dgap_driver.h" #include "dgap_pci.h" #include "dgap_fep5.h" @@ -46,6 +53,11 @@ #include "dgap_parse.h" #include "dgap_trace.h" #include "dgap_sysfs.h" +#include "dgap_types.h" + +#define init_MUTEX(sem) sema_init(sem, 1) +#define DECLARE_MUTEX(name) \ + struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Digi International, http://www.digi.com"); @@ -82,6 +94,38 @@ static void dgap_mbuf(struct board_t *brd, const char *fmt, ...); static int dgap_do_remap(struct board_t *brd); static irqreturn_t dgap_intr(int irq, void *voidbrd); +/* Our function prototypes */ +static int dgap_tty_open(struct tty_struct *tty, struct file *file); +static void dgap_tty_close(struct tty_struct *tty, struct file *file); +static int dgap_block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch); +static int dgap_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); +static int dgap_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo); +static int dgap_tty_digiseta(struct tty_struct *tty, struct digi_t __user *new_info); +static int dgap_tty_digigetedelay(struct tty_struct *tty, int __user *retinfo); +static int dgap_tty_digisetedelay(struct tty_struct *tty, int __user *new_info); +static int dgap_tty_write_room(struct tty_struct* tty); +static int dgap_tty_chars_in_buffer(struct tty_struct* tty); +static void dgap_tty_start(struct tty_struct *tty); +static void dgap_tty_stop(struct tty_struct *tty); +static void dgap_tty_throttle(struct tty_struct *tty); +static void dgap_tty_unthrottle(struct tty_struct *tty); +static void dgap_tty_flush_chars(struct tty_struct *tty); +static void dgap_tty_flush_buffer(struct tty_struct *tty); +static void dgap_tty_hangup(struct tty_struct *tty); +static int dgap_wait_for_drain(struct tty_struct *tty); +static int dgap_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int __user *value); +static int dgap_get_modem_info(struct channel_t *ch, unsigned int __user *value); +static int dgap_tty_digisetcustombaud(struct tty_struct *tty, int __user *new_info); +static int dgap_tty_digigetcustombaud(struct tty_struct *tty, int __user *retinfo); +static int dgap_tty_tiocmget(struct tty_struct *tty); +static int dgap_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int dgap_tty_send_break(struct tty_struct *tty, int msec); +static void dgap_tty_wait_until_sent(struct tty_struct *tty, int timeout); +static int dgap_tty_write(struct tty_struct *tty, const unsigned char *buf, int count); +static void dgap_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios); +static int dgap_tty_put_char(struct tty_struct *tty, unsigned char c); +static void dgap_tty_send_xchar(struct tty_struct *tty, char ch); + /* Driver load/unload functions */ int dgap_init_module(void); void dgap_cleanup_module(void); @@ -121,6 +165,10 @@ static uint dgap_driver_start = FALSE; static struct class * dgap_class; +static struct board_t *dgap_BoardsByMajor[256]; +static uchar *dgap_TmpWriteBuf = NULL; +static DECLARE_MUTEX(dgap_TmpWriteSem); + /* * Poller stuff */ @@ -220,6 +268,62 @@ char *dgap_driver_state_text[] = { "Driver Ready." }; +/* + * Default transparent print information. + */ +static struct digi_t dgap_digi_init = { + .digi_flags = DIGI_COOK, /* Flags */ + .digi_maxcps = 100, /* Max CPS */ + .digi_maxchar = 50, /* Max chars in print queue */ + .digi_bufsize = 100, /* Printer buffer size */ + .digi_onlen = 4, /* size of printer on string */ + .digi_offlen = 4, /* size of printer off string */ + .digi_onstr = "\033[5i", /* ANSI printer on string ] */ + .digi_offstr = "\033[4i", /* ANSI printer off string ] */ + .digi_term = "ansi" /* default terminal type */ +}; + + +/* + * Define a local default termios struct. All ports will be created + * with this termios initially. + * + * This defines a raw port at 9600 baud, 8 data bits, no parity, + * 1 stop bit. + */ + +static struct ktermios DgapDefaultTermios = +{ + .c_iflag = (DEFAULT_IFLAGS), /* iflags */ + .c_oflag = (DEFAULT_OFLAGS), /* oflags */ + .c_cflag = (DEFAULT_CFLAGS), /* cflags */ + .c_lflag = (DEFAULT_LFLAGS), /* lflags */ + .c_cc = INIT_C_CC, + .c_line = 0, +}; + +static const struct tty_operations dgap_tty_ops = { + .open = dgap_tty_open, + .close = dgap_tty_close, + .write = dgap_tty_write, + .write_room = dgap_tty_write_room, + .flush_buffer = dgap_tty_flush_buffer, + .chars_in_buffer = dgap_tty_chars_in_buffer, + .flush_chars = dgap_tty_flush_chars, + .ioctl = dgap_tty_ioctl, + .set_termios = dgap_tty_set_termios, + .stop = dgap_tty_stop, + .start = dgap_tty_start, + .throttle = dgap_tty_throttle, + .unthrottle = dgap_tty_unthrottle, + .hangup = dgap_tty_hangup, + .put_char = dgap_tty_put_char, + .tiocmget = dgap_tty_tiocmget, + .tiocmset = dgap_tty_tiocmset, + .break_ctl = dgap_tty_send_break, + .wait_until_sent = dgap_tty_wait_until_sent, + .send_xchar = dgap_tty_send_xchar +}; /************************************************************************ @@ -1027,3 +1131,3394 @@ char *dgap_ioctl_name(int cmd) default: return("unknown"); } } + +/************************************************************************ + * + * TTY Initialization/Cleanup Functions + * + ************************************************************************/ + +/* + * dgap_tty_preinit() + * + * Initialize any global tty related data before we download any boards. + */ +int dgap_tty_preinit(void) +{ + unsigned long flags; + + DGAP_LOCK(dgap_global_lock, flags); + + /* + * Allocate a buffer for doing the copy from user space to + * kernel space in dgap_input(). We only use one buffer and + * control access to it with a semaphore. If we are paging, we + * are already in trouble so one buffer won't hurt much anyway. + */ + dgap_TmpWriteBuf = kmalloc(WRITEBUFLEN, GFP_ATOMIC); + + if (!dgap_TmpWriteBuf) { + DGAP_UNLOCK(dgap_global_lock, flags); + DPR_INIT(("unable to allocate tmp write buf")); + return (-ENOMEM); + } + + DGAP_UNLOCK(dgap_global_lock, flags); + return(0); +} + + +/* + * dgap_tty_register() + * + * Init the tty subsystem for this board. + */ +int dgap_tty_register(struct board_t *brd) +{ + int rc = 0; + + DPR_INIT(("tty_register start")); + + brd->SerialDriver = alloc_tty_driver(MAXPORTS); + + snprintf(brd->SerialName, MAXTTYNAMELEN, "tty_dgap_%d_", brd->boardnum); + brd->SerialDriver->name = brd->SerialName; + brd->SerialDriver->name_base = 0; + brd->SerialDriver->major = 0; + brd->SerialDriver->minor_start = 0; + brd->SerialDriver->type = TTY_DRIVER_TYPE_SERIAL; + brd->SerialDriver->subtype = SERIAL_TYPE_NORMAL; + brd->SerialDriver->init_termios = DgapDefaultTermios; + brd->SerialDriver->driver_name = DRVSTR; + brd->SerialDriver->flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK); + + /* The kernel wants space to store pointers to tty_structs */ + brd->SerialDriver->ttys = kzalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); + if (!brd->SerialDriver->ttys) + return(-ENOMEM); + + /* + * Entry points for driver. Called by the kernel from + * tty_io.c and n_tty.c. + */ + tty_set_operations(brd->SerialDriver, &dgap_tty_ops); + + /* + * If we're doing transparent print, we have to do all of the above + * again, separately so we don't get the LD confused about what major + * we are when we get into the dgap_tty_open() routine. + */ + brd->PrintDriver = alloc_tty_driver(MAXPORTS); + + snprintf(brd->PrintName, MAXTTYNAMELEN, "pr_dgap_%d_", brd->boardnum); + brd->PrintDriver->name = brd->PrintName; + brd->PrintDriver->name_base = 0; + brd->PrintDriver->major = 0; + brd->PrintDriver->minor_start = 0; + brd->PrintDriver->type = TTY_DRIVER_TYPE_SERIAL; + brd->PrintDriver->subtype = SERIAL_TYPE_NORMAL; + brd->PrintDriver->init_termios = DgapDefaultTermios; + brd->PrintDriver->driver_name = DRVSTR; + brd->PrintDriver->flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK); + + /* The kernel wants space to store pointers to tty_structs */ + brd->PrintDriver->ttys = kzalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); + if (!brd->PrintDriver->ttys) + return(-ENOMEM); + + /* + * Entry points for driver. Called by the kernel from + * tty_io.c and n_tty.c. + */ + tty_set_operations(brd->PrintDriver, &dgap_tty_ops); + + if (!brd->dgap_Major_Serial_Registered) { + /* Register tty devices */ + rc = tty_register_driver(brd->SerialDriver); + if (rc < 0) { + APR(("Can't register tty device (%d)\n", rc)); + return(rc); + } + brd->dgap_Major_Serial_Registered = TRUE; + dgap_BoardsByMajor[brd->SerialDriver->major] = brd; + brd->dgap_Serial_Major = brd->SerialDriver->major; + } + + if (!brd->dgap_Major_TransparentPrint_Registered) { + /* Register Transparent Print devices */ + rc = tty_register_driver(brd->PrintDriver); + if (rc < 0) { + APR(("Can't register Transparent Print device (%d)\n", rc)); + return(rc); + } + brd->dgap_Major_TransparentPrint_Registered = TRUE; + dgap_BoardsByMajor[brd->PrintDriver->major] = brd; + brd->dgap_TransparentPrint_Major = brd->PrintDriver->major; + } + + DPR_INIT(("DGAP REGISTER TTY: MAJORS: %d %d\n", brd->SerialDriver->major, + brd->PrintDriver->major)); + + return (rc); +} + + +/* + * dgap_tty_init() + * + * Init the tty subsystem. Called once per board after board has been + * downloaded and init'ed. + */ +int dgap_tty_init(struct board_t *brd) +{ + int i; + int tlw; + uint true_count = 0; + uchar *vaddr; + uchar modem = 0; + struct channel_t *ch; + struct bs_t *bs; + struct cm_t *cm; + + if (!brd) + return (-ENXIO); + + DPR_INIT(("dgap_tty_init start\n")); + + /* + * Initialize board structure elements. + */ + + vaddr = brd->re_map_membase; + true_count = readw((vaddr + NCHAN)); + + brd->nasync = dgap_config_get_number_of_ports(brd); + + if (!brd->nasync) { + brd->nasync = brd->maxports; + } + + if (brd->nasync > brd->maxports) { + brd->nasync = brd->maxports; + } + + if (true_count != brd->nasync) { + if ((brd->type == PPCM) && (true_count == 64)) { + APR(("***WARNING**** %s configured for %d ports, has %d ports.\nPlease make SURE the EBI cable running from the card\nto each EM module is plugged into EBI IN!\n", + brd->name, brd->nasync, true_count)); + } + else if ((brd->type == PPCM) && (true_count == 0)) { + APR(("***WARNING**** %s configured for %d ports, has %d ports.\nPlease make SURE the EBI cable running from the card\nto each EM module is plugged into EBI IN!\n", + brd->name, brd->nasync, true_count)); + } + else { + APR(("***WARNING**** %s configured for %d ports, has %d ports.\n", + brd->name, brd->nasync, true_count)); + } + + brd->nasync = true_count; + + /* If no ports, don't bother going any further */ + if (!brd->nasync) { + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + return(-ENXIO); + } + } + + /* + * Allocate channel memory that might not have been allocated + * when the driver was first loaded. + */ + for (i = 0; i < brd->nasync; i++) { + if (!brd->channels[i]) { + brd->channels[i] = kzalloc(sizeof(struct channel_t), GFP_ATOMIC); + if (!brd->channels[i]) { + DPR_CORE(("%s:%d Unable to allocate memory for channel struct\n", + __FILE__, __LINE__)); + } + } + } + + ch = brd->channels[0]; + vaddr = brd->re_map_membase; + + bs = (struct bs_t *) ((ulong) vaddr + CHANBUF); + cm = (struct cm_t *) ((ulong) vaddr + CMDBUF); + + brd->bd_bs = bs; + + /* Set up channel variables */ + for (i = 0; i < brd->nasync; i++, ch = brd->channels[i], bs++) { + + if (!brd->channels[i]) + continue; + + DGAP_SPINLOCK_INIT(ch->ch_lock); + + /* Store all our magic numbers */ + ch->magic = DGAP_CHANNEL_MAGIC; + ch->ch_tun.magic = DGAP_UNIT_MAGIC; + ch->ch_tun.un_type = DGAP_SERIAL; + ch->ch_tun.un_ch = ch; + ch->ch_tun.un_dev = i; + + ch->ch_pun.magic = DGAP_UNIT_MAGIC; + ch->ch_pun.un_type = DGAP_PRINT; + ch->ch_pun.un_ch = ch; + ch->ch_pun.un_dev = i; + + ch->ch_vaddr = vaddr; + ch->ch_bs = bs; + ch->ch_cm = cm; + ch->ch_bd = brd; + ch->ch_portnum = i; + ch->ch_digi = dgap_digi_init; + + /* + * Set up digi dsr and dcd bits based on altpin flag. + */ + if (dgap_config_get_altpin(brd)) { + ch->ch_dsr = DM_CD; + ch->ch_cd = DM_DSR; + ch->ch_digi.digi_flags |= DIGI_ALTPIN; + } + else { + ch->ch_cd = DM_CD; + ch->ch_dsr = DM_DSR; + } + + ch->ch_taddr = vaddr + ((ch->ch_bs->tx_seg) << 4); + ch->ch_raddr = vaddr + ((ch->ch_bs->rx_seg) << 4); + ch->ch_tx_win = 0; + ch->ch_rx_win = 0; + ch->ch_tsize = readw(&(ch->ch_bs->tx_max)) + 1; + ch->ch_rsize = readw(&(ch->ch_bs->rx_max)) + 1; + ch->ch_tstart = 0; + ch->ch_rstart = 0; + + /* .25 second delay */ + ch->ch_close_delay = 250; + + /* + * Set queue water marks, interrupt mask, + * and general tty parameters. + */ + ch->ch_tlw = tlw = ch->ch_tsize >= 2000 ? ((ch->ch_tsize * 5) / 8) : ch->ch_tsize / 2; + + dgap_cmdw(ch, STLOW, tlw, 0); + + dgap_cmdw(ch, SRLOW, ch->ch_rsize / 2, 0); + + dgap_cmdw(ch, SRHIGH, 7 * ch->ch_rsize / 8, 0); + + ch->ch_mistat = readb(&(ch->ch_bs->m_stat)); + + init_waitqueue_head(&ch->ch_flags_wait); + init_waitqueue_head(&ch->ch_tun.un_flags_wait); + init_waitqueue_head(&ch->ch_pun.un_flags_wait); + init_waitqueue_head(&ch->ch_sniff_wait); + + /* Turn on all modem interrupts for now */ + modem = (DM_CD | DM_DSR | DM_CTS | DM_RI); + writeb(modem, &(ch->ch_bs->m_int)); + + /* + * Set edelay to 0 if interrupts are turned on, + * otherwise set edelay to the usual 100. + */ + if (brd->intr_used) + writew(0, &(ch->ch_bs->edelay)); + else + writew(100, &(ch->ch_bs->edelay)); + + writeb(1, &(ch->ch_bs->idata)); + } + + + DPR_INIT(("dgap_tty_init finish\n")); + + return (0); +} + + +/* + * dgap_tty_post_uninit() + * + * UnInitialize any global tty related data. + */ +void dgap_tty_post_uninit(void) +{ + kfree(dgap_TmpWriteBuf); + dgap_TmpWriteBuf = NULL; +} + + +/* + * dgap_tty_uninit() + * + * Uninitialize the TTY portion of this driver. Free all memory and + * resources. + */ +void dgap_tty_uninit(struct board_t *brd) +{ + int i = 0; + + if (brd->dgap_Major_Serial_Registered) { + dgap_BoardsByMajor[brd->SerialDriver->major] = NULL; + brd->dgap_Serial_Major = 0; + for (i = 0; i < brd->nasync; i++) { + dgap_remove_tty_sysfs(brd->channels[i]->ch_tun.un_sysfs); + tty_unregister_device(brd->SerialDriver, i); + } + tty_unregister_driver(brd->SerialDriver); + kfree(brd->SerialDriver->ttys); + brd->SerialDriver->ttys = NULL; + put_tty_driver(brd->SerialDriver); + brd->dgap_Major_Serial_Registered = FALSE; + } + + if (brd->dgap_Major_TransparentPrint_Registered) { + dgap_BoardsByMajor[brd->PrintDriver->major] = NULL; + brd->dgap_TransparentPrint_Major = 0; + for (i = 0; i < brd->nasync; i++) { + dgap_remove_tty_sysfs(brd->channels[i]->ch_pun.un_sysfs); + tty_unregister_device(brd->PrintDriver, i); + } + tty_unregister_driver(brd->PrintDriver); + kfree(brd->PrintDriver->ttys); + brd->PrintDriver->ttys = NULL; + put_tty_driver(brd->PrintDriver); + brd->dgap_Major_TransparentPrint_Registered = FALSE; + } +} + + +#define TMPBUFLEN (1024) + +/* + * dgap_sniff - Dump data out to the "sniff" buffer if the + * proc sniff file is opened... + */ +static void dgap_sniff_nowait_nolock(struct channel_t *ch, uchar *text, uchar *buf, int len) +{ + struct timeval tv; + int n; + int r; + int nbuf; + int i; + int tmpbuflen; + char tmpbuf[TMPBUFLEN]; + char *p = tmpbuf; + int too_much_data; + + /* Leave if sniff not open */ + if (!(ch->ch_sniff_flags & SNIFF_OPEN)) + return; + + do_gettimeofday(&tv); + + /* Create our header for data dump */ + p += sprintf(p, "<%ld %ld><%s><", tv.tv_sec, tv.tv_usec, text); + tmpbuflen = p - tmpbuf; + + do { + too_much_data = 0; + + for (i = 0; i < len && tmpbuflen < (TMPBUFLEN - 4); i++) { + p += sprintf(p, "%02x ", *buf); + buf++; + tmpbuflen = p - tmpbuf; + } + + if (tmpbuflen < (TMPBUFLEN - 4)) { + if (i > 0) + p += sprintf(p - 1, "%s\n", ">"); + else + p += sprintf(p, "%s\n", ">"); + } else { + too_much_data = 1; + len -= i; + } + + nbuf = strlen(tmpbuf); + p = tmpbuf; + + /* + * Loop while data remains. + */ + while (nbuf > 0 && ch->ch_sniff_buf) { + /* + * Determine the amount of available space left in the + * buffer. If there's none, wait until some appears. + */ + n = (ch->ch_sniff_out - ch->ch_sniff_in - 1) & SNIFF_MASK; + + /* + * If there is no space left to write to in our sniff buffer, + * we have no choice but to drop the data. + * We *cannot* sleep here waiting for space, because this + * function was probably called by the interrupt/timer routines! + */ + if (n == 0) { + return; + } + + /* + * Copy as much data as will fit. + */ + + if (n > nbuf) + n = nbuf; + + r = SNIFF_MAX - ch->ch_sniff_in; + + if (r <= n) { + memcpy(ch->ch_sniff_buf + ch->ch_sniff_in, p, r); + + n -= r; + ch->ch_sniff_in = 0; + p += r; + nbuf -= r; + } + + memcpy(ch->ch_sniff_buf + ch->ch_sniff_in, p, n); + + ch->ch_sniff_in += n; + p += n; + nbuf -= n; + + /* + * Wakeup any thread waiting for data + */ + if (ch->ch_sniff_flags & SNIFF_WAIT_DATA) { + ch->ch_sniff_flags &= ~SNIFF_WAIT_DATA; + wake_up_interruptible(&ch->ch_sniff_wait); + } + } + + /* + * If the user sent us too much data to push into our tmpbuf, + * we need to keep looping around on all the data. + */ + if (too_much_data) { + p = tmpbuf; + tmpbuflen = 0; + } + + } while (too_much_data); +} + + +/*======================================================================= + * + * dgap_input - Process received data. + * + * ch - Pointer to channel structure. + * + *=======================================================================*/ + +void dgap_input(struct channel_t *ch) +{ + struct board_t *bd; + struct bs_t *bs; + struct tty_struct *tp; + struct tty_ldisc *ld; + uint rmask; + uint head; + uint tail; + int data_len; + ulong lock_flags; + ulong lock_flags2; + int flip_len; + int len = 0; + int n = 0; + uchar *buf; + uchar tmpchar; + int s = 0; + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + tp = ch->ch_tun.un_tty; + + bs = ch->ch_bs; + if (!bs) { + return; + } + + bd = ch->ch_bd; + if(!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_READ(("dgap_input start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + /* + * Figure the number of characters in the buffer. + * Exit immediately if none. + */ + + rmask = ch->ch_rsize - 1; + + head = readw(&(bs->rx_head)); + head &= rmask; + tail = readw(&(bs->rx_tail)); + tail &= rmask; + + data_len = (head - tail) & rmask; + + if (data_len == 0) { + writeb(1, &(bs->idata)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + DPR_READ(("No data on port %d\n", ch->ch_portnum)); + return; + } + + /* + * If the device is not open, or CREAD is off, flush + * input data and return immediately. + */ + if ((bd->state != BOARD_READY) || !tp || (tp->magic != TTY_MAGIC) || + !(ch->ch_tun.un_flags & UN_ISOPEN) || !(tp->termios.c_cflag & CREAD) || + (ch->ch_tun.un_flags & UN_CLOSING)) { + + DPR_READ(("input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum)); + DPR_READ(("input. tp: %p tp->magic: %x MAGIC:%x ch flags: %x\n", + tp, tp ? tp->magic : 0, TTY_MAGIC, ch->ch_tun.un_flags)); + writew(head, &(bs->rx_tail)); + writeb(1, &(bs->idata)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return; + } + + /* + * If we are throttled, simply don't read any data. + */ + if (ch->ch_flags & CH_RXBLOCK) { + writeb(1, &(bs->idata)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + DPR_READ(("Port %d throttled, not reading any data. head: %x tail: %x\n", + ch->ch_portnum, head, tail)); + return; + } + + /* + * Ignore oruns. + */ + tmpchar = readb(&(bs->orun)); + if (tmpchar) { + ch->ch_err_overrun++; + writeb(0, &(bs->orun)); + } + + DPR_READ(("dgap_input start 2\n")); + + /* Decide how much data we can send into the tty layer */ + flip_len = TTY_FLIPBUF_SIZE; + + /* Chop down the length, if needed */ + len = min(data_len, flip_len); + len = min(len, (N_TTY_BUF_SIZE - 1)); + + ld = tty_ldisc_ref(tp); + +#ifdef TTY_DONT_FLIP + /* + * If the DONT_FLIP flag is on, don't flush our buffer, and act + * like the ld doesn't have any space to put the data right now. + */ + if (test_bit(TTY_DONT_FLIP, &tp->flags)) + len = 0; +#endif + + /* + * If we were unable to get a reference to the ld, + * don't flush our buffer, and act like the ld doesn't + * have any space to put the data right now. + */ + if (!ld) { + len = 0; + } else { + /* + * If ld doesn't have a pointer to a receive_buf function, + * flush the data, then act like the ld doesn't have any + * space to put the data right now. + */ + if (!ld->ops->receive_buf) { + writew(head, &(bs->rx_tail)); + len = 0; + } + } + + if (len <= 0) { + writeb(1, &(bs->idata)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + DPR_READ(("dgap_input 1 - finish\n")); + if (ld) + tty_ldisc_deref(ld); + return; + } + + buf = ch->ch_bd->flipbuf; + n = len; + + /* + * n now contains the most amount of data we can copy, + * bounded either by our buffer size or the amount + * of data the card actually has pending... + */ + while (n) { + + s = ((head >= tail) ? head : ch->ch_rsize) - tail; + s = min(s, n); + + if (s <= 0) + break; + + memcpy_fromio(buf, (char *) ch->ch_raddr + tail, s); + dgap_sniff_nowait_nolock(ch, "USER READ", buf, s); + + tail += s; + buf += s; + + n -= s; + /* Flip queue if needed */ + tail &= rmask; + } + + writew(tail, &(bs->rx_tail)); + writeb(1, &(bs->idata)); + ch->ch_rxcount += len; + + /* + * If we are completely raw, we don't need to go through a lot + * of the tty layers that exist. + * In this case, we take the shortest and fastest route we + * can to relay the data to the user. + * + * On the other hand, if we are not raw, we need to go through + * the tty layer, which has its API more well defined. + */ + if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { + dgap_parity_scan(ch, ch->ch_bd->flipbuf, ch->ch_bd->flipflagbuf, &len); + + len = tty_buffer_request_room(tp->port, len); + tty_insert_flip_string_flags(tp->port, ch->ch_bd->flipbuf, + ch->ch_bd->flipflagbuf, len); + } + else { + len = tty_buffer_request_room(tp->port, len); + tty_insert_flip_string(tp->port, ch->ch_bd->flipbuf, len); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + /* Tell the tty layer its okay to "eat" the data now */ + tty_flip_buffer_push(tp->port); + + if (ld) + tty_ldisc_deref(ld); + + DPR_READ(("dgap_input - finish\n")); +} + + +/************************************************************************ + * Determines when CARRIER changes state and takes appropriate + * action. + ************************************************************************/ +void dgap_carrier(struct channel_t *ch) +{ + struct board_t *bd; + + int virt_carrier = 0; + int phys_carrier = 0; + + DPR_CARR(("dgap_carrier called...\n")); + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + /* Make sure altpin is always set correctly */ + if (ch->ch_digi.digi_flags & DIGI_ALTPIN) { + ch->ch_dsr = DM_CD; + ch->ch_cd = DM_DSR; + } + else { + ch->ch_dsr = DM_DSR; + ch->ch_cd = DM_CD; + } + + if (ch->ch_mistat & D_CD(ch)) { + DPR_CARR(("mistat: %x D_CD: %x\n", ch->ch_mistat, D_CD(ch))); + phys_carrier = 1; + } + + if (ch->ch_digi.digi_flags & DIGI_FORCEDCD) { + virt_carrier = 1; + } + + if (ch->ch_c_cflag & CLOCAL) { + virt_carrier = 1; + } + + + DPR_CARR(("DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier)); + + /* + * Test for a VIRTUAL carrier transition to HIGH. + */ + if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { + + /* + * When carrier rises, wake any threads waiting + * for carrier in the open routine. + */ + + DPR_CARR(("carrier: virt DCD rose\n")); + + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + } + + /* + * Test for a PHYSICAL carrier transition to HIGH. + */ + if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { + + /* + * When carrier rises, wake any threads waiting + * for carrier in the open routine. + */ + + DPR_CARR(("carrier: physical DCD rose\n")); + + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + } + + /* + * Test for a PHYSICAL transition to low, so long as we aren't + * currently ignoring physical transitions (which is what "virtual + * carrier" indicates). + * + * The transition of the virtual carrier to low really doesn't + * matter... it really only means "ignore carrier state", not + * "make pretend that carrier is there". + */ + if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) && + (phys_carrier == 0)) + { + + /* + * When carrier drops: + * + * Drop carrier on all open units. + * + * Flush queues, waking up any task waiting in the + * line discipline. + * + * Send a hangup to the control terminal. + * + * Enable all select calls. + */ + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + + if (ch->ch_tun.un_open_count > 0) { + DPR_CARR(("Sending tty hangup\n")); + tty_hangup(ch->ch_tun.un_tty); + } + + if (ch->ch_pun.un_open_count > 0) { + DPR_CARR(("Sending pr hangup\n")); + tty_hangup(ch->ch_pun.un_tty); + } + } + + /* + * Make sure that our cached values reflect the current reality. + */ + if (virt_carrier == 1) + ch->ch_flags |= CH_FCAR; + else + ch->ch_flags &= ~CH_FCAR; + + if (phys_carrier == 1) + ch->ch_flags |= CH_CD; + else + ch->ch_flags &= ~CH_CD; +} + + +/************************************************************************ + * + * TTY Entry points and helper functions + * + ************************************************************************/ + +/* + * dgap_tty_open() + * + */ +static int dgap_tty_open(struct tty_struct *tty, struct file *file) +{ + struct board_t *brd; + struct channel_t *ch; + struct un_t *un; + struct bs_t *bs; + uint major = 0; + uint minor = 0; + int rc = 0; + ulong lock_flags; + ulong lock_flags2; + u16 head; + + rc = 0; + + major = MAJOR(tty_devnum(tty)); + minor = MINOR(tty_devnum(tty)); + + if (major > 255) { + return -ENXIO; + } + + /* Get board pointer from our array of majors we have allocated */ + brd = dgap_BoardsByMajor[major]; + if (!brd) { + return -ENXIO; + } + + /* + * If board is not yet up to a state of READY, go to + * sleep waiting for it to happen or they cancel the open. + */ + rc = wait_event_interruptible(brd->state_wait, + (brd->state & BOARD_READY)); + + if (rc) { + return rc; + } + + DGAP_LOCK(brd->bd_lock, lock_flags); + + /* The wait above should guarantee this cannot happen */ + if (brd->state != BOARD_READY) { + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return -ENXIO; + } + + /* If opened device is greater than our number of ports, bail. */ + if (MINOR(tty_devnum(tty)) > brd->nasync) { + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return -ENXIO; + } + + ch = brd->channels[minor]; + if (!ch) { + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return -ENXIO; + } + + /* Grab channel lock */ + DGAP_LOCK(ch->ch_lock, lock_flags2); + + /* Figure out our type */ + if (major == brd->dgap_Serial_Major) { + un = &brd->channels[minor]->ch_tun; + un->un_type = DGAP_SERIAL; + } + else if (major == brd->dgap_TransparentPrint_Major) { + un = &brd->channels[minor]->ch_pun; + un->un_type = DGAP_PRINT; + } + else { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(brd->bd_lock, lock_flags); + DPR_OPEN(("%d Unknown TYPE!\n", __LINE__)); + return -ENXIO; + } + + /* Store our unit into driver_data, so we always have it available. */ + tty->driver_data = un; + + DPR_OPEN(("Open called. MAJOR: %d MINOR:%d unit: %p NAME: %s\n", + MAJOR(tty_devnum(tty)), MINOR(tty_devnum(tty)), un, brd->name)); + + /* + * Error if channel info pointer is NULL. + */ + bs = ch->ch_bs; + if (!bs) { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(brd->bd_lock, lock_flags); + DPR_OPEN(("%d BS is 0!\n", __LINE__)); + return -ENXIO; + } + + DPR_OPEN(("%d: tflag=%x pflag=%x\n", __LINE__, ch->ch_tun.un_flags, ch->ch_pun.un_flags)); + + /* + * Initialize tty's + */ + if (!(un->un_flags & UN_ISOPEN)) { + /* Store important variables. */ + un->un_tty = tty; + + /* Maybe do something here to the TTY struct as well? */ + } + + /* + * Initialize if neither terminal or printer is open. + */ + if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) { + + DPR_OPEN(("dgap_open: initializing channel in open...\n")); + + ch->ch_mforce = 0; + ch->ch_mval = 0; + + /* + * Flush input queue. + */ + head = readw(&(bs->rx_head)); + writew(head, &(bs->rx_tail)); + + ch->ch_flags = 0; + ch->pscan_state = 0; + ch->pscan_savechar = 0; + + ch->ch_c_cflag = tty->termios.c_cflag; + ch->ch_c_iflag = tty->termios.c_iflag; + ch->ch_c_oflag = tty->termios.c_oflag; + ch->ch_c_lflag = tty->termios.c_lflag; + ch->ch_startc = tty->termios.c_cc[VSTART]; + ch->ch_stopc = tty->termios.c_cc[VSTOP]; + + /* TODO: flush our TTY struct here? */ + } + + dgap_carrier(ch); + /* + * Run param in case we changed anything + */ + dgap_param(tty); + + /* + * follow protocol for opening port + */ + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(brd->bd_lock, lock_flags); + + rc = dgap_block_til_ready(tty, file, ch); + + if (!un->un_tty) { + return -ENODEV; + } + + if (rc) { + DPR_OPEN(("dgap_tty_open returning after dgap_block_til_ready " + "with %d\n", rc)); + } + + /* No going back now, increment our unit and channel counters */ + DGAP_LOCK(ch->ch_lock, lock_flags); + ch->ch_open_count++; + un->un_open_count++; + un->un_flags |= (UN_ISOPEN); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_OPEN(("dgap_tty_open finished\n")); + return (rc); +} + + +/* + * dgap_block_til_ready() + * + * Wait for DCD, if needed. + */ +static int dgap_block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch) +{ + int retval = 0; + struct un_t *un = NULL; + ulong lock_flags; + uint old_flags = 0; + int sleep_on_un_flags = 0; + + if (!tty || tty->magic != TTY_MAGIC || !file || !ch || ch->magic != DGAP_CHANNEL_MAGIC) { + return (-ENXIO); + } + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) { + return (-ENXIO); + } + + DPR_OPEN(("dgap_block_til_ready - before block.\n")); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + ch->ch_wopen++; + + /* Loop forever */ + while (1) { + + sleep_on_un_flags = 0; + + /* + * If board has failed somehow during our sleep, bail with error. + */ + if (ch->ch_bd->state == BOARD_FAILED) { + retval = -ENXIO; + break; + } + + /* If tty was hung up, break out of loop and set error. */ + if (tty_hung_up_p(file)) { + retval = -EAGAIN; + break; + } + + /* + * If either unit is in the middle of the fragile part of close, + * we just cannot touch the channel safely. + * Go back to sleep, knowing that when the channel can be + * touched safely, the close routine will signal the + * ch_wait_flags to wake us back up. + */ + if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_CLOSING)) { + + /* + * Our conditions to leave cleanly and happily: + * 1) NONBLOCKING on the tty is set. + * 2) CLOCAL is set. + * 3) DCD (fake or real) is active. + */ + + if (file->f_flags & O_NONBLOCK) { + break; + } + + if (tty->flags & (1 << TTY_IO_ERROR)) { + break; + } + + if (ch->ch_flags & CH_CD) { + DPR_OPEN(("%d: ch_flags: %x\n", __LINE__, ch->ch_flags)); + break; + } + + if (ch->ch_flags & CH_FCAR) { + DPR_OPEN(("%d: ch_flags: %x\n", __LINE__, ch->ch_flags)); + break; + } + } + else { + sleep_on_un_flags = 1; + } + + /* + * If there is a signal pending, the user probably + * interrupted (ctrl-c) us. + * Leave loop with error set. + */ + if (signal_pending(current)) { + DPR_OPEN(("%d: signal pending...\n", __LINE__)); + retval = -ERESTARTSYS; + break; + } + + DPR_OPEN(("dgap_block_til_ready - blocking.\n")); + + /* + * Store the flags before we let go of channel lock + */ + if (sleep_on_un_flags) + old_flags = ch->ch_tun.un_flags | ch->ch_pun.un_flags; + else + old_flags = ch->ch_flags; + + /* + * Let go of channel lock before calling schedule. + * Our poller will get any FEP events and wake us up when DCD + * eventually goes active. + */ + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_OPEN(("Going to sleep on %s flags...\n", + (sleep_on_un_flags ? "un" : "ch"))); + + /* + * Wait for something in the flags to change from the current value. + */ + if (sleep_on_un_flags) { + retval = wait_event_interruptible(un->un_flags_wait, + (old_flags != (ch->ch_tun.un_flags | ch->ch_pun.un_flags))); + } + else { + retval = wait_event_interruptible(ch->ch_flags_wait, + (old_flags != ch->ch_flags)); + } + + DPR_OPEN(("After sleep... retval: %x\n", retval)); + + /* + * We got woken up for some reason. + * Before looping around, grab our channel lock. + */ + DGAP_LOCK(ch->ch_lock, lock_flags); + } + + ch->ch_wopen--; + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_OPEN(("dgap_block_til_ready - after blocking.\n")); + + if (retval) { + DPR_OPEN(("dgap_block_til_ready - done. error. retval: %x\n", retval)); + return(retval); + } + + DPR_OPEN(("dgap_block_til_ready - done no error. jiffies: %lu\n", jiffies)); + + return(0); +} + + +/* + * dgap_tty_hangup() + * + * Hangup the port. Like a close, but don't wait for output to drain. + */ +static void dgap_tty_hangup(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_CLOSE(("dgap_hangup called. ch->ch_open_count: %d un->un_open_count: %d\n", + ch->ch_open_count, un->un_open_count)); + + /* flush the transmit queues */ + dgap_tty_flush_buffer(tty); + + DPR_CLOSE(("dgap_hangup finished. ch->ch_open_count: %d un->un_open_count: %d\n", + ch->ch_open_count, un->un_open_count)); +} + + + +/* + * dgap_tty_close() + * + */ +static void dgap_tty_close(struct tty_struct *tty, struct file *file) +{ + struct ktermios *ts; + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + int rc = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + ts = &tty->termios; + + DPR_CLOSE(("Close called\n")); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + /* + * Determine if this is the last close or not - and if we agree about + * which type of close it is with the Line Discipline + */ + if ((tty->count == 1) && (un->un_open_count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. un_open_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. + */ + APR(("tty->count is 1, un open count is %d\n", un->un_open_count)); + un->un_open_count = 1; + } + + if (--un->un_open_count < 0) { + APR(("bad serial port open count of %d\n", un->un_open_count)); + un->un_open_count = 0; + } + + ch->ch_open_count--; + + if (ch->ch_open_count && un->un_open_count) { + DPR_CLOSE(("dgap_tty_close: not last close ch: %d un:%d\n", + ch->ch_open_count, un->un_open_count)); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + return; + } + + /* OK, its the last close on the unit */ + DPR_CLOSE(("dgap_tty_close - last close on unit procedures\n")); + + un->un_flags |= UN_CLOSING; + + tty->closing = 1; + + /* + * Only officially close channel if count is 0 and + * DIGI_PRINTER bit is not set. + */ + if ((ch->ch_open_count == 0) && !(ch->ch_digi.digi_flags & DIGI_PRINTER)) { + + ch->ch_flags &= ~(CH_RXBLOCK); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + /* wait for output to drain */ + /* This will also return if we take an interrupt */ + + DPR_CLOSE(("Calling wait_for_drain\n")); + rc = dgap_wait_for_drain(tty); + DPR_CLOSE(("After calling wait_for_drain\n")); + + if (rc) { + DPR_BASIC(("dgap_tty_close - bad return: %d ", rc)); + } + + dgap_tty_flush_buffer(tty); + tty_ldisc_flush(tty); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + tty->closing = 0; + + /* + * If we have HUPCL set, lower DTR and RTS + */ + if (ch->ch_c_cflag & HUPCL ) { + DPR_CLOSE(("Close. HUPCL set, dropping DTR/RTS\n")); + ch->ch_mostat &= ~(D_RTS(ch)|D_DTR(ch)); + dgap_cmdb( ch, SMODEM, 0, D_DTR(ch)|D_RTS(ch), 0 ); + + /* + * Go to sleep to ensure RTS/DTR + * have been dropped for modems to see it. + */ + if (ch->ch_close_delay) { + DPR_CLOSE(("Close. Sleeping for RTS/DTR drop\n")); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + dgap_ms_sleep(ch->ch_close_delay); + DGAP_LOCK(ch->ch_lock, lock_flags); + + DPR_CLOSE(("Close. After sleeping for RTS/DTR drop\n")); + } + } + + ch->pscan_state = 0; + ch->pscan_savechar = 0; + ch->ch_baud_info = 0; + + } + + /* + * turn off print device when closing print device. + */ + if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON) ) { + dgap_wmove(ch, ch->ch_digi.digi_offstr, + (int) ch->ch_digi.digi_offlen); + ch->ch_flags &= ~CH_PRON; + } + + un->un_tty = NULL; + un->un_flags &= ~(UN_ISOPEN | UN_CLOSING); + tty->driver_data = NULL; + + DPR_CLOSE(("Close. Doing wakeups\n")); + wake_up_interruptible(&ch->ch_flags_wait); + wake_up_interruptible(&un->un_flags_wait); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_BASIC(("dgap_tty_close - complete\n")); +} + + +/* + * dgap_tty_chars_in_buffer() + * + * Return number of characters that have not been transmitted yet. + * + * This routine is used by the line discipline to determine if there + * is data waiting to be transmitted/drained/flushed or not. + */ +static int dgap_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct board_t *bd = NULL; + struct channel_t *ch = NULL; + struct un_t *un = NULL; + struct bs_t *bs = NULL; + uchar tbusy; + uint chars = 0; + u16 thead, ttail, tmask, chead, ctail; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + + if (tty == NULL) + return(0); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (0); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (0); + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (0); + + bs = ch->ch_bs; + if (!bs) + return (0); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + tmask = (ch->ch_tsize - 1); + + /* Get Transmit queue pointers */ + thead = readw(&(bs->tx_head)) & tmask; + ttail = readw(&(bs->tx_tail)) & tmask; + + /* Get tbusy flag */ + tbusy = readb(&(bs->tbusy)); + + /* Get Command queue pointers */ + chead = readw(&(ch->ch_cm->cm_head)); + ctail = readw(&(ch->ch_cm->cm_tail)); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + /* + * The only way we know for sure if there is no pending + * data left to be transferred, is if: + * 1) Transmit head and tail are equal (empty). + * 2) Command queue head and tail are equal (empty). + * 3) The "TBUSY" flag is 0. (Transmitter not busy). + */ + + if ((ttail == thead) && (tbusy == 0) && (chead == ctail)) { + chars = 0; + } + else { + if (thead >= ttail) + chars = thead - ttail; + else + chars = thead - ttail + ch->ch_tsize; + /* + * Fudge factor here. + * If chars is zero, we know that the command queue had + * something in it or tbusy was set. Because we cannot + * be sure if there is still some data to be transmitted, + * lets lie, and tell ld we have 1 byte left. + */ + if (chars == 0) { + /* + * If TBUSY is still set, and our tx buffers are empty, + * force the firmware to send me another wakeup after + * TBUSY has been cleared. + */ + if (tbusy != 0) { + DGAP_LOCK(ch->ch_lock, lock_flags); + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + } + chars = 1; + } + } + + DPR_WRITE(("dgap_tty_chars_in_buffer. Port: %x - %d (head: %d tail: %d tsize: %d)\n", + ch->ch_portnum, chars, thead, ttail, ch->ch_tsize)); + return(chars); +} + + +static int dgap_wait_for_drain(struct tty_struct *tty) +{ + struct channel_t *ch; + struct un_t *un; + struct bs_t *bs; + int ret = -EIO; + uint count = 1; + ulong lock_flags = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return ret; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return ret; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return ret; + + bs = ch->ch_bs; + if (!bs) + return ret; + + ret = 0; + + DPR_DRAIN(("dgap_wait_for_drain start\n")); + + /* Loop until data is drained */ + while (count != 0) { + + count = dgap_tty_chars_in_buffer(tty); + + if (count == 0) + break; + + /* Set flag waiting for drain */ + DGAP_LOCK(ch->ch_lock, lock_flags); + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + /* Go to sleep till we get woken up */ + ret = wait_event_interruptible(un->un_flags_wait, ((un->un_flags & UN_EMPTY) == 0)); + /* If ret is non-zero, user ctrl-c'ed us */ + if (ret) { + break; + } + } + + DGAP_LOCK(ch->ch_lock, lock_flags); + un->un_flags &= ~(UN_EMPTY); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_DRAIN(("dgap_wait_for_drain finish\n")); + return (ret); +} + + +/* + * dgap_maxcps_room + * + * Reduces bytes_available to the max number of characters + * that can be sent currently given the maxcps value, and + * returns the new bytes_available. This only affects printer + * output. + */ +static int dgap_maxcps_room(struct tty_struct *tty, int bytes_available) +{ + struct channel_t *ch = NULL; + struct un_t *un = NULL; + + if (tty == NULL) + return (bytes_available); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (bytes_available); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (bytes_available); + + /* + * If its not the Transparent print device, return + * the full data amount. + */ + if (un->un_type != DGAP_PRINT) + return (bytes_available); + + if (ch->ch_digi.digi_maxcps > 0 && ch->ch_digi.digi_bufsize > 0 ) { + int cps_limit = 0; + unsigned long current_time = jiffies; + unsigned long buffer_time = current_time + + (HZ * ch->ch_digi.digi_bufsize) / ch->ch_digi.digi_maxcps; + + if (ch->ch_cpstime < current_time) { + /* buffer is empty */ + ch->ch_cpstime = current_time; /* reset ch_cpstime */ + cps_limit = ch->ch_digi.digi_bufsize; + } + else if (ch->ch_cpstime < buffer_time) { + /* still room in the buffer */ + cps_limit = ((buffer_time - ch->ch_cpstime) * ch->ch_digi.digi_maxcps) / HZ; + } + else { + /* no room in the buffer */ + cps_limit = 0; + } + + bytes_available = min(cps_limit, bytes_available); + } + + return (bytes_available); +} + + +static inline void dgap_set_firmware_event(struct un_t *un, unsigned int event) +{ + struct channel_t *ch = NULL; + struct bs_t *bs = NULL; + + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + bs = ch->ch_bs; + if (!bs) + return; + + if ((event & UN_LOW) != 0) { + if ((un->un_flags & UN_LOW) == 0) { + un->un_flags |= UN_LOW; + writeb(1, &(bs->ilow)); + } + } + if ((event & UN_LOW) != 0) { + if ((un->un_flags & UN_EMPTY) == 0) { + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + } + } +} + + +/* + * dgap_tty_write_room() + * + * Return space available in Tx buffer + */ +static int dgap_tty_write_room(struct tty_struct *tty) +{ + struct channel_t *ch = NULL; + struct un_t *un = NULL; + struct bs_t *bs = NULL; + u16 head, tail, tmask; + int ret = 0; + ulong lock_flags = 0; + + if (tty == NULL || dgap_TmpWriteBuf == NULL) + return(0); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (0); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (0); + + bs = ch->ch_bs; + if (!bs) + return (0); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + tmask = ch->ch_tsize - 1; + head = readw(&(bs->tx_head)) & tmask; + tail = readw(&(bs->tx_tail)) & tmask; + + if ((ret = tail - head - 1) < 0) + ret += ch->ch_tsize; + + /* Limit printer to maxcps */ + ret = dgap_maxcps_room(tty, ret); + + /* + * If we are printer device, leave space for + * possibly both the on and off strings. + */ + if (un->un_type == DGAP_PRINT) { + if (!(ch->ch_flags & CH_PRON)) + ret -= ch->ch_digi.digi_onlen; + ret -= ch->ch_digi.digi_offlen; + } + else { + if (ch->ch_flags & CH_PRON) + ret -= ch->ch_digi.digi_offlen; + } + + if (ret < 0) + ret = 0; + + /* + * Schedule FEP to wake us up if needed. + * + * TODO: This might be overkill... + * Do we really need to schedule callbacks from the FEP + * in every case? Can we get smarter based on ret? + */ + dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_WRITE(("dgap_tty_write_room - %d tail: %d head: %d\n", ret, tail, head)); + + return(ret); +} + + +/* + * dgap_tty_put_char() + * + * Put a character into ch->ch_buf + * + * - used by the line discipline for OPOST processing + */ +static int dgap_tty_put_char(struct tty_struct *tty, unsigned char c) +{ + /* + * Simply call tty_write. + */ + DPR_WRITE(("dgap_tty_put_char called\n")); + dgap_tty_write(tty, &c, 1); + return 1; +} + + +/* + * dgap_tty_write() + * + * Take data from the user or kernel and send it out to the FEP. + * In here exists all the Transparent Print magic as well. + */ +static int dgap_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + struct channel_t *ch = NULL; + struct un_t *un = NULL; + struct bs_t *bs = NULL; + char *vaddr = NULL; + u16 head, tail, tmask, remain; + int bufcount = 0, n = 0; + int orig_count = 0; + ulong lock_flags; + int from_user = 0; + + if (tty == NULL || dgap_TmpWriteBuf == NULL) + return(0); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (0); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return(0); + + bs = ch->ch_bs; + if (!bs) + return(0); + + if (!count) + return(0); + + DPR_WRITE(("dgap_tty_write: Port: %x tty=%p user=%d len=%d\n", + ch->ch_portnum, tty, from_user, count)); + + /* + * Store original amount of characters passed in. + * This helps to figure out if we should ask the FEP + * to send us an event when it has more space available. + */ + orig_count = count; + + DGAP_LOCK(ch->ch_lock, lock_flags); + + /* Get our space available for the channel from the board */ + tmask = ch->ch_tsize - 1; + head = readw(&(bs->tx_head)) & tmask; + tail = readw(&(bs->tx_tail)) & tmask; + + if ((bufcount = tail - head - 1) < 0) + bufcount += ch->ch_tsize; + + DPR_WRITE(("%d: bufcount: %x count: %x tail: %x head: %x tmask: %x\n", + __LINE__, bufcount, count, tail, head, tmask)); + + /* + * Limit printer output to maxcps overall, with bursts allowed + * up to bufsize characters. + */ + bufcount = dgap_maxcps_room(tty, bufcount); + + /* + * Take minimum of what the user wants to send, and the + * space available in the FEP buffer. + */ + count = min(count, bufcount); + + /* + * Bail if no space left. + */ + if (count <= 0) { + dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + return(0); + } + + /* + * Output the printer ON string, if we are in terminal mode, but + * need to be in printer mode. + */ + if ((un->un_type == DGAP_PRINT) && !(ch->ch_flags & CH_PRON)) { + dgap_wmove(ch, ch->ch_digi.digi_onstr, + (int) ch->ch_digi.digi_onlen); + head = readw(&(bs->tx_head)) & tmask; + ch->ch_flags |= CH_PRON; + } + + /* + * On the other hand, output the printer OFF string, if we are + * currently in printer mode, but need to output to the terminal. + */ + if ((un->un_type != DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { + dgap_wmove(ch, ch->ch_digi.digi_offstr, + (int) ch->ch_digi.digi_offlen); + head = readw(&(bs->tx_head)) & tmask; + ch->ch_flags &= ~CH_PRON; + } + + /* + * If there is nothing left to copy, or I can't handle any more data, leave. + */ + if (count <= 0) { + dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + return(0); + } + + if (from_user) { + + count = min(count, WRITEBUFLEN); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + /* + * If data is coming from user space, copy it into a temporary + * buffer so we don't get swapped out while doing the copy to + * the board. + */ + /* we're allowed to block if it's from_user */ + if (down_interruptible(&dgap_TmpWriteSem)) { + return (-EINTR); + } + + if (copy_from_user(dgap_TmpWriteBuf, (const uchar __user *) buf, count)) { + up(&dgap_TmpWriteSem); + printk("Write: Copy from user failed!\n"); + return -EFAULT; + } + + DGAP_LOCK(ch->ch_lock, lock_flags); + + buf = dgap_TmpWriteBuf; + } + + n = count; + + /* + * If the write wraps over the top of the circular buffer, + * move the portion up to the wrap point, and reset the + * pointers to the bottom. + */ + remain = ch->ch_tstart + ch->ch_tsize - head; + + if (n >= remain) { + n -= remain; + vaddr = ch->ch_taddr + head; + + memcpy_toio(vaddr, (uchar *) buf, remain); + dgap_sniff_nowait_nolock(ch, "USER WRITE", (uchar *) buf, remain); + + head = ch->ch_tstart; + buf += remain; + } + + if (n > 0) { + + /* + * Move rest of data. + */ + vaddr = ch->ch_taddr + head; + remain = n; + + memcpy_toio(vaddr, (uchar *) buf, remain); + dgap_sniff_nowait_nolock(ch, "USER WRITE", (uchar *) buf, remain); + + head += remain; + + } + + if (count) { + ch->ch_txcount += count; + head &= tmask; + writew(head, &(bs->tx_head)); + } + + + dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); + + /* + * If this is the print device, and the + * printer is still on, we need to turn it + * off before going idle. If the buffer is + * non-empty, wait until it goes empty. + * Otherwise turn it off right now. + */ + if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { + tail = readw(&(bs->tx_tail)) & tmask; + + if (tail != head) { + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + } + else { + dgap_wmove(ch, ch->ch_digi.digi_offstr, + (int) ch->ch_digi.digi_offlen); + head = readw(&(bs->tx_head)) & tmask; + ch->ch_flags &= ~CH_PRON; + } + } + + /* Update printer buffer empty time. */ + if ((un->un_type == DGAP_PRINT) && (ch->ch_digi.digi_maxcps > 0) + && (ch->ch_digi.digi_bufsize > 0)) { + ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps; + } + + if (from_user) { + DGAP_UNLOCK(ch->ch_lock, lock_flags); + up(&dgap_TmpWriteSem); + } + else { + DGAP_UNLOCK(ch->ch_lock, lock_flags); + } + + DPR_WRITE(("Write finished - Write %d bytes of %d.\n", count, orig_count)); + + return (count); +} + + + +/* + * Return modem signals to ld. + */ +static int dgap_tty_tiocmget(struct tty_struct *tty) +{ + struct channel_t *ch; + struct un_t *un; + int result = -EIO; + uchar mstat = 0; + ulong lock_flags; + + if (!tty || tty->magic != TTY_MAGIC) + return result; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return result; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return result; + + DPR_IOCTL(("dgap_tty_tiocmget start\n")); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + mstat = readb(&(ch->ch_bs->m_stat)); + /* Append any outbound signals that might be pending... */ + mstat |= ch->ch_mostat; + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + result = 0; + + if (mstat & D_DTR(ch)) + result |= TIOCM_DTR; + if (mstat & D_RTS(ch)) + result |= TIOCM_RTS; + if (mstat & D_CTS(ch)) + result |= TIOCM_CTS; + if (mstat & D_DSR(ch)) + result |= TIOCM_DSR; + if (mstat & D_RI(ch)) + result |= TIOCM_RI; + if (mstat & D_CD(ch)) + result |= TIOCM_CD; + + DPR_IOCTL(("dgap_tty_tiocmget finish\n")); + + return result; +} + + +/* + * dgap_tty_tiocmset() + * + * Set modem signals, called by ld. + */ + +static int dgap_tty_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int ret = -EIO; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return ret; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return ret; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return ret; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return ret; + + DPR_IOCTL(("dgap_tty_tiocmset start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + if (set & TIOCM_RTS) { + ch->ch_mforce |= D_RTS(ch); + ch->ch_mval |= D_RTS(ch); + } + + if (set & TIOCM_DTR) { + ch->ch_mforce |= D_DTR(ch); + ch->ch_mval |= D_DTR(ch); + } + + if (clear & TIOCM_RTS) { + ch->ch_mforce |= D_RTS(ch); + ch->ch_mval &= ~(D_RTS(ch)); + } + + if (clear & TIOCM_DTR) { + ch->ch_mforce |= D_DTR(ch); + ch->ch_mval &= ~(D_DTR(ch)); + } + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_tiocmset finish\n")); + + return (0); +} + + + +/* + * dgap_tty_send_break() + * + * Send a Break, called by ld. + */ +static int dgap_tty_send_break(struct tty_struct *tty, int msec) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int ret = -EIO; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return ret; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return ret; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return ret; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return ret; + + switch (msec) { + case -1: + msec = 0xFFFF; + break; + case 0: + msec = 1; + break; + default: + msec /= 10; + break; + } + + DPR_IOCTL(("dgap_tty_send_break start 1. %lx\n", jiffies)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); +#if 0 + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); +#endif + dgap_cmdw(ch, SBREAK, (u16) msec, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_send_break finish\n")); + + return (0); +} + + + + +/* + * dgap_tty_wait_until_sent() + * + * wait until data has been transmitted, called by ld. + */ +static void dgap_tty_wait_until_sent(struct tty_struct *tty, int timeout) +{ + int rc; + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return; + } + return; +} + + + +/* + * dgap_send_xchar() + * + * send a high priority character, called by ld. + */ +static void dgap_tty_send_xchar(struct tty_struct *tty, char c) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_send_xchar start 1. %lx\n", jiffies)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + /* + * This is technically what we should do. + * However, the NIST tests specifically want + * to see each XON or XOFF character that it + * sends, so lets just send each character + * by hand... + */ +#if 0 + if (c == STOP_CHAR(tty)) { + dgap_cmdw(ch, RPAUSE, 0, 0); + } + else if (c == START_CHAR(tty)) { + dgap_cmdw(ch, RRESUME, 0, 0); + } + else { + dgap_wmove(ch, &c, 1); + } +#else + dgap_wmove(ch, &c, 1); +#endif + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_send_xchar finish\n")); + + return; +} + + + + +/* + * Return modem signals to ld. + */ +static int dgap_get_modem_info(struct channel_t *ch, unsigned int __user *value) +{ + int result = 0; + uchar mstat = 0; + ulong lock_flags; + int rc = 0; + + DPR_IOCTL(("dgap_get_modem_info start\n")); + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return(-ENXIO); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + mstat = readb(&(ch->ch_bs->m_stat)); + /* Append any outbound signals that might be pending... */ + mstat |= ch->ch_mostat; + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + result = 0; + + if (mstat & D_DTR(ch)) + result |= TIOCM_DTR; + if (mstat & D_RTS(ch)) + result |= TIOCM_RTS; + if (mstat & D_CTS(ch)) + result |= TIOCM_CTS; + if (mstat & D_DSR(ch)) + result |= TIOCM_DSR; + if (mstat & D_RI(ch)) + result |= TIOCM_RI; + if (mstat & D_CD(ch)) + result |= TIOCM_CD; + + rc = put_user(result, value); + + DPR_IOCTL(("dgap_get_modem_info finish\n")); + return(rc); +} + + +/* + * dgap_set_modem_info() + * + * Set modem signals, called by ld. + */ +static int dgap_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int __user *value) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int ret = -ENXIO; + unsigned int arg = 0; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return ret; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return ret; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return ret; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return ret; + + DPR_IOCTL(("dgap_set_modem_info() start\n")); + + ret = get_user(arg, value); + if (ret) { + DPR_IOCTL(("dgap_set_modem_info %d ret: %x. finished.\n", __LINE__, ret)); + return(ret); + } + + DPR_IOCTL(("dgap_set_modem_info: command: %x arg: %x\n", command, arg)); + + switch (command) { + case TIOCMBIS: + if (arg & TIOCM_RTS) { + ch->ch_mforce |= D_RTS(ch); + ch->ch_mval |= D_RTS(ch); + } + + if (arg & TIOCM_DTR) { + ch->ch_mforce |= D_DTR(ch); + ch->ch_mval |= D_DTR(ch); + } + + break; + + case TIOCMBIC: + if (arg & TIOCM_RTS) { + ch->ch_mforce |= D_RTS(ch); + ch->ch_mval &= ~(D_RTS(ch)); + } + + if (arg & TIOCM_DTR) { + ch->ch_mforce |= D_DTR(ch); + ch->ch_mval &= ~(D_DTR(ch)); + } + + break; + + case TIOCMSET: + ch->ch_mforce = D_DTR(ch)|D_RTS(ch); + + if (arg & TIOCM_RTS) { + ch->ch_mval |= D_RTS(ch); + } + else { + ch->ch_mval &= ~(D_RTS(ch)); + } + + if (arg & TIOCM_DTR) { + ch->ch_mval |= (D_DTR(ch)); + } + else { + ch->ch_mval &= ~(D_DTR(ch)); + } + + break; + + default: + return(-EINVAL); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_set_modem_info finish\n")); + + return (0); +} + + +/* + * dgap_tty_digigeta() + * + * Ioctl to get the information for ditty. + * + * + * + */ +static int dgap_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo) +{ + struct channel_t *ch; + struct un_t *un; + struct digi_t tmp; + ulong lock_flags; + + if (!retinfo) + return (-EFAULT); + + if (!tty || tty->magic != TTY_MAGIC) + return (-EFAULT); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (-EFAULT); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (-EFAULT); + + memset(&tmp, 0, sizeof(tmp)); + + DGAP_LOCK(ch->ch_lock, lock_flags); + memcpy(&tmp, &ch->ch_digi, sizeof(tmp)); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return (-EFAULT); + + return (0); +} + + +/* + * dgap_tty_digiseta() + * + * Ioctl to set the information for ditty. + * + * + * + */ +static int dgap_tty_digiseta(struct tty_struct *tty, struct digi_t __user *new_info) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + struct digi_t new_digi; + ulong lock_flags = 0; + unsigned long lock_flags2; + + DPR_IOCTL(("DIGI_SETA start\n")); + + if (!tty || tty->magic != TTY_MAGIC) + return (-EFAULT); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (-EFAULT); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (-EFAULT); + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (-EFAULT); + + if (copy_from_user(&new_digi, new_info, sizeof(struct digi_t))) { + DPR_IOCTL(("DIGI_SETA failed copy_from_user\n")); + return(-EFAULT); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + memcpy(&ch->ch_digi, &new_digi, sizeof(struct digi_t)); + + if (ch->ch_digi.digi_maxcps < 1) + ch->ch_digi.digi_maxcps = 1; + + if (ch->ch_digi.digi_maxcps > 10000) + ch->ch_digi.digi_maxcps = 10000; + + if (ch->ch_digi.digi_bufsize < 10) + ch->ch_digi.digi_bufsize = 10; + + if (ch->ch_digi.digi_maxchar < 1) + ch->ch_digi.digi_maxchar = 1; + + if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize) + ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize; + + if (ch->ch_digi.digi_onlen > DIGI_PLEN) + ch->ch_digi.digi_onlen = DIGI_PLEN; + + if (ch->ch_digi.digi_offlen > DIGI_PLEN) + ch->ch_digi.digi_offlen = DIGI_PLEN; + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("DIGI_SETA finish\n")); + + return(0); +} + + +/* + * dgap_tty_digigetedelay() + * + * Ioctl to get the current edelay setting. + * + * + * + */ +static int dgap_tty_digigetedelay(struct tty_struct *tty, int __user *retinfo) +{ + struct channel_t *ch; + struct un_t *un; + int tmp; + ulong lock_flags; + + if (!retinfo) + return (-EFAULT); + + if (!tty || tty->magic != TTY_MAGIC) + return (-EFAULT); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (-EFAULT); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (-EFAULT); + + memset(&tmp, 0, sizeof(tmp)); + + DGAP_LOCK(ch->ch_lock, lock_flags); + tmp = readw(&(ch->ch_bs->edelay)); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return (-EFAULT); + + return (0); +} + + +/* + * dgap_tty_digisetedelay() + * + * Ioctl to set the EDELAY setting + * + */ +static int dgap_tty_digisetedelay(struct tty_struct *tty, int __user *new_info) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int new_digi; + ulong lock_flags; + ulong lock_flags2; + + DPR_IOCTL(("DIGI_SETA start\n")); + + if (!tty || tty->magic != TTY_MAGIC) + return (-EFAULT); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (-EFAULT); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (-EFAULT); + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (-EFAULT); + + if (copy_from_user(&new_digi, new_info, sizeof(int))) { + DPR_IOCTL(("DIGI_SETEDELAY failed copy_from_user\n")); + return(-EFAULT); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + writew((u16) new_digi, &(ch->ch_bs->edelay)); + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("DIGI_SETA finish\n")); + + return(0); +} + + +/* + * dgap_tty_digigetcustombaud() + * + * Ioctl to get the current custom baud rate setting. + */ +static int dgap_tty_digigetcustombaud(struct tty_struct *tty, int __user *retinfo) +{ + struct channel_t *ch; + struct un_t *un; + int tmp; + ulong lock_flags; + + if (!retinfo) + return (-EFAULT); + + if (!tty || tty->magic != TTY_MAGIC) + return (-EFAULT); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (-EFAULT); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (-EFAULT); + + memset(&tmp, 0, sizeof(tmp)); + + DGAP_LOCK(ch->ch_lock, lock_flags); + tmp = dgap_get_custom_baud(ch); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_IOCTL(("DIGI_GETCUSTOMBAUD. Returning %d\n", tmp)); + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return (-EFAULT); + + return (0); +} + + +/* + * dgap_tty_digisetcustombaud() + * + * Ioctl to set the custom baud rate setting + */ +static int dgap_tty_digisetcustombaud(struct tty_struct *tty, int __user *new_info) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + uint new_rate; + ulong lock_flags; + ulong lock_flags2; + + DPR_IOCTL(("DIGI_SETCUSTOMBAUD start\n")); + + if (!tty || tty->magic != TTY_MAGIC) + return (-EFAULT); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (-EFAULT); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (-EFAULT); + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (-EFAULT); + + + if (copy_from_user(&new_rate, new_info, sizeof(unsigned int))) { + DPR_IOCTL(("DIGI_SETCUSTOMBAUD failed copy_from_user\n")); + return(-EFAULT); + } + + if (bd->bd_flags & BD_FEP5PLUS) { + + DPR_IOCTL(("DIGI_SETCUSTOMBAUD. Setting %d\n", new_rate)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + ch->ch_custom_speed = new_rate; + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + } + + DPR_IOCTL(("DIGI_SETCUSTOMBAUD finish\n")); + + return(0); +} + + +/* + * dgap_set_termios() + */ +static void dgap_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + unsigned long lock_flags; + unsigned long lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + ch->ch_c_cflag = tty->termios.c_cflag; + ch->ch_c_iflag = tty->termios.c_iflag; + ch->ch_c_oflag = tty->termios.c_oflag; + ch->ch_c_lflag = tty->termios.c_lflag; + ch->ch_startc = tty->termios.c_cc[VSTART]; + ch->ch_stopc = tty->termios.c_cc[VSTOP]; + + dgap_carrier(ch); + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); +} + + +static void dgap_tty_throttle(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_throttle start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + ch->ch_flags |= (CH_RXBLOCK); +#if 1 + dgap_cmdw(ch, RPAUSE, 0, 0); +#endif + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_throttle finish\n")); +} + + +static void dgap_tty_unthrottle(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_unthrottle start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + ch->ch_flags &= ~(CH_RXBLOCK); + +#if 1 + dgap_cmdw(ch, RRESUME, 0, 0); +#endif + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_unthrottle finish\n")); +} + + +static void dgap_tty_start(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_start start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, RESUMETX, 0, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_start finish\n")); +} + + +static void dgap_tty_stop(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_stop start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, PAUSETX, 0, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_stop finish\n")); +} + + +/* + * dgap_tty_flush_chars() + * + * Flush the cook buffer + * + * Note to self, and any other poor souls who venture here: + * + * flush in this case DOES NOT mean dispose of the data. + * instead, it means "stop buffering and send it if you + * haven't already." Just guess how I figured that out... SRW 2-Jun-98 + * + * It is also always called in interrupt context - JAR 8-Sept-99 + */ +static void dgap_tty_flush_chars(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_flush_chars start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + /* TODO: Do something here */ + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_flush_chars finish\n")); +} + + + +/* + * dgap_tty_flush_buffer() + * + * Flush Tx buffer (make in == out) + */ +static void dgap_tty_flush_buffer(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; + u16 head = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_flush_buffer on port: %d start\n", ch->ch_portnum)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + ch->ch_flags &= ~CH_STOP; + head = readw(&(ch->ch_bs->tx_head)); + dgap_cmdw(ch, FLUSHTX, (u16) head, 0); + dgap_cmdw(ch, RESUMETX, 0, 0); + if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_tun.un_flags_wait); + } + if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_pun.un_flags_wait); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + if (waitqueue_active(&tty->write_wait)) + wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); + + DPR_IOCTL(("dgap_tty_flush_buffer finish\n")); +} + + + +/***************************************************************************** + * + * The IOCTL function and all of its helpers + * + *****************************************************************************/ + +/* + * dgap_tty_ioctl() + * + * The usual assortment of ioctl's + */ +static int dgap_tty_ioctl(struct tty_struct *tty, unsigned int cmd, + unsigned long arg) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int rc; + u16 head = 0; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + void __user *uarg = (void __user *) arg; + + if (!tty || tty->magic != TTY_MAGIC) + return (-ENODEV); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (-ENODEV); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (-ENODEV); + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (-ENODEV); + + DPR_IOCTL(("dgap_tty_ioctl start on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + if (un->un_open_count <= 0) { + DPR_BASIC(("dgap_tty_ioctl - unit not open.\n")); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(-EIO); + } + + switch (cmd) { + + /* Here are all the standard ioctl's that we MUST implement */ + + case TCSBRK: + /* + * TCSBRK is SVID version: non-zero arg --> no break + * this behaviour is exploited by tcdrain(). + * + * According to POSIX.1 spec (7.2.2.1.2) breaks should be + * between 0.25 and 0.5 seconds so we'll ask for something + * in the middle: 0.375 seconds. + */ + rc = tty_check_change(tty); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + if (rc) { + return(rc); + } + + rc = dgap_wait_for_drain(tty); + + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + if(((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) { + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + return(0); + + + case TCSBRKP: + /* support for POSIX tcsendbreak() + + * According to POSIX.1 spec (7.2.2.1.2) breaks should be + * between 0.25 and 0.5 seconds so we'll ask for something + * in the middle: 0.375 seconds. + */ + rc = tty_check_change(tty); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + if (rc) { + return(rc); + } + + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + return(0); + + case TIOCSBRK: + /* + * FEP5 doesn't support turning on a break unconditionally. + * The FEP5 device will stop sending a break automatically + * after the specified time value that was sent when turning on + * the break. + */ + rc = tty_check_change(tty); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + if (rc) { + return(rc); + } + + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + return 0; + + case TIOCCBRK: + /* + * FEP5 doesn't support turning off a break unconditionally. + * The FEP5 device will stop sending a break automatically + * after the specified time value that was sent when turning on + * the break. + */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return 0; + + case TIOCGSOFTCAR: + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + rc = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) arg); + return(rc); + + case TIOCSSOFTCAR: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + rc = get_user(arg, (unsigned long __user *) arg); + if (rc) + return(rc); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + tty->termios.c_cflag = ((tty->termios.c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); + dgap_param(tty); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + return(0); + + case TIOCMGET: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_get_modem_info(ch, uarg)); + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_set_modem_info(tty, cmd, uarg)); + + /* + * Here are any additional ioctl's that we want to implement + */ + + case TCFLSH: + /* + * The linux tty driver doesn't have a flush + * input routine for the driver, assuming all backed + * up data is in the line disc. buffers. However, + * we all know that's not the case. Here, we + * act on the ioctl, but then lie and say we didn't + * so the line discipline will process the flush + * also. + */ + rc = tty_check_change(tty); + if (rc) { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(rc); + } + + if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) { + if (!(un->un_type == DGAP_PRINT)) { + head = readw(&(ch->ch_bs->rx_head)); + writew(head, &(ch->ch_bs->rx_tail)); + writeb(0, &(ch->ch_bs->orun)); + } + } + + if ((arg == TCOFLUSH) || (arg == TCIOFLUSH)) { + ch->ch_flags &= ~CH_STOP; + head = readw(&(ch->ch_bs->tx_head)); + dgap_cmdw(ch, FLUSHTX, (u16) head, 0 ); + dgap_cmdw(ch, RESUMETX, 0, 0); + if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_tun.un_flags_wait); + } + if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_pun.un_flags_wait); + } + if (waitqueue_active(&tty->write_wait)) + wake_up_interruptible(&tty->write_wait); + + /* Can't hold any locks when calling tty_wakeup! */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + tty_wakeup(tty); + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + } + + /* pretend we didn't recognize this IOCTL */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl (LINE:%d) finish on port %d - cmd %s (%x), arg %lx\n", + __LINE__, ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + return(-ENOIOCTLCMD); + + case TCSETSF: + case TCSETSW: + /* + * The linux tty driver doesn't have a flush + * input routine for the driver, assuming all backed + * up data is in the line disc. buffers. However, + * we all know that's not the case. Here, we + * act on the ioctl, but then lie and say we didn't + * so the line discipline will process the flush + * also. + */ + if (cmd == TCSETSF) { + /* flush rx */ + ch->ch_flags &= ~CH_STOP; + head = readw(&(ch->ch_bs->rx_head)); + writew(head, &(ch->ch_bs->rx_tail)); + } + + /* now wait for all the output to drain */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + /* pretend we didn't recognize this */ + return(-ENOIOCTLCMD); + + case TCSETAW: + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + /* pretend we didn't recognize this */ + return(-ENOIOCTLCMD); + + case TCXONC: + /* + * The Linux Line Discipline (LD) would do this for us if we + * let it, but we have the special firmware options to do this + * the "right way" regardless of hardware or software flow + * control so we'll do it outselves instead of letting the LD + * do it. + */ + rc = tty_check_change(tty); + if (rc) { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(rc); + } + + DPR_IOCTL(("dgap_ioctl - in TCXONC - %d\n", cmd)); + switch (arg) { + + case TCOON: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + dgap_tty_start(tty); + return(0); + case TCOOFF: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + dgap_tty_stop(tty); + return(0); + case TCION: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + /* Make the ld do it */ + return(-ENOIOCTLCMD); + case TCIOFF: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + /* Make the ld do it */ + return(-ENOIOCTLCMD); + default: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(-EINVAL); + } + + case DIGI_GETA: + /* get information for ditty */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digigeta(tty, uarg)); + + case DIGI_SETAW: + case DIGI_SETAF: + + /* set information for ditty */ + if (cmd == (DIGI_SETAW)) { + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + } + else { + tty_ldisc_flush(tty); + } + /* fall thru */ + + case DIGI_SETA: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digiseta(tty, uarg)); + + case DIGI_GEDELAY: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digigetedelay(tty, uarg)); + + case DIGI_SEDELAY: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digisetedelay(tty, uarg)); + + case DIGI_GETCUSTOMBAUD: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digigetcustombaud(tty, uarg)); + + case DIGI_SETCUSTOMBAUD: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digisetcustombaud(tty, uarg)); + + case DIGI_RESET_PORT: + dgap_firmware_reset_port(ch); + dgap_param(tty); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return 0; + + default: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl - in default\n")); + DPR_IOCTL(("dgap_tty_ioctl end - cmd %s (%x), arg %lx\n", + dgap_ioctl_name(cmd), cmd, arg)); + + return(-ENOIOCTLCMD); + } +} diff --git a/drivers/staging/dgap/dgap_tty.c b/drivers/staging/dgap/dgap_tty.c deleted file mode 100644 index 9b3d3b5..0000000 --- a/drivers/staging/dgap/dgap_tty.c +++ /dev/null @@ -1,3555 +0,0 @@ -/* - * Copyright 2003 Digi International (www.digi.com) - * Scott H Kilau - * - * 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, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. - * - * - * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! - * - * This is shared code between Digi's CVS archive and the - * Linux Kernel sources. - * Changing the source just for reformatting needlessly breaks - * our CVS diff history. - * - * Send any bug fixes/changes to: Eng.Linux at digi dot com. - * Thank you. - */ - -/************************************************************************ - * - * This file implements the tty driver functionality for the - * FEP5 based product lines. - * - ************************************************************************ - * - */ - -#include -#include /* For jiffies, task states */ -#include /* For tasklet and interrupt structs/defines */ -#include -#include -#include -#include -#include -#include -#include /* For udelay */ -#include /* For copy_from_user/copy_to_user */ -#include /* For read[bwl]/write[bwl] */ -#include - -#include "dgap_driver.h" -#include "dgap_tty.h" -#include "dgap_types.h" -#include "dgap_fep5.h" -#include "dgap_parse.h" -#include "dgap_conf.h" -#include "dgap_sysfs.h" - -#define init_MUTEX(sem) sema_init(sem, 1) -#define DECLARE_MUTEX(name) \ - struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1) - -/* - * internal variables - */ -static struct board_t *dgap_BoardsByMajor[256]; -static uchar *dgap_TmpWriteBuf = NULL; -static DECLARE_MUTEX(dgap_TmpWriteSem); - -/* - * Default transparent print information. - */ -static struct digi_t dgap_digi_init = { - .digi_flags = DIGI_COOK, /* Flags */ - .digi_maxcps = 100, /* Max CPS */ - .digi_maxchar = 50, /* Max chars in print queue */ - .digi_bufsize = 100, /* Printer buffer size */ - .digi_onlen = 4, /* size of printer on string */ - .digi_offlen = 4, /* size of printer off string */ - .digi_onstr = "\033[5i", /* ANSI printer on string ] */ - .digi_offstr = "\033[4i", /* ANSI printer off string ] */ - .digi_term = "ansi" /* default terminal type */ -}; - - -/* - * Define a local default termios struct. All ports will be created - * with this termios initially. - * - * This defines a raw port at 9600 baud, 8 data bits, no parity, - * 1 stop bit. - */ - -static struct ktermios DgapDefaultTermios = -{ - .c_iflag = (DEFAULT_IFLAGS), /* iflags */ - .c_oflag = (DEFAULT_OFLAGS), /* oflags */ - .c_cflag = (DEFAULT_CFLAGS), /* cflags */ - .c_lflag = (DEFAULT_LFLAGS), /* lflags */ - .c_cc = INIT_C_CC, - .c_line = 0, -}; - -/* Our function prototypes */ -static int dgap_tty_open(struct tty_struct *tty, struct file *file); -static void dgap_tty_close(struct tty_struct *tty, struct file *file); -static int dgap_block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch); -static int dgap_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); -static int dgap_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo); -static int dgap_tty_digiseta(struct tty_struct *tty, struct digi_t __user *new_info); -static int dgap_tty_digigetedelay(struct tty_struct *tty, int __user *retinfo); -static int dgap_tty_digisetedelay(struct tty_struct *tty, int __user *new_info); -static int dgap_tty_write_room(struct tty_struct* tty); -static int dgap_tty_chars_in_buffer(struct tty_struct* tty); -static void dgap_tty_start(struct tty_struct *tty); -static void dgap_tty_stop(struct tty_struct *tty); -static void dgap_tty_throttle(struct tty_struct *tty); -static void dgap_tty_unthrottle(struct tty_struct *tty); -static void dgap_tty_flush_chars(struct tty_struct *tty); -static void dgap_tty_flush_buffer(struct tty_struct *tty); -static void dgap_tty_hangup(struct tty_struct *tty); -static int dgap_wait_for_drain(struct tty_struct *tty); -static int dgap_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int __user *value); -static int dgap_get_modem_info(struct channel_t *ch, unsigned int __user *value); -static int dgap_tty_digisetcustombaud(struct tty_struct *tty, int __user *new_info); -static int dgap_tty_digigetcustombaud(struct tty_struct *tty, int __user *retinfo); -static int dgap_tty_tiocmget(struct tty_struct *tty); -static int dgap_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -static int dgap_tty_send_break(struct tty_struct *tty, int msec); -static void dgap_tty_wait_until_sent(struct tty_struct *tty, int timeout); -static int dgap_tty_write(struct tty_struct *tty, const unsigned char *buf, int count); -static void dgap_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios); -static int dgap_tty_put_char(struct tty_struct *tty, unsigned char c); -static void dgap_tty_send_xchar(struct tty_struct *tty, char ch); - -static const struct tty_operations dgap_tty_ops = { - .open = dgap_tty_open, - .close = dgap_tty_close, - .write = dgap_tty_write, - .write_room = dgap_tty_write_room, - .flush_buffer = dgap_tty_flush_buffer, - .chars_in_buffer = dgap_tty_chars_in_buffer, - .flush_chars = dgap_tty_flush_chars, - .ioctl = dgap_tty_ioctl, - .set_termios = dgap_tty_set_termios, - .stop = dgap_tty_stop, - .start = dgap_tty_start, - .throttle = dgap_tty_throttle, - .unthrottle = dgap_tty_unthrottle, - .hangup = dgap_tty_hangup, - .put_char = dgap_tty_put_char, - .tiocmget = dgap_tty_tiocmget, - .tiocmset = dgap_tty_tiocmset, - .break_ctl = dgap_tty_send_break, - .wait_until_sent = dgap_tty_wait_until_sent, - .send_xchar = dgap_tty_send_xchar -}; - - - - - -/************************************************************************ - * - * TTY Initialization/Cleanup Functions - * - ************************************************************************/ - -/* - * dgap_tty_preinit() - * - * Initialize any global tty related data before we download any boards. - */ -int dgap_tty_preinit(void) -{ - unsigned long flags; - - DGAP_LOCK(dgap_global_lock, flags); - - /* - * Allocate a buffer for doing the copy from user space to - * kernel space in dgap_input(). We only use one buffer and - * control access to it with a semaphore. If we are paging, we - * are already in trouble so one buffer won't hurt much anyway. - */ - dgap_TmpWriteBuf = kmalloc(WRITEBUFLEN, GFP_ATOMIC); - - if (!dgap_TmpWriteBuf) { - DGAP_UNLOCK(dgap_global_lock, flags); - DPR_INIT(("unable to allocate tmp write buf")); - return (-ENOMEM); - } - - DGAP_UNLOCK(dgap_global_lock, flags); - return(0); -} - - -/* - * dgap_tty_register() - * - * Init the tty subsystem for this board. - */ -int dgap_tty_register(struct board_t *brd) -{ - int rc = 0; - - DPR_INIT(("tty_register start")); - - brd->SerialDriver = alloc_tty_driver(MAXPORTS); - - snprintf(brd->SerialName, MAXTTYNAMELEN, "tty_dgap_%d_", brd->boardnum); - brd->SerialDriver->name = brd->SerialName; - brd->SerialDriver->name_base = 0; - brd->SerialDriver->major = 0; - brd->SerialDriver->minor_start = 0; - brd->SerialDriver->type = TTY_DRIVER_TYPE_SERIAL; - brd->SerialDriver->subtype = SERIAL_TYPE_NORMAL; - brd->SerialDriver->init_termios = DgapDefaultTermios; - brd->SerialDriver->driver_name = DRVSTR; - brd->SerialDriver->flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK); - - /* The kernel wants space to store pointers to tty_structs */ - brd->SerialDriver->ttys = kzalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); - if (!brd->SerialDriver->ttys) - return(-ENOMEM); - - /* - * Entry points for driver. Called by the kernel from - * tty_io.c and n_tty.c. - */ - tty_set_operations(brd->SerialDriver, &dgap_tty_ops); - - /* - * If we're doing transparent print, we have to do all of the above - * again, separately so we don't get the LD confused about what major - * we are when we get into the dgap_tty_open() routine. - */ - brd->PrintDriver = alloc_tty_driver(MAXPORTS); - - snprintf(brd->PrintName, MAXTTYNAMELEN, "pr_dgap_%d_", brd->boardnum); - brd->PrintDriver->name = brd->PrintName; - brd->PrintDriver->name_base = 0; - brd->PrintDriver->major = 0; - brd->PrintDriver->minor_start = 0; - brd->PrintDriver->type = TTY_DRIVER_TYPE_SERIAL; - brd->PrintDriver->subtype = SERIAL_TYPE_NORMAL; - brd->PrintDriver->init_termios = DgapDefaultTermios; - brd->PrintDriver->driver_name = DRVSTR; - brd->PrintDriver->flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK); - - /* The kernel wants space to store pointers to tty_structs */ - brd->PrintDriver->ttys = kzalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); - if (!brd->PrintDriver->ttys) - return(-ENOMEM); - - /* - * Entry points for driver. Called by the kernel from - * tty_io.c and n_tty.c. - */ - tty_set_operations(brd->PrintDriver, &dgap_tty_ops); - - if (!brd->dgap_Major_Serial_Registered) { - /* Register tty devices */ - rc = tty_register_driver(brd->SerialDriver); - if (rc < 0) { - APR(("Can't register tty device (%d)\n", rc)); - return(rc); - } - brd->dgap_Major_Serial_Registered = TRUE; - dgap_BoardsByMajor[brd->SerialDriver->major] = brd; - brd->dgap_Serial_Major = brd->SerialDriver->major; - } - - if (!brd->dgap_Major_TransparentPrint_Registered) { - /* Register Transparent Print devices */ - rc = tty_register_driver(brd->PrintDriver); - if (rc < 0) { - APR(("Can't register Transparent Print device (%d)\n", rc)); - return(rc); - } - brd->dgap_Major_TransparentPrint_Registered = TRUE; - dgap_BoardsByMajor[brd->PrintDriver->major] = brd; - brd->dgap_TransparentPrint_Major = brd->PrintDriver->major; - } - - DPR_INIT(("DGAP REGISTER TTY: MAJORS: %d %d\n", brd->SerialDriver->major, - brd->PrintDriver->major)); - - return (rc); -} - - -/* - * dgap_tty_init() - * - * Init the tty subsystem. Called once per board after board has been - * downloaded and init'ed. - */ -int dgap_tty_init(struct board_t *brd) -{ - int i; - int tlw; - uint true_count = 0; - uchar *vaddr; - uchar modem = 0; - struct channel_t *ch; - struct bs_t *bs; - struct cm_t *cm; - - if (!brd) - return (-ENXIO); - - DPR_INIT(("dgap_tty_init start\n")); - - /* - * Initialize board structure elements. - */ - - vaddr = brd->re_map_membase; - true_count = readw((vaddr + NCHAN)); - - brd->nasync = dgap_config_get_number_of_ports(brd); - - if (!brd->nasync) { - brd->nasync = brd->maxports; - } - - if (brd->nasync > brd->maxports) { - brd->nasync = brd->maxports; - } - - if (true_count != brd->nasync) { - if ((brd->type == PPCM) && (true_count == 64)) { - APR(("***WARNING**** %s configured for %d ports, has %d ports.\nPlease make SURE the EBI cable running from the card\nto each EM module is plugged into EBI IN!\n", - brd->name, brd->nasync, true_count)); - } - else if ((brd->type == PPCM) && (true_count == 0)) { - APR(("***WARNING**** %s configured for %d ports, has %d ports.\nPlease make SURE the EBI cable running from the card\nto each EM module is plugged into EBI IN!\n", - brd->name, brd->nasync, true_count)); - } - else { - APR(("***WARNING**** %s configured for %d ports, has %d ports.\n", - brd->name, brd->nasync, true_count)); - } - - brd->nasync = true_count; - - /* If no ports, don't bother going any further */ - if (!brd->nasync) { - brd->state = BOARD_FAILED; - brd->dpastatus = BD_NOFEP; - return(-ENXIO); - } - } - - /* - * Allocate channel memory that might not have been allocated - * when the driver was first loaded. - */ - for (i = 0; i < brd->nasync; i++) { - if (!brd->channels[i]) { - brd->channels[i] = kzalloc(sizeof(struct channel_t), GFP_ATOMIC); - if (!brd->channels[i]) { - DPR_CORE(("%s:%d Unable to allocate memory for channel struct\n", - __FILE__, __LINE__)); - } - } - } - - ch = brd->channels[0]; - vaddr = brd->re_map_membase; - - bs = (struct bs_t *) ((ulong) vaddr + CHANBUF); - cm = (struct cm_t *) ((ulong) vaddr + CMDBUF); - - brd->bd_bs = bs; - - /* Set up channel variables */ - for (i = 0; i < brd->nasync; i++, ch = brd->channels[i], bs++) { - - if (!brd->channels[i]) - continue; - - DGAP_SPINLOCK_INIT(ch->ch_lock); - - /* Store all our magic numbers */ - ch->magic = DGAP_CHANNEL_MAGIC; - ch->ch_tun.magic = DGAP_UNIT_MAGIC; - ch->ch_tun.un_type = DGAP_SERIAL; - ch->ch_tun.un_ch = ch; - ch->ch_tun.un_dev = i; - - ch->ch_pun.magic = DGAP_UNIT_MAGIC; - ch->ch_pun.un_type = DGAP_PRINT; - ch->ch_pun.un_ch = ch; - ch->ch_pun.un_dev = i; - - ch->ch_vaddr = vaddr; - ch->ch_bs = bs; - ch->ch_cm = cm; - ch->ch_bd = brd; - ch->ch_portnum = i; - ch->ch_digi = dgap_digi_init; - - /* - * Set up digi dsr and dcd bits based on altpin flag. - */ - if (dgap_config_get_altpin(brd)) { - ch->ch_dsr = DM_CD; - ch->ch_cd = DM_DSR; - ch->ch_digi.digi_flags |= DIGI_ALTPIN; - } - else { - ch->ch_cd = DM_CD; - ch->ch_dsr = DM_DSR; - } - - ch->ch_taddr = vaddr + ((ch->ch_bs->tx_seg) << 4); - ch->ch_raddr = vaddr + ((ch->ch_bs->rx_seg) << 4); - ch->ch_tx_win = 0; - ch->ch_rx_win = 0; - ch->ch_tsize = readw(&(ch->ch_bs->tx_max)) + 1; - ch->ch_rsize = readw(&(ch->ch_bs->rx_max)) + 1; - ch->ch_tstart = 0; - ch->ch_rstart = 0; - - /* .25 second delay */ - ch->ch_close_delay = 250; - - /* - * Set queue water marks, interrupt mask, - * and general tty parameters. - */ - ch->ch_tlw = tlw = ch->ch_tsize >= 2000 ? ((ch->ch_tsize * 5) / 8) : ch->ch_tsize / 2; - - dgap_cmdw(ch, STLOW, tlw, 0); - - dgap_cmdw(ch, SRLOW, ch->ch_rsize / 2, 0); - - dgap_cmdw(ch, SRHIGH, 7 * ch->ch_rsize / 8, 0); - - ch->ch_mistat = readb(&(ch->ch_bs->m_stat)); - - init_waitqueue_head(&ch->ch_flags_wait); - init_waitqueue_head(&ch->ch_tun.un_flags_wait); - init_waitqueue_head(&ch->ch_pun.un_flags_wait); - init_waitqueue_head(&ch->ch_sniff_wait); - - /* Turn on all modem interrupts for now */ - modem = (DM_CD | DM_DSR | DM_CTS | DM_RI); - writeb(modem, &(ch->ch_bs->m_int)); - - /* - * Set edelay to 0 if interrupts are turned on, - * otherwise set edelay to the usual 100. - */ - if (brd->intr_used) - writew(0, &(ch->ch_bs->edelay)); - else - writew(100, &(ch->ch_bs->edelay)); - - writeb(1, &(ch->ch_bs->idata)); - } - - - DPR_INIT(("dgap_tty_init finish\n")); - - return (0); -} - - -/* - * dgap_tty_post_uninit() - * - * UnInitialize any global tty related data. - */ -void dgap_tty_post_uninit(void) -{ - kfree(dgap_TmpWriteBuf); - dgap_TmpWriteBuf = NULL; -} - - -/* - * dgap_tty_uninit() - * - * Uninitialize the TTY portion of this driver. Free all memory and - * resources. - */ -void dgap_tty_uninit(struct board_t *brd) -{ - int i = 0; - - if (brd->dgap_Major_Serial_Registered) { - dgap_BoardsByMajor[brd->SerialDriver->major] = NULL; - brd->dgap_Serial_Major = 0; - for (i = 0; i < brd->nasync; i++) { - dgap_remove_tty_sysfs(brd->channels[i]->ch_tun.un_sysfs); - tty_unregister_device(brd->SerialDriver, i); - } - tty_unregister_driver(brd->SerialDriver); - kfree(brd->SerialDriver->ttys); - brd->SerialDriver->ttys = NULL; - put_tty_driver(brd->SerialDriver); - brd->dgap_Major_Serial_Registered = FALSE; - } - - if (brd->dgap_Major_TransparentPrint_Registered) { - dgap_BoardsByMajor[brd->PrintDriver->major] = NULL; - brd->dgap_TransparentPrint_Major = 0; - for (i = 0; i < brd->nasync; i++) { - dgap_remove_tty_sysfs(brd->channels[i]->ch_pun.un_sysfs); - tty_unregister_device(brd->PrintDriver, i); - } - tty_unregister_driver(brd->PrintDriver); - kfree(brd->PrintDriver->ttys); - brd->PrintDriver->ttys = NULL; - put_tty_driver(brd->PrintDriver); - brd->dgap_Major_TransparentPrint_Registered = FALSE; - } -} - - -#define TMPBUFLEN (1024) - -/* - * dgap_sniff - Dump data out to the "sniff" buffer if the - * proc sniff file is opened... - */ -static void dgap_sniff_nowait_nolock(struct channel_t *ch, uchar *text, uchar *buf, int len) -{ - struct timeval tv; - int n; - int r; - int nbuf; - int i; - int tmpbuflen; - char tmpbuf[TMPBUFLEN]; - char *p = tmpbuf; - int too_much_data; - - /* Leave if sniff not open */ - if (!(ch->ch_sniff_flags & SNIFF_OPEN)) - return; - - do_gettimeofday(&tv); - - /* Create our header for data dump */ - p += sprintf(p, "<%ld %ld><%s><", tv.tv_sec, tv.tv_usec, text); - tmpbuflen = p - tmpbuf; - - do { - too_much_data = 0; - - for (i = 0; i < len && tmpbuflen < (TMPBUFLEN - 4); i++) { - p += sprintf(p, "%02x ", *buf); - buf++; - tmpbuflen = p - tmpbuf; - } - - if (tmpbuflen < (TMPBUFLEN - 4)) { - if (i > 0) - p += sprintf(p - 1, "%s\n", ">"); - else - p += sprintf(p, "%s\n", ">"); - } else { - too_much_data = 1; - len -= i; - } - - nbuf = strlen(tmpbuf); - p = tmpbuf; - - /* - * Loop while data remains. - */ - while (nbuf > 0 && ch->ch_sniff_buf) { - /* - * Determine the amount of available space left in the - * buffer. If there's none, wait until some appears. - */ - n = (ch->ch_sniff_out - ch->ch_sniff_in - 1) & SNIFF_MASK; - - /* - * If there is no space left to write to in our sniff buffer, - * we have no choice but to drop the data. - * We *cannot* sleep here waiting for space, because this - * function was probably called by the interrupt/timer routines! - */ - if (n == 0) { - return; - } - - /* - * Copy as much data as will fit. - */ - - if (n > nbuf) - n = nbuf; - - r = SNIFF_MAX - ch->ch_sniff_in; - - if (r <= n) { - memcpy(ch->ch_sniff_buf + ch->ch_sniff_in, p, r); - - n -= r; - ch->ch_sniff_in = 0; - p += r; - nbuf -= r; - } - - memcpy(ch->ch_sniff_buf + ch->ch_sniff_in, p, n); - - ch->ch_sniff_in += n; - p += n; - nbuf -= n; - - /* - * Wakeup any thread waiting for data - */ - if (ch->ch_sniff_flags & SNIFF_WAIT_DATA) { - ch->ch_sniff_flags &= ~SNIFF_WAIT_DATA; - wake_up_interruptible(&ch->ch_sniff_wait); - } - } - - /* - * If the user sent us too much data to push into our tmpbuf, - * we need to keep looping around on all the data. - */ - if (too_much_data) { - p = tmpbuf; - tmpbuflen = 0; - } - - } while (too_much_data); -} - - -/*======================================================================= - * - * dgap_input - Process received data. - * - * ch - Pointer to channel structure. - * - *=======================================================================*/ - -void dgap_input(struct channel_t *ch) -{ - struct board_t *bd; - struct bs_t *bs; - struct tty_struct *tp; - struct tty_ldisc *ld; - uint rmask; - uint head; - uint tail; - int data_len; - ulong lock_flags; - ulong lock_flags2; - int flip_len; - int len = 0; - int n = 0; - uchar *buf; - uchar tmpchar; - int s = 0; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - tp = ch->ch_tun.un_tty; - - bs = ch->ch_bs; - if (!bs) { - return; - } - - bd = ch->ch_bd; - if(!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - DPR_READ(("dgap_input start\n")); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - /* - * Figure the number of characters in the buffer. - * Exit immediately if none. - */ - - rmask = ch->ch_rsize - 1; - - head = readw(&(bs->rx_head)); - head &= rmask; - tail = readw(&(bs->rx_tail)); - tail &= rmask; - - data_len = (head - tail) & rmask; - - if (data_len == 0) { - writeb(1, &(bs->idata)); - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - DPR_READ(("No data on port %d\n", ch->ch_portnum)); - return; - } - - /* - * If the device is not open, or CREAD is off, flush - * input data and return immediately. - */ - if ((bd->state != BOARD_READY) || !tp || (tp->magic != TTY_MAGIC) || - !(ch->ch_tun.un_flags & UN_ISOPEN) || !(tp->termios.c_cflag & CREAD) || - (ch->ch_tun.un_flags & UN_CLOSING)) { - - DPR_READ(("input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum)); - DPR_READ(("input. tp: %p tp->magic: %x MAGIC:%x ch flags: %x\n", - tp, tp ? tp->magic : 0, TTY_MAGIC, ch->ch_tun.un_flags)); - writew(head, &(bs->rx_tail)); - writeb(1, &(bs->idata)); - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return; - } - - /* - * If we are throttled, simply don't read any data. - */ - if (ch->ch_flags & CH_RXBLOCK) { - writeb(1, &(bs->idata)); - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - DPR_READ(("Port %d throttled, not reading any data. head: %x tail: %x\n", - ch->ch_portnum, head, tail)); - return; - } - - /* - * Ignore oruns. - */ - tmpchar = readb(&(bs->orun)); - if (tmpchar) { - ch->ch_err_overrun++; - writeb(0, &(bs->orun)); - } - - DPR_READ(("dgap_input start 2\n")); - - /* Decide how much data we can send into the tty layer */ - flip_len = TTY_FLIPBUF_SIZE; - - /* Chop down the length, if needed */ - len = min(data_len, flip_len); - len = min(len, (N_TTY_BUF_SIZE - 1)); - - ld = tty_ldisc_ref(tp); - -#ifdef TTY_DONT_FLIP - /* - * If the DONT_FLIP flag is on, don't flush our buffer, and act - * like the ld doesn't have any space to put the data right now. - */ - if (test_bit(TTY_DONT_FLIP, &tp->flags)) - len = 0; -#endif - - /* - * If we were unable to get a reference to the ld, - * don't flush our buffer, and act like the ld doesn't - * have any space to put the data right now. - */ - if (!ld) { - len = 0; - } else { - /* - * If ld doesn't have a pointer to a receive_buf function, - * flush the data, then act like the ld doesn't have any - * space to put the data right now. - */ - if (!ld->ops->receive_buf) { - writew(head, &(bs->rx_tail)); - len = 0; - } - } - - if (len <= 0) { - writeb(1, &(bs->idata)); - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - DPR_READ(("dgap_input 1 - finish\n")); - if (ld) - tty_ldisc_deref(ld); - return; - } - - buf = ch->ch_bd->flipbuf; - n = len; - - /* - * n now contains the most amount of data we can copy, - * bounded either by our buffer size or the amount - * of data the card actually has pending... - */ - while (n) { - - s = ((head >= tail) ? head : ch->ch_rsize) - tail; - s = min(s, n); - - if (s <= 0) - break; - - memcpy_fromio(buf, (char *) ch->ch_raddr + tail, s); - dgap_sniff_nowait_nolock(ch, "USER READ", buf, s); - - tail += s; - buf += s; - - n -= s; - /* Flip queue if needed */ - tail &= rmask; - } - - writew(tail, &(bs->rx_tail)); - writeb(1, &(bs->idata)); - ch->ch_rxcount += len; - - /* - * If we are completely raw, we don't need to go through a lot - * of the tty layers that exist. - * In this case, we take the shortest and fastest route we - * can to relay the data to the user. - * - * On the other hand, if we are not raw, we need to go through - * the tty layer, which has its API more well defined. - */ - if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { - dgap_parity_scan(ch, ch->ch_bd->flipbuf, ch->ch_bd->flipflagbuf, &len); - - len = tty_buffer_request_room(tp->port, len); - tty_insert_flip_string_flags(tp->port, ch->ch_bd->flipbuf, - ch->ch_bd->flipflagbuf, len); - } - else { - len = tty_buffer_request_room(tp->port, len); - tty_insert_flip_string(tp->port, ch->ch_bd->flipbuf, len); - } - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - /* Tell the tty layer its okay to "eat" the data now */ - tty_flip_buffer_push(tp->port); - - if (ld) - tty_ldisc_deref(ld); - - DPR_READ(("dgap_input - finish\n")); -} - - -/************************************************************************ - * Determines when CARRIER changes state and takes appropriate - * action. - ************************************************************************/ -void dgap_carrier(struct channel_t *ch) -{ - struct board_t *bd; - - int virt_carrier = 0; - int phys_carrier = 0; - - DPR_CARR(("dgap_carrier called...\n")); - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - /* Make sure altpin is always set correctly */ - if (ch->ch_digi.digi_flags & DIGI_ALTPIN) { - ch->ch_dsr = DM_CD; - ch->ch_cd = DM_DSR; - } - else { - ch->ch_dsr = DM_DSR; - ch->ch_cd = DM_CD; - } - - if (ch->ch_mistat & D_CD(ch)) { - DPR_CARR(("mistat: %x D_CD: %x\n", ch->ch_mistat, D_CD(ch))); - phys_carrier = 1; - } - - if (ch->ch_digi.digi_flags & DIGI_FORCEDCD) { - virt_carrier = 1; - } - - if (ch->ch_c_cflag & CLOCAL) { - virt_carrier = 1; - } - - - DPR_CARR(("DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier)); - - /* - * Test for a VIRTUAL carrier transition to HIGH. - */ - if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { - - /* - * When carrier rises, wake any threads waiting - * for carrier in the open routine. - */ - - DPR_CARR(("carrier: virt DCD rose\n")); - - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - } - - /* - * Test for a PHYSICAL carrier transition to HIGH. - */ - if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { - - /* - * When carrier rises, wake any threads waiting - * for carrier in the open routine. - */ - - DPR_CARR(("carrier: physical DCD rose\n")); - - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - } - - /* - * Test for a PHYSICAL transition to low, so long as we aren't - * currently ignoring physical transitions (which is what "virtual - * carrier" indicates). - * - * The transition of the virtual carrier to low really doesn't - * matter... it really only means "ignore carrier state", not - * "make pretend that carrier is there". - */ - if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) && - (phys_carrier == 0)) - { - - /* - * When carrier drops: - * - * Drop carrier on all open units. - * - * Flush queues, waking up any task waiting in the - * line discipline. - * - * Send a hangup to the control terminal. - * - * Enable all select calls. - */ - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - - if (ch->ch_tun.un_open_count > 0) { - DPR_CARR(("Sending tty hangup\n")); - tty_hangup(ch->ch_tun.un_tty); - } - - if (ch->ch_pun.un_open_count > 0) { - DPR_CARR(("Sending pr hangup\n")); - tty_hangup(ch->ch_pun.un_tty); - } - } - - /* - * Make sure that our cached values reflect the current reality. - */ - if (virt_carrier == 1) - ch->ch_flags |= CH_FCAR; - else - ch->ch_flags &= ~CH_FCAR; - - if (phys_carrier == 1) - ch->ch_flags |= CH_CD; - else - ch->ch_flags &= ~CH_CD; -} - - -/************************************************************************ - * - * TTY Entry points and helper functions - * - ************************************************************************/ - -/* - * dgap_tty_open() - * - */ -static int dgap_tty_open(struct tty_struct *tty, struct file *file) -{ - struct board_t *brd; - struct channel_t *ch; - struct un_t *un; - struct bs_t *bs; - uint major = 0; - uint minor = 0; - int rc = 0; - ulong lock_flags; - ulong lock_flags2; - u16 head; - - rc = 0; - - major = MAJOR(tty_devnum(tty)); - minor = MINOR(tty_devnum(tty)); - - if (major > 255) { - return -ENXIO; - } - - /* Get board pointer from our array of majors we have allocated */ - brd = dgap_BoardsByMajor[major]; - if (!brd) { - return -ENXIO; - } - - /* - * If board is not yet up to a state of READY, go to - * sleep waiting for it to happen or they cancel the open. - */ - rc = wait_event_interruptible(brd->state_wait, - (brd->state & BOARD_READY)); - - if (rc) { - return rc; - } - - DGAP_LOCK(brd->bd_lock, lock_flags); - - /* The wait above should guarantee this cannot happen */ - if (brd->state != BOARD_READY) { - DGAP_UNLOCK(brd->bd_lock, lock_flags); - return -ENXIO; - } - - /* If opened device is greater than our number of ports, bail. */ - if (MINOR(tty_devnum(tty)) > brd->nasync) { - DGAP_UNLOCK(brd->bd_lock, lock_flags); - return -ENXIO; - } - - ch = brd->channels[minor]; - if (!ch) { - DGAP_UNLOCK(brd->bd_lock, lock_flags); - return -ENXIO; - } - - /* Grab channel lock */ - DGAP_LOCK(ch->ch_lock, lock_flags2); - - /* Figure out our type */ - if (major == brd->dgap_Serial_Major) { - un = &brd->channels[minor]->ch_tun; - un->un_type = DGAP_SERIAL; - } - else if (major == brd->dgap_TransparentPrint_Major) { - un = &brd->channels[minor]->ch_pun; - un->un_type = DGAP_PRINT; - } - else { - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(brd->bd_lock, lock_flags); - DPR_OPEN(("%d Unknown TYPE!\n", __LINE__)); - return -ENXIO; - } - - /* Store our unit into driver_data, so we always have it available. */ - tty->driver_data = un; - - DPR_OPEN(("Open called. MAJOR: %d MINOR:%d unit: %p NAME: %s\n", - MAJOR(tty_devnum(tty)), MINOR(tty_devnum(tty)), un, brd->name)); - - /* - * Error if channel info pointer is NULL. - */ - bs = ch->ch_bs; - if (!bs) { - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(brd->bd_lock, lock_flags); - DPR_OPEN(("%d BS is 0!\n", __LINE__)); - return -ENXIO; - } - - DPR_OPEN(("%d: tflag=%x pflag=%x\n", __LINE__, ch->ch_tun.un_flags, ch->ch_pun.un_flags)); - - /* - * Initialize tty's - */ - if (!(un->un_flags & UN_ISOPEN)) { - /* Store important variables. */ - un->un_tty = tty; - - /* Maybe do something here to the TTY struct as well? */ - } - - /* - * Initialize if neither terminal or printer is open. - */ - if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) { - - DPR_OPEN(("dgap_open: initializing channel in open...\n")); - - ch->ch_mforce = 0; - ch->ch_mval = 0; - - /* - * Flush input queue. - */ - head = readw(&(bs->rx_head)); - writew(head, &(bs->rx_tail)); - - ch->ch_flags = 0; - ch->pscan_state = 0; - ch->pscan_savechar = 0; - - ch->ch_c_cflag = tty->termios.c_cflag; - ch->ch_c_iflag = tty->termios.c_iflag; - ch->ch_c_oflag = tty->termios.c_oflag; - ch->ch_c_lflag = tty->termios.c_lflag; - ch->ch_startc = tty->termios.c_cc[VSTART]; - ch->ch_stopc = tty->termios.c_cc[VSTOP]; - - /* TODO: flush our TTY struct here? */ - } - - dgap_carrier(ch); - /* - * Run param in case we changed anything - */ - dgap_param(tty); - - /* - * follow protocol for opening port - */ - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(brd->bd_lock, lock_flags); - - rc = dgap_block_til_ready(tty, file, ch); - - if (!un->un_tty) { - return -ENODEV; - } - - if (rc) { - DPR_OPEN(("dgap_tty_open returning after dgap_block_til_ready " - "with %d\n", rc)); - } - - /* No going back now, increment our unit and channel counters */ - DGAP_LOCK(ch->ch_lock, lock_flags); - ch->ch_open_count++; - un->un_open_count++; - un->un_flags |= (UN_ISOPEN); - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - DPR_OPEN(("dgap_tty_open finished\n")); - return (rc); -} - - -/* - * dgap_block_til_ready() - * - * Wait for DCD, if needed. - */ -static int dgap_block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch) -{ - int retval = 0; - struct un_t *un = NULL; - ulong lock_flags; - uint old_flags = 0; - int sleep_on_un_flags = 0; - - if (!tty || tty->magic != TTY_MAGIC || !file || !ch || ch->magic != DGAP_CHANNEL_MAGIC) { - return (-ENXIO); - } - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) { - return (-ENXIO); - } - - DPR_OPEN(("dgap_block_til_ready - before block.\n")); - - DGAP_LOCK(ch->ch_lock, lock_flags); - - ch->ch_wopen++; - - /* Loop forever */ - while (1) { - - sleep_on_un_flags = 0; - - /* - * If board has failed somehow during our sleep, bail with error. - */ - if (ch->ch_bd->state == BOARD_FAILED) { - retval = -ENXIO; - break; - } - - /* If tty was hung up, break out of loop and set error. */ - if (tty_hung_up_p(file)) { - retval = -EAGAIN; - break; - } - - /* - * If either unit is in the middle of the fragile part of close, - * we just cannot touch the channel safely. - * Go back to sleep, knowing that when the channel can be - * touched safely, the close routine will signal the - * ch_wait_flags to wake us back up. - */ - if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_CLOSING)) { - - /* - * Our conditions to leave cleanly and happily: - * 1) NONBLOCKING on the tty is set. - * 2) CLOCAL is set. - * 3) DCD (fake or real) is active. - */ - - if (file->f_flags & O_NONBLOCK) { - break; - } - - if (tty->flags & (1 << TTY_IO_ERROR)) { - break; - } - - if (ch->ch_flags & CH_CD) { - DPR_OPEN(("%d: ch_flags: %x\n", __LINE__, ch->ch_flags)); - break; - } - - if (ch->ch_flags & CH_FCAR) { - DPR_OPEN(("%d: ch_flags: %x\n", __LINE__, ch->ch_flags)); - break; - } - } - else { - sleep_on_un_flags = 1; - } - - /* - * If there is a signal pending, the user probably - * interrupted (ctrl-c) us. - * Leave loop with error set. - */ - if (signal_pending(current)) { - DPR_OPEN(("%d: signal pending...\n", __LINE__)); - retval = -ERESTARTSYS; - break; - } - - DPR_OPEN(("dgap_block_til_ready - blocking.\n")); - - /* - * Store the flags before we let go of channel lock - */ - if (sleep_on_un_flags) - old_flags = ch->ch_tun.un_flags | ch->ch_pun.un_flags; - else - old_flags = ch->ch_flags; - - /* - * Let go of channel lock before calling schedule. - * Our poller will get any FEP events and wake us up when DCD - * eventually goes active. - */ - - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - DPR_OPEN(("Going to sleep on %s flags...\n", - (sleep_on_un_flags ? "un" : "ch"))); - - /* - * Wait for something in the flags to change from the current value. - */ - if (sleep_on_un_flags) { - retval = wait_event_interruptible(un->un_flags_wait, - (old_flags != (ch->ch_tun.un_flags | ch->ch_pun.un_flags))); - } - else { - retval = wait_event_interruptible(ch->ch_flags_wait, - (old_flags != ch->ch_flags)); - } - - DPR_OPEN(("After sleep... retval: %x\n", retval)); - - /* - * We got woken up for some reason. - * Before looping around, grab our channel lock. - */ - DGAP_LOCK(ch->ch_lock, lock_flags); - } - - ch->ch_wopen--; - - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - DPR_OPEN(("dgap_block_til_ready - after blocking.\n")); - - if (retval) { - DPR_OPEN(("dgap_block_til_ready - done. error. retval: %x\n", retval)); - return(retval); - } - - DPR_OPEN(("dgap_block_til_ready - done no error. jiffies: %lu\n", jiffies)); - - return(0); -} - - -/* - * dgap_tty_hangup() - * - * Hangup the port. Like a close, but don't wait for output to drain. - */ -static void dgap_tty_hangup(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - DPR_CLOSE(("dgap_hangup called. ch->ch_open_count: %d un->un_open_count: %d\n", - ch->ch_open_count, un->un_open_count)); - - /* flush the transmit queues */ - dgap_tty_flush_buffer(tty); - - DPR_CLOSE(("dgap_hangup finished. ch->ch_open_count: %d un->un_open_count: %d\n", - ch->ch_open_count, un->un_open_count)); -} - - - -/* - * dgap_tty_close() - * - */ -static void dgap_tty_close(struct tty_struct *tty, struct file *file) -{ - struct ktermios *ts; - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - int rc = 0; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - ts = &tty->termios; - - DPR_CLOSE(("Close called\n")); - - DGAP_LOCK(ch->ch_lock, lock_flags); - - /* - * Determine if this is the last close or not - and if we agree about - * which type of close it is with the Line Discipline - */ - if ((tty->count == 1) && (un->un_open_count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. un_open_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. - */ - APR(("tty->count is 1, un open count is %d\n", un->un_open_count)); - un->un_open_count = 1; - } - - if (--un->un_open_count < 0) { - APR(("bad serial port open count of %d\n", un->un_open_count)); - un->un_open_count = 0; - } - - ch->ch_open_count--; - - if (ch->ch_open_count && un->un_open_count) { - DPR_CLOSE(("dgap_tty_close: not last close ch: %d un:%d\n", - ch->ch_open_count, un->un_open_count)); - - DGAP_UNLOCK(ch->ch_lock, lock_flags); - return; - } - - /* OK, its the last close on the unit */ - DPR_CLOSE(("dgap_tty_close - last close on unit procedures\n")); - - un->un_flags |= UN_CLOSING; - - tty->closing = 1; - - /* - * Only officially close channel if count is 0 and - * DIGI_PRINTER bit is not set. - */ - if ((ch->ch_open_count == 0) && !(ch->ch_digi.digi_flags & DIGI_PRINTER)) { - - ch->ch_flags &= ~(CH_RXBLOCK); - - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - /* wait for output to drain */ - /* This will also return if we take an interrupt */ - - DPR_CLOSE(("Calling wait_for_drain\n")); - rc = dgap_wait_for_drain(tty); - DPR_CLOSE(("After calling wait_for_drain\n")); - - if (rc) { - DPR_BASIC(("dgap_tty_close - bad return: %d ", rc)); - } - - dgap_tty_flush_buffer(tty); - tty_ldisc_flush(tty); - - DGAP_LOCK(ch->ch_lock, lock_flags); - - tty->closing = 0; - - /* - * If we have HUPCL set, lower DTR and RTS - */ - if (ch->ch_c_cflag & HUPCL ) { - DPR_CLOSE(("Close. HUPCL set, dropping DTR/RTS\n")); - ch->ch_mostat &= ~(D_RTS(ch)|D_DTR(ch)); - dgap_cmdb( ch, SMODEM, 0, D_DTR(ch)|D_RTS(ch), 0 ); - - /* - * Go to sleep to ensure RTS/DTR - * have been dropped for modems to see it. - */ - if (ch->ch_close_delay) { - DPR_CLOSE(("Close. Sleeping for RTS/DTR drop\n")); - - DGAP_UNLOCK(ch->ch_lock, lock_flags); - dgap_ms_sleep(ch->ch_close_delay); - DGAP_LOCK(ch->ch_lock, lock_flags); - - DPR_CLOSE(("Close. After sleeping for RTS/DTR drop\n")); - } - } - - ch->pscan_state = 0; - ch->pscan_savechar = 0; - ch->ch_baud_info = 0; - - } - - /* - * turn off print device when closing print device. - */ - if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON) ) { - dgap_wmove(ch, ch->ch_digi.digi_offstr, - (int) ch->ch_digi.digi_offlen); - ch->ch_flags &= ~CH_PRON; - } - - un->un_tty = NULL; - un->un_flags &= ~(UN_ISOPEN | UN_CLOSING); - tty->driver_data = NULL; - - DPR_CLOSE(("Close. Doing wakeups\n")); - wake_up_interruptible(&ch->ch_flags_wait); - wake_up_interruptible(&un->un_flags_wait); - - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - DPR_BASIC(("dgap_tty_close - complete\n")); -} - - -/* - * dgap_tty_chars_in_buffer() - * - * Return number of characters that have not been transmitted yet. - * - * This routine is used by the line discipline to determine if there - * is data waiting to be transmitted/drained/flushed or not. - */ -static int dgap_tty_chars_in_buffer(struct tty_struct *tty) -{ - struct board_t *bd = NULL; - struct channel_t *ch = NULL; - struct un_t *un = NULL; - struct bs_t *bs = NULL; - uchar tbusy; - uint chars = 0; - u16 thead, ttail, tmask, chead, ctail; - ulong lock_flags = 0; - ulong lock_flags2 = 0; - - if (tty == NULL) - return(0); - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return (0); - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return (0); - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return (0); - - bs = ch->ch_bs; - if (!bs) - return (0); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - tmask = (ch->ch_tsize - 1); - - /* Get Transmit queue pointers */ - thead = readw(&(bs->tx_head)) & tmask; - ttail = readw(&(bs->tx_tail)) & tmask; - - /* Get tbusy flag */ - tbusy = readb(&(bs->tbusy)); - - /* Get Command queue pointers */ - chead = readw(&(ch->ch_cm->cm_head)); - ctail = readw(&(ch->ch_cm->cm_tail)); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - /* - * The only way we know for sure if there is no pending - * data left to be transferred, is if: - * 1) Transmit head and tail are equal (empty). - * 2) Command queue head and tail are equal (empty). - * 3) The "TBUSY" flag is 0. (Transmitter not busy). - */ - - if ((ttail == thead) && (tbusy == 0) && (chead == ctail)) { - chars = 0; - } - else { - if (thead >= ttail) - chars = thead - ttail; - else - chars = thead - ttail + ch->ch_tsize; - /* - * Fudge factor here. - * If chars is zero, we know that the command queue had - * something in it or tbusy was set. Because we cannot - * be sure if there is still some data to be transmitted, - * lets lie, and tell ld we have 1 byte left. - */ - if (chars == 0) { - /* - * If TBUSY is still set, and our tx buffers are empty, - * force the firmware to send me another wakeup after - * TBUSY has been cleared. - */ - if (tbusy != 0) { - DGAP_LOCK(ch->ch_lock, lock_flags); - un->un_flags |= UN_EMPTY; - writeb(1, &(bs->iempty)); - DGAP_UNLOCK(ch->ch_lock, lock_flags); - } - chars = 1; - } - } - - DPR_WRITE(("dgap_tty_chars_in_buffer. Port: %x - %d (head: %d tail: %d tsize: %d)\n", - ch->ch_portnum, chars, thead, ttail, ch->ch_tsize)); - return(chars); -} - - -static int dgap_wait_for_drain(struct tty_struct *tty) -{ - struct channel_t *ch; - struct un_t *un; - struct bs_t *bs; - int ret = -EIO; - uint count = 1; - ulong lock_flags = 0; - - if (!tty || tty->magic != TTY_MAGIC) - return ret; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return ret; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return ret; - - bs = ch->ch_bs; - if (!bs) - return ret; - - ret = 0; - - DPR_DRAIN(("dgap_wait_for_drain start\n")); - - /* Loop until data is drained */ - while (count != 0) { - - count = dgap_tty_chars_in_buffer(tty); - - if (count == 0) - break; - - /* Set flag waiting for drain */ - DGAP_LOCK(ch->ch_lock, lock_flags); - un->un_flags |= UN_EMPTY; - writeb(1, &(bs->iempty)); - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - /* Go to sleep till we get woken up */ - ret = wait_event_interruptible(un->un_flags_wait, ((un->un_flags & UN_EMPTY) == 0)); - /* If ret is non-zero, user ctrl-c'ed us */ - if (ret) { - break; - } - } - - DGAP_LOCK(ch->ch_lock, lock_flags); - un->un_flags &= ~(UN_EMPTY); - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - DPR_DRAIN(("dgap_wait_for_drain finish\n")); - return (ret); -} - - -/* - * dgap_maxcps_room - * - * Reduces bytes_available to the max number of characters - * that can be sent currently given the maxcps value, and - * returns the new bytes_available. This only affects printer - * output. - */ -static int dgap_maxcps_room(struct tty_struct *tty, int bytes_available) -{ - struct channel_t *ch = NULL; - struct un_t *un = NULL; - - if (tty == NULL) - return (bytes_available); - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return (bytes_available); - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return (bytes_available); - - /* - * If its not the Transparent print device, return - * the full data amount. - */ - if (un->un_type != DGAP_PRINT) - return (bytes_available); - - if (ch->ch_digi.digi_maxcps > 0 && ch->ch_digi.digi_bufsize > 0 ) { - int cps_limit = 0; - unsigned long current_time = jiffies; - unsigned long buffer_time = current_time + - (HZ * ch->ch_digi.digi_bufsize) / ch->ch_digi.digi_maxcps; - - if (ch->ch_cpstime < current_time) { - /* buffer is empty */ - ch->ch_cpstime = current_time; /* reset ch_cpstime */ - cps_limit = ch->ch_digi.digi_bufsize; - } - else if (ch->ch_cpstime < buffer_time) { - /* still room in the buffer */ - cps_limit = ((buffer_time - ch->ch_cpstime) * ch->ch_digi.digi_maxcps) / HZ; - } - else { - /* no room in the buffer */ - cps_limit = 0; - } - - bytes_available = min(cps_limit, bytes_available); - } - - return (bytes_available); -} - - -static inline void dgap_set_firmware_event(struct un_t *un, unsigned int event) -{ - struct channel_t *ch = NULL; - struct bs_t *bs = NULL; - - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - bs = ch->ch_bs; - if (!bs) - return; - - if ((event & UN_LOW) != 0) { - if ((un->un_flags & UN_LOW) == 0) { - un->un_flags |= UN_LOW; - writeb(1, &(bs->ilow)); - } - } - if ((event & UN_LOW) != 0) { - if ((un->un_flags & UN_EMPTY) == 0) { - un->un_flags |= UN_EMPTY; - writeb(1, &(bs->iempty)); - } - } -} - - -/* - * dgap_tty_write_room() - * - * Return space available in Tx buffer - */ -static int dgap_tty_write_room(struct tty_struct *tty) -{ - struct channel_t *ch = NULL; - struct un_t *un = NULL; - struct bs_t *bs = NULL; - u16 head, tail, tmask; - int ret = 0; - ulong lock_flags = 0; - - if (tty == NULL || dgap_TmpWriteBuf == NULL) - return(0); - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return (0); - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return (0); - - bs = ch->ch_bs; - if (!bs) - return (0); - - DGAP_LOCK(ch->ch_lock, lock_flags); - - tmask = ch->ch_tsize - 1; - head = readw(&(bs->tx_head)) & tmask; - tail = readw(&(bs->tx_tail)) & tmask; - - if ((ret = tail - head - 1) < 0) - ret += ch->ch_tsize; - - /* Limit printer to maxcps */ - ret = dgap_maxcps_room(tty, ret); - - /* - * If we are printer device, leave space for - * possibly both the on and off strings. - */ - if (un->un_type == DGAP_PRINT) { - if (!(ch->ch_flags & CH_PRON)) - ret -= ch->ch_digi.digi_onlen; - ret -= ch->ch_digi.digi_offlen; - } - else { - if (ch->ch_flags & CH_PRON) - ret -= ch->ch_digi.digi_offlen; - } - - if (ret < 0) - ret = 0; - - /* - * Schedule FEP to wake us up if needed. - * - * TODO: This might be overkill... - * Do we really need to schedule callbacks from the FEP - * in every case? Can we get smarter based on ret? - */ - dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - DPR_WRITE(("dgap_tty_write_room - %d tail: %d head: %d\n", ret, tail, head)); - - return(ret); -} - - -/* - * dgap_tty_put_char() - * - * Put a character into ch->ch_buf - * - * - used by the line discipline for OPOST processing - */ -static int dgap_tty_put_char(struct tty_struct *tty, unsigned char c) -{ - /* - * Simply call tty_write. - */ - DPR_WRITE(("dgap_tty_put_char called\n")); - dgap_tty_write(tty, &c, 1); - return 1; -} - - -/* - * dgap_tty_write() - * - * Take data from the user or kernel and send it out to the FEP. - * In here exists all the Transparent Print magic as well. - */ -static int dgap_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - struct channel_t *ch = NULL; - struct un_t *un = NULL; - struct bs_t *bs = NULL; - char *vaddr = NULL; - u16 head, tail, tmask, remain; - int bufcount = 0, n = 0; - int orig_count = 0; - ulong lock_flags; - int from_user = 0; - - if (tty == NULL || dgap_TmpWriteBuf == NULL) - return(0); - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return (0); - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return(0); - - bs = ch->ch_bs; - if (!bs) - return(0); - - if (!count) - return(0); - - DPR_WRITE(("dgap_tty_write: Port: %x tty=%p user=%d len=%d\n", - ch->ch_portnum, tty, from_user, count)); - - /* - * Store original amount of characters passed in. - * This helps to figure out if we should ask the FEP - * to send us an event when it has more space available. - */ - orig_count = count; - - DGAP_LOCK(ch->ch_lock, lock_flags); - - /* Get our space available for the channel from the board */ - tmask = ch->ch_tsize - 1; - head = readw(&(bs->tx_head)) & tmask; - tail = readw(&(bs->tx_tail)) & tmask; - - if ((bufcount = tail - head - 1) < 0) - bufcount += ch->ch_tsize; - - DPR_WRITE(("%d: bufcount: %x count: %x tail: %x head: %x tmask: %x\n", - __LINE__, bufcount, count, tail, head, tmask)); - - /* - * Limit printer output to maxcps overall, with bursts allowed - * up to bufsize characters. - */ - bufcount = dgap_maxcps_room(tty, bufcount); - - /* - * Take minimum of what the user wants to send, and the - * space available in the FEP buffer. - */ - count = min(count, bufcount); - - /* - * Bail if no space left. - */ - if (count <= 0) { - dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); - DGAP_UNLOCK(ch->ch_lock, lock_flags); - return(0); - } - - /* - * Output the printer ON string, if we are in terminal mode, but - * need to be in printer mode. - */ - if ((un->un_type == DGAP_PRINT) && !(ch->ch_flags & CH_PRON)) { - dgap_wmove(ch, ch->ch_digi.digi_onstr, - (int) ch->ch_digi.digi_onlen); - head = readw(&(bs->tx_head)) & tmask; - ch->ch_flags |= CH_PRON; - } - - /* - * On the other hand, output the printer OFF string, if we are - * currently in printer mode, but need to output to the terminal. - */ - if ((un->un_type != DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { - dgap_wmove(ch, ch->ch_digi.digi_offstr, - (int) ch->ch_digi.digi_offlen); - head = readw(&(bs->tx_head)) & tmask; - ch->ch_flags &= ~CH_PRON; - } - - /* - * If there is nothing left to copy, or I can't handle any more data, leave. - */ - if (count <= 0) { - dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); - DGAP_UNLOCK(ch->ch_lock, lock_flags); - return(0); - } - - if (from_user) { - - count = min(count, WRITEBUFLEN); - - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - /* - * If data is coming from user space, copy it into a temporary - * buffer so we don't get swapped out while doing the copy to - * the board. - */ - /* we're allowed to block if it's from_user */ - if (down_interruptible(&dgap_TmpWriteSem)) { - return (-EINTR); - } - - if (copy_from_user(dgap_TmpWriteBuf, (const uchar __user *) buf, count)) { - up(&dgap_TmpWriteSem); - printk("Write: Copy from user failed!\n"); - return -EFAULT; - } - - DGAP_LOCK(ch->ch_lock, lock_flags); - - buf = dgap_TmpWriteBuf; - } - - n = count; - - /* - * If the write wraps over the top of the circular buffer, - * move the portion up to the wrap point, and reset the - * pointers to the bottom. - */ - remain = ch->ch_tstart + ch->ch_tsize - head; - - if (n >= remain) { - n -= remain; - vaddr = ch->ch_taddr + head; - - memcpy_toio(vaddr, (uchar *) buf, remain); - dgap_sniff_nowait_nolock(ch, "USER WRITE", (uchar *) buf, remain); - - head = ch->ch_tstart; - buf += remain; - } - - if (n > 0) { - - /* - * Move rest of data. - */ - vaddr = ch->ch_taddr + head; - remain = n; - - memcpy_toio(vaddr, (uchar *) buf, remain); - dgap_sniff_nowait_nolock(ch, "USER WRITE", (uchar *) buf, remain); - - head += remain; - - } - - if (count) { - ch->ch_txcount += count; - head &= tmask; - writew(head, &(bs->tx_head)); - } - - - dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); - - /* - * If this is the print device, and the - * printer is still on, we need to turn it - * off before going idle. If the buffer is - * non-empty, wait until it goes empty. - * Otherwise turn it off right now. - */ - if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { - tail = readw(&(bs->tx_tail)) & tmask; - - if (tail != head) { - un->un_flags |= UN_EMPTY; - writeb(1, &(bs->iempty)); - } - else { - dgap_wmove(ch, ch->ch_digi.digi_offstr, - (int) ch->ch_digi.digi_offlen); - head = readw(&(bs->tx_head)) & tmask; - ch->ch_flags &= ~CH_PRON; - } - } - - /* Update printer buffer empty time. */ - if ((un->un_type == DGAP_PRINT) && (ch->ch_digi.digi_maxcps > 0) - && (ch->ch_digi.digi_bufsize > 0)) { - ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps; - } - - if (from_user) { - DGAP_UNLOCK(ch->ch_lock, lock_flags); - up(&dgap_TmpWriteSem); - } - else { - DGAP_UNLOCK(ch->ch_lock, lock_flags); - } - - DPR_WRITE(("Write finished - Write %d bytes of %d.\n", count, orig_count)); - - return (count); -} - - - -/* - * Return modem signals to ld. - */ -static int dgap_tty_tiocmget(struct tty_struct *tty) -{ - struct channel_t *ch; - struct un_t *un; - int result = -EIO; - uchar mstat = 0; - ulong lock_flags; - - if (!tty || tty->magic != TTY_MAGIC) - return result; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return result; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return result; - - DPR_IOCTL(("dgap_tty_tiocmget start\n")); - - DGAP_LOCK(ch->ch_lock, lock_flags); - - mstat = readb(&(ch->ch_bs->m_stat)); - /* Append any outbound signals that might be pending... */ - mstat |= ch->ch_mostat; - - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - result = 0; - - if (mstat & D_DTR(ch)) - result |= TIOCM_DTR; - if (mstat & D_RTS(ch)) - result |= TIOCM_RTS; - if (mstat & D_CTS(ch)) - result |= TIOCM_CTS; - if (mstat & D_DSR(ch)) - result |= TIOCM_DSR; - if (mstat & D_RI(ch)) - result |= TIOCM_RI; - if (mstat & D_CD(ch)) - result |= TIOCM_CD; - - DPR_IOCTL(("dgap_tty_tiocmget finish\n")); - - return result; -} - - -/* - * dgap_tty_tiocmset() - * - * Set modem signals, called by ld. - */ - -static int dgap_tty_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - int ret = -EIO; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return ret; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return ret; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return ret; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return ret; - - DPR_IOCTL(("dgap_tty_tiocmset start\n")); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - if (set & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval |= D_RTS(ch); - } - - if (set & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval |= D_DTR(ch); - } - - if (clear & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval &= ~(D_RTS(ch)); - } - - if (clear & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval &= ~(D_DTR(ch)); - } - - dgap_param(tty); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_tiocmset finish\n")); - - return (0); -} - - - -/* - * dgap_tty_send_break() - * - * Send a Break, called by ld. - */ -static int dgap_tty_send_break(struct tty_struct *tty, int msec) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - int ret = -EIO; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return ret; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return ret; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return ret; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return ret; - - switch (msec) { - case -1: - msec = 0xFFFF; - break; - case 0: - msec = 1; - break; - default: - msec /= 10; - break; - } - - DPR_IOCTL(("dgap_tty_send_break start 1. %lx\n", jiffies)); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); -#if 0 - dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); -#endif - dgap_cmdw(ch, SBREAK, (u16) msec, 0); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_send_break finish\n")); - - return (0); -} - - - - -/* - * dgap_tty_wait_until_sent() - * - * wait until data has been transmitted, called by ld. - */ -static void dgap_tty_wait_until_sent(struct tty_struct *tty, int timeout) -{ - int rc; - rc = dgap_wait_for_drain(tty); - if (rc) { - DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); - return; - } - return; -} - - - -/* - * dgap_send_xchar() - * - * send a high priority character, called by ld. - */ -static void dgap_tty_send_xchar(struct tty_struct *tty, char c) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - DPR_IOCTL(("dgap_tty_send_xchar start 1. %lx\n", jiffies)); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - /* - * This is technically what we should do. - * However, the NIST tests specifically want - * to see each XON or XOFF character that it - * sends, so lets just send each character - * by hand... - */ -#if 0 - if (c == STOP_CHAR(tty)) { - dgap_cmdw(ch, RPAUSE, 0, 0); - } - else if (c == START_CHAR(tty)) { - dgap_cmdw(ch, RRESUME, 0, 0); - } - else { - dgap_wmove(ch, &c, 1); - } -#else - dgap_wmove(ch, &c, 1); -#endif - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_send_xchar finish\n")); - - return; -} - - - - -/* - * Return modem signals to ld. - */ -static int dgap_get_modem_info(struct channel_t *ch, unsigned int __user *value) -{ - int result = 0; - uchar mstat = 0; - ulong lock_flags; - int rc = 0; - - DPR_IOCTL(("dgap_get_modem_info start\n")); - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return(-ENXIO); - - DGAP_LOCK(ch->ch_lock, lock_flags); - - mstat = readb(&(ch->ch_bs->m_stat)); - /* Append any outbound signals that might be pending... */ - mstat |= ch->ch_mostat; - - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - result = 0; - - if (mstat & D_DTR(ch)) - result |= TIOCM_DTR; - if (mstat & D_RTS(ch)) - result |= TIOCM_RTS; - if (mstat & D_CTS(ch)) - result |= TIOCM_CTS; - if (mstat & D_DSR(ch)) - result |= TIOCM_DSR; - if (mstat & D_RI(ch)) - result |= TIOCM_RI; - if (mstat & D_CD(ch)) - result |= TIOCM_CD; - - rc = put_user(result, value); - - DPR_IOCTL(("dgap_get_modem_info finish\n")); - return(rc); -} - - -/* - * dgap_set_modem_info() - * - * Set modem signals, called by ld. - */ -static int dgap_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int __user *value) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - int ret = -ENXIO; - unsigned int arg = 0; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return ret; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return ret; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return ret; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return ret; - - DPR_IOCTL(("dgap_set_modem_info() start\n")); - - ret = get_user(arg, value); - if (ret) { - DPR_IOCTL(("dgap_set_modem_info %d ret: %x. finished.\n", __LINE__, ret)); - return(ret); - } - - DPR_IOCTL(("dgap_set_modem_info: command: %x arg: %x\n", command, arg)); - - switch (command) { - case TIOCMBIS: - if (arg & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval |= D_RTS(ch); - } - - if (arg & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval |= D_DTR(ch); - } - - break; - - case TIOCMBIC: - if (arg & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval &= ~(D_RTS(ch)); - } - - if (arg & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval &= ~(D_DTR(ch)); - } - - break; - - case TIOCMSET: - ch->ch_mforce = D_DTR(ch)|D_RTS(ch); - - if (arg & TIOCM_RTS) { - ch->ch_mval |= D_RTS(ch); - } - else { - ch->ch_mval &= ~(D_RTS(ch)); - } - - if (arg & TIOCM_DTR) { - ch->ch_mval |= (D_DTR(ch)); - } - else { - ch->ch_mval &= ~(D_DTR(ch)); - } - - break; - - default: - return(-EINVAL); - } - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - dgap_param(tty); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_set_modem_info finish\n")); - - return (0); -} - - -/* - * dgap_tty_digigeta() - * - * Ioctl to get the information for ditty. - * - * - * - */ -static int dgap_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo) -{ - struct channel_t *ch; - struct un_t *un; - struct digi_t tmp; - ulong lock_flags; - - if (!retinfo) - return (-EFAULT); - - if (!tty || tty->magic != TTY_MAGIC) - return (-EFAULT); - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return (-EFAULT); - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return (-EFAULT); - - memset(&tmp, 0, sizeof(tmp)); - - DGAP_LOCK(ch->ch_lock, lock_flags); - memcpy(&tmp, &ch->ch_digi, sizeof(tmp)); - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return (-EFAULT); - - return (0); -} - - -/* - * dgap_tty_digiseta() - * - * Ioctl to set the information for ditty. - * - * - * - */ -static int dgap_tty_digiseta(struct tty_struct *tty, struct digi_t __user *new_info) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - struct digi_t new_digi; - ulong lock_flags = 0; - unsigned long lock_flags2; - - DPR_IOCTL(("DIGI_SETA start\n")); - - if (!tty || tty->magic != TTY_MAGIC) - return (-EFAULT); - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return (-EFAULT); - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return (-EFAULT); - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return (-EFAULT); - - if (copy_from_user(&new_digi, new_info, sizeof(struct digi_t))) { - DPR_IOCTL(("DIGI_SETA failed copy_from_user\n")); - return(-EFAULT); - } - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - memcpy(&ch->ch_digi, &new_digi, sizeof(struct digi_t)); - - if (ch->ch_digi.digi_maxcps < 1) - ch->ch_digi.digi_maxcps = 1; - - if (ch->ch_digi.digi_maxcps > 10000) - ch->ch_digi.digi_maxcps = 10000; - - if (ch->ch_digi.digi_bufsize < 10) - ch->ch_digi.digi_bufsize = 10; - - if (ch->ch_digi.digi_maxchar < 1) - ch->ch_digi.digi_maxchar = 1; - - if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize) - ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize; - - if (ch->ch_digi.digi_onlen > DIGI_PLEN) - ch->ch_digi.digi_onlen = DIGI_PLEN; - - if (ch->ch_digi.digi_offlen > DIGI_PLEN) - ch->ch_digi.digi_offlen = DIGI_PLEN; - - dgap_param(tty); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("DIGI_SETA finish\n")); - - return(0); -} - - -/* - * dgap_tty_digigetedelay() - * - * Ioctl to get the current edelay setting. - * - * - * - */ -static int dgap_tty_digigetedelay(struct tty_struct *tty, int __user *retinfo) -{ - struct channel_t *ch; - struct un_t *un; - int tmp; - ulong lock_flags; - - if (!retinfo) - return (-EFAULT); - - if (!tty || tty->magic != TTY_MAGIC) - return (-EFAULT); - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return (-EFAULT); - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return (-EFAULT); - - memset(&tmp, 0, sizeof(tmp)); - - DGAP_LOCK(ch->ch_lock, lock_flags); - tmp = readw(&(ch->ch_bs->edelay)); - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return (-EFAULT); - - return (0); -} - - -/* - * dgap_tty_digisetedelay() - * - * Ioctl to set the EDELAY setting - * - */ -static int dgap_tty_digisetedelay(struct tty_struct *tty, int __user *new_info) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - int new_digi; - ulong lock_flags; - ulong lock_flags2; - - DPR_IOCTL(("DIGI_SETA start\n")); - - if (!tty || tty->magic != TTY_MAGIC) - return (-EFAULT); - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return (-EFAULT); - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return (-EFAULT); - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return (-EFAULT); - - if (copy_from_user(&new_digi, new_info, sizeof(int))) { - DPR_IOCTL(("DIGI_SETEDELAY failed copy_from_user\n")); - return(-EFAULT); - } - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - writew((u16) new_digi, &(ch->ch_bs->edelay)); - - dgap_param(tty); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("DIGI_SETA finish\n")); - - return(0); -} - - -/* - * dgap_tty_digigetcustombaud() - * - * Ioctl to get the current custom baud rate setting. - */ -static int dgap_tty_digigetcustombaud(struct tty_struct *tty, int __user *retinfo) -{ - struct channel_t *ch; - struct un_t *un; - int tmp; - ulong lock_flags; - - if (!retinfo) - return (-EFAULT); - - if (!tty || tty->magic != TTY_MAGIC) - return (-EFAULT); - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return (-EFAULT); - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return (-EFAULT); - - memset(&tmp, 0, sizeof(tmp)); - - DGAP_LOCK(ch->ch_lock, lock_flags); - tmp = dgap_get_custom_baud(ch); - DGAP_UNLOCK(ch->ch_lock, lock_flags); - - DPR_IOCTL(("DIGI_GETCUSTOMBAUD. Returning %d\n", tmp)); - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return (-EFAULT); - - return (0); -} - - -/* - * dgap_tty_digisetcustombaud() - * - * Ioctl to set the custom baud rate setting - */ -static int dgap_tty_digisetcustombaud(struct tty_struct *tty, int __user *new_info) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - uint new_rate; - ulong lock_flags; - ulong lock_flags2; - - DPR_IOCTL(("DIGI_SETCUSTOMBAUD start\n")); - - if (!tty || tty->magic != TTY_MAGIC) - return (-EFAULT); - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return (-EFAULT); - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return (-EFAULT); - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return (-EFAULT); - - - if (copy_from_user(&new_rate, new_info, sizeof(unsigned int))) { - DPR_IOCTL(("DIGI_SETCUSTOMBAUD failed copy_from_user\n")); - return(-EFAULT); - } - - if (bd->bd_flags & BD_FEP5PLUS) { - - DPR_IOCTL(("DIGI_SETCUSTOMBAUD. Setting %d\n", new_rate)); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - ch->ch_custom_speed = new_rate; - - dgap_param(tty); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - } - - DPR_IOCTL(("DIGI_SETCUSTOMBAUD finish\n")); - - return(0); -} - - -/* - * dgap_set_termios() - */ -static void dgap_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - unsigned long lock_flags; - unsigned long lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - ch->ch_c_cflag = tty->termios.c_cflag; - ch->ch_c_iflag = tty->termios.c_iflag; - ch->ch_c_oflag = tty->termios.c_oflag; - ch->ch_c_lflag = tty->termios.c_lflag; - ch->ch_startc = tty->termios.c_cc[VSTART]; - ch->ch_stopc = tty->termios.c_cc[VSTOP]; - - dgap_carrier(ch); - dgap_param(tty); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); -} - - -static void dgap_tty_throttle(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - DPR_IOCTL(("dgap_tty_throttle start\n")); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - ch->ch_flags |= (CH_RXBLOCK); -#if 1 - dgap_cmdw(ch, RPAUSE, 0, 0); -#endif - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_throttle finish\n")); -} - - -static void dgap_tty_unthrottle(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - DPR_IOCTL(("dgap_tty_unthrottle start\n")); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - ch->ch_flags &= ~(CH_RXBLOCK); - -#if 1 - dgap_cmdw(ch, RRESUME, 0, 0); -#endif - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_unthrottle finish\n")); -} - - -static void dgap_tty_start(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - DPR_IOCTL(("dgap_tty_start start\n")); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - dgap_cmdw(ch, RESUMETX, 0, 0); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_start finish\n")); -} - - -static void dgap_tty_stop(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - DPR_IOCTL(("dgap_tty_stop start\n")); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - dgap_cmdw(ch, PAUSETX, 0, 0); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_stop finish\n")); -} - - -/* - * dgap_tty_flush_chars() - * - * Flush the cook buffer - * - * Note to self, and any other poor souls who venture here: - * - * flush in this case DOES NOT mean dispose of the data. - * instead, it means "stop buffering and send it if you - * haven't already." Just guess how I figured that out... SRW 2-Jun-98 - * - * It is also always called in interrupt context - JAR 8-Sept-99 - */ -static void dgap_tty_flush_chars(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - DPR_IOCTL(("dgap_tty_flush_chars start\n")); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - /* TODO: Do something here */ - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_flush_chars finish\n")); -} - - - -/* - * dgap_tty_flush_buffer() - * - * Flush Tx buffer (make in == out) - */ -static void dgap_tty_flush_buffer(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - u16 head = 0; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - DPR_IOCTL(("dgap_tty_flush_buffer on port: %d start\n", ch->ch_portnum)); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - ch->ch_flags &= ~CH_STOP; - head = readw(&(ch->ch_bs->tx_head)); - dgap_cmdw(ch, FLUSHTX, (u16) head, 0); - dgap_cmdw(ch, RESUMETX, 0, 0); - if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) { - ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY); - wake_up_interruptible(&ch->ch_tun.un_flags_wait); - } - if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) { - ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY); - wake_up_interruptible(&ch->ch_pun.un_flags_wait); - } - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - if (waitqueue_active(&tty->write_wait)) - wake_up_interruptible(&tty->write_wait); - tty_wakeup(tty); - - DPR_IOCTL(("dgap_tty_flush_buffer finish\n")); -} - - - -/***************************************************************************** - * - * The IOCTL function and all of its helpers - * - *****************************************************************************/ - -/* - * dgap_tty_ioctl() - * - * The usual assortment of ioctl's - */ -static int dgap_tty_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - int rc; - u16 head = 0; - ulong lock_flags = 0; - ulong lock_flags2 = 0; - void __user *uarg = (void __user *) arg; - - if (!tty || tty->magic != TTY_MAGIC) - return (-ENODEV); - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return (-ENODEV); - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return (-ENODEV); - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return (-ENODEV); - - DPR_IOCTL(("dgap_tty_ioctl start on port %d - cmd %s (%x), arg %lx\n", - ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - if (un->un_open_count <= 0) { - DPR_BASIC(("dgap_tty_ioctl - unit not open.\n")); - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(-EIO); - } - - switch (cmd) { - - /* Here are all the standard ioctl's that we MUST implement */ - - case TCSBRK: - /* - * TCSBRK is SVID version: non-zero arg --> no break - * this behaviour is exploited by tcdrain(). - * - * According to POSIX.1 spec (7.2.2.1.2) breaks should be - * between 0.25 and 0.5 seconds so we'll ask for something - * in the middle: 0.375 seconds. - */ - rc = tty_check_change(tty); - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - if (rc) { - return(rc); - } - - rc = dgap_wait_for_drain(tty); - - if (rc) { - DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); - return(-EINTR); - } - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - if(((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) { - dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); - } - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", - ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); - - return(0); - - - case TCSBRKP: - /* support for POSIX tcsendbreak() - - * According to POSIX.1 spec (7.2.2.1.2) breaks should be - * between 0.25 and 0.5 seconds so we'll ask for something - * in the middle: 0.375 seconds. - */ - rc = tty_check_change(tty); - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - if (rc) { - return(rc); - } - - rc = dgap_wait_for_drain(tty); - if (rc) { - DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); - return(-EINTR); - } - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", - ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); - - return(0); - - case TIOCSBRK: - /* - * FEP5 doesn't support turning on a break unconditionally. - * The FEP5 device will stop sending a break automatically - * after the specified time value that was sent when turning on - * the break. - */ - rc = tty_check_change(tty); - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - if (rc) { - return(rc); - } - - rc = dgap_wait_for_drain(tty); - if (rc) { - DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); - return(-EINTR); - } - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - - dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", - ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); - - return 0; - - case TIOCCBRK: - /* - * FEP5 doesn't support turning off a break unconditionally. - * The FEP5 device will stop sending a break automatically - * after the specified time value that was sent when turning on - * the break. - */ - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return 0; - - case TIOCGSOFTCAR: - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - rc = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) arg); - return(rc); - - case TIOCSSOFTCAR: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - rc = get_user(arg, (unsigned long __user *) arg); - if (rc) - return(rc); - - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - tty->termios.c_cflag = ((tty->termios.c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); - dgap_param(tty); - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - return(0); - - case TIOCMGET: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(dgap_get_modem_info(ch, uarg)); - - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(dgap_set_modem_info(tty, cmd, uarg)); - - /* - * Here are any additional ioctl's that we want to implement - */ - - case TCFLSH: - /* - * The linux tty driver doesn't have a flush - * input routine for the driver, assuming all backed - * up data is in the line disc. buffers. However, - * we all know that's not the case. Here, we - * act on the ioctl, but then lie and say we didn't - * so the line discipline will process the flush - * also. - */ - rc = tty_check_change(tty); - if (rc) { - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(rc); - } - - if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) { - if (!(un->un_type == DGAP_PRINT)) { - head = readw(&(ch->ch_bs->rx_head)); - writew(head, &(ch->ch_bs->rx_tail)); - writeb(0, &(ch->ch_bs->orun)); - } - } - - if ((arg == TCOFLUSH) || (arg == TCIOFLUSH)) { - ch->ch_flags &= ~CH_STOP; - head = readw(&(ch->ch_bs->tx_head)); - dgap_cmdw(ch, FLUSHTX, (u16) head, 0 ); - dgap_cmdw(ch, RESUMETX, 0, 0); - if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) { - ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY); - wake_up_interruptible(&ch->ch_tun.un_flags_wait); - } - if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) { - ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY); - wake_up_interruptible(&ch->ch_pun.un_flags_wait); - } - if (waitqueue_active(&tty->write_wait)) - wake_up_interruptible(&tty->write_wait); - - /* Can't hold any locks when calling tty_wakeup! */ - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - tty_wakeup(tty); - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - } - - /* pretend we didn't recognize this IOCTL */ - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_ioctl (LINE:%d) finish on port %d - cmd %s (%x), arg %lx\n", - __LINE__, ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); - - return(-ENOIOCTLCMD); - - case TCSETSF: - case TCSETSW: - /* - * The linux tty driver doesn't have a flush - * input routine for the driver, assuming all backed - * up data is in the line disc. buffers. However, - * we all know that's not the case. Here, we - * act on the ioctl, but then lie and say we didn't - * so the line discipline will process the flush - * also. - */ - if (cmd == TCSETSF) { - /* flush rx */ - ch->ch_flags &= ~CH_STOP; - head = readw(&(ch->ch_bs->rx_head)); - writew(head, &(ch->ch_bs->rx_tail)); - } - - /* now wait for all the output to drain */ - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - rc = dgap_wait_for_drain(tty); - if (rc) { - DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); - return(-EINTR); - } - - DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", - ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); - - /* pretend we didn't recognize this */ - return(-ENOIOCTLCMD); - - case TCSETAW: - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - rc = dgap_wait_for_drain(tty); - if (rc) { - DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); - return(-EINTR); - } - - /* pretend we didn't recognize this */ - return(-ENOIOCTLCMD); - - case TCXONC: - /* - * The Linux Line Discipline (LD) would do this for us if we - * let it, but we have the special firmware options to do this - * the "right way" regardless of hardware or software flow - * control so we'll do it outselves instead of letting the LD - * do it. - */ - rc = tty_check_change(tty); - if (rc) { - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(rc); - } - - DPR_IOCTL(("dgap_ioctl - in TCXONC - %d\n", cmd)); - switch (arg) { - - case TCOON: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - dgap_tty_start(tty); - return(0); - case TCOOFF: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - dgap_tty_stop(tty); - return(0); - case TCION: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - /* Make the ld do it */ - return(-ENOIOCTLCMD); - case TCIOFF: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - /* Make the ld do it */ - return(-ENOIOCTLCMD); - default: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(-EINVAL); - } - - case DIGI_GETA: - /* get information for ditty */ - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(dgap_tty_digigeta(tty, uarg)); - - case DIGI_SETAW: - case DIGI_SETAF: - - /* set information for ditty */ - if (cmd == (DIGI_SETAW)) { - - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - rc = dgap_wait_for_drain(tty); - if (rc) { - DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); - return(-EINTR); - } - DGAP_LOCK(bd->bd_lock, lock_flags); - DGAP_LOCK(ch->ch_lock, lock_flags2); - } - else { - tty_ldisc_flush(tty); - } - /* fall thru */ - - case DIGI_SETA: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(dgap_tty_digiseta(tty, uarg)); - - case DIGI_GEDELAY: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(dgap_tty_digigetedelay(tty, uarg)); - - case DIGI_SEDELAY: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(dgap_tty_digisetedelay(tty, uarg)); - - case DIGI_GETCUSTOMBAUD: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(dgap_tty_digigetcustombaud(tty, uarg)); - - case DIGI_SETCUSTOMBAUD: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return(dgap_tty_digisetcustombaud(tty, uarg)); - - case DIGI_RESET_PORT: - dgap_firmware_reset_port(ch); - dgap_param(tty); - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - return 0; - - default: - DGAP_UNLOCK(ch->ch_lock, lock_flags2); - DGAP_UNLOCK(bd->bd_lock, lock_flags); - - DPR_IOCTL(("dgap_tty_ioctl - in default\n")); - DPR_IOCTL(("dgap_tty_ioctl end - cmd %s (%x), arg %lx\n", - dgap_ioctl_name(cmd), cmd, arg)); - - return(-ENOIOCTLCMD); - } -} -- 1.8.1.4 _______________________________________________ devel mailing list devel@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel