2008-11-04 00:27:03 +03:00
/*
* Opticon USB barcode to serial driver
*
2009-02-07 05:30:56 +03:00
* Copyright ( C ) 2008 - 2009 Greg Kroah - Hartman < gregkh @ suse . de >
* Copyright ( C ) 2008 - 2009 Novell Inc .
2008-11-04 00:27:03 +03:00
*
* 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/init.h>
# include <linux/tty.h>
# include <linux/tty_driver.h>
# include <linux/tty_flip.h>
2009-02-07 05:31:46 +03:00
# include <linux/serial.h>
2008-11-04 00:27:03 +03:00
# include <linux/module.h>
# include <linux/usb.h>
# include <linux/usb/serial.h>
# include <linux/uaccess.h>
static int debug ;
static struct usb_device_id id_table [ ] = {
{ USB_DEVICE ( 0x065a , 0x0009 ) } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( usb , id_table ) ;
/* This structure holds all of the individual device information */
struct opticon_private {
struct usb_device * udev ;
struct usb_serial * serial ;
struct usb_serial_port * port ;
unsigned char * bulk_in_buffer ;
struct urb * bulk_read_urb ;
int buffer_size ;
u8 bulk_address ;
spinlock_t lock ; /* protects the following flags */
bool throttled ;
bool actually_throttled ;
bool rts ;
2009-02-07 05:30:56 +03:00
int outstanding_urbs ;
2008-11-04 00:27:03 +03:00
} ;
2009-02-07 05:30:56 +03:00
/* max number of write urbs in flight */
# define URB_UPPER_LIMIT 4
2008-11-04 00:27:03 +03:00
static void opticon_bulk_callback ( struct urb * urb )
{
struct opticon_private * priv = urb - > context ;
unsigned char * data = urb - > transfer_buffer ;
struct usb_serial_port * port = priv - > port ;
int status = urb - > status ;
struct tty_struct * tty ;
int result ;
int available_room = 0 ;
int data_length ;
dbg ( " %s - port %d " , __func__ , port - > number ) ;
switch ( status ) {
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
/* this urb is terminated, clean up */
dbg ( " %s - urb shutting down with status: %d " ,
__func__ , status ) ;
return ;
default :
dbg ( " %s - nonzero urb status received: %d " ,
__func__ , status ) ;
goto exit ;
}
usb_serial_debug_data ( debug , & port - > dev , __func__ , urb - > actual_length ,
data ) ;
if ( urb - > actual_length > 2 ) {
data_length = urb - > actual_length - 2 ;
/*
* Data from the device comes with a 2 byte header :
*
* < 0x00 > < 0x00 > data . . .
* This is real data to be sent to the tty layer
* < 0x00 > < 0x01 ) level
* This is a RTS level change , the third byte is the RTS
* value ( 0 for low , 1 for high ) .
*/
if ( ( data [ 0 ] = = 0x00 ) & & ( data [ 1 ] = = 0x00 ) ) {
/* real data, send it to the tty layer */
tty = tty_port_tty_get ( & port - > port ) ;
if ( tty ) {
available_room = tty_buffer_request_room ( tty ,
data_length ) ;
if ( available_room ) {
tty_insert_flip_string ( tty , data ,
available_room ) ;
tty_flip_buffer_push ( tty ) ;
}
tty_kref_put ( tty ) ;
}
} else {
if ( ( data [ 0 ] = = 0x00 ) & & ( data [ 1 ] = = 0x01 ) ) {
if ( data [ 2 ] = = 0x00 )
priv - > rts = false ;
else
priv - > rts = true ;
} else {
dev_dbg ( & priv - > udev - > dev ,
" Unknown data packet received from the device: "
" %2x %2x \n " ,
data [ 0 ] , data [ 1 ] ) ;
}
}
} else {
dev_dbg ( & priv - > udev - > dev ,
" Improper ammount of data received from the device, "
" %d bytes " , urb - > actual_length ) ;
}
exit :
spin_lock ( & priv - > lock ) ;
/* Continue trying to always read if we should */
if ( ! priv - > throttled ) {
usb_fill_bulk_urb ( priv - > bulk_read_urb , priv - > udev ,
usb_rcvbulkpipe ( priv - > udev ,
priv - > bulk_address ) ,
priv - > bulk_in_buffer , priv - > buffer_size ,
opticon_bulk_callback , priv ) ;
result = usb_submit_urb ( port - > read_urb , GFP_ATOMIC ) ;
if ( result )
dev_err ( & port - > dev ,
" %s - failed resubmitting read urb, error %d \n " ,
__func__ , result ) ;
} else
priv - > actually_throttled = true ;
spin_unlock ( & priv - > lock ) ;
}
2009-09-20 00:13:26 +04:00
static int opticon_open ( struct tty_struct * tty , struct usb_serial_port * port )
2008-11-04 00:27:03 +03:00
{
struct opticon_private * priv = usb_get_serial_data ( port - > serial ) ;
unsigned long flags ;
int result = 0 ;
dbg ( " %s - port %d " , __func__ , port - > number ) ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > throttled = false ;
priv - > actually_throttled = false ;
priv - > port = port ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
/* Start reading from the device */
usb_fill_bulk_urb ( priv - > bulk_read_urb , priv - > udev ,
usb_rcvbulkpipe ( priv - > udev ,
priv - > bulk_address ) ,
priv - > bulk_in_buffer , priv - > buffer_size ,
opticon_bulk_callback , priv ) ;
result = usb_submit_urb ( priv - > bulk_read_urb , GFP_KERNEL ) ;
if ( result )
dev_err ( & port - > dev ,
" %s - failed resubmitting read urb, error %d \n " ,
__func__ , result ) ;
return result ;
}
2009-06-11 15:26:29 +04:00
static void opticon_close ( struct usb_serial_port * port )
2008-11-04 00:27:03 +03:00
{
struct opticon_private * priv = usb_get_serial_data ( port - > serial ) ;
dbg ( " %s - port %d " , __func__ , port - > number ) ;
/* shutdown our urbs */
usb_kill_urb ( priv - > bulk_read_urb ) ;
}
2009-02-07 05:30:56 +03:00
static void opticon_write_bulk_callback ( struct urb * urb )
{
struct opticon_private * priv = urb - > context ;
int status = urb - > status ;
unsigned long flags ;
/* free up the transfer buffer, as usb_free_urb() does not do this */
kfree ( urb - > transfer_buffer ) ;
if ( status )
dbg ( " %s - nonzero write bulk status received: %d " ,
__func__ , status ) ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
- - priv - > outstanding_urbs ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
usb_serial_port_softint ( priv - > port ) ;
}
static int opticon_write ( struct tty_struct * tty , struct usb_serial_port * port ,
const unsigned char * buf , int count )
{
struct opticon_private * priv = usb_get_serial_data ( port - > serial ) ;
struct usb_serial * serial = port - > serial ;
struct urb * urb ;
unsigned char * buffer ;
unsigned long flags ;
int status ;
dbg ( " %s - port %d " , __func__ , port - > number ) ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( priv - > outstanding_urbs > URB_UPPER_LIMIT ) {
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
dbg ( " %s - write limit hit \n " , __func__ ) ;
return 0 ;
}
priv - > outstanding_urbs + + ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
buffer = kmalloc ( count , GFP_ATOMIC ) ;
if ( ! buffer ) {
dev_err ( & port - > dev , " out of memory \n " ) ;
count = - ENOMEM ;
goto error_no_buffer ;
}
urb = usb_alloc_urb ( 0 , GFP_ATOMIC ) ;
if ( ! urb ) {
dev_err ( & port - > dev , " no more free urbs \n " ) ;
count = - ENOMEM ;
goto error_no_urb ;
}
memcpy ( buffer , buf , count ) ;
usb_serial_debug_data ( debug , & port - > dev , __func__ , count , buffer ) ;
usb_fill_bulk_urb ( urb , serial - > dev ,
usb_sndbulkpipe ( serial - > dev ,
port - > bulk_out_endpointAddress ) ,
buffer , count , opticon_write_bulk_callback , priv ) ;
/* send it down the pipe */
status = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( status ) {
dev_err ( & port - > dev ,
" %s - usb_submit_urb(write bulk) failed with status = %d \n " ,
__func__ , status ) ;
count = status ;
goto error ;
}
/* we are done with this urb, so let the host driver
* really free it when it is finished with it */
usb_free_urb ( urb ) ;
return count ;
error :
usb_free_urb ( urb ) ;
error_no_urb :
kfree ( buffer ) ;
error_no_buffer :
spin_lock_irqsave ( & priv - > lock , flags ) ;
- - priv - > outstanding_urbs ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return count ;
}
static int opticon_write_room ( struct tty_struct * tty )
{
struct usb_serial_port * port = tty - > driver_data ;
struct opticon_private * priv = usb_get_serial_data ( port - > serial ) ;
unsigned long flags ;
dbg ( " %s - port %d " , __func__ , port - > number ) ;
/*
* We really can take almost anything the user throws at us
* but let ' s pick a nice big number to tell the tty
* layer that we have lots of free space , unless we don ' t .
*/
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( priv - > outstanding_urbs > URB_UPPER_LIMIT * 2 / 3 ) {
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
dbg ( " %s - write limit hit \n " , __func__ ) ;
return 0 ;
}
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return 2048 ;
}
2008-11-04 00:27:03 +03:00
static void opticon_throttle ( struct tty_struct * tty )
{
struct usb_serial_port * port = tty - > driver_data ;
struct opticon_private * priv = usb_get_serial_data ( port - > serial ) ;
unsigned long flags ;
dbg ( " %s - port %d " , __func__ , port - > number ) ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > throttled = true ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
}
static void opticon_unthrottle ( struct tty_struct * tty )
{
struct usb_serial_port * port = tty - > driver_data ;
struct opticon_private * priv = usb_get_serial_data ( port - > serial ) ;
unsigned long flags ;
2009-10-07 11:25:10 +04:00
int result , was_throttled ;
2008-11-04 00:27:03 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > throttled = false ;
2009-10-07 11:25:10 +04:00
was_throttled = priv - > actually_throttled ;
2008-11-04 00:27:03 +03:00
priv - > actually_throttled = false ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
priv - > bulk_read_urb - > dev = port - > serial - > dev ;
2009-10-07 11:25:10 +04:00
if ( was_throttled ) {
result = usb_submit_urb ( priv - > bulk_read_urb , GFP_ATOMIC ) ;
if ( result )
dev_err ( & port - > dev ,
" %s - failed submitting read urb, error %d \n " ,
2008-11-04 00:27:03 +03:00
__func__ , result ) ;
2009-10-07 11:25:10 +04:00
}
2008-11-04 00:27:03 +03:00
}
2009-02-07 05:31:46 +03:00
static int opticon_tiocmget ( struct tty_struct * tty , struct file * file )
{
struct usb_serial_port * port = tty - > driver_data ;
struct opticon_private * priv = usb_get_serial_data ( port - > serial ) ;
unsigned long flags ;
int result = 0 ;
dbg ( " %s - port %d " , __func__ , port - > number ) ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( priv - > rts )
result = TIOCM_RTS ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
dbg ( " %s - %x " , __func__ , result ) ;
return result ;
}
static int get_serial_info ( struct opticon_private * priv ,
struct serial_struct __user * serial )
{
struct serial_struct tmp ;
if ( ! serial )
return - EFAULT ;
memset ( & tmp , 0x00 , sizeof ( tmp ) ) ;
/* fake emulate a 16550 uart to make userspace code happy */
tmp . type = PORT_16550A ;
tmp . line = priv - > serial - > minor ;
tmp . port = 0 ;
tmp . irq = 0 ;
tmp . flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ ;
tmp . xmit_fifo_size = 1024 ;
tmp . baud_base = 9600 ;
tmp . close_delay = 5 * HZ ;
tmp . closing_wait = 30 * HZ ;
if ( copy_to_user ( serial , & tmp , sizeof ( * serial ) ) )
return - EFAULT ;
return 0 ;
}
static int opticon_ioctl ( struct tty_struct * tty , struct file * file ,
unsigned int cmd , unsigned long arg )
{
struct usb_serial_port * port = tty - > driver_data ;
struct opticon_private * priv = usb_get_serial_data ( port - > serial ) ;
dbg ( " %s - port %d, cmd = 0x%x " , __func__ , port - > number , cmd ) ;
switch ( cmd ) {
case TIOCGSERIAL :
return get_serial_info ( priv ,
( struct serial_struct __user * ) arg ) ;
}
return - ENOIOCTLCMD ;
}
2008-11-04 00:27:03 +03:00
static int opticon_startup ( struct usb_serial * serial )
{
struct opticon_private * priv ;
struct usb_host_interface * intf ;
int i ;
int retval = - ENOMEM ;
bool bulk_in_found = false ;
/* create our private serial structure */
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( priv = = NULL ) {
dev_err ( & serial - > dev - > dev , " %s - Out of memory \n " , __func__ ) ;
return - ENOMEM ;
}
spin_lock_init ( & priv - > lock ) ;
priv - > serial = serial ;
priv - > port = serial - > port [ 0 ] ;
priv - > udev = serial - > dev ;
/* find our bulk endpoint */
intf = serial - > interface - > altsetting ;
for ( i = 0 ; i < intf - > desc . bNumEndpoints ; + + i ) {
struct usb_endpoint_descriptor * endpoint ;
endpoint = & intf - > endpoint [ i ] . desc ;
if ( ! usb_endpoint_is_bulk_in ( endpoint ) )
continue ;
priv - > bulk_read_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! priv - > bulk_read_urb ) {
dev_err ( & priv - > udev - > dev , " out of memory \n " ) ;
goto error ;
}
priv - > buffer_size = le16_to_cpu ( endpoint - > wMaxPacketSize ) * 2 ;
priv - > bulk_in_buffer = kmalloc ( priv - > buffer_size , GFP_KERNEL ) ;
if ( ! priv - > bulk_in_buffer ) {
dev_err ( & priv - > udev - > dev , " out of memory \n " ) ;
goto error ;
}
priv - > bulk_address = endpoint - > bEndpointAddress ;
/* set up our bulk urb */
usb_fill_bulk_urb ( priv - > bulk_read_urb , priv - > udev ,
usb_rcvbulkpipe ( priv - > udev ,
endpoint - > bEndpointAddress ) ,
priv - > bulk_in_buffer , priv - > buffer_size ,
opticon_bulk_callback , priv ) ;
bulk_in_found = true ;
break ;
}
if ( ! bulk_in_found ) {
dev_err ( & priv - > udev - > dev ,
" Error - the proper endpoints were not found! \n " ) ;
goto error ;
}
usb_set_serial_data ( serial , priv ) ;
return 0 ;
error :
usb_free_urb ( priv - > bulk_read_urb ) ;
kfree ( priv - > bulk_in_buffer ) ;
kfree ( priv ) ;
return retval ;
}
2009-06-02 19:53:55 +04:00
static void opticon_disconnect ( struct usb_serial * serial )
2008-11-04 00:27:03 +03:00
{
struct opticon_private * priv = usb_get_serial_data ( serial ) ;
dbg ( " %s " , __func__ ) ;
usb_kill_urb ( priv - > bulk_read_urb ) ;
usb_free_urb ( priv - > bulk_read_urb ) ;
2009-06-02 19:53:55 +04:00
}
static void opticon_release ( struct usb_serial * serial )
{
struct opticon_private * priv = usb_get_serial_data ( serial ) ;
dbg ( " %s " , __func__ ) ;
2008-11-04 00:27:03 +03:00
kfree ( priv - > bulk_in_buffer ) ;
kfree ( priv ) ;
}
2009-01-14 20:34:06 +03:00
static int opticon_suspend ( struct usb_interface * intf , pm_message_t message )
{
struct usb_serial * serial = usb_get_intfdata ( intf ) ;
struct opticon_private * priv = usb_get_serial_data ( serial ) ;
usb_kill_urb ( priv - > bulk_read_urb ) ;
return 0 ;
}
static int opticon_resume ( struct usb_interface * intf )
{
struct usb_serial * serial = usb_get_intfdata ( intf ) ;
struct opticon_private * priv = usb_get_serial_data ( serial ) ;
struct usb_serial_port * port = serial - > port [ 0 ] ;
int result ;
mutex_lock ( & port - > mutex ) ;
if ( port - > port . count )
result = usb_submit_urb ( priv - > bulk_read_urb , GFP_NOIO ) ;
else
result = 0 ;
mutex_unlock ( & port - > mutex ) ;
return result ;
}
2008-11-04 00:27:03 +03:00
static struct usb_driver opticon_driver = {
. name = " opticon " ,
. probe = usb_serial_probe ,
. disconnect = usb_serial_disconnect ,
2009-01-14 20:34:06 +03:00
. suspend = opticon_suspend ,
. resume = opticon_resume ,
2008-11-04 00:27:03 +03:00
. id_table = id_table ,
. no_dynamic_id = 1 ,
} ;
static struct usb_serial_driver opticon_device = {
. driver = {
. owner = THIS_MODULE ,
. name = " opticon " ,
} ,
. id_table = id_table ,
. usb_driver = & opticon_driver ,
. num_ports = 1 ,
. attach = opticon_startup ,
. open = opticon_open ,
. close = opticon_close ,
2009-02-07 05:30:56 +03:00
. write = opticon_write ,
. write_room = opticon_write_room ,
2009-06-02 19:53:55 +04:00
. disconnect = opticon_disconnect ,
. release = opticon_release ,
2008-11-04 00:27:03 +03:00
. throttle = opticon_throttle ,
. unthrottle = opticon_unthrottle ,
2009-02-07 05:31:46 +03:00
. ioctl = opticon_ioctl ,
. tiocmget = opticon_tiocmget ,
2008-11-04 00:27:03 +03:00
} ;
static int __init opticon_init ( void )
{
int retval ;
retval = usb_serial_register ( & opticon_device ) ;
if ( retval )
return retval ;
retval = usb_register ( & opticon_driver ) ;
if ( retval )
usb_serial_deregister ( & opticon_device ) ;
return retval ;
}
static void __exit opticon_exit ( void )
{
usb_deregister ( & opticon_driver ) ;
usb_serial_deregister ( & opticon_device ) ;
}
module_init ( opticon_init ) ;
module_exit ( opticon_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( debug , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Debug enabled or not " ) ;