2012-03-09 01:18:43 +04:00
/*
2012-03-09 01:50:54 +04:00
Some of this code is credited to Linux USB open source files that are
distributed with Linux .
2012-03-09 01:18:43 +04:00
Copyright : 2007 Metrologic Instruments . All rights reserved .
Copyright : 2011 Azimut Ltd . < http : //azimutrzn.ru/>
*/
# include <linux/kernel.h>
# include <linux/tty.h>
# include <linux/module.h>
# include <linux/usb.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/tty_driver.h>
# include <linux/tty_flip.h>
# include <linux/moduleparam.h>
# include <linux/spinlock.h>
2012-03-09 01:50:54 +04:00
# include <linux/uaccess.h>
2012-03-09 01:18:43 +04:00
# include <linux/usb/serial.h>
# define DRIVER_DESC "Metrologic Instruments Inc. - USB-POS driver"
2012-03-09 01:42:41 +04:00
/* Product information. */
# define FOCUS_VENDOR_ID 0x0C2E
2012-03-20 00:46:31 +04:00
# define FOCUS_PRODUCT_ID_BI 0x0720
# define FOCUS_PRODUCT_ID_UNI 0x0700
2012-03-09 01:42:41 +04:00
# define METROUSB_SET_REQUEST_TYPE 0x40
# define METROUSB_SET_MODEM_CTRL_REQUEST 10
# define METROUSB_SET_BREAK_REQUEST 0x40
# define METROUSB_MCR_NONE 0x08 /* Deactivate DTR and RTS. */
# define METROUSB_MCR_RTS 0x0a /* Activate RTS. */
# define METROUSB_MCR_DTR 0x09 /* Activate DTR. */
2012-03-09 01:50:54 +04:00
# define WDR_TIMEOUT 5000 /* default urb timeout. */
2012-03-09 01:42:41 +04:00
/* Private data structure. */
struct metrousb_private {
spinlock_t lock ;
int throttled ;
unsigned long control_state ;
} ;
2012-03-09 01:18:43 +04:00
/* Device table list. */
2013-12-29 22:22:54 +04:00
static const struct usb_device_id id_table [ ] = {
2012-03-20 00:46:31 +04:00
{ USB_DEVICE ( FOCUS_VENDOR_ID , FOCUS_PRODUCT_ID_BI ) } ,
2012-03-09 01:18:43 +04:00
{ USB_DEVICE ( FOCUS_VENDOR_ID , FOCUS_PRODUCT_ID_UNI ) } ,
{ } , /* Terminating entry. */
} ;
MODULE_DEVICE_TABLE ( usb , id_table ) ;
2012-03-20 00:46:33 +04:00
/* UNI-Directional mode commands for device configure */
# define UNI_CMD_OPEN 0x80
# define UNI_CMD_CLOSE 0xFF
2013-12-19 14:13:17 +04:00
static inline int metrousb_is_unidirectional_mode ( struct usb_serial_port * port )
2012-03-20 00:46:33 +04:00
{
__u16 product_id = le16_to_cpu (
port - > serial - > dev - > descriptor . idProduct ) ;
return product_id = = FOCUS_PRODUCT_ID_UNI ;
}
static int metrousb_send_unidirectional_cmd ( u8 cmd , struct usb_serial_port * port )
{
int ret ;
int actual_len ;
u8 * buffer_cmd = NULL ;
if ( ! metrousb_is_unidirectional_mode ( port ) )
return 0 ;
buffer_cmd = kzalloc ( sizeof ( cmd ) , GFP_KERNEL ) ;
if ( ! buffer_cmd )
return - ENOMEM ;
* buffer_cmd = cmd ;
ret = usb_interrupt_msg ( port - > serial - > dev ,
usb_sndintpipe ( port - > serial - > dev , port - > interrupt_out_endpointAddress ) ,
buffer_cmd , sizeof ( cmd ) ,
& actual_len , USB_CTRL_SET_TIMEOUT ) ;
kfree ( buffer_cmd ) ;
if ( ret < 0 )
return ret ;
else if ( actual_len ! = sizeof ( cmd ) )
return - EIO ;
return 0 ;
}
2012-03-09 01:55:41 +04:00
static void metrousb_read_int_callback ( struct urb * urb )
2012-03-09 01:18:43 +04:00
{
2012-03-09 02:00:11 +04:00
struct usb_serial_port * port = urb - > context ;
2012-03-09 01:55:41 +04:00
struct metrousb_private * metro_priv = usb_get_serial_port_data ( port ) ;
unsigned char * data = urb - > transfer_buffer ;
int throttled = 0 ;
int result = 0 ;
unsigned long flags = 0 ;
2012-03-09 02:16:12 +04:00
dev_dbg ( & port - > dev , " %s \n " , __func__ ) ;
2012-03-09 01:18:43 +04:00
2012-03-09 01:55:41 +04:00
switch ( urb - > status ) {
case 0 :
/* Success status, read from the port. */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
/* urb has been terminated. */
2012-03-09 02:16:12 +04:00
dev_dbg ( & port - > dev ,
" %s - urb shutting down, error code=%d \n " ,
2012-03-20 00:46:35 +04:00
__func__ , urb - > status ) ;
2012-03-09 01:55:41 +04:00
return ;
default :
2012-03-09 02:16:12 +04:00
dev_dbg ( & port - > dev ,
" %s - non-zero urb received, error code=%d \n " ,
2012-03-20 00:46:35 +04:00
__func__ , urb - > status ) ;
2012-03-09 01:55:41 +04:00
goto exit ;
}
/* Set the data read from the usb port into the serial port buffer. */
2013-01-03 18:53:06 +04:00
if ( urb - > actual_length ) {
2012-03-09 01:55:41 +04:00
/* Loop through the data copying each byte to the tty layer. */
2013-01-03 18:53:04 +04:00
tty_insert_flip_string ( & port - > port , data , urb - > actual_length ) ;
2012-03-09 01:55:41 +04:00
/* Force the data to the tty layer. */
2013-01-03 18:53:06 +04:00
tty_flip_buffer_push ( & port - > port ) ;
2012-03-09 01:55:41 +04:00
}
/* Set any port variables. */
spin_lock_irqsave ( & metro_priv - > lock , flags ) ;
throttled = metro_priv - > throttled ;
spin_unlock_irqrestore ( & metro_priv - > lock , flags ) ;
/* Continue trying to read if set. */
if ( ! throttled ) {
usb_fill_int_urb ( port - > interrupt_in_urb , port - > serial - > dev ,
usb_rcvintpipe ( port - > serial - > dev , port - > interrupt_in_endpointAddress ) ,
port - > interrupt_in_urb - > transfer_buffer ,
port - > interrupt_in_urb - > transfer_buffer_length ,
metrousb_read_int_callback , port , 1 ) ;
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_ATOMIC ) ;
2012-03-09 02:16:12 +04:00
if ( result )
2012-03-20 00:46:34 +04:00
dev_err ( & port - > dev ,
2012-03-09 02:16:12 +04:00
" %s - failed submitting interrupt in urb, error code=%d \n " ,
__func__ , result ) ;
2012-03-09 01:18:43 +04:00
}
2012-03-09 01:55:41 +04:00
return ;
exit :
/* Try to resubmit the urb. */
result = usb_submit_urb ( urb , GFP_ATOMIC ) ;
2012-03-09 02:16:12 +04:00
if ( result )
2012-03-20 00:46:34 +04:00
dev_err ( & port - > dev ,
2012-03-09 02:16:12 +04:00
" %s - failed submitting interrupt in urb, error code=%d \n " ,
__func__ , result ) ;
2012-03-09 01:18:43 +04:00
}
2012-03-20 00:46:32 +04:00
static void metrousb_write_int_callback ( struct urb * urb )
{
struct usb_serial_port * port = urb - > context ;
dev_warn ( & port - > dev , " %s not implemented yet. \n " ,
__func__ ) ;
}
2012-03-09 01:55:41 +04:00
static void metrousb_cleanup ( struct usb_serial_port * port )
2012-03-09 01:18:43 +04:00
{
2012-03-09 02:16:12 +04:00
dev_dbg ( & port - > dev , " %s \n " , __func__ ) ;
2012-03-09 01:55:41 +04:00
2012-10-25 12:29:00 +04:00
usb_unlink_urb ( port - > interrupt_in_urb ) ;
usb_kill_urb ( port - > interrupt_in_urb ) ;
2013-03-21 15:37:40 +04:00
metrousb_send_unidirectional_cmd ( UNI_CMD_CLOSE , port ) ;
2012-03-09 01:18:43 +04:00
}
2012-03-09 01:50:54 +04:00
static int metrousb_open ( struct tty_struct * tty , struct usb_serial_port * port )
2012-03-09 01:18:43 +04:00
{
struct usb_serial * serial = port - > serial ;
struct metrousb_private * metro_priv = usb_get_serial_port_data ( port ) ;
unsigned long flags = 0 ;
int result = 0 ;
2012-03-09 02:16:12 +04:00
dev_dbg ( & port - > dev , " %s \n " , __func__ ) ;
2012-03-09 01:18:43 +04:00
/* Make sure the urb is initialized. */
if ( ! port - > interrupt_in_urb ) {
2012-03-20 00:46:34 +04:00
dev_err ( & port - > dev , " %s - interrupt urb not initialized \n " ,
2012-03-09 02:16:12 +04:00
__func__ ) ;
2012-03-09 01:18:43 +04:00
return - ENODEV ;
}
/* Set the private data information for the port. */
spin_lock_irqsave ( & metro_priv - > lock , flags ) ;
metro_priv - > control_state = 0 ;
metro_priv - > throttled = 0 ;
spin_unlock_irqrestore ( & metro_priv - > lock , flags ) ;
/* Clear the urb pipe. */
usb_clear_halt ( serial - > dev , port - > interrupt_in_urb - > pipe ) ;
/* Start reading from the device */
2012-03-09 01:50:54 +04:00
usb_fill_int_urb ( port - > interrupt_in_urb , serial - > dev ,
usb_rcvintpipe ( serial - > dev , port - > interrupt_in_endpointAddress ) ,
2012-03-09 01:18:43 +04:00
port - > interrupt_in_urb - > transfer_buffer ,
port - > interrupt_in_urb - > transfer_buffer_length ,
metrousb_read_int_callback , port , 1 ) ;
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_KERNEL ) ;
if ( result ) {
2012-03-20 00:46:34 +04:00
dev_err ( & port - > dev ,
2012-03-09 02:16:12 +04:00
" %s - failed submitting interrupt in urb, error code=%d \n " ,
__func__ , result ) ;
2012-03-09 01:18:43 +04:00
goto exit ;
}
2012-03-20 00:46:33 +04:00
/* Send activate cmd to device */
result = metrousb_send_unidirectional_cmd ( UNI_CMD_OPEN , port ) ;
if ( result ) {
dev_err ( & port - > dev ,
2013-06-06 21:32:00 +04:00
" %s - failed to configure device, error code=%d \n " ,
__func__ , result ) ;
2012-03-20 00:46:33 +04:00
goto exit ;
}
2012-03-09 02:16:12 +04:00
dev_dbg ( & port - > dev , " %s - port open \n " , __func__ ) ;
2012-03-09 01:18:43 +04:00
exit :
return result ;
}
static int metrousb_set_modem_ctrl ( struct usb_serial * serial , unsigned int control_state )
{
int retval = 0 ;
unsigned char mcr = METROUSB_MCR_NONE ;
2012-03-09 02:16:12 +04:00
dev_dbg ( & serial - > dev - > dev , " %s - control state = %d \n " ,
__func__ , control_state ) ;
2012-03-09 01:18:43 +04:00
/* Set the modem control value. */
if ( control_state & TIOCM_DTR )
mcr | = METROUSB_MCR_DTR ;
if ( control_state & TIOCM_RTS )
mcr | = METROUSB_MCR_RTS ;
/* Send the command to the usb port. */
retval = usb_control_msg ( serial - > dev , usb_sndctrlpipe ( serial - > dev , 0 ) ,
METROUSB_SET_REQUEST_TYPE , METROUSB_SET_MODEM_CTRL_REQUEST ,
control_state , 0 , NULL , 0 , WDR_TIMEOUT ) ;
if ( retval < 0 )
2012-03-20 00:46:34 +04:00
dev_err ( & serial - > dev - > dev ,
2012-03-09 02:16:12 +04:00
" %s - set modem ctrl=0x%x failed, error code=%d \n " ,
__func__ , mcr , retval ) ;
2012-03-09 01:18:43 +04:00
return retval ;
}
2012-10-25 12:28:59 +04:00
static int metrousb_port_probe ( struct usb_serial_port * port )
2012-03-09 01:18:43 +04:00
{
2012-10-25 12:28:59 +04:00
struct metrousb_private * metro_priv ;
2012-03-09 01:18:43 +04:00
2012-10-25 12:28:59 +04:00
metro_priv = kzalloc ( sizeof ( * metro_priv ) , GFP_KERNEL ) ;
if ( ! metro_priv )
return - ENOMEM ;
2012-03-09 01:18:43 +04:00
2012-10-25 12:28:59 +04:00
spin_lock_init ( & metro_priv - > lock ) ;
2012-03-09 01:18:43 +04:00
2012-10-25 12:28:59 +04:00
usb_set_serial_port_data ( port , metro_priv ) ;
2012-03-09 01:18:43 +04:00
2012-10-25 12:28:59 +04:00
return 0 ;
2012-03-09 01:18:43 +04:00
}
2012-10-25 12:28:59 +04:00
static int metrousb_port_remove ( struct usb_serial_port * port )
2012-03-09 01:18:43 +04:00
{
struct metrousb_private * metro_priv ;
2012-10-25 12:28:59 +04:00
metro_priv = usb_get_serial_port_data ( port ) ;
kfree ( metro_priv ) ;
2012-03-09 01:18:43 +04:00
return 0 ;
}
2012-03-09 01:50:54 +04:00
static void metrousb_throttle ( struct tty_struct * tty )
2012-03-09 01:18:43 +04:00
{
struct usb_serial_port * port = tty - > driver_data ;
struct metrousb_private * metro_priv = usb_get_serial_port_data ( port ) ;
unsigned long flags = 0 ;
2012-03-09 02:16:12 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2012-03-09 01:18:43 +04:00
/* Set the private information for the port to stop reading data. */
spin_lock_irqsave ( & metro_priv - > lock , flags ) ;
metro_priv - > throttled = 1 ;
spin_unlock_irqrestore ( & metro_priv - > lock , flags ) ;
}
2012-03-09 01:50:54 +04:00
static int metrousb_tiocmget ( struct tty_struct * tty )
2012-03-09 01:18:43 +04:00
{
unsigned long control_state = 0 ;
struct usb_serial_port * port = tty - > driver_data ;
struct metrousb_private * metro_priv = usb_get_serial_port_data ( port ) ;
unsigned long flags = 0 ;
2012-03-09 02:16:12 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2012-03-09 01:18:43 +04:00
spin_lock_irqsave ( & metro_priv - > lock , flags ) ;
control_state = metro_priv - > control_state ;
spin_unlock_irqrestore ( & metro_priv - > lock , flags ) ;
return control_state ;
}
2012-03-09 01:50:54 +04:00
static int metrousb_tiocmset ( struct tty_struct * tty ,
unsigned int set , unsigned int clear )
2012-03-09 01:18:43 +04:00
{
struct usb_serial_port * port = tty - > driver_data ;
struct usb_serial * serial = port - > serial ;
struct metrousb_private * metro_priv = usb_get_serial_port_data ( port ) ;
unsigned long flags = 0 ;
unsigned long control_state = 0 ;
2012-03-09 02:16:12 +04:00
dev_dbg ( tty - > dev , " %s - set=%d, clear=%d \n " , __func__ , set , clear ) ;
2012-03-09 01:18:43 +04:00
spin_lock_irqsave ( & metro_priv - > lock , flags ) ;
control_state = metro_priv - > control_state ;
2012-03-09 01:50:54 +04:00
/* Set the RTS and DTR values. */
2012-03-09 01:18:43 +04:00
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 ;
metro_priv - > control_state = control_state ;
spin_unlock_irqrestore ( & metro_priv - > lock , flags ) ;
return metrousb_set_modem_ctrl ( serial , control_state ) ;
}
2012-03-09 01:50:54 +04:00
static void metrousb_unthrottle ( struct tty_struct * tty )
2012-03-09 01:18:43 +04:00
{
struct usb_serial_port * port = tty - > driver_data ;
struct metrousb_private * metro_priv = usb_get_serial_port_data ( port ) ;
unsigned long flags = 0 ;
int result = 0 ;
2012-03-09 02:16:12 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2012-03-09 01:18:43 +04:00
/* Set the private information for the port to resume reading data. */
spin_lock_irqsave ( & metro_priv - > lock , flags ) ;
metro_priv - > throttled = 0 ;
spin_unlock_irqrestore ( & metro_priv - > lock , flags ) ;
/* Submit the urb to read from the port. */
port - > interrupt_in_urb - > dev = port - > serial - > dev ;
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_ATOMIC ) ;
2012-03-09 02:16:12 +04:00
if ( result )
2012-03-20 00:46:34 +04:00
dev_err ( tty - > dev ,
2012-03-09 02:16:12 +04:00
" failed submitting interrupt in urb error code=%d \n " ,
result ) ;
2012-03-09 01:18:43 +04:00
}
2012-03-09 01:55:41 +04:00
static struct usb_serial_driver metrousb_device = {
. driver = {
. owner = THIS_MODULE ,
. name = " metro-usb " ,
} ,
2012-03-20 00:46:37 +04:00
. description = " Metrologic USB to Serial " ,
2012-03-09 01:55:41 +04:00
. id_table = id_table ,
. num_ports = 1 ,
. open = metrousb_open ,
. close = metrousb_cleanup ,
. read_int_callback = metrousb_read_int_callback ,
2012-03-20 00:46:32 +04:00
. write_int_callback = metrousb_write_int_callback ,
2012-10-25 12:28:59 +04:00
. port_probe = metrousb_port_probe ,
. port_remove = metrousb_port_remove ,
2012-03-09 01:55:41 +04:00
. throttle = metrousb_throttle ,
. unthrottle = metrousb_unthrottle ,
. tiocmget = metrousb_tiocmget ,
. tiocmset = metrousb_tiocmset ,
} ;
static struct usb_serial_driver * const serial_drivers [ ] = {
& metrousb_device ,
NULL ,
} ;
2012-05-09 02:46:14 +04:00
module_usb_serial_driver ( serial_drivers , id_table ) ;
2012-03-09 01:39:53 +04:00
2012-03-09 01:18:43 +04:00
MODULE_LICENSE ( " GPL " ) ;
2012-03-09 01:50:54 +04:00
MODULE_AUTHOR ( " Philip Nicastro " ) ;
MODULE_AUTHOR ( " Aleksey Babahin <tamerlan311@gmail.com> " ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;