/*****************************************************************************/ /* * xr17c15x.c -- EXAR multiport serial driver. * * Copyright (C) 1999-2003 Exar. * * 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. * * * XR17C15x Family Multiport Serial Driver * for : LINUX (Tested with RedHat 8.0) * date : April, 2003 * version : 3.1 * author : Ravi Reddy */ /* Revision History 04/05/2001 1.0 Initial Exar serial driver for kernel 2.0.x and 2.2.x 05/05/2001 1.01 Modified Driver to support kernel 2.4.x and to support multiple boards 10/19/2001 2.0 Included Support for Four channel (154) and the future Two channel (152) boards 03/07/2003 3.0 Completely revamped driver based on the original serial.c - Includes support for remote login from dumb terminals. Tested with RedHat 8.0 04/01/2003 3.1 Modified code to avoid freeing nonexistent resource in xr_fini() function; Added MODULE_LICENSE information. // LSI CVS log $Log: exserial.c,v $ Revision 1.24 2004/02/02 19:23:58 billf v3.1-8 print a kernel log message when a port buffer is cleared only allow 1 access for wrote or read/write (test for ! O_RDONLY) Revision 1.23 2003/12/12 14:06:22 billf added some debug code to show pointer values and to trace xr_open and xr_close in xr_open, moved the rdwr count check to before the paranoia and perms check. xr_close will dec. the rdwr count, so I must inc. it before any other test is done that could cause us to exit xr_open Revision 1.22 2003/11/12 20:09:00 billf added past CVS log to the source file Revision 1.21 2003/11/12 20:00:00 billf changed version number to 3.1-6 moved test get_perms (in xr_open) to AFTER I set info and tty fields. THIS WAS A BUG. When it was before, ifa port was open, and someone was denied access to a port, xr_close think there were no more users. This lead to the MOD_USE_COUNT to increase each time it happened. Runing lsmod would show a large number of users. changed the comments mentioning rs_open/close to xr_open/close added debug statements tracing MOD_INC/DEC_USE_COUNT Revision 1.20 date: 2003/10/27 19:08:33; author: billf; state: Exp; lines: +2 -2 changed 480 last 2 uarts to address 2500h and 2600h Revision 1.19 date: 2003/10/24 19:17:52; author: billf; state: Exp; lines: +4 -4 changed revision number to 3.1-3 to match the revision number in the HISTORY notes Revision 1.18 date: 2003/10/24 19:14:21; author: billf; state: Exp; lines: +16 -13 replaced 'serial' with the #define MODULE_NAME prints io addresses in hex, not decimal Revision 1.17 date: 2003/09/10 17:35:49; author: billf; state: Exp; lines: +3 -0 added kernel debug/info statements to show ports being installed or removed Revision 1.16 date: 2003/09/10 15:09:31; author: billf; state: Exp; lines: +2 -2 in xr_open, allow the open to of a missing port occur IF the access mode is read-only. This allows ioctl calls form snmp to still work. Revision 1.15 date: 2003/09/05 13:40:05; author: billf; state: Exp; lines: +652 -457 added code to return SNMP counts (and options to control keeping TX & RX stats) added removable ports support (ioctl calls) if a port is missing, don't allow an open to occur Revision 1.14 date: 2003/07/08 18:08:45; author: billf; state: Exp; lines: +5 -1 modified the LOCAL_VERSTRING so it works with our versions command and the building of the RPMs Revision 1.13 date: 2003/07/02 14:10:02; author: gregg; state: Exp; lines: +7 -9 When resetting the auto flow control, clear the xmit fifo only. Remove DEBUG_NULL statements from proc_poll and monitor_poll. Revision 1.12 date: 2003/06/24 20:16:47; author: gregg; state: Exp; lines: +12 -7 Check buffering flag before call to wake_up_interruptible. Revision 1.11 date: 2003/06/23 19:34:47; author: gregg; state: Exp; lines: +46 -18 Add parameter lsi_buffer_gid. Revision 1.10 date: 2003/06/18 15:12:29; author: gregg; state: Exp; lines: +13 -23 Use immediate queue for bottom half. Disable MSI and THRI interrupts in shutdown. Revision 1.9 date: 2003/06/16 12:39:26; author: gregg; state: Exp; lines: +181 -76 Flow control works. Cannot switch between hardware and software flow control with the old exser driver. Tested against com1 on my PC. Revision 1.8 date: 2003/06/10 20:09:35; author: gregg; state: Exp; lines: +418 -200 Can start ports without a tty. Revision 1.7 date: 2003/06/06 19:35:11; author: gregg; state: Exp; lines: +159 -13 Add ioctl code. Revision 1.6 date: 2003/06/06 15:13:09; author: gregg; state: Exp; lines: +811 -25 Added proc file system driver. Revision 1.5 date: 2003/06/05 18:00:45; author: gregg; state: Exp; lines: +552 -88 Added monitor code. Revision 1.4 date: 2003/06/04 18:32:09; author: gregg; state: Exp; lines: +74 -59 In transmit_chars, reduce FIFO size by TFCNT (Transmit FIFO Level Counter). Added software flow control. Revision 1.3 date: 2003/06/02 12:45:43; author: gregg; state: Exp; lines: +139 -24 Add dte/dce. Revision 1.2 date: 2003/05/29 17:21:58; author: gregg; state: Exp; lines: +513 -164 Initail changes to configure ISA uarts. Revision 1.1 date: 2003/05/27 14:04:39; author: gregg; state: Exp; Initial revision Revision 1.1.1.1 date: 2003/05/27 14:04:39; author: gregg; state: Exp; lines: +0 -0 initial import into CVS */ /*****************************************************************************/ /* NOTE */ /* */ /* Compilation with NON_DEVICE_ALLOWED defined has not been tested!!!!! */ /* */ /*****************************************************************************/ // LSI, we don't use this name #define MODULE_NAME "exserial" #define MODULE_NAME "exser" #define VERSION_NUMBER "3.1" #define LOCAL_VERSTRING "-8" static const char *module_name = MODULE_NAME; static char *serial_name = "Serial driver for XR16L788X UARTs"; static char *serial_version = VERSION_NUMBER; static char *serial_revdate = "2003-12-11"; // define PROC_CONTROL to create /proc/driver/exar // this file is used to record if a port is missing or present #define PROC_CONTROL //#define LW_DEBUG // bits in enabledebug can be used to filter messages at run time // using DEBUG_PRINT macro #define DEBUG_CLOSE 0x00000001 // debug messages in xr_close #define DEBUG_OPEN 0x00000002 // debug messages in xr_open #define DEBUG_PROC 0x00000004 // debug messages in proc related functions #define DEBUG_ID 0x00000008 // debug messages in start_exar_isa_uart #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 #ifdef LW_DEBUG //static int debugenable=(DEBUG_TRACE | DEBUG_OPEN | DEBUG_CLOSE); static int debugenable=0; #define TRACE(str) printk (KERN_DEBUG MODULE_NAME "@%4d %s\n", __LINE__, str); #define DEBUG_PRINT(d, fmt, args...) \ do { \ if (debugenable & d) {\ printk (fmt, ## args);\ }\ } while (0) #else #define TRACE(str) #define DEBUG_PRINT(d, fmt, args...) #endif #define DEBUG_NULL(p,val) if ((p) == NULL) { \ printk (KERN_INFO MODULE_NAME "@%4d "#p " is NULL\n", __LINE__); \ return val; \ } #define DEBUG_NULLV(p) if ((p) == NULL) { \ printk (KERN_INFO MODULE_NAME "@%4d "#p " is NULL\n", __LINE__); \ return; \ } // program that tests access permissions #define FW_LOADER "/usr/local/bin/port_access" // define prefixes for the printk calls #define ISR_PREFIX "" // no level for the printk #define DEFAULT_PREFIX KERN_DEBUG MODULE_NAME ": " #define WARN_PREFIX KERN_WARNING MODULE_NAME ": " #define NOTICE_PREFIX KERN_NOTICE 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) /* * Serial driver configuration section. Here are the various options: * * CONFIG_SERIAL_SHARE_IRQ * Enables support for multiple serial ports on one IRQ * * SERIAL_PARANOIA_CHECK * Check the magic number for the xr_async_structure where * ever possible. */ #include #include #undef SERIAL_PARANOIA_CHECK #define CONFIG_SERIAL_NOPAUSE_IO #define SERIAL_DO_RESTART // this driver is for EXAR's ISA UARTS #undef CONFIG_PCI #ifdef CONFIG_PCI #define ENABLE_SERIAL_PCI #ifndef CONFIG_SERIAL_SHARE_IRQ #define CONFIG_SERIAL_SHARE_IRQ #endif #endif /* Set of debugging defines */ #undef SERIAL_DEBUG_INTR #undef SERIAL_DEBUG_OPEN #undef SERIAL_DEBUG_FLOW #undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT #undef SERIAL_DEBUG_PCI #undef SERIAL_DEBUG_AUTOCONF /* Sanity checks */ #define RS_STROBE_TIME (10*HZ) #define RS_ISR_PASS_LIMIT 256 #if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) #define SERIAL_INLINE #endif /* * End of serial driver configuration section. */ #include #include #ifdef LOCAL_HEADERS #include "serial_local.h" #else #include #include #include #include #ifndef LOCAL_VERSTRING #define LOCAL_VERSTRING "" #endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if (LINUX_VERSION_CODE >= 131343) #include #endif #if (LINUX_VERSION_CODE >= 131336) #include #endif #include #ifdef ENABLE_SERIAL_PCI #include #endif #include // proc file system api // include these 2 lines to get def. of waitpid #define __KERNEL_SYSCALLS__ #include /* * All of the compatibilty code so we can compile serial.c against * older kernels is hidden in serial_compat.h */ #if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ #include "serial_compat.h" #endif #include #include #include #include #include "dbinfo.h" // lsi product information data structure #include "exser.h" #if defined(CONFIG_MAC_SERIAL) #define SERIAL_DEV_OFFSET ((_machine == _MACH_prep || _machine == _MACH_chrp) ? 0 : 2) #else #define SERIAL_DEV_OFFSET 0 #endif #ifdef SERIAL_INLINE #define _INLINE_ inline #else #define _INLINE_ #endif static struct tty_driver serial_driver, callout_driver; static int serial_refcount; #ifdef SERIAL_TIMER static struct timer_list serial_timer; #endif /* serial subtype definitions */ #ifndef SERIAL_TYPE_NORMAL #define SERIAL_TYPE_NORMAL 1 #define SERIAL_TYPE_CALLOUT 2 #endif /* number of characters left in xmit buffer before we ask for more */ #define WAKEUP_CHARS 256 // module parameters 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 char *show_tx_cnts = NULL; // these 2 hold the user supplied string static char *show_rx_cnts = NULL; static bool keep_rx_counts = FALSE; // holds the result of testing the 2 strings above static bool keep_tx_counts = FALSE; #ifndef CONFIG_SERIAL_SHARE_IRQ #define CONFIG_SERIAL_SHARE_IRQ #endif #define DEC_DEV_USAGE_COUNT --(dev->usage_count) #define INC_DEV_USAGE_COUNT ++(dev->usage_count) #define MOD_DEC_RDWR_COUNT(info) \ do { \ if ((filp->f_flags & O_ACCMODE) != O_RDONLY) { \ DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" xr_close@%d: rdwr_count = %d\n", __LINE__, lsi_devices[info->line].rdwr_count); \ if (PORT_TYPE_DEVICE == info->state->port_type) \ if (lsi_devices[info->line].rdwr_count) \ lsi_devices[info->line].rdwr_count--; \ } \ } while (0); #define dtedce_all_off 0 static byte current_dtedce_settings[(EXAR_PORTS+3)/4]; //[0] is not used // default uart settings/tables - these are changed by get_uart_table static unsigned int system_uart_count = 2; // number 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, 0x2500, 15, (U32)0x000000FF }, { 8, 0x2600, 5, (U32)0x000000FF } }; 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 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 */ #ifdef DECLARE_WAITQUEUE wait_queue_head_t in_queue; #else struct wait_queue *in_queue; #endif 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; 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; // The redefinition of serial driver's async_struct with some of our own // member fileds struct xr_serial_state { int magic; int baud_base; unsigned long port; type_port port_type; int irq; int flags; int hub6; int type; int line; int revision; /* Chip revision (950) */ int xmit_fifo_size; int custom_divisor; int count; u8 *iomem_base; u16 iomem_reg_shift; unsigned short close_delay; unsigned short closing_wait; /* time to wait before closing */ struct async_icount icount; struct termios normal_termios; struct termios callout_termios; int io_type; struct xr_async_struct *info; struct pci_dev *dev; }; // port_info_struct struct xr_async_struct { int magic; unsigned long port; int hub6; int flags; int xmit_fifo_size; struct xr_serial_state *state; struct tty_struct *tty; int read_status_mask; int ignore_status_mask; int timeout; int quot; int x_char; /* xon/xoff character */ int close_delay; unsigned short closing_wait; unsigned short closing_wait2; int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ int LCR; /* Line control register */ int ACR; /* 16950 Additional Control Reg. */ unsigned long event; unsigned long last_active; int line; int blocked_open; /* # of blocked opens */ long session; /* Session of opening process */ long pgrp; /* pgrp of opening process */ struct circ_buf xmit; spinlock_t xmit_lock; spinlock_t irq_spinlock; u8 *iomem_base; u16 iomem_reg_shift; int io_type; struct tq_struct tqueue; #ifdef DECLARE_WAITQUEUE wait_queue_head_t open_wait; wait_queue_head_t close_wait; wait_queue_head_t delta_msr_wait; #else struct wait_queue *open_wait; struct wait_queue *close_wait; struct wait_queue *delta_msr_wait; #endif struct xr_async_struct *next_port; /* For the linked list */ struct xr_async_struct *prev_port; }; static struct xr_async_struct *IRQ_ports[NR_IRQS]; /* * IRQ_timeout - How long the timeout should be for each IRQ * should be after the IRQ has been active. */ #ifdef SERIAL_TIMER static int IRQ_timeout[NR_IRQS]; #endif #define XR16L788_UART_CONFIG_INDEX 5 #define PORT_XR16L788 XR16L788_UART_CONFIG_INDEX /* * Here we define the default xmit fifo size used for each type of * UART */ static struct serial_uart_config uart_config[] = { { "unknown", 1, 0 }, // index 0 { "8250", 1, 0 }, { "16450", 1, 0 }, { "16550", 1, 0 }, { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, // index 4 { "XR16L788", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, // index XR16L788_UART_CONFIG_INDEX; { 0, 0 } }; static type_lsi_device lsi_monitor_devices[EXAR_PORTS]; static type_lsi_device lsi_devices[EXAR_PORTS]; struct xr_serial_state xr_table[EXAR_PORTS]; #ifdef PROC_CONTROL static int port_present[EXAR_PORTS]; // FALSE if port is missing #endif /* set TRUE during init, ioctl call to set/reset, if FALSE, then ttyB* or monitor_port* can not be opened */ static bool port_enabled[EXAR_PORTS]; #define NR_PORTS (sizeof(xr_table)/sizeof(struct xr_serial_state)) int serial_nr_ports = NR_PORTS; #if (defined(ENABLE_SERIAL_PCI)) #define NR_PCI_BOARDS 8 struct pciboard { int flags; int num_ports; int base_baud; int uart_offset; int reg_shift; int (*init_fn)(struct pci_dev *dev, struct pciboard *board, int enable); int first_uart_offset; }; struct pciboard_inst { struct pciboard board; struct pci_dev *dev; }; static struct pciboard_inst serial_pci_board[NR_PCI_BOARDS]; #ifndef IS_PCI_REGION_IOPORT #define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ IORESOURCE_IO) #endif #ifndef IS_PCI_REGION_IOMEM #define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ IORESOURCE_MEM) #endif #ifndef PCI_IRQ_RESOURCE #define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) #endif #ifndef pci_get_subvendor #define pci_get_subvendor(dev) ((dev)->subsystem_vendor) #define pci_get_subdevice(dev) ((dev)->subsystem_device) #endif #endif /* ENABLE_SERIAL_PCI */ #ifndef PREPARE_FUNC #define PREPARE_FUNC(dev) (dev->prepare) #define ACTIVATE_FUNC(dev) (dev->activate) #define DEACTIVATE_FUNC(dev) (dev->deactivate) #endif #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) static struct tty_struct *serial_table[NR_PORTS]; static struct termios *serial_termios[NR_PORTS]; static struct termios *serial_termios_locked[NR_PORTS]; #if defined(SERIAL_DEBUG_MCOUNT) #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s) #else #define DBG_CNT(s) #endif /* * tmp_buf is used as a temporary buffer by serial_write. We need to * lock it in case the copy_from_user 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 *tmp_buf; #ifdef DECLARE_MUTEX static DECLARE_MUTEX(tmp_buf_sem); #else static struct semaphore tmp_buf_sem = MUTEX; #endif #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif // 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 static unsigned detect_uart_irq (struct xr_serial_state * state); static void autoconfig(struct xr_serial_state * state); static void change_speed(struct xr_async_struct *info, struct termios *old); static void xr_wait_until_sent(struct tty_struct *tty, int timeout); static void get_uart_table(void); static int start_exar_isa_uart(type_uart_info *lsi_uart_item, struct uart_control_struct *hwconf); static int set_dtedce(int port, te_UART_DTEDCE mode); static int get_dtedce(int port, struct EXAR_STRUCT_DTEDCE_MODE *ioctl_data); static void set_dte_all_off(void); static int monitor_init(void); static void monitor_cleanup_module(void); 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 unsigned int monitor_poll(struct file *filp, poll_table *wait); static int get_perms(int uid, int port, char *mode, int access); static int exec_helper(void *argv); static int proc_create(void); static void proc_remove (void); 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 unsigned int proc_poll(struct file *filp, poll_table *wait); static loff_t proc_llseek(struct file *filp, loff_t off, int whence); static int atoi_base10 (char const *s); static void clear_buffer (type_lsi_device *dev); static void reset_auto_flowctl(struct xr_async_struct *info); static bool show_rx (void); static bool show_tx (void); #ifdef PROC_CONTROL static int control_proc_open(struct inode *inode, struct file *filp); static int control_proc_close(struct inode *inode, struct file *filp); static ssize_t control_proc_read(struct file *filp, char *buf, size_t count, loff_t *f_pos); static int control_proc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); static unsigned int control_proc_poll(struct file *filp, poll_table *wait); #endif // protect against a possible SNMP security hole static bool show_rx (void) { if (show_rx_cnts == NULL) { return (FALSE); } return (*show_rx_cnts == 'y' || *show_rx_cnts == 'Y'); } static bool show_tx (void) { if (show_tx_cnts == NULL) { return (FALSE); } return (*show_tx_cnts == 'y' || *show_tx_cnts == 'Y'); } /* * serial_paranoia_check */ static inline int serial_paranoia_check(struct xr_async_struct *info, kdev_t device, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK static const char *badmagic = "Warning: bad magic number for serial struct (%s) in %s\n"; static const char *badinfo = "Warning: null xr_async_struct for (%s) in %s\n"; if (!info) { printk(badinfo, kdevname(device), routine); return 1; } if (info->magic != SERIAL_MAGIC) { printk(badmagic, kdevname(device), routine); return 1; } #endif return 0; } static _INLINE_ unsigned int serial_in(struct xr_async_struct *info, int offset) { switch (info->io_type) { case SERIAL_IO_MEM: return readb((unsigned long) info->iomem_base + (offset<iomem_reg_shift)); default: return inb(info->port + offset); } } static _INLINE_ void serial_out(struct xr_async_struct *info, int offset, int value) { switch (info->io_type) { case SERIAL_IO_MEM: writeb(value, (unsigned long) info->iomem_base + (offset<iomem_reg_shift)); break; default: outb(value, info->port+offset); } } /* * We used to support using pause I/O for certain machines. We * haven't supported this for a while, but just in case it's badly * needed for certain old 386 machines, I've left these #define's * in.... */ #define serial_inp(info, offset) serial_in(info, offset) #define serial_outp(info, offset, value) serial_out(info, offset, value) /* * ------------------------------------------------------------ * xr_stop() and xr_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */ static void xr_stop(struct tty_struct *tty) { struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "xr_stop")) return; save_flags(flags); cli(); if (info->IER & UART_IER_THRI) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); } restore_flags(flags); } static void xr_start(struct tty_struct *tty) { struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "xr_start")) return; save_flags(flags); cli(); if (info->xmit.head != info->xmit.tail && info->xmit.buf && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); } restore_flags(flags); } /* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines. All of the following * subroutines are declared as inline and are folded into * xr_interrupt(). They were separated out for readability's sake. * * Note: xr_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off. People who may want to modify * xr_interrupt() should try to keep the interrupt handler as fast as * possible. After you are done making modifications, it is not a bad * idea to do: * * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c * * and look at the resulting assemble code in serial.s. * * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 * ----------------------------------------------------------------------- */ /* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */ static _INLINE_ void xr_sched_event(struct xr_async_struct *info, int event) { info->event |= 1 << event; queue_task(&info->tqueue, &tq_immediate); mark_bh(IMMEDIATE_BH); } static _INLINE_ void receive_chars(struct xr_async_struct *info, int *status, struct pt_regs * regs) { struct tty_struct *tty = info->tty; unsigned char ch; struct async_icount *icount; int max_count = 256; type_lsi_device *m, *p; unsigned int lsibuf; TRACE("receive_chars"); icount = &info->state->icount; p = &lsi_devices[info->line]; lsibuf = (0 == p->rdwr_count || !p->stop_buffering_in_direct); m = &lsi_monitor_devices[info->line]; do { ch = serial_inp(info, UART_RX); if (keep_rx_counts) icount->rx++; // add to lsibuf device's memory buffer if (lsibuf) { *(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--; } } #ifndef NO_MONITOR // add to monitor buffer too *(m->head) = ch; m->head++; if (m->head > m->pbuf_end) m->head = m->pbuf; if (m->head == m->tail) { m->tail++; if (m->tail > m->pbuf_end) m->tail = m->pbuf; } #endif if (tty) { if (tty->flip.count >= TTY_FLIPBUF_SIZE) { tty->flip.tqueue.routine((void *) tty); if (tty->flip.count >= TTY_FLIPBUF_SIZE) return; // if TTY_DONT_FLIP is set } *tty->flip.char_buf_ptr = ch; #ifdef SERIAL_DEBUG_INTR printk("DR%02x:%02x...", ch, *status); #endif *tty->flip.flag_buf_ptr = 0; if (*status & (UART_LSR_BI | UART_LSR_PE | UART_LSR_FE | UART_LSR_OE)) { /* * For statistics only */ if (*status & UART_LSR_BI) { *status &= ~(UART_LSR_FE | UART_LSR_PE); icount->brk++; /* * We do the SysRQ and SAK checking * here because otherwise the break * may get masked by ignore_status_mask * or read_status_mask. */ if (info->flags & ASYNC_SAK) do_SAK(tty); } else if (*status & UART_LSR_PE) icount->parity++; else if (*status & UART_LSR_FE) icount->frame++; if (*status & UART_LSR_OE) icount->overrun++; /* * Mask off conditions which should be ignored. */ *status &= info->read_status_mask; if (*status & (UART_LSR_BI)) { #ifdef SERIAL_DEBUG_INTR printk("handling break...."); #endif *tty->flip.flag_buf_ptr = TTY_BREAK; } else if (*status & UART_LSR_PE) *tty->flip.flag_buf_ptr = TTY_PARITY; else if (*status & UART_LSR_FE) *tty->flip.flag_buf_ptr = TTY_FRAME; } if ((*status & info->ignore_status_mask) == 0) { tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; tty->flip.count++; } if ((*status & UART_LSR_OE) && (tty->flip.count < TTY_FLIPBUF_SIZE)) { /* * Overrun is special, since it's reported * immediately, and doesn't affect the current * character */ *tty->flip.flag_buf_ptr = TTY_OVERRUN; tty->flip.count++; tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; } } else { if (*status & (UART_LSR_BI | UART_LSR_PE | UART_LSR_FE | UART_LSR_OE)) { /* * For statistics only */ if (*status & UART_LSR_BI) { *status &= ~(UART_LSR_FE | UART_LSR_PE); icount->brk++; } else if (*status & UART_LSR_PE) icount->parity++; else if (*status & UART_LSR_FE) icount->frame++; if (*status & UART_LSR_OE) icount->overrun++; } } *status = serial_inp(info, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); if (lsibuf) wake_up_interruptible(&p->in_queue); #ifndef NO_MONITOR wake_up_interruptible(&m->in_queue); #endif if (tty) { #if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */ tty_flip_buffer_push(tty); #else queue_task_irq_off(&tty->flip.tqueue, &tq_timer); #endif } } static _INLINE_ void transmit_chars(struct xr_async_struct *info, int *intr_done) { int count; TRACE("transmit_chars"); if (info->x_char) { serial_outp(info, UART_TX, info->x_char); if (keep_tx_counts) info->state->icount.tx++; info->x_char = 0; if (intr_done) *intr_done = 0; return; } if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); return; } count = info->xmit_fifo_size; // how much buffer is availabe now to write? count -= serial_in(info, EXAR_UART_TFCNT); do { serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]); info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); if (keep_tx_counts) info->state->icount.tx++; if (info->xmit.head == info->xmit.tail) break; } while (--count > 0); if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) < WAKEUP_CHARS) xr_sched_event(info, RS_EVENT_WRITE_WAKEUP); #ifdef SERIAL_DEBUG_INTR printk("THRE..."); #endif if (intr_done) *intr_done = 0; if (info->xmit.head == info->xmit.tail) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); } } static _INLINE_ void check_modem_status(struct xr_async_struct *info) { int status; struct async_icount *icount; status = serial_in(info, UART_MSR); if (status & UART_MSR_ANY_DELTA) { icount = &info->state->icount; /* update input line counters */ if (status & UART_MSR_TERI) icount->rng++; if (status & UART_MSR_DDSR) icount->dsr++; if (status & UART_MSR_DDCD) { icount->dcd++; #ifdef CONFIG_HARD_PPS if ((info->flags & ASYNC_HARDPPS_CD) && (status & UART_MSR_DCD)) hardpps(); #endif } if (status & UART_MSR_DCTS) icount->cts++; wake_up_interruptible(&info->delta_msr_wait); } if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("%s%d CD now %s...", DIRECT_DEV_ROOT, info->line, (status & UART_MSR_DCD) ? "on" : "off"); #endif if (status & UART_MSR_DCD) wake_up_interruptible(&info->open_wait); else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_CALLOUT_NOHUP))) { #ifdef SERIAL_DEBUG_OPEN printk("doing serial hangup..."); #endif if (info->tty) tty_hangup(info->tty); } } if (info->flags & ASYNC_CTS_FLOW) { if (info->tty->hw_stopped) { if (status & UART_MSR_CTS) { #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx start..."); #endif info->tty->hw_stopped = 0; info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); xr_sched_event(info, RS_EVENT_WRITE_WAKEUP); return; } } else { if (!(status & UART_MSR_CTS)) { #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx stop..."); #endif info->tty->hw_stopped = 1; info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); } } } } #ifdef CONFIG_SERIAL_SHARE_IRQ /* * This is the serial driver's generic interrupt routine */ static void xr_interrupt(int irq, void *dev_id, struct pt_regs * regs) { int status, iir; struct xr_async_struct * info; int pass_counter = 0; struct xr_async_struct *end_mark = 0; TRACE("xr_interrupt"); info = IRQ_ports[irq]; if (!info) return; do { if ((iir=serial_in(info, UART_IIR)) & UART_IIR_NO_INT) { if (!end_mark) end_mark = info; goto next; } end_mark = 0; info->last_active = jiffies; status = serial_inp(info, UART_LSR); if (status & UART_LSR_DR || serial_in(info, EXAR_UART_RFCNT)) receive_chars(info, &status, regs); if (info->tty) { check_modem_status(info); if ((status & UART_LSR_THRE) || /* for buggy ELAN processors */ ((iir & UART_IIR_ID) == UART_IIR_THRI)) transmit_chars(info, 0); } next: info = info->next_port; if (!info) { info = IRQ_ports[irq]; if (pass_counter++ > RS_ISR_PASS_LIMIT) { break; /* Prevent infinite loops */ } continue; } } while (end_mark != info); } #endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */ /* * This is the serial driver's interrupt routine for a single port */ static void xr_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) { int status, iir; int pass_counter = 0; struct xr_async_struct * info; info = IRQ_ports[irq]; if (!info || !info->tty) return; iir = serial_in(info, UART_IIR); do { status = serial_inp(info, UART_LSR); if (status & UART_LSR_DR) receive_chars(info, &status, regs); check_modem_status(info); if ((status & UART_LSR_THRE) || /* For buggy ELAN processors */ ((iir & UART_IIR_ID) == UART_IIR_THRI)) transmit_chars(info, 0); if (pass_counter++ > RS_ISR_PASS_LIMIT) { break; } iir = serial_in(info, UART_IIR); } while ((iir & UART_IIR_NO_INT) == 0); info->last_active = jiffies; } /* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- */ static void do_softint(void *private_) { struct xr_async_struct *info = (struct xr_async_struct *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &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); #ifdef SERIAL_HAVE_POLL_WAIT wake_up_interruptible(&tty->poll_wait); #endif } } /* * This subroutine is called when the RS_TIMER goes off. It is used * by the serial driver to handle ports that do not have an interrupt * (irq=0). This doesn't work very well for 16450's, but gives barely * passable results for a 16550A. (Although at the expense of much * CPU overhead). */ #ifdef SERIAL_TIMER static void xr_timer(unsigned long dummy) { static unsigned long last_strobe; struct xr_async_struct *info; unsigned int i; unsigned long flags; TRACE("xr_timer"); if ((jiffies - last_strobe) >= RS_STROBE_TIME) { for (i=0; i < NR_IRQS; i++) { info = IRQ_ports[i]; if (!info) continue; save_flags(flags); cli(); #ifdef CONFIG_SERIAL_SHARE_IRQ if (info->next_port) { do { serial_out(info, UART_IER, 0); info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); info = info->next_port; } while (info); xr_interrupt(i, NULL, NULL); } else #endif /* CONFIG_SERIAL_SHARE_IRQ */ xr_interrupt_single(i, NULL, NULL); restore_flags(flags); } } last_strobe = jiffies; mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); if (IRQ_ports[0]) { save_flags(flags); cli(); #ifdef CONFIG_SERIAL_SHARE_IRQ xr_interrupt(0, NULL, NULL); #else xr_interrupt_single(0, NULL, NULL); #endif restore_flags(flags); mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); } } #endif /* * --------------------------------------------------------------- * Low level utility subroutines for the serial driver: routines to * figure out the appropriate timeout for an interrupt chain, routines * to initialize and startup a serial port, and routines to shutdown a * serial port. Useful stuff like that. * --------------------------------------------------------------- */ /* * This routine figures out the correct timeout for a particular IRQ. * It uses the smallest timeout of all of the serial ports in a * particular interrupt chain. Now only used for IRQ 0.... */ #ifdef SERIAL_TIMER static void figure_IRQ_timeout(int irq) { struct xr_async_struct *info; int timeout = 60*HZ; /* 60 seconds === a long time :-) */ info = IRQ_ports[irq]; if (!info) { IRQ_timeout[irq] = 60*HZ; return; } while (info) { if (info->timeout < timeout) timeout = info->timeout; info = info->next_port; } if (!irq) timeout = timeout / 2; IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1; } #endif static int startup(struct xr_async_struct * info) { unsigned long flags; int retval=0; void (*handler)(int, void *, struct pt_regs *); struct xr_serial_state *state= info->state; unsigned long page; TRACE("startup"); page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; spin_lock_irqsave( &info->irq_spinlock, flags); if (info->flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); free_page(page); goto errout; } if (info->xmit.buf) free_page(page); else info->xmit.buf = (unsigned char *) page; #ifdef SERIAL_DEBUG_OPEN printk("starting up %s%d (irq %d)...", DIRECT_DEV_ROOT, info->line, state->irq); #endif if (uart_config[state->type].flags & UART_STARTECH) { serial_out(info, EXAR_UART_EFR2, UART_EFR_ECB); serial_out(info, UART_IER, 0); /* Set the RX trigger level for 32 bytes, with a Hysteresis level of 8. */ /* These are some default values, the OEMs can change these values * according to their best case scenarios */ serial_out(info, EXAR_UART_FCTR, 0xc0 | HYSTERESIS_PLUS_MINUS_8); serial_out(info, EXAR_UART_RXTRIG, 32); serial_out(info, EXAR_UART_TXTRIG, 16); serial_outp(info, UART_LCR, 0); /* Wake up and initialize UART */ serial_out(info, EXAR_UART_EFR2, UART_EFR_ECB); serial_out(info, UART_IER, 0); serial_out(info, UART_LCR, 0); } /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) */ if (uart_config[state->type].flags & UART_CLEAR_FIFO) { serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); serial_outp(info, UART_FCR, 0); } /* * Clear the interrupt registers. */ (void) serial_inp(info, UART_LSR); (void) serial_inp(info, UART_RX); (void) serial_inp(info, UART_IIR); (void) serial_inp(info, UART_MSR); /* * 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 (!(info->flags & ASYNC_BUGGY_UART) && (serial_inp(info, UART_LSR) == 0xff)) { printk(KERN_INFO "%s%d: LSR safety check engaged!\n", DIRECT_DEV_ROOT, state->line); if (capable(CAP_SYS_ADMIN)) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); } else retval = -ENODEV; goto errout; } /* * Allocate the IRQ if necessary */ if (state->irq && (!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port)) { if (IRQ_ports[state->irq]) { #ifdef CONFIG_SERIAL_SHARE_IRQ free_irq(state->irq, &IRQ_ports[state->irq]); handler = xr_interrupt; #else retval = -EBUSY; goto errout; #endif /* CONFIG_SERIAL_SHARE_IRQ */ } else handler = xr_interrupt_single; retval = request_irq(state->irq, handler, SA_SHIRQ, MODULE_NAME, &IRQ_ports[state->irq]); if (retval) { if (capable(CAP_SYS_ADMIN)) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); retval = 0; } goto errout; } } /* * Insert serial port into IRQ chain. */ info->prev_port = 0; info->next_port = IRQ_ports[state->irq]; if (info->next_port) info->next_port->prev_port = info; IRQ_ports[state->irq] = info; #ifdef SERIAL_TIMER figure_IRQ_timeout(state->irq); #endif /* * Now, initialize the UART */ serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ info->MCR = 0; if (info->tty) { if (info->tty->termios->c_cflag & CBAUD) info->MCR = UART_MCR_DTR | UART_MCR_RTS; } else info->MCR = UART_MCR_DTR | UART_MCR_RTS; if (state->irq != 0) info->MCR |= UART_MCR_OUT2; info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ serial_outp(info, UART_MCR, info->MCR); /* * Finally, enable interrupts */ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; serial_outp(info, UART_IER, info->IER); /* enable interrupts */ /* * And clear the interrupt registers again for luck. */ (void)serial_inp(info, UART_LSR); (void)serial_inp(info, UART_RX); (void)serial_inp(info, UART_IIR); (void)serial_inp(info, UART_MSR); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit.head = info->xmit.tail = 0; /* * Set up serial timers... */ #ifdef SERIAL_TIMER mod_timer(&serial_timer, jiffies + 2*HZ/100); #endif /* * Set up the tty->alt_speed kludge */ #if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ if (info->tty) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; } #endif /* * and set the speed of the serial port */ change_speed(info, 0); info->flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore( &info->irq_spinlock, flags); return 0; errout: spin_unlock_irqrestore( &info->irq_spinlock, flags); return retval; } static int exser_lsi_startup(struct xr_async_struct * info) { unsigned long flags; int retval=0; // void (*handler)(int, void *, struct pt_regs *); struct xr_serial_state *state= info->state; unsigned long page; TRACE("exser_lsi_startup"); page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; spin_lock_irqsave( &info->irq_spinlock, flags); if (info->flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); free_page(page); goto errout; } if (info->xmit.buf) free_page(page); else info->xmit.buf = (unsigned char *) page; #ifdef SERIAL_DEBUG_OPEN printk("lsi starting up %s%d (irq %d)...", DIRECT_DEV_ROOT, info->line, state->irq); #endif // if (uart_config[state->type].flags & UART_STARTECH) { // serial_out(info, EXAR_UART_EFR2, UART_EFR_ECB); // serial_out(info, UART_IER, 0); /* Set the RX trigger level for 32 bytes, with a Hysteresis level of 8. */ /* These are some default values, the OEMs can change these values * according to their best case scenarios */ // serial_out(info, EXAR_UART_RXTRIG, 32); // serial_out(info, EXAR_UART_TXTRIG, 16); // serial_out(info, EXAR_UART_FCTR, 0xc0 | HYSTERESIS_PLUS_MINUS_8); // serial_outp(info, UART_LCR, 0); /* Wake up and initialize UART */ // serial_out(info, EXAR_UART_EFR2, UART_EFR_ECB); // serial_out(info, UART_IER, 0); // serial_out(info, UART_LCR, 0); // } /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) */ // if (uart_config[state->type].flags & UART_CLEAR_FIFO) { // serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); // serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | // UART_FCR_CLEAR_RCVR | // UART_FCR_CLEAR_XMIT)); // serial_outp(info, UART_FCR, 0); // } /* * Clear the interrupt registers. */ // (void) serial_inp(info, UART_LSR); // (void) serial_inp(info, UART_RX); // (void) serial_inp(info, UART_IIR); // (void) serial_inp(info, UART_MSR); /* * 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 (!(info->flags & ASYNC_BUGGY_UART) && // (serial_inp(info, UART_LSR) == 0xff)) { // printk("%s%d: LSR safety check engaged!\n", DIRECT_DEV_ROOT, state->line); // if (capable(CAP_SYS_ADMIN)) { // if (info->tty) // set_bit(TTY_IO_ERROR, &info->tty->flags); // } else // retval = -ENODEV; // goto errout; // } /* * Allocate the IRQ if necessary */ // if (state->irq && (!IRQ_ports[state->irq] || // !IRQ_ports[state->irq]->next_port)) { // if (IRQ_ports[state->irq]) { //#ifdef CONFIG_SERIAL_SHARE_IRQ // free_irq(state->irq, &IRQ_ports[state->irq]); // handler = xr_interrupt; //#else // retval = -EBUSY; // goto errout; //#endif /* CONFIG_SERIAL_SHARE_IRQ */ // } else // handler = xr_interrupt_single; // retval = request_irq(state->irq, handler, SA_SHIRQ, // "serial", &IRQ_ports[state->irq]); // if (retval) { // if (capable(CAP_SYS_ADMIN)) { // if (info->tty) // set_bit(TTY_IO_ERROR, // &info->tty->flags); // retval = 0; // } // goto errout; // } // } /* * Insert serial port into IRQ chain. */ // info->prev_port = 0; // info->next_port = IRQ_ports[state->irq]; // if (info->next_port) // info->next_port->prev_port = info; // IRQ_ports[state->irq] = info; // figure_IRQ_timeout(state->irq); /* * Now, initialize the UART */ // serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ info->MCR = 0; if (info->tty) { if (info->tty->termios->c_cflag & CBAUD) info->MCR = UART_MCR_DTR | UART_MCR_RTS; } else info->MCR = UART_MCR_DTR | UART_MCR_RTS; if (state->irq != 0) info->MCR |= UART_MCR_OUT2; info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ serial_outp(info, UART_MCR, info->MCR); /* * Finally, enable interrupts */ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; serial_outp(info, UART_IER, info->IER); /* enable interrupts */ /* * And clear the interrupt registers again for luck. */ // (void)serial_inp(info, UART_LSR); // (void)serial_inp(info, UART_RX); // (void)serial_inp(info, UART_IIR); // (void)serial_inp(info, UART_MSR); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit.head = info->xmit.tail = 0; /* * Set up serial timers... */ #ifdef SERIAL_TIMER mod_timer(&serial_timer, jiffies + 2*HZ/100); #endif /* * Set up the tty->alt_speed kludge */ #if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ if (info->tty) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; } #endif /* * and set the speed of the serial port */ change_speed(info, 0); info->flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore( &info->irq_spinlock, flags); return 0; errout: #ifdef SERIAL_DEBUG_OPEN printk("exser_lsi_startup errout\n"); #endif spin_unlock_irqrestore( &info->irq_spinlock, flags); return retval; } /* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */ static void shutdown(struct xr_async_struct * info) { unsigned long flags; struct xr_serial_state *state; #ifdef NON_DEVICE_ALLOWED int retval; #endif if (!(info->flags & ASYNC_INITIALIZED)) return; state = info->state; #ifdef SERIAL_DEBUG_OPEN printk("Shutting down serial port %d (irq %d)....", info->line, state->irq); #endif spin_lock_irqsave( &info->irq_spinlock, flags); /* * 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); #ifdef NON_DEVICE_ALLOWED if(PORT_TYPE_DEVICE != info->port_type) { info->IER = 0; serial_outp(info, UART_IER, 0x00); /* disable all intrs */ info->MCR &= ~UART_MCR_OUT2; info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ /* disable break condition */ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); serial_outp(info, UART_MCR, info->MCR); /* disable FIFO's */ serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); serial_outp(info, UART_FCR, 0); (void)serial_in(info, UART_RX); /* read data port to reset things */ } #else info->IER &= ~(UART_IER_MSI | UART_IER_THRI); serial_outp(info, UART_IER, info->IER); /* disable intrs */ /* disable break condition */ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); #endif if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~ASYNC_INITIALIZED; #ifdef NON_DEVICE_ALLOWED if(PORT_TYPE_DEVICE != info->port_type) { /* * First unlink the serial port from the IRQ chain... */ if (info->next_port) info->next_port->prev_port = info->prev_port; if (info->prev_port) info->prev_port->next_port = info->next_port; else IRQ_ports[state->irq] = info->next_port; #ifdef SERIAL_TIMER figure_IRQ_timeout(state->irq); #endif /* * Free the IRQ, if necessary */ if (state->irq && (!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port)) { if (IRQ_ports[state->irq]) { free_irq(state->irq, &IRQ_ports[state->irq]); retval = request_irq(state->irq, xr_interrupt_single, SA_SHIRQ, MODULE_NAME, &IRQ_ports[state->irq]); if (retval) printk(KERN_INFO MODULE_NAME " shutdown: request_irq: error %d" " Couldn't reacquire IRQ.\n", retval); } else free_irq(state->irq, &IRQ_ports[state->irq]); } } #endif if (info->xmit.buf) { unsigned long pg = (unsigned long) info->xmit.buf; info->xmit.buf = 0; free_page(pg); } spin_unlock_irqrestore( &info->irq_spinlock, flags); #ifdef SERIAL_DEBUG_OPEN printk("shutdown complete\n"); #endif } #if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 0 }; static int tty_get_baud_rate(struct tty_struct *tty) { struct xr_async_struct * info = (struct xr_async_struct *)tty->driver_data; unsigned int cflag, i; cflag = tty->termios->c_cflag; i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; if (i < 1 || i > 2) tty->termios->c_cflag &= ~CBAUDEX; else i += 15; } if (i == 15) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) i += 1; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) i += 2; } return baud_table[i]; } #endif /* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */ static void change_speed(struct xr_async_struct *info, struct termios *old_termios) { int quot = 0, baud_base, baud; unsigned cflag, cval, fcr = 0; int bits; unsigned long flags; struct tty_struct tty; struct termios termios; TRACE("change_speed"); if (!info->tty) { info->tty = &tty; info->tty->termios = &termios; info->tty->driver_data = info; if (info->flags & ASYNC_NORMAL_ACTIVE) *info->tty->termios = info->state->normal_termios; else *info->tty->termios = info->state->callout_termios; } else if (!info->tty->termios) return; if (!CONFIGURED_SERIAL_PORT(info)) return; cflag = info->tty->termios->c_cflag; /* byte size and parity */ switch (cflag & CSIZE) { case CS5: cval = 0x00; bits = 7; break; case CS6: cval = 0x01; bits = 8; break; case CS7: cval = 0x02; bits = 9; break; case CS8: cval = 0x03; bits = 10; break; /* Never happens, but GCC is too dumb to figure it out */ default: cval = 0x00; bits = 7; break; } if (cflag & CSTOPB) { cval |= 0x04; bits++; } if (cflag & PARENB) { cval |= UART_LCR_PARITY; bits++; } if (!(cflag & PARODD)) cval |= UART_LCR_EPAR; #ifdef CMSPAR if (cflag & CMSPAR) cval |= UART_LCR_SPAR; #endif /* Determine divisor based on baud rate */ baud = tty_get_baud_rate(info->tty); if (!baud) baud = 9600; /* B0 transition handled in xr_set_termios */ baud_base = info->state->baud_base; if (baud == 38400 && ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->state->custom_divisor; else { if (baud == 134) /* Special case since 134 is really 134.5 */ quot = (2*baud_base / 269); else if (baud) quot = baud_base / baud; } /* If the quotient is zero refuse the change */ if (!quot && old_termios) { info->tty->termios->c_cflag &= ~CBAUD; info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); baud = tty_get_baud_rate(info->tty); if (!baud) baud = 9600; if (baud == 38400 && ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->state->custom_divisor; else { if (baud == 134) /* Special case since 134 is really 134.5 */ quot = (2*baud_base / 269); else if (baud) quot = baud_base / baud; } } /* As a last resort, if the quotient is zero, default to 9600 bps */ if (!quot) quot = baud_base / 9600; info->quot = quot; info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); info->timeout += HZ/50; /* Add .02 seconds of slop */ /* Set up FIFO's */ if (uart_config[info->state->type].flags & UART_USE_FIFO) { if ((info->state->baud_base / quot) < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; else fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; } /* CTS flow control flag and modem status interrupts */ info->IER &= ~UART_IER_MSI; if (info->flags & ASYNC_HARDPPS_CD) info->IER |= UART_IER_MSI; if (cflag & CRTSCTS) { info->flags |= ASYNC_CTS_FLOW; info->IER |= UART_IER_MSI; } else info->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; else { info->flags |= ASYNC_CHECK_CD; info->IER |= UART_IER_MSI; } serial_out(info, UART_IER, info->IER); /* * Set up parity check flag */ #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; 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; /* * Characters to ignore */ info->ignore_status_mask = 0; if (I_IGNPAR(info->tty)) info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; if (I_IGNBRK(info->tty)) { info->ignore_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; } /* * !!! ignore all characters if CREAD is not set */ if ((cflag & CREAD) == 0) info->ignore_status_mask |= UART_LSR_DR; save_flags(flags); cli(); if (uart_config[info->state->type].flags & UART_STARTECH) { serial_outp(info, UART_LCR, 0xBF); if(info->state->type == PORT_XR16L788) { unsigned char ier; unsigned char flow_cntl = 0; unsigned int iflag = info->tty->termios->c_iflag; if (cflag & CRTSCTS) { DEBUG_PRINT(DEBUG_SPEED, "%s: CRTSCTS\n", module_name); flow_cntl |= 0xc0; } if (iflag & IXOFF) { DEBUG_PRINT(DEBUG_SPEED, "%s: IXOFF\n", module_name); flow_cntl |= 0x08; } if (iflag & IXON) { DEBUG_PRINT(DEBUG_SPEED, "%s: IXON\n", module_name); flow_cntl |= 0x02; } DEBUG_PRINT(DEBUG_SPEED, "%s: flow_cntl = 0x%x\n", module_name, flow_cntl); ier = serial_in(info, UART_IER); serial_out(info, EXAR_UART_EFR2, UART_EFR_ECB); serial_out(info, UART_IER, 0 ); /* disable interrupts */ serial_out(info, EXAR_UART_XON1, START_CHAR(info->tty)); serial_out(info, EXAR_UART_XOFF1, STOP_CHAR(info->tty)); serial_out(info, EXAR_UART_EFR2, flow_cntl|UART_EFR_ECB); serial_out(info, UART_IER, ier ); /* enable interrupts */ } else serial_outp(info, UART_EFR, (cflag & CRTSCTS) ? UART_EFR_CTS : 0); } serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */ serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */ serial_outp(info, UART_LCR, cval); /* reset DLAB */ info->LCR = cval; /* Save LCR */ DEBUG_PRINT(DEBUG_SPEED, "%s: change speed (fcr=0x%x)\n", module_name, fcr); if(fcr & UART_FCR_ENABLE_FIFO) { serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); } serial_outp(info, UART_FCR, fcr); /* set fcr */ restore_flags(flags); if (&tty == info->tty) info->tty = 0; } static void xr_put_char(struct tty_struct *tty, unsigned char ch) { struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "xr_put_char")) return; if (!tty || !info->xmit.buf) return; save_flags(flags); cli(); if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) { restore_flags(flags); return; } info->xmit.buf[info->xmit.head] = ch; info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); restore_flags(flags); } static void xr_flush_chars(struct tty_struct *tty) { struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "xr_flush_chars")) return; if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped || !info->xmit.buf) return; save_flags(flags); cli(); info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); restore_flags(flags); } static int xr_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { int c, ret = 0; struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; unsigned long flags; TRACE("xr_write"); if (serial_paranoia_check(info, tty->device, "xr_write")) return 0; if (!tty || !info->xmit.buf || !tmp_buf) return 0; save_flags(flags); if (from_user) { down(&tmp_buf_sem); while (1) { int c1; c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); if (count < c) c = count; if (c <= 0) break; c -= copy_from_user(tmp_buf, buf, c); if (!c) { if (!ret) ret = -EFAULT; break; } cli(); c1 = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); if (c1 < c) c = c1; memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); info->xmit.head = ((info->xmit.head + c) & (SERIAL_XMIT_SIZE-1)); restore_flags(flags); buf += c; count -= c; ret += c; } up(&tmp_buf_sem); } else { cli(); while (1) { c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); if (count < c) c = count; if (c <= 0) { break; } memcpy(info->xmit.buf + info->xmit.head, buf, c); info->xmit.head = ((info->xmit.head + c) & (SERIAL_XMIT_SIZE-1)); buf += c; count -= c; ret += c; } restore_flags(flags); } if (info->xmit.head != info->xmit.tail && !tty->stopped && !tty->hw_stopped && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); } return ret; } static int xr_write_room(struct tty_struct *tty) { struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "xr_write_room")) return 0; return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); } static int xr_chars_in_buffer(struct tty_struct *tty) { struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; TRACE("xr_chars_in_buffer"); if (serial_paranoia_check(info, tty->device, "xr_chars_in_buffer")) return 0; return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); } static void xr_flush_buffer(struct tty_struct *tty) { struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "xr_flush_buffer")) return; save_flags(flags); cli(); info->xmit.head = info->xmit.tail = 0; restore_flags(flags); wake_up_interruptible(&tty->write_wait); #ifdef SERIAL_HAVE_POLL_WAIT wake_up_interruptible(&tty->poll_wait); #endif if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); } /* * This function is used to send a high-priority XON/XOFF character to * the device */ static void xr_send_xchar(struct tty_struct *tty, char ch) { struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "xr_send_xchar")) return; info->x_char = ch; if (ch) { /* Make sure transmit interrupts are on */ info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); } } /* * ------------------------------------------------------------ * xr_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */ static void xr_throttle(struct tty_struct * tty) { struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("throttle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty)); #endif if (serial_paranoia_check(info, tty->device, "xr_throttle")) return; if (I_IXOFF(tty)) xr_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) info->MCR &= ~UART_MCR_RTS; save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); restore_flags(flags); } static void xr_unthrottle(struct tty_struct * tty) { struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("unthrottle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty)); #endif if (serial_paranoia_check(info, tty->device, "xr_unthrottle")) return; if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else xr_send_xchar(tty, START_CHAR(tty)); } if (tty->termios->c_cflag & CRTSCTS) info->MCR |= UART_MCR_RTS; save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); restore_flags(flags); } /* * ------------------------------------------------------------ * xr_ioctl() and friends * ------------------------------------------------------------ */ static int get_serial_info(struct xr_async_struct * info, struct serial_struct * retinfo) { struct serial_struct tmp; struct xr_serial_state *state = info->state; TRACE("get_serial_info"); if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); tmp.type = state->type; tmp.line = state->line; tmp.port = state->port; if (HIGH_BITS_OFFSET) tmp.port_high = state->port >> HIGH_BITS_OFFSET; else tmp.port_high = 0; tmp.irq = state->irq; tmp.flags = state->flags; tmp.xmit_fifo_size = state->xmit_fifo_size; tmp.baud_base = state->baud_base; tmp.close_delay = state->close_delay; tmp.closing_wait = state->closing_wait; tmp.custom_divisor = state->custom_divisor; tmp.hub6 = state->hub6; tmp.io_type = state->io_type; if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0; } #ifdef NON_DEVICE_ALLOWED static int set_serial_info(struct xr_async_struct * info, struct serial_struct * new_info) { struct serial_struct new_serial; struct xr_serial_state old_state, *state; unsigned int i,change_irq,change_port; int retval = 0; unsigned long new_port; TRACE("set_serial_info"); if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; state = info->state; old_state = *state; new_port = new_serial.port; if (HIGH_BITS_OFFSET) new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; change_irq = new_serial.irq != state->irq; /* change_port = (new_port != ((int) state->port)) || (new_serial.hub6 != state->hub6); modified by RKR 04/01/2003 */ change_port = 0; if (!capable(CAP_SYS_ADMIN)) { if (change_irq || change_port || (new_serial.baud_base != state->baud_base) || (new_serial.type != state->type) || (new_serial.close_delay != state->close_delay) || (new_serial.xmit_fifo_size != state->xmit_fifo_size) || ((new_serial.flags & ~ASYNC_USR_MASK) != (state->flags & ~ASYNC_USR_MASK))) return -EPERM; state->flags = ((state->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); info->flags = ((info->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); state->custom_divisor = new_serial.custom_divisor; goto check_and_exit; } new_serial.irq = irq_cannonicalize(new_serial.irq); if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) || (new_serial.type == PORT_STARTECH)) { return -EINVAL; } if ((new_serial.type != state->type) || (new_serial.xmit_fifo_size <= 0)) new_serial.xmit_fifo_size = uart_config[new_serial.type].dfl_xmit_fifo_size; /* Make sure address is not already in use */ if (new_serial.type) { for (i = 0 ; i < NR_PORTS; i++) if ((state != &xr_table[i]) && (xr_table[i].io_type == SERIAL_IO_PORT) && (xr_table[i].port == new_port) && xr_table[i].type) return -EADDRINUSE; } if ((change_port || change_irq) && (state->count > 1)) return -EBUSY; /* * OK, past this point, all the error checking has been done. * At this point, we start making changes..... */ state->baud_base = new_serial.baud_base; state->flags = ((state->flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | (info->flags & ASYNC_INTERNAL_FLAGS)); state->custom_divisor = new_serial.custom_divisor; state->close_delay = new_serial.close_delay * HZ/100; state->closing_wait = new_serial.closing_wait * HZ/100; #if (LINUX_VERSION_CODE > 0x20100) info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; #endif info->xmit_fifo_size = state->xmit_fifo_size = new_serial.xmit_fifo_size; if ((state->type != PORT_UNKNOWN) && state->port) { release_region(state->port,16); } state->type = new_serial.type; if (change_port || change_irq) { /* * We need to shutdown the serial port at the old * port/irq combination. */ shutdown(info); state->irq = new_serial.irq; info->port = state->port = new_port; info->hub6 = state->hub6 = new_serial.hub6; if (info->hub6) info->io_type = state->io_type = SERIAL_IO_HUB6; else if (info->io_type == SERIAL_IO_HUB6) info->io_type = state->io_type = SERIAL_IO_PORT; } if ((state->type != PORT_UNKNOWN) && state->port) { request_region(state->port,16,MODULE_NAME "(set)"); } check_and_exit: if ((!state->port && !state->iomem_base) || !state->type) return 0; if (info->flags & ASYNC_INITIALIZED) { if (((old_state.flags & ASYNC_SPD_MASK) != (state->flags & ASYNC_SPD_MASK)) || (old_state.custom_divisor != state->custom_divisor)) { #if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; #endif change_speed(info, 0); } } else retval = startup(info); return retval; } #endif /* * 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 get_lsr_info(struct xr_async_struct * info, unsigned int *value) { unsigned char status; unsigned int result; unsigned long flags; save_flags(flags); cli(); status = serial_in(info, UART_LSR); restore_flags(flags); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); /* * If we're about to load something into the transmit * register, we'll pretend the transmitter isn't empty to * avoid a race condition (depending on when the transmit * interrupt happens). */ if (info->x_char || ((CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) > 0) && !info->tty->stopped && !info->tty->hw_stopped)) result &= ~TIOCSER_TEMT; if (copy_to_user(value, &result, sizeof(int))) return -EFAULT; return 0; } static int get_modem_info(struct xr_async_struct * info, unsigned int *value) { unsigned char control, status; unsigned int result; unsigned long flags; control = info->MCR; save_flags(flags); cli(); status = serial_in(info, UART_MSR); restore_flags(flags); result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) #ifdef TIOCM_OUT1 | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) #endif | ((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); if (copy_to_user(value, &result, sizeof(int))) return -EFAULT; return 0; } static int set_modem_info(struct xr_async_struct * info, unsigned int cmd, unsigned int *value) { unsigned int arg; unsigned long flags; if (copy_from_user(&arg, value, sizeof(int))) return -EFAULT; switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS) info->MCR |= UART_MCR_RTS; if (arg & TIOCM_DTR) info->MCR |= UART_MCR_DTR; #ifdef TIOCM_OUT1 if (arg & TIOCM_OUT1) info->MCR |= UART_MCR_OUT1; if (arg & TIOCM_OUT2) info->MCR |= UART_MCR_OUT2; #endif if (arg & TIOCM_LOOP) info->MCR |= UART_MCR_LOOP; break; case TIOCMBIC: if (arg & TIOCM_RTS) info->MCR &= ~UART_MCR_RTS; if (arg & TIOCM_DTR) info->MCR &= ~UART_MCR_DTR; #ifdef TIOCM_OUT1 if (arg & TIOCM_OUT1) info->MCR &= ~UART_MCR_OUT1; if (arg & TIOCM_OUT2) info->MCR &= ~UART_MCR_OUT2; #endif if (arg & TIOCM_LOOP) info->MCR &= ~UART_MCR_LOOP; break; case TIOCMSET: info->MCR = ((info->MCR & ~(UART_MCR_RTS | #ifdef TIOCM_OUT1 UART_MCR_OUT1 | UART_MCR_OUT2 | #endif UART_MCR_LOOP | UART_MCR_DTR)) | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) #ifdef TIOCM_OUT1 | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) #endif | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0) | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); break; default: return -EINVAL; } save_flags(flags); cli(); info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ serial_out(info, UART_MCR, info->MCR); restore_flags(flags); return 0; } #ifdef NON_DEVICE_ALLOWED static int do_autoconfig(struct xr_async_struct * info) { int irq, retval; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (info->state->count > 1) return -EBUSY; shutdown(info); autoconfig(info->state); if ((info->state->flags & ASYNC_AUTO_IRQ) && (info->state->port != 0 || info->state->iomem_base != 0) && (info->state->type != PORT_UNKNOWN)) { irq = detect_uart_irq(info->state); if (irq > 0) info->state->irq = irq; } retval = startup(info); if (retval) return retval; return 0; } #endif /* * xr_break() --- routine which turns the break handling on or off */ #if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ static void send_break( struct xr_async_struct * info, int duration) { if (!CONFIGURED_SERIAL_PORT(info)) return; current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + duration; cli(); info->LCR |= UART_LCR_SBC; serial_out(info, UART_LCR, info->LCR); schedule(); info->LCR &= ~UART_LCR_SBC; serial_out(info, UART_LCR, info->LCR); sti(); } #else static void xr_break(struct tty_struct *tty, int break_state) { struct xr_async_struct * info = (struct xr_async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "xr_break")) return; if (!CONFIGURED_SERIAL_PORT(info)) return; save_flags(flags); cli(); if (break_state == -1) info->LCR |= UART_LCR_SBC; else info->LCR &= ~UART_LCR_SBC; serial_out(info, UART_LCR, info->LCR); restore_flags(flags); } #endif struct exser_read_reg { int reg; int regvalue; }; #if (LINUX_VERSION_CODE < 131336) #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) #else #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 static int exser_read_reg(struct xr_async_struct * info, struct exser_read_reg *arg) { unsigned char result; unsigned int reg; int error; unsigned long flags; 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 = readb(info->iomem_base + reg); restore_flags(flags); // printk("exser: Read base=0x%08x, Reg=0x%08x, value=0x%08x\n", info->iomem_base, reg, result); put_to_user(result, &(arg->regvalue)); return(0); } static int exser_write_reg(struct xr_async_struct * info, struct exser_read_reg *arg) { unsigned int reg, value; get_from_user(reg,&(arg->reg)); get_from_user(value,&(arg->regvalue)); writeb(value, info->iomem_base + reg); // printk("exser: write base=0x%08x, Reg=0x%08x, value=0x%08x\n", info->iomem_base, reg, value); return(0); } static int xr_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { struct xr_async_struct * info = (struct xr_async_struct *)tty->driver_data; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct icount; unsigned long flags; #if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ int retval, tmp; #endif int error; TRACE("xr_ioctl"); if (serial_paranoia_check(info, tty->device, "xr_ioctl")) return -ENODEV; if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } switch (cmd) { #if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ 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 (signal_pending(current)) return -EINTR; if (!arg) { send_break(info, HZ/4); /* 1/4 second */ if (signal_pending(current)) return -EINTR; } return 0; 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); if (signal_pending(current)) return -EINTR; send_break(info, arg ? arg*(HZ/10) : HZ/4); if (signal_pending(current)) return -EINTR; return 0; case TIOCGSOFTCAR: TRACE("ioctl: TIOCGSOFTCAR \n"); tmp = C_CLOCAL(tty) ? 1 : 0; if (copy_to_user((void *)arg, &tmp, sizeof(int))) return -EFAULT; return 0; case TIOCSSOFTCAR: TRACE("ioctl: TIOCSSOFTCAR \n"); if (copy_from_user(&tmp, (void *)arg, sizeof(int))) return -EFAULT; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (tmp ? CLOCAL : 0)); return 0; #endif case TIOCMGET: TRACE("ioctl: TIOCMGET \n"); return get_modem_info(info, (unsigned int *) arg); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: TRACE("ioctl: TIOCMSET \n"); return set_modem_info(info, cmd, (unsigned int *) arg); case TIOCGSERIAL: TRACE("ioctl: TIOCGSERIAL \n"); return get_serial_info(info, (struct serial_struct *) arg); #ifdef NON_DEVICE_ALLOWED case TIOCSSERIAL: TRACE("ioctl: TIOCSSERIAL \n"); return set_serial_info(info, (struct serial_struct *) arg); #endif #ifdef NON_DEVICE_ALLOWED case TIOCSERCONFIG: TRACE("ioctl: TIOCSERCONFIG \n"); return do_autoconfig(info); #endif case TIOCSERGETLSR: /* Get line status register */ TRACE("ioctl: TIOCSERGETLSR \n"); return get_lsr_info(info, (unsigned int *) arg); case TIOCSERGSTRUCT: TRACE("ioctl: TIOCSERGSTRUCT \n"); if (copy_to_user((struct xr_async_struct *) arg, info, sizeof(struct xr_async_struct))) return -EFAULT; return 0; /* * 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(); /* note the counters on entry */ cprev = info->state->icount; restore_flags(flags); /* Force modem status interrupts on */ info->IER |= UART_IER_MSI; serial_out(info, UART_IER, info->IER); 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->state->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 0; } cprev = cnow; } /* NOTREACHED */ /* * 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"); save_flags(flags); cli(); cnow = info->state->icount; restore_flags(flags); icount.cts = cnow.cts; icount.dsr = cnow.dsr; icount.rng = cnow.rng; icount.dcd = cnow.dcd; icount.rx = cnow.rx; icount.tx = cnow.tx; icount.frame = cnow.frame; icount.overrun = cnow.overrun; icount.parity = cnow.parity; icount.brk = cnow.brk; icount.buf_overrun = cnow.buf_overrun; if (copy_to_user((void *)arg, &icount, sizeof(icount))) return -EFAULT; return 0; case TIOCSERGWILD: case TIOCSERSWILD: /* "setserial -W" is called in Debian boot */ printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); return 0; // Exar's ioctls for the test GUI case EXAR_READ_REG: TRACE("ioctl: EXAR_READ_REG \n"); // printk("\n READ_REG ioctl \n"); 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: TRACE("ioctl: EXAR_WRITE_REG \n"); // printk("\n WRITE_REG ioctl \n"); 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: TRACE("ioctl: READ_IO_PORT \n"); 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 0; } break; case BUFFER_ENABLE: TRACE("ioctl: BUFFER_ENABLE \n"); if (PORT_TYPE_DEVICE == info->state->port_type) { lsi_devices[info->line].stop_buffering_in_direct = !(int)arg; return 0; } return -EIO; break; case QUERY_BUFFER_ENABLE: TRACE("ioctl: QUERY_BUFFER_ENABLE \n"); if (PORT_TYPE_DEVICE == info->state->port_type) { put_user(!(lsi_devices[info->line].stop_buffering_in_direct), (int *)arg); return 0; } return -EIO; break; case PARITY_MARK_SPACE: { unsigned char scratch; TRACE("ioctl: PARITY_MARK_SPACE \n"); // 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->port + UART_LCR) & ~(0x7<<3)) | (0x5<<3); outb(scratch, info->port + UART_LCR); break; case 2: //space scratch = inb(info->port + UART_LCR) | (0x7<<3); outb(scratch, info->port + UART_LCR); break; } } return 0; break; case QUERY_PARITY: { int scratch = inb(info->port + UART_LCR)>>3 & 0x7; TRACE("ioctl: QUERY_PARITY \n"); // 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 // if (scratch & 0x1) { scratch >>= 1; ++scratch; put_user(scratch, (int *)arg); } else { scratch = 0; put_user(scratch, (int *)arg); } return 0; } break; case RESET_RDWR_COUNT: TRACE("ioctl: RESET_RDWR_COUNT \n"); if (PORT_TYPE_DEVICE == info->state->port_type) { lsi_devices[info->line].rdwr_count = 0; return 0; } return -EIO; break; case QUERY_RDWR_COUNT: TRACE("ioctl: QUERY_RDWR_COUNT \n"); if (PORT_TYPE_DEVICE == info->state->port_type) { put_user(lsi_devices[info->line].rdwr_count, (int *)arg); return 0; } return -EIO; break; case SET_PORT_TYPE: TRACE("ioctl: SET_PORT_TYPE \n"); #ifdef NON_DEVICE_ALLOWED if (PORT_TYPE_DEVICE != info->state->port_type && PORT_TYPE_DEVICE == (type_port)arg) { save_flags(flags); cli(); lsi_devices[info->line].rdwr_count = 0; info->state->port_type = PORT_TYPE_DEVICE; restore_flags(flags); // enable reception of data retval = startup(info); if ( retval ) { return(retval); } exser_change_speed (info, NULL, TRUE); return 0; } else if (PORT_TYPE_DEVICE == info->state->port_type && PORT_TYPE_DEVICE != (type_port)arg) { save_flags(flags); cli(); lsi_devices[info->line].rdwr_count = 0; info->state->port_type = (type_port)arg; restore_flags(flags); // disable reception of data exser_shutdown(info); return 0; } else if ((type_port)arg == PORT_TYPE_OFF || (type_port)arg == PORT_TYPE_GETTY || (type_port)arg == PORT_TYPE_NON_GETTY) return 0; return -EIO; #else return(-ENOTSUPP); #endif break; case QUERY_PORT_TYPE: TRACE("ioctl: QUERY_PORT_TYPE \n"); #ifdef NON_DEVICE_ALLOWED if (info->state->port_type < PORT_TYPE_LIST_LAST) { put_user(info->state->port_type, (int *)arg); return 0; } return -EIO; #else return(-ENOTSUPP); #endif break; case EXAR_SET_DTEDCE: TRACE("ioctl: EXAR_SET_DTEDCE \n"); return(set_dtedce (info->line+1, ((struct EXAR_STRUCT_DTEDCE_MODE *)arg)->port_mode)); break; case EXAR_GET_DTEDCE: TRACE("ioctl: EXAR_GET_DTEDCE \n"); return(get_dtedce (info->line+1, (struct EXAR_STRUCT_DTEDCE_MODE *)arg)); break; default: TRACE("ioctl: ENOIOCTLCMD \n"); return -ENOIOCTLCMD; } // end switch return 0; } static void xr_set_termios(struct tty_struct *tty, struct termios *old_termios) { struct xr_async_struct *info = (struct xr_async_struct *)tty->driver_data; unsigned long flags; unsigned int cflag = tty->termios->c_cflag; TRACE("xr_set_termios"); if ( (cflag == old_termios->c_cflag) && ( RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) return; change_speed(info, old_termios); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); restore_flags(flags); } /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { info->MCR |= UART_MCR_DTR; if (!(tty->termios->c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { info->MCR |= UART_MCR_RTS; } save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); restore_flags(flags); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; xr_start(tty); } } /* * ------------------------------------------------------------ * xr_close() * * 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 xr_close(struct tty_struct *tty, struct file * filp) { struct xr_async_struct * info = (struct xr_async_struct *)tty->driver_data; struct xr_serial_state *state; unsigned long flags; TRACE("xr_close"); DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" xr_close@%d tty=%p filp=%p private_data=%p tty->driver_data=%p\n", __LINE__, (void *)tty, (void *)filp, filp->private_data, (void *)(tty->driver_data)); if (!info || serial_paranoia_check(info, tty->device, "xr_close")) { DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - info or tty->device is NULL\n"); return; } state = info->state; save_flags(flags); cli(); if (tty_hung_up_p(filp)) { DBG_CNT("before DEC-hung"); MOD_DEC_RDWR_COUNT(info); MOD_DEC_USE_COUNT; DEBUG_PRINT(DEBUG_TRACE, DEFAULT_PREFIX" @%d MOD_DEC_USE_COUNT\n", __LINE__); restore_flags(flags); return; } #ifdef SERIAL_DEBUG_OPEN printk("xr_close %s%d, count = %d\n", DIRECT_DEV_ROOT, info->line, state->count); #endif if ((tty->count == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->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("xr_close: bad serial port count; tty->count is 1, " "state->count is %d\n", state->count); state->count = 1; } if (--state->count < 0) { printk("xr_close: bad serial port count for %s%d: %d\n", DIRECT_DEV_ROOT, info->line, state->count); state->count = 0; } if (state->count) { DBG_CNT("before DEC-2"); MOD_DEC_RDWR_COUNT(info); MOD_DEC_USE_COUNT; DEBUG_PRINT(DEBUG_TRACE, DEFAULT_PREFIX" @%d MOD_DEC_USE_COUNT\n", __LINE__); restore_flags(flags); return; } info->flags |= ASYNC_CLOSING; restore_flags(flags); /* * Save the termios structure, since this port may have * separate termios for callout and dialin. */ if (info->flags & ASYNC_NORMAL_ACTIVE) info->state->normal_termios = *tty->termios; if (info->flags & ASYNC_CALLOUT_ACTIVE) info->state->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) tty_wait_until_sent(tty, info->closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the * interrupt driver to stop checking the data ready bit in the * line status register. */ #ifdef NON_DEVICE_ALLOWED if(PORT_TYPE_DEVICE != info->port_type) { info->IER &= ~UART_IER_RLSI; info->read_status_mask &= ~UART_LSR_DR; } #endif if (info->flags & ASYNC_INITIALIZED) { #ifdef NON_DEVICE_ALLOWED serial_out(info, UART_IER, info->IER); #endif /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ xr_wait_until_sent(tty, info->timeout); } if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); // do not leave a device channel a in an xoff state if (PORT_TYPE_DEVICE == info->state->port_type && I_IXOFF(tty)&& (lsi_devices[info->line].rdwr_count == 1)) { DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - software flow ctl enabled - send XON\n"); reset_auto_flowctl(info); // in case we are stopped xr_send_xchar(tty, START_CHAR(tty)); } shutdown(info); tty->closing = 0; info->event = 0; info->tty = 0; if (info->blocked_open) { if (info->close_delay) { set_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); wake_up_interruptible(&info->close_wait); MOD_DEC_RDWR_COUNT(info); MOD_DEC_USE_COUNT; DEBUG_PRINT(DEBUG_TRACE, DEFAULT_PREFIX" @%d MOD_DEC_USE_COUNT\n", __LINE__); } /* * xr_wait_until_sent() --- wait until the transmitter is empty */ static void xr_wait_until_sent(struct tty_struct *tty, int timeout) { struct xr_async_struct * info = (struct xr_async_struct *)tty->driver_data; unsigned long orig_jiffies, char_time; int lsr; if (serial_paranoia_check(info, tty->device, "xr_wait_until_sent")) return; if (info->state->type == PORT_UNKNOWN) return; if (info->xmit_fifo_size == 0) return; /* Just in case.... */ orig_jiffies = jiffies; /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check * interval should also be less than the timeout. * * Note: we have to use pretty tight timings here to satisfy * the NIST-PCTS. */ char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; char_time = char_time / 5; if (char_time == 0) char_time = 1; if (timeout && timeout < char_time) char_time = timeout; /* * If the transmitter hasn't cleared in twice the approximate * amount of time to send the entire FIFO, it probably won't * ever clear. This assumes the UART isn't doing flow * control, which is currently the case. Hence, if it ever * takes longer than info->timeout, this is probably due to a * UART bug of some kind. So, we clamp the timeout parameter at * 2*info->timeout. */ if (!timeout || timeout > 2*info->timeout) timeout = 2*info->timeout; #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("In xr_wait_until_sent(%d) check=%lu...", timeout, char_time); printk("jiff=%lu...", jiffies); #endif while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) { #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...", lsr, jiffies); #endif set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; } #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); #endif } /* * xr_hangup() --- called by tty_hangup() when a hangup is signaled. */ static void xr_hangup(struct tty_struct *tty) { struct xr_async_struct * info = (struct xr_async_struct *)tty->driver_data; struct xr_serial_state *state = info->state; TRACE("xr_hangup"); if (serial_paranoia_check(info, tty->device, "xr_hangup")) return; state = info->state; xr_flush_buffer(tty); if (info->flags & ASYNC_CLOSING) return; // do not leave a device channel a in an xoff state if (PORT_TYPE_DEVICE == info->state->port_type && I_IXOFF(tty) && (lsi_devices[info->line].rdwr_count == 1)) { DEBUG_PRINT(DEBUG_CLOSE, DEFAULT_PREFIX" close - software flow ctl enabled - send XON\n"); reset_auto_flowctl(info); // in case we are stopped xr_send_xchar(tty, START_CHAR(tty)); } shutdown(info); info->event = 0; state->count = 0; info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); info->tty = 0; wake_up_interruptible(&info->open_wait); } /* * ------------------------------------------------------------ * xr_open() and friends * ------------------------------------------------------------ */ static int block_til_ready(struct tty_struct *tty, struct file * filp, struct xr_async_struct *info) { DECLARE_WAITQUEUE(wait, current); struct xr_serial_state *state = info->state; int retval; int do_clocal = 0, extra_count = 0; unsigned long flags; /* * 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 return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -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 0; } /* * 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 0; } if (info->flags & ASYNC_CALLOUT_ACTIVE) { if (state->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, state->count is dropped by one, so that * xr_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; add_wait_queue(&info->open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: %s%d, count = %d\n", DIRECT_DEV_ROOT, state->line, state->count); #endif save_flags(flags); cli(); if (!tty_hung_up_p(filp)) { extra_count = 1; state->count--; } restore_flags(flags); info->blocked_open++; while (1) { save_flags(flags); cli(); if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && (tty->termios->c_cflag & CBAUD)) serial_out(info, UART_MCR, serial_inp(info, UART_MCR) | (UART_MCR_DTR | UART_MCR_RTS)); restore_flags(flags); set_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 || (serial_in(info, UART_MSR) & UART_MSR_DCD))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready blocking: %s%d, count = %d\n", DIRECT_DEV_ROOT, info->line, state->count); #endif schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); if (extra_count) state->count++; info->blocked_open--; #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready after blocking: %s%d, count = %d\n", DIRECT_DEV_ROOT, info->line, state->count); #endif if (retval) return retval; info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } static int get_xr_async_struct(int line, struct xr_async_struct **ret_info) { struct xr_async_struct *info; struct xr_serial_state *sstate; sstate = xr_table + line; // sstate->count++; if (sstate->info) { *ret_info = sstate->info; return 0; } info = kmalloc(sizeof(struct xr_async_struct), GFP_KERNEL); if (!info) { // sstate->count--; return -ENOMEM; } memset(info, 0, sizeof(struct xr_async_struct)); init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->delta_msr_wait); info->magic = SERIAL_MAGIC; info->port = sstate->port; info->flags = sstate->flags; info->io_type = sstate->io_type; info->iomem_base = sstate->iomem_base; info->iomem_reg_shift = sstate->iomem_reg_shift; info->xmit_fifo_size = sstate->xmit_fifo_size; info->close_delay = sstate->close_delay; info->closing_wait = sstate->closing_wait; info->line = line; info->tqueue.routine = do_softint; info->tqueue.data = info; info->state = sstate; spin_lock_init(&info->irq_spinlock); if (sstate->info) { kfree(info); *ret_info = sstate->info; return 0; } *ret_info = sstate->info = info; return 0; } /* * 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 that on failure, we don't decrement the module use count - the tty * later will call xr_close, which will decrement it for us as long as * tty->driver_data is set non-NULL. --rmk */ static int xr_open(struct tty_struct *tty, struct file * filp) { struct xr_async_struct *info; int retval, line; unsigned long page; TRACE("xr_open"); DEBUG_PRINT(DEBUG_OPEN, DEFAULT_PREFIX" xr_open@%d tty=%p filp=%p private_data=%p tty->driver_data=%p\n", __LINE__, (void *)tty, (void *)filp, filp->private_data, (void *)(tty->driver_data)); MOD_INC_USE_COUNT; DEBUG_PRINT(DEBUG_TRACE, DEFAULT_PREFIX" @%d MOD_INC_USE_COUNT\n", __LINE__); line = MINOR(tty->device) - tty->driver.minor_start; if ((line < 0) || (line >= NR_PORTS)) { MOD_DEC_USE_COUNT; DEBUG_PRINT(DEBUG_TRACE, DEFAULT_PREFIX" @%d MOD_DEC_USE_COUNT\n", __LINE__); return (-ENODEV); } #ifdef PROC_CONTROL if (!port_present[line] && ((filp->f_flags & O_ACCMODE) != O_RDONLY)) { MOD_DEC_USE_COUNT; DEBUG_PRINT(DEBUG_TRACE, DEFAULT_PREFIX" @%d MOD_DEC_USE_COUNT\n", __LINE__); return (-ENODEV); } #endif retval = get_xr_async_struct(line, &info); if (retval) { MOD_DEC_USE_COUNT; DEBUG_PRINT(DEBUG_TRACE, DEFAULT_PREFIX" @%d MOD_DEC_USE_COUNT\n", __LINE__); return (retval); } DEBUG_PRINT(DEBUG_OPEN, DEFAULT_PREFIX" xr_open@%d [%d]rdwr count=%d\n", __LINE__, line, lsi_devices[line].rdwr_count); info->state->count++; tty->driver_data = info; info->tty = tty; // special check to make sure // only one application opens the buffer for read/write or write only if ((filp->f_flags & O_ACCMODE) != O_RDONLY) { if (PORT_TYPE_DEVICE == info->state->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" xr_open@%d multiple opens count=%d\n", __LINE__, lsi_devices[line].rdwr_count); // rdwr_count will be dec. in xr_close return (-EBUSY); } } } if (serial_paranoia_check(info, tty->device, "xr_open")) 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" xr_open@%d get_perms failed\n", __LINE__); return(-EACCES); } #ifdef SERIAL_DEBUG_OPEN printk("xr_open %s%d, count = %d\n", tty->driver.name, info->line, info->state->count); #endif #if (LINUX_VERSION_CODE > 0x20100) info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; #endif /* * This relies on lock_kernel() stuff so wants tidying for 2.5 */ if (!tmp_buf) { page = get_zeroed_page(GFP_KERNEL); if (!page) return (-ENOMEM); if (tmp_buf) free_page(page); else tmp_buf = (unsigned char *) page; } /* * If the port is the middle of closing, bail out now */ 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 return((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); #else return (-EAGAIN); #endif } /* * Start up serial port */ #ifdef NON_DEVICE_ALLOWED if (PORT_TYPE_DEVICE != info->port_type) retval = startup(info); else #endif retval = exser_lsi_startup(info); if (retval) return (retval); retval = block_til_ready(tty, filp, info); if (retval) { #ifdef SERIAL_DEBUG_OPEN printk("xr_open returning after block_til_ready with %d\n", retval); #endif return (retval); } if ((info->state->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { if (tty->driver.subtype == SERIAL_TYPE_NORMAL) *tty->termios = info->state->normal_termios; else *tty->termios = info->state->callout_termios; change_speed(info, 0); } info->session = current->session; info->pgrp = current->pgrp; #ifdef SERIAL_DEBUG_OPEN printk("xr_open %s%d successful...\n", DIRECT_DEV_ROOT, info->line); #endif set_bit (TTY_DO_WRITE_WAKEUP, &tty->flags); return (0); } /* * /proc fs routines.... */ static inline int line_info(char *buf, struct xr_serial_state *state) { struct xr_async_struct *info = state->info, scr_info; char stat_buf[30], control, status; int ret; unsigned long flags; /* * Return zero characters for ports not claimed by driver. */ if (state->type == PORT_UNKNOWN) { return 0; /* ignore unused ports */ } ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", state->line, uart_config[state->type].name, (state->port ? state->port : (long)state->iomem_base), state->irq); /* * Figure out the current RS-232 lines */ if (!info) { info = &scr_info; /* This is just for serial_{in,out} */ info->magic = SERIAL_MAGIC; info->port = state->port; info->flags = state->flags; info->hub6 = state->hub6; info->io_type = state->io_type; info->iomem_base = state->iomem_base; info->iomem_reg_shift = state->iomem_reg_shift; info->quot = 0; info->tty = 0; } save_flags(flags); cli(); status = serial_in(info, UART_MSR); control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR); restore_flags(flags); stat_buf[0] = 0; stat_buf[1] = 0; if (control & UART_MCR_RTS) strcat(stat_buf, "|RTS"); if (status & UART_MSR_CTS) strcat(stat_buf, "|CTS"); if (control & UART_MCR_DTR) strcat(stat_buf, "|DTR"); if (status & UART_MSR_DSR) strcat(stat_buf, "|DSR"); if (status & UART_MSR_DCD) strcat(stat_buf, "|CD"); if (status & UART_MSR_RI) strcat(stat_buf, "|RI"); if (info->quot) { ret += sprintf(buf+ret, " baud:%d", state->baud_base / info->quot); } ret += sprintf(buf+ret, " tx:%d rx:%d", state->icount.tx, state->icount.rx); if (state->icount.frame) ret += sprintf(buf+ret, " fe:%d", state->icount.frame); if (state->icount.parity) ret += sprintf(buf+ret, " pe:%d", state->icount.parity); if (state->icount.brk) ret += sprintf(buf+ret, " brk:%d", state->icount.brk); if (state->icount.overrun) ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); /* * Last thing is the RS-232 status lines */ ret += sprintf(buf+ret, " %s\n", stat_buf+1); return ret; } int xr_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { int i, len = 0, l; off_t begin = 0; len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n", serial_version, LOCAL_VERSTRING, serial_revdate); for (i = 0; i < NR_PORTS && len < 4000; i++) { l = line_info(page + len, &xr_table[i]); len += l; if (len+begin > off+count) goto done; if (len+begin < off) { begin += len; len = 0; } } *eof = 1; done: if (off >= len+begin) return 0; *start = page + (off-begin); return ((count < begin+len-off) ? count : begin+len-off); } /* * --------------------------------------------------------------------- * xr_init() and friends * * xr_init() is called at boot-time to initialize the serial driver. * --------------------------------------------------------------------- */ /* * This routine prints out the appropriate serial driver version * number, and identifies which options were configured into this * driver. */ static char serial_options[] __initdata = #ifdef CONFIG_SERIAL_SHARE_IRQ " SHARE_IRQ" #define SERIAL_OPT #endif #ifdef ENABLE_SERIAL_PCI " SERIAL_PCI" #define SERIAL_OPT #endif #ifdef SERIAL_OPT " enabled\n"; #else " no serial options enabled\n"; #endif #undef SERIAL_OPT static _INLINE_ void show_serial_version(void) { printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name, serial_version, LOCAL_VERSTRING, serial_revdate, serial_options); } /* * This routine detect the IRQ of a serial port by clearing OUT2 when * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at * each time, as long as no other device permanently request the IRQ. * If no IRQ is detected, or multiple IRQ appear, this function returns 0. * The variable "state" and the field "state->port" should not be null. */ static unsigned detect_uart_irq (struct xr_serial_state * state) { int irq; unsigned long irqs; unsigned char save_mcr, save_ier; struct xr_async_struct scr_info; /* serial_{in,out} because HUB6 */ scr_info.magic = SERIAL_MAGIC; scr_info.state = state; scr_info.port = state->port; scr_info.flags = state->flags; scr_info.io_type = state->io_type; scr_info.iomem_base = state->iomem_base; scr_info.iomem_reg_shift = state->iomem_reg_shift; /* forget possible initially masked and pending IRQ */ probe_irq_off(probe_irq_on()); save_mcr = serial_inp(&scr_info, UART_MCR); save_ier = serial_inp(&scr_info, UART_IER); serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); irqs = probe_irq_on(); serial_outp(&scr_info, UART_MCR, 0); udelay (10); if (state->flags & ASYNC_FOURPORT) { serial_outp(&scr_info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); } else { serial_outp(&scr_info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); } serial_outp(&scr_info, UART_IER, 0x0f); /* enable all intrs */ (void)serial_inp(&scr_info, UART_LSR); (void)serial_inp(&scr_info, UART_RX); (void)serial_inp(&scr_info, UART_IIR); (void)serial_inp(&scr_info, UART_MSR); serial_outp(&scr_info, UART_TX, 0xFF); udelay (20); irq = probe_irq_off(irqs); serial_outp(&scr_info, UART_MCR, save_mcr); serial_outp(&scr_info, UART_IER, save_ier); return (irq > 0)? irq : 0; } /* * This routine is called by xr_init() to initialize a specific serial * port. It determines what type of UART chip this serial port is * using: 8250, 16450, 16550, 16550A. The important question is * whether or not this UART is a 16550A or not, since this will * determine whether or not we can use its FIFO features or not. */ static void autoconfig(struct xr_serial_state * state) { unsigned char scratch, scratch2, scratch3; unsigned char save_lcr, save_mcr; struct xr_async_struct *info, scr_info; unsigned long flags; state->type = PORT_UNKNOWN; #ifdef SERIAL_DEBUG_AUTOCONF printk("Testing %s%d (0x%04lx, 0x%04x)...\n", DIRECT_DEV_ROOT, state->line, state->port, (unsigned) state->iomem_base); #endif if (!CONFIGURED_SERIAL_PORT(state)) return; info = &scr_info; /* This is just for serial_{in,out} */ info->magic = SERIAL_MAGIC; info->state = state; info->port = state->port; info->flags = state->flags; info->io_type = state->io_type; info->iomem_base = state->iomem_base; info->iomem_reg_shift = state->iomem_reg_shift; info->irq_spinlock= (spinlock_t) SPIN_LOCK_UNLOCKED; save_flags(flags); cli(); if (!(state->flags & ASYNC_BUGGY_UART) && !state->iomem_base) { /* * Do a simple existence test first; if we fail this, * there's no point trying anything else. * * 0x80 is used as a nonsense port to prevent against * false positives due to ISA bus float. The * assumption is that 0x80 is a non-existent port; * which should be safe since include/asm/io.h also * makes this assumption. */ scratch = serial_inp(info, UART_IER); serial_outp(info, UART_IER, 0); scratch2 = serial_inp(info, UART_IER); serial_outp(info, UART_IER, 0x0F); scratch3 = serial_inp(info, UART_IER); serial_outp(info, UART_IER, scratch); if (scratch2 || scratch3 != 0x0F) { #ifdef SERIAL_DEBUG_AUTOCONF printk(MODULE_NAME ": %s%d: simple autoconfig failed " "(%02x, %02x)\n", DIRECT_DEV_ROOT, state->line, scratch2, scratch3); #endif restore_flags(flags); return; /* We failed; there's nothing here */ } } save_mcr = serial_in(info, UART_MCR); save_lcr = serial_in(info, UART_LCR); serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */ serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */ serial_outp(info, UART_LCR, 0); serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); scratch = serial_in(info, UART_IIR) >> 6; switch (scratch) { case 0: state->type = PORT_16450; break; case 1: state->type = PORT_UNKNOWN; break; case 2: state->type = PORT_16550; break; case 3: state->type = PORT_16550A; break; } // we can override all the above by simply checking the vendor id and device id // (beacuse) BTW this function won't be called for any other chips beacuse of PCI PNP // still what the heck? if (state->type == PORT_16550A) { state->type = PORT_XR16L788; // This has to be our device XR17C15X when it comes here } else { state->type = PORT_XR16L788; // This has to be our device XR17C15X when it comes here // do we have any other option? } state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; if (state->type == PORT_UNKNOWN) { restore_flags(flags); return; } if (info->port) { request_region(info->port,16,MODULE_NAME "(auto)"); } /* * Reset the UART. */ serial_outp(info, UART_MCR, save_mcr); serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); serial_outp(info, UART_FCR, 0); (void)serial_in(info, UART_RX); serial_outp(info, UART_IER, 0); restore_flags(flags); } int register_serial(struct serial_struct *req); void unregister_serial(int line); #if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) static void __devinit printk_pnp_dev_id(unsigned short vendor, unsigned short device) { printk("%c%c%c%x%x%x%x", 'A' + ((vendor >> 2) & 0x3f) - 1, 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, 'A' + ((vendor >> 8) & 0x1f) - 1, (device >> 4) & 0x0f, device & 0x0f, (device >> 12) & 0x0f, (device >> 8) & 0x0f); } static _INLINE_ int get_pci_port(struct pci_dev *dev, struct pciboard *board, struct serial_struct *req, int idx) { unsigned long port; int base_idx; int offset; base_idx = 0; // 17C15X: BAR0 offset = idx * board->first_uart_offset; // 17C15X: offset = portnum*UART space(0x200) port = pci_resource_start(dev, base_idx) + offset; req->io_type = SERIAL_IO_MEM; req->iomem_base = ioremap(port, board->uart_offset); req->iomem_reg_shift = board->reg_shift; // req->port = idx; req->port = 0; // added by RKR 04/01/2003 return 0; } static _INLINE_ int get_pci_irq(struct pci_dev *dev, struct pciboard *board, int idx) { int base_idx; if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) return dev->irq; base_idx = SPCI_FL_GET_IRQBASE(board->flags); if (board->flags & SPCI_FL_IRQ_TABLE) base_idx += idx; return PCI_IRQ_RESOURCE(dev, base_idx); } /* * Common enabler code shared by both PCI and ISAPNP probes */ static void __devinit start_pci_pnp_board(struct pci_dev *dev, struct pciboard *board) { int k, line; struct serial_struct serial_req; int base_baud; if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) { printk(MODULE_NAME ": PNP device '"); printk_pnp_dev_id(dev->vendor, dev->device); printk("' prepare failed\n"); return; } if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) { printk(MODULE_NAME ": PNP device '"); printk_pnp_dev_id(dev->vendor, dev->device); printk("' activate failed\n"); return; } /* * Run the initialization function, if any */ if (board->init_fn && ((board->init_fn)(dev, board, 1) != 0)) return; /* * Register the serial board in the array if we need to * shutdown the board on a module unload or card removal */ if (DEACTIVATE_FUNC(dev) || board->init_fn) { for (k=0; k < NR_PCI_BOARDS; k++) if (serial_pci_board[k].dev == 0) break; if (k >= NR_PCI_BOARDS) return; serial_pci_board[k].board = *board; serial_pci_board[k].dev = dev; } base_baud = board->base_baud; if (!base_baud) base_baud = BASE_BAUD; memset(&serial_req, 0, sizeof(serial_req)); for (k=0; k < board->num_ports; k++) { serial_req.irq = get_pci_irq(dev, board, k); if (get_pci_port(dev, board, &serial_req, k)) break; serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; #ifdef SERIAL_DEBUG_PCI printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", serial_req.port, serial_req.irq, serial_req.io_type); #endif line = register_serial(&serial_req); if (line < 0) break; xr_table[line].baud_base = base_baud; xr_table[line].dev = dev; } } #endif /* ENABLE_SERIAL_PCI */ #ifdef ENABLE_SERIAL_PCI /* * SIIG serial cards have an PCI interface chip which also controls * the UART clocking frequency. Each UART can be clocked independently * (except cards equiped with 4 UARTs) and initial clocking settings * are stored in the EEPROM chip. It can cause problems because this * version of serial driver doesn't support differently clocked UART's * on single PCI card. To prevent this, initialization functions set * high frequency clocking for all UART's on given card. It is safe (I * hope) because it doesn't touch EEPROM settings to prevent conflicts * with other OSes (like M$ DOS). * * SIIG support added by Andrey Panin , 10/1999 * * There is two family of SIIG serial cards with different PCI * interface chip and different configuration methods: * - 10x cards have control registers in IO and/or memory space; * - 20x cards have control registers in standard PCI configuration space. * * SIIG initialization functions exported for use by parport_serial.c module. */ static struct pciboard pci_boards[] __devinitdata = { /* * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, * Offset to get to next UART's registers, * Register shift to use for memory-mapped I/O, * Initialization function, first UART offset */ { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ //XR17C158 0x200, 0, NULL, 0x200 }, { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ //XR17C154 0x200, 0, NULL, 0x200 }, { SPCI_FL_BASE0, 2, 921600, /* IOMEM */ //XR17C152 0x200, 0, NULL, 0x200 }, }; static int __devinit serial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { struct pciboard *board; int rc; board = &pci_boards[ent->driver_data]; rc = pci_enable_device(dev); if (rc) return rc; start_pci_pnp_board(dev, board); return 0; } static void __devexit serial_remove_one(struct pci_dev *dev) { int i; /* * Iterate through all of the ports finding those that belong * to this PCI device. */ for(i = 0; i < NR_PORTS; i++) { if (xr_table[i].dev != dev) continue; unregister_serial(i); xr_table[i].dev = 0; } /* * Now execute any board-specific shutdown procedure */ for (i=0; i < NR_PCI_BOARDS; i++) { struct pciboard_inst *brd = &serial_pci_board[i]; if (serial_pci_board[i].dev != dev) continue; if (brd->board.init_fn) (brd->board.init_fn)(brd->dev, &brd->board, 0); if (DEACTIVATE_FUNC(brd->dev)) (DEACTIVATE_FUNC(brd->dev))(brd->dev); serial_pci_board[i].dev = 0; } } #define PCI_VENDOR_ID_EXAR 0x13A8 #define PCI_DEVICE_ID_C158 0x158 #define PCI_DEVICE_ID_C154 0x154 #define PCI_DEVICE_ID_C152 0x152 enum { EXSER_BOARD_C158_PCI = 0, EXSER_BOARD_C154_PCI, EXSER_BOARD_C152_PCI }; static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_C158, PCI_ANY_ID, PCI_ANY_ID, 0, 0, EXSER_BOARD_C158_PCI}, { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_C154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, EXSER_BOARD_C154_PCI}, { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_C152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, EXSER_BOARD_C152_PCI}, { 0, } }; MODULE_DEVICE_TABLE(pci, serial_pci_tbl); static struct pci_driver serial_pci_driver = { name: "exarserial", probe: serial_init_one, remove: serial_remove_one, id_table: serial_pci_tbl, }; /* * Query PCI space for known serial boards * If found, add them to the PCI device space in xr_table[] * * Accept a maximum of eight boards * */ static void __devinit probe_serial_pci(void) { #ifdef SERIAL_DEBUG_PCI printk("Entered probe_serial_pci()\n"); #endif /* Register call PCI serial devices. Null out * the driver name upon failure, as a signal * not to attempt to unregister the driver later */ if (pci_module_init (&serial_pci_driver) != 0) serial_pci_driver.name = ""; #ifdef SERIAL_DEBUG_PCI printk("Leaving probe_serial_pci() (probe finished)\n"); #endif return; } #endif /* ENABLE_SERIAL_PCI */ /* * The serial driver boot-time initialization code! */ static int __init xr_init(void) { int i, b, m, retval; struct xr_serial_state * state; struct uart_control_struct hwconf; #ifdef SERIAL_TIMER init_timer(&serial_timer); serial_timer.function = xr_timer; mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); #endif for (i = 0; i < NR_IRQS; i++) { IRQ_ports[i] = 0; #ifdef SERIAL_TIMER IRQ_timeout[i] = 0; #endif } show_serial_version(); memset (current_dtedce_settings, dtedce_all_off, sizeof(current_dtedce_settings)); for (i=0; i 0x20100) serial_driver.driver_name = "exar_serial"; #endif serial_driver.name = DIRECT_DEV_ROOT; serial_driver.major = tty_major; //XR_TTY_MAJOR; serial_driver.minor_start = 0; serial_driver.name_base = SERIAL_DEV_OFFSET; serial_driver.num = NR_PORTS; serial_driver.type = TTY_DRIVER_TYPE_SERIAL; serial_driver.subtype = SERIAL_TYPE_NORMAL; serial_driver.init_termios = tty_std_termios; serial_driver.init_termios.c_iflag = IXON|IXOFF; serial_driver.init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; serial_driver.init_termios.c_lflag = 0; // -isig|-icanon|-xcase|-echo|-echoe|-echok|-echonl| // -noflsh|-tostop|-echoctl|-echoprt|-echoke|-iexten serial_driver.flags = TTY_DRIVER_REAL_RAW ; serial_driver.refcount = &serial_refcount; serial_driver.table = serial_table; serial_driver.termios = serial_termios; serial_driver.termios_locked = serial_termios_locked; serial_driver.open = xr_open; serial_driver.close = xr_close; serial_driver.write = xr_write; serial_driver.put_char = xr_put_char; serial_driver.flush_chars = xr_flush_chars; serial_driver.write_room = xr_write_room; serial_driver.chars_in_buffer = xr_chars_in_buffer; serial_driver.flush_buffer = xr_flush_buffer; serial_driver.ioctl = xr_ioctl; serial_driver.throttle = xr_throttle; serial_driver.unthrottle = xr_unthrottle; serial_driver.set_termios = xr_set_termios; serial_driver.stop = xr_stop; serial_driver.start = xr_start; serial_driver.hangup = xr_hangup; #if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ serial_driver.break_ctl = xr_break; #endif #if (LINUX_VERSION_CODE >= 131343) serial_driver.send_xchar = xr_send_xchar; serial_driver.wait_until_sent = xr_wait_until_sent; serial_driver.read_proc = xr_read_proc; #endif /* * The callout device is just like normal device except for * major number and the subtype code. */ callout_driver = serial_driver; callout_driver.name = CALLOUT_DEV_ROOT; callout_driver.major = callout_major; // XR_TTYAUX_MAJOR; callout_driver.subtype = SERIAL_TYPE_CALLOUT; #if (LINUX_VERSION_CODE >= 131343) callout_driver.read_proc = 0; callout_driver.proc_entry = 0; #endif if (tty_register_driver(&serial_driver)) panic("Couldn't register exar's serial driv