2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0+
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 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 .
*/
# include <linux/kernel.h>
# include <linux/errno.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>
2009-12-08 06:29:05 +03:00
# include <asm/unaligned.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>
2011-01-09 09:00:11 +03:00
# include <linux/serial.h>
2005-04-17 02:20:36 +04:00
# include "mct_u232.h"
# define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
# define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
/*
* Function prototypes
*/
2012-10-25 12:29:13 +04:00
static int mct_u232_port_probe ( struct usb_serial_port * port ) ;
static int mct_u232_port_remove ( struct usb_serial_port * remove ) ;
2009-09-20 00:13:26 +04:00
static int mct_u232_open ( struct tty_struct * tty , struct usb_serial_port * port ) ;
2009-06-11 15:26:29 +04:00
static void mct_u232_close ( struct usb_serial_port * port ) ;
static void mct_u232_dtr_rts ( struct usb_serial_port * port , int on ) ;
2008-07-22 14:14:30 +04:00
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 ) ;
2011-02-14 19:26:14 +03:00
static int mct_u232_tiocmget ( struct tty_struct * tty ) ;
2011-02-14 19:26:50 +03:00
static int mct_u232_tiocmset ( struct tty_struct * tty ,
2008-07-22 14:14:30 +04:00
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 .
*/
2012-05-09 02:46:14 +04:00
static const struct usb_device_id id_table [ ] = {
2005-04-17 02:20:36 +04:00
{ 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 */
} ;
2012-05-09 02:46:14 +04:00
MODULE_DEVICE_TABLE ( usb , id_table ) ;
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 " ,
2012-05-09 02:46:14 +04:00
. id_table = id_table ,
2005-04-17 02:20:36 +04:00
. num_ports = 1 ,
. open = mct_u232_open ,
. close = mct_u232_close ,
2009-06-11 15:26:29 +04:00
. dtr_rts = mct_u232_dtr_rts ,
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 ,
2013-03-21 15:37:13 +04:00
. tiocmiwait = usb_serial_generic_tiocmiwait ,
2012-10-25 12:29:13 +04:00
. port_probe = mct_u232_port_probe ,
. port_remove = mct_u232_port_remove ,
2013-03-21 15:37:12 +04:00
. get_icount = usb_serial_generic_get_icount ,
2005-04-17 02:20:36 +04:00
} ;
2012-02-23 23:57:09 +04:00
static struct usb_serial_driver * const serial_drivers [ ] = {
& mct_u232_device , NULL
} ;
2005-04-17 02:20:36 +04:00
struct mct_u232_private {
2013-04-16 20:01:27 +04:00
struct urb * read_urb ;
2005-04-17 02:20:36 +04:00
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
{
2009-12-08 06:29:05 +03:00
unsigned int divisor ;
2008-07-22 14:14:30 +04:00
int rc ;
2009-12-08 06:29:05 +03:00
unsigned char * buf ;
2008-07-22 14:14:30 +04:00
unsigned char cts_enable_byte = 0 ;
speed_t speed ;
2009-12-08 06:29:05 +03:00
buf = kmalloc ( MCT_U232_MAX_SIZE , GFP_KERNEL ) ;
if ( buf = = NULL )
return - ENOMEM ;
2008-07-22 14:14:30 +04:00
2009-12-08 06:29:05 +03:00
divisor = mct_u232_calculate_baud_rate ( serial , value , & speed ) ;
2017-05-11 12:41:20 +03:00
put_unaligned_le32 ( divisor , buf ) ;
2008-07-22 14:14:30 +04:00
rc = usb_control_msg ( serial - > dev , usb_sndctrlpipe ( serial - > dev , 0 ) ,
MCT_U232_SET_BAUD_RATE_REQUEST ,
MCT_U232_SET_REQUEST_TYPE ,
2009-12-08 06:29:05 +03:00
0 , 0 , buf , MCT_U232_SET_BAUD_RATE_SIZE ,
2008-07-22 14:14:30 +04:00
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 ) ;
2012-09-15 02:08:30 +04:00
dev_dbg ( & port - > dev , " set_baud_rate: value: 0x%x, divisor: 0x%x \n " , value , divisor ) ;
2005-04-17 02:20:36 +04:00
/* 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
2009-12-08 06:29:05 +03:00
buf [ 0 ] = 0 ;
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 ,
2009-12-08 06:29:05 +03:00
0 , 0 , buf , MCT_U232_SET_UNKNOWN1_SIZE ,
2008-07-22 14:14:30 +04:00
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 ;
2012-09-15 02:08:30 +04:00
dev_dbg ( & port - > dev , " set_baud_rate: send second control message, data = %02X \n " ,
cts_enable_byte ) ;
2009-12-08 06:29:05 +03:00
buf [ 0 ] = 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 ,
2009-12-08 06:29:05 +03:00
0 , 0 , buf , MCT_U232_SET_CTS_SIZE ,
2008-07-22 14:14:30 +04:00
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
2009-12-08 06:29:05 +03:00
kfree ( buf ) ;
2008-07-22 14:14:30 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_set_baud_rate */
2012-09-15 02:08:30 +04:00
static int mct_u232_set_line_ctrl ( struct usb_serial_port * port ,
unsigned char lcr )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:14:30 +04:00
int rc ;
2009-12-08 06:29:05 +03:00
unsigned char * buf ;
buf = kmalloc ( MCT_U232_MAX_SIZE , GFP_KERNEL ) ;
if ( buf = = NULL )
return - ENOMEM ;
buf [ 0 ] = lcr ;
2012-09-15 02:08:30 +04:00
rc = usb_control_msg ( port - > serial - > dev , usb_sndctrlpipe ( port - > serial - > dev , 0 ) ,
2008-07-22 14:14:30 +04:00
MCT_U232_SET_LINE_CTRL_REQUEST ,
MCT_U232_SET_REQUEST_TYPE ,
2009-12-08 06:29:05 +03:00
0 , 0 , buf , MCT_U232_SET_LINE_CTRL_SIZE ,
2008-07-22 14:14:30 +04:00
WDR_TIMEOUT ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 )
2012-09-15 02:08:30 +04:00
dev_err ( & port - > dev , " Set LINE CTRL 0x%x failed (error = %d) \n " , lcr , rc ) ;
dev_dbg ( & port - > dev , " set_line_ctrl: 0x%x \n " , lcr ) ;
2009-12-08 06:29:05 +03:00
kfree ( buf ) ;
2008-07-22 14:14:30 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_set_line_ctrl */
2012-09-15 02:08:30 +04:00
static int mct_u232_set_modem_ctrl ( struct usb_serial_port * port ,
2005-04-17 02:20:36 +04:00
unsigned int control_state )
{
2008-07-22 14:14:30 +04:00
int rc ;
2009-12-08 06:29:05 +03:00
unsigned char mcr ;
unsigned char * buf ;
buf = kmalloc ( MCT_U232_MAX_SIZE , GFP_KERNEL ) ;
if ( buf = = NULL )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2009-12-08 06:29:05 +03:00
mcr = MCT_U232_MCR_NONE ;
2005-04-17 02:20:36 +04:00
if ( control_state & TIOCM_DTR )
mcr | = MCT_U232_MCR_DTR ;
if ( control_state & TIOCM_RTS )
mcr | = MCT_U232_MCR_RTS ;
2009-12-08 06:29:05 +03:00
buf [ 0 ] = mcr ;
2012-09-15 02:08:30 +04:00
rc = usb_control_msg ( port - > serial - > dev , usb_sndctrlpipe ( port - > serial - > dev , 0 ) ,
2008-07-22 14:14:30 +04:00
MCT_U232_SET_MODEM_CTRL_REQUEST ,
MCT_U232_SET_REQUEST_TYPE ,
2009-12-08 06:29:05 +03:00
0 , 0 , buf , MCT_U232_SET_MODEM_CTRL_SIZE ,
2008-07-22 14:14:30 +04:00
WDR_TIMEOUT ) ;
2012-05-22 23:45:13 +04:00
kfree ( buf ) ;
2012-09-15 02:08:30 +04:00
dev_dbg ( & port - > dev , " set_modem_ctrl: state=0x%x ==> mcr=0x%x \n " , control_state , mcr ) ;
2005-04-17 02:20:36 +04:00
2012-05-22 23:45:13 +04:00
if ( rc < 0 ) {
2012-09-15 02:08:30 +04:00
dev_err ( & port - > dev , " Set MODEM CTRL 0x%x failed (error = %d) \n " , mcr , rc ) ;
2012-05-22 23:45:13 +04:00
return rc ;
}
return 0 ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_set_modem_ctrl */
2012-09-15 02:08:30 +04:00
static int mct_u232_get_modem_stat ( struct usb_serial_port * port ,
unsigned char * msr )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:14:30 +04:00
int rc ;
2009-12-08 06:29:05 +03:00
unsigned char * buf ;
buf = kmalloc ( MCT_U232_MAX_SIZE , GFP_KERNEL ) ;
if ( buf = = NULL ) {
* msr = 0 ;
return - ENOMEM ;
}
2012-09-15 02:08:30 +04:00
rc = usb_control_msg ( port - > serial - > dev , usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
2008-07-22 14:14:30 +04:00
MCT_U232_GET_MODEM_STAT_REQUEST ,
MCT_U232_GET_REQUEST_TYPE ,
2009-12-08 06:29:05 +03:00
0 , 0 , buf , MCT_U232_GET_MODEM_STAT_SIZE ,
2008-07-22 14:14:30 +04:00
WDR_TIMEOUT ) ;
2017-01-12 16:56:16 +03:00
if ( rc < MCT_U232_GET_MODEM_STAT_SIZE ) {
2012-09-15 02:08:30 +04:00
dev_err ( & port - > dev , " Get MODEM STATus failed (error = %d) \n " , rc ) ;
2017-01-12 16:56:16 +03:00
if ( rc > = 0 )
rc = - EIO ;
2005-04-17 02:20:36 +04:00
* msr = 0 ;
2009-12-08 06:29:05 +03:00
} else {
* msr = buf [ 0 ] ;
2005-04-17 02:20:36 +04:00
}
2012-09-15 02:08:30 +04:00
dev_dbg ( & port - > dev , " get_modem_stat: 0x%x \n " , * msr ) ;
2009-12-08 06:29:05 +03:00
kfree ( buf ) ;
2008-07-22 14:14:30 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_get_modem_stat */
2011-01-09 09:00:11 +03:00
static void mct_u232_msr_to_icount ( struct async_icount * icount ,
unsigned char msr )
{
/* Translate Control Line states */
if ( msr & MCT_U232_MSR_DDSR )
icount - > dsr + + ;
if ( msr & MCT_U232_MSR_DCTS )
icount - > cts + + ;
if ( msr & MCT_U232_MSR_DRI )
icount - > rng + + ;
if ( msr & MCT_U232_MSR_DCD )
icount - > dcd + + ;
} /* mct_u232_msr_to_icount */
2012-09-15 02:08:30 +04:00
static void mct_u232_msr_to_state ( struct usb_serial_port * port ,
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 ;
2012-09-15 02:08:30 +04:00
dev_dbg ( & port - > dev , " msr_to_state: msr=0x%x ==> state=0x%x \n " , msr , * control_state ) ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_msr_to_state */
/*
* Driver ' s tty interface functions
*/
2012-10-25 12:29:13 +04:00
static int mct_u232_port_probe ( struct usb_serial_port * port )
{
2016-03-31 19:04:24 +03:00
struct usb_serial * serial = port - > serial ;
2012-10-25 12:29:13 +04:00
struct mct_u232_private * priv ;
2016-03-31 19:04:24 +03:00
/* check first to simplify error handling */
if ( ! serial - > port [ 1 ] | | ! serial - > port [ 1 ] - > interrupt_in_urb ) {
dev_err ( & port - > dev , " expected endpoint missing \n " ) ;
return - ENODEV ;
}
2012-10-25 12:29:13 +04:00
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2013-04-16 20:01:27 +04:00
/* Use second interrupt-in endpoint for reading. */
2016-03-31 19:04:24 +03:00
priv - > read_urb = serial - > port [ 1 ] - > interrupt_in_urb ;
2013-04-16 20:01:27 +04:00
priv - > read_urb - > context = port ;
2012-10-25 12:29:13 +04:00
spin_lock_init ( & priv - > lock ) ;
usb_set_serial_port_data ( port , priv ) ;
return 0 ;
}
static int mct_u232_port_remove ( struct usb_serial_port * port )
2005-04-17 02:20:36 +04:00
{
struct mct_u232_private * priv ;
2008-07-22 14:14:30 +04:00
2012-10-25 12:29:13 +04:00
priv = usb_get_serial_port_data ( port ) ;
kfree ( priv ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2009-09-20 00:13:26 +04:00
static int mct_u232_open ( struct tty_struct * tty , struct usb_serial_port * port )
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 ;
/* 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 ) ;
2016-01-11 07:36:15 +03:00
if ( tty & & C_BAUD ( tty ) )
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 ) ;
2012-09-15 02:08:30 +04:00
mct_u232_set_modem_ctrl ( port , control_state ) ;
mct_u232_set_line_ctrl ( port , last_lcr ) ;
2005-04-17 02:20:36 +04:00
/* Read modem status and update control state */
2012-09-15 02:08:30 +04:00
mct_u232_get_modem_stat ( port , & last_msr ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > last_msr = last_msr ;
2012-09-15 02:08:30 +04:00
mct_u232_msr_to_state ( port , & priv - > control_state , priv - > last_msr ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2013-04-16 20:01:27 +04:00
retval = usb_submit_urb ( priv - > read_urb , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-08-21 03:56:34 +04:00
dev_err ( & port - > dev ,
2013-04-16 20:01:27 +04:00
" usb_submit_urb(read) failed pipe 0x%x err %d \n " ,
2008-08-21 03:56:34 +04:00
port - > read_urb - > pipe , retval ) ;
2007-03-29 12:45:17 +04:00
goto error ;
2005-04-17 02:20:36 +04:00
}
retval = usb_submit_urb ( port - > interrupt_in_urb , GFP_KERNEL ) ;
2007-03-29 12:45:17 +04:00
if ( retval ) {
2013-04-16 20:01:27 +04:00
usb_kill_urb ( priv - > 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 */
2009-06-11 15:26:29 +04:00
static void mct_u232_dtr_rts ( struct usb_serial_port * port , int on )
2005-04-17 02:20:36 +04:00
{
2007-05-08 22:00:12 +04:00
unsigned int control_state ;
struct mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
2005-04-17 02:20:36 +04:00
2013-02-13 20:53:28 +04:00
spin_lock_irq ( & priv - > lock ) ;
if ( on )
priv - > control_state | = TIOCM_DTR | TIOCM_RTS ;
else
priv - > control_state & = ~ ( TIOCM_DTR | TIOCM_RTS ) ;
control_state = priv - > control_state ;
spin_unlock_irq ( & priv - > lock ) ;
mct_u232_set_modem_ctrl ( port , control_state ) ;
2009-06-11 15:26:29 +04:00
}
2007-05-08 22:00:12 +04:00
2009-06-11 15:26:29 +04:00
static void mct_u232_close ( struct usb_serial_port * port )
{
2013-04-16 20:01:27 +04:00
struct mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
usb_kill_urb ( priv - > read_urb ) ;
2012-10-25 12:29:14 +04:00
usb_kill_urb ( port - > interrupt_in_urb ) ;
usb_serial_generic_close ( port ) ;
2005-04-17 02:20:36 +04:00
} /* 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 ) ;
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 */
2012-09-15 02:08:30 +04:00
dev_dbg ( & port - > dev , " %s - urb shutting down with status: %d \n " ,
__func__ , status ) ;
2005-04-17 02:20:36 +04:00
return ;
default :
2012-09-15 02:08:30 +04:00
dev_dbg ( & port - > dev , " %s - nonzero urb status received: %d \n " ,
__func__ , status ) ;
2005-04-17 02:20:36 +04:00
goto exit ;
}
2012-09-18 12:58:57 +04:00
usb_serial_debug_data ( & 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 ) {
if ( urb - > actual_length ) {
2013-01-03 18:53:06 +04:00
tty_insert_flip_string ( & port - > port , data ,
urb - > actual_length ) ;
tty_flip_buffer_push ( & port - > port ) ;
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 */
2012-09-15 02:08:30 +04:00
mct_u232_msr_to_state ( port , & priv - > control_state , priv - > last_msr ) ;
2005-04-17 02:20:36 +04:00
2013-03-21 15:37:12 +04:00
mct_u232_msr_to_icount ( & port - > icount , priv - > last_msr ) ;
2011-01-09 09:00:11 +03:00
2005-04-17 02:20:36 +04:00
#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
2013-03-21 15:37:13 +04:00
wake_up_interruptible ( & port - > port . delta_msr_wait ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
2012-07-14 18:31:47 +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 ) {
2012-09-15 02:08:30 +04:00
dev_dbg ( & port - > dev , " %s: baud was B0 \n " , __func__ ) ;
2007-05-08 22:00:12 +04:00
control_state | = TIOCM_DTR | TIOCM_RTS ;
2012-09-15 02:08:30 +04:00
mct_u232_set_modem_ctrl ( port , control_state ) ;
2005-04-17 02:20:36 +04:00
}
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 ) {
2012-09-15 02:08:30 +04:00
dev_dbg ( & port - > dev , " %s: baud is B0 \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
/* Drop RTS and DTR */
control_state & = ~ ( TIOCM_DTR | TIOCM_RTS ) ;
2012-09-15 02:08:30 +04:00
mct_u232_set_modem_ctrl ( port , 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 ;
2012-09-15 02:08:30 +04:00
mct_u232_set_line_ctrl ( port , last_lcr ) ;
2005-04-17 02:20:36 +04:00
/* 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 mct_u232_private * priv = usb_get_serial_port_data ( port ) ;
unsigned char lcr ;
unsigned long flags ;
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
2012-09-15 02:08:30 +04:00
mct_u232_set_line_ctrl ( port , lcr ) ;
2005-04-17 02:20:36 +04:00
} /* mct_u232_break_ctl */
2011-02-14 19:26:14 +03:00
static int mct_u232_tiocmget ( struct tty_struct * tty )
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
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 ;
}
2011-02-14 19:26:50 +03:00
static int mct_u232_tiocmset ( struct tty_struct * tty ,
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 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
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 ) ;
2012-09-15 02:08:30 +04:00
return mct_u232_set_modem_ctrl ( port , control_state ) ;
2005-04-17 02:20:36 +04:00
}
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 int control_state ;
2009-10-07 12:50:23 +04:00
spin_lock_irq ( & priv - > lock ) ;
2007-05-08 22:00:12 +04:00
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 ;
2009-10-07 12:50:23 +04:00
spin_unlock_irq ( & priv - > lock ) ;
2012-09-15 02:08:30 +04:00
mct_u232_set_modem_ctrl ( port , control_state ) ;
2007-05-08 22:00:12 +04:00
} else {
2009-10-07 12:50:23 +04:00
spin_unlock_irq ( & priv - > lock ) ;
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 int control_state ;
2009-10-07 12:50:23 +04:00
spin_lock_irq ( & priv - > lock ) ;
2007-05-08 22:00:12 +04:00
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 ;
2009-10-07 12:50:23 +04:00
spin_unlock_irq ( & priv - > lock ) ;
2012-09-15 02:08:30 +04:00
mct_u232_set_modem_ctrl ( port , control_state ) ;
2007-05-08 22:00:12 +04:00
} else {
2009-10-07 12:50:23 +04:00
spin_unlock_irq ( & priv - > lock ) ;
2007-05-08 22:00:12 +04:00
}
}
2005-04-17 02:20:36 +04:00
2012-05-09 02:46:14 +04:00
module_usb_serial_driver ( serial_drivers , id_table ) ;
2005-04-17 02:20:36 +04:00
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 " ) ;