2008-05-14 08:57:12 +04:00
/******************************************************************************
*
* Driver for Option High Speed Mobile Devices .
*
* Copyright ( C ) 2008 Option International
2009-01-02 16:50:36 +03:00
* Filip Aben < f . aben @ option . com >
* Denis Joseph Barrow < d . barow @ option . com >
2008-05-14 08:57:12 +04:00
* Copyright ( C ) 2007 Andrew Bird ( Sphere Systems Ltd )
* < ajb @ spheresystems . co . uk >
* Copyright ( C ) 2008 Greg Kroah - Hartman < gregkh @ suse . de >
* Copyright ( C ) 2008 Novell , Inc .
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 ,
* USA
*
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/******************************************************************************
*
* Description of the device :
*
* Interface 0 : Contains the IP network interface on the bulk end points .
* The multiplexed serial ports are using the interrupt and
* control endpoints .
* Interrupt contains a bitmap telling which multiplexed
* serialport needs servicing .
*
* Interface 1 : Diagnostics port , uses bulk only , do not submit urbs until the
* port is opened , as this have a huge impact on the network port
* throughput .
*
2009-01-02 16:47:52 +03:00
* Interface 2 : Standard modem interface - circuit switched interface , this
* can be used to make a standard ppp connection however it
* should not be used in conjunction with the IP network interface
* enabled for USB performance reasons i . e . if using this set
* ideally disable_net = 1.
2008-05-14 08:57:12 +04:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/netdevice.h>
# include <linux/module.h>
# include <linux/ethtool.h>
# include <linux/usb.h>
# include <linux/timer.h>
# include <linux/tty.h>
# include <linux/tty_driver.h>
# include <linux/tty_flip.h>
# include <linux/kmod.h>
# include <linux/rfkill.h>
# include <linux/ip.h>
# include <linux/uaccess.h>
# include <linux/usb/cdc.h>
# include <net/arp.h>
# include <asm/byteorder.h>
2009-01-02 16:47:52 +03:00
# include <linux/serial_core.h>
# include <linux/serial.h>
2008-05-14 08:57:12 +04:00
# define DRIVER_VERSION "1.2"
# define MOD_AUTHOR "Option Wireless"
# define MOD_DESCRIPTION "USB High Speed Option driver"
# define MOD_LICENSE "GPL"
# define HSO_MAX_NET_DEVICES 10
# define HSO__MAX_MTU 2048
# define DEFAULT_MTU 1500
# define DEFAULT_MRU 1500
# define CTRL_URB_RX_SIZE 1024
# define CTRL_URB_TX_SIZE 64
# define BULK_URB_RX_SIZE 4096
# define BULK_URB_TX_SIZE 8192
# define MUX_BULK_RX_BUF_SIZE HSO__MAX_MTU
# define MUX_BULK_TX_BUF_SIZE HSO__MAX_MTU
# define MUX_BULK_RX_BUF_COUNT 4
# define USB_TYPE_OPTION_VENDOR 0x20
/* These definitions are used with the struct hso_net flags element */
/* - use *_bit operations on it. (bit indices not values.) */
# define HSO_NET_RUNNING 0
# define HSO_NET_TX_TIMEOUT (HZ*10)
# define HSO_SERIAL_MAGIC 0x48534f31
/* Number of ttys to handle */
# define HSO_SERIAL_TTY_MINORS 256
# define MAX_RX_URBS 2
2008-08-08 23:02:57 +04:00
static inline struct hso_serial * get_serial_by_tty ( struct tty_struct * tty )
{
if ( tty )
return tty - > driver_data ;
return NULL ;
}
2008-05-14 08:57:12 +04:00
/*****************************************************************************/
/* Debugging functions */
/*****************************************************************************/
# define D__(lvl_, fmt, arg...) \
do { \
printk ( lvl_ " [%d:%s]: " fmt " \n " , \
__LINE__ , __func__ , # # arg ) ; \
} while ( 0 )
# define D_(lvl, args...) \
do { \
if ( lvl & debug ) \
D__ ( KERN_INFO , args ) ; \
} while ( 0 )
# define D1(args...) D_(0x01, ##args)
# define D2(args...) D_(0x02, ##args)
# define D3(args...) D_(0x04, ##args)
# define D4(args...) D_(0x08, ##args)
# define D5(args...) D_(0x10, ##args)
/*****************************************************************************/
/* Enumerators */
/*****************************************************************************/
enum pkt_parse_state {
WAIT_IP ,
WAIT_DATA ,
WAIT_SYNC
} ;
/*****************************************************************************/
/* Structs */
/*****************************************************************************/
struct hso_shared_int {
struct usb_endpoint_descriptor * intr_endp ;
void * shared_intr_buf ;
struct urb * shared_intr_urb ;
struct usb_device * usb ;
int use_count ;
int ref_count ;
struct mutex shared_int_lock ;
} ;
struct hso_net {
struct hso_device * parent ;
struct net_device * net ;
struct rfkill * rfkill ;
struct usb_endpoint_descriptor * in_endp ;
struct usb_endpoint_descriptor * out_endp ;
struct urb * mux_bulk_rx_urb_pool [ MUX_BULK_RX_BUF_COUNT ] ;
struct urb * mux_bulk_tx_urb ;
void * mux_bulk_rx_buf_pool [ MUX_BULK_RX_BUF_COUNT ] ;
void * mux_bulk_tx_buf ;
struct sk_buff * skb_rx_buf ;
struct sk_buff * skb_tx_buf ;
enum pkt_parse_state rx_parse_state ;
spinlock_t net_lock ;
unsigned short rx_buf_size ;
unsigned short rx_buf_missing ;
struct iphdr rx_ip_hdr ;
unsigned long flags ;
} ;
2008-09-05 19:12:07 +04:00
enum rx_ctrl_state {
RX_IDLE ,
RX_SENT ,
RX_PENDING
} ;
2009-01-02 16:47:52 +03:00
# define BM_REQUEST_TYPE (0xa1)
# define B_NOTIFICATION (0x20)
# define W_VALUE (0x0)
# define W_INDEX (0x2)
# define W_LENGTH (0x2)
# define B_OVERRUN (0x1<<6)
# define B_PARITY (0x1<<5)
# define B_FRAMING (0x1<<4)
# define B_RING_SIGNAL (0x1<<3)
# define B_BREAK (0x1<<2)
# define B_TX_CARRIER (0x1<<1)
# define B_RX_CARRIER (0x1<<0)
struct hso_serial_state_notification {
u8 bmRequestType ;
u8 bNotification ;
u16 wValue ;
u16 wIndex ;
u16 wLength ;
u16 UART_state_bitmap ;
} __attribute__ ( ( packed ) ) ;
struct hso_tiocmget {
struct mutex mutex ;
wait_queue_head_t waitq ;
int intr_completed ;
struct usb_endpoint_descriptor * endp ;
struct urb * urb ;
struct hso_serial_state_notification serial_state_notification ;
u16 prev_UART_state_bitmap ;
struct uart_icount icount ;
} ;
2008-05-14 08:57:12 +04:00
struct hso_serial {
struct hso_device * parent ;
int magic ;
u8 minor ;
struct hso_shared_int * shared_int ;
/* rx/tx urb could be either a bulk urb or a control urb depending
on which serial port it is used on . */
struct urb * rx_urb [ MAX_RX_URBS ] ;
u8 num_rx_urbs ;
u8 * rx_data [ MAX_RX_URBS ] ;
u16 rx_data_length ; /* should contain allocated length */
struct urb * tx_urb ;
u8 * tx_data ;
u8 * tx_buffer ;
u16 tx_data_length ; /* should contain allocated length */
u16 tx_data_count ;
u16 tx_buffer_count ;
struct usb_ctrlrequest ctrl_req_tx ;
struct usb_ctrlrequest ctrl_req_rx ;
struct usb_endpoint_descriptor * in_endp ;
struct usb_endpoint_descriptor * out_endp ;
2008-09-05 19:12:07 +04:00
enum rx_ctrl_state rx_state ;
2008-05-14 08:57:12 +04:00
u8 rts_state ;
u8 dtr_state ;
unsigned tx_urb_used : 1 ;
/* from usb_serial_port */
struct tty_struct * tty ;
int open_count ;
spinlock_t serial_lock ;
int ( * write_data ) ( struct hso_serial * serial ) ;
2009-01-02 16:47:52 +03:00
struct hso_tiocmget * tiocmget ;
2008-09-05 19:12:07 +04:00
/* Hacks required to get flow control
* working on the serial receive buffers
* so as not to drop characters on the floor .
*/
int curr_rx_urb_idx ;
u16 curr_rx_urb_offset ;
u8 rx_urb_filled [ MAX_RX_URBS ] ;
struct tasklet_struct unthrottle_tasklet ;
struct work_struct retry_unthrottle_workqueue ;
2008-05-14 08:57:12 +04:00
} ;
struct hso_device {
union {
struct hso_serial * dev_serial ;
struct hso_net * dev_net ;
} port_data ;
u32 port_spec ;
u8 is_active ;
u8 usb_gone ;
struct work_struct async_get_intf ;
struct work_struct async_put_intf ;
struct usb_device * usb ;
struct usb_interface * interface ;
struct device * dev ;
struct kref ref ;
2008-11-25 14:52:46 +03:00
struct mutex mutex ;
2008-05-14 08:57:12 +04:00
} ;
/* Type of interface */
# define HSO_INTF_MASK 0xFF00
# define HSO_INTF_MUX 0x0100
# define HSO_INTF_BULK 0x0200
/* Type of port */
# define HSO_PORT_MASK 0xFF
# define HSO_PORT_NO_PORT 0x0
# define HSO_PORT_CONTROL 0x1
# define HSO_PORT_APP 0x2
# define HSO_PORT_GPS 0x3
# define HSO_PORT_PCSC 0x4
# define HSO_PORT_APP2 0x5
# define HSO_PORT_GPS_CONTROL 0x6
# define HSO_PORT_MSD 0x7
# define HSO_PORT_VOICE 0x8
# define HSO_PORT_DIAG2 0x9
# define HSO_PORT_DIAG 0x10
# define HSO_PORT_MODEM 0x11
# define HSO_PORT_NETWORK 0x12
/* Additional device info */
# define HSO_INFO_MASK 0xFF000000
# define HSO_INFO_CRC_BUG 0x01000000
/*****************************************************************************/
/* Prototypes */
/*****************************************************************************/
/* Serial driver functions */
static int hso_serial_tiocmset ( struct tty_struct * tty , struct file * file ,
unsigned int set , unsigned int clear ) ;
static void ctrl_callback ( struct urb * urb ) ;
2008-09-05 19:12:07 +04:00
static int put_rxbuf_data ( struct urb * urb , struct hso_serial * serial ) ;
2008-05-14 08:57:12 +04:00
static void hso_kick_transmit ( struct hso_serial * serial ) ;
/* Helper functions */
static int hso_mux_submit_intr_urb ( struct hso_shared_int * mux_int ,
struct usb_device * usb , gfp_t gfp ) ;
static void log_usb_status ( int status , const char * function ) ;
static struct usb_endpoint_descriptor * hso_get_ep ( struct usb_interface * intf ,
int type , int dir ) ;
static int hso_get_mux_ports ( struct usb_interface * intf , unsigned char * ports ) ;
static void hso_free_interface ( struct usb_interface * intf ) ;
static int hso_start_serial_device ( struct hso_device * hso_dev , gfp_t flags ) ;
static int hso_stop_serial_device ( struct hso_device * hso_dev ) ;
static int hso_start_net_device ( struct hso_device * hso_dev ) ;
static void hso_free_shared_int ( struct hso_shared_int * shared_int ) ;
static int hso_stop_net_device ( struct hso_device * hso_dev ) ;
static void hso_serial_ref_free ( struct kref * ref ) ;
2008-09-05 19:12:07 +04:00
static void hso_std_serial_read_bulk_callback ( struct urb * urb ) ;
static int hso_mux_serial_read ( struct hso_serial * serial ) ;
2008-05-14 08:57:12 +04:00
static void async_get_intf ( struct work_struct * data ) ;
static void async_put_intf ( struct work_struct * data ) ;
static int hso_put_activity ( struct hso_device * hso_dev ) ;
static int hso_get_activity ( struct hso_device * hso_dev ) ;
2009-01-02 16:47:52 +03:00
static void tiocmget_intr_callback ( struct urb * urb ) ;
2008-05-14 08:57:12 +04:00
/*****************************************************************************/
/* Helping functions */
/*****************************************************************************/
/* #define DEBUG */
2008-08-08 23:02:57 +04:00
static inline struct hso_net * dev2net ( struct hso_device * hso_dev )
{
return hso_dev - > port_data . dev_net ;
}
static inline struct hso_serial * dev2ser ( struct hso_device * hso_dev )
{
return hso_dev - > port_data . dev_serial ;
}
2008-05-14 08:57:12 +04:00
/* Debugging functions */
# ifdef DEBUG
static void dbg_dump ( int line_count , const char * func_name , unsigned char * buf ,
unsigned int len )
{
2008-08-08 23:02:57 +04:00
static char name [ 255 ] ;
2008-05-14 08:57:12 +04:00
2008-08-08 23:02:57 +04:00
sprintf ( name , " hso[%d:%s] " , line_count , func_name ) ;
print_hex_dump_bytes ( name , DUMP_PREFIX_NONE , buf , len ) ;
2008-05-14 08:57:12 +04:00
}
# define DUMP(buf_, len_) \
dbg_dump ( __LINE__ , __func__ , buf_ , len_ )
# define DUMP1(buf_, len_) \
do { \
if ( 0x01 & debug ) \
DUMP ( buf_ , len_ ) ; \
} while ( 0 )
# else
# define DUMP(buf_, len_)
# define DUMP1(buf_, len_)
# endif
/* module parameters */
static int debug ;
static int tty_major ;
static int disable_net ;
/* driver info */
static const char driver_name [ ] = " hso " ;
static const char tty_filename [ ] = " ttyHS " ;
static const char * version = __FILE__ " : " DRIVER_VERSION " " MOD_AUTHOR ;
/* the usb driver itself (registered in hso_init) */
static struct usb_driver hso_driver ;
/* serial structures */
static struct tty_driver * tty_drv ;
static struct hso_device * serial_table [ HSO_SERIAL_TTY_MINORS ] ;
static struct hso_device * network_table [ HSO_MAX_NET_DEVICES ] ;
static spinlock_t serial_table_lock ;
static const s32 default_port_spec [ ] = {
HSO_INTF_MUX | HSO_PORT_NETWORK ,
HSO_INTF_BULK | HSO_PORT_DIAG ,
HSO_INTF_BULK | HSO_PORT_MODEM ,
0
} ;
static const s32 icon321_port_spec [ ] = {
HSO_INTF_MUX | HSO_PORT_NETWORK ,
HSO_INTF_BULK | HSO_PORT_DIAG2 ,
HSO_INTF_BULK | HSO_PORT_MODEM ,
HSO_INTF_BULK | HSO_PORT_DIAG ,
0
} ;
# define default_port_device(vendor, product) \
USB_DEVICE ( vendor , product ) , \
. driver_info = ( kernel_ulong_t ) default_port_spec
# define icon321_port_device(vendor, product) \
USB_DEVICE ( vendor , product ) , \
. driver_info = ( kernel_ulong_t ) icon321_port_spec
/* list of devices we support */
static const struct usb_device_id hso_ids [ ] = {
{ default_port_device ( 0x0af0 , 0x6711 ) } ,
{ default_port_device ( 0x0af0 , 0x6731 ) } ,
{ default_port_device ( 0x0af0 , 0x6751 ) } ,
{ default_port_device ( 0x0af0 , 0x6771 ) } ,
{ default_port_device ( 0x0af0 , 0x6791 ) } ,
{ default_port_device ( 0x0af0 , 0x6811 ) } ,
{ default_port_device ( 0x0af0 , 0x6911 ) } ,
{ default_port_device ( 0x0af0 , 0x6951 ) } ,
{ default_port_device ( 0x0af0 , 0x6971 ) } ,
{ default_port_device ( 0x0af0 , 0x7011 ) } ,
{ default_port_device ( 0x0af0 , 0x7031 ) } ,
{ default_port_device ( 0x0af0 , 0x7051 ) } ,
{ default_port_device ( 0x0af0 , 0x7071 ) } ,
{ default_port_device ( 0x0af0 , 0x7111 ) } ,
{ default_port_device ( 0x0af0 , 0x7211 ) } ,
{ default_port_device ( 0x0af0 , 0x7251 ) } ,
{ default_port_device ( 0x0af0 , 0x7271 ) } ,
{ default_port_device ( 0x0af0 , 0x7311 ) } ,
{ default_port_device ( 0x0af0 , 0xc031 ) } , /* Icon-Edge */
{ icon321_port_device ( 0x0af0 , 0xd013 ) } , /* Module HSxPA */
{ icon321_port_device ( 0x0af0 , 0xd031 ) } , /* Icon-321 */
2008-08-20 05:07:52 +04:00
{ icon321_port_device ( 0x0af0 , 0xd033 ) } , /* Icon-322 */
2008-05-14 08:57:12 +04:00
{ USB_DEVICE ( 0x0af0 , 0x7301 ) } , /* GE40x */
{ USB_DEVICE ( 0x0af0 , 0x7361 ) } , /* GE40x */
2009-02-04 02:13:26 +03:00
{ USB_DEVICE ( 0x0af0 , 0x7381 ) } , /* GE40x */
2008-05-14 08:57:12 +04:00
{ USB_DEVICE ( 0x0af0 , 0x7401 ) } , /* GI 0401 */
{ USB_DEVICE ( 0x0af0 , 0x7501 ) } , /* GTM 382 */
{ USB_DEVICE ( 0x0af0 , 0x7601 ) } , /* GE40x */
2008-11-25 11:26:12 +03:00
{ USB_DEVICE ( 0x0af0 , 0x7701 ) } ,
{ USB_DEVICE ( 0x0af0 , 0x7801 ) } ,
{ USB_DEVICE ( 0x0af0 , 0x7901 ) } ,
{ USB_DEVICE ( 0x0af0 , 0x7361 ) } ,
2009-02-04 02:13:26 +03:00
{ USB_DEVICE ( 0x0af0 , 0xd057 ) } ,
{ USB_DEVICE ( 0x0af0 , 0xd055 ) } ,
2008-05-14 08:57:12 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( usb , hso_ids ) ;
/* Sysfs attribute */
static ssize_t hso_sysfs_show_porttype ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct hso_device * hso_dev = dev - > driver_data ;
char * port_name ;
if ( ! hso_dev )
return 0 ;
switch ( hso_dev - > port_spec & HSO_PORT_MASK ) {
case HSO_PORT_CONTROL :
port_name = " Control " ;
break ;
case HSO_PORT_APP :
port_name = " Application " ;
break ;
case HSO_PORT_APP2 :
port_name = " Application2 " ;
break ;
case HSO_PORT_GPS :
port_name = " GPS " ;
break ;
case HSO_PORT_GPS_CONTROL :
port_name = " GPS Control " ;
break ;
case HSO_PORT_PCSC :
port_name = " PCSC " ;
break ;
case HSO_PORT_DIAG :
port_name = " Diagnostic " ;
break ;
case HSO_PORT_DIAG2 :
port_name = " Diagnostic2 " ;
break ;
case HSO_PORT_MODEM :
port_name = " Modem " ;
break ;
case HSO_PORT_NETWORK :
port_name = " Network " ;
break ;
default :
port_name = " Unknown " ;
break ;
}
return sprintf ( buf , " %s \n " , port_name ) ;
}
static DEVICE_ATTR ( hsotype , S_IRUGO , hso_sysfs_show_porttype , NULL ) ;
2008-09-05 19:12:07 +04:00
static int hso_urb_to_index ( struct hso_serial * serial , struct urb * urb )
{
int idx ;
for ( idx = 0 ; idx < serial - > num_rx_urbs ; idx + + )
if ( serial - > rx_urb [ idx ] = = urb )
return idx ;
dev_err ( serial - > parent - > dev , " hso_urb_to_index failed \n " ) ;
return - 1 ;
}
2008-05-14 08:57:12 +04:00
/* converts mux value to a port spec value */
static u32 hso_mux_to_port ( int mux )
{
u32 result ;
switch ( mux ) {
case 0x1 :
result = HSO_PORT_CONTROL ;
break ;
case 0x2 :
result = HSO_PORT_APP ;
break ;
case 0x4 :
result = HSO_PORT_PCSC ;
break ;
case 0x8 :
result = HSO_PORT_GPS ;
break ;
case 0x10 :
result = HSO_PORT_APP2 ;
break ;
default :
result = HSO_PORT_NO_PORT ;
}
return result ;
}
/* converts port spec value to a mux value */
static u32 hso_port_to_mux ( int port )
{
u32 result ;
switch ( port & HSO_PORT_MASK ) {
case HSO_PORT_CONTROL :
result = 0x0 ;
break ;
case HSO_PORT_APP :
result = 0x1 ;
break ;
case HSO_PORT_PCSC :
result = 0x2 ;
break ;
case HSO_PORT_GPS :
result = 0x3 ;
break ;
case HSO_PORT_APP2 :
result = 0x4 ;
break ;
default :
result = 0x0 ;
}
return result ;
}
static struct hso_serial * get_serial_by_shared_int_and_type (
struct hso_shared_int * shared_int ,
int mux )
{
int i , port ;
port = hso_mux_to_port ( mux ) ;
for ( i = 0 ; i < HSO_SERIAL_TTY_MINORS ; i + + ) {
if ( serial_table [ i ]
& & ( dev2ser ( serial_table [ i ] ) - > shared_int = = shared_int )
& & ( ( serial_table [ i ] - > port_spec & HSO_PORT_MASK ) = = port ) ) {
return dev2ser ( serial_table [ i ] ) ;
}
}
return NULL ;
}
static struct hso_serial * get_serial_by_index ( unsigned index )
{
2008-08-08 23:02:57 +04:00
struct hso_serial * serial = NULL ;
2008-05-14 08:57:12 +04:00
unsigned long flags ;
spin_lock_irqsave ( & serial_table_lock , flags ) ;
2008-08-08 23:02:57 +04:00
if ( serial_table [ index ] )
serial = dev2ser ( serial_table [ index ] ) ;
2008-05-14 08:57:12 +04:00
spin_unlock_irqrestore ( & serial_table_lock , flags ) ;
return serial ;
}
static int get_free_serial_index ( void )
{
int index ;
unsigned long flags ;
spin_lock_irqsave ( & serial_table_lock , flags ) ;
for ( index = 0 ; index < HSO_SERIAL_TTY_MINORS ; index + + ) {
if ( serial_table [ index ] = = NULL ) {
spin_unlock_irqrestore ( & serial_table_lock , flags ) ;
return index ;
}
}
spin_unlock_irqrestore ( & serial_table_lock , flags ) ;
printk ( KERN_ERR " %s: no free serial devices in table \n " , __func__ ) ;
return - 1 ;
}
static void set_serial_by_index ( unsigned index , struct hso_serial * serial )
{
unsigned long flags ;
2008-08-08 23:02:57 +04:00
2008-05-14 08:57:12 +04:00
spin_lock_irqsave ( & serial_table_lock , flags ) ;
if ( serial )
serial_table [ index ] = serial - > parent ;
else
serial_table [ index ] = NULL ;
spin_unlock_irqrestore ( & serial_table_lock , flags ) ;
}
2008-08-08 23:02:57 +04:00
/* log a meaningful explanation of an USB status */
2008-05-14 08:57:12 +04:00
static void log_usb_status ( int status , const char * function )
{
char * explanation ;
switch ( status ) {
case - ENODEV :
explanation = " no device " ;
break ;
case - ENOENT :
explanation = " endpoint not enabled " ;
break ;
case - EPIPE :
explanation = " endpoint stalled " ;
break ;
case - ENOSPC :
explanation = " not enough bandwidth " ;
break ;
case - ESHUTDOWN :
explanation = " device disabled " ;
break ;
case - EHOSTUNREACH :
explanation = " device suspended " ;
break ;
case - EINVAL :
case - EAGAIN :
case - EFBIG :
case - EMSGSIZE :
explanation = " internal error " ;
break ;
default :
explanation = " unknown status " ;
break ;
}
D1 ( " %s: received USB status - %s (%d) " , function , explanation , status ) ;
}
/* Network interface functions */
/* called when net interface is brought up by ifconfig */
static int hso_net_open ( struct net_device * net )
{
struct hso_net * odev = netdev_priv ( net ) ;
unsigned long flags = 0 ;
if ( ! odev ) {
dev_err ( & net - > dev , " No net device ! \n " ) ;
return - ENODEV ;
}
odev - > skb_tx_buf = NULL ;
/* setup environment */
spin_lock_irqsave ( & odev - > net_lock , flags ) ;
odev - > rx_parse_state = WAIT_IP ;
odev - > rx_buf_size = 0 ;
odev - > rx_buf_missing = sizeof ( struct iphdr ) ;
spin_unlock_irqrestore ( & odev - > net_lock , flags ) ;
/* We are up and running. */
set_bit ( HSO_NET_RUNNING , & odev - > flags ) ;
2008-12-18 06:57:35 +03:00
hso_start_net_device ( odev - > parent ) ;
2008-05-14 08:57:12 +04:00
/* Tell the kernel we are ready to start receiving from it */
netif_start_queue ( net ) ;
return 0 ;
}
/* called when interface is brought down by ifconfig */
static int hso_net_close ( struct net_device * net )
{
struct hso_net * odev = netdev_priv ( net ) ;
/* we don't need the queue anymore */
netif_stop_queue ( net ) ;
/* no longer running */
clear_bit ( HSO_NET_RUNNING , & odev - > flags ) ;
hso_stop_net_device ( odev - > parent ) ;
/* done */
return 0 ;
}
/* USB tells is xmit done, we should start the netqueue again */
static void write_bulk_callback ( struct urb * urb )
{
struct hso_net * odev = urb - > context ;
int status = urb - > status ;
/* Sanity check */
if ( ! odev | | ! test_bit ( HSO_NET_RUNNING , & odev - > flags ) ) {
dev_err ( & urb - > dev - > dev , " %s: device not running \n " , __func__ ) ;
return ;
}
/* Do we still have a valid kernel network device? */
if ( ! netif_device_present ( odev - > net ) ) {
dev_err ( & urb - > dev - > dev , " %s: net device not present \n " ,
__func__ ) ;
return ;
}
/* log status, but don't act on it, we don't need to resubmit anything
* anyhow */
if ( status )
log_usb_status ( status , __func__ ) ;
hso_put_activity ( odev - > parent ) ;
/* Tell the network interface we are ready for another frame */
netif_wake_queue ( odev - > net ) ;
}
/* called by kernel when we need to transmit a packet */
static int hso_net_start_xmit ( struct sk_buff * skb , struct net_device * net )
{
struct hso_net * odev = netdev_priv ( net ) ;
int result ;
/* Tell the kernel, "No more frames 'til we are done with this one." */
netif_stop_queue ( net ) ;
if ( hso_get_activity ( odev - > parent ) = = - EAGAIN ) {
odev - > skb_tx_buf = skb ;
return 0 ;
}
/* log if asked */
DUMP1 ( skb - > data , skb - > len ) ;
/* Copy it from kernel memory to OUR memory */
memcpy ( odev - > mux_bulk_tx_buf , skb - > data , skb - > len ) ;
D1 ( " len: %d/%d " , skb - > len , MUX_BULK_TX_BUF_SIZE ) ;
/* Fill in the URB for shipping it out. */
usb_fill_bulk_urb ( odev - > mux_bulk_tx_urb ,
odev - > parent - > usb ,
usb_sndbulkpipe ( odev - > parent - > usb ,
odev - > out_endp - >
bEndpointAddress & 0x7F ) ,
odev - > mux_bulk_tx_buf , skb - > len , write_bulk_callback ,
odev ) ;
/* Deal with the Zero Length packet problem, I hope */
odev - > mux_bulk_tx_urb - > transfer_flags | = URB_ZERO_PACKET ;
/* Send the URB on its merry way. */
result = usb_submit_urb ( odev - > mux_bulk_tx_urb , GFP_ATOMIC ) ;
if ( result ) {
dev_warn ( & odev - > parent - > interface - > dev ,
" failed mux_bulk_tx_urb %d " , result ) ;
net - > stats . tx_errors + + ;
netif_start_queue ( net ) ;
} else {
net - > stats . tx_packets + + ;
net - > stats . tx_bytes + = skb - > len ;
/* And tell the kernel when the last transmit started. */
net - > trans_start = jiffies ;
}
dev_kfree_skb ( skb ) ;
/* we're done */
return result ;
}
static void hso_get_drvinfo ( struct net_device * net , struct ethtool_drvinfo * info )
{
struct hso_net * odev = netdev_priv ( net ) ;
strncpy ( info - > driver , driver_name , ETHTOOL_BUSINFO_LEN ) ;
strncpy ( info - > version , DRIVER_VERSION , ETHTOOL_BUSINFO_LEN ) ;
usb_make_path ( odev - > parent - > usb , info - > bus_info , sizeof info - > bus_info ) ;
}
static struct ethtool_ops ops = {
. get_drvinfo = hso_get_drvinfo ,
. get_link = ethtool_op_get_link
} ;
/* called when a packet did not ack after watchdogtimeout */
static void hso_net_tx_timeout ( struct net_device * net )
{
struct hso_net * odev = netdev_priv ( net ) ;
if ( ! odev )
return ;
/* Tell syslog we are hosed. */
dev_warn ( & net - > dev , " Tx timed out. \n " ) ;
/* Tear the waiting frame off the list */
if ( odev - > mux_bulk_tx_urb
& & ( odev - > mux_bulk_tx_urb - > status = = - EINPROGRESS ) )
usb_unlink_urb ( odev - > mux_bulk_tx_urb ) ;
/* Update statistics */
net - > stats . tx_errors + + ;
}
/* make a real packet from the received USB buffer */
static void packetizeRx ( struct hso_net * odev , unsigned char * ip_pkt ,
unsigned int count , unsigned char is_eop )
{
unsigned short temp_bytes ;
unsigned short buffer_offset = 0 ;
unsigned short frame_len ;
unsigned char * tmp_rx_buf ;
/* log if needed */
D1 ( " Rx %d bytes " , count ) ;
DUMP ( ip_pkt , min ( 128 , ( int ) count ) ) ;
while ( count ) {
switch ( odev - > rx_parse_state ) {
case WAIT_IP :
/* waiting for IP header. */
/* wanted bytes - size of ip header */
temp_bytes =
( count <
odev - > rx_buf_missing ) ? count : odev - >
rx_buf_missing ;
memcpy ( ( ( unsigned char * ) ( & odev - > rx_ip_hdr ) ) +
odev - > rx_buf_size , ip_pkt + buffer_offset ,
temp_bytes ) ;
odev - > rx_buf_size + = temp_bytes ;
buffer_offset + = temp_bytes ;
odev - > rx_buf_missing - = temp_bytes ;
count - = temp_bytes ;
if ( ! odev - > rx_buf_missing ) {
/* header is complete allocate an sk_buffer and
* continue to WAIT_DATA */
frame_len = ntohs ( odev - > rx_ip_hdr . tot_len ) ;
if ( ( frame_len > DEFAULT_MRU ) | |
( frame_len < sizeof ( struct iphdr ) ) ) {
dev_err ( & odev - > net - > dev ,
" Invalid frame (%d) length \n " ,
frame_len ) ;
odev - > rx_parse_state = WAIT_SYNC ;
continue ;
}
/* Allocate an sk_buff */
odev - > skb_rx_buf = dev_alloc_skb ( frame_len ) ;
if ( ! odev - > skb_rx_buf ) {
/* We got no receive buffer. */
D1 ( " could not allocate memory " ) ;
odev - > rx_parse_state = WAIT_SYNC ;
return ;
}
/* Here's where it came from */
odev - > skb_rx_buf - > dev = odev - > net ;
/* Copy what we got so far. make room for iphdr
* after tail . */
tmp_rx_buf =
skb_put ( odev - > skb_rx_buf ,
sizeof ( struct iphdr ) ) ;
memcpy ( tmp_rx_buf , ( char * ) & ( odev - > rx_ip_hdr ) ,
sizeof ( struct iphdr ) ) ;
/* ETH_HLEN */
odev - > rx_buf_size = sizeof ( struct iphdr ) ;
/* Filip actually use .tot_len */
odev - > rx_buf_missing =
frame_len - sizeof ( struct iphdr ) ;
odev - > rx_parse_state = WAIT_DATA ;
}
break ;
case WAIT_DATA :
temp_bytes = ( count < odev - > rx_buf_missing )
? count : odev - > rx_buf_missing ;
/* Copy the rest of the bytes that are left in the
* buffer into the waiting sk_buf . */
/* Make room for temp_bytes after tail. */
tmp_rx_buf = skb_put ( odev - > skb_rx_buf , temp_bytes ) ;
memcpy ( tmp_rx_buf , ip_pkt + buffer_offset , temp_bytes ) ;
odev - > rx_buf_missing - = temp_bytes ;
count - = temp_bytes ;
buffer_offset + = temp_bytes ;
odev - > rx_buf_size + = temp_bytes ;
if ( ! odev - > rx_buf_missing ) {
/* Packet is complete. Inject into stack. */
/* We have IP packet here */
odev - > skb_rx_buf - > protocol =
__constant_htons ( ETH_P_IP ) ;
/* don't check it */
odev - > skb_rx_buf - > ip_summed =
CHECKSUM_UNNECESSARY ;
skb_reset_mac_header ( odev - > skb_rx_buf ) ;
/* Ship it off to the kernel */
netif_rx ( odev - > skb_rx_buf ) ;
/* No longer our buffer. */
odev - > skb_rx_buf = NULL ;
/* update out statistics */
odev - > net - > stats . rx_packets + + ;
odev - > net - > stats . rx_bytes + = odev - > rx_buf_size ;
odev - > rx_buf_size = 0 ;
odev - > rx_buf_missing = sizeof ( struct iphdr ) ;
odev - > rx_parse_state = WAIT_IP ;
}
break ;
case WAIT_SYNC :
D1 ( " W_S " ) ;
count = 0 ;
break ;
default :
D1 ( " " ) ;
count - - ;
break ;
}
}
/* Recovery mechanism for WAIT_SYNC state. */
if ( is_eop ) {
if ( odev - > rx_parse_state = = WAIT_SYNC ) {
odev - > rx_parse_state = WAIT_IP ;
odev - > rx_buf_size = 0 ;
odev - > rx_buf_missing = sizeof ( struct iphdr ) ;
}
}
}
/* Moving data from usb to kernel (in interrupt state) */
static void read_bulk_callback ( struct urb * urb )
{
struct hso_net * odev = urb - > context ;
struct net_device * net ;
int result ;
int status = urb - > status ;
/* is al ok? (Filip: Who's Al ?) */
if ( status ) {
log_usb_status ( status , __func__ ) ;
return ;
}
/* Sanity check */
if ( ! odev | | ! test_bit ( HSO_NET_RUNNING , & odev - > flags ) ) {
D1 ( " BULK IN callback but driver is not active! " ) ;
return ;
}
usb_mark_last_busy ( urb - > dev ) ;
net = odev - > net ;
if ( ! netif_device_present ( net ) ) {
/* Somebody killed our network interface... */
return ;
}
if ( odev - > parent - > port_spec & HSO_INFO_CRC_BUG ) {
u32 rest ;
u8 crc_check [ 4 ] = { 0xDE , 0xAD , 0xBE , 0xEF } ;
rest = urb - > actual_length % odev - > in_endp - > wMaxPacketSize ;
if ( ( ( rest = = 5 ) | | ( rest = = 6 ) )
& & ! memcmp ( ( ( u8 * ) urb - > transfer_buffer ) +
urb - > actual_length - 4 , crc_check , 4 ) ) {
urb - > actual_length - = 4 ;
}
}
/* do we even have a packet? */
if ( urb - > actual_length ) {
/* Handle the IP stream, add header and push it onto network
* stack if the packet is complete . */
spin_lock ( & odev - > net_lock ) ;
packetizeRx ( odev , urb - > transfer_buffer , urb - > actual_length ,
( urb - > transfer_buffer_length >
urb - > actual_length ) ? 1 : 0 ) ;
spin_unlock ( & odev - > net_lock ) ;
}
/* We are done with this URB, resubmit it. Prep the USB to wait for
* another frame . Reuse same as received . */
usb_fill_bulk_urb ( urb ,
odev - > parent - > usb ,
usb_rcvbulkpipe ( odev - > parent - > usb ,
odev - > in_endp - >
bEndpointAddress & 0x7F ) ,
urb - > transfer_buffer , MUX_BULK_RX_BUF_SIZE ,
read_bulk_callback , odev ) ;
/* Give this to the USB subsystem so it can tell us when more data
* arrives . */
result = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( result )
dev_warn ( & odev - > parent - > interface - > dev ,
" %s failed submit mux_bulk_rx_urb %d " , __func__ ,
result ) ;
}
/* Serial driver functions */
2009-01-02 16:47:45 +03:00
static void hso_init_termios ( struct ktermios * termios )
2008-05-14 08:57:12 +04:00
{
/*
* The default requirements for this device are :
*/
termios - > c_iflag & =
~ ( IGNBRK /* disable ignore break */
| BRKINT /* disable break causes interrupt */
| PARMRK /* disable mark parity errors */
| ISTRIP /* disable clear high bit of input characters */
| INLCR /* disable translate NL to CR */
| IGNCR /* disable ignore CR */
| ICRNL /* disable translate CR to NL */
| IXON ) ; /* disable enable XON/XOFF flow control */
/* disable postprocess output characters */
termios - > c_oflag & = ~ OPOST ;
termios - > c_lflag & =
~ ( ECHO /* disable echo input characters */
| ECHONL /* disable echo new line */
| ICANON /* disable erase, kill, werase, and rprnt
special characters */
| ISIG /* disable interrupt, quit, and suspend special
characters */
| IEXTEN ) ; /* disable non-POSIX special characters */
termios - > c_cflag & =
~ ( CSIZE /* no size */
| PARENB /* disable parity bit */
| CBAUD /* clear current baud rate */
| CBAUDEX ) ; /* clear current buad rate */
termios - > c_cflag | = CS8 ; /* character size 8 bits */
/* baud rate 115200 */
2009-01-02 16:47:45 +03:00
tty_termios_encode_baud_rate ( termios , 115200 , 115200 ) ;
}
static void _hso_serial_set_termios ( struct tty_struct * tty ,
struct ktermios * old )
{
struct hso_serial * serial = get_serial_by_tty ( tty ) ;
struct ktermios * termios ;
if ( ! serial ) {
printk ( KERN_ERR " %s: no tty structures " , __func__ ) ;
return ;
}
D4 ( " port %d " , serial - > minor ) ;
2008-05-14 08:57:12 +04:00
/*
2009-01-02 16:47:45 +03:00
* Fix up unsupported bits
2008-05-14 08:57:12 +04:00
*/
2009-01-02 16:47:45 +03:00
termios = tty - > termios ;
termios - > c_iflag & = ~ IXON ; /* disable enable XON/XOFF flow control */
termios - > c_cflag & =
~ ( CSIZE /* no size */
| PARENB /* disable parity bit */
| CBAUD /* clear current baud rate */
| CBAUDEX ) ; /* clear current buad rate */
termios - > c_cflag | = CS8 ; /* character size 8 bits */
/* baud rate 115200 */
tty_encode_baud_rate ( tty , 115200 , 115200 ) ;
2008-05-14 08:57:12 +04:00
}
2008-09-05 19:12:07 +04:00
static void hso_resubmit_rx_bulk_urb ( struct hso_serial * serial , struct urb * urb )
{
int result ;
# ifdef CONFIG_HSO_AUTOPM
usb_mark_last_busy ( urb - > dev ) ;
# endif
/* We are done with this URB, resubmit it. Prep the USB to wait for
* another frame */
usb_fill_bulk_urb ( urb , serial - > parent - > usb ,
usb_rcvbulkpipe ( serial - > parent - > usb ,
serial - > in_endp - >
bEndpointAddress & 0x7F ) ,
urb - > transfer_buffer , serial - > rx_data_length ,
hso_std_serial_read_bulk_callback , serial ) ;
/* Give this to the USB subsystem so it can tell us when more data
* arrives . */
result = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( result ) {
dev_err ( & urb - > dev - > dev , " %s failed submit serial rx_urb %d \n " ,
__func__ , result ) ;
}
}
static void put_rxbuf_data_and_resubmit_bulk_urb ( struct hso_serial * serial )
{
int count ;
struct urb * curr_urb ;
while ( serial - > rx_urb_filled [ serial - > curr_rx_urb_idx ] ) {
curr_urb = serial - > rx_urb [ serial - > curr_rx_urb_idx ] ;
count = put_rxbuf_data ( curr_urb , serial ) ;
if ( count = = - 1 )
return ;
if ( count = = 0 ) {
serial - > curr_rx_urb_idx + + ;
if ( serial - > curr_rx_urb_idx > = serial - > num_rx_urbs )
serial - > curr_rx_urb_idx = 0 ;
hso_resubmit_rx_bulk_urb ( serial , curr_urb ) ;
}
}
}
static void put_rxbuf_data_and_resubmit_ctrl_urb ( struct hso_serial * serial )
{
int count = 0 ;
struct urb * urb ;
urb = serial - > rx_urb [ 0 ] ;
if ( serial - > open_count > 0 ) {
count = put_rxbuf_data ( urb , serial ) ;
if ( count = = - 1 )
return ;
}
/* Re issue a read as long as we receive data. */
if ( count = = 0 & & ( ( urb - > actual_length ! = 0 ) | |
( serial - > rx_state = = RX_PENDING ) ) ) {
serial - > rx_state = RX_SENT ;
hso_mux_serial_read ( serial ) ;
} else
serial - > rx_state = RX_IDLE ;
}
/* read callback for Diag and CS port */
static void hso_std_serial_read_bulk_callback ( struct urb * urb )
{
struct hso_serial * serial = urb - > context ;
int status = urb - > status ;
/* sanity check */
if ( ! serial ) {
D1 ( " serial == NULL " ) ;
return ;
} else if ( status ) {
log_usb_status ( status , __func__ ) ;
return ;
}
D4 ( " \n --- Got serial_read_bulk callback %02x --- " , status ) ;
D1 ( " Actual length = %d \n " , urb - > actual_length ) ;
DUMP1 ( urb - > transfer_buffer , urb - > actual_length ) ;
/* Anyone listening? */
if ( serial - > open_count = = 0 )
return ;
if ( status = = 0 ) {
if ( serial - > parent - > port_spec & HSO_INFO_CRC_BUG ) {
u32 rest ;
u8 crc_check [ 4 ] = { 0xDE , 0xAD , 0xBE , 0xEF } ;
rest =
urb - > actual_length %
serial - > in_endp - > wMaxPacketSize ;
if ( ( ( rest = = 5 ) | | ( rest = = 6 ) )
& & ! memcmp ( ( ( u8 * ) urb - > transfer_buffer ) +
urb - > actual_length - 4 , crc_check , 4 ) ) {
urb - > actual_length - = 4 ;
}
}
/* Valid data, handle RX data */
spin_lock ( & serial - > serial_lock ) ;
serial - > rx_urb_filled [ hso_urb_to_index ( serial , urb ) ] = 1 ;
put_rxbuf_data_and_resubmit_bulk_urb ( serial ) ;
spin_unlock ( & serial - > serial_lock ) ;
} else if ( status = = - ENOENT | | status = = - ECONNRESET ) {
/* Unlinked - check for throttled port. */
D2 ( " Port %d, successfully unlinked urb " , serial - > minor ) ;
spin_lock ( & serial - > serial_lock ) ;
serial - > rx_urb_filled [ hso_urb_to_index ( serial , urb ) ] = 0 ;
hso_resubmit_rx_bulk_urb ( serial , urb ) ;
spin_unlock ( & serial - > serial_lock ) ;
} else {
D2 ( " Port %d, status = %d for read urb " , serial - > minor , status ) ;
return ;
}
}
/*
* This needs to be a tasklet otherwise we will
* end up recursively calling this function .
*/
void hso_unthrottle_tasklet ( struct hso_serial * serial )
{
unsigned long flags ;
spin_lock_irqsave ( & serial - > serial_lock , flags ) ;
if ( ( serial - > parent - > port_spec & HSO_INTF_MUX ) )
put_rxbuf_data_and_resubmit_ctrl_urb ( serial ) ;
else
put_rxbuf_data_and_resubmit_bulk_urb ( serial ) ;
spin_unlock_irqrestore ( & serial - > serial_lock , flags ) ;
}
static void hso_unthrottle ( struct tty_struct * tty )
{
struct hso_serial * serial = get_serial_by_tty ( tty ) ;
tasklet_hi_schedule ( & serial - > unthrottle_tasklet ) ;
}
void hso_unthrottle_workfunc ( struct work_struct * work )
{
struct hso_serial * serial =
container_of ( work , struct hso_serial ,
retry_unthrottle_workqueue ) ;
hso_unthrottle_tasklet ( serial ) ;
}
2008-05-14 08:57:12 +04:00
/* open the requested serial port */
static int hso_serial_open ( struct tty_struct * tty , struct file * filp )
{
struct hso_serial * serial = get_serial_by_index ( tty - > index ) ;
2008-11-25 14:52:46 +03:00
int result ;
2008-05-14 08:57:12 +04:00
/* sanity check */
if ( serial = = NULL | | serial - > magic ! = HSO_SERIAL_MAGIC ) {
2009-01-02 16:47:39 +03:00
WARN_ON ( 1 ) ;
2008-05-14 08:57:12 +04:00
tty - > driver_data = NULL ;
D1 ( " Failed to open port " ) ;
return - ENODEV ;
}
2008-11-25 14:52:46 +03:00
mutex_lock ( & serial - > parent - > mutex ) ;
result = usb_autopm_get_interface ( serial - > parent - > interface ) ;
if ( result < 0 )
2008-05-14 08:57:12 +04:00
goto err_out ;
D1 ( " Opening %d " , serial - > minor ) ;
kref_get ( & serial - > parent - > ref ) ;
/* setup */
2009-01-02 16:47:39 +03:00
spin_lock_irq ( & serial - > serial_lock ) ;
2008-05-14 08:57:12 +04:00
tty - > driver_data = serial ;
2009-01-15 16:31:15 +03:00
tty_kref_put ( serial - > tty ) ;
2009-01-02 16:47:39 +03:00
serial - > tty = tty_kref_get ( tty ) ;
spin_unlock_irq ( & serial - > serial_lock ) ;
2008-05-14 08:57:12 +04:00
2008-11-25 14:53:09 +03:00
/* check for port already opened, if not set the termios */
serial - > open_count + + ;
2008-05-14 08:57:12 +04:00
if ( serial - > open_count = = 1 ) {
tty - > low_latency = 1 ;
2008-09-05 19:12:07 +04:00
serial - > rx_state = RX_IDLE ;
2008-05-14 08:57:12 +04:00
/* Force default termio settings */
_hso_serial_set_termios ( tty , NULL ) ;
2008-09-05 19:12:07 +04:00
tasklet_init ( & serial - > unthrottle_tasklet ,
( void ( * ) ( unsigned long ) ) hso_unthrottle_tasklet ,
( unsigned long ) serial ) ;
INIT_WORK ( & serial - > retry_unthrottle_workqueue ,
hso_unthrottle_workfunc ) ;
2008-11-25 14:52:46 +03:00
result = hso_start_serial_device ( serial - > parent , GFP_KERNEL ) ;
if ( result ) {
2008-05-14 08:57:12 +04:00
hso_stop_serial_device ( serial - > parent ) ;
serial - > open_count - - ;
2008-11-25 14:52:46 +03:00
kref_put ( & serial - > parent - > ref , hso_serial_ref_free ) ;
2008-05-14 08:57:12 +04:00
}
} else {
D1 ( " Port was already open " ) ;
}
usb_autopm_put_interface ( serial - > parent - > interface ) ;
/* done */
2008-11-25 14:52:46 +03:00
if ( result )
2008-05-14 08:57:12 +04:00
hso_serial_tiocmset ( tty , NULL , TIOCM_RTS | TIOCM_DTR , 0 ) ;
err_out :
2008-11-25 14:52:46 +03:00
mutex_unlock ( & serial - > parent - > mutex ) ;
return result ;
2008-05-14 08:57:12 +04:00
}
/* close the requested serial port */
static void hso_serial_close ( struct tty_struct * tty , struct file * filp )
{
struct hso_serial * serial = tty - > driver_data ;
u8 usb_gone ;
D1 ( " Closing serial port " ) ;
2009-01-02 16:47:39 +03:00
/* Open failed, no close cleanup required */
if ( serial = = NULL )
return ;
2008-11-25 14:52:46 +03:00
mutex_lock ( & serial - > parent - > mutex ) ;
2008-05-14 08:57:12 +04:00
usb_gone = serial - > parent - > usb_gone ;
if ( ! usb_gone )
usb_autopm_get_interface ( serial - > parent - > interface ) ;
/* reset the rts and dtr */
/* do the actual close */
serial - > open_count - - ;
2008-11-25 14:52:46 +03:00
kref_put ( & serial - > parent - > ref , hso_serial_ref_free ) ;
2008-05-14 08:57:12 +04:00
if ( serial - > open_count < = 0 ) {
serial - > open_count = 0 ;
2009-01-02 16:47:39 +03:00
spin_lock_irq ( & serial - > serial_lock ) ;
if ( serial - > tty = = tty ) {
2008-05-14 08:57:12 +04:00
serial - > tty - > driver_data = NULL ;
serial - > tty = NULL ;
2009-01-02 16:47:39 +03:00
tty_kref_put ( tty ) ;
2008-05-14 08:57:12 +04:00
}
2009-01-02 16:47:39 +03:00
spin_unlock_irq ( & serial - > serial_lock ) ;
2008-05-14 08:57:12 +04:00
if ( ! usb_gone )
hso_stop_serial_device ( serial - > parent ) ;
2008-09-05 19:12:07 +04:00
tasklet_kill ( & serial - > unthrottle_tasklet ) ;
cancel_work_sync ( & serial - > retry_unthrottle_workqueue ) ;
2008-05-14 08:57:12 +04:00
}
2008-09-05 19:12:07 +04:00
2008-05-14 08:57:12 +04:00
if ( ! usb_gone )
usb_autopm_put_interface ( serial - > parent - > interface ) ;
2008-11-25 14:52:46 +03:00
mutex_unlock ( & serial - > parent - > mutex ) ;
2008-05-14 08:57:12 +04:00
}
/* close the requested serial port */
static int hso_serial_write ( struct tty_struct * tty , const unsigned char * buf ,
int count )
{
struct hso_serial * serial = get_serial_by_tty ( tty ) ;
int space , tx_bytes ;
unsigned long flags ;
/* sanity check */
if ( serial = = NULL ) {
printk ( KERN_ERR " %s: serial is NULL \n " , __func__ ) ;
return - ENODEV ;
}
spin_lock_irqsave ( & serial - > serial_lock , flags ) ;
space = serial - > tx_data_length - serial - > tx_buffer_count ;
tx_bytes = ( count < space ) ? count : space ;
if ( ! tx_bytes )
goto out ;
memcpy ( serial - > tx_buffer + serial - > tx_buffer_count , buf , tx_bytes ) ;
serial - > tx_buffer_count + = tx_bytes ;
out :
spin_unlock_irqrestore ( & serial - > serial_lock , flags ) ;
hso_kick_transmit ( serial ) ;
/* done */
return tx_bytes ;
}
/* how much room is there for writing */
static int hso_serial_write_room ( struct tty_struct * tty )
{
struct hso_serial * serial = get_serial_by_tty ( tty ) ;
int room ;
unsigned long flags ;
spin_lock_irqsave ( & serial - > serial_lock , flags ) ;
room = serial - > tx_data_length - serial - > tx_buffer_count ;
spin_unlock_irqrestore ( & serial - > serial_lock , flags ) ;
/* return free room */
return room ;
}
/* setup the term */
static void hso_serial_set_termios ( struct tty_struct * tty , struct ktermios * old )
{
struct hso_serial * serial = get_serial_by_tty ( tty ) ;
unsigned long flags ;
if ( old )
D5 ( " Termios called with: cflags new[%d] - old[%d] " ,
tty - > termios - > c_cflag , old - > c_cflag ) ;
/* the actual setup */
spin_lock_irqsave ( & serial - > serial_lock , flags ) ;
if ( serial - > open_count )
_hso_serial_set_termios ( tty , old ) ;
else
tty - > termios = old ;
spin_unlock_irqrestore ( & serial - > serial_lock , flags ) ;
/* done */
return ;
}
/* how many characters in the buffer */
static int hso_serial_chars_in_buffer ( struct tty_struct * tty )
{
struct hso_serial * serial = get_serial_by_tty ( tty ) ;
int chars ;
unsigned long flags ;
/* sanity check */
if ( serial = = NULL )
return 0 ;
spin_lock_irqsave ( & serial - > serial_lock , flags ) ;
chars = serial - > tx_buffer_count ;
spin_unlock_irqrestore ( & serial - > serial_lock , flags ) ;
return chars ;
}
2009-01-02 16:47:52 +03:00
int tiocmget_submit_urb ( struct hso_serial * serial ,
struct hso_tiocmget * tiocmget ,
struct usb_device * usb )
{
int result ;
if ( serial - > parent - > usb_gone )
return - ENODEV ;
usb_fill_int_urb ( tiocmget - > urb , usb ,
usb_rcvintpipe ( usb ,
tiocmget - > endp - >
bEndpointAddress & 0x7F ) ,
& tiocmget - > serial_state_notification ,
sizeof ( struct hso_serial_state_notification ) ,
tiocmget_intr_callback , serial ,
tiocmget - > endp - > bInterval ) ;
result = usb_submit_urb ( tiocmget - > urb , GFP_ATOMIC ) ;
if ( result ) {
dev_warn ( & usb - > dev , " %s usb_submit_urb failed %d \n " , __func__ ,
result ) ;
}
return result ;
}
static void tiocmget_intr_callback ( struct urb * urb )
{
struct hso_serial * serial = urb - > context ;
struct hso_tiocmget * tiocmget ;
int status = urb - > status ;
u16 UART_state_bitmap , prev_UART_state_bitmap ;
struct uart_icount * icount ;
struct hso_serial_state_notification * serial_state_notification ;
struct usb_device * usb ;
/* Sanity checks */
if ( ! serial )
return ;
if ( status ) {
log_usb_status ( status , __func__ ) ;
return ;
}
tiocmget = serial - > tiocmget ;
if ( ! tiocmget )
return ;
usb = serial - > parent - > usb ;
serial_state_notification = & tiocmget - > serial_state_notification ;
if ( serial_state_notification - > bmRequestType ! = BM_REQUEST_TYPE | |
serial_state_notification - > bNotification ! = B_NOTIFICATION | |
le16_to_cpu ( serial_state_notification - > wValue ) ! = W_VALUE | |
le16_to_cpu ( serial_state_notification - > wIndex ) ! = W_INDEX | |
le16_to_cpu ( serial_state_notification - > wLength ) ! = W_LENGTH ) {
dev_warn ( & usb - > dev ,
" hso received invalid serial state notification \n " ) ;
DUMP ( serial_state_notification ,
sizeof ( hso_serial_state_notifation ) )
} else {
UART_state_bitmap = le16_to_cpu ( serial_state_notification - >
UART_state_bitmap ) ;
prev_UART_state_bitmap = tiocmget - > prev_UART_state_bitmap ;
icount = & tiocmget - > icount ;
spin_lock ( & serial - > serial_lock ) ;
if ( ( UART_state_bitmap & B_OVERRUN ) ! =
( prev_UART_state_bitmap & B_OVERRUN ) )
icount - > parity + + ;
if ( ( UART_state_bitmap & B_PARITY ) ! =
( prev_UART_state_bitmap & B_PARITY ) )
icount - > parity + + ;
if ( ( UART_state_bitmap & B_FRAMING ) ! =
( prev_UART_state_bitmap & B_FRAMING ) )
icount - > frame + + ;
if ( ( UART_state_bitmap & B_RING_SIGNAL ) & &
! ( prev_UART_state_bitmap & B_RING_SIGNAL ) )
icount - > rng + + ;
if ( ( UART_state_bitmap & B_BREAK ) ! =
( prev_UART_state_bitmap & B_BREAK ) )
icount - > brk + + ;
if ( ( UART_state_bitmap & B_TX_CARRIER ) ! =
( prev_UART_state_bitmap & B_TX_CARRIER ) )
icount - > dsr + + ;
if ( ( UART_state_bitmap & B_RX_CARRIER ) ! =
( prev_UART_state_bitmap & B_RX_CARRIER ) )
icount - > dcd + + ;
tiocmget - > prev_UART_state_bitmap = UART_state_bitmap ;
spin_unlock ( & serial - > serial_lock ) ;
tiocmget - > intr_completed = 1 ;
wake_up_interruptible ( & tiocmget - > waitq ) ;
}
memset ( serial_state_notification , 0 ,
sizeof ( struct hso_serial_state_notification ) ) ;
tiocmget_submit_urb ( serial ,
tiocmget ,
serial - > parent - > usb ) ;
}
/*
* next few functions largely stolen from drivers / serial / serial_core . c
*/
/* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
* ( use | ' ed TIOCM_RNG / DSR / CD / CTS for masking )
* Caller should use TIOCGICOUNT to see which one it was
*/
static int
hso_wait_modem_status ( struct hso_serial * serial , unsigned long arg )
{
DECLARE_WAITQUEUE ( wait , current ) ;
struct uart_icount cprev , cnow ;
struct hso_tiocmget * tiocmget ;
int ret ;
tiocmget = serial - > tiocmget ;
if ( ! tiocmget )
return - ENOENT ;
/*
* note the counters on entry
*/
spin_lock_irq ( & serial - > serial_lock ) ;
memcpy ( & cprev , & tiocmget - > icount , sizeof ( struct uart_icount ) ) ;
spin_unlock_irq ( & serial - > serial_lock ) ;
add_wait_queue ( & tiocmget - > waitq , & wait ) ;
for ( ; ; ) {
spin_lock_irq ( & serial - > serial_lock ) ;
memcpy ( & cnow , & tiocmget - > icount , sizeof ( struct uart_icount ) ) ;
spin_unlock_irq ( & serial - > serial_lock ) ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( ( ( arg & TIOCM_RNG ) & & ( cnow . rng ! = cprev . rng ) ) | |
( ( arg & TIOCM_DSR ) & & ( cnow . dsr ! = cprev . dsr ) ) | |
( ( arg & TIOCM_CD ) & & ( cnow . dcd ! = cprev . dcd ) ) ) {
ret = 0 ;
break ;
}
schedule ( ) ;
/* see if a signal did it */
if ( signal_pending ( current ) ) {
ret = - ERESTARTSYS ;
break ;
}
cprev = cnow ;
}
current - > state = TASK_RUNNING ;
remove_wait_queue ( & tiocmget - > waitq , & wait ) ;
return ret ;
}
/*
* Get counter of input serial line interrupts ( DCD , RI , DSR , CTS )
* Return : write counters to the user passed counter struct
* NB : both 1 - > 0 and 0 - > 1 transitions are counted except for
* RI where only 0 - > 1 is counted .
*/
static int hso_get_count ( struct hso_serial * serial ,
struct serial_icounter_struct __user * icnt )
{
struct serial_icounter_struct icount ;
struct uart_icount cnow ;
struct hso_tiocmget * tiocmget = serial - > tiocmget ;
if ( ! tiocmget )
return - ENOENT ;
spin_lock_irq ( & serial - > serial_lock ) ;
memcpy ( & cnow , & tiocmget - > icount , sizeof ( struct uart_icount ) ) ;
spin_unlock_irq ( & serial - > serial_lock ) ;
icount . cts = cnow . cts ;
icount . dsr = cnow . dsr ;
icount . rng = cnow . rng ;
icount . dcd = cnow . dcd ;
icount . rx = cnow . rx ;
icount . tx = cnow . tx ;
icount . frame = cnow . frame ;
icount . overrun = cnow . overrun ;
icount . parity = cnow . parity ;
icount . brk = cnow . brk ;
icount . buf_overrun = cnow . buf_overrun ;
return copy_to_user ( icnt , & icount , sizeof ( icount ) ) ? - EFAULT : 0 ;
}
2008-05-14 08:57:12 +04:00
static int hso_serial_tiocmget ( struct tty_struct * tty , struct file * file )
{
2009-01-02 16:47:52 +03:00
int retval ;
2008-05-14 08:57:12 +04:00
struct hso_serial * serial = get_serial_by_tty ( tty ) ;
2009-01-02 16:47:52 +03:00
struct hso_tiocmget * tiocmget ;
u16 UART_state_bitmap ;
2008-05-14 08:57:12 +04:00
/* sanity check */
if ( ! serial ) {
D1 ( " no tty structures " ) ;
return - EINVAL ;
}
2009-01-02 16:47:52 +03:00
spin_lock_irq ( & serial - > serial_lock ) ;
retval = ( ( serial - > rts_state ) ? TIOCM_RTS : 0 ) |
2008-05-14 08:57:12 +04:00
( ( serial - > dtr_state ) ? TIOCM_DTR : 0 ) ;
2009-01-02 16:47:52 +03:00
tiocmget = serial - > tiocmget ;
if ( tiocmget ) {
UART_state_bitmap = le16_to_cpu (
tiocmget - > prev_UART_state_bitmap ) ;
if ( UART_state_bitmap & B_RING_SIGNAL )
retval | = TIOCM_RNG ;
if ( UART_state_bitmap & B_RX_CARRIER )
retval | = TIOCM_CD ;
if ( UART_state_bitmap & B_TX_CARRIER )
retval | = TIOCM_DSR ;
}
spin_unlock_irq ( & serial - > serial_lock ) ;
return retval ;
2008-05-14 08:57:12 +04:00
}
static int hso_serial_tiocmset ( struct tty_struct * tty , struct file * file ,
unsigned int set , unsigned int clear )
{
int val = 0 ;
unsigned long flags ;
int if_num ;
struct hso_serial * serial = get_serial_by_tty ( tty ) ;
/* sanity check */
if ( ! serial ) {
D1 ( " no tty structures " ) ;
return - EINVAL ;
}
if_num = serial - > parent - > interface - > altsetting - > desc . bInterfaceNumber ;
spin_lock_irqsave ( & serial - > serial_lock , flags ) ;
if ( set & TIOCM_RTS )
serial - > rts_state = 1 ;
if ( set & TIOCM_DTR )
serial - > dtr_state = 1 ;
if ( clear & TIOCM_RTS )
serial - > rts_state = 0 ;
if ( clear & TIOCM_DTR )
serial - > dtr_state = 0 ;
if ( serial - > dtr_state )
val | = 0x01 ;
if ( serial - > rts_state )
val | = 0x02 ;
spin_unlock_irqrestore ( & serial - > serial_lock , flags ) ;
return usb_control_msg ( serial - > parent - > usb ,
usb_rcvctrlpipe ( serial - > parent - > usb , 0 ) , 0x22 ,
0x21 , val , if_num , NULL , 0 ,
USB_CTRL_SET_TIMEOUT ) ;
}
2009-01-02 16:47:52 +03:00
static int hso_serial_ioctl ( struct tty_struct * tty , struct file * file ,
unsigned int cmd , unsigned long arg )
{
struct hso_serial * serial = get_serial_by_tty ( tty ) ;
void __user * uarg = ( void __user * ) arg ;
int ret = 0 ;
D4 ( " IOCTL cmd: %d, arg: %ld " , cmd , arg ) ;
if ( ! serial )
return - ENODEV ;
switch ( cmd ) {
case TIOCMIWAIT :
ret = hso_wait_modem_status ( serial , arg ) ;
break ;
case TIOCGICOUNT :
ret = hso_get_count ( serial , uarg ) ;
break ;
default :
ret = - ENOIOCTLCMD ;
break ;
}
return ret ;
}
2008-05-14 08:57:12 +04:00
/* starts a transmit */
static void hso_kick_transmit ( struct hso_serial * serial )
{
u8 * temp ;
unsigned long flags ;
int res ;
spin_lock_irqsave ( & serial - > serial_lock , flags ) ;
if ( ! serial - > tx_buffer_count )
goto out ;
if ( serial - > tx_urb_used )
goto out ;
/* Wakeup USB interface if necessary */
if ( hso_get_activity ( serial - > parent ) = = - EAGAIN )
goto out ;
/* Switch pointers around to avoid memcpy */
temp = serial - > tx_buffer ;
serial - > tx_buffer = serial - > tx_data ;
serial - > tx_data = temp ;
serial - > tx_data_count = serial - > tx_buffer_count ;
serial - > tx_buffer_count = 0 ;
/* If temp is set, it means we switched buffers */
if ( temp & & serial - > write_data ) {
res = serial - > write_data ( serial ) ;
if ( res > = 0 )
serial - > tx_urb_used = 1 ;
}
out :
spin_unlock_irqrestore ( & serial - > serial_lock , flags ) ;
}
/* make a request (for reading and writing data to muxed serial port) */
static int mux_device_request ( struct hso_serial * serial , u8 type , u16 port ,
struct urb * ctrl_urb ,
struct usb_ctrlrequest * ctrl_req ,
u8 * ctrl_urb_data , u32 size )
{
int result ;
int pipe ;
/* Sanity check */
if ( ! serial | | ! ctrl_urb | | ! ctrl_req ) {
printk ( KERN_ERR " %s: Wrong arguments \n " , __func__ ) ;
return - EINVAL ;
}
/* initialize */
ctrl_req - > wValue = 0 ;
2009-01-13 08:56:49 +03:00
ctrl_req - > wIndex = cpu_to_le16 ( hso_port_to_mux ( port ) ) ;
ctrl_req - > wLength = cpu_to_le16 ( size ) ;
2008-05-14 08:57:12 +04:00
if ( type = = USB_CDC_GET_ENCAPSULATED_RESPONSE ) {
/* Reading command */
ctrl_req - > bRequestType = USB_DIR_IN |
USB_TYPE_OPTION_VENDOR |
USB_RECIP_INTERFACE ;
ctrl_req - > bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE ;
pipe = usb_rcvctrlpipe ( serial - > parent - > usb , 0 ) ;
} else {
/* Writing command */
ctrl_req - > bRequestType = USB_DIR_OUT |
USB_TYPE_OPTION_VENDOR |
USB_RECIP_INTERFACE ;
ctrl_req - > bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND ;
pipe = usb_sndctrlpipe ( serial - > parent - > usb , 0 ) ;
}
/* syslog */
D2 ( " %s command (%02x) len: %d, port: %d " ,
type = = USB_CDC_GET_ENCAPSULATED_RESPONSE ? " Read " : " Write " ,
ctrl_req - > bRequestType , ctrl_req - > wLength , port ) ;
/* Load ctrl urb */
ctrl_urb - > transfer_flags = 0 ;
usb_fill_control_urb ( ctrl_urb ,
serial - > parent - > usb ,
pipe ,
( u8 * ) ctrl_req ,
ctrl_urb_data , size , ctrl_callback , serial ) ;
/* Send it on merry way */
result = usb_submit_urb ( ctrl_urb , GFP_ATOMIC ) ;
if ( result ) {
dev_err ( & ctrl_urb - > dev - > dev ,
" %s failed submit ctrl_urb %d type %d " , __func__ ,
result , type ) ;
return result ;
}
/* done */
return size ;
}
/* called by intr_callback when read occurs */
static int hso_mux_serial_read ( struct hso_serial * serial )
{
if ( ! serial )
return - EINVAL ;
/* clean data */
memset ( serial - > rx_data [ 0 ] , 0 , CTRL_URB_RX_SIZE ) ;
/* make the request */
if ( serial - > num_rx_urbs ! = 1 ) {
dev_err ( & serial - > parent - > interface - > dev ,
" ERROR: mux'd reads with multiple buffers "
" not possible \n " ) ;
return 0 ;
}
return mux_device_request ( serial ,
USB_CDC_GET_ENCAPSULATED_RESPONSE ,
serial - > parent - > port_spec & HSO_PORT_MASK ,
serial - > rx_urb [ 0 ] ,
& serial - > ctrl_req_rx ,
serial - > rx_data [ 0 ] , serial - > rx_data_length ) ;
}
/* used for muxed serial port callback (muxed serial read) */
static void intr_callback ( struct urb * urb )
{
struct hso_shared_int * shared_int = urb - > context ;
struct hso_serial * serial ;
unsigned char * port_req ;
int status = urb - > status ;
int i ;
usb_mark_last_busy ( urb - > dev ) ;
/* sanity check */
if ( ! shared_int )
return ;
/* status check */
if ( status ) {
log_usb_status ( status , __func__ ) ;
return ;
}
D4 ( " \n --- Got intr callback 0x%02X --- " , status ) ;
/* what request? */
port_req = urb - > transfer_buffer ;
D4 ( " port_req = 0x%.2X \n " , * port_req ) ;
/* loop over all muxed ports to find the one sending this */
for ( i = 0 ; i < 8 ; i + + ) {
/* max 8 channels on MUX */
if ( * port_req & ( 1 < < i ) ) {
serial = get_serial_by_shared_int_and_type ( shared_int ,
( 1 < < i ) ) ;
if ( serial ! = NULL ) {
D1 ( " Pending read interrupt on port %d \n " , i ) ;
2008-09-05 19:12:07 +04:00
spin_lock ( & serial - > serial_lock ) ;
if ( serial - > rx_state = = RX_IDLE ) {
2008-05-14 08:57:12 +04:00
/* Setup and send a ctrl req read on
* port i */
2008-09-05 19:12:07 +04:00
if ( ! serial - > rx_urb_filled [ 0 ] ) {
serial - > rx_state = RX_SENT ;
hso_mux_serial_read ( serial ) ;
} else
serial - > rx_state = RX_PENDING ;
2008-05-14 08:57:12 +04:00
} else {
D1 ( " Already pending a read on "
" port %d \n " , i ) ;
}
2008-09-05 19:12:07 +04:00
spin_unlock ( & serial - > serial_lock ) ;
2008-05-14 08:57:12 +04:00
}
}
}
/* Resubmit interrupt urb */
hso_mux_submit_intr_urb ( shared_int , urb - > dev , GFP_ATOMIC ) ;
}
/* called for writing to muxed serial port */
static int hso_mux_serial_write_data ( struct hso_serial * serial )
{
if ( NULL = = serial )
return - EINVAL ;
return mux_device_request ( serial ,
USB_CDC_SEND_ENCAPSULATED_COMMAND ,
serial - > parent - > port_spec & HSO_PORT_MASK ,
serial - > tx_urb ,
& serial - > ctrl_req_tx ,
serial - > tx_data , serial - > tx_data_count ) ;
}
/* write callback for Diag and CS port */
static void hso_std_serial_write_bulk_callback ( struct urb * urb )
{
struct hso_serial * serial = urb - > context ;
int status = urb - > status ;
2009-01-02 16:47:39 +03:00
struct tty_struct * tty ;
2008-05-14 08:57:12 +04:00
/* sanity check */
if ( ! serial ) {
D1 ( " serial == NULL " ) ;
return ;
}
spin_lock ( & serial - > serial_lock ) ;
serial - > tx_urb_used = 0 ;
2009-01-02 16:47:39 +03:00
tty = tty_kref_get ( serial - > tty ) ;
2008-05-14 08:57:12 +04:00
spin_unlock ( & serial - > serial_lock ) ;
if ( status ) {
log_usb_status ( status , __func__ ) ;
2009-01-02 16:47:39 +03:00
tty_kref_put ( tty ) ;
2008-05-14 08:57:12 +04:00
return ;
}
hso_put_activity ( serial - > parent ) ;
2009-01-02 16:47:39 +03:00
if ( tty ) {
tty_wakeup ( tty ) ;
tty_kref_put ( tty ) ;
}
2008-05-14 08:57:12 +04:00
hso_kick_transmit ( serial ) ;
D1 ( " " ) ;
return ;
}
/* called for writing diag or CS serial port */
static int hso_std_serial_write_data ( struct hso_serial * serial )
{
int count = serial - > tx_data_count ;
int result ;
usb_fill_bulk_urb ( serial - > tx_urb ,
serial - > parent - > usb ,
usb_sndbulkpipe ( serial - > parent - > usb ,
serial - > out_endp - >
bEndpointAddress & 0x7F ) ,
serial - > tx_data , serial - > tx_data_count ,
hso_std_serial_write_bulk_callback , serial ) ;
result = usb_submit_urb ( serial - > tx_urb , GFP_ATOMIC ) ;
if ( result ) {
dev_warn ( & serial - > parent - > usb - > dev ,
" Failed to submit urb - res %d \n " , result ) ;
return result ;
}
return count ;
}
/* callback after read or write on muxed serial port */
static void ctrl_callback ( struct urb * urb )
{
struct hso_serial * serial = urb - > context ;
struct usb_ctrlrequest * req ;
int status = urb - > status ;
2009-01-02 16:47:39 +03:00
struct tty_struct * tty ;
2008-05-14 08:57:12 +04:00
/* sanity check */
if ( ! serial )
return ;
spin_lock ( & serial - > serial_lock ) ;
serial - > tx_urb_used = 0 ;
2009-01-02 16:47:39 +03:00
tty = tty_kref_get ( serial - > tty ) ;
2008-05-14 08:57:12 +04:00
spin_unlock ( & serial - > serial_lock ) ;
if ( status ) {
log_usb_status ( status , __func__ ) ;
2009-01-02 16:47:39 +03:00
tty_kref_put ( tty ) ;
2008-05-14 08:57:12 +04:00
return ;
}
/* what request? */
req = ( struct usb_ctrlrequest * ) ( urb - > setup_packet ) ;
D4 ( " \n --- Got muxed ctrl callback 0x%02X --- " , status ) ;
D4 ( " Actual length of urb = %d \n " , urb - > actual_length ) ;
DUMP1 ( urb - > transfer_buffer , urb - > actual_length ) ;
if ( req - > bRequestType = =
( USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE ) ) {
/* response to a read command */
2008-09-05 19:12:07 +04:00
serial - > rx_urb_filled [ 0 ] = 1 ;
spin_lock ( & serial - > serial_lock ) ;
put_rxbuf_data_and_resubmit_ctrl_urb ( serial ) ;
spin_unlock ( & serial - > serial_lock ) ;
2008-05-14 08:57:12 +04:00
} else {
hso_put_activity ( serial - > parent ) ;
2009-01-02 16:47:39 +03:00
if ( tty )
tty_wakeup ( tty ) ;
2008-05-14 08:57:12 +04:00
/* response to a write command */
hso_kick_transmit ( serial ) ;
}
2009-01-02 16:47:39 +03:00
tty_kref_put ( tty ) ;
2008-05-14 08:57:12 +04:00
}
/* handle RX data for serial port */
2008-09-05 19:12:07 +04:00
static int put_rxbuf_data ( struct urb * urb , struct hso_serial * serial )
2008-05-14 08:57:12 +04:00
{
2009-01-02 16:47:39 +03:00
struct tty_struct * tty ;
2008-09-05 19:12:07 +04:00
int write_length_remaining = 0 ;
int curr_write_len ;
2009-01-02 16:47:39 +03:00
2008-05-14 08:57:12 +04:00
/* Sanity check */
if ( urb = = NULL | | serial = = NULL ) {
D1 ( " serial = NULL " ) ;
2008-09-05 19:12:07 +04:00
return - 2 ;
2008-05-14 08:57:12 +04:00
}
2009-01-15 16:31:24 +03:00
/* All callers to put_rxbuf_data hold serial_lock */
2009-01-02 16:47:39 +03:00
tty = tty_kref_get ( serial - > tty ) ;
2008-05-14 08:57:12 +04:00
/* Push data to tty */
2008-09-05 19:12:07 +04:00
if ( tty ) {
write_length_remaining = urb - > actual_length -
serial - > curr_rx_urb_offset ;
2008-05-14 08:57:12 +04:00
D1 ( " data to push to tty " ) ;
2008-09-05 19:12:07 +04:00
while ( write_length_remaining ) {
2009-01-15 16:31:34 +03:00
if ( test_bit ( TTY_THROTTLED , & tty - > flags ) ) {
tty_kref_put ( tty ) ;
2008-09-05 19:12:07 +04:00
return - 1 ;
2009-01-15 16:31:34 +03:00
}
2008-09-05 19:12:07 +04:00
curr_write_len = tty_insert_flip_string
( tty , urb - > transfer_buffer +
serial - > curr_rx_urb_offset ,
write_length_remaining ) ;
serial - > curr_rx_urb_offset + = curr_write_len ;
write_length_remaining - = curr_write_len ;
tty_flip_buffer_push ( tty ) ;
2008-05-14 08:57:12 +04:00
}
}
2008-09-05 19:12:07 +04:00
if ( write_length_remaining = = 0 ) {
serial - > curr_rx_urb_offset = 0 ;
serial - > rx_urb_filled [ hso_urb_to_index ( serial , urb ) ] = 0 ;
2008-05-14 08:57:12 +04:00
}
2009-01-02 16:47:39 +03:00
tty_kref_put ( tty ) ;
2008-09-05 19:12:07 +04:00
return write_length_remaining ;
2008-05-14 08:57:12 +04:00
}
2008-09-05 19:12:07 +04:00
2008-05-14 08:57:12 +04:00
/* Base driver functions */
static void hso_log_port ( struct hso_device * hso_dev )
{
char * port_type ;
char port_dev [ 20 ] ;
switch ( hso_dev - > port_spec & HSO_PORT_MASK ) {
case HSO_PORT_CONTROL :
port_type = " Control " ;
break ;
case HSO_PORT_APP :
port_type = " Application " ;
break ;
case HSO_PORT_GPS :
port_type = " GPS " ;
break ;
case HSO_PORT_GPS_CONTROL :
port_type = " GPS control " ;
break ;
case HSO_PORT_APP2 :
port_type = " Application2 " ;
break ;
case HSO_PORT_PCSC :
port_type = " PCSC " ;
break ;
case HSO_PORT_DIAG :
port_type = " Diagnostic " ;
break ;
case HSO_PORT_DIAG2 :
port_type = " Diagnostic2 " ;
break ;
case HSO_PORT_MODEM :
port_type = " Modem " ;
break ;
case HSO_PORT_NETWORK :
port_type = " Network " ;
break ;
default :
port_type = " Unknown " ;
break ;
}
if ( ( hso_dev - > port_spec & HSO_PORT_MASK ) = = HSO_PORT_NETWORK ) {
sprintf ( port_dev , " %s " , dev2net ( hso_dev ) - > net - > name ) ;
} else
sprintf ( port_dev , " /dev/%s%d " , tty_filename ,
dev2ser ( hso_dev ) - > minor ) ;
dev_dbg ( & hso_dev - > interface - > dev , " HSO: Found %s port %s \n " ,
port_type , port_dev ) ;
}
static int hso_start_net_device ( struct hso_device * hso_dev )
{
int i , result = 0 ;
struct hso_net * hso_net = dev2net ( hso_dev ) ;
if ( ! hso_net )
return - ENODEV ;
/* send URBs for all read buffers */
for ( i = 0 ; i < MUX_BULK_RX_BUF_COUNT ; i + + ) {
/* Prep a receive URB */
usb_fill_bulk_urb ( hso_net - > mux_bulk_rx_urb_pool [ i ] ,
hso_dev - > usb ,
usb_rcvbulkpipe ( hso_dev - > usb ,
hso_net - > in_endp - >
bEndpointAddress & 0x7F ) ,
hso_net - > mux_bulk_rx_buf_pool [ i ] ,
MUX_BULK_RX_BUF_SIZE , read_bulk_callback ,
hso_net ) ;
/* Put it out there so the device can send us stuff */
result = usb_submit_urb ( hso_net - > mux_bulk_rx_urb_pool [ i ] ,
GFP_NOIO ) ;
if ( result )
dev_warn ( & hso_dev - > usb - > dev ,
" %s failed mux_bulk_rx_urb[%d] %d \n " , __func__ ,
i , result ) ;
}
return result ;
}
static int hso_stop_net_device ( struct hso_device * hso_dev )
{
int i ;
struct hso_net * hso_net = dev2net ( hso_dev ) ;
if ( ! hso_net )
return - ENODEV ;
for ( i = 0 ; i < MUX_BULK_RX_BUF_COUNT ; i + + ) {
if ( hso_net - > mux_bulk_rx_urb_pool [ i ] )
usb_kill_urb ( hso_net - > mux_bulk_rx_urb_pool [ i ] ) ;
}
if ( hso_net - > mux_bulk_tx_urb )
usb_kill_urb ( hso_net - > mux_bulk_tx_urb ) ;
return 0 ;
}
static int hso_start_serial_device ( struct hso_device * hso_dev , gfp_t flags )
{
int i , result = 0 ;
struct hso_serial * serial = dev2ser ( hso_dev ) ;
if ( ! serial )
return - ENODEV ;
/* If it is not the MUX port fill in and submit a bulk urb (already
* allocated in hso_serial_start ) */
if ( ! ( serial - > parent - > port_spec & HSO_INTF_MUX ) ) {
for ( i = 0 ; i < serial - > num_rx_urbs ; i + + ) {
usb_fill_bulk_urb ( serial - > rx_urb [ i ] ,
serial - > parent - > usb ,
usb_rcvbulkpipe ( serial - > parent - > usb ,
serial - > in_endp - >
bEndpointAddress &
0x7F ) ,
serial - > rx_data [ i ] ,
serial - > rx_data_length ,
hso_std_serial_read_bulk_callback ,
serial ) ;
result = usb_submit_urb ( serial - > rx_urb [ i ] , flags ) ;
if ( result ) {
dev_warn ( & serial - > parent - > usb - > dev ,
" Failed to submit urb - res %d \n " ,
result ) ;
break ;
}
}
} else {
mutex_lock ( & serial - > shared_int - > shared_int_lock ) ;
if ( ! serial - > shared_int - > use_count ) {
result =
hso_mux_submit_intr_urb ( serial - > shared_int ,
hso_dev - > usb , flags ) ;
}
serial - > shared_int - > use_count + + ;
mutex_unlock ( & serial - > shared_int - > shared_int_lock ) ;
}
2009-01-02 16:47:52 +03:00
if ( serial - > tiocmget )
tiocmget_submit_urb ( serial ,
serial - > tiocmget ,
serial - > parent - > usb ) ;
2008-05-14 08:57:12 +04:00
return result ;
}
static int hso_stop_serial_device ( struct hso_device * hso_dev )
{
int i ;
struct hso_serial * serial = dev2ser ( hso_dev ) ;
2009-01-02 16:47:52 +03:00
struct hso_tiocmget * tiocmget ;
2008-05-14 08:57:12 +04:00
if ( ! serial )
return - ENODEV ;
for ( i = 0 ; i < serial - > num_rx_urbs ; i + + ) {
2008-09-05 19:12:07 +04:00
if ( serial - > rx_urb [ i ] ) {
2008-05-14 08:57:12 +04:00
usb_kill_urb ( serial - > rx_urb [ i ] ) ;
2008-09-05 19:12:07 +04:00
serial - > rx_urb_filled [ i ] = 0 ;
}
2008-05-14 08:57:12 +04:00
}
2008-09-05 19:12:07 +04:00
serial - > curr_rx_urb_idx = 0 ;
serial - > curr_rx_urb_offset = 0 ;
2008-05-14 08:57:12 +04:00
if ( serial - > tx_urb )
usb_kill_urb ( serial - > tx_urb ) ;
if ( serial - > shared_int ) {
mutex_lock ( & serial - > shared_int - > shared_int_lock ) ;
if ( serial - > shared_int - > use_count & &
( - - serial - > shared_int - > use_count = = 0 ) ) {
struct urb * urb ;
urb = serial - > shared_int - > shared_intr_urb ;
if ( urb )
usb_kill_urb ( urb ) ;
}
mutex_unlock ( & serial - > shared_int - > shared_int_lock ) ;
}
2009-01-02 16:47:52 +03:00
tiocmget = serial - > tiocmget ;
if ( tiocmget ) {
wake_up_interruptible ( & tiocmget - > waitq ) ;
usb_kill_urb ( tiocmget - > urb ) ;
}
2008-05-14 08:57:12 +04:00
return 0 ;
}
static void hso_serial_common_free ( struct hso_serial * serial )
{
int i ;
if ( serial - > parent - > dev )
device_remove_file ( serial - > parent - > dev , & dev_attr_hsotype ) ;
tty_unregister_device ( tty_drv , serial - > minor ) ;
for ( i = 0 ; i < serial - > num_rx_urbs ; i + + ) {
/* unlink and free RX URB */
usb_free_urb ( serial - > rx_urb [ i ] ) ;
/* free the RX buffer */
kfree ( serial - > rx_data [ i ] ) ;
}
/* unlink and free TX URB */
usb_free_urb ( serial - > tx_urb ) ;
kfree ( serial - > tx_data ) ;
}
static int hso_serial_common_create ( struct hso_serial * serial , int num_urbs ,
int rx_size , int tx_size )
{
struct device * dev ;
int minor ;
int i ;
minor = get_free_serial_index ( ) ;
if ( minor < 0 )
goto exit ;
/* register our minor number */
serial - > parent - > dev = tty_register_device ( tty_drv , minor ,
& serial - > parent - > interface - > dev ) ;
dev = serial - > parent - > dev ;
dev - > driver_data = serial - > parent ;
i = device_create_file ( dev , & dev_attr_hsotype ) ;
/* fill in specific data for later use */
serial - > minor = minor ;
serial - > magic = HSO_SERIAL_MAGIC ;
spin_lock_init ( & serial - > serial_lock ) ;
serial - > num_rx_urbs = num_urbs ;
/* RX, allocate urb and initialize */
/* prepare our RX buffer */
serial - > rx_data_length = rx_size ;
for ( i = 0 ; i < serial - > num_rx_urbs ; i + + ) {
serial - > rx_urb [ i ] = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! serial - > rx_urb [ i ] ) {
dev_err ( dev , " Could not allocate urb? \n " ) ;
goto exit ;
}
serial - > rx_urb [ i ] - > transfer_buffer = NULL ;
serial - > rx_urb [ i ] - > transfer_buffer_length = 0 ;
serial - > rx_data [ i ] = kzalloc ( serial - > rx_data_length ,
GFP_KERNEL ) ;
if ( ! serial - > rx_data [ i ] ) {
dev_err ( dev , " %s - Out of memory \n " , __func__ ) ;
goto exit ;
}
}
/* TX, allocate urb and initialize */
serial - > tx_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! serial - > tx_urb ) {
dev_err ( dev , " Could not allocate urb? \n " ) ;
goto exit ;
}
serial - > tx_urb - > transfer_buffer = NULL ;
serial - > tx_urb - > transfer_buffer_length = 0 ;
/* prepare our TX buffer */
serial - > tx_data_count = 0 ;
serial - > tx_buffer_count = 0 ;
serial - > tx_data_length = tx_size ;
serial - > tx_data = kzalloc ( serial - > tx_data_length , GFP_KERNEL ) ;
if ( ! serial - > tx_data ) {
dev_err ( dev , " %s - Out of memory " , __func__ ) ;
goto exit ;
}
serial - > tx_buffer = kzalloc ( serial - > tx_data_length , GFP_KERNEL ) ;
if ( ! serial - > tx_buffer ) {
dev_err ( dev , " %s - Out of memory " , __func__ ) ;
goto exit ;
}
return 0 ;
exit :
hso_serial_common_free ( serial ) ;
return - 1 ;
}
/* Frees a general hso device */
static void hso_free_device ( struct hso_device * hso_dev )
{
kfree ( hso_dev ) ;
}
/* Creates a general hso device */
static struct hso_device * hso_create_device ( struct usb_interface * intf ,
int port_spec )
{
struct hso_device * hso_dev ;
hso_dev = kzalloc ( sizeof ( * hso_dev ) , GFP_ATOMIC ) ;
if ( ! hso_dev )
return NULL ;
hso_dev - > port_spec = port_spec ;
hso_dev - > usb = interface_to_usbdev ( intf ) ;
hso_dev - > interface = intf ;
kref_init ( & hso_dev - > ref ) ;
2008-11-25 14:52:46 +03:00
mutex_init ( & hso_dev - > mutex ) ;
2008-05-14 08:57:12 +04:00
INIT_WORK ( & hso_dev - > async_get_intf , async_get_intf ) ;
INIT_WORK ( & hso_dev - > async_put_intf , async_put_intf ) ;
return hso_dev ;
}
/* Removes a network device in the network device table */
static int remove_net_device ( struct hso_device * hso_dev )
{
int i ;
for ( i = 0 ; i < HSO_MAX_NET_DEVICES ; i + + ) {
if ( network_table [ i ] = = hso_dev ) {
network_table [ i ] = NULL ;
break ;
}
}
if ( i = = HSO_MAX_NET_DEVICES )
return - 1 ;
return 0 ;
}
/* Frees our network device */
static void hso_free_net_device ( struct hso_device * hso_dev )
{
int i ;
struct hso_net * hso_net = dev2net ( hso_dev ) ;
if ( ! hso_net )
return ;
/* start freeing */
for ( i = 0 ; i < MUX_BULK_RX_BUF_COUNT ; i + + ) {
usb_free_urb ( hso_net - > mux_bulk_rx_urb_pool [ i ] ) ;
kfree ( hso_net - > mux_bulk_rx_buf_pool [ i ] ) ;
}
usb_free_urb ( hso_net - > mux_bulk_tx_urb ) ;
kfree ( hso_net - > mux_bulk_tx_buf ) ;
remove_net_device ( hso_net - > parent ) ;
if ( hso_net - > net ) {
unregister_netdev ( hso_net - > net ) ;
free_netdev ( hso_net - > net ) ;
}
2008-11-25 14:52:46 +03:00
2008-05-14 08:57:12 +04:00
hso_free_device ( hso_dev ) ;
}
/* initialize the network interface */
static void hso_net_init ( struct net_device * net )
{
struct hso_net * hso_net = netdev_priv ( net ) ;
D1 ( " sizeof hso_net is %d " , ( int ) sizeof ( * hso_net ) ) ;
/* fill in the other fields */
net - > open = hso_net_open ;
net - > stop = hso_net_close ;
net - > hard_start_xmit = hso_net_start_xmit ;
net - > tx_timeout = hso_net_tx_timeout ;
net - > watchdog_timeo = HSO_NET_TX_TIMEOUT ;
net - > flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST ;
net - > type = ARPHRD_NONE ;
net - > mtu = DEFAULT_MTU - 14 ;
net - > tx_queue_len = 10 ;
SET_ETHTOOL_OPS ( net , & ops ) ;
/* and initialize the semaphore */
spin_lock_init ( & hso_net - > net_lock ) ;
}
/* Adds a network device in the network device table */
static int add_net_device ( struct hso_device * hso_dev )
{
int i ;
for ( i = 0 ; i < HSO_MAX_NET_DEVICES ; i + + ) {
if ( network_table [ i ] = = NULL ) {
network_table [ i ] = hso_dev ;
break ;
}
}
if ( i = = HSO_MAX_NET_DEVICES )
return - 1 ;
return 0 ;
}
static int hso_radio_toggle ( void * data , enum rfkill_state state )
{
struct hso_device * hso_dev = data ;
int enabled = ( state = = RFKILL_STATE_ON ) ;
int rv ;
2008-11-25 14:52:46 +03:00
mutex_lock ( & hso_dev - > mutex ) ;
2008-05-14 08:57:12 +04:00
if ( hso_dev - > usb_gone )
rv = 0 ;
else
rv = usb_control_msg ( hso_dev - > usb , usb_rcvctrlpipe ( hso_dev - > usb , 0 ) ,
enabled ? 0x82 : 0x81 , 0x40 , 0 , 0 , NULL , 0 ,
USB_CTRL_SET_TIMEOUT ) ;
2008-11-25 14:52:46 +03:00
mutex_unlock ( & hso_dev - > mutex ) ;
2008-05-14 08:57:12 +04:00
return rv ;
}
/* Creates and sets up everything for rfkill */
static void hso_create_rfkill ( struct hso_device * hso_dev ,
struct usb_interface * interface )
{
struct hso_net * hso_net = dev2net ( hso_dev ) ;
2008-11-04 10:51:38 +03:00
struct device * dev = & hso_net - > net - > dev ;
2008-05-14 08:57:12 +04:00
char * rfkn ;
hso_net - > rfkill = rfkill_allocate ( & interface_to_usbdev ( interface ) - > dev ,
2008-11-04 14:32:31 +03:00
RFKILL_TYPE_WWAN ) ;
2008-05-14 08:57:12 +04:00
if ( ! hso_net - > rfkill ) {
2008-11-04 10:51:38 +03:00
dev_err ( dev , " %s - Out of memory \n " , __func__ ) ;
2008-05-14 08:57:12 +04:00
return ;
}
rfkn = kzalloc ( 20 , GFP_KERNEL ) ;
if ( ! rfkn ) {
rfkill_free ( hso_net - > rfkill ) ;
2008-11-04 10:51:38 +03:00
hso_net - > rfkill = NULL ;
dev_err ( dev , " %s - Out of memory \n " , __func__ ) ;
2008-05-14 08:57:12 +04:00
return ;
}
snprintf ( rfkn , 20 , " hso-%d " ,
interface - > altsetting - > desc . bInterfaceNumber ) ;
hso_net - > rfkill - > name = rfkn ;
hso_net - > rfkill - > state = RFKILL_STATE_ON ;
hso_net - > rfkill - > data = hso_dev ;
hso_net - > rfkill - > toggle_radio = hso_radio_toggle ;
if ( rfkill_register ( hso_net - > rfkill ) < 0 ) {
kfree ( rfkn ) ;
hso_net - > rfkill - > name = NULL ;
rfkill_free ( hso_net - > rfkill ) ;
2008-11-04 10:51:38 +03:00
hso_net - > rfkill = NULL ;
dev_err ( dev , " %s - Failed to register rfkill \n " , __func__ ) ;
2008-05-14 08:57:12 +04:00
return ;
}
}
/* Creates our network device */
static struct hso_device * hso_create_net_device ( struct usb_interface * interface )
{
int result , i ;
struct net_device * net ;
struct hso_net * hso_net ;
struct hso_device * hso_dev ;
hso_dev = hso_create_device ( interface , HSO_INTF_MUX | HSO_PORT_NETWORK ) ;
if ( ! hso_dev )
return NULL ;
/* allocate our network device, then we can put in our private data */
/* call hso_net_init to do the basic initialization */
net = alloc_netdev ( sizeof ( struct hso_net ) , " hso%d " , hso_net_init ) ;
if ( ! net ) {
dev_err ( & interface - > dev , " Unable to create ethernet device \n " ) ;
goto exit ;
}
hso_net = netdev_priv ( net ) ;
hso_dev - > port_data . dev_net = hso_net ;
hso_net - > net = net ;
hso_net - > parent = hso_dev ;
hso_net - > in_endp = hso_get_ep ( interface , USB_ENDPOINT_XFER_BULK ,
USB_DIR_IN ) ;
if ( ! hso_net - > in_endp ) {
dev_err ( & interface - > dev , " Can't find BULK IN endpoint \n " ) ;
goto exit ;
}
hso_net - > out_endp = hso_get_ep ( interface , USB_ENDPOINT_XFER_BULK ,
USB_DIR_OUT ) ;
if ( ! hso_net - > out_endp ) {
dev_err ( & interface - > dev , " Can't find BULK OUT endpoint \n " ) ;
goto exit ;
}
SET_NETDEV_DEV ( net , & interface - > dev ) ;
/* registering our net device */
result = register_netdev ( net ) ;
if ( result ) {
dev_err ( & interface - > dev , " Failed to register device \n " ) ;
goto exit ;
}
/* start allocating */
for ( i = 0 ; i < MUX_BULK_RX_BUF_COUNT ; i + + ) {
hso_net - > mux_bulk_rx_urb_pool [ i ] = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! hso_net - > mux_bulk_rx_urb_pool [ i ] ) {
dev_err ( & interface - > dev , " Could not allocate rx urb \n " ) ;
goto exit ;
}
hso_net - > mux_bulk_rx_buf_pool [ i ] = kzalloc ( MUX_BULK_RX_BUF_SIZE ,
GFP_KERNEL ) ;
if ( ! hso_net - > mux_bulk_rx_buf_pool [ i ] ) {
dev_err ( & interface - > dev , " Could not allocate rx buf \n " ) ;
goto exit ;
}
}
hso_net - > mux_bulk_tx_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! hso_net - > mux_bulk_tx_urb ) {
dev_err ( & interface - > dev , " Could not allocate tx urb \n " ) ;
goto exit ;
}
hso_net - > mux_bulk_tx_buf = kzalloc ( MUX_BULK_TX_BUF_SIZE , GFP_KERNEL ) ;
if ( ! hso_net - > mux_bulk_tx_buf ) {
dev_err ( & interface - > dev , " Could not allocate tx buf \n " ) ;
goto exit ;
}
add_net_device ( hso_dev ) ;
hso_log_port ( hso_dev ) ;
hso_create_rfkill ( hso_dev , interface ) ;
return hso_dev ;
exit :
hso_free_net_device ( hso_dev ) ;
return NULL ;
}
2009-01-02 16:47:52 +03:00
static void hso_free_tiomget ( struct hso_serial * serial )
{
struct hso_tiocmget * tiocmget = serial - > tiocmget ;
if ( tiocmget ) {
kfree ( tiocmget ) ;
if ( tiocmget - > urb ) {
usb_free_urb ( tiocmget - > urb ) ;
tiocmget - > urb = NULL ;
}
serial - > tiocmget = NULL ;
}
}
2008-05-14 08:57:12 +04:00
/* Frees an AT channel ( goes for both mux and non-mux ) */
static void hso_free_serial_device ( struct hso_device * hso_dev )
{
struct hso_serial * serial = dev2ser ( hso_dev ) ;
if ( ! serial )
return ;
set_serial_by_index ( serial - > minor , NULL ) ;
hso_serial_common_free ( serial ) ;
if ( serial - > shared_int ) {
mutex_lock ( & serial - > shared_int - > shared_int_lock ) ;
if ( - - serial - > shared_int - > ref_count = = 0 )
hso_free_shared_int ( serial - > shared_int ) ;
else
mutex_unlock ( & serial - > shared_int - > shared_int_lock ) ;
}
2009-01-02 16:47:52 +03:00
hso_free_tiomget ( serial ) ;
2008-05-14 08:57:12 +04:00
kfree ( serial ) ;
hso_free_device ( hso_dev ) ;
}
/* Creates a bulk AT channel */
static struct hso_device * hso_create_bulk_serial_device (
struct usb_interface * interface , int port )
{
struct hso_device * hso_dev ;
struct hso_serial * serial ;
int num_urbs ;
2009-01-02 16:47:52 +03:00
struct hso_tiocmget * tiocmget ;
2008-05-14 08:57:12 +04:00
hso_dev = hso_create_device ( interface , port ) ;
if ( ! hso_dev )
return NULL ;
serial = kzalloc ( sizeof ( * serial ) , GFP_KERNEL ) ;
if ( ! serial )
goto exit ;
serial - > parent = hso_dev ;
hso_dev - > port_data . dev_serial = serial ;
2009-01-02 16:50:29 +03:00
if ( ( port & HSO_PORT_MASK ) = = HSO_PORT_MODEM ) {
2008-05-14 08:57:12 +04:00
num_urbs = 2 ;
2009-01-02 16:47:52 +03:00
serial - > tiocmget = kzalloc ( sizeof ( struct hso_tiocmget ) ,
GFP_KERNEL ) ;
/* it isn't going to break our heart if serial->tiocmget
* allocation fails don ' t bother checking this .
*/
if ( serial - > tiocmget ) {
tiocmget = serial - > tiocmget ;
tiocmget - > urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( tiocmget - > urb ) {
mutex_init ( & tiocmget - > mutex ) ;
init_waitqueue_head ( & tiocmget - > waitq ) ;
tiocmget - > endp = hso_get_ep (
interface ,
USB_ENDPOINT_XFER_INT ,
USB_DIR_IN ) ;
} else
hso_free_tiomget ( serial ) ;
}
}
2008-05-14 08:57:12 +04:00
else
num_urbs = 1 ;
if ( hso_serial_common_create ( serial , num_urbs , BULK_URB_RX_SIZE ,
BULK_URB_TX_SIZE ) )
goto exit ;
serial - > in_endp = hso_get_ep ( interface , USB_ENDPOINT_XFER_BULK ,
USB_DIR_IN ) ;
if ( ! serial - > in_endp ) {
dev_err ( & interface - > dev , " Failed to find BULK IN ep \n " ) ;
2008-08-28 02:02:37 +04:00
goto exit2 ;
2008-05-14 08:57:12 +04:00
}
if ( !
( serial - > out_endp =
hso_get_ep ( interface , USB_ENDPOINT_XFER_BULK , USB_DIR_OUT ) ) ) {
dev_err ( & interface - > dev , " Failed to find BULK IN ep \n " ) ;
2008-08-28 02:02:37 +04:00
goto exit2 ;
2008-05-14 08:57:12 +04:00
}
serial - > write_data = hso_std_serial_write_data ;
/* and record this serial */
set_serial_by_index ( serial - > minor , serial ) ;
/* setup the proc dirs and files if needed */
hso_log_port ( hso_dev ) ;
/* done, return it */
return hso_dev ;
2008-08-28 02:02:37 +04:00
exit2 :
hso_serial_common_free ( serial ) ;
2008-05-14 08:57:12 +04:00
exit :
2009-01-02 16:47:52 +03:00
hso_free_tiomget ( serial ) ;
2008-05-14 08:57:12 +04:00
kfree ( serial ) ;
hso_free_device ( hso_dev ) ;
return NULL ;
}
/* Creates a multiplexed AT channel */
static
struct hso_device * hso_create_mux_serial_device ( struct usb_interface * interface ,
int port ,
struct hso_shared_int * mux )
{
struct hso_device * hso_dev ;
struct hso_serial * serial ;
int port_spec ;
port_spec = HSO_INTF_MUX ;
port_spec & = ~ HSO_PORT_MASK ;
port_spec | = hso_mux_to_port ( port ) ;
if ( ( port_spec & HSO_PORT_MASK ) = = HSO_PORT_NO_PORT )
return NULL ;
hso_dev = hso_create_device ( interface , port_spec ) ;
if ( ! hso_dev )
return NULL ;
serial = kzalloc ( sizeof ( * serial ) , GFP_KERNEL ) ;
if ( ! serial )
goto exit ;
hso_dev - > port_data . dev_serial = serial ;
serial - > parent = hso_dev ;
if ( hso_serial_common_create
( serial , 1 , CTRL_URB_RX_SIZE , CTRL_URB_TX_SIZE ) )
goto exit ;
serial - > tx_data_length - - ;
serial - > write_data = hso_mux_serial_write_data ;
serial - > shared_int = mux ;
mutex_lock ( & serial - > shared_int - > shared_int_lock ) ;
serial - > shared_int - > ref_count + + ;
mutex_unlock ( & serial - > shared_int - > shared_int_lock ) ;
/* and record this serial */
set_serial_by_index ( serial - > minor , serial ) ;
/* setup the proc dirs and files if needed */
hso_log_port ( hso_dev ) ;
/* done, return it */
return hso_dev ;
exit :
if ( serial ) {
tty_unregister_device ( tty_drv , serial - > minor ) ;
kfree ( serial ) ;
}
if ( hso_dev )
hso_free_device ( hso_dev ) ;
return NULL ;
}
static void hso_free_shared_int ( struct hso_shared_int * mux )
{
usb_free_urb ( mux - > shared_intr_urb ) ;
kfree ( mux - > shared_intr_buf ) ;
mutex_unlock ( & mux - > shared_int_lock ) ;
kfree ( mux ) ;
}
static
struct hso_shared_int * hso_create_shared_int ( struct usb_interface * interface )
{
struct hso_shared_int * mux = kzalloc ( sizeof ( * mux ) , GFP_KERNEL ) ;
if ( ! mux )
return NULL ;
mux - > intr_endp = hso_get_ep ( interface , USB_ENDPOINT_XFER_INT ,
USB_DIR_IN ) ;
if ( ! mux - > intr_endp ) {
dev_err ( & interface - > dev , " Can't find INT IN endpoint \n " ) ;
goto exit ;
}
mux - > shared_intr_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! mux - > shared_intr_urb ) {
dev_err ( & interface - > dev , " Could not allocate intr urb? " ) ;
goto exit ;
}
mux - > shared_intr_buf = kzalloc ( mux - > intr_endp - > wMaxPacketSize ,
GFP_KERNEL ) ;
if ( ! mux - > shared_intr_buf ) {
dev_err ( & interface - > dev , " Could not allocate intr buf? " ) ;
goto exit ;
}
mutex_init ( & mux - > shared_int_lock ) ;
return mux ;
exit :
kfree ( mux - > shared_intr_buf ) ;
usb_free_urb ( mux - > shared_intr_urb ) ;
kfree ( mux ) ;
return NULL ;
}
/* Gets the port spec for a certain interface */
static int hso_get_config_data ( struct usb_interface * interface )
{
struct usb_device * usbdev = interface_to_usbdev ( interface ) ;
u8 config_data [ 17 ] ;
u32 if_num = interface - > altsetting - > desc . bInterfaceNumber ;
s32 result ;
if ( usb_control_msg ( usbdev , usb_rcvctrlpipe ( usbdev , 0 ) ,
0x86 , 0xC0 , 0 , 0 , config_data , 17 ,
USB_CTRL_SET_TIMEOUT ) ! = 0x11 ) {
return - EIO ;
}
switch ( config_data [ if_num ] ) {
case 0x0 :
result = 0 ;
break ;
case 0x1 :
result = HSO_PORT_DIAG ;
break ;
case 0x2 :
result = HSO_PORT_GPS ;
break ;
case 0x3 :
result = HSO_PORT_GPS_CONTROL ;
break ;
case 0x4 :
result = HSO_PORT_APP ;
break ;
case 0x5 :
result = HSO_PORT_APP2 ;
break ;
case 0x6 :
result = HSO_PORT_CONTROL ;
break ;
case 0x7 :
result = HSO_PORT_NETWORK ;
break ;
case 0x8 :
result = HSO_PORT_MODEM ;
break ;
case 0x9 :
result = HSO_PORT_MSD ;
break ;
case 0xa :
result = HSO_PORT_PCSC ;
break ;
case 0xb :
result = HSO_PORT_VOICE ;
break ;
default :
result = 0 ;
}
if ( result )
result | = HSO_INTF_BULK ;
if ( config_data [ 16 ] & 0x1 )
result | = HSO_INFO_CRC_BUG ;
return result ;
}
/* called once for each interface upon device insertion */
static int hso_probe ( struct usb_interface * interface ,
const struct usb_device_id * id )
{
int mux , i , if_num , port_spec ;
unsigned char port_mask ;
struct hso_device * hso_dev = NULL ;
struct hso_shared_int * shared_int ;
struct hso_device * tmp_dev = NULL ;
if_num = interface - > altsetting - > desc . bInterfaceNumber ;
/* Get the interface/port specification from either driver_info or from
* the device itself */
if ( id - > driver_info )
port_spec = ( ( u32 * ) ( id - > driver_info ) ) [ if_num ] ;
else
port_spec = hso_get_config_data ( interface ) ;
if ( interface - > cur_altsetting - > desc . bInterfaceClass ! = 0xFF ) {
dev_err ( & interface - > dev , " Not our interface \n " ) ;
return - ENODEV ;
}
/* Check if we need to switch to alt interfaces prior to port
* configuration */
if ( interface - > num_altsetting > 1 )
usb_set_interface ( interface_to_usbdev ( interface ) , if_num , 1 ) ;
interface - > needs_remote_wakeup = 1 ;
/* Allocate new hso device(s) */
switch ( port_spec & HSO_INTF_MASK ) {
case HSO_INTF_MUX :
if ( ( port_spec & HSO_PORT_MASK ) = = HSO_PORT_NETWORK ) {
/* Create the network device */
if ( ! disable_net ) {
hso_dev = hso_create_net_device ( interface ) ;
if ( ! hso_dev )
goto exit ;
tmp_dev = hso_dev ;
}
}
if ( hso_get_mux_ports ( interface , & port_mask ) )
/* TODO: de-allocate everything */
goto exit ;
shared_int = hso_create_shared_int ( interface ) ;
if ( ! shared_int )
goto exit ;
for ( i = 1 , mux = 0 ; i < 0x100 ; i = i < < 1 , mux + + ) {
if ( port_mask & i ) {
hso_dev = hso_create_mux_serial_device (
interface , i , shared_int ) ;
if ( ! hso_dev )
goto exit ;
}
}
if ( tmp_dev )
hso_dev = tmp_dev ;
break ;
case HSO_INTF_BULK :
/* It's a regular bulk interface */
if ( ( ( port_spec & HSO_PORT_MASK ) = = HSO_PORT_NETWORK )
& & ! disable_net )
hso_dev = hso_create_net_device ( interface ) ;
else
hso_dev =
hso_create_bulk_serial_device ( interface , port_spec ) ;
if ( ! hso_dev )
goto exit ;
break ;
default :
goto exit ;
}
usb_driver_claim_interface ( & hso_driver , interface , hso_dev ) ;
/* save our data pointer in this device */
usb_set_intfdata ( interface , hso_dev ) ;
/* done */
return 0 ;
exit :
hso_free_interface ( interface ) ;
return - ENODEV ;
}
/* device removed, cleaning up */
static void hso_disconnect ( struct usb_interface * interface )
{
hso_free_interface ( interface ) ;
/* remove reference of our private data */
usb_set_intfdata ( interface , NULL ) ;
usb_driver_release_interface ( & hso_driver , interface ) ;
}
static void async_get_intf ( struct work_struct * data )
{
struct hso_device * hso_dev =
container_of ( data , struct hso_device , async_get_intf ) ;
usb_autopm_get_interface ( hso_dev - > interface ) ;
}
static void async_put_intf ( struct work_struct * data )
{
struct hso_device * hso_dev =
container_of ( data , struct hso_device , async_put_intf ) ;
usb_autopm_put_interface ( hso_dev - > interface ) ;
}
static int hso_get_activity ( struct hso_device * hso_dev )
{
if ( hso_dev - > usb - > state = = USB_STATE_SUSPENDED ) {
if ( ! hso_dev - > is_active ) {
hso_dev - > is_active = 1 ;
schedule_work ( & hso_dev - > async_get_intf ) ;
}
}
if ( hso_dev - > usb - > state ! = USB_STATE_CONFIGURED )
return - EAGAIN ;
usb_mark_last_busy ( hso_dev - > usb ) ;
return 0 ;
}
static int hso_put_activity ( struct hso_device * hso_dev )
{
if ( hso_dev - > usb - > state ! = USB_STATE_SUSPENDED ) {
if ( hso_dev - > is_active ) {
hso_dev - > is_active = 0 ;
schedule_work ( & hso_dev - > async_put_intf ) ;
return - EAGAIN ;
}
}
hso_dev - > is_active = 0 ;
return 0 ;
}
/* called by kernel when we need to suspend device */
static int hso_suspend ( struct usb_interface * iface , pm_message_t message )
{
int i , result ;
/* Stop all serial ports */
for ( i = 0 ; i < HSO_SERIAL_TTY_MINORS ; i + + ) {
if ( serial_table [ i ] & & ( serial_table [ i ] - > interface = = iface ) ) {
result = hso_stop_serial_device ( serial_table [ i ] ) ;
if ( result )
goto out ;
}
}
/* Stop all network ports */
for ( i = 0 ; i < HSO_MAX_NET_DEVICES ; i + + ) {
if ( network_table [ i ] & &
( network_table [ i ] - > interface = = iface ) ) {
result = hso_stop_net_device ( network_table [ i ] ) ;
if ( result )
goto out ;
}
}
out :
return 0 ;
}
/* called by kernel when we need to resume device */
static int hso_resume ( struct usb_interface * iface )
{
int i , result = 0 ;
struct hso_net * hso_net ;
/* Start all serial ports */
for ( i = 0 ; i < HSO_SERIAL_TTY_MINORS ; i + + ) {
if ( serial_table [ i ] & & ( serial_table [ i ] - > interface = = iface ) ) {
if ( dev2ser ( serial_table [ i ] ) - > open_count ) {
result =
hso_start_serial_device ( serial_table [ i ] , GFP_NOIO ) ;
hso_kick_transmit ( dev2ser ( serial_table [ i ] ) ) ;
if ( result )
goto out ;
}
}
}
/* Start all network ports */
for ( i = 0 ; i < HSO_MAX_NET_DEVICES ; i + + ) {
if ( network_table [ i ] & &
( network_table [ i ] - > interface = = iface ) ) {
hso_net = dev2net ( network_table [ i ] ) ;
2008-11-25 11:30:48 +03:00
if ( hso_net - > flags & IFF_UP ) {
/* First transmit any lingering data,
then restart the device . */
if ( hso_net - > skb_tx_buf ) {
dev_dbg ( & iface - > dev ,
" Transmitting "
" lingering data \n " ) ;
hso_net_start_xmit ( hso_net - > skb_tx_buf ,
hso_net - > net ) ;
hso_net - > skb_tx_buf = NULL ;
}
result = hso_start_net_device ( network_table [ i ] ) ;
if ( result )
goto out ;
2008-05-14 08:57:12 +04:00
}
}
}
out :
return result ;
}
static void hso_serial_ref_free ( struct kref * ref )
{
struct hso_device * hso_dev = container_of ( ref , struct hso_device , ref ) ;
hso_free_serial_device ( hso_dev ) ;
}
static void hso_free_interface ( struct usb_interface * interface )
{
struct hso_serial * hso_dev ;
2009-01-02 16:47:39 +03:00
struct tty_struct * tty ;
2008-05-14 08:57:12 +04:00
int i ;
for ( i = 0 ; i < HSO_SERIAL_TTY_MINORS ; i + + ) {
if ( serial_table [ i ]
& & ( serial_table [ i ] - > interface = = interface ) ) {
hso_dev = dev2ser ( serial_table [ i ] ) ;
2009-01-02 16:47:39 +03:00
spin_lock_irq ( & hso_dev - > serial_lock ) ;
tty = tty_kref_get ( hso_dev - > tty ) ;
spin_unlock_irq ( & hso_dev - > serial_lock ) ;
if ( tty )
tty_hangup ( tty ) ;
2008-11-25 14:52:46 +03:00
mutex_lock ( & hso_dev - > parent - > mutex ) ;
2009-01-02 16:47:39 +03:00
tty_kref_put ( tty ) ;
2008-05-14 08:57:12 +04:00
hso_dev - > parent - > usb_gone = 1 ;
2008-11-25 14:52:46 +03:00
mutex_unlock ( & hso_dev - > parent - > mutex ) ;
kref_put ( & serial_table [ i ] - > ref , hso_serial_ref_free ) ;
2008-05-14 08:57:12 +04:00
}
}
for ( i = 0 ; i < HSO_MAX_NET_DEVICES ; i + + ) {
if ( network_table [ i ]
& & ( network_table [ i ] - > interface = = interface ) ) {
struct rfkill * rfk = dev2net ( network_table [ i ] ) - > rfkill ;
/* hso_stop_net_device doesn't stop the net queue since
* traffic needs to start it again when suspended */
netif_stop_queue ( dev2net ( network_table [ i ] ) - > net ) ;
hso_stop_net_device ( network_table [ i ] ) ;
cancel_work_sync ( & network_table [ i ] - > async_put_intf ) ;
cancel_work_sync ( & network_table [ i ] - > async_get_intf ) ;
2008-08-08 23:02:57 +04:00
if ( rfk )
2008-05-14 08:57:12 +04:00
rfkill_unregister ( rfk ) ;
hso_free_net_device ( network_table [ i ] ) ;
}
}
}
/* Helper functions */
/* Get the endpoint ! */
static struct usb_endpoint_descriptor * hso_get_ep ( struct usb_interface * intf ,
int type , int dir )
{
int i ;
struct usb_host_interface * iface = intf - > cur_altsetting ;
struct usb_endpoint_descriptor * endp ;
for ( i = 0 ; i < iface - > desc . bNumEndpoints ; i + + ) {
endp = & iface - > endpoint [ i ] . desc ;
if ( ( ( endp - > bEndpointAddress & USB_ENDPOINT_DIR_MASK ) = = dir ) & &
2008-12-29 03:21:07 +03:00
( usb_endpoint_type ( endp ) = = type ) )
2008-05-14 08:57:12 +04:00
return endp ;
}
return NULL ;
}
/* Get the byte that describes which ports are enabled */
static int hso_get_mux_ports ( struct usb_interface * intf , unsigned char * ports )
{
int i ;
struct usb_host_interface * iface = intf - > cur_altsetting ;
if ( iface - > extralen = = 3 ) {
* ports = iface - > extra [ 2 ] ;
return 0 ;
}
for ( i = 0 ; i < iface - > desc . bNumEndpoints ; i + + ) {
if ( iface - > endpoint [ i ] . extralen = = 3 ) {
* ports = iface - > endpoint [ i ] . extra [ 2 ] ;
return 0 ;
}
}
return - 1 ;
}
/* interrupt urb needs to be submitted, used for serial read of muxed port */
static int hso_mux_submit_intr_urb ( struct hso_shared_int * shared_int ,
struct usb_device * usb , gfp_t gfp )
{
int result ;
usb_fill_int_urb ( shared_int - > shared_intr_urb , usb ,
usb_rcvintpipe ( usb ,
shared_int - > intr_endp - > bEndpointAddress & 0x7F ) ,
shared_int - > shared_intr_buf ,
shared_int - > intr_endp - > wMaxPacketSize ,
intr_callback , shared_int ,
shared_int - > intr_endp - > bInterval ) ;
result = usb_submit_urb ( shared_int - > shared_intr_urb , gfp ) ;
if ( result )
dev_warn ( & usb - > dev , " %s failed mux_intr_urb %d " , __func__ ,
result ) ;
return result ;
}
/* operations setup of the serial interface */
2008-08-08 23:02:14 +04:00
static const struct tty_operations hso_serial_ops = {
2008-05-14 08:57:12 +04:00
. open = hso_serial_open ,
. close = hso_serial_close ,
. write = hso_serial_write ,
. write_room = hso_serial_write_room ,
2009-01-02 16:47:52 +03:00
. ioctl = hso_serial_ioctl ,
2008-05-14 08:57:12 +04:00
. set_termios = hso_serial_set_termios ,
. chars_in_buffer = hso_serial_chars_in_buffer ,
. tiocmget = hso_serial_tiocmget ,
. tiocmset = hso_serial_tiocmset ,
2008-09-05 19:12:07 +04:00
. unthrottle = hso_unthrottle
2008-05-14 08:57:12 +04:00
} ;
static struct usb_driver hso_driver = {
. name = driver_name ,
. probe = hso_probe ,
. disconnect = hso_disconnect ,
. id_table = hso_ids ,
. suspend = hso_suspend ,
. resume = hso_resume ,
2008-11-25 11:36:10 +03:00
. reset_resume = hso_resume ,
2008-05-14 08:57:12 +04:00
. supports_autosuspend = 1 ,
} ;
static int __init hso_init ( void )
{
int i ;
int result ;
/* put it in the log */
printk ( KERN_INFO " hso: %s \n " , version ) ;
/* Initialise the serial table semaphore and table */
spin_lock_init ( & serial_table_lock ) ;
for ( i = 0 ; i < HSO_SERIAL_TTY_MINORS ; i + + )
serial_table [ i ] = NULL ;
/* allocate our driver using the proper amount of supported minors */
tty_drv = alloc_tty_driver ( HSO_SERIAL_TTY_MINORS ) ;
if ( ! tty_drv )
return - ENOMEM ;
/* fill in all needed values */
tty_drv - > magic = TTY_DRIVER_MAGIC ;
tty_drv - > owner = THIS_MODULE ;
tty_drv - > driver_name = driver_name ;
tty_drv - > name = tty_filename ;
/* if major number is provided as parameter, use that one */
if ( tty_major )
tty_drv - > major = tty_major ;
tty_drv - > minor_start = 0 ;
tty_drv - > num = HSO_SERIAL_TTY_MINORS ;
tty_drv - > type = TTY_DRIVER_TYPE_SERIAL ;
tty_drv - > subtype = SERIAL_TYPE_NORMAL ;
tty_drv - > flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV ;
tty_drv - > init_termios = tty_std_termios ;
2009-01-02 16:47:45 +03:00
hso_init_termios ( & tty_drv - > init_termios ) ;
2008-05-14 08:57:12 +04:00
tty_set_operations ( tty_drv , & hso_serial_ops ) ;
/* register the tty driver */
result = tty_register_driver ( tty_drv ) ;
if ( result ) {
printk ( KERN_ERR " %s - tty_register_driver failed(%d) \n " ,
__func__ , result ) ;
return result ;
}
/* register this module as an usb driver */
result = usb_register ( & hso_driver ) ;
if ( result ) {
printk ( KERN_ERR " Could not register hso driver? error: %d \n " ,
result ) ;
/* cleanup serial interface */
tty_unregister_driver ( tty_drv ) ;
return result ;
}
/* done */
return 0 ;
}
static void __exit hso_exit ( void )
{
printk ( KERN_INFO " hso: unloaded \n " ) ;
tty_unregister_driver ( tty_drv ) ;
/* deregister the usb driver */
usb_deregister ( & hso_driver ) ;
}
/* Module definitions */
module_init ( hso_init ) ;
module_exit ( hso_exit ) ;
MODULE_AUTHOR ( MOD_AUTHOR ) ;
MODULE_DESCRIPTION ( MOD_DESCRIPTION ) ;
MODULE_LICENSE ( MOD_LICENSE ) ;
MODULE_INFO ( Version , DRIVER_VERSION ) ;
/* change the debug level (eg: insmod hso.ko debug=0x04) */
MODULE_PARM_DESC ( debug , " Level of debug [0x01 | 0x02 | 0x04 | 0x08 | 0x10] " ) ;
module_param ( debug , int , S_IRUGO | S_IWUSR ) ;
/* set the major tty number (eg: insmod hso.ko tty_major=245) */
MODULE_PARM_DESC ( tty_major , " Set the major tty number " ) ;
module_param ( tty_major , int , S_IRUGO | S_IWUSR ) ;
/* disable network interface (eg: insmod hso.ko disable_net=1) */
MODULE_PARM_DESC ( disable_net , " Disable the network interface " ) ;
module_param ( disable_net , int , S_IRUGO | S_IWUSR ) ;