2012-02-29 01:36:35 +04:00
/*
* Fintek F81232 USB to serial adaptor driver
*
* Copyright ( C ) 2012 Greg Kroah - Hartman ( gregkh @ linuxfoundation . org )
* Copyright ( C ) 2012 Linux Foundation
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
*/
# 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/serial.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/spinlock.h>
# include <linux/uaccess.h>
# include <linux/usb.h>
# include <linux/usb/serial.h>
static const struct usb_device_id id_table [ ] = {
{ USB_DEVICE ( 0x1934 , 0x0706 ) } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , id_table ) ;
# define CONTROL_DTR 0x01
# define CONTROL_RTS 0x02
# define UART_STATE 0x08
# define UART_STATE_TRANSIENT_MASK 0x74
# define UART_DCD 0x01
# define UART_DSR 0x02
# define UART_BREAK_ERROR 0x04
# define UART_RING 0x08
# define UART_FRAME_ERROR 0x10
# define UART_PARITY_ERROR 0x20
# define UART_OVERRUN_ERROR 0x40
# define UART_CTS 0x80
struct f81232_private {
spinlock_t lock ;
u8 line_control ;
u8 line_status ;
} ;
static void f81232_update_line_status ( struct usb_serial_port * port ,
unsigned char * data ,
unsigned int actual_length )
{
2013-12-29 22:22:57 +04:00
/*
2013-12-29 22:22:58 +04:00
* FIXME : Update port - > icount , and call
2013-12-29 22:22:57 +04:00
*
* wake_up_interruptible ( & port - > port . delta_msr_wait ) ;
*
* on MSR changes .
*/
2012-02-29 01:36:35 +04:00
}
static void f81232_read_int_callback ( struct urb * urb )
{
struct usb_serial_port * port = urb - > context ;
unsigned char * data = urb - > transfer_buffer ;
unsigned int actual_length = urb - > actual_length ;
int status = urb - > status ;
int retval ;
switch ( status ) {
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
/* this urb is terminated, clean up */
2012-05-16 03:27:17 +04:00
dev_dbg ( & port - > dev , " %s - urb shutting down with status: %d \n " ,
__func__ , status ) ;
2012-02-29 01:36:35 +04:00
return ;
default :
2012-05-16 03:27:17 +04:00
dev_dbg ( & port - > dev , " %s - nonzero urb status received: %d \n " ,
__func__ , status ) ;
2012-02-29 01:36:35 +04:00
goto exit ;
}
2012-09-18 12:58:57 +04:00
usb_serial_debug_data ( & port - > dev , __func__ ,
2012-02-29 01:36:35 +04:00
urb - > actual_length , urb - > transfer_buffer ) ;
f81232_update_line_status ( port , data , actual_length ) ;
exit :
retval = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( retval )
dev_err ( & urb - > dev - > dev ,
" %s - usb_submit_urb failed with result %d \n " ,
__func__ , retval ) ;
}
static void f81232_process_read_urb ( struct urb * urb )
{
struct usb_serial_port * port = urb - > context ;
struct f81232_private * priv = usb_get_serial_port_data ( port ) ;
unsigned char * data = urb - > transfer_buffer ;
char tty_flag = TTY_NORMAL ;
unsigned long flags ;
u8 line_status ;
int i ;
/* update line status */
spin_lock_irqsave ( & priv - > lock , flags ) ;
line_status = priv - > line_status ;
priv - > line_status & = ~ UART_STATE_TRANSIENT_MASK ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
if ( ! urb - > actual_length )
return ;
/* break takes precedence over parity, */
/* which takes precedence over framing errors */
if ( line_status & UART_BREAK_ERROR )
tty_flag = TTY_BREAK ;
else if ( line_status & UART_PARITY_ERROR )
tty_flag = TTY_PARITY ;
else if ( line_status & UART_FRAME_ERROR )
tty_flag = TTY_FRAME ;
2012-05-16 03:27:17 +04:00
dev_dbg ( & port - > dev , " %s - tty_flag = %d \n " , __func__ , tty_flag ) ;
2012-02-29 01:36:35 +04:00
/* overrun is special, not associated with a char */
if ( line_status & UART_OVERRUN_ERROR )
2013-01-03 18:53:03 +04:00
tty_insert_flip_char ( & port - > port , 0 , TTY_OVERRUN ) ;
2012-02-29 01:36:35 +04:00
if ( port - > port . console & & port - > sysrq ) {
for ( i = 0 ; i < urb - > actual_length ; + + i )
if ( ! usb_serial_handle_sysrq_char ( port , data [ i ] ) )
2013-01-03 18:53:03 +04:00
tty_insert_flip_char ( & port - > port , data [ i ] ,
tty_flag ) ;
2012-02-29 01:36:35 +04:00
} else {
2013-01-03 18:53:02 +04:00
tty_insert_flip_string_fixed_flag ( & port - > port , data , tty_flag ,
2012-02-29 01:36:35 +04:00
urb - > actual_length ) ;
}
2013-01-03 18:53:06 +04:00
tty_flip_buffer_push ( & port - > port ) ;
2012-02-29 01:36:35 +04:00
}
static int set_control_lines ( struct usb_device * dev , u8 value )
{
/* FIXME - Stubbed out for now */
return 0 ;
}
static void f81232_break_ctl ( struct tty_struct * tty , int break_state )
{
/* FIXME - Stubbed out for now */
/*
* break_state = - 1 to turn on break , and 0 to turn off break
* see drivers / char / tty_io . c to see it used .
* last_set_data_urb_value NEVER has the break bit set in it .
*/
}
static void f81232_set_termios ( struct tty_struct * tty ,
struct usb_serial_port * port , struct ktermios * old_termios )
{
/* FIXME - Stubbed out for now */
/* Don't change anything if nothing has changed */
2013-06-10 20:29:37 +04:00
if ( old_termios & & ! tty_termios_hw_change ( & tty - > termios , old_termios ) )
2012-02-29 01:36:35 +04:00
return ;
/* Do the real work here... */
2013-06-10 20:29:37 +04:00
if ( old_termios )
tty_termios_copy_hw ( & tty - > termios , old_termios ) ;
2012-02-29 01:36:35 +04:00
}
static int f81232_tiocmget ( struct tty_struct * tty )
{
/* FIXME - Stubbed out for now */
return 0 ;
}
static int f81232_tiocmset ( struct tty_struct * tty ,
unsigned int set , unsigned int clear )
{
/* FIXME - Stubbed out for now */
return 0 ;
}
static int f81232_open ( struct tty_struct * tty , struct usb_serial_port * port )
{
int result ;
/* Setup termios */
if ( tty )
2013-06-10 20:29:37 +04:00
f81232_set_termios ( tty , port , NULL ) ;
2012-02-29 01:36:35 +04:00
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_KERNEL ) ;
if ( result ) {
dev_err ( & port - > dev , " %s - failed submitting interrupt urb, "
" error %d \n " , __func__ , result ) ;
return result ;
}
result = usb_serial_generic_open ( tty , port ) ;
if ( result ) {
usb_kill_urb ( port - > interrupt_in_urb ) ;
return result ;
}
return 0 ;
}
static void f81232_close ( struct usb_serial_port * port )
{
usb_serial_generic_close ( port ) ;
usb_kill_urb ( port - > interrupt_in_urb ) ;
}
static void f81232_dtr_rts ( struct usb_serial_port * port , int on )
{
struct f81232_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
u8 control ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
/* Change DTR and RTS */
if ( on )
priv - > line_control | = ( CONTROL_DTR | CONTROL_RTS ) ;
else
priv - > line_control & = ~ ( CONTROL_DTR | CONTROL_RTS ) ;
control = priv - > line_control ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
set_control_lines ( port - > serial - > dev , control ) ;
}
static int f81232_carrier_raised ( struct usb_serial_port * port )
{
struct f81232_private * priv = usb_get_serial_port_data ( port ) ;
if ( priv - > line_status & UART_DCD )
return 1 ;
return 0 ;
}
static int f81232_ioctl ( struct tty_struct * tty ,
unsigned int cmd , unsigned long arg )
{
struct serial_struct ser ;
struct usb_serial_port * port = tty - > driver_data ;
2012-05-16 03:27:17 +04:00
2012-02-29 01:36:35 +04:00
switch ( cmd ) {
case TIOCGSERIAL :
memset ( & ser , 0 , sizeof ser ) ;
ser . type = PORT_16654 ;
2013-06-07 22:04:28 +04:00
ser . line = port - > minor ;
2013-06-06 21:32:00 +04:00
ser . port = port - > port_number ;
2012-02-29 01:36:35 +04:00
ser . baud_base = 460800 ;
if ( copy_to_user ( ( void __user * ) arg , & ser , sizeof ser ) )
return - EFAULT ;
return 0 ;
default :
break ;
}
return - ENOIOCTLCMD ;
}
2012-10-17 15:34:56 +04:00
static int f81232_port_probe ( struct usb_serial_port * port )
2012-02-29 01:36:35 +04:00
{
struct f81232_private * priv ;
2012-10-17 15:34:56 +04:00
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2012-02-29 01:36:35 +04:00
2012-10-17 15:34:56 +04:00
spin_lock_init ( & priv - > lock ) ;
usb_set_serial_port_data ( port , priv ) ;
2013-06-26 18:47:23 +04:00
port - > port . drain_delay = 256 ;
2012-10-17 15:34:56 +04:00
return 0 ;
2012-02-29 01:36:35 +04:00
}
2012-10-17 15:34:56 +04:00
static int f81232_port_remove ( struct usb_serial_port * port )
2012-02-29 01:36:35 +04:00
{
struct f81232_private * priv ;
2012-10-17 15:34:56 +04:00
priv = usb_get_serial_port_data ( port ) ;
kfree ( priv ) ;
return 0 ;
2012-02-29 01:36:35 +04:00
}
static struct usb_serial_driver f81232_device = {
. driver = {
. owner = THIS_MODULE ,
. name = " f81232 " ,
} ,
. id_table = id_table ,
. num_ports = 1 ,
. bulk_in_size = 256 ,
. bulk_out_size = 256 ,
. open = f81232_open ,
. close = f81232_close ,
. dtr_rts = f81232_dtr_rts ,
. carrier_raised = f81232_carrier_raised ,
. ioctl = f81232_ioctl ,
. break_ctl = f81232_break_ctl ,
. set_termios = f81232_set_termios ,
. tiocmget = f81232_tiocmget ,
. tiocmset = f81232_tiocmset ,
2013-12-29 22:22:58 +04:00
. tiocmiwait = usb_serial_generic_tiocmiwait ,
2012-02-29 01:36:35 +04:00
. process_read_urb = f81232_process_read_urb ,
. read_int_callback = f81232_read_int_callback ,
2012-10-17 15:34:56 +04:00
. port_probe = f81232_port_probe ,
. port_remove = f81232_port_remove ,
2012-02-29 01:36:35 +04:00
} ;
static struct usb_serial_driver * const serial_drivers [ ] = {
& f81232_device ,
NULL ,
} ;
2012-05-09 02:46:14 +04:00
module_usb_serial_driver ( serial_drivers , id_table ) ;
2012-02-29 01:36:35 +04:00
MODULE_DESCRIPTION ( " Fintek F81232 USB to serial adaptor driver " ) ;
MODULE_AUTHOR ( " Greg Kroah-Hartman <gregkh@linuxfoundation.org " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;