2005-04-17 02:20:36 +04:00
/*
* USB HandSpring Visor , Palm m50x , and Sony Clie driver
* ( supports all of the Palm OS USB devices )
*
* Copyright ( C ) 1999 - 2004
* Greg Kroah - Hartman ( greg @ kroah . com )
*
2007-06-12 22:43:37 +04:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation .
2005-04-17 02:20:36 +04:00
*
2008-07-22 14:12:15 +04:00
* See Documentation / usb / usb - serial . txt for more information on using this
* driver
2005-04-17 02:20:36 +04:00
*
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/tty.h>
# include <linux/tty_driver.h>
# include <linux/tty_flip.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/spinlock.h>
2008-07-22 14:12:15 +04:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <linux/usb.h>
2006-07-12 08:22:58 +04:00
# include <linux/usb/serial.h>
2005-04-17 02:20:36 +04:00
# include "visor.h"
/*
* Version Information
*/
# define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
# define DRIVER_DESC "USB HandSpring Visor / Palm OS driver"
/* function prototypes for a handspring visor */
2008-07-22 14:12:15 +04:00
static int visor_open ( struct tty_struct * tty , struct usb_serial_port * port ,
struct file * filp ) ;
static void visor_close ( struct tty_struct * tty , struct usb_serial_port * port ,
struct file * filp ) ;
static int visor_write ( struct tty_struct * tty , struct usb_serial_port * port ,
const unsigned char * buf , int count ) ;
static int visor_write_room ( struct tty_struct * tty ) ;
static void visor_throttle ( struct tty_struct * tty ) ;
static void visor_unthrottle ( struct tty_struct * tty ) ;
static int visor_probe ( struct usb_serial * serial ,
const struct usb_device_id * id ) ;
2005-04-17 02:20:36 +04:00
static int visor_calc_num_ports ( struct usb_serial * serial ) ;
2008-07-22 14:12:15 +04:00
static void visor_shutdown ( struct usb_serial * serial ) ;
static void visor_write_bulk_callback ( struct urb * urb ) ;
static void visor_read_bulk_callback ( struct urb * urb ) ;
static void visor_read_int_callback ( struct urb * urb ) ;
static int clie_3_5_startup ( struct usb_serial * serial ) ;
static int treo_attach ( struct usb_serial * serial ) ;
static int clie_5_attach ( struct usb_serial * serial ) ;
static int palm_os_3_probe ( struct usb_serial * serial ,
const struct usb_device_id * id ) ;
static int palm_os_4_probe ( struct usb_serial * serial ,
const struct usb_device_id * id ) ;
2005-04-17 02:20:36 +04:00
/* Parameters that may be passed into the module. */
static int debug ;
static __u16 vendor ;
static __u16 product ;
static struct usb_device_id id_table [ ] = {
{ USB_DEVICE ( HANDSPRING_VENDOR_ID , HANDSPRING_VISOR_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_3_probe } ,
{ USB_DEVICE ( HANDSPRING_VENDOR_ID , HANDSPRING_TREO_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( HANDSPRING_VENDOR_ID , HANDSPRING_TREO600_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
2006-02-19 21:00:04 +03:00
{ USB_DEVICE ( GSPDA_VENDOR_ID , GSPDA_XPLORE_M68_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
2005-04-17 02:20:36 +04:00
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M500_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M505_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M515_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_I705_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M100_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M125_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M130_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_TUNGSTEN_T_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
2005-04-19 04:39:20 +04:00
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_TREO_650 ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
2005-04-17 02:20:36 +04:00
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_TUNGSTEN_Z_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_ZIRE_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_4_0_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_S360_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_4_1_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_NX60_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_NZ90V_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_TJ25_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
2007-08-04 12:19:41 +04:00
{ USB_DEVICE ( ACER_VENDOR_ID , ACER_S10_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
2008-07-22 14:12:15 +04:00
{ USB_DEVICE ( SAMSUNG_VENDOR_ID , SAMSUNG_SCH_I330_ID ) ,
2005-04-17 02:20:36 +04:00
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
2008-07-22 14:12:15 +04:00
{ USB_DEVICE ( SAMSUNG_VENDOR_ID , SAMSUNG_SPH_I500_ID ) ,
2005-04-17 02:20:36 +04:00
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
2005-04-19 04:39:20 +04:00
{ USB_DEVICE ( TAPWAVE_VENDOR_ID , TAPWAVE_ZODIAC_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
2008-07-22 14:12:15 +04:00
{ USB_DEVICE ( GARMIN_VENDOR_ID , GARMIN_IQUE_3600_ID ) ,
2005-04-17 02:20:36 +04:00
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( ACEECA_VENDOR_ID , ACEECA_MEZ1000_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( KYOCERA_VENDOR_ID , KYOCERA_7135_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ USB_DEVICE ( FOSSIL_VENDOR_ID , FOSSIL_ABACUS_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ } , /* optional parameter entry */
{ } /* Terminating entry */
} ;
static struct usb_device_id clie_id_5_table [ ] = {
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_UX50_ID ) ,
. driver_info = ( kernel_ulong_t ) & palm_os_4_probe } ,
{ } , /* optional parameter entry */
{ } /* Terminating entry */
} ;
static struct usb_device_id clie_id_3_5_table [ ] = {
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_3_5_ID ) } ,
{ } /* Terminating entry */
} ;
static struct usb_device_id id_table_combined [ ] = {
{ USB_DEVICE ( HANDSPRING_VENDOR_ID , HANDSPRING_VISOR_ID ) } ,
{ USB_DEVICE ( HANDSPRING_VENDOR_ID , HANDSPRING_TREO_ID ) } ,
{ USB_DEVICE ( HANDSPRING_VENDOR_ID , HANDSPRING_TREO600_ID ) } ,
2006-02-19 21:00:04 +03:00
{ USB_DEVICE ( GSPDA_VENDOR_ID , GSPDA_XPLORE_M68_ID ) } ,
2005-04-17 02:20:36 +04:00
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M500_ID ) } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M505_ID ) } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M515_ID ) } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_I705_ID ) } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M100_ID ) } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M125_ID ) } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_M130_ID ) } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_TUNGSTEN_T_ID ) } ,
2005-04-19 04:39:20 +04:00
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_TREO_650 ) } ,
2005-04-17 02:20:36 +04:00
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_TUNGSTEN_Z_ID ) } ,
{ USB_DEVICE ( PALM_VENDOR_ID , PALM_ZIRE_ID ) } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_3_5_ID ) } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_4_0_ID ) } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_S360_ID ) } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_4_1_ID ) } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_NX60_ID ) } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_NZ90V_ID ) } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_UX50_ID ) } ,
{ USB_DEVICE ( SONY_VENDOR_ID , SONY_CLIE_TJ25_ID ) } ,
{ USB_DEVICE ( SAMSUNG_VENDOR_ID , SAMSUNG_SCH_I330_ID ) } ,
{ USB_DEVICE ( SAMSUNG_VENDOR_ID , SAMSUNG_SPH_I500_ID ) } ,
2005-04-19 04:39:20 +04:00
{ USB_DEVICE ( TAPWAVE_VENDOR_ID , TAPWAVE_ZODIAC_ID ) } ,
2005-04-17 02:20:36 +04:00
{ USB_DEVICE ( GARMIN_VENDOR_ID , GARMIN_IQUE_3600_ID ) } ,
{ USB_DEVICE ( ACEECA_VENDOR_ID , ACEECA_MEZ1000_ID ) } ,
{ USB_DEVICE ( KYOCERA_VENDOR_ID , KYOCERA_7135_ID ) } ,
{ USB_DEVICE ( FOSSIL_VENDOR_ID , FOSSIL_ABACUS_ID ) } ,
{ } , /* optional parameter entry */
{ } /* Terminating entry */
} ;
2008-07-22 14:12:15 +04:00
MODULE_DEVICE_TABLE ( usb , id_table_combined ) ;
2005-04-17 02:20:36 +04:00
static struct usb_driver visor_driver = {
. name = " visor " ,
. probe = usb_serial_probe ,
. disconnect = usb_serial_disconnect ,
. id_table = id_table_combined ,
2005-11-17 00:41:28 +03:00
. no_dynamic_id = 1 ,
2005-04-17 02:20:36 +04:00
} ;
2008-07-22 14:12:15 +04:00
/* All of the device info needed for the Handspring Visor,
and Palm 4.0 devices */
2005-06-21 08:15:16 +04:00
static struct usb_serial_driver handspring_device = {
2005-06-21 08:15:16 +04:00
. driver = {
. owner = THIS_MODULE ,
2005-06-21 08:15:16 +04:00
. name = " visor " ,
2005-06-21 08:15:16 +04:00
} ,
2005-06-21 08:15:16 +04:00
. description = " Handspring Visor / Palm OS " ,
2006-12-17 23:50:24 +03:00
. usb_driver = & visor_driver ,
2005-04-17 02:20:36 +04:00
. id_table = id_table ,
. num_ports = 2 ,
. open = visor_open ,
. close = visor_close ,
. throttle = visor_throttle ,
. unthrottle = visor_unthrottle ,
. attach = treo_attach ,
. probe = visor_probe ,
. calc_num_ports = visor_calc_num_ports ,
. shutdown = visor_shutdown ,
. write = visor_write ,
. write_room = visor_write_room ,
. write_bulk_callback = visor_write_bulk_callback ,
. read_bulk_callback = visor_read_bulk_callback ,
. read_int_callback = visor_read_int_callback ,
} ;
/* All of the device info needed for the Clie UX50, TH55 Palm 5.0 devices */
2005-06-21 08:15:16 +04:00
static struct usb_serial_driver clie_5_device = {
2005-06-21 08:15:16 +04:00
. driver = {
. owner = THIS_MODULE ,
2005-06-21 08:15:16 +04:00
. name = " clie_5 " ,
2005-06-21 08:15:16 +04:00
} ,
2005-06-21 08:15:16 +04:00
. description = " Sony Clie 5.0 " ,
2006-12-17 23:50:24 +03:00
. usb_driver = & visor_driver ,
2005-04-17 02:20:36 +04:00
. id_table = clie_id_5_table ,
. num_ports = 2 ,
. open = visor_open ,
. close = visor_close ,
. throttle = visor_throttle ,
. unthrottle = visor_unthrottle ,
. attach = clie_5_attach ,
. probe = visor_probe ,
. calc_num_ports = visor_calc_num_ports ,
. shutdown = visor_shutdown ,
. write = visor_write ,
. write_room = visor_write_room ,
. write_bulk_callback = visor_write_bulk_callback ,
. read_bulk_callback = visor_read_bulk_callback ,
. read_int_callback = visor_read_int_callback ,
} ;
/* device info for the Sony Clie OS version 3.5 */
2005-06-21 08:15:16 +04:00
static struct usb_serial_driver clie_3_5_device = {
2005-06-21 08:15:16 +04:00
. driver = {
. owner = THIS_MODULE ,
2005-06-21 08:15:16 +04:00
. name = " clie_3.5 " ,
2005-06-21 08:15:16 +04:00
} ,
2005-06-21 08:15:16 +04:00
. description = " Sony Clie 3.5 " ,
2006-12-17 23:50:24 +03:00
. usb_driver = & visor_driver ,
2005-04-17 02:20:36 +04:00
. id_table = clie_id_3_5_table ,
. num_ports = 1 ,
. open = visor_open ,
. close = visor_close ,
. throttle = visor_throttle ,
. unthrottle = visor_unthrottle ,
. attach = clie_3_5_startup ,
. write = visor_write ,
. write_room = visor_write_room ,
. write_bulk_callback = visor_write_bulk_callback ,
. read_bulk_callback = visor_read_bulk_callback ,
} ;
struct visor_private {
spinlock_t lock ;
int bytes_in ;
int bytes_out ;
int outstanding_urbs ;
2007-05-07 12:37:08 +04:00
unsigned char throttled ;
unsigned char actually_throttled ;
2005-04-17 02:20:36 +04:00
} ;
/* number of outstanding urbs to prevent userspace DoS from happening */
# define URB_UPPER_LIMIT 42
static int stats ;
/******************************************************************************
* Handspring Visor specific driver functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-07-22 14:12:15 +04:00
static int visor_open ( struct tty_struct * tty , struct usb_serial_port * port ,
struct file * filp )
2005-04-17 02:20:36 +04:00
{
struct usb_serial * serial = port - > serial ;
struct visor_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
int result = 0 ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
if ( ! port - > read_urb ) {
/* this is needed for some brain dead Sony devices */
dev_err ( & port - > dev , " Device lied about number of ports, please use a lower one. \n " ) ;
return - ENODEV ;
}
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > bytes_in = 0 ;
priv - > bytes_out = 0 ;
priv - > throttled = 0 ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
/* Start reading from the device */
2008-07-22 14:12:15 +04:00
usb_fill_bulk_urb ( port - > read_urb , serial - > dev ,
usb_rcvbulkpipe ( serial - > dev ,
2005-04-17 02:20:36 +04:00
port - > bulk_in_endpointAddress ) ,
port - > read_urb - > transfer_buffer ,
port - > read_urb - > transfer_buffer_length ,
visor_read_bulk_callback , port ) ;
result = usb_submit_urb ( port - > read_urb , GFP_KERNEL ) ;
if ( result ) {
2008-07-22 14:12:15 +04:00
dev_err ( & port - > dev ,
" %s - failed submitting read urb, error %d \n " ,
__func__ , result ) ;
2005-04-17 02:20:36 +04:00
goto exit ;
}
2008-07-22 14:12:15 +04:00
2005-04-17 02:20:36 +04:00
if ( port - > interrupt_in_urb ) {
2008-03-04 03:08:34 +03:00
dbg ( " %s - adding interrupt input for treo " , __func__ ) ;
2005-04-17 02:20:36 +04:00
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_KERNEL ) ;
if ( result )
2008-07-22 14:12:15 +04:00
dev_err ( & port - > dev ,
" %s - failed submitting interrupt urb, error %d \n " ,
__func__ , result ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:15 +04:00
exit :
2005-04-17 02:20:36 +04:00
return result ;
}
2008-07-22 14:09:07 +04:00
static void visor_close ( struct tty_struct * tty ,
2008-07-22 14:12:15 +04:00
struct usb_serial_port * port , struct file * filp )
2005-04-17 02:20:36 +04:00
{
struct visor_private * priv = usb_get_serial_port_data ( port ) ;
unsigned char * transfer_buffer ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2008-07-22 14:12:15 +04:00
2005-04-17 02:20:36 +04:00
/* shutdown our urbs */
usb_kill_urb ( port - > read_urb ) ;
2006-11-08 17:36:55 +03:00
usb_kill_urb ( port - > interrupt_in_urb ) ;
2005-04-17 02:20:36 +04:00
2008-01-21 19:44:10 +03:00
mutex_lock ( & port - > serial - > disc_mutex ) ;
if ( ! port - > serial - > disconnected ) {
/* Try to send shutdown message, unless the device is gone */
2008-07-22 14:12:15 +04:00
transfer_buffer = kmalloc ( 0x12 , GFP_KERNEL ) ;
2008-01-21 19:44:10 +03:00
if ( transfer_buffer ) {
2008-07-22 14:12:15 +04:00
usb_control_msg ( port - > serial - > dev ,
2008-01-21 19:44:10 +03:00
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
VISOR_CLOSE_NOTIFICATION , 0xc2 ,
0x0000 , 0x0000 ,
transfer_buffer , 0x12 , 300 ) ;
2008-07-22 14:12:15 +04:00
kfree ( transfer_buffer ) ;
2008-01-21 19:44:10 +03:00
}
2005-04-17 02:20:36 +04:00
}
2008-01-22 15:56:18 +03:00
mutex_unlock ( & port - > serial - > disc_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( stats )
dev_info ( & port - > dev , " Bytes In = %d Bytes Out = %d \n " ,
priv - > bytes_in , priv - > bytes_out ) ;
}
2008-07-22 14:09:07 +04:00
static int visor_write ( struct tty_struct * tty , struct usb_serial_port * port ,
const unsigned char * buf , int count )
2005-04-17 02:20:36 +04:00
{
struct visor_private * priv = usb_get_serial_port_data ( port ) ;
struct usb_serial * serial = port - > serial ;
struct urb * urb ;
unsigned char * buffer ;
unsigned long flags ;
int status ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( priv - > outstanding_urbs > URB_UPPER_LIMIT ) {
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - write limit hit \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-03-23 13:58:03 +03:00
priv - > outstanding_urbs + + ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2008-07-22 14:12:15 +04:00
buffer = kmalloc ( count , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( ! buffer ) {
dev_err ( & port - > dev , " out of memory \n " ) ;
2007-03-23 13:58:03 +03:00
count = - ENOMEM ;
goto error_no_buffer ;
2005-04-17 02:20:36 +04:00
}
urb = usb_alloc_urb ( 0 , GFP_ATOMIC ) ;
if ( ! urb ) {
dev_err ( & port - > dev , " no more free urbs \n " ) ;
2007-03-23 13:58:03 +03:00
count = - ENOMEM ;
goto error_no_urb ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:15 +04:00
memcpy ( buffer , buf , count ) ;
2005-04-17 02:20:36 +04:00
2008-03-04 03:08:34 +03:00
usb_serial_debug_data ( debug , & port - > dev , __func__ , count , buffer ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:12:15 +04:00
usb_fill_bulk_urb ( urb , serial - > dev ,
usb_sndbulkpipe ( serial - > dev ,
2005-04-17 02:20:36 +04:00
port - > bulk_out_endpointAddress ) ,
2008-07-22 14:12:15 +04:00
buffer , count ,
2005-04-17 02:20:36 +04:00
visor_write_bulk_callback , port ) ;
/* send it down the pipe */
status = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( status ) {
2008-07-22 14:12:15 +04:00
dev_err ( & port - > dev ,
" %s - usb_submit_urb(write bulk) failed with status = %d \n " ,
__func__ , status ) ;
2005-04-17 02:20:36 +04:00
count = status ;
2007-03-23 13:58:03 +03:00
goto error ;
2005-04-17 02:20:36 +04:00
} else {
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > bytes_out + = count ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
}
/* we are done with this urb, so let the host driver
* really free it when it is finished with it */
2007-03-23 13:58:03 +03:00
usb_free_urb ( urb ) ;
2005-04-17 02:20:36 +04:00
2007-03-23 13:58:03 +03:00
return count ;
error :
usb_free_urb ( urb ) ;
error_no_urb :
kfree ( buffer ) ;
error_no_buffer :
spin_lock_irqsave ( & priv - > lock , flags ) ;
- - priv - > outstanding_urbs ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2008-07-22 14:12:15 +04:00
static int visor_write_room ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2006-06-22 02:03:40 +04:00
struct visor_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
/*
* We really can take anything the user throws at us
* but let ' s pick a nice big number to tell the tty
2006-06-22 02:03:40 +04:00
* layer that we have lots of free space , unless we don ' t .
2005-04-17 02:20:36 +04:00
*/
2006-06-22 02:03:40 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( priv - > outstanding_urbs > URB_UPPER_LIMIT * 2 / 3 ) {
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - write limit hit \n " , __func__ ) ;
2006-06-22 02:03:40 +04:00
return 0 ;
}
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
return 2048 ;
}
2008-07-22 14:12:15 +04:00
static void visor_write_bulk_callback ( struct urb * urb )
2005-04-17 02:20:36 +04:00
{
2008-02-24 13:41:47 +03:00
struct usb_serial_port * port = urb - > context ;
2005-04-17 02:20:36 +04:00
struct visor_private * priv = usb_get_serial_port_data ( port ) ;
2007-06-16 02:44:13 +04:00
int status = urb - > status ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
/* free up the transfer buffer, as usb_free_urb() does not do this */
2008-07-22 14:12:15 +04:00
kfree ( urb - > transfer_buffer ) ;
2005-04-17 02:20:36 +04:00
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2007-06-16 02:44:13 +04:00
if ( status )
2005-04-17 02:20:36 +04:00
dbg ( " %s - nonzero write bulk status received: %d " ,
2008-03-04 03:08:34 +03:00
__func__ , status ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
- - priv - > outstanding_urbs ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2006-05-23 08:58:49 +04:00
usb_serial_port_softint ( port ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:15 +04:00
static void visor_read_bulk_callback ( struct urb * urb )
2005-04-17 02:20:36 +04:00
{
2008-02-24 13:41:47 +03:00
struct usb_serial_port * port = urb - > context ;
2005-04-17 02:20:36 +04:00
struct visor_private * priv = usb_get_serial_port_data ( port ) ;
unsigned char * data = urb - > transfer_buffer ;
2007-06-16 02:44:13 +04:00
int status = urb - > status ;
2005-04-17 02:20:36 +04:00
struct tty_struct * tty ;
int result ;
2008-10-13 13:39:46 +04:00
int available_room = 0 ;
2005-04-17 02:20:36 +04:00
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2007-06-16 02:44:13 +04:00
if ( status ) {
dbg ( " %s - nonzero read bulk status received: %d " ,
2008-03-04 03:08:34 +03:00
__func__ , status ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2008-07-22 14:12:15 +04:00
usb_serial_debug_data ( debug , & port - > dev , __func__ ,
urb - > actual_length , data ) ;
2005-04-17 02:20:36 +04:00
2008-10-13 13:39:46 +04:00
if ( urb - > actual_length ) {
tty = tty_port_tty_get ( & port - > port ) ;
if ( tty ) {
available_room = tty_buffer_request_room ( tty ,
2008-07-22 14:12:15 +04:00
urb - > actual_length ) ;
2008-10-13 13:39:46 +04:00
if ( available_room ) {
tty_insert_flip_string ( tty , data ,
available_room ) ;
tty_flip_buffer_push ( tty ) ;
}
tty_kref_put ( tty ) ;
2007-05-07 12:37:08 +04:00
}
spin_lock ( & priv - > lock ) ;
priv - > bytes_in + = available_room ;
} else {
spin_lock ( & priv - > lock ) ;
2005-04-17 02:20:36 +04:00
}
/* Continue trying to always read if we should */
2007-05-07 12:37:08 +04:00
if ( ! priv - > throttled ) {
2008-07-22 14:12:15 +04:00
usb_fill_bulk_urb ( port - > read_urb , port - > serial - > dev ,
2005-04-17 02:20:36 +04:00
usb_rcvbulkpipe ( port - > serial - > dev ,
2008-07-22 14:12:15 +04:00
port - > bulk_in_endpointAddress ) ,
2005-04-17 02:20:36 +04:00
port - > read_urb - > transfer_buffer ,
port - > read_urb - > transfer_buffer_length ,
visor_read_bulk_callback , port ) ;
result = usb_submit_urb ( port - > read_urb , GFP_ATOMIC ) ;
if ( result )
2008-07-22 14:12:15 +04:00
dev_err ( & port - > dev ,
" %s - failed resubmitting read urb, error %d \n " ,
__func__ , result ) ;
} else
2007-05-07 12:37:08 +04:00
priv - > actually_throttled = 1 ;
spin_unlock ( & priv - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:15 +04:00
static void visor_read_int_callback ( struct urb * urb )
2005-04-17 02:20:36 +04:00
{
2008-02-24 13:41:47 +03:00
struct usb_serial_port * port = urb - > context ;
2007-06-16 02:44:13 +04:00
int status = urb - > status ;
2005-04-17 02:20:36 +04:00
int result ;
2007-06-16 02:44:13 +04:00
switch ( status ) {
2005-04-17 02:20:36 +04:00
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
/* this urb is terminated, clean up */
dbg ( " %s - urb shutting down with status: %d " ,
2008-03-04 03:08:34 +03:00
__func__ , status ) ;
2005-04-17 02:20:36 +04:00
return ;
default :
dbg ( " %s - nonzero urb status received: %d " ,
2008-03-04 03:08:34 +03:00
__func__ , status ) ;
2005-04-17 02:20:36 +04:00
goto exit ;
}
/*
* This information is still unknown what it can be used for .
* If anyone has an idea , please let the author know . . .
*
* Rumor has it this endpoint is used to notify when data
* is ready to be read from the bulk ones .
*/
2008-03-04 03:08:34 +03:00
usb_serial_debug_data ( debug , & port - > dev , __func__ ,
2005-04-17 02:20:36 +04:00
urb - > actual_length , urb - > transfer_buffer ) ;
exit :
2008-07-22 14:12:15 +04:00
result = usb_submit_urb ( urb , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( result )
2008-07-22 14:12:15 +04:00
dev_err ( & urb - > dev - > dev ,
" %s - Error %d submitting interrupt urb \n " ,
__func__ , result ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:15 +04:00
static void visor_throttle ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
struct visor_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > throttled = 1 ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
}
2008-07-22 14:12:15 +04:00
static void visor_unthrottle ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
struct visor_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
int result ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > throttled = 0 ;
2007-05-07 12:37:08 +04:00
priv - > actually_throttled = 0 ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
port - > read_urb - > dev = port - > serial - > dev ;
result = usb_submit_urb ( port - > read_urb , GFP_ATOMIC ) ;
if ( result )
2008-07-22 14:12:15 +04:00
dev_err ( & port - > dev ,
" %s - failed submitting read urb, error %d \n " ,
__func__ , result ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:15 +04:00
static int palm_os_3_probe ( struct usb_serial * serial ,
const struct usb_device_id * id )
2005-04-17 02:20:36 +04:00
{
struct device * dev = & serial - > dev - > dev ;
struct visor_connection_info * connection_info ;
unsigned char * transfer_buffer ;
char * string ;
int retval = 0 ;
int i ;
int num_ports = 0 ;
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:12:15 +04:00
transfer_buffer = kmalloc ( sizeof ( * connection_info ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! transfer_buffer ) {
2008-03-04 03:08:34 +03:00
dev_err ( dev , " %s - kmalloc(%Zd) failed. \n " , __func__ ,
2005-04-17 02:20:36 +04:00
sizeof ( * connection_info ) ) ;
return - ENOMEM ;
}
/* send a get connection info request */
2008-07-22 14:12:15 +04:00
retval = usb_control_msg ( serial - > dev ,
2005-04-17 02:20:36 +04:00
usb_rcvctrlpipe ( serial - > dev , 0 ) ,
VISOR_GET_CONNECTION_INFORMATION ,
0xc2 , 0x0000 , 0x0000 , transfer_buffer ,
sizeof ( * connection_info ) , 300 ) ;
if ( retval < 0 ) {
dev_err ( dev , " %s - error %d getting connection information \n " ,
2008-03-04 03:08:34 +03:00
__func__ , retval ) ;
2005-04-17 02:20:36 +04:00
goto exit ;
}
if ( retval = = sizeof ( * connection_info ) ) {
2008-07-22 14:12:15 +04:00
connection_info = ( struct visor_connection_info * )
transfer_buffer ;
2005-04-17 02:20:36 +04:00
num_ports = le16_to_cpu ( connection_info - > num_ports ) ;
for ( i = 0 ; i < num_ports ; + + i ) {
2008-07-22 14:12:15 +04:00
switch (
connection_info - > connections [ i ] . port_function_id ) {
case VISOR_FUNCTION_GENERIC :
string = " Generic " ;
break ;
case VISOR_FUNCTION_DEBUGGER :
string = " Debugger " ;
break ;
case VISOR_FUNCTION_HOTSYNC :
string = " HotSync " ;
break ;
case VISOR_FUNCTION_CONSOLE :
string = " Console " ;
break ;
case VISOR_FUNCTION_REMOTE_FILE_SYS :
string = " Remote File System " ;
break ;
default :
string = " unknown " ;
break ;
2005-04-17 02:20:36 +04:00
}
dev_info ( dev , " %s: port %d, is for %s use \n " ,
2005-06-21 08:15:16 +04:00
serial - > type - > description ,
2005-04-17 02:20:36 +04:00
connection_info - > connections [ i ] . port , string ) ;
}
}
/*
* Handle devices that report invalid stuff here .
*/
if ( num_ports = = 0 | | num_ports > 2 ) {
2008-07-22 14:12:15 +04:00
dev_warn ( dev , " %s: No valid connect info available \n " ,
2005-06-21 08:15:16 +04:00
serial - > type - > description ) ;
2005-04-17 02:20:36 +04:00
num_ports = 2 ;
}
2008-07-22 14:12:15 +04:00
2005-06-21 08:15:16 +04:00
dev_info ( dev , " %s: Number of ports: %d \n " , serial - > type - > description ,
2005-04-17 02:20:36 +04:00
num_ports ) ;
/*
* save off our num_ports info so that we can use it in the
* calc_num_ports callback
*/
usb_set_serial_data ( serial , ( void * ) ( long ) num_ports ) ;
2008-07-22 14:12:15 +04:00
/* ask for the number of bytes available, but ignore the
response as it is broken */
retval = usb_control_msg ( serial - > dev ,
2005-04-17 02:20:36 +04:00
usb_rcvctrlpipe ( serial - > dev , 0 ) ,
VISOR_REQUEST_BYTES_AVAILABLE ,
0xc2 , 0x0000 , 0x0005 , transfer_buffer ,
0x02 , 300 ) ;
if ( retval < 0 )
dev_err ( dev , " %s - error %d getting bytes available request \n " ,
2008-03-04 03:08:34 +03:00
__func__ , retval ) ;
2005-04-17 02:20:36 +04:00
retval = 0 ;
exit :
2008-07-22 14:12:15 +04:00
kfree ( transfer_buffer ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2008-07-22 14:12:15 +04:00
static int palm_os_4_probe ( struct usb_serial * serial ,
const struct usb_device_id * id )
2005-04-17 02:20:36 +04:00
{
struct device * dev = & serial - > dev - > dev ;
struct palm_ext_connection_info * connection_info ;
unsigned char * transfer_buffer ;
int retval ;
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:12:15 +04:00
transfer_buffer = kmalloc ( sizeof ( * connection_info ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! transfer_buffer ) {
2008-03-04 03:08:34 +03:00
dev_err ( dev , " %s - kmalloc(%Zd) failed. \n " , __func__ ,
2005-04-17 02:20:36 +04:00
sizeof ( * connection_info ) ) ;
return - ENOMEM ;
}
2008-07-22 14:12:15 +04:00
retval = usb_control_msg ( serial - > dev ,
usb_rcvctrlpipe ( serial - > dev , 0 ) ,
2005-04-17 02:20:36 +04:00
PALM_GET_EXT_CONNECTION_INFORMATION ,
0xc2 , 0x0000 , 0x0000 , transfer_buffer ,
2008-07-22 14:12:15 +04:00
sizeof ( * connection_info ) , 300 ) ;
2005-04-17 02:20:36 +04:00
if ( retval < 0 )
dev_err ( dev , " %s - error %d getting connection info \n " ,
2008-03-04 03:08:34 +03:00
__func__ , retval ) ;
2005-04-17 02:20:36 +04:00
else
2008-03-04 03:08:34 +03:00
usb_serial_debug_data ( debug , & serial - > dev - > dev , __func__ ,
2005-04-17 02:20:36 +04:00
retval , transfer_buffer ) ;
2008-07-22 14:12:15 +04:00
kfree ( transfer_buffer ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-07-22 14:12:15 +04:00
static int visor_probe ( struct usb_serial * serial ,
const struct usb_device_id * id )
2005-04-17 02:20:36 +04:00
{
int retval = 0 ;
2008-07-22 14:12:15 +04:00
int ( * startup ) ( struct usb_serial * serial ,
const struct usb_device_id * id ) ;
2005-04-17 02:20:36 +04:00
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( serial - > dev - > actconfig - > desc . bConfigurationValue ! = 1 ) {
2008-08-21 03:56:34 +04:00
dev_err ( & serial - > dev - > dev , " active config #%d != 1 ?? \n " ,
2005-04-17 02:20:36 +04:00
serial - > dev - > actconfig - > desc . bConfigurationValue ) ;
return - ENODEV ;
}
if ( id - > driver_info ) {
startup = ( void * ) id - > driver_info ;
retval = startup ( serial , id ) ;
}
return retval ;
}
2008-07-22 14:12:15 +04:00
static int visor_calc_num_ports ( struct usb_serial * serial )
2005-04-17 02:20:36 +04:00
{
int num_ports = ( int ) ( long ) ( usb_get_serial_data ( serial ) ) ;
if ( num_ports )
usb_set_serial_data ( serial , NULL ) ;
return num_ports ;
}
static int generic_startup ( struct usb_serial * serial )
{
2006-06-22 02:03:40 +04:00
struct usb_serial_port * * ports = serial - > port ;
2005-04-17 02:20:36 +04:00
struct visor_private * priv ;
int i ;
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
2008-07-22 14:12:15 +04:00
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
2006-06-22 02:03:40 +04:00
if ( ! priv ) {
while ( i - - ! = 0 ) {
priv = usb_get_serial_port_data ( ports [ i ] ) ;
usb_set_serial_port_data ( ports [ i ] , NULL ) ;
kfree ( priv ) ;
}
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2006-06-22 02:03:40 +04:00
}
2005-04-17 02:20:36 +04:00
spin_lock_init ( & priv - > lock ) ;
2006-06-22 02:03:40 +04:00
usb_set_serial_port_data ( ports [ i ] , priv ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2008-07-22 14:12:15 +04:00
static int clie_3_5_startup ( struct usb_serial * serial )
2005-04-17 02:20:36 +04:00
{
struct device * dev = & serial - > dev - > dev ;
int result ;
u8 data ;
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
/*
* Note that PEG - 300 series devices expect the following two calls .
*/
/* get the config number */
2008-07-22 14:12:15 +04:00
result = usb_control_msg ( serial - > dev , usb_rcvctrlpipe ( serial - > dev , 0 ) ,
2005-04-17 02:20:36 +04:00
USB_REQ_GET_CONFIGURATION , USB_DIR_IN ,
0 , 0 , & data , 1 , 3000 ) ;
if ( result < 0 ) {
2008-07-22 14:12:15 +04:00
dev_err ( dev , " %s: get config number failed: %d \n " ,
__func__ , result ) ;
2005-04-17 02:20:36 +04:00
return result ;
}
if ( result ! = 1 ) {
2008-07-22 14:12:15 +04:00
dev_err ( dev , " %s: get config number bad return length: %d \n " ,
__func__ , result ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
/* get the interface number */
2008-07-22 14:12:15 +04:00
result = usb_control_msg ( serial - > dev , usb_rcvctrlpipe ( serial - > dev , 0 ) ,
USB_REQ_GET_INTERFACE ,
2005-04-17 02:20:36 +04:00
USB_DIR_IN | USB_RECIP_INTERFACE ,
0 , 0 , & data , 1 , 3000 ) ;
if ( result < 0 ) {
2008-07-22 14:12:15 +04:00
dev_err ( dev , " %s: get interface number failed: %d \n " ,
__func__ , result ) ;
2005-04-17 02:20:36 +04:00
return result ;
}
if ( result ! = 1 ) {
2008-07-22 14:12:15 +04:00
dev_err ( dev ,
" %s: get interface number bad return length: %d \n " ,
__func__ , result ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
return generic_startup ( serial ) ;
}
2008-07-22 14:12:15 +04:00
static int treo_attach ( struct usb_serial * serial )
2005-04-17 02:20:36 +04:00
{
struct usb_serial_port * swap_port ;
/* Only do this endpoint hack for the Handspring devices with
* interrupt in endpoints , which for now are the Treo devices . */
2008-07-22 14:12:15 +04:00
if ( ! ( ( le16_to_cpu ( serial - > dev - > descriptor . idVendor )
= = HANDSPRING_VENDOR_ID ) | |
( le16_to_cpu ( serial - > dev - > descriptor . idVendor )
= = KYOCERA_VENDOR_ID ) ) | |
( serial - > num_interrupt_in = = 0 ) )
2005-04-17 02:20:36 +04:00
goto generic_startup ;
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
/*
2008-07-22 14:12:15 +04:00
* It appears that Treos and Kyoceras want to use the
* 1 st bulk in endpoint to communicate with the 2 nd bulk out endpoint ,
* so let ' s swap the 1 st and 2 nd bulk in and interrupt endpoints .
* Note that swapping the bulk out endpoints would break lots of
2005-04-17 02:20:36 +04:00
* apps that want to communicate on the second port .
*/
# define COPY_PORT(dest, src) \
2008-07-22 14:12:15 +04:00
do { \
dest - > read_urb = src - > read_urb ; \
dest - > bulk_in_endpointAddress = src - > bulk_in_endpointAddress ; \
dest - > bulk_in_buffer = src - > bulk_in_buffer ; \
dest - > interrupt_in_urb = src - > interrupt_in_urb ; \
dest - > interrupt_in_endpointAddress = \
src - > interrupt_in_endpointAddress ; \
dest - > interrupt_in_buffer = src - > interrupt_in_buffer ; \
} while ( 0 ) ;
2005-04-17 02:20:36 +04:00
swap_port = kmalloc ( sizeof ( * swap_port ) , GFP_KERNEL ) ;
if ( ! swap_port )
return - ENOMEM ;
COPY_PORT ( swap_port , serial - > port [ 0 ] ) ;
COPY_PORT ( serial - > port [ 0 ] , serial - > port [ 1 ] ) ;
COPY_PORT ( serial - > port [ 1 ] , swap_port ) ;
kfree ( swap_port ) ;
generic_startup :
return generic_startup ( serial ) ;
}
2008-07-22 14:12:15 +04:00
static int clie_5_attach ( struct usb_serial * serial )
2005-04-17 02:20:36 +04:00
{
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:12:15 +04:00
/* TH55 registers 2 ports.
Communication in from the UX50 / TH55 uses bulk_in_endpointAddress
from port 0. Communication out to the UX50 / TH55 uses
bulk_out_endpointAddress from port 1
2005-04-17 02:20:36 +04:00
Lets do a quick and dirty mapping
*/
2008-07-22 14:12:15 +04:00
2005-04-17 02:20:36 +04:00
/* some sanity check */
if ( serial - > num_ports < 2 )
return - 1 ;
2008-07-22 14:12:15 +04:00
2005-04-17 02:20:36 +04:00
/* port 0 now uses the modified endpoint Address */
2008-07-22 14:12:15 +04:00
serial - > port [ 0 ] - > bulk_out_endpointAddress =
serial - > port [ 1 ] - > bulk_out_endpointAddress ;
2005-04-17 02:20:36 +04:00
return generic_startup ( serial ) ;
}
2008-07-22 14:12:15 +04:00
static void visor_shutdown ( struct usb_serial * serial )
2005-04-17 02:20:36 +04:00
{
2006-06-22 02:03:40 +04:00
struct visor_private * priv ;
int i ;
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2006-06-22 02:03:40 +04:00
for ( i = 0 ; i < serial - > num_ports ; i + + ) {
priv = usb_get_serial_port_data ( serial - > port [ i ] ) ;
if ( priv ) {
usb_set_serial_port_data ( serial - > port [ i ] , NULL ) ;
kfree ( priv ) ;
}
}
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:15 +04:00
static int __init visor_init ( void )
2005-04-17 02:20:36 +04:00
{
int i , retval ;
/* Only if parameters were passed to us */
2008-07-22 14:12:15 +04:00
if ( vendor > 0 & & product > 0 ) {
struct usb_device_id usb_dev_temp [ ] = {
{
USB_DEVICE ( vendor , product ) ,
. driver_info =
( kernel_ulong_t ) & palm_os_4_probe
}
} ;
2005-04-17 02:20:36 +04:00
/* Find the last entry in id_table */
2008-07-22 14:12:15 +04:00
for ( i = 0 ; ; i + + ) {
if ( id_table [ i ] . idVendor = = 0 ) {
2005-04-17 02:20:36 +04:00
id_table [ i ] = usb_dev_temp [ 0 ] ;
break ;
}
}
/* Find the last entry in id_table_combined */
2008-07-22 14:12:15 +04:00
for ( i = 0 ; ; i + + ) {
if ( id_table_combined [ i ] . idVendor = = 0 ) {
2005-04-17 02:20:36 +04:00
id_table_combined [ i ] = usb_dev_temp [ 0 ] ;
break ;
}
}
2008-08-19 00:21:04 +04:00
printk ( KERN_INFO KBUILD_MODNAME
" : Untested USB device specified at time of module insertion \n " ) ;
printk ( KERN_INFO KBUILD_MODNAME
" : Warning: This is not guaranteed to work \n " ) ;
printk ( KERN_INFO KBUILD_MODNAME
" : Using a newer kernel is preferred to this method \n " ) ;
printk ( KERN_INFO KBUILD_MODNAME
" : Adding Palm OS protocol 4.x support for unknown device: 0x%x/0x%x \n " ,
2005-04-17 02:20:36 +04:00
vendor , product ) ;
}
retval = usb_serial_register ( & handspring_device ) ;
if ( retval )
goto failed_handspring_register ;
retval = usb_serial_register ( & clie_3_5_device ) ;
if ( retval )
goto failed_clie_3_5_register ;
retval = usb_serial_register ( & clie_5_device ) ;
if ( retval )
goto failed_clie_5_register ;
retval = usb_register ( & visor_driver ) ;
2008-07-22 14:12:15 +04:00
if ( retval )
2005-04-17 02:20:36 +04:00
goto failed_usb_register ;
2008-08-19 00:21:04 +04:00
printk ( KERN_INFO KBUILD_MODNAME " : " DRIVER_DESC " \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
failed_usb_register :
usb_serial_deregister ( & clie_5_device ) ;
failed_clie_5_register :
usb_serial_deregister ( & clie_3_5_device ) ;
failed_clie_3_5_register :
usb_serial_deregister ( & handspring_device ) ;
failed_handspring_register :
return retval ;
}
static void __exit visor_exit ( void )
{
2008-07-22 14:12:15 +04:00
usb_deregister ( & visor_driver ) ;
usb_serial_deregister ( & handspring_device ) ;
usb_serial_deregister ( & clie_3_5_device ) ;
usb_serial_deregister ( & clie_5_device ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( visor_init ) ;
module_exit ( visor_exit ) ;
2008-07-22 14:12:15 +04:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
module_param ( debug , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Debug enabled or not " ) ;
module_param ( stats , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( stats , " Enables statistics or not " ) ;
module_param ( vendor , ushort , 0 ) ;
MODULE_PARM_DESC ( vendor , " User specified vendor ID " ) ;
module_param ( product , ushort , 0 ) ;
MODULE_PARM_DESC ( product , " User specified product ID " ) ;