2005-05-11 22:17:09 +04:00
/******************************************************************************
* xusbatm . c - dumb usbatm - based driver for modems initialized in userspace
*
* Copyright ( C ) 2005 Duncan Sands , Roman Kagan ( rkagan % mail ! ru )
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation ; either version 2 of the License , or ( at your option )
* any later version .
*
* 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 . , 59
* Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/module.h>
2012-07-13 09:33:11 +04:00
# include <linux/etherdevice.h> /* for eth_random_addr() */
2005-05-11 22:17:09 +04:00
# include "usbatm.h"
# define XUSBATM_DRIVERS_MAX 8
# define XUSBATM_PARM(name, type, parmtype, desc) \
static type name [ XUSBATM_DRIVERS_MAX ] ; \
2007-10-14 22:35:30 +04:00
static unsigned int num_ # # name ; \
2005-05-11 22:17:09 +04:00
module_param_array ( name , parmtype , & num_ # # name , 0444 ) ; \
MODULE_PARM_DESC ( name , desc )
XUSBATM_PARM ( vendor , unsigned short , ushort , " USB device vendor " ) ;
XUSBATM_PARM ( product , unsigned short , ushort , " USB device product " ) ;
XUSBATM_PARM ( rx_endpoint , unsigned char , byte , " rx endpoint number " ) ;
XUSBATM_PARM ( tx_endpoint , unsigned char , byte , " tx endpoint number " ) ;
XUSBATM_PARM ( rx_padding , unsigned char , byte , " rx padding (default 0) " ) ;
XUSBATM_PARM ( tx_padding , unsigned char , byte , " tx padding (default 0) " ) ;
2006-01-13 11:48:36 +03:00
XUSBATM_PARM ( rx_altsetting , unsigned char , byte , " rx altsetting (default 0) " ) ;
XUSBATM_PARM ( tx_altsetting , unsigned char , byte , " rx altsetting (default 0) " ) ;
2005-05-11 22:17:09 +04:00
static const char xusbatm_driver_name [ ] = " xusbatm " ;
static struct usbatm_driver xusbatm_drivers [ XUSBATM_DRIVERS_MAX ] ;
static struct usb_device_id xusbatm_usb_ids [ XUSBATM_DRIVERS_MAX + 1 ] ;
static struct usb_driver xusbatm_usb_driver ;
2010-06-17 13:55:49 +04:00
static struct usb_interface * xusbatm_find_intf ( struct usb_device * usb_dev , int altsetting , u8 ep )
2005-05-11 22:17:09 +04:00
{
2006-01-13 11:48:36 +03:00
struct usb_host_interface * alt ;
struct usb_interface * intf ;
2005-05-11 22:17:09 +04:00
int i , j ;
2010-06-17 13:55:49 +04:00
for ( i = 0 ; i < usb_dev - > actconfig - > desc . bNumInterfaces ; i + + )
2006-01-13 11:48:36 +03:00
if ( ( intf = usb_dev - > actconfig - > interface [ i ] ) & & ( alt = usb_altnum_to_altsetting ( intf , altsetting ) ) )
for ( j = 0 ; j < alt - > desc . bNumEndpoints ; j + + )
if ( alt - > endpoint [ j ] . desc . bEndpointAddress = = ep )
return intf ;
return NULL ;
}
2010-06-17 13:55:49 +04:00
static int xusbatm_capture_intf ( struct usbatm_data * usbatm , struct usb_device * usb_dev ,
2006-01-13 11:48:36 +03:00
struct usb_interface * intf , int altsetting , int claim )
{
int ifnum = intf - > altsetting - > desc . bInterfaceNumber ;
int ret ;
if ( claim & & ( ret = usb_driver_claim_interface ( & xusbatm_usb_driver , intf , usbatm ) ) ) {
usb_err ( usbatm , " %s: failed to claim interface %2d (%d)! \n " , __func__ , ifnum , ret ) ;
return ret ;
}
if ( ( ret = usb_set_interface ( usb_dev , ifnum , altsetting ) ) ) {
usb_err ( usbatm , " %s: altsetting %2d for interface %2d failed (%d)! \n " , __func__ , altsetting , ifnum , ret ) ;
return ret ;
2005-05-11 22:17:09 +04:00
}
return 0 ;
}
2010-06-17 13:55:49 +04:00
static void xusbatm_release_intf ( struct usb_device * usb_dev , struct usb_interface * intf , int claimed )
2006-01-13 11:48:36 +03:00
{
if ( claimed ) {
usb_set_intfdata ( intf , NULL ) ;
usb_driver_release_interface ( & xusbatm_usb_driver , intf ) ;
}
}
2006-01-17 13:15:13 +03:00
static int xusbatm_bind ( struct usbatm_data * usbatm ,
2006-01-17 13:16:13 +03:00
struct usb_interface * intf , const struct usb_device_id * id )
2005-05-11 22:17:09 +04:00
{
struct usb_device * usb_dev = interface_to_usbdev ( intf ) ;
int drv_ix = id - xusbatm_usb_ids ;
2006-01-13 11:48:36 +03:00
int rx_alt = rx_altsetting [ drv_ix ] ;
int tx_alt = tx_altsetting [ drv_ix ] ;
struct usb_interface * rx_intf = xusbatm_find_intf ( usb_dev , rx_alt , rx_endpoint [ drv_ix ] ) ;
struct usb_interface * tx_intf = xusbatm_find_intf ( usb_dev , tx_alt , tx_endpoint [ drv_ix ] ) ;
int ret ;
2005-05-11 22:17:09 +04:00
2006-01-17 13:15:13 +03:00
usb_dbg ( usbatm , " %s: binding driver %d: vendor %04x product %04x "
2006-01-13 11:48:36 +03:00
" rx: ep %02x padd %d alt %2d tx: ep %02x padd %d alt %2d \n " ,
2005-05-11 22:17:09 +04:00
__func__ , drv_ix , vendor [ drv_ix ] , product [ drv_ix ] ,
2006-01-13 11:48:36 +03:00
rx_endpoint [ drv_ix ] , rx_padding [ drv_ix ] , rx_alt ,
tx_endpoint [ drv_ix ] , tx_padding [ drv_ix ] , tx_alt ) ;
if ( ! rx_intf | | ! tx_intf ) {
if ( ! rx_intf )
usb_dbg ( usbatm , " %s: no interface contains endpoint %02x in altsetting %2d \n " ,
__func__ , rx_endpoint [ drv_ix ] , rx_alt ) ;
if ( ! tx_intf )
usb_dbg ( usbatm , " %s: no interface contains endpoint %02x in altsetting %2d \n " ,
__func__ , tx_endpoint [ drv_ix ] , tx_alt ) ;
return - ENODEV ;
}
2005-05-11 22:17:09 +04:00
2006-01-13 11:48:36 +03:00
if ( ( rx_intf ! = intf ) & & ( tx_intf ! = intf ) )
2005-05-11 22:17:09 +04:00
return - ENODEV ;
2006-01-13 11:48:36 +03:00
if ( ( rx_intf = = tx_intf ) & & ( rx_alt ! = tx_alt ) ) {
usb_err ( usbatm , " %s: altsettings clash on interface %2d (%2d vs %2d)! \n " , __func__ ,
rx_intf - > altsetting - > desc . bInterfaceNumber , rx_alt , tx_alt ) ;
return - EINVAL ;
2005-05-11 22:17:09 +04:00
}
2006-01-13 11:48:36 +03:00
usb_dbg ( usbatm , " %s: rx If#=%2d; tx If#=%2d \n " , __func__ ,
rx_intf - > altsetting - > desc . bInterfaceNumber ,
tx_intf - > altsetting - > desc . bInterfaceNumber ) ;
2005-05-11 22:17:09 +04:00
2006-01-13 11:48:36 +03:00
if ( ( ret = xusbatm_capture_intf ( usbatm , usb_dev , rx_intf , rx_alt , rx_intf ! = intf ) ) )
return ret ;
if ( ( tx_intf ! = rx_intf ) & & ( ret = xusbatm_capture_intf ( usbatm , usb_dev , tx_intf , tx_alt , tx_intf ! = intf ) ) ) {
xusbatm_release_intf ( usb_dev , rx_intf , rx_intf ! = intf ) ;
return ret ;
2005-05-11 22:17:09 +04:00
}
2006-01-13 11:48:36 +03:00
return 0 ;
2005-05-11 22:17:09 +04:00
}
2006-01-17 13:15:13 +03:00
static void xusbatm_unbind ( struct usbatm_data * usbatm ,
2005-05-11 22:17:09 +04:00
struct usb_interface * intf )
{
struct usb_device * usb_dev = interface_to_usbdev ( intf ) ;
int i ;
2006-01-17 13:15:13 +03:00
usb_dbg ( usbatm , " %s entered \n " , __func__ ) ;
2005-05-11 22:17:09 +04:00
2010-06-17 13:55:49 +04:00
for ( i = 0 ; i < usb_dev - > actconfig - > desc . bNumInterfaces ; i + + ) {
2006-01-13 11:48:36 +03:00
struct usb_interface * cur_intf = usb_dev - > actconfig - > interface [ i ] ;
if ( cur_intf & & ( usb_get_intfdata ( cur_intf ) = = usbatm ) ) {
usb_set_intfdata ( cur_intf , NULL ) ;
usb_driver_release_interface ( & xusbatm_usb_driver , cur_intf ) ;
}
2005-05-11 22:17:09 +04:00
}
}
2006-01-17 13:15:13 +03:00
static int xusbatm_atm_start ( struct usbatm_data * usbatm ,
2005-05-11 22:17:09 +04:00
struct atm_dev * atm_dev )
{
2006-01-17 13:15:13 +03:00
atm_dbg ( usbatm , " %s entered \n " , __func__ ) ;
2005-05-11 22:17:09 +04:00
/* use random MAC as we've no way to get it from the device */
2012-07-13 09:33:11 +04:00
eth_random_addr ( atm_dev - > esi ) ;
2005-05-11 22:17:09 +04:00
return 0 ;
}
static int xusbatm_usb_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
return usbatm_usb_probe ( intf , id ,
xusbatm_drivers + ( id - xusbatm_usb_ids ) ) ;
}
static struct usb_driver xusbatm_usb_driver = {
. name = xusbatm_driver_name ,
. probe = xusbatm_usb_probe ,
. disconnect = usbatm_usb_disconnect ,
. id_table = xusbatm_usb_ids
} ;
static int __init xusbatm_init ( void )
{
int i ;
if ( ! num_vendor | |
num_vendor ! = num_product | |
num_vendor ! = num_rx_endpoint | |
num_vendor ! = num_tx_endpoint ) {
2008-08-14 20:37:34 +04:00
printk ( KERN_WARNING " xusbatm: malformed module parameters \n " ) ;
2005-05-11 22:17:09 +04:00
return - EINVAL ;
}
for ( i = 0 ; i < num_vendor ; i + + ) {
2006-01-13 11:48:36 +03:00
rx_endpoint [ i ] | = USB_DIR_IN ;
tx_endpoint [ i ] & = USB_ENDPOINT_NUMBER_MASK ;
2005-05-11 22:17:09 +04:00
xusbatm_usb_ids [ i ] . match_flags = USB_DEVICE_ID_MATCH_DEVICE ;
xusbatm_usb_ids [ i ] . idVendor = vendor [ i ] ;
xusbatm_usb_ids [ i ] . idProduct = product [ i ] ;
xusbatm_drivers [ i ] . driver_name = xusbatm_driver_name ;
xusbatm_drivers [ i ] . bind = xusbatm_bind ;
xusbatm_drivers [ i ] . unbind = xusbatm_unbind ;
xusbatm_drivers [ i ] . atm_start = xusbatm_atm_start ;
2006-01-13 12:59:23 +03:00
xusbatm_drivers [ i ] . bulk_in = rx_endpoint [ i ] ;
xusbatm_drivers [ i ] . bulk_out = tx_endpoint [ i ] ;
2005-05-11 22:17:09 +04:00
xusbatm_drivers [ i ] . rx_padding = rx_padding [ i ] ;
xusbatm_drivers [ i ] . tx_padding = tx_padding [ i ] ;
}
return usb_register ( & xusbatm_usb_driver ) ;
}
module_init ( xusbatm_init ) ;
static void __exit xusbatm_exit ( void )
{
usb_deregister ( & xusbatm_usb_driver ) ;
}
module_exit ( xusbatm_exit ) ;
MODULE_AUTHOR ( " Roman Kagan, Duncan Sands " ) ;
MODULE_DESCRIPTION ( " Driver for USB ADSL modems initialized in userspace " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( " 0.1 " ) ;