/* * 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 * Neo and ClassicBoard PCI based product lines. * ************************************************************************ * */ #include #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 #include "dgnc_driver.h" #include "dgnc_tty.h" #include "dgnc_types.h" #include "dgnc_trace.h" #include "dgnc_neo.h" #include "dgnc_cls.h" #include "dpacompat.h" #include "dgnc_sysfs.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) #define init_MUTEX(sem) sema_init(sem, 1) #define DECLARE_MUTEX(name) \ struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1) #endif /* * internal variables */ static struct board_t *dgnc_BoardsByMajor[256]; static uchar *dgnc_TmpWriteBuf = NULL; static DECLARE_MUTEX(dgnc_TmpWriteSem); /* * Default transparent print information. */ static struct digi_t dgnc_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 DgncDefaultTermios = { .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 dgnc_tty_open(struct tty_struct *tty, struct file *file); static void dgnc_tty_close(struct tty_struct *tty, struct file *file); static int dgnc_block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch); static int dgnc_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static int dgnc_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo); static int dgnc_tty_digiseta(struct tty_struct *tty, struct digi_t __user *new_info); static int dgnc_tty_write_room(struct tty_struct *tty); static int dgnc_tty_put_char(struct tty_struct *tty, unsigned char c); static int dgnc_tty_chars_in_buffer(struct tty_struct *tty); static void dgnc_tty_start(struct tty_struct *tty); static void dgnc_tty_stop(struct tty_struct *tty); static void dgnc_tty_throttle(struct tty_struct *tty); static void dgnc_tty_unthrottle(struct tty_struct *tty); static void dgnc_tty_flush_chars(struct tty_struct *tty); static void dgnc_tty_flush_buffer(struct tty_struct *tty); static void dgnc_tty_hangup(struct tty_struct *tty); static int dgnc_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int __user *value); static int dgnc_get_modem_info(struct channel_t *ch, unsigned int __user *value); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) static int dgnc_tty_tiocmget(struct tty_struct *tty); static int dgnc_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); #else static int dgnc_tty_tiocmget(struct tty_struct *tty, struct file *file); static int dgnc_tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); #endif static int dgnc_tty_send_break(struct tty_struct *tty, int msec); static void dgnc_tty_wait_until_sent(struct tty_struct *tty, int timeout); static int dgnc_tty_write(struct tty_struct *tty, const unsigned char *buf, int count); static void dgnc_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios); static void dgnc_tty_send_xchar(struct tty_struct *tty, char ch); static const struct tty_operations dgnc_tty_ops = { .open = dgnc_tty_open, .close = dgnc_tty_close, .write = dgnc_tty_write, .write_room = dgnc_tty_write_room, .flush_buffer = dgnc_tty_flush_buffer, .chars_in_buffer = dgnc_tty_chars_in_buffer, .flush_chars = dgnc_tty_flush_chars, .ioctl = dgnc_tty_ioctl, .set_termios = dgnc_tty_set_termios, .stop = dgnc_tty_stop, .start = dgnc_tty_start, .throttle = dgnc_tty_throttle, .unthrottle = dgnc_tty_unthrottle, .hangup = dgnc_tty_hangup, .put_char = dgnc_tty_put_char, .tiocmget = dgnc_tty_tiocmget, .tiocmset = dgnc_tty_tiocmset, .break_ctl = dgnc_tty_send_break, .wait_until_sent = dgnc_tty_wait_until_sent, .send_xchar = dgnc_tty_send_xchar }; /************************************************************************ * * TTY Initialization/Cleanup Functions * ************************************************************************/ /* * dgnc_tty_preinit() * * Initialize any global tty related data before we download any boards. */ int dgnc_tty_preinit(void) { /* * Allocate a buffer for doing the copy from user space to * kernel space in dgnc_write(). 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. * * We are okay to sleep in the malloc, as this routine * is only called during module load, (not in interrupt context), * and with no locks held. */ dgnc_TmpWriteBuf = kmalloc(WRITEBUFLEN, GFP_KERNEL); if (!dgnc_TmpWriteBuf) { DPR_INIT(("unable to allocate tmp write buf")); return (-ENOMEM); } return(0); } /* * dgnc_tty_register() * * Init the tty subsystem for this board. */ int dgnc_tty_register(struct board_t *brd) { int rc = 0; DPR_INIT(("tty_register start\n")); memset(&brd->SerialDriver, 0, sizeof(struct tty_driver)); memset(&brd->PrintDriver, 0, sizeof(struct tty_driver)); brd->SerialDriver.magic = TTY_DRIVER_MAGIC; snprintf(brd->SerialName, MAXTTYNAMELEN, "tty_dgnc_%d_", brd->boardnum); brd->SerialDriver.name = brd->SerialName; brd->SerialDriver.name_base = 0; brd->SerialDriver.major = 0; brd->SerialDriver.minor_start = 0; brd->SerialDriver.num = brd->maxports; brd->SerialDriver.type = TTY_DRIVER_TYPE_SERIAL; brd->SerialDriver.subtype = SERIAL_TYPE_NORMAL; brd->SerialDriver.init_termios = DgncDefaultTermios; 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_struct's and termios's. */ brd->SerialDriver.ttys = kzalloc(brd->maxports * sizeof(struct tty_struct *), GFP_KERNEL); if (!brd->SerialDriver.ttys) return(-ENOMEM); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) brd->SerialDriver.refcount = brd->TtyRefCnt; #else kref_init(&brd->SerialDriver.kref); #endif brd->SerialDriver.termios = kzalloc(brd->maxports * sizeof(struct ktermios *), GFP_KERNEL); if (!brd->SerialDriver.termios) return(-ENOMEM); #if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) brd->SerialDriver.termios_locked = kzalloc(brd->maxports * sizeof(struct ktermios *), GFP_KERNEL); if (!brd->SerialDriver.termios_locked) return(-ENOMEM); #endif /* * Entry points for driver. Called by the kernel from * tty_io.c and n_tty.c. */ tty_set_operations(&brd->SerialDriver, &dgnc_tty_ops); if (!brd->dgnc_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->dgnc_Major_Serial_Registered = TRUE; } /* * If we're doing transparent print, we have to do all of the above * again, seperately so we don't get the LD confused about what major * we are when we get into the dgnc_tty_open() routine. */ brd->PrintDriver.magic = TTY_DRIVER_MAGIC; snprintf(brd->PrintName, MAXTTYNAMELEN, "pr_dgnc_%d_", brd->boardnum); brd->PrintDriver.name = brd->PrintName; brd->PrintDriver.name_base = 0; brd->PrintDriver.major = brd->SerialDriver.major; brd->PrintDriver.minor_start = 0x80; brd->PrintDriver.num = brd->maxports; brd->PrintDriver.type = TTY_DRIVER_TYPE_SERIAL; brd->PrintDriver.subtype = SERIAL_TYPE_NORMAL; brd->PrintDriver.init_termios = DgncDefaultTermios; 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_struct's and termios's. Must be seperate from * the Serial Driver so we don't get confused */ brd->PrintDriver.ttys = kzalloc(brd->maxports * sizeof(struct tty_struct *), GFP_KERNEL); if (!brd->PrintDriver.ttys) return(-ENOMEM); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) brd->PrintDriver.refcount = brd->TtyRefCnt; #else kref_init(&brd->PrintDriver.kref); #endif brd->PrintDriver.termios = kzalloc(brd->maxports * sizeof(struct ktermios *), GFP_KERNEL); if (!brd->PrintDriver.termios) return(-ENOMEM); #if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) brd->PrintDriver.termios_locked = kzalloc(brd->maxports * sizeof(struct ktermios *), GFP_KERNEL); if (!brd->PrintDriver.termios_locked) return(-ENOMEM); #endif /* * Entry points for driver. Called by the kernel from * tty_io.c and n_tty.c. */ tty_set_operations(&brd->PrintDriver, &dgnc_tty_ops); if (!brd->dgnc_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->dgnc_Major_TransparentPrint_Registered = TRUE; } dgnc_BoardsByMajor[brd->SerialDriver.major] = brd; brd->dgnc_Serial_Major = brd->SerialDriver.major; brd->dgnc_TransparentPrint_Major = brd->PrintDriver.major; DPR_INIT(("DGNC REGISTER TTY: MAJOR: %d\n", brd->SerialDriver.major)); return (rc); } /* * dgnc_tty_init() * * Init the tty subsystem. Called once per board after board has been * downloaded and init'ed. */ int dgnc_tty_init(struct board_t *brd) { int i; void __iomem *vaddr; struct channel_t *ch; if (!brd) return (-ENXIO); DPR_INIT(("dgnc_tty_init start\n")); /* * Initialize board structure elements. */ vaddr = brd->re_map_membase; brd->nasync = brd->maxports; /* * 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]) { /* * Okay to malloc with GFP_KERNEL, we are not at * interrupt context, and there are no locks held. */ brd->channels[i] = kzalloc(sizeof(struct channel_t), GFP_KERNEL); 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; /* Set up channel variables */ for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { if (!brd->channels[i]) continue; DGNC_SPINLOCK_INIT(ch->ch_lock); /* Store all our magic numbers */ ch->magic = DGNC_CHANNEL_MAGIC; ch->ch_tun.magic = DGNC_UNIT_MAGIC; ch->ch_tun.un_ch = ch; ch->ch_tun.un_type = DGNC_SERIAL; ch->ch_tun.un_dev = i; ch->ch_pun.magic = DGNC_UNIT_MAGIC; ch->ch_pun.un_ch = ch; ch->ch_pun.un_type = DGNC_PRINT; ch->ch_pun.un_dev = i + 128; if (brd->bd_uart_offset == 0x200) ch->ch_neo_uart = vaddr + (brd->bd_uart_offset * i); else ch->ch_cls_uart = vaddr + (brd->bd_uart_offset * i); ch->ch_bd = brd; ch->ch_portnum = i; ch->ch_digi = dgnc_digi_init; /* .25 second delay */ ch->ch_close_delay = 250; 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); { struct device *classp; classp = tty_register_device(&brd->SerialDriver, i, &(ch->ch_bd->pdev->dev)); ch->ch_tun.un_sysfs = classp; dgnc_create_tty_sysfs(&ch->ch_tun, classp); classp = tty_register_device(&brd->PrintDriver, i, &(ch->ch_bd->pdev->dev)); ch->ch_pun.un_sysfs = classp; dgnc_create_tty_sysfs(&ch->ch_pun, classp); } } DPR_INIT(("dgnc_tty_init finish\n")); return (0); } /* * dgnc_tty_post_uninit() * * UnInitialize any global tty related data. */ void dgnc_tty_post_uninit(void) { if (dgnc_TmpWriteBuf) { kfree(dgnc_TmpWriteBuf); dgnc_TmpWriteBuf = NULL; } } /* * dgnc_tty_uninit() * * Uninitialize the TTY portion of this driver. Free all memory and * resources. */ void dgnc_tty_uninit(struct board_t *brd) { int i = 0; if (brd->dgnc_Major_Serial_Registered) { dgnc_BoardsByMajor[brd->SerialDriver.major] = NULL; brd->dgnc_Serial_Major = 0; for (i = 0; i < brd->nasync; i++) { dgnc_remove_tty_sysfs(brd->channels[i]->ch_tun.un_sysfs); tty_unregister_device(&brd->SerialDriver, i); } tty_unregister_driver(&brd->SerialDriver); brd->dgnc_Major_Serial_Registered = FALSE; } if (brd->dgnc_Major_TransparentPrint_Registered) { dgnc_BoardsByMajor[brd->PrintDriver.major] = NULL; brd->dgnc_TransparentPrint_Major = 0; for (i = 0; i < brd->nasync; i++) { dgnc_remove_tty_sysfs(brd->channels[i]->ch_pun.un_sysfs); tty_unregister_device(&brd->PrintDriver, i); } tty_unregister_driver(&brd->PrintDriver); brd->dgnc_Major_TransparentPrint_Registered = FALSE; } if (brd->SerialDriver.ttys) { kfree(brd->SerialDriver.ttys); brd->SerialDriver.ttys = NULL; } if (brd->PrintDriver.ttys) { kfree(brd->PrintDriver.ttys); brd->PrintDriver.ttys = NULL; } } #define TMPBUFLEN (1024) /* * dgnc_sniff - Dump data out to the "sniff" buffer if the * proc sniff file is opened... */ void dgnc_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 != 0) { /* * 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); } /*======================================================================= * * dgnc_wmove - Write data to transmit queue. * * ch - Pointer to channel structure. * buf - Poiter to characters to be moved. * n - Number of characters to move. * *=======================================================================*/ static void dgnc_wmove(struct channel_t *ch, char *buf, uint n) { int remain; uint head; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; head = ch->ch_w_head & WQUEUEMASK; /* * 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 = WQUEUESIZE - head; if (n >= remain) { n -= remain; memcpy(ch->ch_wqueue + head, buf, remain); head = 0; buf += remain; } if (n > 0) { /* * Move rest of data. */ remain = n; memcpy(ch->ch_wqueue + head, buf, remain); head += remain; } head &= WQUEUEMASK; ch->ch_w_head = head; } /*======================================================================= * * dgnc_input - Process received data. * * ch - Pointer to channel structure. * *=======================================================================*/ void dgnc_input(struct channel_t *ch) { struct board_t *bd; struct tty_struct *tp; struct tty_ldisc *ld; uint rmask; ushort head; ushort tail; int data_len; ulong lock_flags; int flip_len; int len = 0; int n = 0; int s = 0; int i = 0; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; tp = ch->ch_tun.un_tty; bd = ch->ch_bd; if(!bd || bd->magic != DGNC_BOARD_MAGIC) return; DGNC_LOCK(ch->ch_lock, lock_flags); /* * Figure the number of characters in the buffer. * Exit immediately if none. */ rmask = RQUEUEMASK; head = ch->ch_r_head & rmask; tail = ch->ch_r_tail & rmask; data_len = (head - tail) & rmask; if (data_len == 0) { DGNC_UNLOCK(ch->ch_lock, lock_flags); return; } DPR_READ(("dgnc_input start\n")); /* * If the device is not open, or CREAD is off, * flush input data and return immediately. */ if (!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)); ch->ch_r_head = tail; /* Force queue flow control to be released, if needed */ dgnc_check_queue_flow_control(ch); DGNC_UNLOCK(ch->ch_lock, lock_flags); return; } /* * If we are throttled, simply don't read any data. */ if (ch->ch_flags & CH_FORCED_STOPI) { DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_READ(("Port %d throttled, not reading any data. head: %x tail: %x\n", ch->ch_portnum, head, tail)); return; } DPR_READ(("dgnc_input start 2\n")); 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) { ch->ch_r_head = ch->ch_r_tail; len = 0; } } if (len <= 0) { DGNC_UNLOCK(ch->ch_lock, lock_flags); if (ld) tty_ldisc_deref(ld); return; } /* * The tty layer in the kernel has changed in 2.6.16+. * * The flip buffers in the tty structure are no longer exposed, * and probably will be going away eventually. * * 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 new 2.6.16+ tty layer, which has its API more well defined. */ len = tty_buffer_request_room(tp->port, len); n = len; /* * n now contains the most amount of data we can copy, * bounded either by how much the Linux tty layer can handle, * or the amount of data the card actually has pending... */ while (n) { s = ((head >= tail) ? head : RQUEUESIZE) - tail; s = min(s, n); if (s <= 0) break; /* * If conditions are such that ld needs to see all * UART errors, we will have to walk each character * and error byte and send them to the buffer one at * a time. */ if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { for (i = 0; i < s; i++) { if (*(ch->ch_equeue + tail + i) & UART_LSR_BI) tty_insert_flip_char(tp->port, *(ch->ch_rqueue + tail + i), TTY_BREAK); else if (*(ch->ch_equeue + tail + i) & UART_LSR_PE) tty_insert_flip_char(tp->port, *(ch->ch_rqueue + tail + i), TTY_PARITY); else if (*(ch->ch_equeue + tail + i) & UART_LSR_FE) tty_insert_flip_char(tp->port, *(ch->ch_rqueue + tail + i), TTY_FRAME); else tty_insert_flip_char(tp->port, *(ch->ch_rqueue + tail + i), TTY_NORMAL); } } else { tty_insert_flip_string(tp->port, ch->ch_rqueue + tail, s); } dgnc_sniff_nowait_nolock(ch, "USER READ", ch->ch_rqueue + tail, s); tail += s; n -= s; /* Flip queue if needed */ tail &= rmask; } ch->ch_r_tail = tail & rmask; ch->ch_e_tail = tail & rmask; dgnc_check_queue_flow_control(ch); DGNC_UNLOCK(ch->ch_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(("dgnc_input - finish\n")); } /************************************************************************ * Determines when CARRIER changes state and takes appropriate * action. ************************************************************************/ void dgnc_carrier(struct channel_t *ch) { struct board_t *bd; int virt_carrier = 0; int phys_carrier = 0; DPR_CARR(("dgnc_carrier called...\n")); if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return; if (ch->ch_mistat & UART_MSR_DCD) { DPR_CARR(("mistat: %x D_CD: %x\n", ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD)); 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; } /* * Assign the custom baud rate to the channel structure */ static void dgnc_set_custom_speed(struct channel_t *ch, uint newrate) { int testdiv; int testrate_high; int testrate_low; int deltahigh; int deltalow; if (newrate < 0) newrate = 0; /* * Since the divisor is stored in a 16-bit integer, we make sure * we don't allow any rates smaller than a 16-bit integer would allow. * And of course, rates above the dividend won't fly. */ if (newrate && newrate < ((ch->ch_bd->bd_dividend / 0xFFFF) + 1)) newrate = ((ch->ch_bd->bd_dividend / 0xFFFF) + 1); if (newrate && newrate > ch->ch_bd->bd_dividend) newrate = ch->ch_bd->bd_dividend; while (newrate > 0) { testdiv = ch->ch_bd->bd_dividend / newrate; /* * If we try to figure out what rate the board would use * with the test divisor, it will be either equal or higher * than the requested baud rate. If we then determine the * rate with a divisor one higher, we will get the next lower * supported rate below the requested. */ testrate_high = ch->ch_bd->bd_dividend / testdiv; testrate_low = ch->ch_bd->bd_dividend / (testdiv + 1); /* * If the rate for the requested divisor is correct, just * use it and be done. */ if (testrate_high == newrate ) break; /* * Otherwise, pick the rate that is closer (i.e. whichever rate * has a smaller delta). */ deltahigh = testrate_high - newrate; deltalow = newrate - testrate_low; if (deltahigh < deltalow) { newrate = testrate_high; } else { newrate = testrate_low; } break; } ch->ch_custom_speed = newrate; return; } void dgnc_check_queue_flow_control(struct channel_t *ch) { int qleft = 0; /* Store how much space we have left in the queue */ if ((qleft = ch->ch_r_tail - ch->ch_r_head - 1) < 0) qleft += RQUEUEMASK + 1; /* * Check to see if we should enforce flow control on our queue because * the ld (or user) isn't reading data out of our queue fast enuf. * * NOTE: This is done based on what the current flow control of the * port is set for. * * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt. * This will cause the UART's FIFO to back up, and force * the RTS signal to be dropped. * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to * the other side, in hopes it will stop sending data to us. * 3) NONE - Nothing we can do. We will simply drop any extra data * that gets sent into us when the queue fills up. */ if (qleft < 256) { /* HWFLOW */ if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS) { if(!(ch->ch_flags & CH_RECEIVER_OFF)) { ch->ch_bd->bd_ops->disable_receiver(ch); ch->ch_flags |= (CH_RECEIVER_OFF); DPR_READ(("Internal queue hit hilevel mark (%d)! Turning off interrupts.\n", qleft)); } } /* SWFLOW */ else if (ch->ch_c_iflag & IXOFF) { if (ch->ch_stops_sent <= MAX_STOPS_SENT) { ch->ch_bd->bd_ops->send_stop_character(ch); ch->ch_stops_sent++; DPR_READ(("Sending stop char! Times sent: %x\n", ch->ch_stops_sent)); } } /* No FLOW */ else { /* Empty... Can't do anything about the impending overflow... */ } } /* * Check to see if we should unenforce flow control because * ld (or user) finally read enuf data out of our queue. * * NOTE: This is done based on what the current flow control of the * port is set for. * * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt. * This will cause the UART's FIFO to raise RTS back up, * which will allow the other side to start sending data again. * 2) SWFLOW (IXOFF) - Send a start character to * the other side, so it will start sending data to us again. * 3) NONE - Do nothing. Since we didn't do anything to turn off the * other side, we don't need to do anything now. */ if (qleft > (RQUEUESIZE / 2)) { /* HWFLOW */ if (ch->ch_digi.digi_flags & RTSPACE || ch->ch_c_cflag & CRTSCTS) { if (ch->ch_flags & CH_RECEIVER_OFF) { ch->ch_bd->bd_ops->enable_receiver(ch); ch->ch_flags &= ~(CH_RECEIVER_OFF); DPR_READ(("Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n", qleft)); } } /* SWFLOW */ else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) { ch->ch_stops_sent = 0; ch->ch_bd->bd_ops->send_start_character(ch); DPR_READ(("Sending start char!\n")); } /* No FLOW */ else { /* Nothing needed. */ } } } void dgnc_wakeup_writes(struct channel_t *ch) { int qlen = 0; ulong lock_flags; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; DGNC_LOCK(ch->ch_lock, lock_flags); /* * If channel now has space, wake up anyone waiting on the condition. */ if ((qlen = ch->ch_w_head - ch->ch_w_tail) < 0) qlen += WQUEUESIZE; if (qlen >= (WQUEUESIZE - 256)) { DGNC_UNLOCK(ch->ch_lock, lock_flags); return; } if (ch->ch_tun.un_flags & UN_ISOPEN) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) if ((ch->ch_tun.un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && ch->ch_tun.un_tty->ldisc->ops->write_wakeup) { DGNC_UNLOCK(ch->ch_lock, lock_flags); (ch->ch_tun.un_tty->ldisc->ops->write_wakeup)(ch->ch_tun.un_tty); DGNC_LOCK(ch->ch_lock, lock_flags); } #else if ((ch->ch_tun.un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && ch->ch_tun.un_tty->ldisc.ops->write_wakeup) { DGNC_UNLOCK(ch->ch_lock, lock_flags); (ch->ch_tun.un_tty->ldisc.ops->write_wakeup)(ch->ch_tun.un_tty); DGNC_LOCK(ch->ch_lock, lock_flags); } #endif wake_up_interruptible(&ch->ch_tun.un_tty->write_wait); /* * If unit is set to wait until empty, check to make sure * the queue AND FIFO are both empty. */ if (ch->ch_tun.un_flags & UN_EMPTY) { if ((qlen == 0) && (ch->ch_bd->bd_ops->get_uart_bytes_left(ch) == 0)) { ch->ch_tun.un_flags &= ~(UN_EMPTY); /* * If RTS Toggle mode is on, whenever * the queue and UART is empty, keep RTS low. */ if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) { ch->ch_mostat &= ~(UART_MCR_RTS); ch->ch_bd->bd_ops->assert_modem_signals(ch); } /* * If DTR Toggle mode is on, whenever * the queue and UART is empty, keep DTR low. */ if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) { ch->ch_mostat &= ~(UART_MCR_DTR); ch->ch_bd->bd_ops->assert_modem_signals(ch); } } } wake_up_interruptible(&ch->ch_tun.un_flags_wait); } if (ch->ch_pun.un_flags & UN_ISOPEN) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) if ((ch->ch_pun.un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && ch->ch_pun.un_tty->ldisc->ops->write_wakeup) { DGNC_UNLOCK(ch->ch_lock, lock_flags); (ch->ch_pun.un_tty->ldisc->ops->write_wakeup)(ch->ch_pun.un_tty); DGNC_LOCK(ch->ch_lock, lock_flags); } #else if ((ch->ch_pun.un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && ch->ch_pun.un_tty->ldisc.ops->write_wakeup) { DGNC_UNLOCK(ch->ch_lock, lock_flags); (ch->ch_pun.un_tty->ldisc.ops->write_wakeup)(ch->ch_pun.un_tty); DGNC_LOCK(ch->ch_lock, lock_flags); } #endif wake_up_interruptible(&ch->ch_pun.un_tty->write_wait); /* * If unit is set to wait until empty, check to make sure * the queue AND FIFO are both empty. */ if (ch->ch_pun.un_flags & UN_EMPTY) { if ((qlen == 0) && (ch->ch_bd->bd_ops->get_uart_bytes_left(ch) == 0)) { ch->ch_pun.un_flags &= ~(UN_EMPTY); } } wake_up_interruptible(&ch->ch_pun.un_flags_wait); } DGNC_UNLOCK(ch->ch_lock, lock_flags); } /************************************************************************ * * TTY Entry points and helper functions * ************************************************************************/ /* * dgnc_tty_open() * */ static int dgnc_tty_open(struct tty_struct *tty, struct file *file) { struct board_t *brd; struct channel_t *ch; struct un_t *un; uint major = 0; uint minor = 0; int rc = 0; ulong lock_flags; 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 = dgnc_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; } DGNC_LOCK(brd->bd_lock, lock_flags); /* If opened device is greater than our number of ports, bail. */ if (PORT_NUM(minor) > brd->nasync) { DGNC_UNLOCK(brd->bd_lock, lock_flags); return -ENXIO; } ch = brd->channels[PORT_NUM(minor)]; if (!ch) { DGNC_UNLOCK(brd->bd_lock, lock_flags); return -ENXIO; } /* Drop board lock */ DGNC_UNLOCK(brd->bd_lock, lock_flags); /* Grab channel lock */ DGNC_LOCK(ch->ch_lock, lock_flags); /* Figure out our type */ if (!IS_PRINT(minor)) { un = &brd->channels[PORT_NUM(minor)]->ch_tun; un->un_type = DGNC_SERIAL; } else if (IS_PRINT(minor)) { un = &brd->channels[PORT_NUM(minor)]->ch_pun; un->un_type = DGNC_PRINT; } else { DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_OPEN(("%d Unknown TYPE!\n", __LINE__)); return -ENXIO; } /* * If the port is still in a previous open, and in a state * where we simply cannot safely keep going, wait until the * state clears. */ DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = wait_event_interruptible(ch->ch_flags_wait, ((ch->ch_flags & CH_OPENING) == 0)); /* If ret is non-zero, user ctrl-c'ed us */ if (rc) { DPR_OPEN(("%d User ctrl c'ed\n", __LINE__)); return -EINTR; } /* * If either unit is in the middle of the fragile part of close, * we just cannot touch the channel safely. * Go to sleep, knowing that when the channel can be * touched safely, the close routine will signal the * ch_flags_wait to wake us back up. */ rc = wait_event_interruptible(ch->ch_flags_wait, (((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_CLOSING) == 0)); /* If ret is non-zero, user ctrl-c'ed us */ if (rc) { DPR_OPEN(("%d User ctrl c'ed\n", __LINE__)); return -EINTR; } DGNC_LOCK(ch->ch_lock, lock_flags); /* Store our unit into driver_data, so we always have it available. */ tty->driver_data = un; DPR_OPEN(("Open called. MAJOR: %d MINOR:%d PORT_NUM: %x unit: %p NAME: %s\n", MAJOR(tty_devnum(tty)), MINOR(tty_devnum(tty)), PORT_NUM(minor), un, brd->name)); 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? */ } /* * Allocate channel buffers for read/write/error. * Set flag, so we don't get trounced on. */ ch->ch_flags |= (CH_OPENING); /* Drop locks, as malloc with GFP_KERNEL can sleep */ DGNC_UNLOCK(ch->ch_lock, lock_flags); if (!ch->ch_rqueue) ch->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL); if (!ch->ch_equeue) ch->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL); if (!ch->ch_wqueue) ch->ch_wqueue = kzalloc(WQUEUESIZE, GFP_KERNEL); DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_flags &= ~(CH_OPENING); wake_up_interruptible(&ch->ch_flags_wait); /* * Initialize if neither terminal or printer is open. */ if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) { DPR_OPEN(("dgnc_open: initializing channel in open...\n")); /* * Flush input queues. */ ch->ch_r_head = ch->ch_r_tail = 0; ch->ch_e_head = ch->ch_e_tail = 0; ch->ch_w_head = ch->ch_w_tail = 0; brd->bd_ops->flush_uart_write(ch); brd->bd_ops->flush_uart_read(ch); ch->ch_flags = 0; ch->ch_cached_lsr = 0; ch->ch_stop_sending_break = 0; ch->ch_stops_sent = 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]; /* * Bring up RTS and DTR... * Also handle RTS or DTR toggle if set. */ if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE)) ch->ch_mostat |= (UART_MCR_RTS); if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE)) ch->ch_mostat |= (UART_MCR_DTR); /* Tell UART to init itself */ brd->bd_ops->uart_init(ch); } /* * Run param in case we changed anything */ brd->bd_ops->param(tty); dgnc_carrier(ch); /* * follow protocol for opening port */ DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = dgnc_block_til_ready(tty, file, ch); if (rc) { DPR_OPEN(("dgnc_tty_open returning after dgnc_block_til_ready " "with %d\n", rc)); } /* No going back now, increment our unit and channel counters */ DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_open_count++; un->un_open_count++; un->un_flags |= (UN_ISOPEN); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_OPEN(("dgnc_tty_open finished\n")); return (rc); } /* * dgnc_block_til_ready() * * Wait for DCD, if needed. */ static int dgnc_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 != DGNC_CHANNEL_MAGIC) { return (-ENXIO); } un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) { return (-ENXIO); } DPR_OPEN(("dgnc_block_til_ready - before block.\n")); DGNC_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)) { retval = -EIO; 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(("dgnc_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. */ DGNC_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. */ DGNC_LOCK(ch->ch_lock, lock_flags); } ch->ch_wopen--; DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_OPEN(("dgnc_block_til_ready - after blocking.\n")); if (retval) { DPR_OPEN(("dgnc_block_til_ready - done. error. retval: %x\n", retval)); return(retval); } DPR_OPEN(("dgnc_block_til_ready - done no error. jiffies: %lu\n", jiffies)); return(0); } /* * dgnc_tty_hangup() * * Hangup the port. Like a close, but don't wait for output to drain. */ static void dgnc_tty_hangup(struct tty_struct *tty) { struct un_t *un; if (!tty || tty->magic != TTY_MAGIC) return; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return; DPR_CLOSE(("dgnc_hangup called. ch->ch_open_count: %d un->un_open_count: %d\n", un->un_ch->ch_open_count, un->un_open_count)); /* flush the transmit queues */ dgnc_tty_flush_buffer(tty); DPR_CLOSE(("dgnc_hangup finished. ch->ch_open_count: %d un->un_open_count: %d\n", un->un_ch->ch_open_count, un->un_open_count)); } /* * dgnc_tty_close() * */ static void dgnc_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 != DGNC_UNIT_MAGIC) return; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return; ts = &tty->termios; DPR_CLOSE(("Close called\n")); DGNC_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(("dgnc_tty_close: not last close ch: %d un:%d\n", ch->ch_open_count, un->un_open_count)); DGNC_UNLOCK(ch->ch_lock, lock_flags); return; } /* OK, its the last close on the unit */ DPR_CLOSE(("dgnc_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_STOPI | CH_FORCED_STOPI); /* * turn off print device when closing print device. */ if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON) ) { dgnc_wmove(ch, ch->ch_digi.digi_offstr, (int) ch->ch_digi.digi_offlen); ch->ch_flags &= ~CH_PRON; } DGNC_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 = bd->bd_ops->drain(tty, 0); DPR_CLOSE(("After calling wait_for_drain\n")); if (rc) { DPR_BASIC(("dgnc_tty_close - bad return: %d ", rc)); } dgnc_tty_flush_buffer(tty); tty_ldisc_flush(tty); DGNC_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")); /* Drop RTS/DTR */ ch->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS); bd->bd_ops->assert_modem_signals(ch); /* * 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")); DGNC_UNLOCK(ch->ch_lock, lock_flags); dgnc_ms_sleep(ch->ch_close_delay); DGNC_LOCK(ch->ch_lock, lock_flags); DPR_CLOSE(("Close. After sleeping for RTS/DTR drop\n")); } } ch->ch_old_baud = 0; /* Turn off UART interrupts for this port */ ch->ch_bd->bd_ops->uart_off(ch); } else { /* * turn off print device when closing print device. */ if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON) ) { dgnc_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); DPR_CLOSE(("Close. Doing wakeups\n")); wake_up_interruptible(&ch->ch_flags_wait); wake_up_interruptible(&un->un_flags_wait); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_BASIC(("dgnc_tty_close - complete\n")); } /* * dgnc_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 dgnc_tty_chars_in_buffer(struct tty_struct *tty) { struct channel_t *ch = NULL; struct un_t *un = NULL; ushort thead; ushort ttail; uint tmask; uint chars = 0; ulong lock_flags = 0; if (tty == NULL) return(0); un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return (0); ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return (0); DGNC_LOCK(ch->ch_lock, lock_flags); tmask = WQUEUEMASK; thead = ch->ch_w_head & tmask; ttail = ch->ch_w_tail & tmask; DGNC_UNLOCK(ch->ch_lock, lock_flags); if (ttail == thead) { chars = 0; } else { if (thead >= ttail) chars = thead - ttail; else chars = thead - ttail + WQUEUESIZE; } DPR_WRITE(("dgnc_tty_chars_in_buffer. Port: %x - %d (head: %d tail: %d)\n", ch->ch_portnum, chars, thead, ttail)); return(chars); } /* * dgnc_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 dgnc_maxcps_room(struct tty_struct *tty, int bytes_available) { struct channel_t *ch = NULL; struct un_t *un = NULL; if (!tty) return (bytes_available); un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return (bytes_available); ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return (bytes_available); /* * If its not the Transparent print device, return * the full data amount. */ if (un->un_type != DGNC_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); } /* * dgnc_tty_write_room() * * Return space available in Tx buffer */ static int dgnc_tty_write_room(struct tty_struct *tty) { struct channel_t *ch = NULL; struct un_t *un = NULL; ushort head; ushort tail; ushort tmask; int ret = 0; ulong lock_flags = 0; if (tty == NULL || dgnc_TmpWriteBuf == NULL) return(0); un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return (0); ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return (0); DGNC_LOCK(ch->ch_lock, lock_flags); tmask = WQUEUEMASK; head = (ch->ch_w_head) & tmask; tail = (ch->ch_w_tail) & tmask; if ((ret = tail - head - 1) < 0) ret += WQUEUESIZE; /* Limit printer to maxcps */ ret = dgnc_maxcps_room(tty, ret); /* * If we are printer device, leave space for * possibly both the on and off strings. */ if (un->un_type == DGNC_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; DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_WRITE(("dgnc_tty_write_room - %d tail: %d head: %d\n", ret, tail, head)); return(ret); } /* * dgnc_tty_put_char() * * Put a character into ch->ch_buf * * - used by the line discipline for OPOST processing */ static int dgnc_tty_put_char(struct tty_struct *tty, unsigned char c) { /* * Simply call tty_write. */ DPR_WRITE(("dgnc_tty_put_char called\n")); dgnc_tty_write(tty, &c, 1); return 1; } /* * dgnc_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 dgnc_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct channel_t *ch = NULL; struct un_t *un = NULL; int bufcount = 0, n = 0; int orig_count = 0; ulong lock_flags; ushort head; ushort tail; ushort tmask; uint remain; int from_user = 0; if (tty == NULL || dgnc_TmpWriteBuf == NULL) return(0); un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return(0); ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return(0); if (!count) return(0); DPR_WRITE(("dgnc_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; DGNC_LOCK(ch->ch_lock, lock_flags); /* Get our space available for the channel from the board */ tmask = WQUEUEMASK; head = (ch->ch_w_head) & tmask; tail = (ch->ch_w_tail) & tmask; if ((bufcount = tail - head - 1) < 0) bufcount += WQUEUESIZE; 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 = dgnc_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) { DGNC_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 == DGNC_PRINT) && !(ch->ch_flags & CH_PRON)) { dgnc_wmove(ch, ch->ch_digi.digi_onstr, (int) ch->ch_digi.digi_onlen); head = (ch->ch_w_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 != DGNC_PRINT) && (ch->ch_flags & CH_PRON)) { dgnc_wmove(ch, ch->ch_digi.digi_offstr, (int) ch->ch_digi.digi_offlen); head = (ch->ch_w_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) { DGNC_UNLOCK(ch->ch_lock, lock_flags); return(0); } if (from_user) { count = min(count, WRITEBUFLEN); DGNC_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(&dgnc_TmpWriteSem)) { return (-EINTR); } /* * copy_from_user() returns the number * of bytes that could *NOT* be copied. */ count -= copy_from_user(dgnc_TmpWriteBuf, (const uchar __user *) buf, count); if (!count) { up(&dgnc_TmpWriteSem); return(-EFAULT); } DGNC_LOCK(ch->ch_lock, lock_flags); buf = dgnc_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 = WQUEUESIZE - head; if (n >= remain) { n -= remain; memcpy(ch->ch_wqueue + head, buf, remain); dgnc_sniff_nowait_nolock(ch, "USER WRITE", ch->ch_wqueue + head, remain); head = 0; buf += remain; } if (n > 0) { /* * Move rest of data. */ remain = n; memcpy(ch->ch_wqueue + head, buf, remain); dgnc_sniff_nowait_nolock(ch, "USER WRITE", ch->ch_wqueue + head, remain); head += remain; } if (count) { head &= tmask; ch->ch_w_head = head; } #if 0 /* * If this is the print device, and the * printer is still on, we need to turn it * off before going idle. */ if (count == orig_count) { if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON)) { head &= tmask; ch->ch_w_head = head; dgnc_wmove(ch, ch->ch_digi.digi_offstr, (int) ch->ch_digi.digi_offlen); head = (ch->ch_w_head) & tmask; ch->ch_flags &= ~CH_PRON; } } #endif /* Update printer buffer empty time. */ if ((un->un_type == DGNC_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) { DGNC_UNLOCK(ch->ch_lock, lock_flags); up(&dgnc_TmpWriteSem); } else { DGNC_UNLOCK(ch->ch_lock, lock_flags); } DPR_WRITE(("Write finished - Write %d bytes of %d.\n", count, orig_count)); if (count) { /* * Channel lock is grabbed and then released * inside this routine. */ ch->ch_bd->bd_ops->copy_data_from_queue_to_uart(ch); } return (count); } /* * Return modem signals to ld. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) static int dgnc_tty_tiocmget(struct tty_struct *tty) #else static int dgnc_tty_tiocmget(struct tty_struct *tty, struct file *file) #endif { 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 != DGNC_UNIT_MAGIC) return result; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return result; DPR_IOCTL(("dgnc_tty_tiocmget start\n")); DGNC_LOCK(ch->ch_lock, lock_flags); mstat = (ch->ch_mostat | ch->ch_mistat); DGNC_UNLOCK(ch->ch_lock, lock_flags); result = 0; if (mstat & UART_MCR_DTR) result |= TIOCM_DTR; if (mstat & UART_MCR_RTS) result |= TIOCM_RTS; if (mstat & UART_MSR_CTS) result |= TIOCM_CTS; if (mstat & UART_MSR_DSR) result |= TIOCM_DSR; if (mstat & UART_MSR_RI) result |= TIOCM_RI; if (mstat & UART_MSR_DCD) result |= TIOCM_CD; DPR_IOCTL(("dgnc_tty_tiocmget finish\n")); return result; } /* * dgnc_tty_tiocmset() * * Set modem signals, called by ld. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) static int dgnc_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) #else static int dgnc_tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) #endif { struct board_t *bd; struct channel_t *ch; struct un_t *un; int ret = -EIO; ulong lock_flags; if (!tty || tty->magic != TTY_MAGIC) return ret; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return ret; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return ret; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return ret; DPR_IOCTL(("dgnc_tty_tiocmset start\n")); DGNC_LOCK(ch->ch_lock, lock_flags); if (set & TIOCM_RTS) { ch->ch_mostat |= UART_MCR_RTS; } if (set & TIOCM_DTR) { ch->ch_mostat |= UART_MCR_DTR; } if (clear & TIOCM_RTS) { ch->ch_mostat &= ~(UART_MCR_RTS); } if (clear & TIOCM_DTR) { ch->ch_mostat &= ~(UART_MCR_DTR); } ch->ch_bd->bd_ops->assert_modem_signals(ch); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_tiocmset finish\n")); return (0); } /* * dgnc_tty_send_break() * * Send a Break, called by ld. */ static int dgnc_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; if (!tty || tty->magic != TTY_MAGIC) return ret; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return ret; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return ret; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return ret; switch (msec) { case -1: msec = 0xFFFF; break; case 0: msec = 0; break; default: break; } DPR_IOCTL(("dgnc_tty_send_break start 1. %lx\n", jiffies)); DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_bd->bd_ops->send_break(ch, msec); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_send_break finish\n")); return (0); } /* * dgnc_tty_wait_until_sent() * * wait until data has been transmitted, called by ld. */ static void dgnc_tty_wait_until_sent(struct tty_struct *tty, int timeout) { struct board_t *bd; struct channel_t *ch; struct un_t *un; int rc; if (!tty || tty->magic != TTY_MAGIC) return; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return; rc = bd->bd_ops->drain(tty, 0); if (rc) { DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc)); return; } return; } /* * dgnc_send_xchar() * * send a high priority character, called by ld. */ static void dgnc_tty_send_xchar(struct tty_struct *tty, char c) { struct board_t *bd; struct channel_t *ch; struct un_t *un; ulong lock_flags; if (!tty || tty->magic != TTY_MAGIC) return; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return; DPR_IOCTL(("dgnc_tty_send_xchar start\n")); printk("dgnc_tty_send_xchar start\n"); DGNC_LOCK(ch->ch_lock, lock_flags); bd->bd_ops->send_immediate_char(ch, c); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_send_xchar finish\n")); printk("dgnc_tty_send_xchar finish\n"); return; } /* * Return modem signals to ld. */ static inline int dgnc_get_mstat(struct channel_t *ch) { unsigned char mstat; int result = -EIO; ulong lock_flags; DPR_IOCTL(("dgnc_getmstat start\n")); if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return(-ENXIO); DGNC_LOCK(ch->ch_lock, lock_flags); mstat = (ch->ch_mostat | ch->ch_mistat); DGNC_UNLOCK(ch->ch_lock, lock_flags); result = 0; if (mstat & UART_MCR_DTR) result |= TIOCM_DTR; if (mstat & UART_MCR_RTS) result |= TIOCM_RTS; if (mstat & UART_MSR_CTS) result |= TIOCM_CTS; if (mstat & UART_MSR_DSR) result |= TIOCM_DSR; if (mstat & UART_MSR_RI) result |= TIOCM_RI; if (mstat & UART_MSR_DCD) result |= TIOCM_CD; DPR_IOCTL(("dgnc_getmstat finish\n")); return(result); } /* * Return modem signals to ld. */ static int dgnc_get_modem_info(struct channel_t *ch, unsigned int __user *value) { int result; int rc; DPR_IOCTL(("dgnc_get_modem_info start\n")); if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return(-ENXIO); result = dgnc_get_mstat(ch); if (result < 0) return (-ENXIO); rc = put_user(result, value); DPR_IOCTL(("dgnc_get_modem_info finish\n")); return(rc); } /* * dgnc_set_modem_info() * * Set modem signals, called by ld. */ static int dgnc_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; if (!tty || tty->magic != TTY_MAGIC) return ret; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return ret; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return ret; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return ret; ret = 0; DPR_IOCTL(("dgnc_set_modem_info() start\n")); ret = get_user(arg, value); if (ret) return(ret); switch (command) { case TIOCMBIS: if (arg & TIOCM_RTS) { ch->ch_mostat |= UART_MCR_RTS; } if (arg & TIOCM_DTR) { ch->ch_mostat |= UART_MCR_DTR; } break; case TIOCMBIC: if (arg & TIOCM_RTS) { ch->ch_mostat &= ~(UART_MCR_RTS); } if (arg & TIOCM_DTR) { ch->ch_mostat &= ~(UART_MCR_DTR); } break; case TIOCMSET: if (arg & TIOCM_RTS) { ch->ch_mostat |= UART_MCR_RTS; } else { ch->ch_mostat &= ~(UART_MCR_RTS); } if (arg & TIOCM_DTR) { ch->ch_mostat |= UART_MCR_DTR; } else { ch->ch_mostat &= ~(UART_MCR_DTR); } break; default: return(-EINVAL); } DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_bd->bd_ops->assert_modem_signals(ch); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_set_modem_info finish\n")); return (0); } /* * dgnc_tty_digigeta() * * Ioctl to get the information for ditty. * * * */ static int dgnc_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 != DGNC_UNIT_MAGIC) return (-EFAULT); ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return (-EFAULT); memset(&tmp, 0, sizeof(tmp)); DGNC_LOCK(ch->ch_lock, lock_flags); memcpy(&tmp, &ch->ch_digi, sizeof(tmp)); DGNC_UNLOCK(ch->ch_lock, lock_flags); if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return (-EFAULT); return (0); } /* * dgnc_tty_digiseta() * * Ioctl to set the information for ditty. * * * */ static int dgnc_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; DPR_IOCTL(("DIGI_SETA start\n")); if (!tty || tty->magic != TTY_MAGIC) return (-EFAULT); un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return (-EFAULT); ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return (-EFAULT); bd = ch->ch_bd; if (!bd || bd->magic != DGNC_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); } DGNC_LOCK(ch->ch_lock, lock_flags); /* * Handle transistions to and from RTS Toggle. */ if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) && (new_digi.digi_flags & DIGI_RTS_TOGGLE)) ch->ch_mostat &= ~(UART_MCR_RTS); if ((ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) && !(new_digi.digi_flags & DIGI_RTS_TOGGLE)) ch->ch_mostat |= (UART_MCR_RTS); /* * Handle transistions to and from DTR Toggle. */ if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) && (new_digi.digi_flags & DIGI_DTR_TOGGLE)) ch->ch_mostat &= ~(UART_MCR_DTR); if ((ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) && !(new_digi.digi_flags & DIGI_DTR_TOGGLE)) ch->ch_mostat |= (UART_MCR_DTR); 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; ch->ch_bd->bd_ops->param(tty); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("DIGI_SETA finish\n")); return(0); } /* * dgnc_set_termios() */ static void dgnc_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; if (!tty || tty->magic != TTY_MAGIC) return; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return; DGNC_LOCK(ch->ch_lock, lock_flags); 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]; ch->ch_bd->bd_ops->param(tty); dgnc_carrier(ch); DGNC_UNLOCK(ch->ch_lock, lock_flags); } static void dgnc_tty_throttle(struct tty_struct *tty) { struct channel_t *ch; struct un_t *un; ulong lock_flags = 0; if (!tty || tty->magic != TTY_MAGIC) return; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; DPR_IOCTL(("dgnc_tty_throttle start\n")); DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_flags |= (CH_FORCED_STOPI); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_throttle finish\n")); } static void dgnc_tty_unthrottle(struct tty_struct *tty) { struct channel_t *ch; struct un_t *un; ulong lock_flags; if (!tty || tty->magic != TTY_MAGIC) return; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; DPR_IOCTL(("dgnc_tty_unthrottle start\n")); DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_flags &= ~(CH_FORCED_STOPI); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_unthrottle finish\n")); } static void dgnc_tty_start(struct tty_struct *tty) { struct board_t *bd; struct channel_t *ch; struct un_t *un; ulong lock_flags; if (!tty || tty->magic != TTY_MAGIC) return; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return; DPR_IOCTL(("dgcn_tty_start start\n")); DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_flags &= ~(CH_FORCED_STOP); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_start finish\n")); } static void dgnc_tty_stop(struct tty_struct *tty) { struct board_t *bd; struct channel_t *ch; struct un_t *un; ulong lock_flags; if (!tty || tty->magic != TTY_MAGIC) return; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return; DPR_IOCTL(("dgnc_tty_stop start\n")); DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_flags |= (CH_FORCED_STOP); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_stop finish\n")); } /* * dgnc_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 dgnc_tty_flush_chars(struct tty_struct *tty) { struct board_t *bd; struct channel_t *ch; struct un_t *un; ulong lock_flags; if (!tty || tty->magic != TTY_MAGIC) return; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return; DPR_IOCTL(("dgnc_tty_flush_chars start\n")); DGNC_LOCK(ch->ch_lock, lock_flags); /* Do something maybe here */ DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_flush_chars finish\n")); } /* * dgnc_tty_flush_buffer() * * Flush Tx buffer (make in == out) */ static void dgnc_tty_flush_buffer(struct tty_struct *tty) { struct channel_t *ch; struct un_t *un; ulong lock_flags; if (!tty || tty->magic != TTY_MAGIC) return; un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; DPR_IOCTL(("dgnc_tty_flush_buffer on port: %d start\n", ch->ch_portnum)); DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_flags &= ~CH_STOP; /* Flush our write queue */ ch->ch_w_head = ch->ch_w_tail; /* Flush UARTs transmit FIFO */ ch->ch_bd->bd_ops->flush_uart_write(ch); 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); } DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_flush_buffer finish\n")); } /***************************************************************************** * * The IOCTL function and all of its helpers * *****************************************************************************/ /* * dgnc_tty_ioctl() * * The usual assortment of ioctl's */ static int dgnc_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; ulong lock_flags; void __user *uarg = (void __user *) arg; if (!tty || tty->magic != TTY_MAGIC) return (-ENODEV); un = tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return (-ENODEV); ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return (-ENODEV); bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return (-ENODEV); DPR_IOCTL(("dgnc_tty_ioctl start on port %d - cmd %s (%x), arg %lx\n", ch->ch_portnum, dgnc_ioctl_name(cmd), cmd, arg)); DGNC_LOCK(ch->ch_lock, lock_flags); if (un->un_open_count <= 0) { DPR_BASIC(("dgnc_tty_ioctl - unit not open.\n")); DGNC_UNLOCK(ch->ch_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); DGNC_UNLOCK(ch->ch_lock, lock_flags); if (rc) { return(rc); } rc = ch->ch_bd->bd_ops->drain(tty, 0); if (rc) { DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc)); return(-EINTR); } DGNC_LOCK(ch->ch_lock, lock_flags); if(((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) { ch->ch_bd->bd_ops->send_break(ch, 250); } DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", ch->ch_portnum, dgnc_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); DGNC_UNLOCK(ch->ch_lock, lock_flags); if (rc) { return(rc); } rc = ch->ch_bd->bd_ops->drain(tty, 0); if (rc) { DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc)); return(-EINTR); } DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_bd->bd_ops->send_break(ch, 250); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", ch->ch_portnum, dgnc_ioctl_name(cmd), cmd, arg)); return(0); case TIOCSBRK: rc = tty_check_change(tty); DGNC_UNLOCK(ch->ch_lock, lock_flags); if (rc) { return(rc); } rc = ch->ch_bd->bd_ops->drain(tty, 0); if (rc) { DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc)); return(-EINTR); } DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_bd->bd_ops->send_break(ch, 250); DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", ch->ch_portnum, dgnc_ioctl_name(cmd), cmd, arg)); return(0); case TIOCCBRK: /* Do Nothing */ DGNC_UNLOCK(ch->ch_lock, lock_flags); return 0; case TIOCGSOFTCAR: DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) arg); return(rc); case TIOCSSOFTCAR: DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = get_user(arg, (unsigned long __user *) arg); if (rc) return(rc); DGNC_LOCK(ch->ch_lock, lock_flags); tty->termios.c_cflag = ((tty->termios.c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); ch->ch_bd->bd_ops->param(tty); DGNC_UNLOCK(ch->ch_lock, lock_flags); return(0); case TIOCMGET: DGNC_UNLOCK(ch->ch_lock, lock_flags); return(dgnc_get_modem_info(ch, uarg)); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: DGNC_UNLOCK(ch->ch_lock, lock_flags); return(dgnc_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) { DGNC_UNLOCK(ch->ch_lock, lock_flags); return(rc); } if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) { ch->ch_r_head = ch->ch_r_tail; ch->ch_bd->bd_ops->flush_uart_read(ch); /* Force queue flow control to be released, if needed */ dgnc_check_queue_flow_control(ch); } if ((arg == TCOFLUSH) || (arg == TCIOFLUSH)) { if (!(un->un_type == DGNC_PRINT)) { ch->ch_w_head = ch->ch_w_tail; ch->ch_bd->bd_ops->flush_uart_write(ch); 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); } } } /* pretend we didn't recognize this IOCTL */ DGNC_UNLOCK(ch->ch_lock, lock_flags); 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; ch->ch_r_head = ch->ch_r_tail; ch->ch_bd->bd_ops->flush_uart_read(ch); /* Force queue flow control to be released, if needed */ dgnc_check_queue_flow_control(ch); } /* now wait for all the output to drain */ DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = ch->ch_bd->bd_ops->drain(tty, 0); if (rc) { DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d\n", rc)); return(-EINTR); } DPR_IOCTL(("dgnc_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", ch->ch_portnum, dgnc_ioctl_name(cmd), cmd, arg)); /* pretend we didn't recognize this */ return(-ENOIOCTLCMD); case TCSETAW: DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = ch->ch_bd->bd_ops->drain(tty, 0); if (rc) { DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc)); return(-EINTR); } /* pretend we didn't recognize this */ return(-ENOIOCTLCMD); case TCXONC: DGNC_UNLOCK(ch->ch_lock, lock_flags); /* Make the ld do it */ return(-ENOIOCTLCMD); case DIGI_GETA: /* get information for ditty */ DGNC_UNLOCK(ch->ch_lock, lock_flags); return(dgnc_tty_digigeta(tty, uarg)); case DIGI_SETAW: case DIGI_SETAF: /* set information for ditty */ if (cmd == (DIGI_SETAW)) { DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = ch->ch_bd->bd_ops->drain(tty, 0); if (rc) { DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc)); return(-EINTR); } DGNC_LOCK(ch->ch_lock, lock_flags); } else { tty_ldisc_flush(tty); } /* fall thru */ case DIGI_SETA: DGNC_UNLOCK(ch->ch_lock, lock_flags); return(dgnc_tty_digiseta(tty, uarg)); case DIGI_LOOPBACK: { uint loopback = 0; /* Let go of locks when accessing user space, could sleep */ DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = get_user(loopback, (unsigned int __user *) arg); if (rc) return(rc); DGNC_LOCK(ch->ch_lock, lock_flags); /* Enable/disable internal loopback for this port */ if (loopback) ch->ch_flags |= CH_LOOPBACK; else ch->ch_flags &= ~(CH_LOOPBACK); ch->ch_bd->bd_ops->param(tty); DGNC_UNLOCK(ch->ch_lock, lock_flags); return(0); } case DIGI_GETCUSTOMBAUD: DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = put_user(ch->ch_custom_speed, (unsigned int __user *) arg); return(rc); case DIGI_SETCUSTOMBAUD: { uint new_rate; /* Let go of locks when accessing user space, could sleep */ DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = get_user(new_rate, (unsigned int __user *) arg); if (rc) return(rc); DGNC_LOCK(ch->ch_lock, lock_flags); dgnc_set_custom_speed(ch, new_rate); ch->ch_bd->bd_ops->param(tty); DGNC_UNLOCK(ch->ch_lock, lock_flags); return(0); } /* * This ioctl allows insertion of a character into the front * of any pending data to be transmitted. * * This ioctl is to satify the "Send Character Immediate" * call that the RealPort protocol spec requires. */ case DIGI_REALPORT_SENDIMMEDIATE: { unsigned char c; DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = get_user(c, (unsigned char __user *) arg); if (rc) return(rc); DGNC_LOCK(ch->ch_lock, lock_flags); ch->ch_bd->bd_ops->send_immediate_char(ch, c); DGNC_UNLOCK(ch->ch_lock, lock_flags); return(0); } /* * This ioctl returns all the current counts for the port. * * This ioctl is to satify the "Line Error Counters" * call that the RealPort protocol spec requires. */ case DIGI_REALPORT_GETCOUNTERS: { struct digi_getcounter buf; buf.norun = ch->ch_err_overrun; buf.noflow = 0; /* The driver doesn't keep this stat */ buf.nframe = ch->ch_err_frame; buf.nparity = ch->ch_err_parity; buf.nbreak = ch->ch_err_break; buf.rbytes = ch->ch_rxcount; buf.tbytes = ch->ch_txcount; DGNC_UNLOCK(ch->ch_lock, lock_flags); if (copy_to_user(uarg, &buf, sizeof(struct digi_getcounter))) { return (-EFAULT); } return(0); } /* * This ioctl returns all current events. * * This ioctl is to satify the "Event Reporting" * call that the RealPort protocol spec requires. */ case DIGI_REALPORT_GETEVENTS: { unsigned int events = 0; /* NOTE: MORE EVENTS NEEDS TO BE ADDED HERE */ if (ch->ch_flags & CH_BREAK_SENDING) events |= EV_TXB; if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_FORCED_STOP)) { events |= (EV_OPU | EV_OPS); } if ((ch->ch_flags & CH_STOPI) || (ch->ch_flags & CH_FORCED_STOPI)) { events |= (EV_IPU | EV_IPS); } DGNC_UNLOCK(ch->ch_lock, lock_flags); rc = put_user(events, (unsigned int __user *) arg); return(rc); } /* * This ioctl returns TOUT and TIN counters based * upon the values passed in by the RealPort Server. * It also passes back whether the UART Transmitter is * empty as well. */ case DIGI_REALPORT_GETBUFFERS: { struct digi_getbuffer buf; int tdist; int count; DGNC_UNLOCK(ch->ch_lock, lock_flags); /* * Get data from user first. */ if (copy_from_user(&buf, uarg, sizeof(struct digi_getbuffer))) { return (-EFAULT); } DGNC_LOCK(ch->ch_lock, lock_flags); /* * Figure out how much data is in our RX and TX queues. */ buf.rxbuf = (ch->ch_r_head - ch->ch_r_tail) & RQUEUEMASK; buf.txbuf = (ch->ch_w_head - ch->ch_w_tail) & WQUEUEMASK; /* * Is the UART empty? Add that value to whats in our TX queue. */ count = buf.txbuf + ch->ch_bd->bd_ops->get_uart_bytes_left(ch); /* * Figure out how much data the RealPort Server believes should * be in our TX queue. */ tdist = (buf.tIn - buf.tOut) & 0xffff; /* * If we have more data than the RealPort Server believes we * should have, reduce our count to its amount. * * This count difference CAN happen because the Linux LD can * insert more characters into our queue for OPOST processing * that the RealPort Server doesn't know about. */ if (buf.txbuf > tdist) { buf.txbuf = tdist; } /* * Report whether our queue and UART TX are completely empty. */ if (count) { buf.txdone = 0; } else { buf.txdone = 1; } DGNC_UNLOCK(ch->ch_lock, lock_flags); if (copy_to_user(uarg, &buf, sizeof(struct digi_getbuffer))) { return (-EFAULT); } return(0); } default: DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_ioctl - in default\n")); DPR_IOCTL(("dgnc_tty_ioctl end - cmd %s (%x), arg %lx\n", dgnc_ioctl_name(cmd), cmd, arg)); return(-ENOIOCTLCMD); } DGNC_UNLOCK(ch->ch_lock, lock_flags); DPR_IOCTL(("dgnc_tty_ioctl end - cmd %s (%x), arg %lx\n", dgnc_ioctl_name(cmd), cmd, arg)); return(0); }