2005-04-17 02:20:36 +04:00
/*
* MCT ( Magic Control Technology Corp . ) USB RS232 Converter Driver
*
* Copyright ( C ) 2000 Wolfgang Grandegger ( wolfgang @ ces . ch )
*
* 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 largely derived from the Belkin USB Serial Adapter Driver
* ( see belkin_sa . [ ch ] ) . All of the information about the device was acquired
* by using SniffUSB on Windows98 . For technical details see mct_u232 . h .
*
* William G . Greathouse and Greg Kroah - Hartman provided great help on how to
* do the reverse engineering and how to write a USB serial device driver .
*
* TO BE DONE , TO BE CHECKED :
* DTR / RTS signal handling may be incomplete or incorrect . I have mainly
* implemented what I have seen with SniffUSB or found in belkin_sa . c .
* For further TODOs check also belkin_sa . c .
*
* TEST STATUS :
* Basic tests have been performed with minicom / zmodem transfers and
* modem dialing under Linux 2.4 .0 - test10 ( for me it works fine ) .
*
* 04 - Nov - 2003 Bill Marr < marr at flex dot com >
* - Mimic Windows driver by sending 2 USB ' device request ' messages
* following normal ' baud rate change ' message . This allows data to be
* transmitted to RS - 232 devices which don ' t assert the ' CTS ' signal .
*
* 10 - Nov - 2001 Wolfgang Grandegger
* - Fixed an endianess problem with the baudrate selection for PowerPC .
*
* 06 - Dec - 2001 Martin Hamilton < martinh @ gnu . org >
2008-07-22 14:14:30 +04:00
* - Added support for the Belkin F5U109 DB9 adaptor
2005-04-17 02:20:36 +04:00
*
* 30 - May - 2001 Greg Kroah - Hartman
2008-07-22 14:14:30 +04:00
* - switched from using spinlock to a semaphore , which fixes lots of
* problems .
2005-04-17 02:20:36 +04:00
*
* 04 - May - 2001 Stelian Pop
* - Set the maximum bulk output size for Sitecom U232 - P25 model to 16 bytes
* instead of the device reported 32 ( using 32 bytes causes many data
* loss , Windows driver uses 16 too ) .
*
* 02 - May - 2001 Stelian Pop
* - Fixed the baud calculation for Sitecom U232 - P25 model
*
* 08 - Apr - 2001 gb
* - Identify version on module load .
*
2008-07-22 14:14:30 +04:00
* 06 - Jan - 2001 Cornel Ciocirlan
2005-04-17 02:20:36 +04:00
* - Added support for Sitecom U232 - P25 model ( Product Id 0x0230 )
* - Added support for D - Link DU - H3SP USB BAY ( Product Id 0x0200 )
*
* 29 - Nov - 2000 Greg Kroah - Hartman
* - Added device id table to fit with 2.4 .0 - test11 structure .
* - took out DEAL_WITH_TWO_INT_IN_ENDPOINTS # define as it ' s not needed
* ( lots of things will change if / when the usb - serial core changes to
* handle these issues .
*
2008-07-22 14:14:30 +04:00
* 27 - Nov - 2000 Wolfgang Grandegge
* A version for kernel 2.4 .0 - test10 released to the Linux community
2005-04-17 02:20:36 +04:00
* ( via linux - usb - devel ) .
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/tty.h>
# include <linux/tty_driver.h>
# include <linux/tty_flip.h>
# include <linux/module.h>
# include <linux/spinlock.h>
2008-07-22 14:14:30 +04:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <linux/usb.h>
2006-07-12 08:22:58 +04:00
# include <linux/usb/serial.h>
2005-04-17 02:20:36 +04:00
# include "mct_u232.h"
/*
* Version Information
*/
2007-05-08 22:00:12 +04:00
# define DRIVER_VERSION "z2.1" /* Linux in-kernel version */
2005-04-17 02:20:36 +04:00
# define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
# define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
static int debug ;
/*
* Function prototypes
*/
2008-07-22 14:14:30 +04:00
static int mct_u232_startup ( struct usb_serial * serial ) ;
static void mct_u232_shutdown ( struct usb_serial * serial ) ;
static int mct_u232_open ( struct tty_struct * tty ,
struct usb_serial_port * port , struct file * filp ) ;
static void mct_u232_close ( struct tty_struct * tty ,
struct usb_serial_port * port , struct file * filp ) ;
static void mct_u232_read_int_callback ( struct urb * urb ) ;
static void mct_u232_set_termios ( struct tty_struct * tty ,
struct usb_serial_port * port , struct ktermios * old ) ;
static void mct_u232_break_ctl ( struct tty_struct * tty , int break_state ) ;
static int mct_u232_tiocmget ( struct tty_struct * tty , struct file * file ) ;
static int mct_u232_tiocmset ( struct tty_struct * tty , struct file * file ,
unsigned int set , unsigned int clear ) ;
static void mct_u232_throttle ( struct tty_struct * tty ) ;
static void mct_u232_unthrottle ( struct tty_struct * tty ) ;
2007-05-08 22:00:12 +04:00
2005-04-17 02:20:36 +04:00
/*
* All of the device info needed for the MCT USB - RS232 converter .
*/
static struct usb_device_id id_table_combined [ ] = {
{ USB_DEVICE ( MCT_U232_VID , MCT_U232_PID ) } ,
{ USB_DEVICE ( MCT_U232_VID , MCT_U232_SITECOM_PID ) } ,
{ USB_DEVICE ( MCT_U232_VID , MCT_U232_DU_H3SP_PID ) } ,
{ USB_DEVICE ( MCT_U232_BELKIN_F5U109_VID , MCT_U232_BELKIN_F5U109_PID ) } ,
{ } /* Terminating entry */
} ;
2008-07-22 14:14:30 +04:00
MODULE_DEVICE_TABLE ( usb , id_table_combined ) ;
2005-04-17 02:20:36 +04:00
static struct usb_driver mct_u232_driver = {
. name = " mct_u232 " ,
. probe = usb_serial_probe ,
. disconnect = usb_serial_disconnect ,
. id_table = id_table_combined ,
2005-11-17 00:41:28 +03:00
. no_dynamic_id = 1 ,
2005-04-17 02:20:36 +04:00
} ;
2005-06-21 08:15:16 +04:00
static struct usb_serial_driver mct_u232_device = {
2005-06-21 08:15:16 +04:00
. driver = {
. owner = THIS_MODULE ,
2005-06-21 08:15:16 +04:00
. name = " mct_u232 " ,
2005-06-21 08:15:16 +04:00
} ,
2005-06-21 08:15:16 +04:00
. description = " MCT U232 " ,
2006-12-17 23:50:24 +03:00
. usb_driver = & mct_u232_driver ,
2005-04-17 02:20:36 +04:00
. id_table = id_table_combined ,
. num_ports = 1 ,
. open = mct_u232_open ,
. close = mct_u232_close ,
2007-05-08 22:00:12 +04:00
. throttle = mct_u232_throttle ,
. unthrottle = mct_u232_unthrottle ,
2005-04-17 02:20:36 +04:00
. read_int_callback = mct_u232_read_int_callback ,
. set_termios = mct_u232_set_termios ,
. break_ctl = mct_u232_break_ctl ,
. tiocmget = mct_u232_tiocmget ,
. tiocmset = mct_u232_tiocmset ,
. attach = mct_u232_startup ,
. shutdown = mct_u232_shutdown ,
} ;
struct mct_u232_private {
spinlock_t lock ;
unsigned int control_state ; /* Modem Line Setting (TIOCM) */
unsigned char last_lcr ; /* Line Control Register */
unsigned char last_lsr ; /* Line Status Register */
unsigned char last_msr ; /* Modem Status Register */
2007-05-08 22:00:12 +04:00
unsigned int rx_flags ; /* Throttling flags */
2005-04-17 02:20:36 +04:00
} ;
2007-05-08 22:00:12 +04:00
# define THROTTLED 0x01
2005-04-17 02:20:36 +04:00
/*
* Handle vendor specific USB requests
*/
# define WDR_TIMEOUT 5000 /* default urb timeout */
/*
* Later day 2.6 .0 - test kernels have new baud rates like B230400 which
* we do not know how to support . We ignore them for the moment .
*/
2008-07-22 14:14:30 +04:00
static int mct_u232_calculate_baud_rate ( struct usb_serial * serial ,
speed_t value , speed_t * result )
2005-04-17 02:20:36 +04:00
{
2007-12-14 03:15:29 +03:00
* result = value ;
2005-04-17 02:20:36 +04:00
if ( le16_to_cpu ( serial - > dev - > descriptor . idProduct ) = = MCT_U232_SITECOM_PID
2008-07-22 14:14:30 +04:00
| | le16_to_cpu ( serial - > dev - > descriptor . idProduct ) = = MCT_U232_BELKIN_F5U109_PID ) {
2005-04-17 02:20:36 +04:00
switch ( value ) {
2008-07-22 14:14:30 +04:00
case 300 :
return 0x01 ;
case 600 :
return 0x02 ; /* this one not tested */
case 1200 :
return 0x03 ;
case 2400 :
return 0x04 ;
case 4800 :
return 0x06 ;
case 9600 :
return 0x08 ;
case 19200 :
return 0x09 ;
case 38400 :
return 0x0a ;
case 57600 :
return 0x0b ;
case 115200 :
return 0x0c ;
2005-04-17 02:20:36 +04:00
default :
2007-12-14 03:15:29 +03:00
* result = 9600 ;
2005-04-17 02:20:36 +04:00
return 0x08 ;
}
} else {
2007-12-14 03:15:29 +03:00
/* FIXME: Can we use any divider - should we do
divider = 115200 / value ;
real baud = 115200 / divider */
2005-04-17 02:20:36 +04:00
switch ( value ) {
2007-08-11 01:53:35 +04:00
case 300 : break ;
case 600 : break ;
case 1200 : break ;
case 2400 : break ;
case 4800 : break ;
case 9600 : break ;
case 19200 : break ;
case 38400 : break ;
case 57600 : break ;
case 115200 : break ;
default :
value = 9600 ;
2007-12-14 03:15:29 +03:00
* result = 9600 ;
2005-04-17 02:20:36 +04:00
}
return 115200 / value ;
}
}
2008-07-22 14:09:07 +04:00
static int mct_u232_set_baud_rate ( struct tty_struct * tty ,
struct usb_serial * serial , struct usb_serial_port * port , speed_t value )
2005-04-17 02:20:36 +04:00
{
__le32 divisor ;
2008-07-22 14:14:30 +04:00
int rc ;
unsigned char zero_byte = 0 ;
unsigned char cts_enable_byte = 0 ;
speed_t speed ;
divisor = cpu_to_le32 ( mct_u232_calculate_baud_rate ( serial , value ,
& speed ) ) ;
rc = usb_control_msg ( serial - > dev , usb_sndctrlpipe ( serial - > dev , 0 ) ,
MCT_U232_SET_BAUD_RATE_REQUEST ,
MCT_U232_SET_REQUEST_TYPE ,
0 , 0 , & divisor , MCT_U232_SET_BAUD_RATE_SIZE ,
WDR_TIMEOUT ) ;
2007-12-14 03:15:29 +03:00
if ( rc < 0 ) /*FIXME: What value speed results */
2008-08-21 03:56:34 +04:00
dev_err ( & port - > dev , " Set BAUD RATE %d failed (error = %d) \n " ,
value , rc ) ;
2007-12-14 03:15:29 +03:00
else
2008-07-22 14:09:07 +04:00
tty_encode_baud_rate ( tty , speed , speed ) ;
2005-04-17 02:20:36 +04:00
dbg ( " set_baud_rate: value: 0x%x, divisor: 0x%x " , value , divisor ) ;
/* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which
always sends two extra USB ' device request ' messages after the
' baud rate change ' message . The actual functionality of the
request codes in these messages is not fully understood but these
particular codes are never seen in any operation besides a baud
2007-05-08 22:00:12 +04:00
rate change . Both of these messages send a single byte of data .
In the first message , the value of this byte is always zero .
The second message has been determined experimentally to control
whether data will be transmitted to a device which is not asserting
the ' CTS ' signal . If the second message ' s data byte is zero , data
will be transmitted even if ' CTS ' is not asserted ( i . e . no hardware
2008-07-22 14:14:30 +04:00
flow control ) . if the second message ' s data byte is nonzero ( a
value of 1 is used by this driver ) , data will not be transmitted to
a device which is not asserting ' CTS ' .
2007-05-08 22:00:12 +04:00
*/
2005-04-17 02:20:36 +04:00
rc = usb_control_msg ( serial - > dev , usb_sndctrlpipe ( serial - > dev , 0 ) ,
2008-07-22 14:14:30 +04:00
MCT_U232_SET_UNKNOWN1_REQUEST ,
MCT_U232_SET_REQUEST_TYPE ,
0 , 0 , & zero_byte , MCT_U232_SET_UNKNOWN1_SIZE ,
WDR_TIMEOUT ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 )
2008-08-21 03:56:34 +04:00
dev_err ( & port - > dev , " Sending USB device request code %d "
" failed (error = %d) \n " , MCT_U232_SET_UNKNOWN1_REQUEST ,
rc ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:14:30 +04:00
if ( port & & C_CRTSCTS ( tty ) )
2007-05-08 22:00:12 +04:00
cts_enable_byte = 1 ;
2008-07-22 14:14:30 +04:00
dbg ( " set_baud_rate: send second control message, data = %02X " ,
cts_enable_byte ) ;
2005-04-17 02:20:36 +04:00
rc = usb_control_msg ( serial - > dev , usb_sndctrlpipe ( serial - > dev , 0 ) ,
2008-07-22 14:14:30 +04:00
MCT_U232_SET_CTS_REQUEST ,
MCT_U232_SET_REQUEST_TYPE ,
0 , 0 , & cts_enable_byte , MCT_U232_SET_CTS_SIZE ,
WDR_TIMEOUT ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 )
2008-08-21 03:56:34 +04:00
dev_err ( & port - > dev , " Sending USB device request code %d "
" failed (error = %d) \n " , MCT_U232_SET_CTS_REQUEST , rc ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:14:30 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_set_baud_rate */
static int mct_u232_set_line_ctrl ( struct usb_serial * serial , unsigned char lcr )
{
2008-07-22 14:14:30 +04:00
int rc ;
rc = usb_control_msg ( serial - > dev , usb_sndctrlpipe ( serial - > dev , 0 ) ,
MCT_U232_SET_LINE_CTRL_REQUEST ,
MCT_U232_SET_REQUEST_TYPE ,
0 , 0 , & lcr , MCT_U232_SET_LINE_CTRL_SIZE ,
WDR_TIMEOUT ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 )
2008-08-21 03:56:34 +04:00
dev_err ( & serial - > dev - > dev ,
" Set LINE CTRL 0x%x failed (error = %d) \n " , lcr , rc ) ;
2005-04-17 02:20:36 +04:00
dbg ( " set_line_ctrl: 0x%x " , lcr ) ;
2008-07-22 14:14:30 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_set_line_ctrl */
static int mct_u232_set_modem_ctrl ( struct usb_serial * serial ,
unsigned int control_state )
{
2008-07-22 14:14:30 +04:00
int rc ;
2005-04-17 02:20:36 +04:00
unsigned char mcr = MCT_U232_MCR_NONE ;
if ( control_state & TIOCM_DTR )
mcr | = MCT_U232_MCR_DTR ;
if ( control_state & TIOCM_RTS )
mcr | = MCT_U232_MCR_RTS ;
2008-07-22 14:14:30 +04:00
rc = usb_control_msg ( serial - > dev , usb_sndctrlpipe ( serial - > dev , 0 ) ,
MCT_U232_SET_MODEM_CTRL_REQUEST ,
MCT_U232_SET_REQUEST_TYPE ,
0 , 0 , & mcr , MCT_U232_SET_MODEM_CTRL_SIZE ,
WDR_TIMEOUT ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 )
2008-08-21 03:56:34 +04:00
dev_err ( & serial - > dev - > dev ,
" Set MODEM CTRL 0x%x failed (error = %d) \n " , mcr , rc ) ;
2005-04-17 02:20:36 +04:00
dbg ( " set_modem_ctrl: state=0x%x ==> mcr=0x%x " , control_state , mcr ) ;
2008-07-22 14:14:30 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_set_modem_ctrl */
2008-07-22 14:14:30 +04:00
static int mct_u232_get_modem_stat ( struct usb_serial * serial ,
unsigned char * msr )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:14:30 +04:00
int rc ;
rc = usb_control_msg ( serial - > dev , usb_rcvctrlpipe ( serial - > dev , 0 ) ,
MCT_U232_GET_MODEM_STAT_REQUEST ,
MCT_U232_GET_REQUEST_TYPE ,
0 , 0 , msr , MCT_U232_GET_MODEM_STAT_SIZE ,
WDR_TIMEOUT ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 ) {
2008-08-21 03:56:34 +04:00
dev_err ( & serial - > dev - > dev ,
" Get MODEM STATus failed (error = %d) \n " , rc ) ;
2005-04-17 02:20:36 +04:00
* msr = 0 ;
}
dbg ( " get_modem_stat: 0x%x " , * msr ) ;
2008-07-22 14:14:30 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_get_modem_stat */
2008-07-22 14:14:30 +04:00
static void mct_u232_msr_to_state ( unsigned int * control_state ,
unsigned char msr )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:14:30 +04:00
/* Translate Control Line states */
2005-04-17 02:20:36 +04:00
if ( msr & MCT_U232_MSR_DSR )
* control_state | = TIOCM_DSR ;
else
* control_state & = ~ TIOCM_DSR ;
if ( msr & MCT_U232_MSR_CTS )
* control_state | = TIOCM_CTS ;
else
* control_state & = ~ TIOCM_CTS ;
if ( msr & MCT_U232_MSR_RI )
* control_state | = TIOCM_RI ;
else
* control_state & = ~ TIOCM_RI ;
if ( msr & MCT_U232_MSR_CD )
* control_state | = TIOCM_CD ;
else
* control_state & = ~ TIOCM_CD ;
2008-07-22 14:14:30 +04:00
dbg ( " msr_to_state: msr=0x%x ==> state=0x%x " , msr , * control_state ) ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_msr_to_state */
/*
* Driver ' s tty interface functions
*/
2008-07-22 14:14:30 +04:00
static int mct_u232_startup ( struct usb_serial * serial )
2005-04-17 02:20:36 +04:00
{
struct mct_u232_private * priv ;
struct usb_serial_port * port , * rport ;
2006-02-27 23:29:43 +03:00
priv = kzalloc ( sizeof ( struct mct_u232_private ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! priv )
return - ENOMEM ;
spin_lock_init ( & priv - > lock ) ;
usb_set_serial_port_data ( serial - > port [ 0 ] , priv ) ;
init_waitqueue_head ( & serial - > port [ 0 ] - > write_wait ) ;
/* Puh, that's dirty */
port = serial - > port [ 0 ] ;
rport = serial - > port [ 1 ] ;
2006-11-08 17:36:42 +03:00
/* No unlinking, it wasn't submitted yet. */
usb_free_urb ( port - > read_urb ) ;
2005-04-17 02:20:36 +04:00
port - > read_urb = rport - > interrupt_in_urb ;
rport - > interrupt_in_urb = NULL ;
port - > read_urb - > context = port ;
2008-07-22 14:14:30 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_startup */
2008-07-22 14:14:30 +04:00
static void mct_u232_shutdown ( struct usb_serial * serial )
2005-04-17 02:20:36 +04:00
{
struct mct_u232_private * priv ;
int i ;
2008-07-22 14:14:30 +04:00
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:14:30 +04:00
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
2005-04-17 02:20:36 +04:00
/* My special items, the standard routines free my urbs */
priv = usb_get_serial_port_data ( serial - > port [ i ] ) ;
if ( priv ) {
usb_set_serial_port_data ( serial - > port [ i ] , NULL ) ;
kfree ( priv ) ;
}
}
} /* mct_u232_shutdown */
2008-07-22 14:14:30 +04:00
static int mct_u232_open ( struct tty_struct * tty ,
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port , struct file * filp )
2005-04-17 02:20:36 +04:00
{
struct usb_serial * serial = port - > serial ;
struct mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
int retval = 0 ;
unsigned int control_state ;
unsigned long flags ;
unsigned char last_lcr ;
unsigned char last_msr ;
2008-03-04 03:08:34 +03:00
dbg ( " %s port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
/* Compensate for a hardware bug: although the Sitecom U232-P25
* device reports a maximum output packet size of 32 bytes ,
* it seems to be able to accept only 16 bytes ( and that ' s what
* SniffUSB says too . . . )
*/
2008-07-22 14:14:30 +04:00
if ( le16_to_cpu ( serial - > dev - > descriptor . idProduct )
= = MCT_U232_SITECOM_PID )
2005-04-17 02:20:36 +04:00
port - > bulk_out_size = 16 ;
2008-07-22 14:14:30 +04:00
/* Do a defined restart: the normal serial device seems to
2005-04-17 02:20:36 +04:00
* always turn on DTR and RTS here , so do the same . I ' m not
* sure if this is really necessary . But it should not harm
* either .
*/
spin_lock_irqsave ( & priv - > lock , flags ) ;
2008-07-22 14:09:07 +04:00
if ( tty & & ( tty - > termios - > c_cflag & CBAUD ) )
2005-04-17 02:20:36 +04:00
priv - > control_state = TIOCM_DTR | TIOCM_RTS ;
else
priv - > control_state = 0 ;
2008-07-22 14:14:30 +04:00
priv - > last_lcr = ( MCT_U232_DATA_BITS_8 |
2005-04-17 02:20:36 +04:00
MCT_U232_PARITY_NONE |
MCT_U232_STOP_BITS_1 ) ;
control_state = priv - > control_state ;
last_lcr = priv - > last_lcr ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
mct_u232_set_modem_ctrl ( serial , control_state ) ;
mct_u232_set_line_ctrl ( serial , last_lcr ) ;
/* Read modem status and update control state */
mct_u232_get_modem_stat ( serial , & last_msr ) ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > last_msr = last_msr ;
mct_u232_msr_to_state ( & priv - > control_state , priv - > last_msr ) ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
port - > read_urb - > dev = port - > serial - > dev ;
retval = usb_submit_urb ( port - > read_urb , GFP_KERNEL ) ;
if ( retval ) {
2008-08-21 03:56:34 +04:00
dev_err ( & port - > dev ,
" usb_submit_urb(read bulk) failed pipe 0x%x err %d \n " ,
port - > read_urb - > pipe , retval ) ;
2007-03-29 12:45:17 +04:00
goto error ;
2005-04-17 02:20:36 +04:00
}
port - > interrupt_in_urb - > dev = port - > serial - > dev ;
retval = usb_submit_urb ( port - > interrupt_in_urb , GFP_KERNEL ) ;
2007-03-29 12:45:17 +04:00
if ( retval ) {
usb_kill_urb ( port - > read_urb ) ;
2008-08-21 03:56:34 +04:00
dev_err ( & port - > dev ,
" usb_submit_urb(read int) failed pipe 0x%x err %d " ,
port - > interrupt_in_urb - > pipe , retval ) ;
2007-03-29 12:45:17 +04:00
goto error ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
2007-03-29 12:45:17 +04:00
error :
return retval ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_open */
2008-07-22 14:09:07 +04:00
static void mct_u232_close ( struct tty_struct * tty ,
struct usb_serial_port * port , struct file * filp )
2005-04-17 02:20:36 +04:00
{
2007-05-08 22:00:12 +04:00
unsigned int c_cflag ;
unsigned int control_state ;
struct mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
2008-03-04 03:08:34 +03:00
dbg ( " %s port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:14:30 +04:00
if ( tty ) {
2008-07-22 14:09:07 +04:00
c_cflag = tty - > termios - > c_cflag ;
2008-01-21 19:44:10 +03:00
mutex_lock ( & port - > serial - > disc_mutex ) ;
if ( c_cflag & HUPCL & & ! port - > serial - > disconnected ) {
/* drop DTR and RTS */
spin_lock_irq ( & priv - > lock ) ;
priv - > control_state & = ~ ( TIOCM_DTR | TIOCM_RTS ) ;
control_state = priv - > control_state ;
spin_unlock_irq ( & priv - > lock ) ;
mct_u232_set_modem_ctrl ( port - > serial , control_state ) ;
2007-05-08 22:00:12 +04:00
}
2008-01-21 19:44:10 +03:00
mutex_unlock ( & port - > serial - > disc_mutex ) ;
2007-05-08 22:00:12 +04:00
}
2005-04-17 02:20:36 +04:00
if ( port - > serial - > dev ) {
/* shutdown our urbs */
usb_kill_urb ( port - > write_urb ) ;
usb_kill_urb ( port - > read_urb ) ;
usb_kill_urb ( port - > interrupt_in_urb ) ;
}
} /* mct_u232_close */
2008-07-22 14:14:30 +04:00
static void mct_u232_read_int_callback ( struct urb * urb )
2005-04-17 02:20:36 +04:00
{
2008-02-24 13:41:47 +03:00
struct usb_serial_port * port = urb - > context ;
2005-04-17 02:20:36 +04:00
struct mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
struct usb_serial * serial = port - > serial ;
struct tty_struct * tty ;
unsigned char * data = urb - > transfer_buffer ;
2007-06-16 02:44:13 +04:00
int retval ;
int status = urb - > status ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2007-06-16 02:44:13 +04:00
switch ( status ) {
2005-04-17 02:20:36 +04:00
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
/* this urb is terminated, clean up */
2007-06-16 02:44:13 +04:00
dbg ( " %s - urb shutting down with status: %d " ,
2008-03-04 03:08:34 +03:00
__func__ , status ) ;
2005-04-17 02:20:36 +04:00
return ;
default :
2007-06-16 02:44:13 +04:00
dbg ( " %s - nonzero urb status received: %d " ,
2008-03-04 03:08:34 +03:00
__func__ , status ) ;
2005-04-17 02:20:36 +04:00
goto exit ;
}
if ( ! serial ) {
2008-03-04 03:08:34 +03:00
dbg ( " %s - bad serial pointer, exiting " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2008-07-22 14:14:30 +04:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
usb_serial_debug_data ( debug , & port - > dev , __func__ ,
urb - > actual_length , data ) ;
2005-04-17 02:20:36 +04:00
/*
* Work - a - round : handle the ' usual ' bulk - in pipe here
*/
if ( urb - > transfer_buffer_length > 2 ) {
2008-10-13 13:39:46 +04:00
tty = tty_port_tty_get ( & port - > port ) ;
2005-04-17 02:20:36 +04:00
if ( urb - > actual_length ) {
2008-07-22 14:14:40 +04:00
tty_insert_flip_string ( tty , data , urb - > actual_length ) ;
2005-04-17 02:20:36 +04:00
tty_flip_buffer_push ( tty ) ;
2008-10-13 13:39:46 +04:00
tty_kref_put ( tty ) ;
2005-04-17 02:20:36 +04:00
}
goto exit ;
}
2008-07-22 14:14:30 +04:00
2005-04-17 02:20:36 +04:00
/*
* The interrupt - in pipe signals exceptional conditions ( modem line
* signal changes and errors ) . data [ 0 ] holds MSR , data [ 1 ] holds LSR .
*/
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > last_msr = data [ MCT_U232_MSR_INDEX ] ;
2008-07-22 14:14:30 +04:00
2005-04-17 02:20:36 +04:00
/* Record Control Line states */
mct_u232_msr_to_state ( & priv - > control_state , priv - > last_msr ) ;
#if 0
2008-07-22 14:14:30 +04:00
/* Not yet handled. See belkin_sa.c for further information */
2005-04-17 02:20:36 +04:00
/* Now to report any errors */
priv - > last_lsr = data [ MCT_U232_LSR_INDEX ] ;
/*
* fill in the flip buffer here , but I do not know the relation
* to the current / next receive buffer or characters . I need
* to look in to this before committing any code .
*/
if ( priv - > last_lsr & MCT_U232_LSR_ERR ) {
2008-10-13 13:39:46 +04:00
tty = tty_port_tty_get ( & port - > port ) ;
2005-04-17 02:20:36 +04:00
/* Overrun Error */
if ( priv - > last_lsr & MCT_U232_LSR_OE ) {
}
/* Parity Error */
if ( priv - > last_lsr & MCT_U232_LSR_PE ) {
}
/* Framing Error */
if ( priv - > last_lsr & MCT_U232_LSR_FE ) {
}
/* Break Indicator */
if ( priv - > last_lsr & MCT_U232_LSR_BI ) {
}
2008-10-13 13:39:46 +04:00
tty_kref_put ( tty ) ;
2005-04-17 02:20:36 +04:00
}
# endif
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
exit :
2008-07-22 14:14:30 +04:00
retval = usb_submit_urb ( urb , GFP_ATOMIC ) ;
2007-06-16 02:44:13 +04:00
if ( retval )
2008-08-21 03:56:34 +04:00
dev_err ( & port - > dev ,
" %s - usb_submit_urb failed with result %d \n " ,
__func__ , retval ) ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_read_int_callback */
2008-07-22 14:14:30 +04:00
static void mct_u232_set_termios ( struct tty_struct * tty ,
struct usb_serial_port * port ,
struct ktermios * old_termios )
2005-04-17 02:20:36 +04:00
{
struct usb_serial * serial = port - > serial ;
struct mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
2008-07-22 14:09:07 +04:00
struct ktermios * termios = tty - > termios ;
2007-12-14 03:15:29 +03:00
unsigned int cflag = termios - > c_cflag ;
2005-04-17 02:20:36 +04:00
unsigned int old_cflag = old_termios - > c_cflag ;
unsigned long flags ;
2007-05-08 22:00:12 +04:00
unsigned int control_state ;
2005-04-17 02:20:36 +04:00
unsigned char last_lcr ;
/* get a local copy of the current port settings */
spin_lock_irqsave ( & priv - > lock , flags ) ;
control_state = priv - > control_state ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
last_lcr = 0 ;
/*
* Update baud rate .
* Do not attempt to cache old rates and skip settings ,
* disconnects screw such tricks up completely .
* Premature optimization is the root of all evil .
*/
2008-07-22 14:14:30 +04:00
/* reassert DTR and RTS on transition from B0 */
2005-04-17 02:20:36 +04:00
if ( ( old_cflag & CBAUD ) = = B0 ) {
2008-03-04 03:08:34 +03:00
dbg ( " %s: baud was B0 " , __func__ ) ;
2007-05-08 22:00:12 +04:00
control_state | = TIOCM_DTR | TIOCM_RTS ;
2005-04-17 02:20:36 +04:00
mct_u232_set_modem_ctrl ( serial , control_state ) ;
}
2008-07-22 14:09:07 +04:00
mct_u232_set_baud_rate ( tty , serial , port , tty_get_baud_rate ( tty ) ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:14:30 +04:00
if ( ( cflag & CBAUD ) = = B0 ) {
2008-03-04 03:08:34 +03:00
dbg ( " %s: baud is B0 " , __func__ ) ;
2005-04-17 02:20:36 +04:00
/* Drop RTS and DTR */
control_state & = ~ ( TIOCM_DTR | TIOCM_RTS ) ;
2008-07-22 14:14:30 +04:00
mct_u232_set_modem_ctrl ( serial , control_state ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Update line control register ( LCR )
*/
/* set the parity */
if ( cflag & PARENB )
last_lcr | = ( cflag & PARODD ) ?
MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN ;
else
last_lcr | = MCT_U232_PARITY_NONE ;
/* set the number of data bits */
switch ( cflag & CSIZE ) {
case CS5 :
last_lcr | = MCT_U232_DATA_BITS_5 ; break ;
case CS6 :
last_lcr | = MCT_U232_DATA_BITS_6 ; break ;
case CS7 :
last_lcr | = MCT_U232_DATA_BITS_7 ; break ;
case CS8 :
last_lcr | = MCT_U232_DATA_BITS_8 ; break ;
default :
2008-08-21 03:56:34 +04:00
dev_err ( & port - > dev ,
" CSIZE was not CS5-CS8, using default of 8 \n " ) ;
2005-04-17 02:20:36 +04:00
last_lcr | = MCT_U232_DATA_BITS_8 ;
break ;
}
2007-12-14 03:15:29 +03:00
termios - > c_cflag & = ~ CMSPAR ;
2005-04-17 02:20:36 +04:00
/* set the number of stop bits */
last_lcr | = ( cflag & CSTOPB ) ?
MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1 ;
mct_u232_set_line_ctrl ( serial , last_lcr ) ;
/* save off the modified port settings */
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > control_state = control_state ;
priv - > last_lcr = last_lcr ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
} /* mct_u232_set_termios */
2008-07-22 14:09:07 +04:00
static void mct_u232_break_ctl ( struct tty_struct * tty , int break_state )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
struct usb_serial * serial = port - > serial ;
struct mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
unsigned char lcr ;
unsigned long flags ;
2008-03-04 03:08:34 +03:00
dbg ( " %sstate=%d " , __func__ , break_state ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
lcr = priv - > last_lcr ;
if ( break_state )
lcr | = MCT_U232_SET_BREAK ;
2009-01-02 16:48:56 +03:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
mct_u232_set_line_ctrl ( serial , lcr ) ;
} /* mct_u232_break_ctl */
2008-07-22 14:09:07 +04:00
static int mct_u232_tiocmget ( struct tty_struct * tty , struct file * file )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
struct mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
unsigned int control_state ;
unsigned long flags ;
2008-07-22 14:14:30 +04:00
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
control_state = priv - > control_state ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return control_state ;
}
2008-07-22 14:09:07 +04:00
static int mct_u232_tiocmset ( struct tty_struct * tty , struct file * file ,
2005-04-17 02:20:36 +04:00
unsigned int set , unsigned int clear )
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
struct usb_serial * serial = port - > serial ;
struct mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
unsigned int control_state ;
unsigned long flags ;
2008-07-22 14:14:30 +04:00
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
control_state = priv - > control_state ;
if ( set & TIOCM_RTS )
control_state | = TIOCM_RTS ;
if ( set & TIOCM_DTR )
control_state | = TIOCM_DTR ;
if ( clear & TIOCM_RTS )
control_state & = ~ TIOCM_RTS ;
if ( clear & TIOCM_DTR )
control_state & = ~ TIOCM_DTR ;
priv - > control_state = control_state ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return mct_u232_set_modem_ctrl ( serial , control_state ) ;
}
2008-07-22 14:09:07 +04:00
static void mct_u232_throttle ( struct tty_struct * tty )
2007-05-08 22:00:12 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2007-05-08 22:00:12 +04:00
struct mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
unsigned int control_state ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2007-05-08 22:00:12 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > rx_flags | = THROTTLED ;
if ( C_CRTSCTS ( tty ) ) {
2008-07-22 14:09:07 +04:00
priv - > control_state & = ~ TIOCM_RTS ;
control_state = priv - > control_state ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
( void ) mct_u232_set_modem_ctrl ( port - > serial , control_state ) ;
2007-05-08 22:00:12 +04:00
} else {
2008-07-22 14:09:07 +04:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2007-05-08 22:00:12 +04:00
}
}
2008-07-22 14:09:07 +04:00
static void mct_u232_unthrottle ( struct tty_struct * tty )
2007-05-08 22:00:12 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2007-05-08 22:00:12 +04:00
struct mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
unsigned int control_state ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2007-05-08 22:00:12 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( ( priv - > rx_flags & THROTTLED ) & & C_CRTSCTS ( tty ) ) {
2008-07-22 14:09:07 +04:00
priv - > rx_flags & = ~ THROTTLED ;
priv - > control_state | = TIOCM_RTS ;
control_state = priv - > control_state ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
( void ) mct_u232_set_modem_ctrl ( port - > serial , control_state ) ;
2007-05-08 22:00:12 +04:00
} else {
2008-07-22 14:09:07 +04:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2007-05-08 22:00:12 +04:00
}
}
2005-04-17 02:20:36 +04:00
2008-07-22 14:14:30 +04:00
static int __init mct_u232_init ( void )
2005-04-17 02:20:36 +04:00
{
int retval ;
retval = usb_serial_register ( & mct_u232_device ) ;
if ( retval )
goto failed_usb_serial_register ;
retval = usb_register ( & mct_u232_driver ) ;
if ( retval )
goto failed_usb_register ;
2008-08-19 00:21:04 +04:00
printk ( KERN_INFO KBUILD_MODNAME " : " DRIVER_VERSION " : "
DRIVER_DESC " \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
failed_usb_register :
usb_serial_deregister ( & mct_u232_device ) ;
failed_usb_serial_register :
return retval ;
}
2008-07-22 14:14:30 +04:00
static void __exit mct_u232_exit ( void )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:14:30 +04:00
usb_deregister ( & mct_u232_driver ) ;
usb_serial_deregister ( & mct_u232_device ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:14:30 +04:00
module_init ( mct_u232_init ) ;
2005-04-17 02:20:36 +04:00
module_exit ( mct_u232_exit ) ;
2008-07-22 14:14:30 +04:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
module_param ( debug , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Debug enabled or not " ) ;