2005-04-17 02:20:36 +04:00
/*
* mxser . c - - MOXA Smartio / Industio family multiport serial driver .
*
2008-02-07 11:16:51 +03:00
* Copyright ( C ) 1999 - 2006 Moxa Technologies ( support @ moxa . com ) .
* Copyright ( C ) 2006 - 2008 Jiri Slaby < jirislaby @ gmail . com >
2005-04-17 02:20:36 +04:00
*
2008-02-07 11:16:46 +03:00
* This code is loosely based on the 1.8 moxa driver which is based on
* Linux serial driver , written by Linus Torvalds , Theodore T ' so and
* others .
2005-04-17 02:20:36 +04:00
*
* 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
2006-06-25 16:47:54 +04:00
* ( at your option ) any later version .
2005-04-17 02:20:36 +04:00
*
* Fed through a cleanup , indent and remove of non 2.6 code by Alan Cox
* < alan @ redhat . com > . The original 1.8 code is available on www . moxa . com .
* - Fixed x86_64 cleanness
* - Fixed sleep with spinlock held in mxser_send_break
*/
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/signal.h>
# include <linux/sched.h>
# include <linux/timer.h>
# include <linux/interrupt.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <linux/serial.h>
# include <linux/serial_reg.h>
# include <linux/major.h>
# include <linux/string.h>
# include <linux/fcntl.h>
# include <linux/ptrace.h>
# include <linux/gfp.h>
# include <linux/ioport.h>
# include <linux/mm.h>
# include <linux/delay.h>
# include <linux/pci.h>
2007-10-19 10:40:25 +04:00
# include <linux/bitops.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/uaccess.h>
# include "mxser.h"
2008-02-07 11:16:51 +03:00
# define MXSER_VERSION "2.0.3" /* 1.11 */
2005-04-17 02:20:36 +04:00
# define MXSERMAJOR 174
# define MXSERCUMAJOR 175
# define MXSER_BOARDS 4 /* Max. boards */
# define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */
2008-02-07 11:16:46 +03:00
# define MXSER_PORTS (MXSER_BOARDS * MXSER_PORTS_PER_BOARD)
# define MXSER_ISR_PASS_LIMIT 100
2005-04-17 02:20:36 +04:00
# define MXSER_ERR_IOADDR -1
# define MXSER_ERR_IRQ -2
# define MXSER_ERR_IRQ_CONFLIT -3
# define MXSER_ERR_VECTOR -4
2008-02-07 11:16:46 +03:00
/*CheckIsMoxaMust return value*/
# define MOXA_OTHER_UART 0x00
# define MOXA_MUST_MU150_HWID 0x01
# define MOXA_MUST_MU860_HWID 0x02
2005-04-17 02:20:36 +04:00
# define WAKEUP_CHARS 256
# define UART_MCR_AFE 0x20
# define UART_LSR_SPECIAL 0x1E
2008-02-07 11:16:46 +03:00
# define PCI_DEVICE_ID_CB108 0x1080
# define PCI_DEVICE_ID_CB114 0x1142
2008-02-07 11:16:51 +03:00
# define PCI_DEVICE_ID_CP114UL 0x1143
2008-02-07 11:16:46 +03:00
# define PCI_DEVICE_ID_CB134I 0x1341
# define PCI_DEVICE_ID_CP138U 0x1380
# define PCI_DEVICE_ID_POS104UL 0x1044
2005-04-17 02:20:36 +04:00
# define C168_ASIC_ID 1
# define C104_ASIC_ID 2
# define C102_ASIC_ID 0xB
# define CI132_ASIC_ID 4
# define CI134_ASIC_ID 3
# define CI104J_ASIC_ID 5
2008-02-07 11:16:46 +03:00
# define MXSER_HIGHBAUD 1
# define MXSER_HAS2 2
2005-04-17 02:20:36 +04:00
2006-06-25 16:47:54 +04:00
/* This is only for PCI */
2008-02-07 11:16:46 +03:00
static const struct {
2005-04-17 02:20:36 +04:00
int type ;
int tx_fifo ;
int rx_fifo ;
int xmit_fifo_size ;
int rx_high_water ;
int rx_trigger ;
int rx_low_water ;
long max_baud ;
2008-02-07 11:16:46 +03:00
} Gpci_uart_info [ ] = {
2005-04-17 02:20:36 +04:00
{ MOXA_OTHER_UART , 16 , 16 , 16 , 14 , 14 , 1 , 921600L } ,
{ MOXA_MUST_MU150_HWID , 64 , 64 , 64 , 48 , 48 , 16 , 230400L } ,
{ MOXA_MUST_MU860_HWID , 128 , 128 , 128 , 96 , 96 , 32 , 921600L }
} ;
2008-02-07 11:16:46 +03:00
# define UART_INFO_NUM ARRAY_SIZE(Gpci_uart_info)
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
struct mxser_cardinfo {
char * name ;
unsigned int nports ;
unsigned int flags ;
} ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
static const struct mxser_cardinfo mxser_cards [ ] = {
/* 0*/ { " C168 series " , 8 , } ,
{ " C104 series " , 4 , } ,
{ " CI-104J series " , 4 , } ,
{ " C168H/PCI series " , 8 , } ,
{ " C104H/PCI series " , 4 , } ,
/* 5*/ { " C102 series " , 4 , MXSER_HAS2 } , /* C102-ISA */
{ " CI-132 series " , 4 , MXSER_HAS2 } ,
{ " CI-134 series " , 4 , } ,
{ " CP-132 series " , 2 , } ,
{ " CP-114 series " , 4 , } ,
/*10*/ { " CT-114 series " , 4 , } ,
{ " CP-102 series " , 2 , MXSER_HIGHBAUD } ,
{ " CP-104U series " , 4 , } ,
{ " CP-168U series " , 8 , } ,
{ " CP-132U series " , 2 , } ,
/*15*/ { " CP-134U series " , 4 , } ,
{ " CP-104JU series " , 4 , } ,
{ " Moxa UC7000 Serial " , 8 , } , /* RC7000 */
{ " CP-118U series " , 8 , } ,
{ " CP-102UL series " , 2 , } ,
/*20*/ { " CP-102U series " , 2 , } ,
{ " CP-118EL series " , 8 , } ,
{ " CP-168EL series " , 8 , } ,
{ " CP-104EL series " , 4 , } ,
{ " CB-108 series " , 8 , } ,
/*25*/ { " CB-114 series " , 4 , } ,
{ " CB-134I series " , 4 , } ,
{ " CP-138U series " , 8 , } ,
2008-02-07 11:16:51 +03:00
{ " POS-104UL series " , 4 , } ,
{ " CP-114UL series " , 4 , }
2008-02-07 11:16:46 +03:00
} ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/* driver_data correspond to the lines in the structure above
see also ISA probe function before you change something */
2005-04-17 02:20:36 +04:00
static struct pci_device_id mxser_pcibrds [ ] = {
2008-02-07 11:16:46 +03:00
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_C168 ) , . driver_data = 3 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_C104 ) , . driver_data = 4 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP132 ) , . driver_data = 8 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP114 ) , . driver_data = 9 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CT114 ) , . driver_data = 10 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP102 ) , . driver_data = 11 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP104U ) , . driver_data = 12 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP168U ) , . driver_data = 13 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP132U ) , . driver_data = 14 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP134U ) , . driver_data = 15 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP104JU ) , . driver_data = 16 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_RC7000 ) , . driver_data = 17 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP118U ) , . driver_data = 18 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP102UL ) , . driver_data = 19 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP102U ) , . driver_data = 20 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP118EL ) , . driver_data = 21 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP168EL ) , . driver_data = 22 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_MOXA_CP104EL ) , . driver_data = 23 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_CB108 ) , . driver_data = 24 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_CB114 ) , . driver_data = 25 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_CB134I ) , . driver_data = 26 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_CP138U ) , . driver_data = 27 } ,
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_POS104UL ) , . driver_data = 28 } ,
2008-02-07 11:16:51 +03:00
{ PCI_VDEVICE ( MOXA , PCI_DEVICE_ID_CP114UL ) , . driver_data = 29 } ,
2008-02-07 11:16:46 +03:00
{ }
2005-04-17 02:20:36 +04:00
} ;
MODULE_DEVICE_TABLE ( pci , mxser_pcibrds ) ;
static int ioaddr [ MXSER_BOARDS ] = { 0 , 0 , 0 , 0 } ;
static int ttymajor = MXSERMAJOR ;
/* Variables for insmod */
MODULE_AUTHOR ( " Casper Yang " ) ;
MODULE_DESCRIPTION ( " MOXA Smartio/Industio Family Multiport Board Device Driver " ) ;
2006-03-25 14:07:05 +03:00
module_param_array ( ioaddr , int , NULL , 0 ) ;
module_param ( ttymajor , int , 0 ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
struct mxser_log {
int tick ;
unsigned long rxcnt [ MXSER_PORTS ] ;
unsigned long txcnt [ MXSER_PORTS ] ;
} ;
struct mxser_mon {
unsigned long rxcnt ;
unsigned long txcnt ;
unsigned long up_rxcnt ;
unsigned long up_txcnt ;
int modem_status ;
unsigned char hold_reason ;
} ;
struct mxser_mon_ext {
unsigned long rx_cnt [ 32 ] ;
unsigned long tx_cnt [ 32 ] ;
unsigned long up_rxcnt [ 32 ] ;
unsigned long up_txcnt [ 32 ] ;
int modem_status [ 32 ] ;
long baudrate [ 32 ] ;
int databits [ 32 ] ;
int stopbits [ 32 ] ;
int parity [ 32 ] ;
int flowctrl [ 32 ] ;
int fifo [ 32 ] ;
int iftype [ 32 ] ;
} ;
2006-06-25 16:47:54 +04:00
2008-02-07 11:16:46 +03:00
struct mxser_board ;
struct mxser_port {
struct mxser_board * board ;
struct tty_struct * tty ;
unsigned long ioaddr ;
unsigned long opmode_ioaddr ;
int max_baud ;
2005-04-17 02:20:36 +04:00
int rx_high_water ;
int rx_trigger ; /* Rx fifo trigger level */
int rx_low_water ;
int baud_base ; /* max. speed */
int type ; /* UART type */
2008-02-07 11:16:46 +03:00
int flags ; /* defined in tty.h */
2005-04-17 02:20:36 +04:00
int x_char ; /* xon/xoff character */
int IER ; /* Interrupt Enable Register */
int MCR ; /* Modem control register */
2008-02-07 11:16:46 +03:00
unsigned char stop_rx ;
unsigned char ldisc_stop_rx ;
int custom_divisor ;
int close_delay ;
unsigned short closing_wait ;
unsigned char err_shadow ;
2005-04-17 02:20:36 +04:00
unsigned long event ;
2008-02-07 11:16:46 +03:00
2005-04-17 02:20:36 +04:00
int count ; /* # of fd on device */
int blocked_open ; /* # of blocked opens */
2008-02-07 11:16:46 +03:00
struct async_icount icount ; /* kernel counters for 4 input interrupts */
int timeout ;
int read_status_mask ;
int ignore_status_mask ;
int xmit_fifo_size ;
2005-04-17 02:20:36 +04:00
unsigned char * xmit_buf ;
int xmit_head ;
int xmit_tail ;
int xmit_cnt ;
2008-02-07 11:16:46 +03:00
2006-12-08 13:38:45 +03:00
struct ktermios normal_termios ;
2008-02-07 11:16:46 +03:00
2005-04-17 02:20:36 +04:00
struct mxser_mon mon_data ;
2008-02-07 11:16:46 +03:00
2005-04-17 02:20:36 +04:00
spinlock_t slock ;
2008-02-07 11:16:46 +03:00
wait_queue_head_t open_wait ;
wait_queue_head_t delta_msr_wait ;
} ;
struct mxser_board {
unsigned int idx ;
int irq ;
const struct mxser_cardinfo * info ;
unsigned long vector ;
unsigned long vector_mask ;
int chip_flag ;
int uart_type ;
struct mxser_port ports [ MXSER_PORTS_PER_BOARD ] ;
2005-04-17 02:20:36 +04:00
} ;
struct mxser_mstatus {
tcflag_t cflag ;
int cts ;
int dsr ;
int ri ;
int dcd ;
} ;
static struct mxser_mstatus GMStatus [ MXSER_PORTS ] ;
static int mxserBoardCAP [ MXSER_BOARDS ] = {
0 , 0 , 0 , 0
2006-06-25 16:47:54 +04:00
/* 0x180, 0x280, 0x200, 0x320 */
2005-04-17 02:20:36 +04:00
} ;
2008-02-07 11:16:46 +03:00
static struct mxser_board mxser_boards [ MXSER_BOARDS ] ;
2005-04-17 02:20:36 +04:00
static struct tty_driver * mxvar_sdriver ;
static struct mxser_log mxvar_log ;
static int mxvar_diagflag ;
static unsigned char mxser_msr [ MXSER_PORTS + 1 ] ;
static struct mxser_mon_ext mon_data_ext ;
static int mxser_set_baud_method [ MXSER_PORTS + 1 ] ;
2007-10-18 14:06:03 +04:00
# ifdef CONFIG_PCI
2008-02-07 11:16:46 +03:00
static int __devinit CheckIsMoxaMust ( unsigned long io )
2005-04-17 02:20:36 +04:00
{
u8 oldmcr , hwid ;
int i ;
outb ( 0 , io + UART_LCR ) ;
DISABLE_MOXA_MUST_ENCHANCE_MODE ( io ) ;
oldmcr = inb ( io + UART_MCR ) ;
outb ( 0 , io + UART_MCR ) ;
SET_MOXA_MUST_XON1_VALUE ( io , 0x11 ) ;
if ( ( hwid = inb ( io + UART_MCR ) ) ! = 0 ) {
outb ( oldmcr , io + UART_MCR ) ;
2006-06-25 16:47:54 +04:00
return MOXA_OTHER_UART ;
2005-04-17 02:20:36 +04:00
}
GET_MOXA_MUST_HARDWARE_ID ( io , & hwid ) ;
2008-02-07 11:16:46 +03:00
for ( i = 1 ; i < UART_INFO_NUM ; i + + ) { /* 0 = OTHER_UART */
if ( hwid = = Gpci_uart_info [ i ] . type )
2006-06-25 16:47:54 +04:00
return ( int ) hwid ;
2005-04-17 02:20:36 +04:00
}
return MOXA_OTHER_UART ;
}
2007-10-18 14:06:03 +04:00
# endif
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
static void process_txrx_fifo ( struct mxser_port * info )
2005-04-17 02:20:36 +04:00
{
int i ;
if ( ( info - > type = = PORT_16450 ) | | ( info - > type = = PORT_8250 ) ) {
info - > rx_trigger = 1 ;
info - > rx_high_water = 1 ;
info - > rx_low_water = 1 ;
info - > xmit_fifo_size = 1 ;
2008-02-07 11:16:46 +03:00
} else
for ( i = 0 ; i < UART_INFO_NUM ; i + + )
if ( info - > board - > chip_flag = = Gpci_uart_info [ i ] . type ) {
2005-04-17 02:20:36 +04:00
info - > rx_trigger = Gpci_uart_info [ i ] . rx_trigger ;
info - > rx_low_water = Gpci_uart_info [ i ] . rx_low_water ;
info - > rx_high_water = Gpci_uart_info [ i ] . rx_high_water ;
info - > xmit_fifo_size = Gpci_uart_info [ i ] . xmit_fifo_size ;
break ;
}
}
2008-02-07 11:16:46 +03:00
static unsigned char mxser_get_msr ( int baseaddr , int mode , int port )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
unsigned char status = 0 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
status = inb ( baseaddr + UART_MSR ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
mxser_msr [ port ] & = 0x0F ;
mxser_msr [ port ] | = status ;
status = mxser_msr [ port ] ;
if ( mode )
mxser_msr [ port ] = 0 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
return status ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
static int mxser_block_til_ready ( struct tty_struct * tty , struct file * filp ,
struct mxser_port * port )
{
DECLARE_WAITQUEUE ( wait , current ) ;
int retval ;
int do_clocal = 0 ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/*
* 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 ) | |
test_bit ( TTY_IO_ERROR , & tty - > flags ) ) {
port - > flags | = ASYNC_NORMAL_ACTIVE ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( tty - > termios - > c_cflag & CLOCAL )
do_clocal = 1 ;
2005-04-17 02:20:36 +04:00
/*
2008-02-07 11:16:46 +03:00
* 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 , port - > count is dropped by one , so that
* mxser_close ( ) knows when to free things . We restore it upon
* exit , either normal or abnormal .
2005-04-17 02:20:36 +04:00
*/
2008-02-07 11:16:46 +03:00
retval = 0 ;
add_wait_queue ( & port - > open_wait , & wait ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
spin_lock_irqsave ( & port - > slock , flags ) ;
if ( ! tty_hung_up_p ( filp ) )
port - > count - - ;
spin_unlock_irqrestore ( & port - > slock , flags ) ;
port - > blocked_open + + ;
while ( 1 ) {
spin_lock_irqsave ( & port - > slock , flags ) ;
outb ( inb ( port - > ioaddr + UART_MCR ) |
UART_MCR_DTR | UART_MCR_RTS , port - > ioaddr + UART_MCR ) ;
spin_unlock_irqrestore ( & port - > slock , flags ) ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( tty_hung_up_p ( filp ) | | ! ( port - > flags & ASYNC_INITIALIZED ) ) {
if ( port - > flags & ASYNC_HUP_NOTIFY )
retval = - EAGAIN ;
else
retval = - ERESTARTSYS ;
break ;
}
if ( ! ( port - > flags & ASYNC_CLOSING ) & &
( do_clocal | |
( inb ( port - > ioaddr + UART_MSR ) & UART_MSR_DCD ) ) )
break ;
if ( signal_pending ( current ) ) {
retval = - ERESTARTSYS ;
break ;
}
schedule ( ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
set_current_state ( TASK_RUNNING ) ;
remove_wait_queue ( & port - > open_wait , & wait ) ;
if ( ! tty_hung_up_p ( filp ) )
port - > count + + ;
port - > blocked_open - - ;
if ( retval )
2005-04-17 02:20:36 +04:00
return retval ;
2008-02-07 11:16:46 +03:00
port - > flags | = ASYNC_NORMAL_ACTIVE ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-02-07 11:16:46 +03:00
static int mxser_set_baud ( struct mxser_port * info , long newspd )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
int quot = 0 , baud ;
unsigned char cval ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( ! info - > tty | | ! info - > tty - > termios )
return - 1 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( ! ( info - > ioaddr ) )
return - 1 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( newspd > info - > max_baud )
return - 1 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( newspd = = 134 ) {
quot = 2 * info - > baud_base / 269 ;
tty_encode_baud_rate ( info - > tty , 134 , 134 ) ;
} else if ( newspd ) {
quot = info - > baud_base / newspd ;
if ( quot = = 0 )
quot = 1 ;
baud = info - > baud_base / quot ;
tty_encode_baud_rate ( info - > tty , baud , baud ) ;
} else {
quot = 0 ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
info - > timeout = ( ( info - > xmit_fifo_size * HZ * 10 * quot ) / info - > baud_base ) ;
info - > timeout + = HZ / 50 ; /* Add .02 seconds of slop */
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( quot ) {
info - > MCR | = UART_MCR_DTR ;
outb ( info - > MCR , info - > ioaddr + UART_MCR ) ;
} else {
info - > MCR & = ~ UART_MCR_DTR ;
outb ( info - > MCR , info - > ioaddr + UART_MCR ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
cval = inb ( info - > ioaddr + UART_LCR ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
outb ( cval | UART_LCR_DLAB , info - > ioaddr + UART_LCR ) ; /* set DLAB */
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
outb ( quot & 0xff , info - > ioaddr + UART_DLL ) ; /* LS of divisor */
outb ( quot > > 8 , info - > ioaddr + UART_DLM ) ; /* MS of divisor */
outb ( cval , info - > ioaddr + UART_LCR ) ; /* reset DLAB */
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
# ifdef BOTHER
if ( C_BAUD ( info - > tty ) = = BOTHER ) {
quot = info - > baud_base % newspd ;
quot * = 8 ;
if ( quot % newspd > newspd / 2 ) {
quot / = newspd ;
quot + + ;
} else
quot / = newspd ;
SET_MOXA_MUST_ENUM_VALUE ( info - > ioaddr , quot ) ;
} else
# endif
SET_MOXA_MUST_ENUM_VALUE ( info - > ioaddr , 0 ) ;
2005-04-17 02:20:36 +04:00
2006-06-25 16:47:54 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
/*
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port .
*/
static int mxser_change_speed ( struct mxser_port * info ,
struct ktermios * old_termios )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
unsigned cflag , cval , fcr ;
int ret = 0 ;
unsigned char status ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( ! info - > tty | | ! info - > tty - > termios )
return ret ;
cflag = info - > tty - > termios - > c_cflag ;
if ( ! ( info - > ioaddr ) )
return ret ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( mxser_set_baud_method [ info - > tty - > index ] = = 0 )
mxser_set_baud ( info , tty_get_baud_rate ( info - > tty ) ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/* byte size and parity */
switch ( cflag & CSIZE ) {
case CS5 :
cval = 0x00 ;
break ;
case CS6 :
cval = 0x01 ;
break ;
case CS7 :
cval = 0x02 ;
break ;
case CS8 :
cval = 0x03 ;
break ;
default :
cval = 0x00 ;
break ; /* too keep GCC shut... */
}
if ( cflag & CSTOPB )
cval | = 0x04 ;
if ( cflag & PARENB )
cval | = UART_LCR_PARITY ;
if ( ! ( cflag & PARODD ) )
cval | = UART_LCR_EPAR ;
if ( cflag & CMSPAR )
cval | = UART_LCR_SPAR ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( ( info - > type = = PORT_8250 ) | | ( info - > type = = PORT_16450 ) ) {
if ( info - > board - > chip_flag ) {
fcr = UART_FCR_ENABLE_FIFO ;
fcr | = MOXA_MUST_FCR_GDA_MODE_ENABLE ;
SET_MOXA_MUST_FIFO_VALUE ( info ) ;
} else
fcr = 0 ;
} else {
fcr = UART_FCR_ENABLE_FIFO ;
if ( info - > board - > chip_flag ) {
fcr | = MOXA_MUST_FCR_GDA_MODE_ENABLE ;
SET_MOXA_MUST_FIFO_VALUE ( info ) ;
} else {
switch ( info - > rx_trigger ) {
case 1 :
fcr | = UART_FCR_TRIGGER_1 ;
break ;
case 4 :
fcr | = UART_FCR_TRIGGER_4 ;
break ;
case 8 :
fcr | = UART_FCR_TRIGGER_8 ;
break ;
default :
fcr | = UART_FCR_TRIGGER_14 ;
break ;
}
2005-04-17 02:20:36 +04:00
}
}
2008-02-07 11:16:46 +03:00
/* CTS flow control flag and modem status interrupts */
info - > IER & = ~ UART_IER_MSI ;
info - > MCR & = ~ UART_MCR_AFE ;
if ( cflag & CRTSCTS ) {
info - > flags | = ASYNC_CTS_FLOW ;
info - > IER | = UART_IER_MSI ;
if ( ( info - > type = = PORT_16550A ) | | ( info - > board - > chip_flag ) ) {
info - > MCR | = UART_MCR_AFE ;
} else {
status = inb ( info - > ioaddr + UART_MSR ) ;
if ( info - > tty - > hw_stopped ) {
if ( status & UART_MSR_CTS ) {
info - > tty - > hw_stopped = 0 ;
if ( info - > type ! = PORT_16550A & &
! info - > board - > chip_flag ) {
outb ( info - > IER & ~ UART_IER_THRI ,
info - > ioaddr +
UART_IER ) ;
info - > IER | = UART_IER_THRI ;
outb ( info - > IER , info - > ioaddr +
UART_IER ) ;
}
tty_wakeup ( info - > tty ) ;
}
} else {
if ( ! ( status & UART_MSR_CTS ) ) {
info - > tty - > hw_stopped = 1 ;
if ( ( info - > type ! = PORT_16550A ) & &
( ! info - > board - > chip_flag ) ) {
info - > IER & = ~ UART_IER_THRI ;
outb ( info - > IER , info - > ioaddr +
UART_IER ) ;
}
}
}
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
} else {
info - > flags & = ~ ASYNC_CTS_FLOW ;
}
outb ( info - > MCR , info - > ioaddr + UART_MCR ) ;
if ( cflag & CLOCAL ) {
info - > flags & = ~ ASYNC_CHECK_CD ;
} else {
info - > flags | = ASYNC_CHECK_CD ;
info - > IER | = UART_IER_MSI ;
}
outb ( info - > IER , info - > ioaddr + UART_IER ) ;
/*
* Set up parity check flag
*/
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 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
info - > ignore_status_mask = 0 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( I_IGNBRK ( info - > tty ) ) {
info - > ignore_status_mask | = UART_LSR_BI ;
info - > read_status_mask | = UART_LSR_BI ;
2006-06-25 16:47:54 +04:00
/*
2008-02-07 11:16:46 +03:00
* If we ' re ignore parity and break indicators , ignore
* overruns too . ( For real raw support ) .
2006-06-25 16:47:54 +04:00
*/
2008-02-07 11:16:46 +03:00
if ( I_IGNPAR ( info - > tty ) ) {
info - > ignore_status_mask | =
UART_LSR_OE |
UART_LSR_PE |
UART_LSR_FE ;
info - > read_status_mask | =
UART_LSR_OE |
UART_LSR_PE |
UART_LSR_FE ;
}
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
if ( info - > board - > chip_flag ) {
SET_MOXA_MUST_XON1_VALUE ( info - > ioaddr , START_CHAR ( info - > tty ) ) ;
SET_MOXA_MUST_XOFF1_VALUE ( info - > ioaddr , STOP_CHAR ( info - > tty ) ) ;
if ( I_IXON ( info - > tty ) ) {
ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL ( info - > ioaddr ) ;
} else {
DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL ( info - > ioaddr ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
if ( I_IXOFF ( info - > tty ) ) {
ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL ( info - > ioaddr ) ;
} else {
DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL ( info - > ioaddr ) ;
2005-04-17 02:20:36 +04:00
}
}
2008-02-07 11:16:46 +03:00
outb ( fcr , info - > ioaddr + UART_FCR ) ; /* set fcr */
outb ( cval , info - > ioaddr + UART_LCR ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
static void mxser_check_modem_status ( struct mxser_port * port , int status )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
/* update input line counters */
if ( status & UART_MSR_TERI )
port - > icount . rng + + ;
if ( status & UART_MSR_DDSR )
port - > icount . dsr + + ;
if ( status & UART_MSR_DDCD )
port - > icount . dcd + + ;
if ( status & UART_MSR_DCTS )
port - > icount . cts + + ;
port - > mon_data . modem_status = status ;
wake_up_interruptible ( & port - > delta_msr_wait ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( ( port - > flags & ASYNC_CHECK_CD ) & & ( status & UART_MSR_DDCD ) ) {
if ( status & UART_MSR_DCD )
wake_up_interruptible ( & port - > open_wait ) ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( port - > flags & ASYNC_CTS_FLOW ) {
if ( port - > tty - > hw_stopped ) {
if ( status & UART_MSR_CTS ) {
port - > tty - > hw_stopped = 0 ;
if ( ( port - > type ! = PORT_16550A ) & &
( ! port - > board - > chip_flag ) ) {
outb ( port - > IER & ~ UART_IER_THRI ,
port - > ioaddr + UART_IER ) ;
port - > IER | = UART_IER_THRI ;
outb ( port - > IER , port - > ioaddr +
UART_IER ) ;
}
tty_wakeup ( port - > tty ) ;
}
} else {
if ( ! ( status & UART_MSR_CTS ) ) {
port - > tty - > hw_stopped = 1 ;
if ( port - > type ! = PORT_16550A & &
! port - > board - > chip_flag ) {
port - > IER & = ~ UART_IER_THRI ;
outb ( port - > IER , port - > ioaddr +
UART_IER ) ;
}
}
}
2005-04-17 02:20:36 +04:00
}
}
2008-02-07 11:16:46 +03:00
static int mxser_startup ( struct mxser_port * info )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
unsigned long page ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
page = __get_free_page ( GFP_KERNEL ) ;
if ( ! page )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
spin_lock_irqsave ( & info - > slock , flags ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( info - > flags & ASYNC_INITIALIZED ) {
free_page ( page ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
return 0 ;
}
2005-11-07 11:59:23 +03:00
2008-02-07 11:16:46 +03:00
if ( ! info - > ioaddr | | ! info - > type ) {
if ( info - > tty )
set_bit ( TTY_IO_ERROR , & info - > tty - > flags ) ;
free_page ( page ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2008-02-07 11:16:46 +03:00
}
if ( info - > xmit_buf )
free_page ( page ) ;
else
info - > xmit_buf = ( unsigned char * ) page ;
2005-04-17 02:20:36 +04:00
/*
2008-02-07 11:16:46 +03:00
* Clear the FIFO buffers and disable them
* ( they will be reenabled in mxser_change_speed ( ) )
2005-04-17 02:20:36 +04:00
*/
2008-02-07 11:16:46 +03:00
if ( info - > board - > chip_flag )
outb ( ( UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT |
MOXA_MUST_FCR_GDA_MODE_ENABLE ) , info - > ioaddr + UART_FCR ) ;
else
outb ( ( UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT ) ,
info - > ioaddr + UART_FCR ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/*
* At this point there ' s no way the LSR could still be 0xFF ;
* if it is , then bail out , because there ' s likely no UART
* here .
*/
if ( inb ( info - > ioaddr + UART_LSR ) = = 0xff ) {
spin_unlock_irqrestore ( & info - > slock , flags ) ;
if ( capable ( CAP_SYS_ADMIN ) ) {
if ( info - > tty )
set_bit ( TTY_IO_ERROR , & info - > tty - > flags ) ;
return 0 ;
} else
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/*
* Clear the interrupt registers .
*/
( void ) inb ( info - > ioaddr + UART_LSR ) ;
( void ) inb ( info - > ioaddr + UART_RX ) ;
( void ) inb ( info - > ioaddr + UART_IIR ) ;
( void ) inb ( info - > ioaddr + UART_MSR ) ;
/*
* Now , initialize the UART
*/
outb ( UART_LCR_WLEN8 , info - > ioaddr + UART_LCR ) ; /* reset DLAB */
info - > MCR = UART_MCR_DTR | UART_MCR_RTS ;
outb ( info - > MCR , info - > ioaddr + UART_MCR ) ;
/*
* Finally , enable interrupts
*/
info - > IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI ;
if ( info - > board - > chip_flag )
info - > IER | = MOXA_MUST_IER_EGDAI ;
outb ( info - > IER , info - > ioaddr + UART_IER ) ; /* enable interrupts */
/*
* And clear the interrupt registers again for luck .
*/
( void ) inb ( info - > ioaddr + UART_LSR ) ;
( void ) inb ( info - > ioaddr + UART_RX ) ;
( void ) inb ( info - > ioaddr + UART_IIR ) ;
( void ) inb ( info - > ioaddr + UART_MSR ) ;
if ( info - > tty )
clear_bit ( TTY_IO_ERROR , & info - > tty - > flags ) ;
info - > xmit_cnt = info - > xmit_head = info - > xmit_tail = 0 ;
/*
* and set the speed of the serial port
*/
mxser_change_speed ( info , NULL ) ;
info - > flags | = ASYNC_INITIALIZED ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
return 0 ;
}
/*
* This routine will shutdown a serial port ; interrupts maybe disabled , and
* DTR is dropped if the hangup on close termio flag is on .
*/
static void mxser_shutdown ( struct mxser_port * info )
{
unsigned long flags ;
if ( ! ( info - > flags & ASYNC_INITIALIZED ) )
return ;
spin_lock_irqsave ( & info - > slock , 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 ) ;
/*
* Free the IRQ , if necessary
*/
if ( info - > xmit_buf ) {
free_page ( ( unsigned long ) info - > xmit_buf ) ;
info - > xmit_buf = NULL ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
info - > IER = 0 ;
outb ( 0x00 , info - > ioaddr + UART_IER ) ;
if ( ! info - > tty | | ( info - > tty - > termios - > c_cflag & HUPCL ) )
info - > MCR & = ~ ( UART_MCR_DTR | UART_MCR_RTS ) ;
outb ( info - > MCR , info - > ioaddr + UART_MCR ) ;
/* clear Rx/Tx FIFO's */
if ( info - > board - > chip_flag )
outb ( UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |
MOXA_MUST_FCR_GDA_MODE_ENABLE ,
info - > ioaddr + UART_FCR ) ;
else
outb ( UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT ,
info - > ioaddr + UART_FCR ) ;
/* read data port to reset things */
( void ) inb ( info - > ioaddr + UART_RX ) ;
if ( info - > tty )
set_bit ( TTY_IO_ERROR , & info - > tty - > flags ) ;
info - > flags & = ~ ASYNC_INITIALIZED ;
if ( info - > board - > chip_flag )
SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL ( info - > ioaddr ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
}
/*
* 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 .
*/
static int mxser_open ( struct tty_struct * tty , struct file * filp )
{
struct mxser_port * info ;
unsigned long flags ;
int retval , line ;
line = tty - > index ;
if ( line = = MXSER_PORTS )
return 0 ;
if ( line < 0 | | line > MXSER_PORTS )
return - ENODEV ;
info = & mxser_boards [ line / MXSER_PORTS_PER_BOARD ] . ports [ line % MXSER_PORTS_PER_BOARD ] ;
if ( ! info - > ioaddr )
return - ENODEV ;
tty - > driver_data = info ;
info - > tty = tty ;
2006-06-25 16:47:54 +04:00
/*
2008-02-07 11:16:46 +03:00
* Start up serial port
*/
spin_lock_irqsave ( & info - > slock , flags ) ;
info - > count + + ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
retval = mxser_startup ( info ) ;
if ( retval )
return retval ;
retval = mxser_block_til_ready ( tty , filp , info ) ;
if ( retval )
return retval ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:46:33 +03:00
/* unmark here for very high baud rate (ex. 921600 bps) used */
2005-04-17 02:20:36 +04:00
tty - > low_latency = 1 ;
return 0 ;
}
2008-04-30 11:53:59 +04:00
static void mxser_flush_buffer ( struct tty_struct * tty )
{
struct mxser_port * info = tty - > driver_data ;
char fcr ;
unsigned long flags ;
spin_lock_irqsave ( & info - > slock , flags ) ;
info - > xmit_cnt = info - > xmit_head = info - > xmit_tail = 0 ;
fcr = inb ( info - > ioaddr + UART_FCR ) ;
outb ( ( fcr | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT ) ,
info - > ioaddr + UART_FCR ) ;
outb ( fcr , info - > ioaddr + UART_FCR ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
tty_wakeup ( tty ) ;
}
2005-04-17 02:20:36 +04:00
/*
* 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 mxser_close ( struct tty_struct * tty , struct file * filp )
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
unsigned long timeout ;
unsigned long flags ;
if ( tty - > index = = MXSER_PORTS )
return ;
if ( ! info )
2005-11-07 11:59:23 +03:00
return ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & info - > slock , flags ) ;
if ( tty_hung_up_p ( filp ) ) {
spin_unlock_irqrestore ( & info - > slock , flags ) ;
return ;
}
if ( ( tty - > count = = 1 ) & & ( info - > count ! = 1 ) ) {
/*
* Uh , oh . tty - > count is 1 , which means that the tty
* structure will be freed . Info - > count should always
* be one in these conditions . If it ' s greater than
* one , we ' ve got real problems , since it means the
* serial port won ' t be shutdown .
*/
2006-06-25 16:47:54 +04:00
printk ( KERN_ERR " mxser_close: bad serial port count; "
" tty->count is 1, info->count is %d \n " , info - > count ) ;
2005-04-17 02:20:36 +04:00
info - > count = 1 ;
}
if ( - - info - > count < 0 ) {
2006-06-25 16:47:54 +04:00
printk ( KERN_ERR " mxser_close: bad serial port count for "
2008-02-07 11:16:46 +03:00
" ttys%d: %d \n " , tty - > index , info - > count ) ;
2005-04-17 02:20:36 +04:00
info - > count = 0 ;
}
if ( info - > count ) {
spin_unlock_irqrestore ( & info - > slock , flags ) ;
return ;
}
info - > flags | = ASYNC_CLOSING ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
/*
* Save the termios structure , since this port may have
* separate termios for callout and dialin .
*/
if ( info - > flags & ASYNC_NORMAL_ACTIVE )
info - > normal_termios = * tty - > termios ;
/*
* 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 .
*/
info - > IER & = ~ UART_IER_RLSI ;
2008-02-07 11:16:46 +03:00
if ( info - > board - > chip_flag )
2005-04-17 02:20:36 +04:00
info - > IER & = ~ MOXA_MUST_RECV_ISR ;
2008-02-07 11:16:46 +03:00
2005-04-17 02:20:36 +04:00
if ( info - > flags & ASYNC_INITIALIZED ) {
2008-02-07 11:16:46 +03:00
outb ( info - > IER , info - > ioaddr + UART_IER ) ;
2005-04-17 02:20:36 +04:00
/*
* Before we drop DTR , make sure the UART transmitter
* has completely drained ; this is especially
* important if there is a transmit FIFO !
*/
timeout = jiffies + HZ ;
2008-02-07 11:16:46 +03:00
while ( ! ( inb ( info - > ioaddr + UART_LSR ) & UART_LSR_TEMT ) ) {
2005-09-10 11:27:30 +04:00
schedule_timeout_interruptible ( 5 ) ;
2005-04-17 02:20:36 +04:00
if ( time_after ( jiffies , timeout ) )
break ;
}
}
mxser_shutdown ( info ) ;
2008-04-30 11:53:59 +04:00
mxser_flush_buffer ( tty ) ;
2008-02-07 11:16:46 +03:00
tty_ldisc_flush ( tty ) ;
2005-04-17 02:20:36 +04:00
tty - > closing = 0 ;
info - > event = 0 ;
info - > tty = NULL ;
if ( info - > blocked_open ) {
2005-09-10 11:27:30 +04:00
if ( info - > close_delay )
schedule_timeout_interruptible ( info - > close_delay ) ;
2005-04-17 02:20:36 +04:00
wake_up_interruptible ( & info - > open_wait ) ;
}
info - > flags & = ~ ( ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING ) ;
}
static int mxser_write ( struct tty_struct * tty , const unsigned char * buf , int count )
{
int c , total = 0 ;
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2006-06-25 16:47:53 +04:00
if ( ! info - > xmit_buf )
2006-06-25 16:47:54 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
while ( 1 ) {
2006-06-25 16:47:54 +04:00
c = min_t ( int , count , min ( SERIAL_XMIT_SIZE - info - > xmit_cnt - 1 ,
SERIAL_XMIT_SIZE - info - > xmit_head ) ) ;
2005-04-17 02:20:36 +04:00
if ( c < = 0 )
break ;
memcpy ( info - > xmit_buf + info - > xmit_head , buf , c ) ;
spin_lock_irqsave ( & info - > slock , flags ) ;
2006-06-25 16:47:54 +04:00
info - > xmit_head = ( info - > xmit_head + c ) &
( SERIAL_XMIT_SIZE - 1 ) ;
2005-04-17 02:20:36 +04:00
info - > xmit_cnt + = c ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
buf + = c ;
count - = c ;
total + = c ;
}
2008-02-07 11:16:46 +03:00
if ( info - > xmit_cnt & & ! tty - > stopped ) {
2006-06-25 16:47:54 +04:00
if ( ! tty - > hw_stopped | |
( info - > type = = PORT_16550A ) | |
2008-02-07 11:16:46 +03:00
( info - > board - > chip_flag ) ) {
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & info - > slock , flags ) ;
2008-02-07 11:16:46 +03:00
outb ( info - > IER & ~ UART_IER_THRI , info - > ioaddr +
UART_IER ) ;
2005-04-17 02:20:36 +04:00
info - > IER | = UART_IER_THRI ;
2008-02-07 11:16:46 +03:00
outb ( info - > IER , info - > ioaddr + UART_IER ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & info - > slock , flags ) ;
}
}
return total ;
}
static void mxser_put_char ( struct tty_struct * tty , unsigned char ch )
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2006-06-25 16:47:53 +04:00
if ( ! info - > xmit_buf )
2005-04-17 02:20:36 +04:00
return ;
if ( info - > xmit_cnt > = SERIAL_XMIT_SIZE - 1 )
return ;
spin_lock_irqsave ( & info - > slock , flags ) ;
info - > xmit_buf [ info - > xmit_head + + ] = ch ;
info - > xmit_head & = SERIAL_XMIT_SIZE - 1 ;
info - > xmit_cnt + + ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
2008-02-07 11:16:46 +03:00
if ( ! tty - > stopped ) {
2006-06-25 16:47:54 +04:00
if ( ! tty - > hw_stopped | |
( info - > type = = PORT_16550A ) | |
2008-02-07 11:16:46 +03:00
info - > board - > chip_flag ) {
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & info - > slock , flags ) ;
2008-02-07 11:16:46 +03:00
outb ( info - > IER & ~ UART_IER_THRI , info - > ioaddr + UART_IER ) ;
2005-04-17 02:20:36 +04:00
info - > IER | = UART_IER_THRI ;
2008-02-07 11:16:46 +03:00
outb ( info - > IER , info - > ioaddr + UART_IER ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & info - > slock , flags ) ;
}
}
}
static void mxser_flush_chars ( struct tty_struct * tty )
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2006-06-25 16:47:54 +04:00
if ( info - > xmit_cnt < = 0 | |
tty - > stopped | |
! info - > xmit_buf | |
( tty - > hw_stopped & &
( info - > type ! = PORT_16550A ) & &
2008-02-07 11:16:46 +03:00
( ! info - > board - > chip_flag )
2006-06-25 16:47:54 +04:00
) )
2005-04-17 02:20:36 +04:00
return ;
spin_lock_irqsave ( & info - > slock , flags ) ;
2008-02-07 11:16:46 +03:00
outb ( info - > IER & ~ UART_IER_THRI , info - > ioaddr + UART_IER ) ;
2005-04-17 02:20:36 +04:00
info - > IER | = UART_IER_THRI ;
2008-02-07 11:16:46 +03:00
outb ( info - > IER , info - > ioaddr + UART_IER ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & info - > slock , flags ) ;
}
static int mxser_write_room ( struct tty_struct * tty )
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
int ret ;
ret = SERIAL_XMIT_SIZE - info - > xmit_cnt - 1 ;
if ( ret < 0 )
ret = 0 ;
2006-06-25 16:47:54 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
static int mxser_chars_in_buffer ( struct tty_struct * tty )
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
return info - > xmit_cnt ;
}
2008-02-07 11:16:46 +03:00
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* friends of mxser_ioctl ( )
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static int mxser_get_serial_info ( struct mxser_port * info ,
struct serial_struct __user * retinfo )
{
struct serial_struct tmp = {
. type = info - > type ,
. line = info - > tty - > index ,
. port = info - > ioaddr ,
. irq = info - > board - > irq ,
. flags = info - > flags ,
. baud_base = info - > baud_base ,
. close_delay = info - > close_delay ,
. closing_wait = info - > closing_wait ,
. custom_divisor = info - > custom_divisor ,
. hub6 = 0
} ;
if ( copy_to_user ( retinfo , & tmp , sizeof ( * retinfo ) ) )
return - EFAULT ;
return 0 ;
}
static int mxser_set_serial_info ( struct mxser_port * info ,
struct serial_struct __user * new_info )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
struct serial_struct new_serial ;
2008-02-07 11:16:51 +03:00
speed_t baud ;
2008-02-07 11:16:46 +03:00
unsigned long sl_flags ;
unsigned int flags ;
int retval = 0 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( ! new_info | | ! info - > ioaddr )
2008-02-07 11:16:51 +03:00
return - ENODEV ;
2008-02-07 11:16:46 +03:00
if ( copy_from_user ( & new_serial , new_info , sizeof ( new_serial ) ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:51 +03:00
if ( new_serial . irq ! = info - > board - > irq | |
new_serial . port ! = info - > ioaddr )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
flags = info - > flags & ASYNC_SPD_MASK ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( ! capable ( CAP_SYS_ADMIN ) ) {
if ( ( new_serial . baud_base ! = info - > baud_base ) | |
( new_serial . close_delay ! = info - > close_delay ) | |
( ( new_serial . flags & ~ ASYNC_USR_MASK ) ! = ( info - > flags & ~ ASYNC_USR_MASK ) ) )
return - EPERM ;
info - > flags = ( ( info - > flags & ~ ASYNC_USR_MASK ) |
( new_serial . flags & ASYNC_USR_MASK ) ) ;
} else {
2005-04-17 02:20:36 +04:00
/*
2008-02-07 11:16:46 +03:00
* OK , past this point , all the error checking has been done .
* At this point , we start making changes . . . . .
2005-04-17 02:20:36 +04:00
*/
2008-02-07 11:16:46 +03:00
info - > flags = ( ( info - > flags & ~ ASYNC_FLAGS ) |
( new_serial . flags & ASYNC_FLAGS ) ) ;
info - > close_delay = new_serial . close_delay * HZ / 100 ;
info - > closing_wait = new_serial . closing_wait * HZ / 100 ;
info - > tty - > low_latency =
( info - > flags & ASYNC_LOW_LATENCY ) ? 1 : 0 ;
info - > tty - > low_latency = 0 ;
2008-02-07 11:16:51 +03:00
if ( ( info - > flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_CUST & &
( new_serial . baud_base ! = info - > baud_base | |
new_serial . custom_divisor ! =
info - > custom_divisor ) ) {
baud = new_serial . baud_base / new_serial . custom_divisor ;
tty_encode_baud_rate ( info - > tty , baud , baud ) ;
}
2008-02-07 11:16:46 +03:00
}
2007-04-24 01:41:04 +04:00
2008-02-07 11:16:46 +03:00
info - > type = new_serial . type ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
process_txrx_fifo ( info ) ;
if ( info - > flags & ASYNC_INITIALIZED ) {
if ( flags ! = ( info - > flags & ASYNC_SPD_MASK ) ) {
spin_lock_irqsave ( & info - > slock , sl_flags ) ;
mxser_change_speed ( info , NULL ) ;
spin_unlock_irqrestore ( & info - > slock , sl_flags ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
} else
retval = mxser_startup ( info ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
return retval ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/*
* mxser_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 mxser_get_lsr_info ( struct mxser_port * info ,
unsigned int __user * value )
{
unsigned char status ;
unsigned int result ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
spin_lock_irqsave ( & info - > slock , flags ) ;
status = inb ( info - > ioaddr + UART_LSR ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
result = ( ( status & UART_LSR_TEMT ) ? TIOCSER_TEMT : 0 ) ;
return put_user ( result , value ) ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/*
* This routine sends a break character out the serial port .
*/
static void mxser_send_break ( struct mxser_port * info , int duration )
{
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( ! info - > ioaddr )
return ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
spin_lock_irqsave ( & info - > slock , flags ) ;
outb ( inb ( info - > ioaddr + UART_LCR ) | UART_LCR_SBC ,
info - > ioaddr + UART_LCR ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
schedule_timeout ( duration ) ;
spin_lock_irqsave ( & info - > slock , flags ) ;
outb ( inb ( info - > ioaddr + UART_LCR ) & ~ UART_LCR_SBC ,
info - > ioaddr + UART_LCR ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
static int mxser_tiocmget ( struct tty_struct * tty , struct file * file )
{
struct mxser_port * info = tty - > driver_data ;
unsigned char control , status ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2006-06-25 16:47:54 +04:00
2008-02-07 11:16:46 +03:00
if ( tty - > index = = MXSER_PORTS )
return - ENOIOCTLCMD ;
if ( test_bit ( TTY_IO_ERROR , & tty - > flags ) )
return - EIO ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
control = info - > MCR ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
spin_lock_irqsave ( & info - > slock , flags ) ;
status = inb ( info - > ioaddr + UART_MSR ) ;
if ( status & UART_MSR_ANY_DELTA )
mxser_check_modem_status ( info , status ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
return ( ( control & UART_MCR_RTS ) ? TIOCM_RTS : 0 ) |
( ( control & UART_MCR_DTR ) ? TIOCM_DTR : 0 ) |
( ( status & UART_MSR_DCD ) ? TIOCM_CAR : 0 ) |
( ( status & UART_MSR_RI ) ? TIOCM_RNG : 0 ) |
( ( status & UART_MSR_DSR ) ? TIOCM_DSR : 0 ) |
( ( status & UART_MSR_CTS ) ? TIOCM_CTS : 0 ) ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
static int mxser_tiocmset ( struct tty_struct * tty , struct file * file ,
unsigned int set , unsigned int clear )
{
struct mxser_port * info = tty - > driver_data ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( tty - > index = = MXSER_PORTS )
return - ENOIOCTLCMD ;
if ( test_bit ( TTY_IO_ERROR , & tty - > flags ) )
return - EIO ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
spin_lock_irqsave ( & info - > slock , flags ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( set & TIOCM_RTS )
info - > MCR | = UART_MCR_RTS ;
if ( set & TIOCM_DTR )
info - > MCR | = UART_MCR_DTR ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( clear & TIOCM_RTS )
info - > MCR & = ~ UART_MCR_RTS ;
if ( clear & TIOCM_DTR )
info - > MCR & = ~ UART_MCR_DTR ;
2006-06-25 16:47:54 +04:00
2008-02-07 11:16:46 +03:00
outb ( info - > MCR , info - > ioaddr + UART_MCR ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
static int __init mxser_program_mode ( int port )
{
int id , i , j , n ;
outb ( 0 , port ) ;
outb ( 0 , port ) ;
outb ( 0 , port ) ;
( void ) inb ( port ) ;
( void ) inb ( port ) ;
outb ( 0 , port ) ;
( void ) inb ( port ) ;
id = inb ( port + 1 ) & 0x1F ;
if ( ( id ! = C168_ASIC_ID ) & &
( id ! = C104_ASIC_ID ) & &
( id ! = C102_ASIC_ID ) & &
( id ! = CI132_ASIC_ID ) & &
( id ! = CI134_ASIC_ID ) & &
( id ! = CI104J_ASIC_ID ) )
return - 1 ;
for ( i = 0 , j = 0 ; i < 4 ; i + + ) {
n = inb ( port + 2 ) ;
if ( n = = ' M ' ) {
j = 1 ;
} else if ( ( j = = 1 ) & & ( n = = 1 ) ) {
j = 2 ;
break ;
} else
j = 0 ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
if ( j ! = 2 )
id = - 2 ;
return id ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
static void __init mxser_normal_mode ( int port )
{
int i , n ;
outb ( 0xA5 , port + 1 ) ;
outb ( 0x80 , port + 3 ) ;
outb ( 12 , port + 0 ) ; /* 9600 bps */
outb ( 0 , port + 1 ) ;
outb ( 0x03 , port + 3 ) ; /* 8 data bits */
outb ( 0x13 , port + 4 ) ; /* loop back mode */
for ( i = 0 ; i < 16 ; i + + ) {
n = inb ( port + 5 ) ;
if ( ( n & 0x61 ) = = 0x60 )
break ;
if ( ( n & 1 ) = = 1 )
( void ) inb ( port ) ;
}
outb ( 0x00 , port + 4 ) ;
}
# define CHIP_SK 0x01 /* Serial Data Clock in Eprom */
# define CHIP_DO 0x02 /* Serial Data Output in Eprom */
# define CHIP_CS 0x04 /* Serial Chip Select in Eprom */
# define CHIP_DI 0x08 /* Serial Data Input in Eprom */
# define EN_CCMD 0x000 /* Chip's command register */
# define EN0_RSARLO 0x008 /* Remote start address reg 0 */
# define EN0_RSARHI 0x009 /* Remote start address reg 1 */
# define EN0_RCNTLO 0x00A /* Remote byte count reg WR */
# define EN0_RCNTHI 0x00B /* Remote byte count reg WR */
# define EN0_DCFG 0x00E /* Data configuration reg WR */
# define EN0_PORT 0x010 /* Rcv missed frame error counter RD */
# define ENC_PAGE0 0x000 /* Select page 0 of chip registers */
# define ENC_PAGE3 0x0C0 /* Select page 3 of chip registers */
static int __init mxser_read_register ( int port , unsigned short * regs )
{
int i , k , value , id ;
unsigned int j ;
id = mxser_program_mode ( port ) ;
if ( id < 0 )
return id ;
for ( i = 0 ; i < 14 ; i + + ) {
k = ( i & 0x3F ) | 0x180 ;
for ( j = 0x100 ; j > 0 ; j > > = 1 ) {
outb ( CHIP_CS , port ) ;
if ( k & j ) {
outb ( CHIP_CS | CHIP_DO , port ) ;
outb ( CHIP_CS | CHIP_DO | CHIP_SK , port ) ; /* A? bit of read */
} else {
outb ( CHIP_CS , port ) ;
outb ( CHIP_CS | CHIP_SK , port ) ; /* A? bit of read */
}
}
( void ) inb ( port ) ;
value = 0 ;
for ( k = 0 , j = 0x8000 ; k < 16 ; k + + , j > > = 1 ) {
outb ( CHIP_CS , port ) ;
outb ( CHIP_CS | CHIP_SK , port ) ;
if ( inb ( port ) & CHIP_DI )
value | = j ;
}
regs [ i ] = value ;
outb ( 0 , port ) ;
}
mxser_normal_mode ( port ) ;
return id ;
}
2005-04-17 02:20:36 +04:00
static int mxser_ioctl_special ( unsigned int cmd , void __user * argp )
{
2008-02-07 11:16:46 +03:00
struct mxser_port * port ;
int result , status ;
unsigned int i , j ;
2008-04-30 11:53:20 +04:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case MOXA_GET_MAJOR :
2008-02-07 11:16:46 +03:00
return put_user ( ttymajor , ( int __user * ) argp ) ;
2005-04-17 02:20:36 +04:00
case MOXA_CHKPORTENABLE :
result = 0 ;
2008-04-30 11:53:20 +04:00
lock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
for ( i = 0 ; i < MXSER_BOARDS ; i + + )
for ( j = 0 ; j < MXSER_PORTS_PER_BOARD ; j + + )
if ( mxser_boards [ i ] . ports [ j ] . ioaddr )
result | = ( 1 < < i ) ;
2008-04-30 11:53:20 +04:00
unlock_kernel ( ) ;
2006-06-25 16:47:54 +04:00
return put_user ( result , ( unsigned long __user * ) argp ) ;
2005-04-17 02:20:36 +04:00
case MOXA_GETDATACOUNT :
2008-04-30 11:53:20 +04:00
lock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
if ( copy_to_user ( argp , & mxvar_log , sizeof ( mxvar_log ) ) )
2008-04-30 11:53:20 +04:00
ret = - EFAULT ;
unlock_kernel ( ) ;
return ret ;
2005-04-17 02:20:36 +04:00
case MOXA_GETMSTATUS :
2008-04-30 11:53:20 +04:00
lock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
for ( i = 0 ; i < MXSER_BOARDS ; i + + )
for ( j = 0 ; j < MXSER_PORTS_PER_BOARD ; j + + ) {
port = & mxser_boards [ i ] . ports [ j ] ;
GMStatus [ i ] . ri = 0 ;
if ( ! port - > ioaddr ) {
GMStatus [ i ] . dcd = 0 ;
GMStatus [ i ] . dsr = 0 ;
GMStatus [ i ] . cts = 0 ;
continue ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( ! port - > tty | | ! port - > tty - > termios )
GMStatus [ i ] . cflag =
port - > normal_termios . c_cflag ;
else
GMStatus [ i ] . cflag =
port - > tty - > termios - > c_cflag ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
status = inb ( port - > ioaddr + UART_MSR ) ;
if ( status & 0x80 /*UART_MSR_DCD */ )
GMStatus [ i ] . dcd = 1 ;
else
GMStatus [ i ] . dcd = 0 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( status & 0x20 /*UART_MSR_DSR */ )
GMStatus [ i ] . dsr = 1 ;
else
GMStatus [ i ] . dsr = 0 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( status & 0x10 /*UART_MSR_CTS */ )
GMStatus [ i ] . cts = 1 ;
else
GMStatus [ i ] . cts = 0 ;
}
2008-04-30 11:53:20 +04:00
unlock_kernel ( ) ;
2006-06-25 16:47:54 +04:00
if ( copy_to_user ( argp , GMStatus ,
sizeof ( struct mxser_mstatus ) * MXSER_PORTS ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
return 0 ;
2006-06-25 16:47:54 +04:00
case MOXA_ASPP_MON_EXT : {
2008-02-07 11:16:46 +03:00
int p , shiftbit ;
unsigned long opmode ;
unsigned cflag , iflag ;
2008-04-30 11:53:20 +04:00
lock_kernel ( ) ;
for ( i = 0 ; i < MXSER_BOARDS ; i + + ) {
2008-02-07 11:16:46 +03:00
for ( j = 0 ; j < MXSER_PORTS_PER_BOARD ; j + + ) {
port = & mxser_boards [ i ] . ports [ j ] ;
if ( ! port - > ioaddr )
2005-04-17 02:20:36 +04:00
continue ;
2008-02-07 11:16:46 +03:00
status = mxser_get_msr ( port - > ioaddr , 0 , i ) ;
2005-04-17 02:20:36 +04:00
if ( status & UART_MSR_TERI )
2008-02-07 11:16:46 +03:00
port - > icount . rng + + ;
2005-04-17 02:20:36 +04:00
if ( status & UART_MSR_DDSR )
2008-02-07 11:16:46 +03:00
port - > icount . dsr + + ;
2005-04-17 02:20:36 +04:00
if ( status & UART_MSR_DDCD )
2008-02-07 11:16:46 +03:00
port - > icount . dcd + + ;
2005-04-17 02:20:36 +04:00
if ( status & UART_MSR_DCTS )
2008-02-07 11:16:46 +03:00
port - > icount . cts + + ;
port - > mon_data . modem_status = status ;
mon_data_ext . rx_cnt [ i ] = port - > mon_data . rxcnt ;
mon_data_ext . tx_cnt [ i ] = port - > mon_data . txcnt ;
mon_data_ext . up_rxcnt [ i ] =
port - > mon_data . up_rxcnt ;
mon_data_ext . up_txcnt [ i ] =
port - > mon_data . up_txcnt ;
mon_data_ext . modem_status [ i ] =
port - > mon_data . modem_status ;
mon_data_ext . baudrate [ i ] =
tty_get_baud_rate ( port - > tty ) ;
if ( ! port - > tty | | ! port - > tty - > termios ) {
cflag = port - > normal_termios . c_cflag ;
iflag = port - > normal_termios . c_iflag ;
2005-04-17 02:20:36 +04:00
} else {
2008-02-07 11:16:46 +03:00
cflag = port - > tty - > termios - > c_cflag ;
iflag = port - > tty - > termios - > c_iflag ;
2005-04-17 02:20:36 +04:00
}
mon_data_ext . databits [ i ] = cflag & CSIZE ;
mon_data_ext . stopbits [ i ] = cflag & CSTOPB ;
2008-02-07 11:16:46 +03:00
mon_data_ext . parity [ i ] =
cflag & ( PARENB | PARODD | CMSPAR ) ;
2005-04-17 02:20:36 +04:00
mon_data_ext . flowctrl [ i ] = 0x00 ;
if ( cflag & CRTSCTS )
mon_data_ext . flowctrl [ i ] | = 0x03 ;
if ( iflag & ( IXON | IXOFF ) )
mon_data_ext . flowctrl [ i ] | = 0x0C ;
2008-02-07 11:16:46 +03:00
if ( port - > type = = PORT_16550A )
2005-04-17 02:20:36 +04:00
mon_data_ext . fifo [ i ] = 1 ;
else
mon_data_ext . fifo [ i ] = 0 ;
p = i % 4 ;
shiftbit = p * 2 ;
2008-02-07 11:16:46 +03:00
opmode = inb ( port - > opmode_ioaddr ) > > shiftbit ;
2005-04-17 02:20:36 +04:00
opmode & = OP_MODE_MASK ;
mon_data_ext . iftype [ i ] = opmode ;
}
2008-04-30 11:53:20 +04:00
}
unlock_kernel ( ) ;
if ( copy_to_user ( argp , & mon_data_ext ,
sizeof ( mon_data_ext ) ) )
return - EFAULT ;
return 0 ;
}
default :
2005-04-17 02:20:36 +04:00
return - ENOIOCTLCMD ;
}
return 0 ;
}
2008-02-07 11:16:46 +03:00
static int mxser_cflags_changed ( struct mxser_port * info , unsigned long arg ,
struct async_icount * cprev )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
struct async_icount cnow ;
unsigned long flags ;
int ret ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
spin_lock_irqsave ( & info - > slock , flags ) ;
cnow = info - > icount ; /* atomic copy */
spin_unlock_irqrestore ( & info - > slock , flags ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
ret = ( ( 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 ) ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
* cprev = cnow ;
return ret ;
}
static int mxser_ioctl ( struct tty_struct * tty , struct file * file ,
unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
struct async_icount cnow ;
struct serial_icounter_struct __user * p_cuser ;
unsigned long flags ;
void __user * argp = ( void __user * ) arg ;
int retval ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( tty - > index = = MXSER_PORTS )
return mxser_ioctl_special ( cmd , argp ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( cmd = = MOXA_SET_OP_MODE | | cmd = = MOXA_GET_OP_MODE ) {
int p ;
unsigned long opmode ;
static unsigned char ModeMask [ ] = { 0xfc , 0xf3 , 0xcf , 0x3f } ;
int shiftbit ;
unsigned char val , mask ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
p = tty - > index % 4 ;
if ( cmd = = MOXA_SET_OP_MODE ) {
if ( get_user ( opmode , ( int __user * ) argp ) )
return - EFAULT ;
if ( opmode ! = RS232_MODE & &
opmode ! = RS485_2WIRE_MODE & &
opmode ! = RS422_MODE & &
opmode ! = RS485_4WIRE_MODE )
return - EFAULT ;
2008-04-30 11:53:20 +04:00
lock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
mask = ModeMask [ p ] ;
shiftbit = p * 2 ;
val = inb ( info - > opmode_ioaddr ) ;
val & = mask ;
val | = ( opmode < < shiftbit ) ;
outb ( val , info - > opmode_ioaddr ) ;
2008-04-30 11:53:20 +04:00
unlock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
} else {
2008-04-30 11:53:20 +04:00
lock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
shiftbit = p * 2 ;
opmode = inb ( info - > opmode_ioaddr ) > > shiftbit ;
opmode & = OP_MODE_MASK ;
2008-04-30 11:53:20 +04:00
unlock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
if ( put_user ( opmode , ( int __user * ) argp ) )
return - EFAULT ;
}
return 0 ;
}
if ( cmd ! = TIOCGSERIAL & & cmd ! = TIOCMIWAIT & & cmd ! = TIOCGICOUNT & &
test_bit ( TTY_IO_ERROR , & tty - > flags ) )
return - EIO ;
switch ( cmd ) {
case TCSBRK : /* SVID version: non-zero arg --> no break */
retval = tty_check_change ( tty ) ;
if ( retval )
return retval ;
tty_wait_until_sent ( tty , 0 ) ;
if ( ! arg )
mxser_send_break ( info , HZ / 4 ) ; /* 1/4 second */
return 0 ;
case TCSBRKP : /* support for POSIX tcsendbreak() */
retval = tty_check_change ( tty ) ;
if ( retval )
return retval ;
tty_wait_until_sent ( tty , 0 ) ;
mxser_send_break ( info , arg ? arg * ( HZ / 10 ) : HZ / 4 ) ;
return 0 ;
case TIOCGSERIAL :
2008-04-30 11:53:20 +04:00
lock_kernel ( ) ;
retval = mxser_get_serial_info ( info , argp ) ;
unlock_kernel ( ) ;
return retval ;
2008-02-07 11:16:46 +03:00
case TIOCSSERIAL :
2008-04-30 11:53:20 +04:00
lock_kernel ( ) ;
retval = mxser_set_serial_info ( info , argp ) ;
unlock_kernel ( ) ;
return retval ;
2008-02-07 11:16:46 +03:00
case TIOCSERGETLSR : /* Get line status register */
2008-04-30 11:53:20 +04:00
return mxser_get_lsr_info ( info , argp ) ;
2008-02-07 11:16:46 +03:00
/*
* 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 :
spin_lock_irqsave ( & info - > slock , flags ) ;
cnow = info - > icount ; /* note the counters on entry */
spin_unlock_irqrestore ( & info - > slock , flags ) ;
return wait_event_interruptible ( info - > delta_msr_wait ,
mxser_cflags_changed ( info , arg , & cnow ) ) ;
/*
* 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 :
spin_lock_irqsave ( & info - > slock , flags ) ;
cnow = info - > icount ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
p_cuser = argp ;
if ( put_user ( cnow . frame , & p_cuser - > frame ) )
return - EFAULT ;
if ( put_user ( cnow . brk , & p_cuser - > brk ) )
return - EFAULT ;
if ( put_user ( cnow . overrun , & p_cuser - > overrun ) )
return - EFAULT ;
if ( put_user ( cnow . buf_overrun , & p_cuser - > buf_overrun ) )
return - EFAULT ;
if ( put_user ( cnow . parity , & p_cuser - > parity ) )
return - EFAULT ;
if ( put_user ( cnow . rx , & p_cuser - > rx ) )
return - EFAULT ;
if ( put_user ( cnow . tx , & p_cuser - > tx ) )
return - EFAULT ;
put_user ( cnow . cts , & p_cuser - > cts ) ;
put_user ( cnow . dsr , & p_cuser - > dsr ) ;
put_user ( cnow . rng , & p_cuser - > rng ) ;
put_user ( cnow . dcd , & p_cuser - > dcd ) ;
return 0 ;
case MOXA_HighSpeedOn :
return put_user ( info - > baud_base ! = 115200 ? 1 : 0 , ( int __user * ) argp ) ;
case MOXA_SDS_RSTICOUNTER :
2008-04-30 11:53:20 +04:00
lock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
info - > mon_data . rxcnt = 0 ;
info - > mon_data . txcnt = 0 ;
2008-04-30 11:53:20 +04:00
unlock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
return 0 ;
case MOXA_ASPP_OQUEUE : {
int len , lsr ;
2008-04-30 11:53:20 +04:00
lock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
len = mxser_chars_in_buffer ( tty ) ;
lsr = inb ( info - > ioaddr + UART_LSR ) & UART_LSR_TEMT ;
len + = ( lsr ? 0 : 1 ) ;
2008-04-30 11:53:20 +04:00
unlock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
return put_user ( len , ( int __user * ) argp ) ;
}
case MOXA_ASPP_MON : {
int mcr , status ;
2008-04-30 11:53:20 +04:00
lock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
status = mxser_get_msr ( info - > ioaddr , 1 , tty - > index ) ;
mxser_check_modem_status ( info , status ) ;
mcr = inb ( info - > ioaddr + UART_MCR ) ;
if ( mcr & MOXA_MUST_MCR_XON_FLAG )
info - > mon_data . hold_reason & = ~ NPPI_NOTIFY_XOFFHOLD ;
else
info - > mon_data . hold_reason | = NPPI_NOTIFY_XOFFHOLD ;
if ( mcr & MOXA_MUST_MCR_TX_XON )
info - > mon_data . hold_reason & = ~ NPPI_NOTIFY_XOFFXENT ;
else
info - > mon_data . hold_reason | = NPPI_NOTIFY_XOFFXENT ;
if ( info - > tty - > hw_stopped )
info - > mon_data . hold_reason | = NPPI_NOTIFY_CTSHOLD ;
else
info - > mon_data . hold_reason & = ~ NPPI_NOTIFY_CTSHOLD ;
2008-04-30 11:53:20 +04:00
unlock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
if ( copy_to_user ( argp , & info - > mon_data ,
sizeof ( struct mxser_mon ) ) )
return - EFAULT ;
return 0 ;
}
case MOXA_ASPP_LSTATUS : {
if ( put_user ( info - > err_shadow , ( unsigned char __user * ) argp ) )
return - EFAULT ;
info - > err_shadow = 0 ;
return 0 ;
}
case MOXA_SET_BAUD_METHOD : {
int method ;
if ( get_user ( method , ( int __user * ) argp ) )
return - EFAULT ;
mxser_set_baud_method [ tty - > index ] = method ;
return put_user ( method , ( int __user * ) argp ) ;
}
default :
return - ENOIOCTLCMD ;
}
return 0 ;
}
static void mxser_stoprx ( struct tty_struct * tty )
{
struct mxser_port * info = tty - > driver_data ;
info - > ldisc_stop_rx = 1 ;
if ( I_IXOFF ( tty ) ) {
if ( info - > board - > chip_flag ) {
info - > IER & = ~ MOXA_MUST_RECV_ISR ;
outb ( info - > IER , info - > ioaddr + UART_IER ) ;
} else {
info - > x_char = STOP_CHAR ( tty ) ;
outb ( 0 , info - > ioaddr + UART_IER ) ;
info - > IER | = UART_IER_THRI ;
outb ( info - > IER , info - > ioaddr + UART_IER ) ;
2005-04-17 02:20:36 +04:00
}
}
if ( info - > tty - > termios - > c_cflag & CRTSCTS ) {
2008-02-07 11:16:46 +03:00
info - > MCR & = ~ UART_MCR_RTS ;
outb ( info - > MCR , info - > ioaddr + UART_MCR ) ;
2005-04-17 02:20:36 +04:00
}
}
/*
* This routine is called by the upper - layer tty layer to signal that
* incoming characters should be throttled .
*/
static void mxser_throttle ( struct tty_struct * tty )
{
mxser_stoprx ( tty ) ;
}
static void mxser_unthrottle ( struct tty_struct * tty )
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/* startrx */
info - > ldisc_stop_rx = 0 ;
if ( I_IXOFF ( tty ) ) {
if ( info - > x_char )
info - > x_char = 0 ;
else {
if ( info - > board - > chip_flag ) {
info - > IER | = MOXA_MUST_RECV_ISR ;
outb ( info - > IER , info - > ioaddr + UART_IER ) ;
} else {
info - > x_char = START_CHAR ( tty ) ;
outb ( 0 , info - > ioaddr + UART_IER ) ;
info - > IER | = UART_IER_THRI ;
outb ( info - > IER , info - > ioaddr + UART_IER ) ;
}
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( info - > tty - > termios - > c_cflag & CRTSCTS ) {
info - > MCR | = UART_MCR_RTS ;
outb ( info - > MCR , info - > ioaddr + UART_MCR ) ;
2005-04-17 02:20:36 +04:00
}
}
/*
* mxser_stop ( ) and mxser_start ( )
*
* This routines are called before setting or resetting tty - > stopped .
* They enable or disable transmitter interrupts , as necessary .
*/
static void mxser_stop ( struct tty_struct * tty )
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
spin_lock_irqsave ( & info - > slock , flags ) ;
if ( info - > IER & UART_IER_THRI ) {
info - > IER & = ~ UART_IER_THRI ;
2008-02-07 11:16:46 +03:00
outb ( info - > IER , info - > ioaddr + UART_IER ) ;
2005-04-17 02:20:36 +04:00
}
spin_unlock_irqrestore ( & info - > slock , flags ) ;
}
static void mxser_start ( struct tty_struct * tty )
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
spin_lock_irqsave ( & info - > slock , flags ) ;
2008-02-07 11:16:46 +03:00
if ( info - > xmit_cnt & & info - > xmit_buf ) {
outb ( info - > IER & ~ UART_IER_THRI , info - > ioaddr + UART_IER ) ;
2005-04-17 02:20:36 +04:00
info - > IER | = UART_IER_THRI ;
2008-02-07 11:16:46 +03:00
outb ( info - > IER , info - > ioaddr + UART_IER ) ;
2005-04-17 02:20:36 +04:00
}
spin_unlock_irqrestore ( & info - > slock , flags ) ;
}
2008-02-07 11:16:46 +03:00
static void mxser_set_termios ( struct tty_struct * tty , struct ktermios * old_termios )
{
struct mxser_port * info = tty - > driver_data ;
unsigned long flags ;
spin_lock_irqsave ( & info - > slock , flags ) ;
mxser_change_speed ( info , old_termios ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
if ( ( old_termios - > c_cflag & CRTSCTS ) & &
! ( tty - > termios - > c_cflag & CRTSCTS ) ) {
tty - > hw_stopped = 0 ;
mxser_start ( tty ) ;
}
/* Handle sw stopped */
if ( ( old_termios - > c_iflag & IXON ) & &
! ( tty - > termios - > c_iflag & IXON ) ) {
tty - > stopped = 0 ;
if ( info - > board - > chip_flag ) {
spin_lock_irqsave ( & info - > slock , flags ) ;
DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL ( info - > ioaddr ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
}
mxser_start ( tty ) ;
}
}
2005-04-17 02:20:36 +04:00
/*
* mxser_wait_until_sent ( ) - - - wait until the transmitter is empty
*/
static void mxser_wait_until_sent ( struct tty_struct * tty , int timeout )
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
unsigned long orig_jiffies , char_time ;
int lsr ;
if ( info - > 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
2006-06-25 16:47:54 +04:00
printk ( KERN_DEBUG " In rs_wait_until_sent(%d) check=%lu... " ,
timeout , char_time ) ;
2005-04-17 02:20:36 +04:00
printk ( " jiff=%lu... " , jiffies ) ;
# endif
2008-04-30 11:53:59 +04:00
lock_kernel ( ) ;
2008-02-07 11:16:46 +03:00
while ( ! ( ( lsr = inb ( info - > ioaddr + UART_LSR ) ) & UART_LSR_TEMT ) ) {
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk ( " lsr = %d (jiff=%lu)... " , lsr , jiffies ) ;
# endif
2005-09-10 11:27:30 +04:00
schedule_timeout_interruptible ( char_time ) ;
2005-04-17 02:20:36 +04:00
if ( signal_pending ( current ) )
2008-02-07 11:16:46 +03:00
break ;
if ( timeout & & time_after ( jiffies , orig_jiffies + timeout ) )
break ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
set_current_state ( TASK_RUNNING ) ;
2008-04-30 11:53:59 +04:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
# ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk ( " lsr = %d (jiff=%lu)...done \n " , lsr , jiffies ) ;
# endif
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/*
* This routine is called by tty_hangup ( ) when a hangup is signaled .
*/
static void mxser_hangup ( struct tty_struct * tty )
{
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
mxser_flush_buffer ( tty ) ;
mxser_shutdown ( info ) ;
info - > event = 0 ;
info - > count = 0 ;
info - > flags & = ~ ASYNC_NORMAL_ACTIVE ;
info - > tty = NULL ;
wake_up_interruptible ( & info - > open_wait ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
/*
* mxser_rs_break ( ) - - - routine which turns the break handling on or off
*/
static void mxser_rs_break ( struct tty_struct * tty , int break_state )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2008-02-07 11:16:46 +03:00
spin_lock_irqsave ( & info - > slock , flags ) ;
if ( break_state = = - 1 )
outb ( inb ( info - > ioaddr + UART_LCR ) | UART_LCR_SBC ,
info - > ioaddr + UART_LCR ) ;
else
outb ( inb ( info - > ioaddr + UART_LCR ) & ~ UART_LCR_SBC ,
info - > ioaddr + UART_LCR ) ;
spin_unlock_irqrestore ( & info - > slock , flags ) ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
static void mxser_receive_chars ( struct mxser_port * port , int * status )
{
struct tty_struct * tty = port - > tty ;
unsigned char ch , gdl ;
int ignored = 0 ;
int cnt = 0 ;
int recv_room ;
int max = 256 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
recv_room = tty - > receive_room ;
if ( ( recv_room = = 0 ) & & ( ! port - > ldisc_stop_rx ) )
mxser_stoprx ( tty ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( port - > board - > chip_flag ! = MOXA_OTHER_UART ) {
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( * status & UART_LSR_SPECIAL )
goto intr_old ;
if ( port - > board - > chip_flag = = MOXA_MUST_MU860_HWID & &
( * status & MOXA_MUST_LSR_RERR ) )
goto intr_old ;
if ( * status & MOXA_MUST_LSR_RERR )
goto intr_old ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
gdl = inb ( port - > ioaddr + MOXA_MUST_GDL_REGISTER ) ;
if ( port - > board - > chip_flag = = MOXA_MUST_MU150_HWID )
gdl & = MOXA_MUST_GDL_MASK ;
if ( gdl > = recv_room ) {
if ( ! port - > ldisc_stop_rx )
mxser_stoprx ( tty ) ;
}
while ( gdl - - ) {
ch = inb ( port - > ioaddr + UART_RX ) ;
tty_insert_flip_char ( tty , ch , 0 ) ;
cnt + + ;
}
goto end_intr ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
intr_old :
do {
if ( max - - < 0 )
break ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
ch = inb ( port - > ioaddr + UART_RX ) ;
if ( port - > board - > chip_flag & & ( * status & UART_LSR_OE ) )
outb ( 0x23 , port - > ioaddr + UART_FCR ) ;
* status & = port - > read_status_mask ;
if ( * status & port - > ignore_status_mask ) {
if ( + + ignored > 100 )
break ;
} else {
char flag = 0 ;
if ( * status & UART_LSR_SPECIAL ) {
if ( * status & UART_LSR_BI ) {
flag = TTY_BREAK ;
port - > icount . brk + + ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( port - > flags & ASYNC_SAK )
do_SAK ( tty ) ;
} else if ( * status & UART_LSR_PE ) {
flag = TTY_PARITY ;
port - > icount . parity + + ;
} else if ( * status & UART_LSR_FE ) {
flag = TTY_FRAME ;
port - > icount . frame + + ;
} else if ( * status & UART_LSR_OE ) {
flag = TTY_OVERRUN ;
port - > icount . overrun + + ;
} else
flag = TTY_BREAK ;
}
tty_insert_flip_char ( tty , ch , flag ) ;
cnt + + ;
if ( cnt > = recv_room ) {
if ( ! port - > ldisc_stop_rx )
mxser_stoprx ( tty ) ;
break ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( port - > board - > chip_flag )
break ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
* status = inb ( port - > ioaddr + UART_LSR ) ;
} while ( * status & UART_LSR_DR ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
end_intr :
mxvar_log . rxcnt [ port - > tty - > index ] + = cnt ;
port - > mon_data . rxcnt + = cnt ;
port - > mon_data . up_rxcnt + = cnt ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/*
* We are called from an interrupt context with & port - > slock
* being held . Drop it temporarily in order to prevent
* recursive locking .
*/
spin_unlock ( & port - > slock ) ;
tty_flip_buffer_push ( tty ) ;
spin_lock ( & port - > slock ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
static void mxser_transmit_chars ( struct mxser_port * port )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
int count , cnt ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( port - > x_char ) {
outb ( port - > x_char , port - > ioaddr + UART_TX ) ;
port - > x_char = 0 ;
mxvar_log . txcnt [ port - > tty - > index ] + + ;
port - > mon_data . txcnt + + ;
port - > mon_data . up_txcnt + + ;
port - > icount . tx + + ;
return ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( port - > xmit_buf = = NULL )
return ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( ( port - > xmit_cnt < = 0 ) | | port - > tty - > stopped | |
( port - > tty - > hw_stopped & &
( port - > type ! = PORT_16550A ) & &
( ! port - > board - > chip_flag ) ) ) {
port - > IER & = ~ UART_IER_THRI ;
outb ( port - > IER , port - > ioaddr + UART_IER ) ;
return ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
cnt = port - > xmit_cnt ;
count = port - > xmit_fifo_size ;
do {
outb ( port - > xmit_buf [ port - > xmit_tail + + ] ,
port - > ioaddr + UART_TX ) ;
port - > xmit_tail = port - > xmit_tail & ( SERIAL_XMIT_SIZE - 1 ) ;
if ( - - port - > xmit_cnt < = 0 )
break ;
} while ( - - count > 0 ) ;
mxvar_log . txcnt [ port - > tty - > index ] + = ( cnt - port - > xmit_cnt ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
port - > mon_data . txcnt + = ( cnt - port - > xmit_cnt ) ;
port - > mon_data . up_txcnt + = ( cnt - port - > xmit_cnt ) ;
port - > icount . tx + = ( cnt - port - > xmit_cnt ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( port - > xmit_cnt < WAKEUP_CHARS )
tty_wakeup ( port - > tty ) ;
if ( port - > xmit_cnt < = 0 ) {
port - > IER & = ~ UART_IER_THRI ;
outb ( port - > IER , port - > ioaddr + UART_IER ) ;
2005-04-17 02:20:36 +04:00
}
}
/*
2008-02-07 11:16:46 +03:00
* This is the serial driver ' s generic interrupt routine
2005-04-17 02:20:36 +04:00
*/
2008-02-07 11:16:46 +03:00
static irqreturn_t mxser_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
int status , iir , i ;
struct mxser_board * brd = NULL ;
struct mxser_port * port ;
int max , irqbits , bits , msr ;
unsigned int int_cnt , pass_counter = 0 ;
int handled = IRQ_NONE ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
for ( i = 0 ; i < MXSER_BOARDS ; i + + )
if ( dev_id = = & mxser_boards [ i ] ) {
brd = dev_id ;
break ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
if ( i = = MXSER_BOARDS )
goto irq_stop ;
if ( brd = = NULL )
goto irq_stop ;
max = brd - > info - > nports ;
while ( pass_counter + + < MXSER_ISR_PASS_LIMIT ) {
irqbits = inb ( brd - > vector ) & brd - > vector_mask ;
if ( irqbits = = brd - > vector_mask )
break ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
handled = IRQ_HANDLED ;
for ( i = 0 , bits = 1 ; i < max ; i + + , irqbits | = bits , bits < < = 1 ) {
if ( irqbits = = brd - > vector_mask )
break ;
if ( bits & irqbits )
continue ;
port = & brd - > ports [ i ] ;
int_cnt = 0 ;
spin_lock ( & port - > slock ) ;
do {
iir = inb ( port - > ioaddr + UART_IIR ) ;
if ( iir & UART_IIR_NO_INT )
break ;
iir & = MOXA_MUST_IIR_MASK ;
if ( ! port - > tty | |
( port - > flags & ASYNC_CLOSING ) | |
! ( port - > flags &
ASYNC_INITIALIZED ) ) {
status = inb ( port - > ioaddr + UART_LSR ) ;
outb ( 0x27 , port - > ioaddr + UART_FCR ) ;
inb ( port - > ioaddr + UART_MSR ) ;
break ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
status = inb ( port - > ioaddr + UART_LSR ) ;
if ( status & UART_LSR_PE )
port - > err_shadow | = NPPI_NOTIFY_PARITY ;
if ( status & UART_LSR_FE )
port - > err_shadow | = NPPI_NOTIFY_FRAMING ;
if ( status & UART_LSR_OE )
port - > err_shadow | =
NPPI_NOTIFY_HW_OVERRUN ;
if ( status & UART_LSR_BI )
port - > err_shadow | = NPPI_NOTIFY_BREAK ;
if ( port - > board - > chip_flag ) {
if ( iir = = MOXA_MUST_IIR_GDA | |
iir = = MOXA_MUST_IIR_RDA | |
iir = = MOXA_MUST_IIR_RTO | |
iir = = MOXA_MUST_IIR_LSR )
mxser_receive_chars ( port ,
& status ) ;
} else {
status & = port - > read_status_mask ;
if ( status & UART_LSR_DR )
mxser_receive_chars ( port ,
& status ) ;
}
msr = inb ( port - > ioaddr + UART_MSR ) ;
if ( msr & UART_MSR_ANY_DELTA )
mxser_check_modem_status ( port , msr ) ;
if ( port - > board - > chip_flag ) {
if ( iir = = 0x02 & & ( status &
UART_LSR_THRE ) )
mxser_transmit_chars ( port ) ;
} else {
if ( status & UART_LSR_THRE )
mxser_transmit_chars ( port ) ;
}
} while ( int_cnt + + < MXSER_ISR_PASS_LIMIT ) ;
spin_unlock ( & port - > slock ) ;
}
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
irq_stop :
return handled ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
static const struct tty_operations mxser_ops = {
. open = mxser_open ,
. close = mxser_close ,
. write = mxser_write ,
. put_char = mxser_put_char ,
. flush_chars = mxser_flush_chars ,
. write_room = mxser_write_room ,
. chars_in_buffer = mxser_chars_in_buffer ,
. flush_buffer = mxser_flush_buffer ,
. ioctl = mxser_ioctl ,
. throttle = mxser_throttle ,
. unthrottle = mxser_unthrottle ,
. set_termios = mxser_set_termios ,
. stop = mxser_stop ,
. start = mxser_start ,
. hangup = mxser_hangup ,
. break_ctl = mxser_rs_break ,
. wait_until_sent = mxser_wait_until_sent ,
. tiocmget = mxser_tiocmget ,
. tiocmset = mxser_tiocmset ,
} ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/*
* The MOXA Smartio / Industio serial driver boot - time initialization code !
*/
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
static void mxser_release_res ( struct mxser_board * brd , struct pci_dev * pdev ,
unsigned int irq )
{
if ( irq )
free_irq ( brd - > irq , brd ) ;
if ( pdev ! = NULL ) { /* PCI */
# ifdef CONFIG_PCI
pci_release_region ( pdev , 2 ) ;
pci_release_region ( pdev , 3 ) ;
# endif
} else {
release_region ( brd - > ports [ 0 ] . ioaddr , 8 * brd - > info - > nports ) ;
release_region ( brd - > vector , 1 ) ;
}
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
static int __devinit mxser_initbrd ( struct mxser_board * brd ,
struct pci_dev * pdev )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
struct mxser_port * info ;
unsigned int i ;
int retval ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
printk ( KERN_INFO " max. baud rate = %d bps. \n " , brd - > ports [ 0 ] . max_baud ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
for ( i = 0 ; i < brd - > info - > nports ; i + + ) {
info = & brd - > ports [ i ] ;
info - > board = brd ;
info - > stop_rx = 0 ;
info - > ldisc_stop_rx = 0 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/* Enhance mode enabled here */
if ( brd - > chip_flag ! = MOXA_OTHER_UART )
ENABLE_MOXA_MUST_ENCHANCE_MODE ( info - > ioaddr ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
info - > flags = ASYNC_SHARE_IRQ ;
info - > type = brd - > uart_type ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
process_txrx_fifo ( info ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
info - > custom_divisor = info - > baud_base * 16 ;
info - > close_delay = 5 * HZ / 10 ;
info - > closing_wait = 30 * HZ ;
info - > normal_termios = mxvar_sdriver - > init_termios ;
init_waitqueue_head ( & info - > open_wait ) ;
init_waitqueue_head ( & info - > delta_msr_wait ) ;
memset ( & info - > mon_data , 0 , sizeof ( struct mxser_mon ) ) ;
info - > err_shadow = 0 ;
spin_lock_init ( & info - > slock ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
/* before set INT ISR, disable all int */
outb ( inb ( info - > ioaddr + UART_IER ) & 0xf0 ,
info - > ioaddr + UART_IER ) ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
retval = request_irq ( brd - > irq , mxser_interrupt , IRQF_SHARED , " mxser " ,
brd ) ;
if ( retval ) {
printk ( KERN_ERR " Board %s: Request irq failed, IRQ (%d) may "
" conflict with another device. \n " ,
brd - > info - > name , brd - > irq ) ;
/* We hold resources, we need to release them. */
mxser_release_res ( brd , pdev , 0 ) ;
}
return retval ;
}
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
static int __init mxser_get_ISA_conf ( int cap , struct mxser_board * brd )
2005-04-17 02:20:36 +04:00
{
int id , i , bits ;
unsigned short regs [ 16 ] , irq ;
unsigned char scratch , scratch2 ;
2008-02-07 11:16:46 +03:00
brd - > chip_flag = MOXA_OTHER_UART ;
2005-04-17 02:20:36 +04:00
id = mxser_read_register ( cap , regs ) ;
2008-02-07 11:16:46 +03:00
switch ( id ) {
case C168_ASIC_ID :
brd - > info = & mxser_cards [ 0 ] ;
break ;
case C104_ASIC_ID :
brd - > info = & mxser_cards [ 1 ] ;
break ;
case CI104J_ASIC_ID :
brd - > info = & mxser_cards [ 2 ] ;
break ;
case C102_ASIC_ID :
brd - > info = & mxser_cards [ 5 ] ;
break ;
case CI132_ASIC_ID :
brd - > info = & mxser_cards [ 6 ] ;
break ;
case CI134_ASIC_ID :
brd - > info = & mxser_cards [ 7 ] ;
break ;
default :
2006-06-25 16:47:54 +04:00
return 0 ;
2008-02-07 11:16:46 +03:00
}
2005-04-17 02:20:36 +04:00
irq = 0 ;
2008-02-07 11:16:46 +03:00
/* some ISA cards have 2 ports, but we want to see them as 4-port (why?)
Flag - hack checks if configuration should be read as 2 - port here . */
if ( brd - > info - > nports = = 2 | | ( brd - > info - > flags & MXSER_HAS2 ) ) {
2005-04-17 02:20:36 +04:00
irq = regs [ 9 ] & 0xF000 ;
irq = irq | ( irq > > 4 ) ;
if ( irq ! = ( regs [ 9 ] & 0xFF00 ) )
2006-06-25 16:47:54 +04:00
return MXSER_ERR_IRQ_CONFLIT ;
2008-02-07 11:16:46 +03:00
} else if ( brd - > info - > nports = = 4 ) {
2005-04-17 02:20:36 +04:00
irq = regs [ 9 ] & 0xF000 ;
irq = irq | ( irq > > 4 ) ;
irq = irq | ( irq > > 8 ) ;
if ( irq ! = regs [ 9 ] )
2006-06-25 16:47:54 +04:00
return MXSER_ERR_IRQ_CONFLIT ;
2008-02-07 11:16:46 +03:00
} else if ( brd - > info - > nports = = 8 ) {
2005-04-17 02:20:36 +04:00
irq = regs [ 9 ] & 0xF000 ;
irq = irq | ( irq > > 4 ) ;
irq = irq | ( irq > > 8 ) ;
if ( ( irq ! = regs [ 9 ] ) | | ( irq ! = regs [ 10 ] ) )
2006-06-25 16:47:54 +04:00
return MXSER_ERR_IRQ_CONFLIT ;
2005-04-17 02:20:36 +04:00
}
2006-06-25 16:47:54 +04:00
if ( ! irq )
return MXSER_ERR_IRQ ;
2008-02-07 11:16:46 +03:00
brd - > irq = ( ( int ) ( irq & 0xF000 ) > > 12 ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 8 ; i + + )
2008-02-07 11:16:46 +03:00
brd - > ports [ i ] . ioaddr = ( int ) regs [ i + 1 ] & 0xFFF8 ;
2006-06-25 16:47:54 +04:00
if ( ( regs [ 12 ] & 0x80 ) = = 0 )
return MXSER_ERR_VECTOR ;
2008-02-07 11:16:46 +03:00
brd - > vector = ( int ) regs [ 11 ] ; /* interrupt vector */
2005-04-17 02:20:36 +04:00
if ( id = = 1 )
2008-02-07 11:16:46 +03:00
brd - > vector_mask = 0x00FF ;
2005-04-17 02:20:36 +04:00
else
2008-02-07 11:16:46 +03:00
brd - > vector_mask = 0x000F ;
2005-04-17 02:20:36 +04:00
for ( i = 7 , bits = 0x0100 ; i > = 0 ; i - - , bits < < = 1 ) {
if ( regs [ 12 ] & bits ) {
2008-02-07 11:16:46 +03:00
brd - > ports [ i ] . baud_base = 921600 ;
brd - > ports [ i ] . max_baud = 921600 ;
2005-04-17 02:20:36 +04:00
} else {
2008-02-07 11:16:46 +03:00
brd - > ports [ i ] . baud_base = 115200 ;
brd - > ports [ i ] . max_baud = 115200 ;
2005-04-17 02:20:36 +04:00
}
}
scratch2 = inb ( cap + UART_LCR ) & ( ~ UART_LCR_DLAB ) ;
outb ( scratch2 | UART_LCR_DLAB , cap + UART_LCR ) ;
outb ( 0 , cap + UART_EFR ) ; /* EFR is the same as FCR */
outb ( scratch2 , cap + UART_LCR ) ;
outb ( UART_FCR_ENABLE_FIFO , cap + UART_FCR ) ;
scratch = inb ( cap + UART_IIR ) ;
if ( scratch & 0xC0 )
2008-02-07 11:16:46 +03:00
brd - > uart_type = PORT_16550A ;
2005-04-17 02:20:36 +04:00
else
2008-02-07 11:16:46 +03:00
brd - > uart_type = PORT_16450 ;
if ( ! request_region ( brd - > ports [ 0 ] . ioaddr , 8 * brd - > info - > nports ,
" mxser(IO) " ) )
return MXSER_ERR_IOADDR ;
if ( ! request_region ( brd - > vector , 1 , " mxser(vector) " ) ) {
release_region ( brd - > ports [ 0 ] . ioaddr , 8 * brd - > info - > nports ) ;
return MXSER_ERR_VECTOR ;
}
return brd - > info - > nports ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
static int __devinit mxser_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
# ifdef CONFIG_PCI
struct mxser_board * brd ;
unsigned int i , j ;
unsigned long ioaddress ;
int retval = - EINVAL ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
for ( i = 0 ; i < MXSER_BOARDS ; i + + )
if ( mxser_boards [ i ] . info = = NULL )
break ;
if ( i > = MXSER_BOARDS ) {
printk ( KERN_ERR " Too many Smartio/Industio family boards found "
" (maximum %d), board not configured \n " , MXSER_BOARDS ) ;
goto err ;
}
brd = & mxser_boards [ i ] ;
brd - > idx = i * MXSER_PORTS_PER_BOARD ;
printk ( KERN_INFO " Found MOXA %s board (BusNo=%d, DevNo=%d) \n " ,
mxser_cards [ ent - > driver_data ] . name ,
pdev - > bus - > number , PCI_SLOT ( pdev - > devfn ) ) ;
retval = pci_enable_device ( pdev ) ;
if ( retval ) {
printk ( KERN_ERR " Moxa SmartI/O PCI enable fail ! \n " ) ;
goto err ;
}
/* io address */
ioaddress = pci_resource_start ( pdev , 2 ) ;
retval = pci_request_region ( pdev , 2 , " mxser(IO) " ) ;
if ( retval )
goto err ;
brd - > info = & mxser_cards [ ent - > driver_data ] ;
for ( i = 0 ; i < brd - > info - > nports ; i + + )
brd - > ports [ i ] . ioaddr = ioaddress + 8 * i ;
/* vector */
ioaddress = pci_resource_start ( pdev , 3 ) ;
retval = pci_request_region ( pdev , 3 , " mxser(vector) " ) ;
if ( retval )
goto err_relio ;
brd - > vector = ioaddress ;
/* irq */
brd - > irq = pdev - > irq ;
brd - > chip_flag = CheckIsMoxaMust ( brd - > ports [ 0 ] . ioaddr ) ;
brd - > uart_type = PORT_16550A ;
brd - > vector_mask = 0 ;
for ( i = 0 ; i < brd - > info - > nports ; i + + ) {
for ( j = 0 ; j < UART_INFO_NUM ; j + + ) {
if ( Gpci_uart_info [ j ] . type = = brd - > chip_flag ) {
brd - > ports [ i ] . max_baud =
Gpci_uart_info [ j ] . max_baud ;
/* exception....CP-102 */
if ( brd - > info - > flags & MXSER_HIGHBAUD )
brd - > ports [ i ] . max_baud = 921600 ;
break ;
2005-04-17 02:20:36 +04:00
}
}
2008-02-07 11:16:46 +03:00
}
if ( brd - > chip_flag = = MOXA_MUST_MU860_HWID ) {
for ( i = 0 ; i < brd - > info - > nports ; i + + ) {
if ( i < 4 )
brd - > ports [ i ] . opmode_ioaddr = ioaddress + 4 ;
else
brd - > ports [ i ] . opmode_ioaddr = ioaddress + 0x0c ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
outb ( 0 , ioaddress + 4 ) ; /* default set to RS232 mode */
outb ( 0 , ioaddress + 0x0c ) ; /* default set to RS232 mode */
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
for ( i = 0 ; i < brd - > info - > nports ; i + + ) {
brd - > vector_mask | = ( 1 < < i ) ;
brd - > ports [ i ] . baud_base = 921600 ;
}
/* mxser_initbrd will hook ISR. */
retval = mxser_initbrd ( brd , pdev ) ;
if ( retval )
goto err_null ;
for ( i = 0 ; i < brd - > info - > nports ; i + + )
tty_register_device ( mxvar_sdriver , brd - > idx + i , & pdev - > dev ) ;
pci_set_drvdata ( pdev , brd ) ;
return 0 ;
err_relio :
pci_release_region ( pdev , 2 ) ;
err_null :
brd - > info = NULL ;
err :
return retval ;
# else
return - ENODEV ;
# endif
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
static void __devexit mxser_remove ( struct pci_dev * pdev )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
struct mxser_board * brd = pci_get_drvdata ( pdev ) ;
unsigned int i ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
for ( i = 0 ; i < brd - > info - > nports ; i + + )
tty_unregister_device ( mxvar_sdriver , brd - > idx + i ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
mxser_release_res ( brd , pdev , 1 ) ;
brd - > info = NULL ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
static struct pci_driver mxser_driver = {
. name = " mxser " ,
. id_table = mxser_pcibrds ,
. probe = mxser_probe ,
. remove = __devexit_p ( mxser_remove )
} ;
static int __init mxser_module_init ( void )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:16:46 +03:00
struct mxser_board * brd ;
unsigned long cap ;
unsigned int i , m , isaloop ;
int retval , b ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:16:46 +03:00
pr_debug ( " Loading module mxser ... \n " ) ;
mxvar_sdriver = alloc_tty_driver ( MXSER_PORTS + 1 ) ;
if ( ! mxvar_sdriver )
return - ENOMEM ;
printk ( KERN_INFO " MOXA Smartio/Industio family driver version %s \n " ,
MXSER_VERSION ) ;
/* Initialize the tty_driver structure */
mxvar_sdriver - > owner = THIS_MODULE ;
mxvar_sdriver - > magic = TTY_DRIVER_MAGIC ;
mxvar_sdriver - > name = " ttyMI " ;
mxvar_sdriver - > major = ttymajor ;
mxvar_sdriver - > minor_start = 0 ;
mxvar_sdriver - > num = MXSER_PORTS + 1 ;
mxvar_sdriver - > type = TTY_DRIVER_TYPE_SERIAL ;
mxvar_sdriver - > subtype = SERIAL_TYPE_NORMAL ;
mxvar_sdriver - > init_termios = tty_std_termios ;
mxvar_sdriver - > init_termios . c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL ;
mxvar_sdriver - > flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV ;
tty_set_operations ( mxvar_sdriver , & mxser_ops ) ;
retval = tty_register_driver ( mxvar_sdriver ) ;
if ( retval ) {
printk ( KERN_ERR " Couldn't install MOXA Smartio/Industio family "
" tty driver ! \n " ) ;
goto err_put ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:16:46 +03:00
mxvar_diagflag = 0 ;
m = 0 ;
/* Start finding ISA boards here */
for ( isaloop = 0 ; isaloop < 2 ; isaloop + + )
for ( b = 0 ; b < MXSER_BOARDS & & m < MXSER_BOARDS ; b + + ) {
if ( ! isaloop )
cap = mxserBoardCAP [ b ] ; /* predefined */
else
cap = ioaddr [ b ] ; /* module param */
if ( ! cap )
continue ;
brd = & mxser_boards [ m ] ;
retval = mxser_get_ISA_conf ( cap , brd ) ;
if ( retval ! = 0 )
printk ( KERN_INFO " Found MOXA %s board "
" (CAP=0x%x) \n " ,
brd - > info - > name , ioaddr [ b ] ) ;
if ( retval < = 0 ) {
if ( retval = = MXSER_ERR_IRQ )
printk ( KERN_ERR " Invalid interrupt "
" number, board not "
" configured \n " ) ;
else if ( retval = = MXSER_ERR_IRQ_CONFLIT )
printk ( KERN_ERR " Invalid interrupt "
" number, board not "
" configured \n " ) ;
else if ( retval = = MXSER_ERR_VECTOR )
printk ( KERN_ERR " Invalid interrupt "
" vector, board not "
" configured \n " ) ;
else if ( retval = = MXSER_ERR_IOADDR )
printk ( KERN_ERR " Invalid I/O address, "
" board not configured \n " ) ;
brd - > info = NULL ;
continue ;
}
/* mxser_initbrd will hook ISR. */
if ( mxser_initbrd ( brd , NULL ) < 0 ) {
brd - > info = NULL ;
continue ;
}
brd - > idx = m * MXSER_PORTS_PER_BOARD ;
for ( i = 0 ; i < brd - > info - > nports ; i + + )
tty_register_device ( mxvar_sdriver , brd - > idx + i ,
NULL ) ;
m + + ;
}
retval = pci_register_driver ( & mxser_driver ) ;
if ( retval ) {
printk ( KERN_ERR " Can't register pci driver \n " ) ;
if ( ! m ) {
retval = - ENODEV ;
goto err_unr ;
} /* else: we have some ISA cards under control */
}
pr_debug ( " Done. \n " ) ;
return 0 ;
err_unr :
tty_unregister_driver ( mxvar_sdriver ) ;
err_put :
put_tty_driver ( mxvar_sdriver ) ;
return retval ;
}
static void __exit mxser_module_exit ( void )
{
unsigned int i , j ;
pr_debug ( " Unloading module mxser ... \n " ) ;
pci_unregister_driver ( & mxser_driver ) ;
for ( i = 0 ; i < MXSER_BOARDS ; i + + ) /* ISA remains */
if ( mxser_boards [ i ] . info ! = NULL )
for ( j = 0 ; j < mxser_boards [ i ] . info - > nports ; j + + )
tty_unregister_device ( mxvar_sdriver ,
mxser_boards [ i ] . idx + j ) ;
tty_unregister_driver ( mxvar_sdriver ) ;
put_tty_driver ( mxvar_sdriver ) ;
for ( i = 0 ; i < MXSER_BOARDS ; i + + )
if ( mxser_boards [ i ] . info ! = NULL )
mxser_release_res ( & mxser_boards [ i ] , NULL , 1 ) ;
pr_debug ( " Done. \n " ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( mxser_module_init ) ;
module_exit ( mxser_module_exit ) ;