2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0
2010-07-29 11:05:41 -04:00
/*
* usb - serial driver for Quatech SSU - 100
*
* based on ftdi_sio . c and the original serqt_usb . c from Quatech
*
*/
# 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/serial.h>
# include <linux/usb.h>
# include <linux/usb/serial.h>
2010-08-05 17:01:07 -04:00
# include <linux/serial_reg.h>
2010-07-29 11:05:41 -04:00
# include <linux/uaccess.h>
# define QT_OPEN_CLOSE_CHANNEL 0xca
# define QT_SET_GET_DEVICE 0xc2
# define QT_SET_GET_REGISTER 0xc0
# define QT_GET_SET_PREBUF_TRIG_LVL 0xcc
# define QT_SET_ATF 0xcd
# define QT_GET_SET_UART 0xc1
# define QT_TRANSFER_IN 0xc0
# define QT_HW_FLOW_CONTROL_MASK 0xc5
# define QT_SW_FLOW_CONTROL_MASK 0xc6
# define SERIAL_MSR_MASK 0xf0
2010-08-05 17:01:07 -04:00
# define SERIAL_CRTSCTS ((UART_MCR_RTS << 8) | UART_MSR_CTS)
2010-07-29 11:05:41 -04:00
2010-08-05 17:01:07 -04:00
# define SERIAL_EVEN_PARITY (UART_LCR_PARITY | UART_LCR_EPAR)
2010-07-29 11:05:41 -04:00
# define MAX_BAUD_RATE 460800
# define ATC_DISABLED 0x00
# define DUPMODE_BITS 0xc0
# define RR_BITS 0x03
# define LOOPMODE_BITS 0x41
# define RS232_MODE 0x00
# define RTSCTS_TO_CONNECTOR 0x40
# define CLKS_X4 0x02
# define FULLPWRBIT 0x00000080
# define NEXT_BOARD_POWER_BIT 0x00000004
# define DRIVER_DESC "Quatech SSU-100 USB to Serial Driver"
# define USB_VENDOR_ID_QUATECH 0x061d /* Quatech VID */
# define QUATECH_SSU100 0xC020 /* SSU100 */
static const struct usb_device_id id_table [ ] = {
{ USB_DEVICE ( USB_VENDOR_ID_QUATECH , QUATECH_SSU100 ) } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , id_table ) ;
struct ssu100_port_private {
2010-08-05 17:01:05 -04:00
spinlock_t status_lock ;
2010-07-29 11:05:41 -04:00
u8 shadowLSR ;
u8 shadowMSR ;
} ;
static inline int ssu100_control_msg ( struct usb_device * dev ,
u8 request , u16 data , u16 index )
{
return usb_control_msg ( dev , usb_sndctrlpipe ( dev , 0 ) ,
request , 0x40 , data , index ,
NULL , 0 , 300 ) ;
}
static inline int ssu100_setdevice ( struct usb_device * dev , u8 * data )
{
u16 x = ( ( u16 ) ( data [ 1 ] < < 8 ) | ( u16 ) ( data [ 0 ] ) ) ;
return ssu100_control_msg ( dev , QT_SET_GET_DEVICE , x , 0 ) ;
}
static inline int ssu100_getdevice ( struct usb_device * dev , u8 * data )
{
2017-01-12 14:56:22 +01:00
int ret ;
ret = usb_control_msg ( dev , usb_rcvctrlpipe ( dev , 0 ) ,
QT_SET_GET_DEVICE , 0xc0 , 0 , 0 ,
data , 3 , 300 ) ;
if ( ret < 3 ) {
if ( ret > = 0 )
ret = - EIO ;
}
return ret ;
2010-07-29 11:05:41 -04:00
}
static inline int ssu100_getregister ( struct usb_device * dev ,
unsigned short uart ,
unsigned short reg ,
u8 * data )
{
2017-01-12 14:56:22 +01:00
int ret ;
ret = usb_control_msg ( dev , usb_rcvctrlpipe ( dev , 0 ) ,
QT_SET_GET_REGISTER , 0xc0 , reg ,
uart , data , sizeof ( * data ) , 300 ) ;
if ( ret < sizeof ( * data ) ) {
if ( ret > = 0 )
ret = - EIO ;
}
2010-07-29 11:05:41 -04:00
2017-01-12 14:56:22 +01:00
return ret ;
2010-07-29 11:05:41 -04:00
}
static inline int ssu100_setregister ( struct usb_device * dev ,
unsigned short uart ,
2010-08-05 17:01:08 -04:00
unsigned short reg ,
2010-07-29 11:05:41 -04:00
u16 data )
{
2010-08-05 17:01:08 -04:00
u16 value = ( data < < 8 ) | reg ;
2010-07-29 11:05:41 -04:00
return usb_control_msg ( dev , usb_sndctrlpipe ( dev , 0 ) ,
QT_SET_GET_REGISTER , 0x40 , value , uart ,
NULL , 0 , 300 ) ;
}
# define set_mctrl(dev, set) update_mctrl((dev), (set), 0)
# define clear_mctrl(dev, clear) update_mctrl((dev), 0, (clear))
/* these do not deal with device that have more than 1 port */
static inline int update_mctrl ( struct usb_device * dev , unsigned int set ,
unsigned int clear )
{
unsigned urb_value ;
int result ;
if ( ( ( set | clear ) & ( TIOCM_DTR | TIOCM_RTS ) ) = = 0 ) {
2012-09-14 11:50:35 -07:00
dev_dbg ( & dev - > dev , " %s - DTR|RTS not being set|cleared \n " , __func__ ) ;
2010-07-29 11:05:41 -04:00
return 0 ; /* no change */
}
clear & = ~ set ; /* 'set' takes precedence over 'clear' */
urb_value = 0 ;
if ( set & TIOCM_DTR )
2010-08-05 17:01:07 -04:00
urb_value | = UART_MCR_DTR ;
2010-07-29 11:05:41 -04:00
if ( set & TIOCM_RTS )
2010-08-05 17:01:07 -04:00
urb_value | = UART_MCR_RTS ;
2010-07-29 11:05:41 -04:00
2010-08-05 17:01:08 -04:00
result = ssu100_setregister ( dev , 0 , UART_MCR , urb_value ) ;
2010-07-29 11:05:41 -04:00
if ( result < 0 )
2012-09-14 11:50:35 -07:00
dev_dbg ( & dev - > dev , " %s Error from MODEM_CTRL urb \n " , __func__ ) ;
2010-07-29 11:05:41 -04:00
return result ;
}
static int ssu100_initdevice ( struct usb_device * dev )
{
u8 * data ;
int result = 0 ;
data = kzalloc ( 3 , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
result = ssu100_getdevice ( dev , data ) ;
if ( result < 0 ) {
2012-09-14 11:50:35 -07:00
dev_dbg ( & dev - > dev , " %s - get_device failed %i \n " , __func__ , result ) ;
2010-07-29 11:05:41 -04:00
goto out ;
}
data [ 1 ] & = ~ FULLPWRBIT ;
result = ssu100_setdevice ( dev , data ) ;
if ( result < 0 ) {
2012-09-14 11:50:35 -07:00
dev_dbg ( & dev - > dev , " %s - setdevice failed %i \n " , __func__ , result ) ;
2010-07-29 11:05:41 -04:00
goto out ;
}
result = ssu100_control_msg ( dev , QT_GET_SET_PREBUF_TRIG_LVL , 128 , 0 ) ;
if ( result < 0 ) {
2012-09-14 11:50:35 -07:00
dev_dbg ( & dev - > dev , " %s - set prebuffer level failed %i \n " , __func__ , result ) ;
2010-07-29 11:05:41 -04:00
goto out ;
}
result = ssu100_control_msg ( dev , QT_SET_ATF , ATC_DISABLED , 0 ) ;
if ( result < 0 ) {
2012-09-14 11:50:35 -07:00
dev_dbg ( & dev - > dev , " %s - set ATFprebuffer level failed %i \n " , __func__ , result ) ;
2010-07-29 11:05:41 -04:00
goto out ;
}
result = ssu100_getdevice ( dev , data ) ;
if ( result < 0 ) {
2012-09-14 11:50:35 -07:00
dev_dbg ( & dev - > dev , " %s - get_device failed %i \n " , __func__ , result ) ;
2010-07-29 11:05:41 -04:00
goto out ;
}
data [ 0 ] & = ~ ( RR_BITS | DUPMODE_BITS ) ;
data [ 0 ] | = CLKS_X4 ;
data [ 1 ] & = ~ ( LOOPMODE_BITS ) ;
data [ 1 ] | = RS232_MODE ;
result = ssu100_setdevice ( dev , data ) ;
if ( result < 0 ) {
2012-09-14 11:50:35 -07:00
dev_dbg ( & dev - > dev , " %s - setdevice failed %i \n " , __func__ , result ) ;
2010-07-29 11:05:41 -04:00
goto out ;
}
out : kfree ( data ) ;
return result ;
}
static void ssu100_set_termios ( struct tty_struct * tty ,
struct usb_serial_port * port ,
struct ktermios * old_termios )
{
struct usb_device * dev = port - > serial - > dev ;
2012-07-14 15:31:47 +01:00
struct ktermios * termios = & tty - > termios ;
2010-07-29 11:05:41 -04:00
u16 baud , divisor , remainder ;
unsigned int cflag = termios - > c_cflag ;
u16 urb_value = 0 ; /* will hold the new flags */
int result ;
if ( cflag & PARENB ) {
if ( cflag & PARODD )
2010-08-05 17:01:07 -04:00
urb_value | = UART_LCR_PARITY ;
2010-07-29 11:05:41 -04:00
else
urb_value | = SERIAL_EVEN_PARITY ;
}
switch ( cflag & CSIZE ) {
case CS5 :
2010-08-05 17:01:07 -04:00
urb_value | = UART_LCR_WLEN5 ;
2010-07-29 11:05:41 -04:00
break ;
case CS6 :
2010-08-05 17:01:07 -04:00
urb_value | = UART_LCR_WLEN6 ;
2010-07-29 11:05:41 -04:00
break ;
case CS7 :
2010-08-05 17:01:07 -04:00
urb_value | = UART_LCR_WLEN7 ;
2010-07-29 11:05:41 -04:00
break ;
default :
case CS8 :
2010-08-05 17:01:07 -04:00
urb_value | = UART_LCR_WLEN8 ;
2010-07-29 11:05:41 -04:00
break ;
}
baud = tty_get_baud_rate ( tty ) ;
if ( ! baud )
baud = 9600 ;
2012-09-14 11:50:35 -07:00
dev_dbg ( & port - > dev , " %s - got baud = %d \n " , __func__ , baud ) ;
2010-07-29 11:05:41 -04:00
divisor = MAX_BAUD_RATE / baud ;
remainder = MAX_BAUD_RATE % baud ;
if ( ( ( remainder * 2 ) > = baud ) & & ( baud ! = 110 ) )
divisor + + ;
urb_value = urb_value < < 8 ;
result = ssu100_control_msg ( dev , QT_GET_SET_UART , divisor , urb_value ) ;
if ( result < 0 )
2012-09-14 11:50:35 -07:00
dev_dbg ( & port - > dev , " %s - set uart failed \n " , __func__ ) ;
2010-07-29 11:05:41 -04:00
if ( cflag & CRTSCTS )
result = ssu100_control_msg ( dev , QT_HW_FLOW_CONTROL_MASK ,
SERIAL_CRTSCTS , 0 ) ;
else
result = ssu100_control_msg ( dev , QT_HW_FLOW_CONTROL_MASK ,
0 , 0 ) ;
if ( result < 0 )
2012-09-14 11:50:35 -07:00
dev_dbg ( & port - > dev , " %s - set HW flow control failed \n " , __func__ ) ;
2010-07-29 11:05:41 -04:00
if ( I_IXOFF ( tty ) | | I_IXON ( tty ) ) {
u16 x = ( ( u16 ) ( START_CHAR ( tty ) < < 8 ) | ( u16 ) ( STOP_CHAR ( tty ) ) ) ;
result = ssu100_control_msg ( dev , QT_SW_FLOW_CONTROL_MASK ,
x , 0 ) ;
} else
result = ssu100_control_msg ( dev , QT_SW_FLOW_CONTROL_MASK ,
0 , 0 ) ;
if ( result < 0 )
2012-09-14 11:50:35 -07:00
dev_dbg ( & port - > dev , " %s - set SW flow control failed \n " , __func__ ) ;
2010-07-29 11:05:41 -04:00
}
static int ssu100_open ( struct tty_struct * tty , struct usb_serial_port * port )
{
struct usb_device * dev = port - > serial - > dev ;
struct ssu100_port_private * priv = usb_get_serial_port_data ( port ) ;
u8 * data ;
int result ;
2010-08-05 17:01:05 -04:00
unsigned long flags ;
2010-07-29 11:05:41 -04:00
data = kzalloc ( 2 , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
result = usb_control_msg ( dev , usb_rcvctrlpipe ( dev , 0 ) ,
QT_OPEN_CLOSE_CHANNEL ,
QT_TRANSFER_IN , 0x01 ,
0 , data , 2 , 300 ) ;
2017-01-12 14:56:22 +01:00
if ( result < 2 ) {
2012-09-14 11:50:35 -07:00
dev_dbg ( & port - > dev , " %s - open failed %i \n " , __func__ , result ) ;
2017-01-12 14:56:22 +01:00
if ( result > = 0 )
result = - EIO ;
2010-07-29 11:05:41 -04:00
kfree ( data ) ;
return result ;
}
2010-08-05 17:01:05 -04:00
spin_lock_irqsave ( & priv - > status_lock , flags ) ;
2010-08-05 17:01:09 -04:00
priv - > shadowLSR = data [ 0 ] ;
priv - > shadowMSR = data [ 1 ] ;
2010-08-05 17:01:05 -04:00
spin_unlock_irqrestore ( & priv - > status_lock , flags ) ;
2010-07-29 11:05:41 -04:00
kfree ( data ) ;
/* set to 9600 */
result = ssu100_control_msg ( dev , QT_GET_SET_UART , 0x30 , 0x0300 ) ;
if ( result < 0 )
2012-09-14 11:50:35 -07:00
dev_dbg ( & port - > dev , " %s - set uart failed \n " , __func__ ) ;
2010-07-29 11:05:41 -04:00
if ( tty )
2012-07-14 15:31:47 +01:00
ssu100_set_termios ( tty , port , & tty - > termios ) ;
2010-07-29 11:05:41 -04:00
return usb_serial_generic_open ( tty , port ) ;
}
static int get_serial_info ( struct usb_serial_port * port ,
struct serial_struct __user * retinfo )
{
struct serial_struct tmp ;
memset ( & tmp , 0 , sizeof ( tmp ) ) ;
2013-06-07 11:04:28 -07:00
tmp . line = port - > minor ;
2010-07-29 11:05:41 -04:00
tmp . port = 0 ;
tmp . irq = 0 ;
tmp . xmit_fifo_size = port - > bulk_out_size ;
tmp . baud_base = 9600 ;
tmp . close_delay = 5 * HZ ;
tmp . closing_wait = 30 * HZ ;
if ( copy_to_user ( retinfo , & tmp , sizeof ( * retinfo ) ) )
return - EFAULT ;
return 0 ;
}
2011-02-14 16:27:06 +00:00
static int ssu100_ioctl ( struct tty_struct * tty ,
2010-07-29 11:05:41 -04:00
unsigned int cmd , unsigned long arg )
{
struct usb_serial_port * port = tty - > driver_data ;
switch ( cmd ) {
case TIOCGSERIAL :
return get_serial_info ( port ,
( struct serial_struct __user * ) arg ) ;
default :
break ;
}
return - ENOIOCTLCMD ;
}
static int ssu100_attach ( struct usb_serial * serial )
2012-10-17 16:31:34 +02:00
{
return ssu100_initdevice ( serial - > dev ) ;
}
static int ssu100_port_probe ( struct usb_serial_port * port )
2010-07-29 11:05:41 -04:00
{
struct ssu100_port_private * priv ;
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
2012-10-17 16:31:34 +02:00
if ( ! priv )
2010-07-29 11:05:41 -04:00
return - ENOMEM ;
2010-08-05 17:01:05 -04:00
spin_lock_init ( & priv - > status_lock ) ;
2012-10-17 16:31:34 +02:00
2010-07-29 11:05:41 -04:00
usb_set_serial_port_data ( port , priv ) ;
2012-10-17 16:31:34 +02:00
return 0 ;
}
static int ssu100_port_remove ( struct usb_serial_port * port )
{
struct ssu100_port_private * priv ;
priv = usb_get_serial_port_data ( port ) ;
kfree ( priv ) ;
return 0 ;
2010-07-29 11:05:41 -04:00
}
2011-02-14 16:26:14 +00:00
static int ssu100_tiocmget ( struct tty_struct * tty )
2010-07-29 11:05:41 -04:00
{
struct usb_serial_port * port = tty - > driver_data ;
struct usb_device * dev = port - > serial - > dev ;
u8 * d ;
int r ;
d = kzalloc ( 2 , GFP_KERNEL ) ;
if ( ! d )
return - ENOMEM ;
2010-08-05 17:01:07 -04:00
r = ssu100_getregister ( dev , 0 , UART_MCR , d ) ;
2010-07-29 11:05:41 -04:00
if ( r < 0 )
goto mget_out ;
2010-08-05 17:01:07 -04:00
r = ssu100_getregister ( dev , 0 , UART_MSR , d + 1 ) ;
2010-07-29 11:05:41 -04:00
if ( r < 0 )
goto mget_out ;
2010-08-05 17:01:07 -04:00
r = ( d [ 0 ] & UART_MCR_DTR ? TIOCM_DTR : 0 ) |
( d [ 0 ] & UART_MCR_RTS ? TIOCM_RTS : 0 ) |
( d [ 1 ] & UART_MSR_CTS ? TIOCM_CTS : 0 ) |
( d [ 1 ] & UART_MSR_DCD ? TIOCM_CAR : 0 ) |
( d [ 1 ] & UART_MSR_RI ? TIOCM_RI : 0 ) |
( d [ 1 ] & UART_MSR_DSR ? TIOCM_DSR : 0 ) ;
2010-07-29 11:05:41 -04:00
mget_out :
kfree ( d ) ;
return r ;
}
2011-02-14 16:26:50 +00:00
static int ssu100_tiocmset ( struct tty_struct * tty ,
2010-07-29 11:05:41 -04:00
unsigned int set , unsigned int clear )
{
struct usb_serial_port * port = tty - > driver_data ;
struct usb_device * dev = port - > serial - > dev ;
return update_mctrl ( dev , set , clear ) ;
}
static void ssu100_dtr_rts ( struct usb_serial_port * port , int on )
{
struct usb_device * dev = port - > serial - > dev ;
2013-02-13 17:53:28 +01:00
/* Disable flow control */
if ( ! on ) {
if ( ssu100_setregister ( dev , 0 , UART_MCR , 0 ) < 0 )
2010-07-29 11:05:41 -04:00
dev_err ( & port - > dev , " error from flowcontrol urb \n " ) ;
}
2013-02-13 17:53:28 +01:00
/* drop RTS and DTR */
if ( on )
set_mctrl ( dev , TIOCM_DTR | TIOCM_RTS ) ;
else
clear_mctrl ( dev , TIOCM_DTR | TIOCM_RTS ) ;
2010-07-29 11:05:41 -04:00
}
2010-08-05 17:01:09 -04:00
static void ssu100_update_msr ( struct usb_serial_port * port , u8 msr )
{
struct ssu100_port_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
spin_lock_irqsave ( & priv - > status_lock , flags ) ;
priv - > shadowMSR = msr ;
spin_unlock_irqrestore ( & priv - > status_lock , flags ) ;
if ( msr & UART_MSR_ANY_DELTA ) {
/* update input line counters */
if ( msr & UART_MSR_DCTS )
2013-03-21 12:37:31 +01:00
port - > icount . cts + + ;
2010-08-05 17:01:09 -04:00
if ( msr & UART_MSR_DDSR )
2013-03-21 12:37:31 +01:00
port - > icount . dsr + + ;
2010-08-05 17:01:09 -04:00
if ( msr & UART_MSR_DDCD )
2013-03-21 12:37:31 +01:00
port - > icount . dcd + + ;
2010-08-05 17:01:09 -04:00
if ( msr & UART_MSR_TERI )
2013-03-21 12:37:31 +01:00
port - > icount . rng + + ;
2013-03-21 12:37:32 +01:00
wake_up_interruptible ( & port - > port . delta_msr_wait ) ;
2010-08-05 17:01:09 -04:00
}
}
2010-08-13 09:59:31 -04:00
static void ssu100_update_lsr ( struct usb_serial_port * port , u8 lsr ,
char * tty_flag )
2010-08-05 17:01:09 -04:00
{
struct ssu100_port_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
spin_lock_irqsave ( & priv - > status_lock , flags ) ;
priv - > shadowLSR = lsr ;
spin_unlock_irqrestore ( & priv - > status_lock , flags ) ;
2010-08-13 09:59:31 -04:00
* tty_flag = TTY_NORMAL ;
2010-08-05 17:01:09 -04:00
if ( lsr & UART_LSR_BRK_ERROR_BITS ) {
2010-08-13 09:59:31 -04:00
/* we always want to update icount, but we only want to
* update tty_flag for one case */
if ( lsr & UART_LSR_BI ) {
2013-03-21 12:37:31 +01:00
port - > icount . brk + + ;
2010-08-13 09:59:31 -04:00
* tty_flag = TTY_BREAK ;
usb_serial_handle_break ( port ) ;
}
if ( lsr & UART_LSR_PE ) {
2013-03-21 12:37:31 +01:00
port - > icount . parity + + ;
2010-08-13 09:59:31 -04:00
if ( * tty_flag = = TTY_NORMAL )
* tty_flag = TTY_PARITY ;
}
if ( lsr & UART_LSR_FE ) {
2013-03-21 12:37:31 +01:00
port - > icount . frame + + ;
2010-08-13 09:59:31 -04:00
if ( * tty_flag = = TTY_NORMAL )
* tty_flag = TTY_FRAME ;
}
2014-11-18 11:25:21 +01:00
if ( lsr & UART_LSR_OE ) {
2013-03-21 12:37:31 +01:00
port - > icount . overrun + + ;
2014-11-18 11:25:21 +01:00
tty_insert_flip_char ( & port - > port , 0 , TTY_OVERRUN ) ;
2010-08-13 09:59:31 -04:00
}
2010-08-05 17:01:09 -04:00
}
2010-08-13 09:59:31 -04:00
2010-08-05 17:01:09 -04:00
}
2013-01-03 15:53:06 +01:00
static void ssu100_process_read_urb ( struct urb * urb )
2010-07-29 11:05:41 -04:00
{
2010-10-21 14:43:05 -04:00
struct usb_serial_port * port = urb - > context ;
char * packet = ( char * ) urb - > transfer_buffer ;
2010-08-13 09:59:31 -04:00
char flag = TTY_NORMAL ;
2010-10-21 14:43:05 -04:00
u32 len = urb - > actual_length ;
int i ;
2010-07-29 11:05:41 -04:00
char * ch ;
2010-08-05 17:01:06 -04:00
if ( ( len > = 4 ) & &
( packet [ 0 ] = = 0x1b ) & & ( packet [ 1 ] = = 0x1b ) & &
2010-07-29 11:05:41 -04:00
( ( packet [ 2 ] = = 0x00 ) | | ( packet [ 2 ] = = 0x01 ) ) ) {
2014-11-18 11:25:21 +01:00
if ( packet [ 2 ] = = 0x00 )
2010-08-13 09:59:31 -04:00
ssu100_update_lsr ( port , packet [ 3 ] , & flag ) ;
2010-08-05 17:01:09 -04:00
if ( packet [ 2 ] = = 0x01 )
ssu100_update_msr ( port , packet [ 3 ] ) ;
2010-07-29 11:05:41 -04:00
len - = 4 ;
ch = packet + 4 ;
} else
ch = packet ;
if ( ! len )
2013-01-03 15:53:06 +01:00
return ; /* status only */
2010-07-29 11:05:41 -04:00
if ( port - > port . console & & port - > sysrq ) {
for ( i = 0 ; i < len ; i + + , ch + + ) {
2010-08-17 21:15:47 -07:00
if ( ! usb_serial_handle_sysrq_char ( port , * ch ) )
2013-01-03 15:53:03 +01:00
tty_insert_flip_char ( & port - > port , * ch , flag ) ;
2010-07-29 11:05:41 -04:00
}
} else
2013-01-03 15:53:02 +01:00
tty_insert_flip_string_fixed_flag ( & port - > port , ch , flag , len ) ;
2010-07-29 11:05:41 -04:00
2013-01-03 15:53:06 +01:00
tty_flip_buffer_push ( & port - > port ) ;
2010-07-29 11:05:41 -04:00
}
static struct usb_serial_driver ssu100_device = {
. driver = {
. owner = THIS_MODULE ,
. name = " ssu100 " ,
} ,
. description = DRIVER_DESC ,
. id_table = id_table ,
. num_ports = 1 ,
. open = ssu100_open ,
. attach = ssu100_attach ,
2012-10-17 16:31:34 +02:00
. port_probe = ssu100_port_probe ,
. port_remove = ssu100_port_remove ,
2010-07-29 11:05:41 -04:00
. dtr_rts = ssu100_dtr_rts ,
. process_read_urb = ssu100_process_read_urb ,
. tiocmget = ssu100_tiocmget ,
. tiocmset = ssu100_tiocmset ,
2013-03-21 12:37:32 +01:00
. tiocmiwait = usb_serial_generic_tiocmiwait ,
2013-03-21 12:37:31 +01:00
. get_icount = usb_serial_generic_get_icount ,
2010-07-29 11:05:41 -04:00
. ioctl = ssu100_ioctl ,
. set_termios = ssu100_set_termios ,
} ;
2012-02-23 14:57:25 -05:00
static struct usb_serial_driver * const serial_drivers [ ] = {
& ssu100_device , NULL
} ;
2012-05-08 15:46:14 -07:00
module_usb_serial_driver ( serial_drivers , id_table ) ;
2010-07-29 11:05:41 -04:00
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
2017-11-03 18:12:08 +01:00
MODULE_LICENSE ( " GPL v2 " ) ;