2005-04-17 02:20:36 +04:00
/*
* KOBIL USB Smart Card Terminal Driver
*
2008-07-22 14:14:10 +04:00
* Copyright ( C ) 2002 KOBIL Systems GmbH
2005-04-17 02:20:36 +04:00
* Author : Thomas Wahrenbruch
*
* Contact : linuxusb @ kobil . de
*
* This program is largely derived from work by the linux - usb group
* and associated source files . Please see the usb / serial files for
* individual credits and copyrights .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* Thanks to Greg Kroah - Hartman ( greg @ kroah . com ) for his help and
* patience .
*
* Supported readers : USB TWIN , KAAN Standard Plus and SecOVID Reader Plus
* ( Adapter K ) , B1 Professional and KAAN Professional ( Adapter B )
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/init.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:10 +04:00
# include <linux/uaccess.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>
2005-04-17 02:20:36 +04:00
# include <linux/ioctl.h>
# include "kobil_sct.h"
static int debug ;
/* Version Information */
# define DRIVER_VERSION "21 / 05 / 2004"
# define DRIVER_AUTHOR "KOBIL Systems GmbH - http: //www.kobil.com"
# define DRIVER_DESC "KOBIL USB Smart Card Terminal Driver (experimental)"
# define KOBIL_VENDOR_ID 0x0D46
# define KOBIL_ADAPTER_B_PRODUCT_ID 0x2011
# define KOBIL_ADAPTER_K_PRODUCT_ID 0x2012
# define KOBIL_USBTWIN_PRODUCT_ID 0x0078
# define KOBIL_KAAN_SIM_PRODUCT_ID 0x0081
# define KOBIL_TIMEOUT 500
# define KOBIL_BUF_LENGTH 300
/* Function prototypes */
2008-07-22 14:14:10 +04:00
static int kobil_startup ( struct usb_serial * serial ) ;
2009-06-02 19:53:55 +04:00
static void kobil_release ( struct usb_serial * serial ) ;
2009-09-20 00:13:26 +04:00
static int kobil_open ( struct tty_struct * tty , struct usb_serial_port * port ) ;
2009-06-11 15:26:29 +04:00
static void kobil_close ( struct usb_serial_port * port ) ;
2008-07-22 14:14:10 +04:00
static int kobil_write ( struct tty_struct * tty , struct usb_serial_port * port ,
2005-04-17 02:20:36 +04:00
const unsigned char * buf , int count ) ;
2008-07-22 14:09:07 +04:00
static int kobil_write_room ( struct tty_struct * tty ) ;
2011-02-14 19:27:06 +03:00
static int kobil_ioctl ( struct tty_struct * tty ,
2005-04-17 02:20:36 +04:00
unsigned int cmd , unsigned long arg ) ;
2011-02-14 19:26:14 +03:00
static int kobil_tiocmget ( struct tty_struct * tty ) ;
2011-02-14 19:26:50 +03:00
static int kobil_tiocmset ( struct tty_struct * tty ,
2005-04-17 02:20:36 +04:00
unsigned int set , unsigned int clear ) ;
2008-07-22 14:14:10 +04:00
static void kobil_read_int_callback ( struct urb * urb ) ;
static void kobil_write_callback ( struct urb * purb ) ;
static void kobil_set_termios ( struct tty_struct * tty ,
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port , struct ktermios * old ) ;
2009-09-20 00:13:33 +04:00
static void kobil_init_termios ( struct tty_struct * tty ) ;
2005-04-17 02:20:36 +04:00
2010-01-10 17:34:24 +03:00
static const struct usb_device_id id_table [ ] = {
2005-04-17 02:20:36 +04:00
{ USB_DEVICE ( KOBIL_VENDOR_ID , KOBIL_ADAPTER_B_PRODUCT_ID ) } ,
{ USB_DEVICE ( KOBIL_VENDOR_ID , KOBIL_ADAPTER_K_PRODUCT_ID ) } ,
{ USB_DEVICE ( KOBIL_VENDOR_ID , KOBIL_USBTWIN_PRODUCT_ID ) } ,
{ USB_DEVICE ( KOBIL_VENDOR_ID , KOBIL_KAAN_SIM_PRODUCT_ID ) } ,
{ } /* Terminating entry */
} ;
2008-07-22 14:14:10 +04:00
MODULE_DEVICE_TABLE ( usb , id_table ) ;
2005-04-17 02:20:36 +04:00
static struct usb_driver kobil_driver = {
. name = " kobil " ,
. probe = usb_serial_probe ,
. disconnect = usb_serial_disconnect ,
. id_table = id_table ,
2005-11-17 00:41:28 +03:00
. no_dynamic_id = 1 ,
2005-04-17 02:20:36 +04:00
} ;
2005-06-21 08:15:16 +04:00
static struct usb_serial_driver kobil_device = {
2005-06-21 08:15:16 +04:00
. driver = {
. owner = THIS_MODULE ,
2005-06-21 08:15:16 +04:00
. name = " kobil " ,
2005-06-21 08:15:16 +04:00
} ,
2005-06-21 08:15:16 +04:00
. description = " KOBIL USB smart card terminal " ,
2006-12-17 23:50:24 +03:00
. usb_driver = & kobil_driver ,
2005-04-17 02:20:36 +04:00
. id_table = id_table ,
. num_ports = 1 ,
. attach = kobil_startup ,
2009-06-02 19:53:55 +04:00
. release = kobil_release ,
2005-04-17 02:20:36 +04:00
. ioctl = kobil_ioctl ,
2007-08-23 02:09:16 +04:00
. set_termios = kobil_set_termios ,
2009-09-20 00:13:33 +04:00
. init_termios = kobil_init_termios ,
2005-04-17 02:20:36 +04:00
. tiocmget = kobil_tiocmget ,
. tiocmset = kobil_tiocmset ,
. open = kobil_open ,
. close = kobil_close ,
. write = kobil_write ,
. write_room = kobil_write_room ,
. read_int_callback = kobil_read_int_callback ,
} ;
struct kobil_private {
int write_int_endpoint_address ;
int read_int_endpoint_address ;
2008-07-22 14:14:10 +04:00
unsigned char buf [ KOBIL_BUF_LENGTH ] ; /* buffer for the APDU to send */
int filled ; /* index of the last char in buf */
int cur_pos ; /* index of the next char to send in buf */
2005-04-17 02:20:36 +04:00
__u16 device_type ;
} ;
2008-07-22 14:14:10 +04:00
static int kobil_startup ( struct usb_serial * serial )
2005-04-17 02:20:36 +04:00
{
int i ;
struct kobil_private * priv ;
struct usb_device * pdev ;
struct usb_host_config * actconfig ;
struct usb_interface * interface ;
struct usb_host_interface * altsetting ;
struct usb_host_endpoint * endpoint ;
priv = kmalloc ( sizeof ( struct kobil_private ) , GFP_KERNEL ) ;
2008-07-22 14:14:10 +04:00
if ( ! priv )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
priv - > filled = 0 ;
priv - > cur_pos = 0 ;
priv - > device_type = le16_to_cpu ( serial - > dev - > descriptor . idProduct ) ;
2008-07-22 14:14:10 +04:00
switch ( priv - > device_type ) {
2005-04-17 02:20:36 +04:00
case KOBIL_ADAPTER_B_PRODUCT_ID :
printk ( KERN_DEBUG " KOBIL B1 PRO / KAAN PRO detected \n " ) ;
break ;
case KOBIL_ADAPTER_K_PRODUCT_ID :
2008-07-22 14:14:10 +04:00
printk ( KERN_DEBUG
" KOBIL KAAN Standard Plus / SecOVID Reader Plus detected \n " ) ;
2005-04-17 02:20:36 +04:00
break ;
case KOBIL_USBTWIN_PRODUCT_ID :
printk ( KERN_DEBUG " KOBIL USBTWIN detected \n " ) ;
break ;
case KOBIL_KAAN_SIM_PRODUCT_ID :
printk ( KERN_DEBUG " KOBIL KAAN SIM detected \n " ) ;
break ;
}
usb_set_serial_port_data ( serial - > port [ 0 ] , priv ) ;
2008-07-22 14:14:10 +04:00
/* search for the necessary endpoints */
2005-04-17 02:20:36 +04:00
pdev = serial - > dev ;
2008-07-22 14:14:10 +04:00
actconfig = pdev - > actconfig ;
interface = actconfig - > interface [ 0 ] ;
2005-04-17 02:20:36 +04:00
altsetting = interface - > cur_altsetting ;
2008-07-22 14:14:10 +04:00
endpoint = altsetting - > endpoint ;
for ( i = 0 ; i < altsetting - > desc . bNumEndpoints ; i + + ) {
2005-04-17 02:20:36 +04:00
endpoint = & altsetting - > endpoint [ i ] ;
2006-10-26 20:02:53 +04:00
if ( usb_endpoint_is_int_out ( & endpoint - > desc ) ) {
2008-07-22 14:14:10 +04:00
dbg ( " %s Found interrupt out endpoint. Address: %d " ,
__func__ , endpoint - > desc . bEndpointAddress ) ;
priv - > write_int_endpoint_address =
endpoint - > desc . bEndpointAddress ;
}
2006-10-26 20:02:53 +04:00
if ( usb_endpoint_is_int_in ( & endpoint - > desc ) ) {
2008-07-22 14:14:10 +04:00
dbg ( " %s Found interrupt in endpoint. Address: %d " ,
__func__ , endpoint - > desc . bEndpointAddress ) ;
priv - > read_int_endpoint_address =
endpoint - > desc . bEndpointAddress ;
}
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2009-06-02 19:53:55 +04:00
static void kobil_release ( struct usb_serial * serial )
2005-04-17 02:20:36 +04:00
{
int i ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , serial - > port [ 0 ] - > number ) ;
2005-04-17 02:20:36 +04:00
2009-06-02 19:53:55 +04:00
for ( i = 0 ; i < serial - > num_ports ; + + i )
2005-04-17 02:20:36 +04:00
kfree ( usb_get_serial_port_data ( serial - > port [ i ] ) ) ;
}
2009-09-20 00:13:33 +04:00
static void kobil_init_termios ( struct tty_struct * tty )
{
/* Default to echo off and other sane device settings */
tty - > termios - > c_lflag = 0 ;
tty - > termios - > c_lflag & = ~ ( ISIG | ICANON | ECHO | IEXTEN | XCASE ) ;
tty - > termios - > c_iflag = IGNBRK | IGNPAR | IXOFF ;
/* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */
tty - > termios - > c_oflag & = ~ ONLCR ;
}
2005-04-17 02:20:36 +04:00
2009-09-20 00:13:26 +04:00
static int kobil_open ( struct tty_struct * tty , struct usb_serial_port * port )
2005-04-17 02:20:36 +04:00
{
2007-08-23 02:09:16 +04:00
int result = 0 ;
2005-04-17 02:20:36 +04:00
struct kobil_private * priv ;
unsigned char * transfer_buffer ;
int transfer_buffer_length = 8 ;
int write_urb_transfer_buffer_length = 8 ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
priv = usb_get_serial_port_data ( port ) ;
2008-07-22 14:14:10 +04:00
/* allocate memory for transfer buffer */
2006-02-27 23:29:43 +03:00
transfer_buffer = kzalloc ( transfer_buffer_length , GFP_KERNEL ) ;
2008-07-22 14:14:10 +04:00
if ( ! transfer_buffer )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2008-07-22 14:14:10 +04:00
/* allocate write_urb */
if ( ! port - > write_urb ) {
dbg ( " %s - port %d Allocating port->write_urb " ,
__func__ , port - > number ) ;
port - > write_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! port - > write_urb ) {
2008-07-22 14:14:10 +04:00
dbg ( " %s - port %d usb_alloc_urb failed " ,
__func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
kfree ( transfer_buffer ) ;
return - ENOMEM ;
}
}
2008-07-22 14:14:10 +04:00
/* allocate memory for write_urb transfer buffer */
port - > write_urb - > transfer_buffer =
kmalloc ( write_urb_transfer_buffer_length , GFP_KERNEL ) ;
if ( ! port - > write_urb - > transfer_buffer ) {
2005-04-17 02:20:36 +04:00
kfree ( transfer_buffer ) ;
usb_free_urb ( port - > write_urb ) ;
port - > write_urb = NULL ;
return - ENOMEM ;
2008-07-22 14:14:10 +04:00
}
/* get hardware version */
result = usb_control_msg ( port - > serial - > dev ,
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
SUSBCRequest_GetMisc ,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN ,
SUSBCR_MSC_GetHWVersion ,
0 ,
transfer_buffer ,
transfer_buffer_length ,
KOBIL_TIMEOUT
) ;
dbg ( " %s - port %d Send get_HW_version URB returns: %i " ,
__func__ , port - > number , result ) ;
dbg ( " Harware version: %i.%i.%i " ,
transfer_buffer [ 0 ] , transfer_buffer [ 1 ] , transfer_buffer [ 2 ] ) ;
/* get firmware version */
result = usb_control_msg ( port - > serial - > dev ,
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
SUSBCRequest_GetMisc ,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN ,
SUSBCR_MSC_GetFWVersion ,
0 ,
transfer_buffer ,
transfer_buffer_length ,
KOBIL_TIMEOUT
) ;
dbg ( " %s - port %d Send get_FW_version URB returns: %i " ,
__func__ , port - > number , result ) ;
dbg ( " Firmware version: %i.%i.%i " ,
transfer_buffer [ 0 ] , transfer_buffer [ 1 ] , transfer_buffer [ 2 ] ) ;
if ( priv - > device_type = = KOBIL_ADAPTER_B_PRODUCT_ID | |
priv - > device_type = = KOBIL_ADAPTER_K_PRODUCT_ID ) {
/* Setting Baudrate, Parity and Stopbits */
result = usb_control_msg ( port - > serial - > dev ,
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
SUSBCRequest_SetBaudRateParityAndStopBits ,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT ,
SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity |
SUSBCR_SPASB_1StopBit ,
0 ,
transfer_buffer ,
0 ,
KOBIL_TIMEOUT
2005-04-17 02:20:36 +04:00
) ;
2008-07-22 14:14:10 +04:00
dbg ( " %s - port %d Send set_baudrate URB returns: %i " ,
__func__ , port - > number , result ) ;
/* reset all queues */
result = usb_control_msg ( port - > serial - > dev ,
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
SUSBCRequest_Misc ,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT ,
SUSBCR_MSC_ResetAllQueues ,
0 ,
transfer_buffer ,
0 ,
KOBIL_TIMEOUT
2005-04-17 02:20:36 +04:00
) ;
2008-07-22 14:14:10 +04:00
dbg ( " %s - port %d Send reset_all_queues URB returns: %i " ,
__func__ , port - > number , result ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:14:10 +04:00
if ( priv - > device_type = = KOBIL_USBTWIN_PRODUCT_ID | |
priv - > device_type = = KOBIL_ADAPTER_B_PRODUCT_ID | |
2005-04-17 02:20:36 +04:00
priv - > device_type = = KOBIL_KAAN_SIM_PRODUCT_ID ) {
2008-07-22 14:14:10 +04:00
/* start reading (Adapter B 'cause PNP string) */
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_ATOMIC ) ;
dbg ( " %s - port %d Send read URB returns: %i " ,
__func__ , port - > number , result ) ;
2005-04-17 02:20:36 +04:00
}
kfree ( transfer_buffer ) ;
return 0 ;
}
2009-06-11 15:26:29 +04:00
static void kobil_close ( struct usb_serial_port * port )
2005-04-17 02:20:36 +04:00
{
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2009-06-11 15:26:29 +04:00
/* FIXME: Add rts/dtr methods */
2005-04-17 02:20:36 +04:00
if ( port - > write_urb ) {
2010-05-15 19:53:43 +04:00
usb_poison_urb ( port - > write_urb ) ;
kfree ( port - > write_urb - > transfer_buffer ) ;
2008-07-22 14:14:10 +04:00
usb_free_urb ( port - > write_urb ) ;
2005-04-17 02:20:36 +04:00
port - > write_urb = NULL ;
}
2006-11-08 17:36:38 +03:00
usb_kill_urb ( port - > interrupt_in_urb ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-16 02:44:13 +04:00
static void kobil_read_int_callback ( struct urb * urb )
2005-04-17 02:20:36 +04:00
{
int result ;
2007-06-16 02:44:13 +04:00
struct usb_serial_port * port = urb - > context ;
2005-04-17 02:20:36 +04:00
struct tty_struct * tty ;
2007-06-16 02:44:13 +04:00
unsigned char * data = urb - > transfer_buffer ;
int status = urb - > status ;
2008-07-22 14:14:10 +04:00
/* char *dbg_data; */
2005-04-17 02:20:36 +04:00
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2007-06-16 02:44:13 +04:00
if ( status ) {
dbg ( " %s - port %d Read int status not zero: %d " ,
2008-03-04 03:08:34 +03:00
__func__ , port - > number , status ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2007-06-16 02:44:13 +04:00
2008-10-13 13:39:46 +04:00
tty = tty_port_tty_get ( & port - > port ) ;
2011-02-28 12:34:06 +03:00
if ( tty & & urb - > actual_length ) {
2007-06-16 02:44:13 +04:00
2008-07-22 14:14:10 +04:00
/* BEGIN DEBUG */
2005-04-17 02:20:36 +04:00
/*
2008-07-22 14:14:10 +04:00
dbg_data = kzalloc ( ( 3 * purb - > actual_length + 10 )
* sizeof ( char ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! dbg_data ) {
2008-07-22 14:14:10 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:14:10 +04:00
for ( i = 0 ; i < purb - > actual_length ; i + + ) {
sprintf ( dbg_data + 3 * i , " %02X " , data [ i ] ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:14:10 +04:00
dbg ( " <-- %s " , dbg_data ) ;
2005-04-17 02:20:36 +04:00
kfree ( dbg_data ) ;
*/
2008-07-22 14:14:10 +04:00
/* END DEBUG */
2005-04-17 02:20:36 +04:00
2007-06-16 02:44:13 +04:00
tty_insert_flip_string ( tty , data , urb - > actual_length ) ;
2005-04-17 02:20:36 +04:00
tty_flip_buffer_push ( tty ) ;
}
2008-10-13 13:39:46 +04:00
tty_kref_put ( tty ) ;
2005-04-17 02:20:36 +04:00
2007-06-16 02:44:13 +04:00
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_ATOMIC ) ;
2008-07-22 14:14:10 +04:00
dbg ( " %s - port %d Send read URB returns: %i " ,
__func__ , port - > number , result ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:14:10 +04:00
static void kobil_write_callback ( struct urb * purb )
2005-04-17 02:20:36 +04:00
{
}
2008-07-22 14:14:10 +04:00
static int kobil_write ( struct tty_struct * tty , struct usb_serial_port * port ,
2005-04-17 02:20:36 +04:00
const unsigned char * buf , int count )
{
int length = 0 ;
int result = 0 ;
int todo = 0 ;
2008-07-22 14:14:10 +04:00
struct kobil_private * priv ;
2005-04-17 02:20:36 +04:00
if ( count = = 0 ) {
2008-07-22 14:14:10 +04:00
dbg ( " %s - port %d write request of 0 bytes " ,
__func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
priv = usb_get_serial_port_data ( port ) ;
if ( count > ( KOBIL_BUF_LENGTH - priv - > filled ) ) {
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d Error: write request bigger than buffer size " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
2008-07-22 14:14:10 +04:00
/* Copy data to buffer */
memcpy ( priv - > buf + priv - > filled , buf , count ) ;
usb_serial_debug_data ( debug , & port - > dev , __func__ , count ,
priv - > buf + priv - > filled ) ;
2005-04-17 02:20:36 +04:00
priv - > filled = priv - > filled + count ;
2008-07-22 14:14:10 +04:00
/* only send complete block. TWIN, KAAN SIM and adapter K
use the same protocol . */
if ( ( ( priv - > device_type ! = KOBIL_ADAPTER_B_PRODUCT_ID ) & & ( priv - > filled > 2 ) & & ( priv - > filled > = ( priv - > buf [ 1 ] + 3 ) ) ) | |
( ( priv - > device_type = = KOBIL_ADAPTER_B_PRODUCT_ID ) & & ( priv - > filled > 3 ) & & ( priv - > filled > = ( priv - > buf [ 2 ] + 4 ) ) ) ) {
/* stop reading (except TWIN and KAAN SIM) */
if ( ( priv - > device_type = = KOBIL_ADAPTER_B_PRODUCT_ID )
| | ( priv - > device_type = = KOBIL_ADAPTER_K_PRODUCT_ID ) )
2005-04-17 02:20:36 +04:00
usb_kill_urb ( port - > interrupt_in_urb ) ;
todo = priv - > filled - priv - > cur_pos ;
2008-07-22 14:14:10 +04:00
while ( todo > 0 ) {
/* max 8 byte in one urb (endpoint size) */
2005-04-17 02:20:36 +04:00
length = ( todo < 8 ) ? todo : 8 ;
2008-07-22 14:14:10 +04:00
/* copy data to transfer buffer */
memcpy ( port - > write_urb - > transfer_buffer ,
priv - > buf + priv - > cur_pos , length ) ;
usb_fill_int_urb ( port - > write_urb ,
port - > serial - > dev ,
usb_sndintpipe ( port - > serial - > dev ,
priv - > write_int_endpoint_address ) ,
port - > write_urb - > transfer_buffer ,
length ,
kobil_write_callback ,
port ,
8
) ;
2005-04-17 02:20:36 +04:00
priv - > cur_pos = priv - > cur_pos + length ;
2008-07-22 14:14:10 +04:00
result = usb_submit_urb ( port - > write_urb , GFP_NOIO ) ;
dbg ( " %s - port %d Send write URB returns: %i " ,
__func__ , port - > number , result ) ;
2005-04-17 02:20:36 +04:00
todo = priv - > filled - priv - > cur_pos ;
2008-07-22 14:14:10 +04:00
if ( todo > 0 )
2005-04-17 02:20:36 +04:00
msleep ( 24 ) ;
2008-07-22 14:14:10 +04:00
}
2005-04-17 02:20:36 +04:00
priv - > filled = 0 ;
priv - > cur_pos = 0 ;
2008-07-22 14:14:10 +04:00
/* start reading (except TWIN and KAAN SIM) */
if ( priv - > device_type = = KOBIL_ADAPTER_B_PRODUCT_ID | |
priv - > device_type = = KOBIL_ADAPTER_K_PRODUCT_ID ) {
result = usb_submit_urb ( port - > interrupt_in_urb ,
GFP_NOIO ) ;
dbg ( " %s - port %d Send read URB returns: %i " ,
__func__ , port - > number , result ) ;
2005-04-17 02:20:36 +04:00
}
}
return count ;
}
2008-07-22 14:14:10 +04:00
static int kobil_write_room ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:14:10 +04:00
/* dbg("%s - port %d", __func__, port->number); */
2008-07-22 14:09:07 +04:00
/* FIXME */
2005-04-17 02:20:36 +04:00
return 8 ;
}
2011-02-14 19:26:14 +03:00
static int kobil_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 ;
2008-07-22 14:14:10 +04:00
struct kobil_private * priv ;
2005-04-17 02:20:36 +04:00
int result ;
unsigned char * transfer_buffer ;
int transfer_buffer_length = 8 ;
priv = usb_get_serial_port_data ( port ) ;
2008-07-22 14:14:10 +04:00
if ( priv - > device_type = = KOBIL_USBTWIN_PRODUCT_ID
| | priv - > device_type = = KOBIL_KAAN_SIM_PRODUCT_ID ) {
/* This device doesn't support ioctl calls */
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2008-07-22 14:14:10 +04:00
/* allocate memory for transfer buffer */
2006-02-27 23:29:43 +03:00
transfer_buffer = kzalloc ( transfer_buffer_length , GFP_KERNEL ) ;
2008-07-22 14:14:10 +04:00
if ( ! transfer_buffer )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2008-07-22 14:14:10 +04:00
result = usb_control_msg ( port - > serial - > dev ,
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
SUSBCRequest_GetStatusLineState ,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN ,
0 ,
0 ,
transfer_buffer ,
transfer_buffer_length ,
KOBIL_TIMEOUT ) ;
dbg ( " %s - port %d Send get_status_line_state URB returns: %i. Statusline: %02x " ,
2008-03-04 03:08:34 +03:00
__func__ , port - > number , result , transfer_buffer [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
2008-02-21 00:40:34 +03:00
result = 0 ;
if ( ( transfer_buffer [ 0 ] & SUSBCR_GSL_DSR ) ! = 0 )
result = TIOCM_DSR ;
2005-04-17 02:20:36 +04:00
kfree ( transfer_buffer ) ;
2008-02-21 00:40:34 +03:00
return result ;
2005-04-17 02:20:36 +04:00
}
2011-02-14 19:26:50 +03:00
static int kobil_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 ;
2008-07-22 14:14:10 +04:00
struct kobil_private * priv ;
2005-04-17 02:20:36 +04:00
int result ;
int dtr = 0 ;
int rts = 0 ;
unsigned char * transfer_buffer ;
int transfer_buffer_length = 8 ;
2008-02-21 00:40:34 +03:00
/* FIXME: locking ? */
2005-04-17 02:20:36 +04:00
priv = usb_get_serial_port_data ( port ) ;
2008-07-22 14:14:10 +04:00
if ( priv - > device_type = = KOBIL_USBTWIN_PRODUCT_ID
| | priv - > device_type = = KOBIL_KAAN_SIM_PRODUCT_ID ) {
/* This device doesn't support ioctl calls */
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2008-07-22 14:14:10 +04:00
/* allocate memory for transfer buffer */
2006-02-27 23:29:43 +03:00
transfer_buffer = kzalloc ( transfer_buffer_length , GFP_KERNEL ) ;
2008-07-22 14:14:10 +04:00
if ( ! transfer_buffer )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
if ( set & TIOCM_RTS )
rts = 1 ;
if ( set & TIOCM_DTR )
dtr = 1 ;
if ( clear & TIOCM_RTS )
rts = 0 ;
if ( clear & TIOCM_DTR )
dtr = 0 ;
if ( priv - > device_type = = KOBIL_ADAPTER_B_PRODUCT_ID ) {
if ( dtr ! = 0 )
2008-07-22 14:14:10 +04:00
dbg ( " %s - port %d Setting DTR " ,
__func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
else
2008-07-22 14:14:10 +04:00
dbg ( " %s - port %d Clearing DTR " ,
__func__ , port - > number ) ;
result = usb_control_msg ( port - > serial - > dev ,
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
SUSBCRequest_SetStatusLinesOrQueues ,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT ,
( ( dtr ! = 0 ) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR ) ,
0 ,
transfer_buffer ,
0 ,
KOBIL_TIMEOUT ) ;
2005-04-17 02:20:36 +04:00
} else {
if ( rts ! = 0 )
2008-07-22 14:14:10 +04:00
dbg ( " %s - port %d Setting RTS " ,
__func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
else
2008-07-22 14:14:10 +04:00
dbg ( " %s - port %d Clearing RTS " ,
__func__ , port - > number ) ;
result = usb_control_msg ( port - > serial - > dev ,
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
SUSBCRequest_SetStatusLinesOrQueues ,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT ,
( ( rts ! = 0 ) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS ) ,
0 ,
transfer_buffer ,
0 ,
KOBIL_TIMEOUT ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:14:10 +04:00
dbg ( " %s - port %d Send set_status_line URB returns: %i " ,
__func__ , port - > number , result ) ;
2005-04-17 02:20:36 +04:00
kfree ( transfer_buffer ) ;
return ( result < 0 ) ? result : 0 ;
}
2008-07-22 14:09:07 +04:00
static void kobil_set_termios ( struct tty_struct * tty ,
struct usb_serial_port * port , struct ktermios * old )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:14:10 +04:00
struct kobil_private * priv ;
2005-04-17 02:20:36 +04:00
int result ;
unsigned short urb_val = 0 ;
2008-07-22 14:09:07 +04:00
int c_cflag = tty - > termios - > c_cflag ;
2007-08-23 02:09:16 +04:00
speed_t speed ;
2005-04-17 02:20:36 +04:00
priv = usb_get_serial_port_data ( port ) ;
2008-07-22 14:14:10 +04:00
if ( priv - > device_type = = KOBIL_USBTWIN_PRODUCT_ID | |
2008-07-22 14:14:22 +04:00
priv - > device_type = = KOBIL_KAAN_SIM_PRODUCT_ID ) {
2008-07-22 14:14:10 +04:00
/* This device doesn't support ioctl calls */
2008-07-22 14:14:22 +04:00
* tty - > termios = * old ;
2007-08-23 02:09:16 +04:00
return ;
2008-07-22 14:14:22 +04:00
}
2005-04-17 02:20:36 +04:00
2008-07-22 14:14:10 +04:00
speed = tty_get_baud_rate ( tty ) ;
switch ( speed ) {
case 1200 :
urb_val = SUSBCR_SBR_1200 ;
break ;
default :
speed = 9600 ;
case 9600 :
urb_val = SUSBCR_SBR_9600 ;
break ;
2007-08-23 02:09:16 +04:00
}
2008-07-22 14:14:10 +04:00
urb_val | = ( c_cflag & CSTOPB ) ? SUSBCR_SPASB_2StopBits :
SUSBCR_SPASB_1StopBit ;
2007-08-23 02:09:16 +04:00
if ( c_cflag & PARENB ) {
2009-12-29 01:01:58 +03:00
if ( c_cflag & PARODD )
2007-08-23 02:09:16 +04:00
urb_val | = SUSBCR_SPASB_OddParity ;
2009-12-29 01:01:58 +03:00
else
2007-08-23 02:09:16 +04:00
urb_val | = SUSBCR_SPASB_EvenParity ;
2009-12-29 01:01:58 +03:00
} else
2007-08-23 02:09:16 +04:00
urb_val | = SUSBCR_SPASB_NoParity ;
2008-07-22 14:09:07 +04:00
tty - > termios - > c_cflag & = ~ CMSPAR ;
tty_encode_baud_rate ( tty , speed , speed ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:14:10 +04:00
result = usb_control_msg ( port - > serial - > dev ,
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
SUSBCRequest_SetBaudRateParityAndStopBits ,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT ,
urb_val ,
0 ,
2009-12-29 01:01:58 +03:00
NULL ,
2008-07-22 14:14:10 +04:00
0 ,
KOBIL_TIMEOUT
2007-08-23 02:09:16 +04:00
) ;
}
2005-04-17 02:20:36 +04:00
2011-02-14 19:27:06 +03:00
static int kobil_ioctl ( struct tty_struct * tty ,
2008-07-22 14:14:10 +04:00
unsigned int cmd , unsigned long arg )
2007-08-23 02:09:16 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2008-07-22 14:14:10 +04:00
struct kobil_private * priv = usb_get_serial_port_data ( port ) ;
2007-08-23 02:09:16 +04:00
unsigned char * transfer_buffer ;
int transfer_buffer_length = 8 ;
int result ;
2008-07-22 14:14:10 +04:00
if ( priv - > device_type = = KOBIL_USBTWIN_PRODUCT_ID | |
priv - > device_type = = KOBIL_KAAN_SIM_PRODUCT_ID )
/* This device doesn't support ioctl calls */
2008-07-22 14:14:22 +04:00
return - ENOIOCTLCMD ;
2005-04-17 02:20:36 +04:00
2007-08-23 02:09:16 +04:00
switch ( cmd ) {
2008-07-22 14:09:07 +04:00
case TCFLSH :
2006-12-13 11:35:56 +03:00
transfer_buffer = kmalloc ( transfer_buffer_length , GFP_KERNEL ) ;
2008-07-22 14:14:10 +04:00
if ( ! transfer_buffer )
return - ENOBUFS ;
result = usb_control_msg ( port - > serial - > dev ,
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
SUSBCRequest_Misc ,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT ,
SUSBCR_MSC_ResetAllQueues ,
0 ,
NULL , /* transfer_buffer, */
0 ,
KOBIL_TIMEOUT
2005-04-17 02:20:36 +04:00
) ;
2008-07-22 14:14:10 +04:00
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d Send reset_all_queues (FLUSH) URB returns: %i " , __func__ , port - > number , result ) ;
2005-04-17 02:20:36 +04:00
kfree ( transfer_buffer ) ;
2008-07-22 14:09:07 +04:00
return ( result < 0 ) ? - EIO : 0 ;
2007-08-23 02:09:16 +04:00
default :
return - ENOIOCTLCMD ;
2005-04-17 02:20:36 +04:00
}
}
2008-07-22 14:14:10 +04:00
static int __init kobil_init ( void )
2005-04-17 02:20:36 +04:00
{
int retval ;
retval = usb_serial_register ( & kobil_device ) ;
if ( retval )
goto failed_usb_serial_register ;
retval = usb_register ( & kobil_driver ) ;
2008-07-22 14:14:10 +04:00
if ( retval )
2005-04-17 02:20:36 +04:00
goto failed_usb_register ;
2008-08-19 00:21:04 +04:00
printk ( KERN_INFO KBUILD_MODNAME " : " DRIVER_VERSION " : "
DRIVER_DESC " \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
failed_usb_register :
usb_serial_deregister ( & kobil_device ) ;
failed_usb_serial_register :
return retval ;
}
2008-07-22 14:14:10 +04:00
static void __exit kobil_exit ( void )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:14:10 +04:00
usb_deregister ( & kobil_driver ) ;
usb_serial_deregister ( & kobil_device ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( kobil_init ) ;
module_exit ( kobil_exit ) ;
2008-07-22 14:14:10 +04:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-04-17 02:20:36 +04:00
module_param ( debug , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Debug enabled or not " ) ;