/***************************************************************************** * * exser.c XR17C158 (XR16L788) Family Multiport Serial Driver with mods * * Copyright (C) 2003 Logical Solutions Inc. (www.thinklogical.com) * Copyright (C) 2002 Lantronix (www.lantronix.com) by Anthony Petillo * Copyright (C) 2001 Exar Corp. (www.exar.com) * Copyright (C) 1991, 1992, Linus Torvalds * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, * 1998, 1999, Theodore Ts'o * * Modified by Bill Feero to work with the 2.4.x kernel * * Modified by Anthony Petillo to make use of the auto-flow control * capabilities of the Exar17C1788, and added buffer device, listen * device, and /proc entries to read the serial data buffers. * * This code is loosely based on the Linux serial driver, written by * Linus Torvalds, Theodore T'so and others. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * XR17C158 Family Multiport Serial Driver * for : LINUX (Tested with RedHat 7.0 and 7.1) * date : June, 2001 * version : 1.01 * *----------------------------------------------------------------------- * Bill Feero * * because LSI uses the ISA version of the UART, I've removed the PCI code * Jan 2003 - removed separate buffer devices, now buffer access is via /proc * added exec_usermodehelper to check access permissions * * This version of the driver has been tested with 2.4.18 version * of the linux kernel. * * *----------------------------------------------------------------------- * * Anthony Petillo: April-29-2002 * * The exser driver is a "tty" driver. It works with tty driver interface. * The behavior is slightly different from a driver that is directly * registered with the kernel. One of the main differences is that if * the open fails then close is still called. This is by design so * that you can perform any clean-up if the open failed and the * clean-up code is better placed in the "close" function. * * There are three device drivers in this module. They are connected * via the exser's interrupt routine. The ISR basically writes the * incomming bytes to up to three buffers. One: the history buffer * (a circular queue), two: the listen buffer (to support listen * clients) and three: the buffer of an application who opened the * device. The lsibuf device's function is to provide a means of * reading the history buffer for a serial port. The listen device * allows an third party to see the data that is comming into the * serial port. * * In addition to the three devices, there are also proc entries * defined. The provide an alternative way to access the history * buffer. This alternative method provides the characteristics * to allow applications to see the block of data as a "regular file". * This allows programs such as ftp to be used to transfer the file * off the box. * * This version of the driver has been tested with 2.2.12 and * 2.2.19 versions of the linux kernel. * gcc -c -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O2 / -fomit-frame-pointer -mcpu=i486 -I /usr/src/linux-2.4/include / -I ../common -o exser.o exser.c * meaning of parameters: * * -c compile only (produce an object file only) * -DMODULE used to include module related functions * -D__KERNEL__ kernel header files look for this * -Wall enable all warnings * -Wstrict-prototypes force all prototypes to exist * -O2 optimize level 2 * -fomit-frame-pointer avoid functions to set, save, and restore frame pointers * -mcpu=486 target is 486 or higher * * * $Log: exser.c,v $ * Revision 1.24 2003/06/30 15:35:14 billf * the 2 pool routines (monitor and prov) return masks, not ints; so I had to renove the debug tests for NULL pointers. * returning a EFAULT error was being interpreted as mask bits, not an error condition. * * Revision 1.23 2003/06/27 20:12:44 billf * changed version no. to 2.6.12 * * Revision 1.22 2003/06/27 20:04:29 billf * removed the test for null in the wait routines, it is OK for the pointer to poll_table to be NULL (according to the Linux drivers book) * * Revision 1.21 2003/06/18 16:48:59 billf * added code in exar_shutdown to turn off MSI & THRI interrupts * We don't want these interrupts when there is no one connected * * Revision 1.20 2003/06/17 19:17:03 billf * added lsi_buffer_gid as a module parameter, in place of the hard-coded user GID. This way we can use any GID for the group scsusers. * * Revision 1.19 2003/06/02 19:01:30 tomv * turned off all of the debugging and fixed exser_change_speed. Also changed the rev number to 2.6.9 * * Revision 1.18 2003/06/02 18:38:53 tomv * deleted the counter debug lines used to test SNMP (OOPS!!!!) * * Revision 1.17 2003/05/30 20:08:58 tomv * Changed ioctl to allow SNMP to display the correct counter information * * Revision 1.16 2003/05/23 19:29:43 billf * removed more debug code * * Revision 1.15 2003/05/23 15:26:32 billf * the DEBUG NULL tests are always done, even if debug is not set * changed version number to 2.6.8 * Now I will only output a Ctrl-Q in rs_close when I am the only user left (rdwr count == 1, soon to be dec. to zero) * * Revision 1.14 2003/05/15 13:51:35 billf * show buffer size as zero after a clear and when it is first created * (it was being set to 256K) * Changed version number to 2.6.7 * * Revision 1.13 2003/05/02 14:45:50 billf * added a comment to rs_open where we return EBUSY * * Revision 1.12 2003/05/01 16:17:39 billf * return EBUSY is the ttyBnn port is already open * * Revision 1.11 2003/04/25 16:41:29 billf * moved exser_log definition to exser.h (used by snmp routines) * * Revision 1.10 2003/04/24 20:45:30 gregg * Modified monitor driver to use f_pos offset. * * Revision 1.9 2003/04/21 14:56:17 billf * enabled MONITOR * * Revision 1.8 2003/04/17 17:47:52 billf * updated the copyright notice * * Revision 1.7 2003/04/16 18:54:07 billf * removed debug code * * Revision 1.6 2003/04/11 13:21:04 billf * no monitor * * Revision 1.5 2003/04/05 00:02:52 billf * when creating /proc/buffer entries, only create the number of ports we have, not the max of 48 * * Revision 1.4 2003/03/25 20:45:08 gregg * In proc_open: if opening for write, no others allowed. Changed * condition to check for greater thn zero.. * * Revision 1.3 2003/03/18 19:17:51 billf * fixed get_dtedce * * Revision 1.2 2003/03/18 14:57:47 billf * fixed set_dtedce * * Revision 1.1.1.1 2003/03/17 15:41:36 billf * Initial import * * * * * * * ***************************************************************************/ //----------------------------------------------------------------- // Set up debugging //----------------------------------------------------------------- //#define PROC_DEBUG //#define DEBUG_IO // when defined, inb and outb are replaced with printk //#define LW_DEBUG // when defined allows debug messages to be compiled in // add date & time to the version for debug builds #define LW_DEBUG_TIMESTAMP "" //" : "__DATE__" " __TIME__ #ifdef MODVERSIONS #ifndef MODULE #define MODULE #endif #endif #ifdef CONFIG_PCI // we don't want PCI functions involved #undef CONFIG_PCI #endif // uncomment next line if ports can be more than device ports i.e. getty allowed //#define NON_DEVICE_ALLOWED //----------------------------------------------------------------- // MACROS and constants //----------------------------------------------------------------- #define MODULE_NAME "exser" static const char module_name[] = MODULE_NAME; #define VERSION_NUMBER "2.6.13" #ifdef LW_DEBUG #define __EXAR_VERSION VERSION_NUMBER"debug" #else #define __EXAR_VERSION VERSION_NUMBER #endif #define VERSION_CODE(ver,rel,seq) ((ver << 16) | (rel << 8) | seq) #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif // This is used to figure out the divisor speeds and the timeouts static const int exvar_baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 0 }; #define CONTROL_MINOR_NUMBER 160 // program that tests access permissions #define FW_LOADER "/usr/local/bin/port_access" // bits in enabledebug can be used to filter messages at run time // using DEBUG_PRINT macro #define DEBUG_CLOSE 0x00000001 // debug messages in rs_close #define DEBUG_OPEN 0x00000002 // debug messages in rs_open #define DEBUG_PROC 0x00000004 // debug messages in proc related functions #define DEBUG_ID 0x00000008 // debug messages in exser_get_LSI_conf #define DEBUG_LSIBUF_R 0x00000010 // debug messages in proc_read #define DEBUG_LSIBUF_OPEN 0x00000020 // debug messages in proc_open #define DEBUG_LSIBUF_CLOSE 0x00000040 // debug messages in proc_close #define DEBUG_ISR 0x00000080 // debug messages in exser_interrupt (isr) #define DEBUG_SPEED 0x00000100 // debug messages in exser_change_speed #define DEBUG_TRACE 0x00000200 // debug messages for tracing the driver #define DEBUG_TERMIOS 0x00000400 // debug messages for exser_set_termios #define DEBUG_COMM_ERR 0x00000800 // debug messages for exser_receive_chars #define DEBUG_INFO 0x10000000 #define DEBUG_ANY 0xffffffff // enable any debug messages // define prefixes for the printk calls #define ISR_PREFIX "" // no level for the printk #define DEFAULT_PREFIX KERN_DEBUG MODULE_NAME ": " #define INFO_PREFIX KERN_INFO MODULE_NAME ": " #define ERROR_PREFIX KERN_ERR MODULE_NAME ": " // for messages we want to appear all the time // i.e. during initialization, etc. #define PRINT(fmt, args...) printk (DEFAULT_PREFIX fmt, ## args) #ifdef LW_DEBUG_TIMESTAMP #define EXAR_VERSION __EXAR_VERSION LW_DEBUG_TIMESTAMP #else #define EXAR_VERSION __EXAR_VERSION #endif #ifdef LW_DEBUG static int debugenable=0; #define TRACE(str) printk (KERN_DEBUG "exser@%4d %s\n", __LINE__, str); //#define TRACE(str) #define DEBUG_PRINT(d, fmt, args...) \ do { \ if (debugenable & d) {\ printk (fmt, ## args);\ }\ } while (0) #define DEC_DEV_USAGE_COUNT --(dev->usage_count) //#define DEC_DEV_USAGE_COUNT printk ("@%d dec count=%d port=%d\n", __LINE__, dev->usage_count, dev->port_num); --(dev->usage_count) #else #define TRACE(str) #define DEBUG_PRINT(d, fmt, args...) //#define DEBUG_NULL(p,val) if ((p) == NULL) { return val; } //#define DEBUG_NULLV(p) if ((p) == NULL) { return; } #define DEC_DEV_USAGE_COUNT --(dev->usage_count) #endif #define DEBUG_NULL(p,val) if ((p) == NULL) { \ printk (KERN_INFO "exser@%4d "#p " is NULL\n", __LINE__); \ return val; \ } #define DEBUG_NULLV(p) if ((p) == NULL) { \ printk (KERN_INFO "exser@%4d "#p " is NULL\n", __LINE__); \ return; \ } //----------------------------------------------------------------- // includes //----------------------------------------------------------------- #ifdef MODULE #include #ifdef MODVERSIONS #include #endif #include #else #define MOD_INC_USE_COUNT #define MOD_DEC_USE_COUNT #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // printk () #include // everything... #include // size_t #include // proc file system api #include #include #include #include #include #include #include // include these 2 lines to get def. of waitpid #define __KERNEL_SYSCALLS__ #include #include "sysdep.h" #include "cs_hardware.h" #include "dbinfo.h" // lsi product information data structure #include "exser.h" #if (EXAR_CHIPS > _DB_MAX_NUM_UARTS) // you need to adjust dbinfo.h #error __FILE__ too many ports defined #endif //------------------------------------------------------------------------ // some kernel version defines //------------------------------------------------------------------------ #if (LINUX_VERSION_CODE < VERSION_CODE(2,1,0)) #define copy_from_user memcpy_fromfs #define copy_to_user memcpy_tofs #define put_to_user(arg1, arg2) put_fs_long(arg1, (unsigned long *)arg2) #define get_from_user(arg1, arg2) arg1 = get_fs_long((unsigned long *)arg2) #define schedule_timeout(x) {current->timeout = jiffies + (x); schedule();} #define signal_pending(x) ((x)->signal & ~(x)->blocked) #else #include #define put_to_user(arg1, arg2) put_user(arg1, (unsigned long *)arg2) #define get_from_user(arg1, arg2) get_user(arg1, (unsigned int *)arg2) #endif #if 0 //def LW_DEBUG #define LOCAL_INC_USE_COUNT printk(KERN_DEBUG "exser@%d inc module use count\n", __LINE__); MOD_INC_USE_COUNT #define LOCAL_DEC_USE_COUNT printk(KERN_DEBUG "exser@%d dec module use count\n", __LINE__); MOD_DEC_USE_COUNT #else #define LOCAL_INC_USE_COUNT MOD_INC_USE_COUNT #define LOCAL_DEC_USE_COUNT MOD_DEC_USE_COUNT #endif //------------------------------------------------------------------------ // driver setup options and chip constants //------------------------------------------------------------------------ #define EXAR_EVENT_TXLOW 1 #define EXAR_EVENT_HANGUP 2 #define SERIAL_DO_RESTART #define SERIAL_TYPE_NORMAL 1 #define SERIAL_TYPE_CALLOUT 2 #define WAKEUP_CHARS 256 #define PORTNO(x) (MINOR((x)->device) - (x)->driver.minor_start) #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|IXOFF|IXON)) #define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) // hyteresis for use with autoflow control #define HYSTERESIS_NONE 0 #define HYSTERESIS_PLUS_MINUS_4 1 #define HYSTERESIS_PLUS_MINUS_6 2 #define HYSTERESIS_PLUS_MINUS_8 3 #define HYSTERESIS_PLUS_MINUS_12 12 #define HYSTERESIS_PLUS_MINUS_16 4 #define HYSTERESIS_PLUS_MINUS_20 13 #define HYSTERESIS_PLUS_MINUS_24 6 #define HYSTERESIS_PLUS_MINUS_28 14 #define HYSTERESIS_PLUS_MINUS_32 7 #define HYSTERESIS_PLUS_MINUS_36 15 #define HYSTERESIS_PLUS_MINUS_40 8 #define HYSTERESIS_PLUS_MINUS_44 9 #define HYSTERESIS_PLUS_MINUS_48 10 #define HYSTERESIS_PLUS_MINUS_52 11 #define SUCCESS 0 //------------------------------------------------------------------------ // typedefs and structure definitions //------------------------------------------------------------------------ struct uart_control_struct { int in_use; // 0 means not in use int ports; // number of ports on a board? int irq; // interrupt for this chip int vector; // interrupt vector address int vector_mask; // interrupt vector mask register int uart_type; int ioaddr[EXAR_PORTS_PER_CHIP]; // ioaddresses for each uart port int baud_base[EXAR_PORTS_PER_CHIP]; // baud base value for each uart port int buffer_flags; // tells which channels are to // start with history buffer enabled }; typedef enum { #ifdef NON_DEVICE_ALLOWED PORT_TYPE_OFF =0, PORT_TYPE_GETTY =1, PORT_TYPE_NON_GETTY =2, #endif PORT_TYPE_DEVICE =3, PORT_TYPE_LIST_LAST // just a place holder for enum list } type_port; struct port_info_struct { int port; /* port 0-n */ type_port port_type; /* see type_port */ int base; /* port base address */ int irq; /* port using irq no. */ int vector; /* port irq vector */ int vectormask; /* port vector mask */ int rx_trigger; /* Rx fifo trigger level */ int baud_base; /* max. speed */ int flags; /* defined in tty.h */ int type; /* UART type */ int buffer_flag; /* lsi device history buffer */ struct tty_struct * tty; int read_status_mask; int ignore_status_mask; int xmit_fifo_size; int custom_divisor; int x_char; /* xon/xoff character */ int close_delay; unsigned short closing_wait; int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ unsigned long event; int count; /* # of fd on device */ int blocked_open; /* # of blocked opens */ long session; /* Session of opening process */ long pgrp; /* pgrp of opening process */ unsigned char * xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt; struct tq_struct tqueue; struct termios normal_termios; struct termios callout_termios; wait_queue_head_t open_wait; wait_queue_head_t close_wait; wait_queue_head_t delta_msr_wait; struct async_icount icount; /* kernel counters for the 4 input interrupts */ }; struct exser_mstatus{ tcflag_t cflag; int cts; int dsr; int ri; int dcd; }; struct exser_read_reg { int reg; int regvalue; }; typedef struct tag_lsi_device { char * pbuf; /* memory allocated for buffer - circular queue */ char * pbuf_end; /* end of buffer pointer */ char * volatile head; char * volatile tail; unsigned int size; /* size of the buffer */ struct semaphore sem; /* mutual exclusion semaphore */ volatile unsigned int usage_count; /* number open */ volatile unsigned int rdwr_count; /* number open read/write */ int stop_buffering_in_direct; /* if true, stop buffering input data in direct mode */ wait_queue_head_t in_queue; struct proc_dir_entry * ent; // used with lwbuf proc entries, mainly to // update the size of the buffer unsigned long inode; // used in proc open to find buffer int port_num; // port number (1-n) } type_lsi_device; //------------------------------------------------------------------------ // local variables and structures //------------------------------------------------------------------------ #define dtedce_all_off 0 static byte current_dtedce_settings[(EXAR_PORTS+3)/4]; //[0] is not used static type_lsi_device lsi_devices[EXAR_PORTS]; static type_lsi_device lsi_monitor_devices[EXAR_PORTS]; static int tty_major = EXAR_DIRECT_MAJOR; //was 30 static int monitor_major = EXAR_MONITOR_MAJOR; //was 32 static int callout_major = EXAR_CU_MAJOR; //was 35 static bool verbose = FALSE; static int lsi_buffer_gid = USERS_GID; static struct uart_control_struct uart_settings[EXAR_CHIPS]; static struct tty_driver ttyB_driver; // serial - ttyBx static struct tty_driver cuB_driver; // callout - cuBx static struct exser_mstatus GMStatus[EXAR_PORTS]; static struct port_info_struct port_table[EXAR_PORTS]; static struct tty_struct * exvar_tty[EXAR_PORTS+1]; static struct termios * exvar_termios[EXAR_PORTS+1]; static struct termios * exvar_termios_locked[EXAR_PORTS+1]; static struct exser_log exvar_log; static int exvar_refcount; /* * exvar_tmp_buf is used as a temporary buffer by serial_write. We need * to lock it in case the memcpy_fromfs blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves * memory if large numbers of serial ports are open. */ static unsigned char * exvar_tmp_buf = NULL; DECLARE_MUTEX(exvar_tmp_buf_sem) ; // default uart settings/tables - these are changed by get_uart_table static unsigned int system_uart_count = 2; // contains the no.of uarts in system static unsigned int lsi_serial_channels = 0; static type_uart_info default_uart_db_values[EXAR_CHIPS] = { { 8, 0x2100, 6, (U32)0x000000FF }, { 8, 0x2200, 7, (U32)0x000000FF }, { 8, 0x2300, 9, (U32)0x000000FF }, { 8, 0x2400, 10, (U32)0x000000FF }, { 8, 0x2900, 15, (U32)0x000000FF }, { 8, 0x2a00, 5, (U32)0x000000FF } }; //------------------------------------------------------------------------ // function prototypes //------------------------------------------------------------------------ #ifndef _INLINE_ #define _INLINE_ static inline #endif #ifdef MODULE int init_module (void); void cleanup_module (void); static int exser_init (void); static void monitor_cleanup_module (void); #else int exser_init (void); // function to be called by kernel init() #endif static int get_dtedce (int port, struct EXAR_STRUCT_DTEDCE_MODE *ioctl_data); static int set_dtedce (int port, te_UART_DTEDCE mode); static void set_dte_all_off (void); static int monitor_init (void); static void get_uart_table (void); static int exser_get_LSI_conf (type_uart_info *lsi_uart_item, struct uart_control_struct *hwconf); static void exser_getcfg (int board,struct uart_control_struct *hwconf); static void do_softint (void *); static int rs_open (struct tty_struct *, struct file *); static void rs_close (struct tty_struct *, struct file *); static int rs_write (struct tty_struct *, int, const unsigned char *, int); static int exser_write_room (struct tty_struct *); static void rs_flush_buffer (struct tty_struct *); static int exser_chars_in_buffer (struct tty_struct *); static void exser_flush_chars (struct tty_struct *); static void rs_put_char (struct tty_struct *, unsigned char); static int exser_ioctl (struct tty_struct *, struct file *, uint, ulong); static void rs_throttle (struct tty_struct *); static void rs_unthrottle (struct tty_struct *); static void exser_set_termios (struct tty_struct *, struct termios *); static void exser_stop (struct tty_struct *); static void exser_start (struct tty_struct *); static void exser_hangup (struct tty_struct *); static void exser_interrupt (int, void *, struct pt_regs *); static int block_til_ready (struct tty_struct *, struct file *, struct port_info_struct *); static int exser_startup (struct port_info_struct *); static void exser_shutdown (struct port_info_struct *); static int exser_change_speed (struct port_info_struct *, struct termios *old_termios, unsigned char lsi_special); static int exser_get_serial_info (struct port_info_struct *, struct serial_struct *); static int exser_set_serial_info (struct port_info_struct *, struct serial_struct *); static int exser_get_lsr_info (struct port_info_struct *, unsigned int *); static void exser_send_break (struct port_info_struct *, int); static int exser_get_modem_info (struct port_info_struct *, unsigned int *); static int exser_set_modem_info (struct port_info_struct *, unsigned int, unsigned int *); static int exser_read_reg (struct port_info_struct * info, struct exser_read_reg *arg); static int exser_write_reg (struct port_info_struct * info, struct exser_read_reg *arg); static int startup_device_ports (void); static int exser_lsi_startup (struct port_info_struct * info); static void proc_remove (void); static int proc_create (void); _INLINE_ void exser_receive_chars (struct port_info_struct *, int *, unsigned char, int); _INLINE_ void exser_transmit_chars (struct port_info_struct *); _INLINE_ void exser_check_modem_status (struct port_info_struct *, int); static void clear_buffer (type_lsi_device *dev); static int proc_control_open (struct inode *inode, struct file *filp); static int proc_control_close (struct inode *inode, struct file *filp); static ssize_t proc_control_read (struct file *filp, char *buf, size_t count, loff_t *f_pos); static int proc_control_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); static int ioctl_special (unsigned int, unsigned long); static int proc_open (struct inode *inode, struct file *filp); static int proc_close (struct inode *inode, struct file *filp); static ssize_t proc_read (struct file *filp, char *buf, size_t count, loff_t *f_pos); static ssize_t proc_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos); static int proc_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); static loff_t proc_llseek (struct file *filp, loff_t off, int whence); static unsigned int proc_poll (struct file *filp, poll_table *wait); static int monitor_clear_buffer (type_lsi_device *dev, struct file *filp); static int monitor_open (struct inode *inode, struct file *filp); static int monitor_close (struct inode *inode, struct file *filp); static ssize_t monitor_read (struct file *filp, char *buf, size_t count, loff_t *f_pos); static ssize_t monitor_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos); // static int monitor_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); // static loff_t monitor_llseek (struct file *filp, loff_t off, int whence); static int atoi_base10 (char const *s); static int get_perms (int uid, int port, char *mode, int access); static int exec_helper (void *argv); #ifdef DEBUG_IO #define inb(port) (0) #define outb(val,port) #else #define inb(port) inb(port) #define outb(val,port) outb((val), (port)) #endif //------------------------------------------------------------------------ // module startup //------------------------------------------------------------------------ #ifdef MODULE int init_module (void) { int ret; printk (KERN_INFO "Loading %s port buffer driver V%s\n", module_name, __EXAR_VERSION); // initialize the exser, lsibuf, monitor, and proc drivers ret = exser_init(); if (verbose) printk (DEFAULT_PREFIX "driver loading complete\n"); return (ret); } //------------------------------------------------------------------------ // module shutdown //------------------------------------------------------------------------ void cleanup_module (void) { int i,err = 0; TRACE("cleanup_module"); if (verbose) printk (INFO_PREFIX "start unloading driver\n"); if ((err |= tty_unregister_driver (&cuB_driver))) printk (ERROR_PREFIX "Couldn't unregister callout driver\n"); if ((err |= tty_unregister_driver (&ttyB_driver))) printk (ERROR_PREFIX "Couldn't unregister serial driver\n"); proc_remove(); for(i=0; i= EXAR_CHIPS) { if (verbose) printk (ERROR_PREFIX "exser initchip@%d chip out of range %d\n", __LINE__, chip); return(-1); } if (hwconf == NULL) { if (verbose) printk (ERROR_PREFIX "exser initchip@%d hwconf pointer is NULL\n", __LINE__); return(-1); } #endif port = chip*EXAR_PORTS_PER_CHIP; info = &port_table[port]; for ( i=0; iports; i++, port++, info++ ) { info->port = port; info->base = hwconf->ioaddr[i]; info->irq = hwconf->irq; info->vector = hwconf->vector; info->vectormask = hwconf->vector_mask; info->rx_trigger = 14; info->baud_base = hwconf->baud_base[i]; info->buffer_flag = (hwconf->buffer_flags >> i) & 0x1; info->flags = ASYNC_SHARE_IRQ; info->type = hwconf->uart_type; if ( (info->type == PORT_16450) || (info->type == PORT_8250) ) info->xmit_fifo_size = 1; else info->xmit_fifo_size = EXAR_XMIT_FIFO_SIZE; info->custom_divisor = hwconf->baud_base[i] * 16; info->close_delay = 5*HZ/10; info->closing_wait = 4*HZ; // 1.14 driver used: 30*HZ; afp-2002-03-11 info->tqueue.routine = do_softint; info->tqueue.data = info; info->callout_termios = cuB_driver.init_termios; info->normal_termios = ttyB_driver.init_termios; init_waitqueue_head (&info->open_wait); init_waitqueue_head (&info->close_wait); init_waitqueue_head (&info->delta_msr_wait); } /* * Allocate the IRQ if necessary */ save_flags(flags); port = chip*EXAR_PORTS_PER_CHIP; info = &port_table[port]; cli(); retval = request_irq (hwconf->irq, exser_interrupt, IRQ_T(info), MODULE_NAME, info); if ( retval ) { restore_flags(flags); printk (ERROR_PREFIX "Request irq fail, IRQ (%d) may be conflit with another device\n", hwconf->irq); return(retval); } restore_flags(flags); /* check and reserve io ports for the driver */ retval = check_region(default_uart_db_values[chip].ioAddress, EXAR_IO_RANGE); if (retval < 0) { printk (ERROR_PREFIX "reserve io ports failed for region 0x%x to 0x%x.\n", default_uart_db_values[chip].ioAddress, default_uart_db_values[chip].ioAddress+EXAR_IO_RANGE-1); return(retval); } request_region (default_uart_db_values[chip].ioAddress, EXAR_IO_RANGE, MODULE_NAME); return (SUCCESS); } /*========================================================= * FUNCTION: set_dtedce * * PARAMETERS: port: from 1 to n * * DESCRIPTION: * * RETURNS: * *=========================================================*/ static int set_dtedce (int port, te_UART_DTEDCE mode) { int index, shift; byte mask, value; if (port < 1 || port > EXAR_PORTS) { return(EINVAL); } index = (port-1)/4; shift = 2*((port-1) % 4); mask = 0x03 << shift; value = (((byte)(mode)) & 0x03) << shift; current_dtedce_settings[index] &= ~mask; current_dtedce_settings[index] |= value; outb (current_dtedce_settings[index], index + _DB_DTEDCE_BASE); return (SUCCESS); } /*========================================================= * FUNCTION: get_dtedce * * PARAMETERS: port: from 1 to n * * DESCRIPTION: * * RETURNS: * *=========================================================*/ static int get_dtedce (int port, struct EXAR_STRUCT_DTEDCE_MODE *ioctl_data) { int index, shift; int error; byte value, mask; struct EXAR_STRUCT_DTEDCE_MODE my_data; if (port < 1 || port > EXAR_PORTS) { return(EINVAL); } error = verify_area(VERIFY_WRITE, (void *)ioctl_data, sizeof(struct EXAR_STRUCT_DTEDCE_MODE)); if ( error ) return (error); index = (port-1)/4; shift = 2*((port-1) % 4); mask = 0x03 << shift; value = (current_dtedce_settings[index] & mask) >> shift; my_data.port_number = port; my_data.port_mode = value; copy_to_user(ioctl_data, &my_data, sizeof(my_data)); return (SUCCESS); } static void set_dte_all_off (void) { int port, ioaddr; for (port=0, ioaddr=_DB_DTEDCE_BASE; port < EXAR_PORTS; port += 4) { outb (dtedce_all_off, ioaddr++); } } static void exser_getcfg(int chip, struct uart_control_struct *hwconf) { TRACE("exser_getcfg"); uart_settings[chip] = *hwconf; } #ifdef MODULE static #endif int exser_init(void) { int i, m, retval, b; int ret1, ret2; struct uart_control_struct hwconf; TRACE("exser_init"); /* Initialize the tty_driver structure and others */ memset (&ttyB_driver, 0, sizeof(ttyB_driver)); memset (&cuB_driver, 0, sizeof(cuB_driver)); memset (&port_table, 0, sizeof(port_table)); memset (&exvar_log, 0, sizeof(exvar_log)); memset (&GMStatus, 0, sizeof(GMStatus)); memset (&uart_settings, 0, sizeof(uart_settings)); memset (&exvar_tty, 0, sizeof(exvar_tty)); memset (&exvar_termios, 0, sizeof(exvar_termios)); memset (&exvar_termios_locked, 0, sizeof(exvar_termios_locked)); // locate lsi product specific information // describing the characteristics of the product // includes information about the uart chips // (uses default data if information is not present) get_uart_table (); // fills in default_uart_db_values for(i=0; iport_type) { retval = exser_startup(info); if ( retval ) { return(retval); } exser_change_speed (info, NULL, TRUE); info->IER &= ~UART_IER_MSI; info->MCR &= ~EXAR_UART_MCR_AFE; outb(info->MCR, info->base + UART_MCR); outb(info->IER, info->base + UART_IER); } else { // disable all others info->IER = 0; outb(info->IER, info->base + UART_IER); } } return (SUCCESS); } /* */ static void do_softint(void *private_) { struct port_info_struct * info = (struct port_info_struct *)private_; struct tty_struct * tty; TRACE("do_softint"); tty = info->tty; if ( !tty ) return; #ifdef __SMP__ lock_kernel(); #endif #if (LINUX_VERSION_CODE < VERSION_CODE(2,1,0)) if ( clear_bit(EXAR_EVENT_TXLOW, &info->event) ) { if ( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup ) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); } if ( clear_bit(EXAR_EVENT_HANGUP, &info->event) ) { tty_hangup(tty); } #else if ( test_and_clear_bit(EXAR_EVENT_TXLOW, &info->event) ) { if ( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup ) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); } if ( test_and_clear_bit(EXAR_EVENT_HANGUP, &info->event) ) { tty_hangup(tty); } #endif #ifdef __SMP__ unlock_kernel(); #endif } /* * This routine is called whenever a serial port is opened. It * enables interrupts for a serial port, linking in its async structure into * the IRQ chain. It also performs the serial-specific * initialization for the tty structure. * * Note: For character devices directly registered with the kernel, * the open returns a negative value to signify failure. Returning * a negative value below does not guarantee that the close * function will not be called. The tty layer above makes the * determination based on the driver_data tty_struct item. * */ static int rs_open(struct tty_struct * tty, struct file * filp) { struct port_info_struct * info; int retval, line; unsigned long page; TRACE("rs_open"); DEBUG_NULL(tty,-ENODEV) DEBUG_NULL(filp,-ENODEV) // if driver returns a negative number, then // make sure the the MOD_DEC_USE_COUNT is // called before returning the negative value // 12/2002 wmf - I don't know if the above is a true statement // 01/15/03 wmf - what I have found is that if we exit with an error, // someone calls rs_close // this does not happen with proc_open and monitor_open LOCAL_INC_USE_COUNT; //only 1 tty struct per device, so don't set it null tty->driver_data = NULL; line = PORTNO(tty); DEBUG_PRINT(DEBUG_OPEN, DEFAULT_PREFIX" rs_open line=%d\n", line); // invalid minor number - shouldn't happen if ( (line < 0) || (line > lsi_serial_channels) ) { return (-ENODEV); } // make sure that the base io address is not 0 info = port_table + line; if ( !info->base ) { return (-ENODEV); } retval = get_perms (filp->f_uid, line+1, "-c", (filp->f_flags & O_ACCMODE)); if ( retval != 0 ) { DEBUG_PRINT(DEBUG_OPEN, DEFAULT_PREFIX" rs_open@%d get_perms failed\n", __LINE__); return (-EACCES); } info->count++; tty->driver_data = info; info->tty = tty; // special check to make sure // only one application opens the buffer for read/write if ((filp->f_flags & O_ACCMODE) == O_RDWR) { if (PORT_TYPE_DEVICE == info->port_type) { // only allow one user in as RDWR lsi_devices[line].rdwr_count++; if (lsi_devices[line].rdwr_count > 1) { DEBUG_PRINT(DEBUG_OPEN, DEFAULT_PREFIX" rs_open@%d multiple opens count=%d\n", __LINE__, lsi_devices[line].rdwr_count); // rdwr_count will be dec. in rs_close return -EBUSY; } } } // allocate a buffer if ( !exvar_tmp_buf ) { page = get_free_page(GFP_KERNEL); if ( !page ) { return (-ENOMEM); } if ( exvar_tmp_buf ) free_page(page); else exvar_tmp_buf = (unsigned char *)page; } /* * Start up serial port */ #ifdef NON_DEVICE_ALLOWED if (PORT_TYPE_DEVICE != info->port_type) retval = exser_startup(info); else #endif retval = exser_lsi_startup(info); if ( retval ) { return (retval); } retval = block_til_ready(tty, filp, info); if ( retval ) { return (retval); } // we want special behavior for our buffered ports if ( (info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS) ) { if ( tty->driver.subtype == SERIAL_TYPE_NORMAL ) *tty->termios = info->normal_termios; else *tty->termios = info->callout_termios; exser_change_speed(info, NULL, FALSE); } info->session = current->session; info->pgrp = current->pgrp; return (SUCCESS); } #ifdef LW_DEBUG static char *tty_driver_name(struct tty_struct *tty) { static char buf[64]="Invalid tty"; TRACE("tty_driver_name"); DEBUG_NULL(tty,buf) if (tty) { sprintf(buf, "%s%d", tty->driver.name, PORTNO(tty)); } else { strncpy(buf, "NULL tty", sizeof(buf)-1); buf[sizeof(buf)-1] = 0; } return buf; } #endif //============================================================== // Reset the auto flow control of the chip. // Function was created to correct problem // during the closing of a port. The problem // was that the close operation would hang // if software flow control was active // and the port had received an XOFF // by accident (say because of cat'ing // a binary file to the terminal). // // static void exser_reset_auto_flowctl(struct port_info_struct * info) { unsigned long flags; unsigned char ier; unsigned char efr2; unsigned char mcr; TRACE("exser_reset_auto_flowctl"); DEBUG_NULLV(info) save_flags(flags); cli(); ier = inb(info->base + UART_IER); outb(0x00, info->base + UART_IER); // disable interrupts restore_flags(flags); // to be restored later efr2 = inb(info->base + EXAR_UART_EFR2); mcr = inb(info->base + UART_MCR); // found note in manual stating that this register must be set to 0 // before trying to change the auto flow control outb(0x00, info->base + EXAR_UART_EFR2); // before we can change flow control must zero these bits outb(0x00, info->base + UART_MCR); outb(efr2, info->base + EXAR_UART_EFR2 ); // restore flow control settings outb(mcr, info->base + UART_MCR); save_flags(flags); cli(); outb(ier, info->base + UART_IER); restore_flags(flags); } /* * This routine is called when the serial port gets closed. First, we * wait for the last remaining data to be sent. Then, we unlink its * async structure from the interrupt chain if necessary, and we free * that IRQ if nothing is left in the chain. */ static void rs_close(struct tty_struct * tty, struct file * filp) { struct port_info_struct * info; unsigned long flags; unsigned long timeout; unsigned int line; int count; TRACE("rs_close"); DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - start\n"); DEBUG_NULLV(tty) DEBUG_NULLV(filp) info = (struct port_info_struct *)tty->driver_data; line=PORTNO(tty); // clean-up if ( !info ) { LOCAL_DEC_USE_COUNT; return; } DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - xon indicator: %d\n", inb(info->base + 12)); DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - (1 - rcv xoff, 2 - rcv xon)\n"); //--------------------------------------------------------- // // if this port is a "DEVICE" port and software flow ctl // is enabled and there // //--------------------------------------------------------- if (PORT_TYPE_DEVICE == info->port_type && I_IXOFF(tty) && (count=tty->driver.chars_in_buffer(tty)) > 0) { tty_wait_until_sent(tty, HZ); if(tty->driver.chars_in_buffer(tty) > 0) { if ( tty->driver.flush_buffer ) tty->driver.flush_buffer(tty); if ( tty->ldisc.flush_buffer ) tty->ldisc.flush_buffer(tty); exser_reset_auto_flowctl(info); } } DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - xon indicator: %d\n", inb(info->base + 12)); //--------------------------------------------------------- // for device channels and software flow control // make sure we don't ever leave a device in an xoff state //--------------------------------------------------------- if (PORT_TYPE_DEVICE == info->port_type && I_IXOFF(tty) && (lsi_devices[line].rdwr_count == 1)) { DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - software flow ctl enabled - send XON\n"); info->x_char = START_CHAR(tty); save_flags(flags); cli(); info->IER |= UART_IER_THRI; // trigger Tx interrupt outb(info->IER, info->base + UART_IER); restore_flags(flags); } save_flags(flags); cli(); if ( tty_hung_up_p(filp) ) { LOCAL_DEC_USE_COUNT; restore_flags(flags); return; } if ( (tty->count == 1) && (info->count != 1) ) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ printk (ERROR_PREFIX "rs_close: bad serial port count; tty->count is 1, " "info->count is %d\n", info->count); info->count = 1; } if ( --info->count < 0 ) { printk (ERROR_PREFIX "rs_close: bad serial port count for ttys%d: %d\n", info->port, info->count); info->count = 0; } // decrement read/write count if necessary if ((filp->f_flags & O_ACCMODE) == O_RDWR) { DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close(1): rdwr_count = %d\n", lsi_devices[line].rdwr_count); if (PORT_TYPE_DEVICE == info->port_type) if (lsi_devices[line].rdwr_count) lsi_devices[line].rdwr_count--; } if ( info->count ) { LOCAL_DEC_USE_COUNT; restore_flags(flags); return; } info->flags |= ASYNC_CLOSING; /* * Save the termios structure, since this port may have * separate termios for callout and dialin. */ if ( info->flags & ASYNC_NORMAL_ACTIVE ) info->normal_termios = *tty->termios; if ( info->flags & ASYNC_CALLOUT_ACTIVE ) info->callout_termios = *tty->termios; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; if ( info->closing_wait != ASYNC_CLOSING_WAIT_NONE ) { DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - tty_wait_until_sent...\n"); DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" waiting %s...(%d)\n", tty_driver_name(tty), tty->driver.chars_in_buffer(tty)); tty_wait_until_sent(tty, info->closing_wait); DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - tty_wait_until_sent - done\n"); } /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the * interrupt driver to stop checking the data ready bit in the * line status register. */ #ifdef NON_DEVICE_ALLOWED if(PORT_TYPE_DEVICE != info->port_type) info->IER &= ~UART_IER_RLSI; #endif /* by William info->read_status_mask &= ~UART_LSR_DR; */ if ( info->flags & ASYNC_INITIALIZED ) { #ifdef NON_DEVICE_ALLOWED if(PORT_TYPE_DEVICE != info->port_type) outb(info->IER, info->base + UART_IER); #endif /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ timeout = jiffies + HZ; #ifndef DEBUG_IO while ( !(inb(info->base + UART_LSR) & UART_LSR_TEMT) ) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(5); if ( jiffies > timeout ) break; } #endif } DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - exser_shutdown...\n"); exser_shutdown(info); DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - exser_shutdown - done\n"); #ifdef NON_DEVICE_ALLOWED if (PORT_TYPE_DEVICE != info->port_type) { if ( tty->driver.flush_buffer ) tty->driver.flush_buffer(tty); if ( tty->ldisc.flush_buffer ) tty->ldisc.flush_buffer(tty); } #endif tty->closing = 0; info->event = 0; info->tty = 0; if ( info->blocked_open ) { if ( info->close_delay ) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE | ASYNC_CLOSING); DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - wake_up_interruptible...\n"); wake_up_interruptible(&info->close_wait); LOCAL_DEC_USE_COUNT; restore_flags(flags); } static int rs_write(struct tty_struct * tty, int from_user, const unsigned char * buf, int count) { int c, total = 0; struct port_info_struct *info; unsigned long flags; TRACE("rs_write"); DEBUG_NULL(tty,-EFAULT) DEBUG_NULL(buf,-EFAULT) DEBUG_NULL(tty->driver_data,-EFAULT) info = (struct port_info_struct *)tty->driver_data; if ( !tty || !info->xmit_buf || !exvar_tmp_buf ) return (SUCCESS); if ( from_user ) down(&exvar_tmp_buf_sem); save_flags(flags); while ( 1 ) { cli(); c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if ( c <= 0 ) break; if ( from_user ) { copy_from_user(exvar_tmp_buf, buf, c); /* used for debugging { int i; for(i=0; ixmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, exvar_tmp_buf, c); } else memcpy(info->xmit_buf + info->xmit_head, buf, c); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1); info->xmit_cnt += c; restore_flags(flags); buf += c; count -= c; total += c; } if ( from_user ) up(&exvar_tmp_buf_sem); if ( info->xmit_cnt && !tty->stopped && !tty->hw_stopped && !(info->IER & UART_IER_THRI) ) { info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); } restore_flags(flags); return (total); } static void rs_put_char(struct tty_struct * tty, unsigned char ch) { struct port_info_struct *info; unsigned long flags; TRACE("rs_put_char"); DEBUG_NULLV(tty) DEBUG_NULLV(tty->driver_data) info = (struct port_info_struct *)tty->driver_data; if ( !tty || !info->xmit_buf ) return; save_flags(flags); cli(); if ( info->xmit_cnt >= SERIAL_XMIT_SIZE - 1 ) { restore_flags(flags); return; } info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= SERIAL_XMIT_SIZE - 1; info->xmit_cnt++; /********************************************** why ??? *********** if ( !tty->stopped && !tty->hw_stopped && !(info->IER & UART_IER_THRI) ) { info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); } *****************************************************************/ restore_flags(flags); } static void exser_flush_chars(struct tty_struct * tty) { struct port_info_struct *info; unsigned long flags; TRACE("exser_flush_chars"); DEBUG_NULLV(tty) DEBUG_NULLV(tty->driver_data) info = (struct port_info_struct *)tty->driver_data; if ( info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !info->xmit_buf ) return; save_flags(flags); cli(); info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); restore_flags(flags); } static int exser_write_room(struct tty_struct * tty) { struct port_info_struct *info; int ret; TRACE("exser_write_room"); DEBUG_NULL(tty,-EFAULT) DEBUG_NULL(tty->driver_data,-EFAULT) info = (struct port_info_struct *)tty->driver_data; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; if ( ret < 0 ) ret = 0; return (ret); } static int exser_chars_in_buffer(struct tty_struct * tty) { struct port_info_struct *info; TRACE("exser_chars_in_buffer"); DEBUG_NULL(tty,-EFAULT) // DEBUG_NULL(tty->driver_data,-EFAULT) DEBUG_NULL(tty->driver_data, 1) info = (struct port_info_struct *)tty->driver_data; return (info->xmit_cnt); } static void rs_flush_buffer(struct tty_struct * tty) { struct port_info_struct *info; unsigned long flags; TRACE("rs_flush buffer"); DEBUG_NULLV(tty) DEBUG_NULLV(tty->driver_data) info = (struct port_info_struct *)tty->driver_data; save_flags(flags); cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; restore_flags(flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup ) (tty->ldisc.write_wakeup)(tty); } static int exser_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg) { int error; struct port_info_struct * info; int retval; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct *p_cuser; /* user space */ unsigned long templ; unsigned long flags; TRACE("exser_ioctl"); DEBUG_NULL(tty,-EFAULT) DEBUG_NULL(file,-EFAULT) DEBUG_NULL(tty->driver_data,-EFAULT) info = (struct port_info_struct *)tty->driver_data; if ( (cmd != TIOCGSERIAL) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT) ) { if ( tty->flags & (1 << TTY_IO_ERROR) ) { return (-EIO); } } switch ( cmd ) { case TCSBRK: /* SVID version: non-zero arg --> no break */ TRACE("ioctl: TCSBRK \n"); retval = tty_check_change(tty); if ( retval ) return (retval); tty_wait_until_sent(tty, 0); if ( !arg ) exser_send_break(info, HZ/4); /* 1/4 second */ return (SUCCESS); break; case TCSBRKP: /* support for POSIX tcsendbreak() */ TRACE("ioctl: TCSBRKP \n"); retval = tty_check_change(tty); if ( retval ) return (retval); tty_wait_until_sent(tty, 0); exser_send_break(info, arg ? arg*(HZ/10) : HZ/4); return (SUCCESS); break; case TIOCGSOFTCAR: TRACE("ioctl: TIOCGSOFTCAR \n"); error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(long)); if ( error ) return (error); put_to_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg); return (SUCCESS); break; case TIOCSSOFTCAR: TRACE("ioctl: TIOCSSOFTCAR \n"); error = verify_area(VERIFY_READ, (void *)arg, sizeof(long)); if ( error ) return (error); get_from_user(templ,(unsigned long *)arg); arg = templ; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return (SUCCESS); break; case TIOCMGET: TRACE("ioctl: TIOCMGET \n"); error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)); if ( error ) return (error); return (exser_get_modem_info(info, (unsigned int *)arg)); break; case TIOCMBIS: TRACE("ioctl: TIOCMBIS \n"); case TIOCMBIC: TRACE("ioctl: TIOCMBIC \n"); case TIOCMSET: TRACE("ioctl: TIOCMSET \n"); return (exser_set_modem_info(info, cmd, (unsigned int *)arg)); break; case TIOCGSERIAL: TRACE("ioctl: TIOCGSERIAL \n"); error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct serial_struct)); if ( error ) return (error); return (exser_get_serial_info(info, (struct serial_struct *)arg)); break; case TIOCSSERIAL: TRACE("ioctl: TIOCSSERIAL \n"); error = verify_area(VERIFY_READ, (void *)arg, sizeof(struct serial_struct)); if ( error ) return (error); return (exser_set_serial_info(info, (struct serial_struct *)arg)); break; case TIOCSERGETLSR: /* Get line status register */ TRACE("ioctl: TIOCSERGETLSR \n"); error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)); if ( error ) return (error); else return (exser_get_lsr_info(info, (unsigned int *)arg)); break; /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */ case TIOCMIWAIT: TRACE("ioctl: TIOCMIWAIT \n"); save_flags(flags); cli(); cprev = info->icount; /* note the counters on entry */ restore_flags(flags); while ( 1 ) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if ( signal_pending(current) ) return (-ERESTARTSYS); save_flags(flags); cli(); cnow = info->icount; /* atomic copy */ restore_flags(flags); if ( cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts ) return (-EIO); /* no change => error */ if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { return (SUCCESS); } cprev = cnow; } /* NOTREACHED */ break; /* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */ case TIOCGICOUNT: TRACE("ioctl: TIOCGICOUNT \n"); error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct serial_icounter_struct)); if ( error ) return (error); save_flags(flags); cli(); cnow = info->icount; restore_flags(flags); p_cuser = (struct serial_icounter_struct *)arg; put_to_user(cnow.cts, &p_cuser->cts); put_to_user(cnow.dsr, &p_cuser->dsr); put_to_user(cnow.rng, &p_cuser->rng); put_to_user(cnow.dcd, &p_cuser->dcd); return (SUCCESS); break; case EXAR_HighSpeedOn: TRACE("ioctl: EXAR_HighSpeedOn \n"); error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int)); if ( error ) return (error); put_to_user(info->baud_base != 115200 ? 1 : 0, (int *)arg); return (SUCCESS); break; case EXAR_READ_REG: error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct exser_read_reg)); if ( error ) return (error); else return (exser_read_reg(info, (struct exser_read_reg *)arg)); break; case EXAR_WRITE_REG: error = verify_area(VERIFY_READ, (void *)arg, sizeof(struct exser_read_reg)); if ( error ) return (error); else return (exser_write_reg(info, (struct exser_read_reg *)arg)); break; case READ_IO_PORT: error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct exser_read_reg)); if ( error ) return (error); else { unsigned char result; struct exser_read_reg *rr = (struct exser_read_reg *)arg; result = inb(rr->reg); put_to_user(result, &(rr->regvalue)); return (SUCCESS); } break; case BUFFER_ENABLE: { int port = PORTNO(tty); if (PORT_TYPE_DEVICE == info->port_type) { lsi_devices[port].stop_buffering_in_direct = !(int)arg; return 0; } return -EIO; } break; case QUERY_BUFFER_ENABLE: { int port = PORTNO(tty); if (PORT_TYPE_DEVICE == info->port_type) { put_user(!(lsi_devices[port].stop_buffering_in_direct), (int *)arg); return 0; } return -EIO; } break; case PARITY_MARK_SPACE: { unsigned char scratch; // LCR bits 5 4 3 // x x 0 no parity // 0 0 1 odd // 0 1 1 even // 1 0 1 mark (always '1') // 1 1 1 space (always '0') // 0 - do nothing; 1 - mark; 2 - space switch ((int)arg) { case 0: default: break; case 1: //mark scratch = (inb(info->base + UART_LCR) & ~(0x7<<3)) | (0x5<<3); outb(scratch, info->base + UART_LCR); break; case 2: //space scratch = inb(info->base + UART_LCR) | (0x7<<3); outb(scratch, info->base + UART_LCR); break; } } return (SUCCESS); break; case QUERY_PARITY: { // LCR bits 5 4 3 // x x 0 no parity // 0 0 1 odd // 0 1 1 even // 1 0 1 mark (always '1') // 1 1 1 space (always '0') // // return values: // 0 - no parity // 1 - odd parity // 2 - even parity // 3 - mark // 4 - space // int scratch = inb(info->base + UART_LCR)>>3 & 0x7; if (scratch & 0x1) { scratch >>= 1; ++scratch; put_user(scratch, (int *)arg); } else { scratch = 0; put_user(scratch, (int *)arg); } return (SUCCESS); } break; case RESET_RDWR_COUNT: { int port = PORTNO(tty); if (PORT_TYPE_DEVICE == info->port_type) { lsi_devices[port].rdwr_count = 0; return (SUCCESS); } return -EIO; } break; case QUERY_RDWR_COUNT: { int port = PORTNO(tty); if (PORT_TYPE_DEVICE == info->port_type) { put_user(lsi_devices[port].rdwr_count, (int *)arg); return (SUCCESS); } return -EIO; } break; case SET_PORT_TYPE: #ifdef NON_DEVICE_ALLOWED { unsigned long flags; int port = PORTNO(tty); if (PORT_TYPE_DEVICE != info->port_type && PORT_TYPE_DEVICE == (type_port)arg) { save_flags(flags); cli(); lsi_devices[port].rdwr_count = 0; info->port_type = PORT_TYPE_DEVICE; restore_flags(flags); // enable reception of data retval = exser_startup(info); if ( retval ) { return (retval); } exser_change_speed (info, NULL, TRUE); return (SUCCESS); } else if (PORT_TYPE_DEVICE == info->port_type && PORT_TYPE_DEVICE != (type_port)arg) { save_flags(flags); cli(); lsi_devices[port].rdwr_count = 0; info->port_type = (type_port)arg; restore_flags(flags); // disable reception of data exser_shutdown(info); return (SUCCESS); } else if ((type_port)arg == PORT_TYPE_OFF || (type_port)arg == PORT_TYPE_GETTY || (type_port)arg == PORT_TYPE_NON_GETTY) return (SUCCESS); return -EIO; } #else return (-ENOTSUPP); #endif break; case QUERY_PORT_TYPE: #ifdef NON_DEVICE_ALLOWED { if (info->port_type < PORT_TYPE_LIST_LAST) { put_user(info->port_type, (int *)arg); return (SUCCESS); } return -EIO; } #else return (-ENOTSUPP); #endif break; /* tty driver handles these, by calling this drivers set_termios */ case TCSETS: case TCSETSW: case TCGETS: case TCFLSH: case TCSETSF: return (-ENOIOCTLCMD); break; case EXAR_SET_DTEDCE: { int port = PORTNO(tty); // starts at 0 return (set_dtedce (port+1, ((struct EXAR_STRUCT_DTEDCE_MODE *)arg)->port_mode)); } break; case EXAR_GET_DTEDCE: { int port = PORTNO(tty); // starts at 0 return (get_dtedce (port+1, (struct EXAR_STRUCT_DTEDCE_MODE *)arg)); } break; case EXAR_GETDATACOUNT : // user for SNMP counter request error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct exser_log)); if(error) return (error); copy_to_user((struct exser_log *)arg, &exvar_log, sizeof(exvar_log)); break; default: return (-ENOIOCTLCMD); break; } return (SUCCESS); } static int exser_read_reg(struct port_info_struct * info, struct exser_read_reg *arg) { unsigned char result; unsigned int reg; unsigned long flags; int error; TRACE("exser_read_reg"); DEBUG_NULL(info,-EFAULT) DEBUG_NULL(arg,-EFAULT) error = verify_area(VERIFY_READ, (void *)arg->reg, sizeof(struct exser_read_reg)); if ( error ) return (error); get_from_user(reg,&(arg->reg)); save_flags(flags); cli(); result = inb(info->base + reg); restore_flags(flags); DEBUG_PRINT (DEBUG_INFO, "%s: Read base=0x%08x, Reg=0x%08x, value=0x%08x\n", module_name, info->base, reg, result); put_to_user(result, &(arg->regvalue)); return (SUCCESS); } static int exser_write_reg(struct port_info_struct * info, struct exser_read_reg *arg) { unsigned int reg, value; TRACE("exser_write_reg"); DEBUG_NULL(info,-EFAULT) DEBUG_NULL(arg,-EFAULT) get_from_user(reg,&(arg->reg)); get_from_user(value,&(arg->regvalue)); outb(value, info->base + reg); DEBUG_PRINT (DEBUG_INFO, "%s: write base=0x%08x, Reg=0x%08x, value=0x%08x\n", module_name, info->base, reg, value); return (SUCCESS); } /* * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. */ static void rs_throttle(struct tty_struct * tty) { struct port_info_struct *info; unsigned long flags; TRACE("rs_throttle"); DEBUG_NULLV(tty) DEBUG_NULLV(tty->driver_data) #if 0 // 1/14/03 if ( PORTNO(tty) == CONTROL_MINOR_NUMBER ) { return; } #endif info = (struct port_info_struct *)tty->driver_data; if ( I_IXOFF(tty) ) { info->x_char = STOP_CHAR(tty); save_flags(flags); cli(); // outb(info->IER, 0); don't know what this is supposed to to??? info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); /* force Tx interrupt */ restore_flags(flags); } if ( info->tty->termios->c_cflag & CRTSCTS ) { info->MCR &= ~UART_MCR_RTS; save_flags(flags); cli(); outb(info->MCR, info->base + UART_MCR); restore_flags(flags); exvar_log.rts_cnt[info->port]++; } } /* */ static void rs_unthrottle(struct tty_struct * tty) { struct port_info_struct *info; unsigned long flags; TRACE("rs_unthrottle"); DEBUG_NULLV(tty) DEBUG_NULLV(tty->driver_data) info = (struct port_info_struct *)tty->driver_data; if ( I_IXOFF(tty) ) { if ( info->x_char ) info->x_char = 0; else { info->x_char = START_CHAR(tty); save_flags(flags); cli(); info->IER |= UART_IER_THRI; /* force Tx interrupt */ outb(info->IER, info->base + UART_IER); restore_flags(flags); } } if ( info->tty->termios->c_cflag & CRTSCTS ) { info->MCR |= UART_MCR_RTS; save_flags(flags); cli(); outb(info->MCR, info->base + UART_MCR); restore_flags(flags); exvar_log.rts_cnt[info->port]++; } } /* */ static void exser_set_termios(struct tty_struct * tty, struct termios * old_termios) { struct port_info_struct *info; TRACE("exser_set termios"); DEBUG_NULLV(tty) DEBUG_NULLV(old_termios) DEBUG_NULLV(tty->driver_data) info = (struct port_info_struct *)tty->driver_data; /* 8-2-99 by William if ( (tty->termios->c_cflag == old_termios->c_cflag) && (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag)) ) return; exser_change_speed (info, old_termios); if ( (old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS) ) { tty->hw_stopped = 0; exser_start(tty); } */ DEBUG_PRINT(DEBUG_TERMIOS, DEFAULT_PREFIX" set termios exser@%d \n", __LINE__); if ( (tty->termios->c_cflag != old_termios->c_cflag) || (RELEVANT_IFLAG(tty->termios->c_iflag) != RELEVANT_IFLAG(old_termios->c_iflag)) ) { DEBUG_PRINT(DEBUG_TERMIOS, DEFAULT_PREFIX" set_termios calling change_speed\n"); // similar to change_speed, but resets the channel // and sets the auto config parameters // also waits for the channel's fifo to clear exser_change_speed(info, old_termios, FALSE); if ( (old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS) ) { tty->hw_stopped = 0; exser_start(tty); } } /* Handle sw stopped */ if ( (old_termios->c_iflag & IXON) && !(tty->termios->c_iflag & IXON) ) { tty->stopped = 0; DEBUG_PRINT(DEBUG_TERMIOS, DEFAULT_PREFIX" set termios exser@%d (before start 2)\n", __LINE__); exser_start(tty); } } /* * exser_stop() and exser_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. */ static void exser_stop(struct tty_struct * tty) { struct port_info_struct *info; unsigned long flags; TRACE("exser_stop"); DEBUG_NULLV(tty) DEBUG_NULLV(tty->driver_data) info = (struct port_info_struct *)tty->driver_data; save_flags(flags); cli(); if ( info->IER & UART_IER_THRI ) { info->IER &= ~UART_IER_THRI; outb(info->IER, info->base + UART_IER); } restore_flags(flags); } /* */ static void exser_start(struct tty_struct * tty) { struct port_info_struct *info; unsigned long flags; TRACE("exser_start"); DEBUG_NULLV(tty) DEBUG_NULLV(tty->driver_data) info = (struct port_info_struct *)tty->driver_data; save_flags(flags); cli(); if ( info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI) ) { info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); } restore_flags(flags); } /* * This routine is called by tty_hangup() when a hangup is signaled. */ static void exser_hangup(struct tty_struct * tty) { struct port_info_struct * info; TRACE("exser_hangup"); DEBUG_NULLV(tty) DEBUG_NULLV(tty->driver_data) info = (struct port_info_struct *)tty->driver_data; rs_flush_buffer(tty); exser_shutdown(info); info->event = 0; info->count = 0; info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); info->tty = 0; wake_up_interruptible(&info->open_wait); } /* * This is the serial driver's generic interrupt routine */ static void exser_interrupt(int irq, void *dev_id, struct pt_regs * regs) { int status, i; struct port_info_struct * info; struct port_info_struct * port; int max, bits, msr, isr_bits; volatile int irqbits; int pass_counter = 0; unsigned char device_channel = ~0; unsigned char chip_number=0; unsigned char more_interrupts=2; //return; // for debugging auto flow control TRACE("exser_interrupt"); DEBUG_NULLV(dev_id) DEBUG_NULLV(regs) port = 0; for (i=0; i EXAR_PORTS) printk (ERROR_PREFIX "isr: bad chip number found (%d)\n", chip_number); if (i==EXAR_CHIPS) { printk (ERROR_PREFIX "isr: could not find port\n"); return; } if (port==0) { printk (ERROR_PREFIX "isr: port var == 0\n"); return; } max = EXAR_PORTS_PER_CHIP; while ( more_interrupts ) { irqbits = inb(port->vector) & port->vectormask; for ( i=0, bits=1; itty) { status = inb(info->base + UART_LSR) & info->read_status_mask; if ( status & UART_LSR_DR ) exser_receive_chars(info, &status, FALSE, device_channel); // we can clear the TXRDY interrupt // also clears any XON/XOFF special character interrupts isr_bits = inb(info->base + EXAR_UART_ISR); // clear any msr interrupts // also clears RTS/DTR or CTS/DSR status change interrupts msr = inb(info->base + UART_MSR); continue; } if ( (inb(info->base + UART_IIR) & UART_IIR_NO_INT) ) // removed "!info->tty ||" afp 04-25-02 continue; status = inb(info->base + UART_LSR) & info->read_status_mask; if ( status & UART_LSR_DR ) exser_receive_chars(info, &status, TRUE, device_channel); msr = inb(info->base + UART_MSR); if ( msr & UART_MSR_ANY_DELTA ) { exser_check_modem_status(info, msr); } if ( status & UART_LSR_THRE ) { /* 8-2-99 by William if ( info->x_char || (info->xmit_cnt > 0) ) */ isr_bits = inb(info->base + EXAR_UART_ISR); exser_transmit_chars(info); } } // end for --more_interrupts; if ( pass_counter++ > EXAR_ISR_PASS_LIMIT ) { printk (ERROR_PREFIX " exser interrupt (%d) loop break mask@%0x=%0x\n", irq, port->vector, inb(port->vector) & port->vectormask); break; /* Prevent infinite loops */ } } //end while } _INLINE_ void exser_receive_chars(struct port_info_struct *info, int *status, unsigned char normal_receive, int device_channel) { int ignored = 0; int cnt = 0; struct tty_struct * tty = info->tty; unsigned char ch; unsigned char lsi_status = (unsigned char)*status; unsigned char rcv_cnt; type_lsi_device * p = NULL; //----------------------------------------------------------------------- do { rcv_cnt = inb(info->base + EXAR_UART_RFCNT); ch = inb(info->base + UART_RX); DEBUG_PRINT(DEBUG_ISR, ISR_PREFIX">%x", ch); /* add to history buffer */ if(PORT_TYPE_DEVICE == info->port_type) { if (rcv_cnt) { p = &lsi_devices[device_channel]; // keep a count of the types of communications errors if any occur // only do this if it will not be performed below for if (!normal_receive && (lsi_status & EXAR_UART_LSR_SPECIAL) ) { lsi_status &= EXAR_UART_LSR_SPECIAL; if ( lsi_status & 0x10) //UART_LSR_BI ) { // break detected DEBUG_PRINT(DEBUG_COMM_ERR, DEFAULT_PREFIX"rcv isr: break received (%d)\n", device_channel); exvar_log.break_rcv_cnt[info->port]++; } if ( lsi_status & 0x04) //UART_LSR_PE ) { // parity DEBUG_PRINT(DEBUG_COMM_ERR, DEFAULT_PREFIX"rcv isr: parity error (%d)\n", device_channel); exvar_log.parity_err_cnt[info->port]++; } if ( lsi_status & 0x08) //UART_LSR_FE ) { // frame DEBUG_PRINT(DEBUG_COMM_ERR, DEFAULT_PREFIX"rcv isr: framing error (%d)\n", device_channel); exvar_log.framing_err_cnt[info->port]++; } if ( lsi_status & 0x02) //UART_LSR_OE ) { // overrun DEBUG_PRINT(DEBUG_COMM_ERR, DEFAULT_PREFIX"rcv isr: overrun error (%d)\n", device_channel); exvar_log.overrun_err_cnt[info->port]++; } else // something weird happened DEBUG_PRINT(DEBUG_COMM_ERR, DEFAULT_PREFIX"rcv isr: undefined %x (%d)\n", lsi_status & EXAR_UART_LSR_SPECIAL, device_channel); } // add to lsibuf device's memory buffer if (0 == p->rdwr_count || !p->stop_buffering_in_direct) { *(p->head) = ch; p->head++; if(p->head > p->pbuf_end) p->head = p->pbuf; if(p->head == p->tail) { p->tail++; if(p->tail > p->pbuf_end) p->tail = p->pbuf; } // calculate the new size of the file // for proc entry file size if (p->head < p->tail) { p->ent->size = p->size - 1; } else { p->ent->size = p->head - p->tail + 1; if (p->ent->size > p->size) p->ent->size = p->size; p->ent->size--; } } wake_up_interruptible(&p->in_queue); #ifndef NO_MONITOR // add to monitor buffer too p = &lsi_monitor_devices[device_channel]; *(p->head) = ch; p->head++; if(p->head > p->pbuf_end) p->head = p->pbuf; if(p->head == p->tail) { p->tail++; if(p->tail > p->pbuf_end) p->tail = p->pbuf; } wake_up_interruptible(&p->in_queue); #endif } } if(normal_receive) { if ( *status & info->ignore_status_mask ) { if ( ++ignored > 100 ) break; } else { if ( tty->flip.count >= TTY_FLIPBUF_SIZE ) { break; } tty->flip.count++; if ( *status & EXAR_UART_LSR_SPECIAL ) { if ( *status & UART_LSR_BI ) { *tty->flip.flag_buf_ptr++ = TTY_BREAK; if ( info->flags & ASYNC_SAK ) do_SAK(tty); exvar_log.break_rcv_cnt[info->port]++; } else if ( *status & UART_LSR_PE ) { *tty->flip.flag_buf_ptr++ = TTY_PARITY; exvar_log.parity_err_cnt[info->port]++; } else if ( *status & UART_LSR_FE ) { *tty->flip.flag_buf_ptr++ = TTY_FRAME; exvar_log.framing_err_cnt[info->port]++; } else if ( *status & UART_LSR_OE ) { *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; exvar_log.overrun_err_cnt[info->port]++; } else *tty->flip.flag_buf_ptr++ = 0; } else *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = ch; cnt++; } } #ifdef LSI_DEBUG_ISR printk (":b=%x", info->base); printk (":rsm=%x", info->read_status_mask); printk (":ier=%x", inb(info->base + UART_IER)); printk (":isr=%x", inb(info->base + 2)); // UART_IIR #endif lsi_status = *status = inb(info->base + UART_LSR); #ifdef LSI_DEBUG_ISR printk (":s=%x", *status); #endif if (normal_receive) // afp - must always check for DataReady *status = *status & (UART_LSR_DR | info->read_status_mask); } while ( *status & UART_LSR_DR ); // tally the number of bytes received exvar_log.rxcnt[info->port] += cnt; if(normal_receive) { #if (LINUX_VERSION_CODE >= VERSION_CODE(2,1,0)) queue_task(&tty->flip.tqueue, &tq_timer); #else queue_task_irq_off(&tty->flip.tqueue, &tq_timer); #endif } } /* */ _INLINE_ void exser_transmit_chars(struct port_info_struct *info) { int count, cnt; TRACE("exser_transmit_chars"); DEBUG_NULLV(info) if ( info->x_char ) { outb(info->x_char, info->base + UART_TX); info->x_char = 0; exvar_log.txcnt[info->port]++; DEBUG_PRINT(DEBUG_TRACE, "exvar_log.txcnt[%d]=%d",info->port, exvar_log.txcnt[info->port]); return; } /* disable the tx interrupt and exit if any of the following */ /* become true: */ if ( (info->xmit_cnt <= 0) || info->tty->stopped || info->tty->hw_stopped ) { info->IER &= ~UART_IER_THRI; outb(info->IER, info->base + UART_IER); return; } cnt = info->xmit_cnt; count = info->xmit_fifo_size; do { /* used for debugging int cnt; DEBUG_PRINT(DEBUG_TRACE, TRACE_PREFIX"i0x%x ", info->xmit_buf[info->xmit_tail]); */ outb(info->xmit_buf[info->xmit_tail], info->base + UART_TX); /* used for debugging if (info->xmit_buf[info->xmit_tail] == 0x0a) { volatile int cnt; TRACE("isr waiting for cr\n"); while ( (cnt=inb(info->base + EXAR_UART_TFCNT)) != 0) ; #ifndef DEBUG_IO while ( (inb(info->base + UART_LSR) & 0x60) == 0) ; #endif TRACE("isr thr empty\n"); for(cnt=0; cnt<65000; ++cnt) ; } */ info->xmit_tail++; info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1); if ( --info->xmit_cnt <= 0 ) break; } while ( --count > 0 ); exvar_log.txcnt[info->port] += (cnt - info->xmit_cnt); if ( info->xmit_cnt < WAKEUP_CHARS ) { set_bit(EXAR_EVENT_TXLOW,&info->event); schedule_task(&info->tqueue); } if ( info->xmit_cnt <= 0 ) { info->IER &= ~UART_IER_THRI; outb(info->IER, info->base + UART_IER); } } _INLINE_ void exser_check_modem_status(struct port_info_struct *info, int status) { TRACE("exser_check_modem_status"); DEBUG_NULLV(info) /* update input line counters */ if ( status & UART_MSR_TERI ) info->icount.rng++; if ( status & UART_MSR_DDSR ) info->icount.dsr++; if ( status & UART_MSR_DDCD ) info->icount.dcd++; if ( status & UART_MSR_DCTS ) info->icount.cts++; wake_up_interruptible(&info->delta_msr_wait); if ( (info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD) ) { if ( status & UART_MSR_DCD ) wake_up_interruptible(&info->open_wait); else if ( !((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_CALLOUT_NOHUP)) ) { set_bit(EXAR_EVENT_HANGUP, &info->event); if (info->tty) tty_hangup(info->tty); } #if (LINUX_VERSION_CODE >= VERSION_CODE(2,1,0)) // queue_task(&info->tqueue, &tq_scheduler); schedule_task(&info->tqueue); #else queue_task_irq_off(&info->tqueue, &tq_scheduler); #endif } if ( info->flags & ASYNC_CTS_FLOW ) { if ( info->tty->hw_stopped ) { if ( status & UART_MSR_CTS ) { info->tty->hw_stopped = 0; info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); set_bit(EXAR_EVENT_TXLOW,&info->event); #if (LINUX_VERSION_CODE >= VERSION_CODE(2,1,0)) schedule_task(&info->tqueue); #else queue_task_irq_off(&info->tqueue, &tq_scheduler); #endif } } else { if ( !(status & UART_MSR_CTS) ) { info->tty->hw_stopped = 1; info->IER &= ~UART_IER_THRI; outb(info->IER, info->base + UART_IER); } } } } static int block_til_ready(struct tty_struct *tty, struct file * filp, struct port_info_struct *info) { DECLARE_WAITQUEUE(wait, current); int retval; int do_clocal = 0; unsigned long flags; TRACE("block til ready"); DEBUG_NULL(tty,-EFAULT) DEBUG_NULL(filp,-EFAULT) DEBUG_NULL(info,-EFAULT) /* * If the device is in the middle of being closed, then block * until it's done, and then try again. */ if ( tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING) ) { if ( info->flags & ASYNC_CLOSING ) interruptible_sleep_on(&info->close_wait); #ifdef SERIAL_DO_RESTART if ( info->flags & ASYNC_HUP_NOTIFY ) return (-EAGAIN); else return (-ERESTARTSYS); #else return (-EAGAIN); #endif } /* * If this is a callout device, then just make sure the normal * device isn't being used. */ if ( tty->driver.subtype == SERIAL_TYPE_CALLOUT ) { if ( info->flags & ASYNC_NORMAL_ACTIVE ) return (-EBUSY); if ( (info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_SESSION_LOCKOUT) && (info->session != current->session) ) return (-EBUSY); if ( (info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_PGRP_LOCKOUT) && (info->pgrp != current->pgrp) ) return (-EBUSY); info->flags |= ASYNC_CALLOUT_ACTIVE; return (SUCCESS); } /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. */ if ( (filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR)) ) { if ( info->flags & ASYNC_CALLOUT_ACTIVE ) return (-EBUSY); info->flags |= ASYNC_NORMAL_ACTIVE; return (SUCCESS); } if ( info->flags & ASYNC_CALLOUT_ACTIVE ) { if ( info->normal_termios.c_cflag & CLOCAL ) do_clocal = 1; } else { if ( tty->termios->c_cflag & CLOCAL ) do_clocal = 1; } /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in * this loop, info->count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; add_wait_queue(&info->open_wait, &wait); save_flags(flags); cli(); if ( !tty_hung_up_p(filp) ) info->count--; restore_flags(flags); info->blocked_open++; while ( 1 ) { save_flags(flags); cli(); if ( !(info->flags & ASYNC_CALLOUT_ACTIVE) ) outb(inb(info->base + UART_MCR) | UART_MCR_DTR | UART_MCR_RTS, info->base + UART_MCR); restore_flags(flags); current->state = TASK_INTERRUPTIBLE; if ( tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED) ) { #ifdef SERIAL_DO_RESTART if ( info->flags & ASYNC_HUP_NOTIFY ) retval = -EAGAIN; else retval = -ERESTARTSYS; #else retval = -EAGAIN; #endif break; } if ( !(info->flags & ASYNC_CALLOUT_ACTIVE) && !(info->flags & ASYNC_CLOSING) && (do_clocal || (inb(info->base + UART_MSR) & UART_MSR_DCD)) ) break; if ( signal_pending(current) ) { retval = -ERESTARTSYS; break; } schedule(); } current->state = TASK_RUNNING; remove_wait_queue(&info->open_wait, &wait); if ( !tty_hung_up_p(filp) ) info->count++; info->blocked_open--; if ( retval ) return (retval); info->flags |= ASYNC_NORMAL_ACTIVE; return (SUCCESS); } static int exser_startup(struct port_info_struct * info) { unsigned long flags; unsigned long page; TRACE("exser_startup"); DEBUG_NULL(info,-EFAULT) page = get_free_page(GFP_KERNEL); if ( !page ) return (-ENOMEM); save_flags(flags); cli(); if ( info->flags & ASYNC_INITIALIZED ) { free_page(page); restore_flags(flags); return (SUCCESS); } if ( !info->base || !info->type ) { if ( info->tty ) set_bit(TTY_IO_ERROR, &info->tty->flags); free_page(page); restore_flags(flags); return (SUCCESS); } if ( info->xmit_buf ) free_page(page); else info->xmit_buf = (unsigned char *)page; /* * Clear the FIFO buffers and disable them * (they will be reenabled in exser_change_speed()) */ if ( info->xmit_fifo_size > 1 ) outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), info->base + UART_FCR); /* * At this point there's no way the LSR could still be 0xFF; * if it is, then bail out, because there's likely no UART * here. */ if ( inb(info->base + UART_LSR) == 0xff ) { restore_flags(flags); if ( suser() ) { if ( info->tty ) set_bit(TTY_IO_ERROR, &info->tty->flags); return (SUCCESS); } else return (-ENODEV); } /* * Clear the interrupt registers. */ (void)inb(info->base + UART_LSR); (void)inb(info->base + UART_RX); (void)inb(info->base + UART_IIR); (void)inb(info->base + UART_MSR); /* * Now, initialize the UART */ outb(UART_LCR_WLEN8, info->base + UART_LCR); /* reset DLAB */ info->MCR = UART_MCR_DTR | UART_MCR_RTS; outb(info->MCR, info->base + UART_MCR); /* * Finally, enable interrupts */ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; outb(info->IER, info->base + UART_IER); /* enable interrupts */ /* * And clear the interrupt registers again for luck. */ (void)inb(info->base + UART_LSR); (void)inb(info->base + UART_RX); (void)inb(info->base + UART_IIR); (void)inb(info->base + UART_MSR); #if (LINUX_VERSION_CODE < VERSION_CODE(2,1,0)) if ( info->tty ) clear_bit(TTY_IO_ERROR, &info->tty->flags); #else if ( info->tty ) test_and_clear_bit(TTY_IO_ERROR, &info->tty->flags); #endif info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * and set the speed of the serial port */ exser_change_speed(info, NULL, FALSE); info->flags |= ASYNC_INITIALIZED; restore_flags(flags); return (SUCCESS); } //********************************************************** // // special startup for device ports // //********************************************************** static int exser_lsi_startup(struct port_info_struct * info) { unsigned long flags; unsigned long page; TRACE("exser_lsi_startup"); DEBUG_NULL(info,-EFAULT) page = get_free_page(GFP_KERNEL); if ( !page ) return (-ENOMEM); save_flags(flags); cli(); if ( info->flags & ASYNC_INITIALIZED ) { free_page(page); restore_flags(flags); return (SUCCESS); } if ( !info->base || !info->type ) { if ( info->tty ) set_bit(TTY_IO_ERROR, &info->tty->flags); free_page(page); restore_flags(flags); return (SUCCESS); } if ( info->xmit_buf ) free_page(page); else info->xmit_buf = (unsigned char *)page; /* * Clear the FIFO buffers and disable them * (they will be reenabled in exser_change_speed()) */ // because the uart is already "on", we don't want to clear the fifo's here // if ( info->xmit_fifo_size == 16 ) // outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), info->base + UART_FCR); /* * At this point there's no way the LSR could still be 0xFF; * if it is, then bail out, because there's likely no UART * here. * if ( inb(info->base + UART_LSR) == 0xff ) { restore_flags(flags); if ( suser() ) { if ( info->tty ) set_bit(TTY_IO_ERROR, &info->tty->flags); return (SUCCESS); } else return (-ENODEV); } */ /* * Clear the interrupt registers. */ // we want to leave the interrupt register alone // (void)inb(info->base + UART_LSR); // (void)inb(info->base + UART_RX); // (void)inb(info->base + UART_IIR); // (void)inb(info->base + UART_MSR); /* * Now, initialize the UART */ // afp don't reset the LCR because the last settings will be wiped out // outb(UART_LCR_WLEN8, info->base + UART_LCR); /* reset DLAB */ //??? this should not be a problem, but... info->MCR = UART_MCR_DTR | UART_MCR_RTS; // outb(info->MCR, info->base + UART_MCR); /* * Finally, enable interrupts */ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; // outb(info->IER, info->base + UART_IER); /* enable interrupts */ /* * And clear the interrupt registers again for luck. */ // (void)inb(info->base + UART_LSR); // (void)inb(info->base + UART_RX); // (void)inb(info->base + UART_IIR); // (void)inb(info->base + UART_MSR); #if (LINUX_VERSION_CODE < VERSION_CODE(2,1,0)) if ( info->tty ) clear_bit(TTY_IO_ERROR, &info->tty->flags); #else if ( info->tty ) test_and_clear_bit(TTY_IO_ERROR, &info->tty->flags); #endif info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * and set the speed of the serial port */ exser_change_speed(info, NULL, FALSE); info->flags |= ASYNC_INITIALIZED; restore_flags(flags); TRACE("leave lsi startup\n"); return (SUCCESS); } /* * This routine will shutdown a serial port; interrupts maybe disabled, and * DTR is dropped if the hangup on close termio flag is on. */ static void exser_shutdown(struct port_info_struct * info) { unsigned long flags; TRACE("exser_shutdown"); DEBUG_NULLV(info) if ( !(info->flags & ASYNC_INITIALIZED) ) return; save_flags(flags); cli(); /* Disable interrupts */ /* * clear delta_msr_wait queue to avoid mem leaks: we may free the irq * here so the queue might never be waken up */ wake_up_interruptible(&info->delta_msr_wait); /* * Free the IRQ, if necessary */ if ( info->xmit_buf ) { free_page((unsigned long)info->xmit_buf); info->xmit_buf = 0; } #ifdef NON_DEVICE_ALLOWED if(PORT_TYPE_DEVICE != info->port_type) { info->IER = 0; outb(0x00, info->base + UART_IER); /* disable all intrs */ // ??? debug the following code, the hang-up should be skipped // ??? maybe info->MCR is not set properly // ??? *** this was dropping the line when exiting minicom // ??? *** even though we told minicom not to //if ( !info->tty || (info->tty->termios->c_cflag & HUPCL) ) // info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS); //outb(info->MCR, info->base + UART_MCR); /* clear Rx/Tx FIFO's */ outb((UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), info->base + UART_FCR); /* read data port to reset things */ (void)inb(info->base + UART_RX); } #else info->IER &= ~(UART_IER_MSI | UART_IER_THRI); outb(info->IER, info->base + UART_IER); // disable MSI & THRI intrs // disable break condition, it might be set outb(inb(info->base + UART_LCR) & ~ UART_LCR_SBC, info->base + UART_LCR); #endif if ( info->tty ) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags); } static int exser_get_quotient(struct port_info_struct *info, int baud_code) { int i; int quot; TRACE("exser_get_quotient"); DEBUG_NULL(info,-EFAULT); switch( baud_code ) { case B921600 : i = 20; break; case B460800 : i = 19; break; case B230400 : i = 18; break; case B115200 : i = 17; break; case B57600 : i = 16; break; case B38400 : i = 15; break; case B19200 : i = 14; break; case B9600 : i = 13; break; case B4800 : i = 12; break; case B2400 : i = 11; break; case B1800 : i = 10; break; case B1200 : i = 9; break; case B600 : i = 8; break; case B300 : i = 7; break; case B200 : i = 6; break; case B150 : i = 5; break; case B134 : i = 4; break; case B110 : i = 3; break; case B75 : i = 2; break; case B50 : i = 1; break; default : i = 0; break; } DEBUG_PRINT(DEBUG_SPEED, "change speed to (i = %d)\n", i); if (i==15) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) i = 16; /* 57600 bps */ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) i = 17; /* 115200 bps */ #ifdef ASYNC_SPD_SHI if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) i = 18; #endif #ifdef ASYNC_SPD_WARP if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) i = 19; #endif } if ( exvar_baud_table[i] == 134 ) { quot = (2 * info->baud_base / 269); } else if ( exvar_baud_table[i] ) { quot = info->baud_base / exvar_baud_table[i]; if(0==quot) quot = 1; } else { quot = 0; } if (0 == quot) quot = 1; return quot; } /* exser_get_quotient */ static void exser_set_auto_flowctl(unsigned int chip_baseaddr, unsigned int channel_baseaddr, unsigned char ier, unsigned int t_trg, unsigned int r_trg, unsigned int hysteresis, unsigned char flowcontrol) { unsigned long flags; TRACE("exser_set_auto_flowctl"); save_flags(flags); cli(); // outb((0x01 << ((channel_baseaddr&0x00F0) >> 4)), chip_baseaddr|0x8a); // reset channel 0 // outb(0x01, baseaddr|0x8e); // enable simultaneous write to all channels outb(0x00, channel_baseaddr + UART_IER); // disable interrupts restore_flags(flags); // found note in manual stating that this register must be set to 0 // before trying to change the auto flow control outb(0x00, channel_baseaddr + EXAR_UART_EFR2); // before we can change flow control must zero these bits outb(0x00, channel_baseaddr + UART_MCR); // outb(0x07, channel_baseaddr + UART_FCR); // enable & flush fifo's // outb(0x80, channel_baseaddr + UART_LCR); // set DLAB // outb(quot & 0xff, channel_baseaddr + UART_DLL); // LS of divisor // outb(quot >> 8, channel_baseaddr + UART_DLM); // MS of divisor // outb(0x03, channel_baseaddr + UART_LCR); // reset DLAB outb(0xc0 | hysteresis, channel_baseaddr + EXAR_UART_FCTR); // trigger table D selected with hysteresis outb(r_trg, channel_baseaddr + EXAR_UART_RXTRIG); // RX trigger level - usually 64 outb(t_trg, channel_baseaddr + EXAR_UART_TXTRIG); // TX trigger level - usually 16 // auto flow control //outb('s', channel_baseaddr + EXAR_UART_XOFF1); outb(0x13, channel_baseaddr + EXAR_UART_XOFF1); //outb('g', channel_baseaddr + EXAR_UART_XON1); outb(0x11, channel_baseaddr + EXAR_UART_XON1); outb(0x12, channel_baseaddr + EXAR_UART_XOFF2); outb(0x14, channel_baseaddr + EXAR_UART_XON2); outb(0x10|flowcontrol, channel_baseaddr + EXAR_UART_EFR2 ); //enable shaded bits + flow control outb(0x03, channel_baseaddr + UART_MCR); save_flags(flags); cli(); outb(ier, channel_baseaddr + UART_IER); restore_flags(flags); /* - from sample code config_write ( 0, RESET, 0xff ); // reset all channels config_write ( 8, RESET, 0xff ); // reset all channels config_write ( 16, RESET, 0xff ); // reset all channels config_write ( ch, REG2, 0x01 ); // enable simultaneous write to all channels pwrite ( ch, IER, 0x00 ); // disable interrupts pwrite ( ch, FCR, 0x07 ); // enable & flush FIFOs pwrite ( ch, LCR, 0x80 ); // setup baud rate registers pwrite ( ch, DLL, dll ); pwrite ( ch, DLM, dlm ); pwrite ( ch, LCR, 0x03 ); //setting 8N1 pwrite ( ch, FCTR, 0xc0 | hyst ); //trigger table D selected with hysteresis pwrite ( ch, RXTRIG, r_trg ); //RX trigger level - usually 64 pwrite ( ch, TXTRIG, t_trg ); //TX trigger level - usually 16 pwrite ( ch, EFR, 0x10 | flowcontrol ); //enable shaded bits + flow control pwrite ( ch, XOFF1, 0x11 ); pwrite ( ch, XON1, 0x13 ); pwrite ( ch, XOFF2, 0x12 ); pwrite ( ch, XON2, 0x14 ); pwrite ( ch, MCR, 0x03 ); // enable RTS and DTR lines all the time // disable simultaneous write to all channels config_write ( ch, REG2, 0x00 ); pwrite ( ch, IER, 0x03 ); // enable rx/tx interrupts */ } /* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. * */ static int exser_change_speed(struct port_info_struct *info, struct termios *old_termios, // may be NULL unsigned char lsi_special) { int quot = 0; unsigned cflag, cval, fcr, iflag; int i; int ret = 0; unsigned long flags; unsigned char flowcontrol; unsigned int t_trg, r_trg; unsigned int hysteresis; TRACE("exser_change speed"); DEBUG_NULL(info,-EFAULT) if(!lsi_special) { if ( !info->tty || !info->tty->termios ) return ret; cflag = info->tty->termios->c_cflag; iflag = info->tty->termios->c_iflag; } else { DEBUG_PRINT(DEBUG_SPEED, " change speed exser@%d (force 9600)\n", __LINE__); cflag = B9600|CS8|CLOCAL|HUPCL|CREAD; iflag = 0; // default NO FLOWCONTROL } DEBUG_PRINT(DEBUG_SPEED, "1"); if ( !(info->base) ) return ret; // DEBUG_PRINT(DEBUG_SPEED, "2"); #ifndef B921600 #define B921600 (B460800 +1) #endif // using the info value to set baud rate switch( cflag & (CBAUD | CBAUDEX) ) { case B921600: i = 20; break; case B460800: i = 19; break; case B230400: i = 18; break; case B115200: i = 17; break; case B57600: i = 16; break; case B38400: i = 15; break; case B19200: i = 14; break; case B9600: i = 13; break; case B4800: i = 12; break; case B2400: i = 11; break; case B1800: i = 10; break; case B1200: i = 9; break; case B600: i = 8; break; case B300: i = 7; break; case B200: i = 6; break; case B150: i = 5; break; case B134: i = 4; break; case B110: i = 3; break; case B75: i = 2; break; case B50: i = 1; break; default: i = 0; break; } DEBUG_PRINT(DEBUG_SPEED, "%s: speed is (i = %d)\n", module_name, i); DEBUG_PRINT(DEBUG_SPEED, "3"); if ( i == 15 ) { if ( (info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI ) i = 16; // 57600 bps if ( (info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI ) i = 17; // 115200 bps #ifdef ASYNC_SPD_SHI if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) i = 18; #endif #ifdef ASYNC_SPD_WARP if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) i = 19; #endif } if ( exvar_baud_table[i] == 134 ) { quot = (2 * info->baud_base / 269); } else if ( exvar_baud_table[i] ) { quot = info->baud_base / exvar_baud_table[i]; DEBUG_PRINT(DEBUG_SPEED, "%s: quot = %d (i = %d)\n", module_name, quot, i); DEBUG_PRINT(DEBUG_SPEED, "%s: info->baud_base = %d \n", module_name, info->baud_base); DEBUG_PRINT(DEBUG_SPEED, "4"); if (!quot && old_termios) { DEBUG_PRINT(DEBUG_SPEED, "5"); // re-calculate info->tty->termios->c_cflag &= ~CBAUD; info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); DEBUG_PRINT(DEBUG_SPEED, "%s: change speed to (c_cflag = %x)\n", module_name, info->tty->termios->c_cflag); quot = exser_get_quotient(info, old_termios->c_cflag & (CBAUD | CBAUDEX)); } else if(quot==0) quot = 1; } else { quot = 0; } DEBUG_PRINT(DEBUG_SPEED, "6"); if ( quot ) { DEBUG_PRINT(DEBUG_SPEED, "%s: change speed exser@%d (i = %d)\n", module_name, __LINE__, i); info->MCR |= UART_MCR_DTR; save_flags(flags); cli(); outb(info->MCR, info->base + UART_MCR); restore_flags(flags); } else { info->MCR &= ~UART_MCR_DTR; save_flags(flags); cli(); outb(info->MCR, info->base + UART_MCR); restore_flags(flags); return ret; } DEBUG_PRINT(DEBUG_SPEED, "7"); // byte size and parity switch ( cflag & CSIZE ) { case CS5: cval = 0x00; break; case CS6: cval = 0x01; break; case CS7: cval = 0x02; break; case CS8: cval = 0x03; break; default: cval = 0x00; break; // too keep GCC shut... } if ( cflag & CSTOPB ) cval |= 0x04; if ( cflag & PARENB ) cval |= UART_LCR_PARITY; if ( !(cflag & PARODD) ) cval |= UART_LCR_EPAR; if ( (info->type == PORT_8250) || (info->type == PORT_16450) ) { fcr = 0; } else { DEBUG_PRINT(DEBUG_SPEED, "8"); fcr = UART_FCR_ENABLE_FIFO; switch ( info->rx_trigger ) { case 1: fcr |= UART_FCR_TRIGGER_1; break; case 4: fcr |= UART_FCR_TRIGGER_4; break; case 8: fcr |= UART_FCR_TRIGGER_8; break; default: fcr |= UART_FCR_TRIGGER_14; } } // CTS flow control flag and modem status interrupts info->IER &= ~UART_IER_MSI; info->MCR &= ~EXAR_UART_MCR_AFE; if ( cflag & CRTSCTS ) { info->flags |= ASYNC_CTS_FLOW; info->IER |= UART_IER_MSI; if ( info->type == PORT_16550A ) info->MCR |= EXAR_UART_MCR_AFE; } else { info->flags &= ~ASYNC_CTS_FLOW; } outb(info->MCR, info->base + UART_MCR); if ( cflag & CLOCAL ) info->flags &= ~ASYNC_CHECK_CD; else { info->flags |= ASYNC_CHECK_CD; info->IER |= UART_IER_MSI; } outb(info->IER, info->base + UART_IER); // // Set up parity check flag // DEBUG_PRINT(DEBUG_SPEED, "9"); info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (!lsi_special) { if ( I_INPCK(info->tty) ) info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; if ( I_BRKINT(info->tty) || I_PARMRK(info->tty) ) info->read_status_mask |= UART_LSR_BI; } info->ignore_status_mask = 0; #if 0 // 1/14/03 - why is this disabled? (wmf) // This should be safe, but for some broken bits of hardware... if ( I_IGNPAR(info->tty) ) { info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; info->read_status_mask |= UART_LSR_PE | UART_LSR_FE; } #endif DEBUG_PRINT(DEBUG_SPEED, "A"); if (!lsi_special) { if ( I_IGNBRK(info->tty) ) { info->ignore_status_mask |= UART_LSR_BI; info->read_status_mask |= UART_LSR_BI; // // If we're ignore parity and break indicators, ignore // overruns too. (For real raw support). // if ( I_IGNPAR(info->tty) ) { info->ignore_status_mask |= UART_LSR_OE|UART_LSR_PE|UART_LSR_FE; info->read_status_mask |= UART_LSR_OE|UART_LSR_PE|UART_LSR_FE; } } } DEBUG_PRINT(DEBUG_SPEED, "B"); save_flags(flags); cli(); outb(cval | UART_LCR_DLAB, info->base + UART_LCR); // set DLAB outb(quot & 0xff, info->base + UART_DLL); // LS of divisor outb(quot >> 8, info->base + UART_DLM); // MS of divisor outb(cval, info->base + UART_LCR); // reset DLAB outb(fcr, info->base + UART_FCR); // set fcr (enable fifo & set trigger level) restore_flags(flags); DEBUG_PRINT(DEBUG_SPEED, "%s: change speed (fcr=0x%x)\n", module_name, fcr); DEBUG_PRINT(DEBUG_SPEED, "C"); DEBUG_PRINT(DEBUG_SPEED, "%s: update flow control too!\n", module_name); t_trg = 16; r_trg = 32; flowcontrol = 0; // assume none hysteresis = 0; // assume none if ( cflag & CRTSCTS ) { flowcontrol |= 0xc0; hysteresis = HYSTERESIS_PLUS_MINUS_8; DEBUG_PRINT(DEBUG_SPEED, "%s: CRTSCTS\n", module_name); } if ( iflag & IXOFF ) { flowcontrol |= 0x08; hysteresis = HYSTERESIS_PLUS_MINUS_8; DEBUG_PRINT(DEBUG_SPEED, "%s: IXOFF\n", module_name); } if ( iflag & IXON ) { flowcontrol |= 0x02; hysteresis = HYSTERESIS_PLUS_MINUS_8; DEBUG_PRINT(DEBUG_SPEED, "%s: IXON\n", module_name); } if (!flowcontrol) { DEBUG_PRINT(DEBUG_SPEED, "%s: no flow control\n", module_name); } exser_set_auto_flowctl(info->base & 0xFF00, info->base, info->IER, t_trg, r_trg, hysteresis, flowcontrol); return ret; } /* * ------------------------------------------------------------ * friends of exser_ioctl() * ------------------------------------------------------------ */ static int exser_get_serial_info(struct port_info_struct * info, struct serial_struct * retinfo) { struct serial_struct tmp; TRACE("exser_get_serial_info"); DEBUG_NULL(info,-EFAULT) DEBUG_NULL(retinfo,-EFAULT) if ( !retinfo ) return (-EFAULT); memset(&tmp, 0, sizeof(tmp)); tmp.type = info->type; tmp.line = info->port; tmp.port = info->base; tmp.irq = info->irq; tmp.flags = info->flags; tmp.baud_base = info->baud_base; tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; tmp.custom_divisor = info->custom_divisor; tmp.hub6 = 0; copy_to_user(retinfo, &tmp, sizeof(*retinfo)); return (SUCCESS); } static int exser_set_serial_info(struct port_info_struct * info, struct serial_struct * new_info) { struct serial_struct new_serial; unsigned int flags; int retval = 0; TRACE("exser_set_serial_info"); DEBUG_NULL(info,-EFAULT) DEBUG_NULL(new_info,-EFAULT) if ( !new_info || !info->base ) return (-EFAULT); copy_from_user(&new_serial, new_info, sizeof(new_serial)); if ((new_serial.irq != info->irq) || (new_serial.port != info->base) || (new_serial.type != info->type) || (new_serial.custom_divisor != info->custom_divisor) || (new_serial.baud_base != info->baud_base) ) return (-EPERM); flags = info->flags & ASYNC_SPD_MASK; if ( !suser() ) { if ((new_serial.baud_base != info->baud_base) || (new_serial.close_delay != info->close_delay) || ( (new_serial.flags & ~ASYNC_USR_MASK) != (info->flags & ~ASYNC_USR_MASK))) return (-EPERM); info->flags = ((info->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); } else { /* * OK, past this point, all the error checking has been done. * At this point, we start making changes..... */ info->flags = ((info->flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->close_delay = new_serial.close_delay * HZ/100; info->closing_wait = new_serial.closing_wait * HZ/100; } if ( info->flags & ASYNC_INITIALIZED ) { if ( flags != (info->flags & ASYNC_SPD_MASK) ) { exser_change_speed (info, NULL, FALSE); } } else retval = exser_startup(info); return (retval); } /* * exser_get_lsr_info - get line status register info * * Purpose: Let user call ioctl() to get info when the UART physically * is emptied. On bus types like RS485, the transmitter must * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */ static int exser_get_lsr_info(struct port_info_struct * info, unsigned int *value) { unsigned char status; unsigned int result; unsigned long flags; TRACE("exser_get_lsr_info"); DEBUG_NULL(info,-EFAULT) DEBUG_NULL(value,-EFAULT) save_flags(flags); cli(); status = inb(info->base + UART_LSR); restore_flags(flags); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); put_to_user(result, value); return (SUCCESS); } /* * This routine sends a break character out the serial port. */ static void exser_send_break(struct port_info_struct * info, int duration) { unsigned long flags; TRACE("exser_send_break"); DEBUG_NULLV(info) if ( !info->base ) return; current->state = TASK_INTERRUPTIBLE; exvar_log.break_xmit_cnt[info->port]++; save_flags(flags); cli(); outb(inb(info->base + UART_LCR) | UART_LCR_SBC, info->base + UART_LCR); schedule_timeout(duration); outb(inb(info->base + UART_LCR) & ~UART_LCR_SBC, info->base + UART_LCR); restore_flags(flags); } static int exser_get_modem_info(struct port_info_struct * info, unsigned int *value) { unsigned char control, status; unsigned int result; unsigned long flags; TRACE("exser_get_modem_info"); DEBUG_NULL(info,-EFAULT) DEBUG_NULL(value,-EFAULT) control = info->MCR; save_flags(flags); cli(); status = inb(info->base + UART_MSR); if ( status & UART_MSR_ANY_DELTA ) exser_check_modem_status(info, status); restore_flags(flags); result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); put_to_user(result, value); return (SUCCESS); } static int exser_set_modem_info(struct port_info_struct * info, unsigned int cmd, unsigned int *value) { int error; unsigned int arg; unsigned long flags; TRACE("exser_set_modem_info"); DEBUG_NULL(info,-EFAULT) DEBUG_NULL(value,-EFAULT) error = verify_area(VERIFY_READ, value, sizeof(int)); if ( error ) return (error); get_from_user(arg,value); switch ( cmd ) { case TIOCMBIS: if ( arg & TIOCM_RTS ) info->MCR |= UART_MCR_RTS; if ( arg & TIOCM_DTR ) info->MCR |= UART_MCR_DTR; break; case TIOCMBIC: if ( arg & TIOCM_RTS ) info->MCR &= ~UART_MCR_RTS; if ( arg & TIOCM_DTR ) info->MCR &= ~UART_MCR_DTR; break; case TIOCMSET: info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR)) | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); break; default: return (-EINVAL); } save_flags(flags); cli(); outb(info->MCR, info->base + UART_MCR); restore_flags(flags); return (SUCCESS); } static int exser_get_LSI_conf(type_uart_info *lsi_uart_item, struct uart_control_struct *hwconf) { int i; unsigned char scratch, scratch2; unsigned short base_ioaddrs[EXAR_PORTS_PER_CHIP]={0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70}; TRACE("exser_get_LSI_conf"); DEBUG_NULL(lsi_uart_item,-EFAULT) DEBUG_NULL(hwconf,-EFAULT) memset (hwconf, 0, sizeof(struct uart_control_struct)); // if number_of_ports < 0, then we are at the end of the table // if it == 0, then we skip this chip, but there are more entries to look for if (lsi_uart_item->numberOfUarts < 1 || lsi_uart_item->numberOfUarts > EXAR_PORTS_PER_CHIP) return (-1); hwconf->in_use = 1; DEBUG_PRINT(DEBUG_ID, DEFAULT_PREFIX" number of uarts in system: %d\n", lsi_uart_item->numberOfUarts); DEBUG_PRINT(DEBUG_ID, DEFAULT_PREFIX" base i/o address 0x%x\n", lsi_uart_item->ioAddress); // base io address for each port for ( i=0; iioaddr); i++ ) hwconf->ioaddr[i] = lsi_uart_item->ioAddress + base_ioaddrs[i]; hwconf->irq = (int)(lsi_uart_item->irqNumber & 0x0F); hwconf->vector = (int)(lsi_uart_item->ioAddress + 0x80); /* bits 0-7 tell which channel interrupted */ hwconf->vector_mask = 0x00FF; hwconf->buffer_flags = lsi_uart_item->bufferFlag; hwconf->uart_type = PORT_16550A; hwconf->ports = lsi_uart_item->numberOfUarts; for ( i=0; ibaud_base); ++i ) hwconf->baud_base[i] = 921600; lsi_serial_channels += lsi_uart_item->numberOfUarts; // lets make sure the data registers are selected DEBUG_PRINT(DEBUG_ID, DEFAULT_PREFIX" read from LCR (0x%x)\n", lsi_uart_item->ioAddress + UART_LCR); scratch2 = inb(lsi_uart_item->ioAddress + UART_LCR) & (~UART_LCR_DLAB); DEBUG_PRINT(DEBUG_ID, DEFAULT_PREFIX" value = 0x%x\n", scratch2); DEBUG_PRINT(DEBUG_ID, DEFAULT_PREFIX" write value back to LCR 0x%x\n", scratch2|UART_LCR_DLAB); outb(scratch2|UART_LCR_DLAB, lsi_uart_item->ioAddress + UART_LCR); outb(0, lsi_uart_item->ioAddress + UART_EFR); /* EFR is the same as FCR */ outb(scratch2, lsi_uart_item->ioAddress + UART_LCR); outb(UART_FCR_ENABLE_FIFO, lsi_uart_item->ioAddress + UART_FCR); scratch = inb(lsi_uart_item->ioAddress + UART_IIR); DEBUG_PRINT(DEBUG_ID, DEFAULT_PREFIX" value of IIR = 0x%x\n", scratch); return (hwconf->ports); } /************************************************************************* * *Function Name: clear_buffer * *Parameters: pointer to device structure * *Description: clears the device history buffer by resetting the pointers * *Returns: void * *************************************************************************/ static void clear_buffer (type_lsi_device *dev) { unsigned long flags; TRACE("clear_buffer"); DEBUG_NULLV(dev) save_flags(flags); cli(); dev->head = dev->tail = dev->pbuf; dev->ent->size = 0; // show size is zero restore_flags(flags); } #ifdef PROC_DEBUG static int proc_debug_open(struct inode *inode, struct file *filp) { TRACE("proc_debug_open"); DEBUG_NULL(inode,-EFAULT) DEBUG_NULL(filp,-EFAULT) //----------------------------------------------------------------------- LOCAL_INC_USE_COUNT; /* Before we maybe sleep */ // we have no private data filp->private_data = NULL; return (SUCCESS); /* success */ } static int proc_debug_close(struct inode *inode, struct file *filp) { TRACE("proc_debug_close"); DEBUG_NULL(filp,-EFAULT) LOCAL_DEC_USE_COUNT; return (SUCCESS); } static ssize_t proc_debug_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { int i; ssize_t nbytes = 0; char buffer[2000]; TRACE("proc_debug_read"); DEBUG_NULL(filp,-EFAULT) DEBUG_NULL(buf,-EFAULT) DEBUG_NULL(f_pos,-EFAULT) for (i=0,nbytes=0; i