2005-04-16 15:20:36 -07:00
/******************************************************************************
* speedtch . c - Alcatel SpeedTouch USB xDSL modem driver
*
* Copyright ( C ) 2001 , Alcatel
* Copyright ( C ) 2003 , Duncan Sands
* Copyright ( C ) 2004 , David Woodhouse
*
2005-05-11 20:20:40 +02:00
* Based on " modem_run.c " , copyright ( C ) 2001 , Benoit Papillault
*
2005-04-16 15:20:36 -07:00
* 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 .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-05-11 20:20:40 +02:00
# include <asm/page.h>
# include <linux/device.h>
# include <linux/errno.h>
# include <linux/firmware.h>
2005-04-16 15:20:36 -07:00
# include <linux/gfp.h>
2005-05-11 20:20:40 +02:00
# include <linux/init.h>
2005-04-16 15:20:36 -07:00
# include <linux/kernel.h>
2005-05-11 20:20:40 +02:00
# include <linux/module.h>
# include <linux/moduleparam.h>
2005-04-16 15:20:36 -07:00
# include <linux/slab.h>
2005-05-11 20:20:40 +02:00
# include <linux/stat.h>
# include <linux/timer.h>
# include <linux/workqueue.h>
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
# include "usbatm.h"
2005-04-16 15:20:36 -07:00
# define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
2005-05-11 20:20:40 +02:00
# define DRIVER_VERSION "1.9"
2005-04-16 15:20:36 -07:00
# define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
static const char speedtch_driver_name [ ] = " speedtch " ;
2005-05-11 20:20:40 +02:00
# define CTRL_TIMEOUT 2000 /* milliseconds */
# define DATA_TIMEOUT 2000 /* milliseconds */
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
# define OFFSET_7 0 /* size 1 */
# define OFFSET_b 1 /* size 8 */
# define OFFSET_d 9 /* size 4 */
# define OFFSET_e 13 /* size 1 */
# define OFFSET_f 14 /* size 1 */
# define TOTAL 15
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
# define SIZE_7 1
# define SIZE_b 8
# define SIZE_d 4
# define SIZE_e 1
# define SIZE_f 1
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
# define MIN_POLL_DELAY 5000 /* milliseconds */
# define MAX_POLL_DELAY 60000 /* milliseconds */
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
# define RESUBMIT_DELAY 1000 /* milliseconds */
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
# define DEFAULT_ALTSETTING 1
# define DEFAULT_DL_512_FIRST 0
# define DEFAULT_SW_BUFFERING 0
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
static int altsetting = DEFAULT_ALTSETTING ;
static int dl_512_first = DEFAULT_DL_512_FIRST ;
static int sw_buffering = DEFAULT_SW_BUFFERING ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
module_param ( altsetting , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( altsetting ,
" Alternative setting for data interface (default: "
__MODULE_STRING ( DEFAULT_ALTSETTING ) " ) " ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
module_param ( dl_512_first , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( dl_512_first ,
" Read 512 bytes before sending firmware (default: "
__MODULE_STRING ( DEFAULT_DL_512_FIRST ) " ) " ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
module_param ( sw_buffering , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( sw_buffering ,
" Enable software buffering (default: "
__MODULE_STRING ( DEFAULT_SW_BUFFERING ) " ) " ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
# define ENDPOINT_INT 0x81
# define ENDPOINT_DATA 0x07
# define ENDPOINT_FIRMWARE 0x05
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
# define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
2005-04-16 15:20:36 -07:00
struct speedtch_instance_data {
2005-05-11 20:20:40 +02:00
struct usbatm_data * usbatm ;
struct work_struct status_checker ;
2005-04-16 15:20:36 -07:00
2005-06-23 09:37:56 +02:00
unsigned char last_status ;
2005-05-11 20:20:40 +02:00
int poll_delay ; /* milliseconds */
struct timer_list resubmit_timer ;
2005-04-16 15:20:36 -07:00
struct urb * int_urb ;
unsigned char int_data [ 16 ] ;
2005-05-11 20:20:40 +02:00
unsigned char scratch_buffer [ TOTAL ] ;
2005-04-16 15:20:36 -07:00
} ;
/***************
* * firmware * *
* * * * * * * * * * * * * * */
2005-05-11 20:20:40 +02:00
static void speedtch_set_swbuff ( struct speedtch_instance_data * instance , int state )
2005-04-16 15:20:36 -07:00
{
2005-05-11 20:20:40 +02:00
struct usbatm_data * usbatm = instance - > usbatm ;
struct usb_device * usb_dev = usbatm - > usb_dev ;
2005-04-16 15:20:36 -07:00
int ret ;
2005-05-11 20:20:40 +02:00
ret = usb_control_msg ( usb_dev , usb_sndctrlpipe ( usb_dev , 0 ) ,
0x32 , 0x40 , state ? 0x01 : 0x00 , 0x00 , NULL , 0 , CTRL_TIMEOUT ) ;
if ( ret < 0 )
usb_warn ( usbatm ,
" %sabling SW buffering: usb_control_msg returned %d \n " ,
state ? " En " : " Dis " , ret ) ;
else
dbg ( " speedtch_set_swbuff: %sbled SW buffering " , state ? " En " : " Dis " ) ;
2005-04-16 15:20:36 -07:00
}
static void speedtch_test_sequence ( struct speedtch_instance_data * instance )
{
2005-05-11 20:20:40 +02:00
struct usbatm_data * usbatm = instance - > usbatm ;
struct usb_device * usb_dev = usbatm - > usb_dev ;
unsigned char * buf = instance - > scratch_buffer ;
2005-04-16 15:20:36 -07:00
int ret ;
/* URB 147 */
buf [ 0 ] = 0x1c ;
buf [ 1 ] = 0x50 ;
2005-05-11 20:20:40 +02:00
ret = usb_control_msg ( usb_dev , usb_sndctrlpipe ( usb_dev , 0 ) ,
0x01 , 0x40 , 0x0b , 0x00 , buf , 2 , CTRL_TIMEOUT ) ;
2005-04-16 15:20:36 -07:00
if ( ret < 0 )
2005-05-11 20:20:40 +02:00
usb_warn ( usbatm , " %s failed on URB147: %d \n " , __func__ , ret ) ;
2005-04-16 15:20:36 -07:00
/* URB 148 */
buf [ 0 ] = 0x32 ;
buf [ 1 ] = 0x00 ;
2005-05-11 20:20:40 +02:00
ret = usb_control_msg ( usb_dev , usb_sndctrlpipe ( usb_dev , 0 ) ,
0x01 , 0x40 , 0x02 , 0x00 , buf , 2 , CTRL_TIMEOUT ) ;
2005-04-16 15:20:36 -07:00
if ( ret < 0 )
2005-05-11 20:20:40 +02:00
usb_warn ( usbatm , " %s failed on URB148: %d \n " , __func__ , ret ) ;
2005-04-16 15:20:36 -07:00
/* URB 149 */
buf [ 0 ] = 0x01 ;
buf [ 1 ] = 0x00 ;
buf [ 2 ] = 0x01 ;
2005-05-11 20:20:40 +02:00
ret = usb_control_msg ( usb_dev , usb_sndctrlpipe ( usb_dev , 0 ) ,
0x01 , 0x40 , 0x03 , 0x00 , buf , 3 , CTRL_TIMEOUT ) ;
2005-04-16 15:20:36 -07:00
if ( ret < 0 )
2005-05-11 20:20:40 +02:00
usb_warn ( usbatm , " %s failed on URB149: %d \n " , __func__ , ret ) ;
2005-04-16 15:20:36 -07:00
/* URB 150 */
buf [ 0 ] = 0x01 ;
buf [ 1 ] = 0x00 ;
buf [ 2 ] = 0x01 ;
2005-05-11 20:20:40 +02:00
ret = usb_control_msg ( usb_dev , usb_sndctrlpipe ( usb_dev , 0 ) ,
0x01 , 0x40 , 0x04 , 0x00 , buf , 3 , CTRL_TIMEOUT ) ;
2005-04-16 15:20:36 -07:00
if ( ret < 0 )
2005-05-11 20:20:40 +02:00
usb_warn ( usbatm , " %s failed on URB150: %d \n " , __func__ , ret ) ;
2005-04-16 15:20:36 -07:00
}
2005-05-11 20:20:40 +02:00
static int speedtch_upload_firmware ( struct speedtch_instance_data * instance ,
const struct firmware * fw1 ,
const struct firmware * fw2 )
2005-04-16 15:20:36 -07:00
{
2005-05-11 20:20:40 +02:00
unsigned char * buffer ;
struct usbatm_data * usbatm = instance - > usbatm ;
struct usb_interface * intf ;
struct usb_device * usb_dev = usbatm - > usb_dev ;
int actual_length ;
int ret = 0 ;
int offset ;
usb_dbg ( usbatm , " %s entered \n " , __func__ ) ;
if ( ! ( buffer = ( unsigned char * ) __get_free_page ( GFP_KERNEL ) ) ) {
ret = - ENOMEM ;
usb_dbg ( usbatm , " %s: no memory for buffer! \n " , __func__ ) ;
goto out ;
}
if ( ! ( intf = usb_ifnum_to_if ( usb_dev , 2 ) ) ) {
ret = - ENODEV ;
usb_dbg ( usbatm , " %s: interface not found! \n " , __func__ ) ;
goto out_free ;
}
/* URB 7 */
if ( dl_512_first ) { /* some modems need a read before writing the firmware */
ret = usb_bulk_msg ( usb_dev , usb_rcvbulkpipe ( usb_dev , ENDPOINT_FIRMWARE ) ,
buffer , 0x200 , & actual_length , 2000 ) ;
if ( ret < 0 & & ret ! = - ETIMEDOUT )
2006-01-17 11:15:13 +01:00
usb_warn ( usbatm , " %s: read BLOCK0 from modem failed (%d)! \n " , __func__ , ret ) ;
2005-05-11 20:20:40 +02:00
else
usb_dbg ( usbatm , " %s: BLOCK0 downloaded (%d bytes) \n " , __func__ , ret ) ;
}
/* URB 8 : both leds are static green */
for ( offset = 0 ; offset < fw1 - > size ; offset + = PAGE_SIZE ) {
int thislen = min_t ( int , PAGE_SIZE , fw1 - > size - offset ) ;
memcpy ( buffer , fw1 - > data + offset , thislen ) ;
ret = usb_bulk_msg ( usb_dev , usb_sndbulkpipe ( usb_dev , ENDPOINT_FIRMWARE ) ,
buffer , thislen , & actual_length , DATA_TIMEOUT ) ;
if ( ret < 0 ) {
2006-01-17 11:15:13 +01:00
usb_err ( usbatm , " %s: write BLOCK1 to modem failed (%d)! \n " , __func__ , ret ) ;
2005-05-11 20:20:40 +02:00
goto out_free ;
}
usb_dbg ( usbatm , " %s: BLOCK1 uploaded (%zu bytes) \n " , __func__ , fw1 - > size ) ;
}
/* USB led blinking green, ADSL led off */
/* URB 11 */
ret = usb_bulk_msg ( usb_dev , usb_rcvbulkpipe ( usb_dev , ENDPOINT_FIRMWARE ) ,
buffer , 0x200 , & actual_length , DATA_TIMEOUT ) ;
2005-04-16 15:20:36 -07:00
if ( ret < 0 ) {
2006-01-17 11:15:13 +01:00
usb_err ( usbatm , " %s: read BLOCK2 from modem failed (%d)! \n " , __func__ , ret ) ;
2005-05-11 20:20:40 +02:00
goto out_free ;
2005-04-16 15:20:36 -07:00
}
2005-05-11 20:20:40 +02:00
usb_dbg ( usbatm , " %s: BLOCK2 downloaded (%d bytes) \n " , __func__ , actual_length ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
/* URBs 12 to 139 - USB led blinking green, ADSL led off */
for ( offset = 0 ; offset < fw2 - > size ; offset + = PAGE_SIZE ) {
int thislen = min_t ( int , PAGE_SIZE , fw2 - > size - offset ) ;
memcpy ( buffer , fw2 - > data + offset , thislen ) ;
ret = usb_bulk_msg ( usb_dev , usb_sndbulkpipe ( usb_dev , ENDPOINT_FIRMWARE ) ,
buffer , thislen , & actual_length , DATA_TIMEOUT ) ;
if ( ret < 0 ) {
2006-01-17 11:15:13 +01:00
usb_err ( usbatm , " %s: write BLOCK3 to modem failed (%d)! \n " , __func__ , ret ) ;
2005-05-11 20:20:40 +02:00
goto out_free ;
}
}
usb_dbg ( usbatm , " %s: BLOCK3 uploaded (%zu bytes) \n " , __func__ , fw2 - > size ) ;
/* USB led static green, ADSL led static red */
/* URB 142 */
ret = usb_bulk_msg ( usb_dev , usb_rcvbulkpipe ( usb_dev , ENDPOINT_FIRMWARE ) ,
buffer , 0x200 , & actual_length , DATA_TIMEOUT ) ;
if ( ret < 0 ) {
2006-01-17 11:15:13 +01:00
usb_err ( usbatm , " %s: read BLOCK4 from modem failed (%d)! \n " , __func__ , ret ) ;
2005-05-11 20:20:40 +02:00
goto out_free ;
}
/* success */
usb_dbg ( usbatm , " %s: BLOCK4 downloaded (%d bytes) \n " , __func__ , actual_length ) ;
/* Delay to allow firmware to start up. We can do this here
because we ' re in our own kernel thread anyway . */
msleep_interruptible ( 1000 ) ;
/* Enable software buffering, if requested */
if ( sw_buffering )
speedtch_set_swbuff ( instance , 1 ) ;
/* Magic spell; don't ask us what this does */
speedtch_test_sequence ( instance ) ;
ret = 0 ;
out_free :
free_page ( ( unsigned long ) buffer ) ;
out :
return ret ;
2005-04-16 15:20:36 -07:00
}
2006-01-17 11:15:13 +01:00
static int speedtch_find_firmware ( struct usbatm_data * usbatm , struct usb_interface * intf ,
int phase , const struct firmware * * fw_p )
2005-04-16 15:20:36 -07:00
{
2005-05-11 20:20:40 +02:00
struct device * dev = & intf - > dev ;
const u16 bcdDevice = le16_to_cpu ( interface_to_usbdev ( intf ) - > descriptor . bcdDevice ) ;
const u8 major_revision = bcdDevice > > 8 ;
const u8 minor_revision = bcdDevice & 0xff ;
char buf [ 24 ] ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
sprintf ( buf , " speedtch-%d.bin.%x.%02x " , phase , major_revision , minor_revision ) ;
2006-01-17 11:15:13 +01:00
usb_dbg ( usbatm , " %s: looking for %s \n " , __func__ , buf ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
if ( request_firmware ( fw_p , buf , dev ) ) {
sprintf ( buf , " speedtch-%d.bin.%x " , phase , major_revision ) ;
2006-01-17 11:15:13 +01:00
usb_dbg ( usbatm , " %s: looking for %s \n " , __func__ , buf ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
if ( request_firmware ( fw_p , buf , dev ) ) {
sprintf ( buf , " speedtch-%d.bin " , phase ) ;
2006-01-17 11:15:13 +01:00
usb_dbg ( usbatm , " %s: looking for %s \n " , __func__ , buf ) ;
2005-05-11 20:20:40 +02:00
if ( request_firmware ( fw_p , buf , dev ) ) {
2006-01-17 11:15:13 +01:00
usb_err ( usbatm , " %s: no stage %d firmware found! \n " , __func__ , phase ) ;
2005-05-11 20:20:40 +02:00
return - ENOENT ;
}
}
2005-04-16 15:20:36 -07:00
}
2006-01-17 11:15:13 +01:00
usb_info ( usbatm , " found stage %d firmware %s \n " , phase , buf ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
return 0 ;
}
static int speedtch_heavy_init ( struct usbatm_data * usbatm , struct usb_interface * intf )
{
const struct firmware * fw1 , * fw2 ;
struct speedtch_instance_data * instance = usbatm - > driver_data ;
int ret ;
2006-01-17 11:15:13 +01:00
if ( ( ret = speedtch_find_firmware ( usbatm , intf , 1 , & fw1 ) ) < 0 )
return ret ;
2005-05-11 20:20:40 +02:00
2006-01-17 11:15:13 +01:00
if ( ( ret = speedtch_find_firmware ( usbatm , intf , 2 , & fw2 ) ) < 0 ) {
2005-05-11 20:20:40 +02:00
release_firmware ( fw1 ) ;
return ret ;
2005-04-16 15:20:36 -07:00
}
2006-01-17 11:15:13 +01:00
if ( ( ret = speedtch_upload_firmware ( instance , fw1 , fw2 ) ) < 0 )
usb_err ( usbatm , " %s: firmware upload failed (%d)! \n " , __func__ , ret ) ;
2005-05-11 20:20:40 +02:00
release_firmware ( fw2 ) ;
release_firmware ( fw1 ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
return ret ;
2005-04-16 15:20:36 -07:00
}
2005-05-11 20:20:40 +02:00
/**********
* * ATM * *
* * * * * * * * * */
static int speedtch_read_status ( struct speedtch_instance_data * instance )
2005-04-16 15:20:36 -07:00
{
2005-05-11 20:20:40 +02:00
struct usbatm_data * usbatm = instance - > usbatm ;
struct usb_device * usb_dev = usbatm - > usb_dev ;
unsigned char * buf = instance - > scratch_buffer ;
2005-04-16 15:20:36 -07:00
int ret ;
memset ( buf , 0 , TOTAL ) ;
2005-05-11 20:20:40 +02:00
ret = usb_control_msg ( usb_dev , usb_rcvctrlpipe ( usb_dev , 0 ) ,
2005-04-16 15:20:36 -07:00
0x12 , 0xc0 , 0x07 , 0x00 , buf + OFFSET_7 , SIZE_7 ,
CTRL_TIMEOUT ) ;
if ( ret < 0 ) {
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s: MSG 7 failed \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
return ret ;
}
2005-05-11 20:20:40 +02:00
ret = usb_control_msg ( usb_dev , usb_rcvctrlpipe ( usb_dev , 0 ) ,
2005-04-16 15:20:36 -07:00
0x12 , 0xc0 , 0x0b , 0x00 , buf + OFFSET_b , SIZE_b ,
CTRL_TIMEOUT ) ;
if ( ret < 0 ) {
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s: MSG B failed \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
return ret ;
}
2005-05-11 20:20:40 +02:00
ret = usb_control_msg ( usb_dev , usb_rcvctrlpipe ( usb_dev , 0 ) ,
2005-04-16 15:20:36 -07:00
0x12 , 0xc0 , 0x0d , 0x00 , buf + OFFSET_d , SIZE_d ,
CTRL_TIMEOUT ) ;
if ( ret < 0 ) {
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s: MSG D failed \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
return ret ;
}
2005-05-11 20:20:40 +02:00
ret = usb_control_msg ( usb_dev , usb_rcvctrlpipe ( usb_dev , 0 ) ,
2005-04-16 15:20:36 -07:00
0x01 , 0xc0 , 0x0e , 0x00 , buf + OFFSET_e , SIZE_e ,
CTRL_TIMEOUT ) ;
if ( ret < 0 ) {
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s: MSG E failed \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
return ret ;
}
2005-05-11 20:20:40 +02:00
ret = usb_control_msg ( usb_dev , usb_rcvctrlpipe ( usb_dev , 0 ) ,
2005-04-16 15:20:36 -07:00
0x01 , 0xc0 , 0x0f , 0x00 , buf + OFFSET_f , SIZE_f ,
CTRL_TIMEOUT ) ;
if ( ret < 0 ) {
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s: MSG F failed \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
return ret ;
}
return 0 ;
}
2005-05-11 20:20:40 +02:00
static int speedtch_start_synchro ( struct speedtch_instance_data * instance )
2005-04-16 15:20:36 -07:00
{
2005-05-11 20:20:40 +02:00
struct usbatm_data * usbatm = instance - > usbatm ;
struct usb_device * usb_dev = usbatm - > usb_dev ;
unsigned char * buf = instance - > scratch_buffer ;
2005-04-16 15:20:36 -07:00
int ret ;
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s entered \n " , __func__ ) ;
memset ( buf , 0 , 2 ) ;
ret = usb_control_msg ( usb_dev , usb_rcvctrlpipe ( usb_dev , 0 ) ,
0x12 , 0xc0 , 0x04 , 0x00 ,
buf , 2 , CTRL_TIMEOUT ) ;
if ( ret < 0 )
atm_warn ( usbatm , " failed to start ADSL synchronisation: %d \n " , ret ) ;
else
atm_dbg ( usbatm , " %s: modem prodded. %d bytes returned: %02x %02x \n " ,
__func__ , ret , buf [ 0 ] , buf [ 1 ] ) ;
return ret ;
}
static void speedtch_check_status ( struct speedtch_instance_data * instance )
{
struct usbatm_data * usbatm = instance - > usbatm ;
struct atm_dev * atm_dev = usbatm - > atm_dev ;
unsigned char * buf = instance - > scratch_buffer ;
2005-06-23 09:37:56 +02:00
int down_speed , up_speed , ret ;
unsigned char status ;
2005-05-11 20:20:40 +02:00
2006-01-17 11:15:13 +01:00
# ifdef VERBOSE_DEBUG
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s entered \n " , __func__ ) ;
2006-01-17 11:15:13 +01:00
# endif
2005-05-11 20:20:40 +02:00
ret = speedtch_read_status ( instance ) ;
if ( ret < 0 ) {
atm_warn ( usbatm , " error %d fetching device status \n " , ret ) ;
2005-06-23 09:23:10 +02:00
instance - > poll_delay = min ( 2 * instance - > poll_delay , MAX_POLL_DELAY ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2005-06-23 09:23:10 +02:00
instance - > poll_delay = max ( instance - > poll_delay / 2 , MIN_POLL_DELAY ) ;
2005-05-11 20:20:40 +02:00
2005-06-23 09:37:56 +02:00
status = buf [ OFFSET_7 ] ;
2005-04-16 15:20:36 -07:00
2005-06-23 09:37:56 +02:00
if ( ( status ! = instance - > last_status ) | | ! status ) {
2006-01-17 11:15:13 +01:00
atm_dbg ( usbatm , " %s: line state 0x%02x \n " , __func__ , status ) ;
2005-06-23 09:37:56 +02:00
switch ( status ) {
case 0 :
2005-05-11 20:20:40 +02:00
atm_dev - > signal = ATM_PHY_SIG_LOST ;
2005-06-23 09:37:56 +02:00
if ( instance - > last_status )
2005-07-25 19:54:35 -07:00
atm_info ( usbatm , " ADSL line is down \n " ) ;
2005-06-23 09:37:56 +02:00
/* It may never resync again unless we ask it to... */
2005-05-11 20:20:40 +02:00
ret = speedtch_start_synchro ( instance ) ;
2005-06-23 09:37:56 +02:00
break ;
2005-04-16 15:20:36 -07:00
2005-06-23 09:37:56 +02:00
case 0x08 :
2005-05-11 20:20:40 +02:00
atm_dev - > signal = ATM_PHY_SIG_UNKNOWN ;
2005-07-25 19:54:35 -07:00
atm_info ( usbatm , " ADSL line is blocked? \n " ) ;
2005-06-23 09:37:56 +02:00
break ;
2005-04-16 15:20:36 -07:00
2005-06-23 09:37:56 +02:00
case 0x10 :
2005-05-11 20:20:40 +02:00
atm_dev - > signal = ATM_PHY_SIG_LOST ;
2005-07-25 19:54:35 -07:00
atm_info ( usbatm , " ADSL line is synchronising \n " ) ;
2005-06-23 09:37:56 +02:00
break ;
2005-04-16 15:20:36 -07:00
2005-06-23 09:37:56 +02:00
case 0x20 :
down_speed = buf [ OFFSET_b ] | ( buf [ OFFSET_b + 1 ] < < 8 )
2005-04-16 15:20:36 -07:00
| ( buf [ OFFSET_b + 2 ] < < 16 ) | ( buf [ OFFSET_b + 3 ] < < 24 ) ;
2005-06-23 09:37:56 +02:00
up_speed = buf [ OFFSET_b + 4 ] | ( buf [ OFFSET_b + 5 ] < < 8 )
2005-04-16 15:20:36 -07:00
| ( buf [ OFFSET_b + 6 ] < < 16 ) | ( buf [ OFFSET_b + 7 ] < < 24 ) ;
2005-05-11 20:20:40 +02:00
if ( ! ( down_speed & 0x0000ffff ) & & ! ( up_speed & 0x0000ffff ) ) {
2005-04-16 15:20:36 -07:00
down_speed > > = 16 ;
up_speed > > = 16 ;
}
2005-05-11 20:20:40 +02:00
atm_dev - > link_rate = down_speed * 1000 / 424 ;
atm_dev - > signal = ATM_PHY_SIG_FOUND ;
atm_info ( usbatm ,
2005-06-23 09:20:50 +02:00
" ADSL line is up (%d kb/s down | %d kb/s up) \n " ,
2005-05-11 20:20:40 +02:00
down_speed , up_speed ) ;
2005-06-23 09:37:56 +02:00
break ;
2005-04-16 15:20:36 -07:00
2005-06-23 09:37:56 +02:00
default :
2005-05-11 20:20:40 +02:00
atm_dev - > signal = ATM_PHY_SIG_UNKNOWN ;
2006-01-17 11:15:13 +01:00
atm_info ( usbatm , " unknown line state %02x \n " , status ) ;
2005-06-23 09:37:56 +02:00
break ;
2005-04-16 15:20:36 -07:00
}
2005-06-23 09:37:56 +02:00
instance - > last_status = status ;
2005-04-16 15:20:36 -07:00
}
}
2005-05-11 20:20:40 +02:00
static void speedtch_status_poll ( unsigned long data )
2005-04-16 15:20:36 -07:00
{
struct speedtch_instance_data * instance = ( void * ) data ;
2005-05-11 20:20:40 +02:00
schedule_work ( & instance - > status_checker ) ;
/* The following check is racy, but the race is harmless */
if ( instance - > poll_delay < MAX_POLL_DELAY )
mod_timer ( & instance - > status_checker . timer , jiffies + msecs_to_jiffies ( instance - > poll_delay ) ) ;
else
2005-07-25 19:54:35 -07:00
atm_warn ( instance - > usbatm , " Too many failures - disabling line status polling \n " ) ;
2005-04-16 15:20:36 -07:00
}
2005-05-11 20:20:40 +02:00
static void speedtch_resubmit_int ( unsigned long data )
2005-04-16 15:20:36 -07:00
{
2005-05-11 20:20:40 +02:00
struct speedtch_instance_data * instance = ( void * ) data ;
struct urb * int_urb = instance - > int_urb ;
int ret ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
atm_dbg ( instance - > usbatm , " %s entered \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
if ( int_urb ) {
ret = usb_submit_urb ( int_urb , GFP_ATOMIC ) ;
if ( ! ret )
schedule_work ( & instance - > status_checker ) ;
else {
atm_dbg ( instance - > usbatm , " %s: usb_submit_urb failed with result %d \n " , __func__ , ret ) ;
mod_timer ( & instance - > resubmit_timer , jiffies + msecs_to_jiffies ( RESUBMIT_DELAY ) ) ;
2005-04-16 15:20:36 -07:00
}
}
2005-05-11 20:20:40 +02:00
}
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
static void speedtch_handle_int ( struct urb * int_urb , struct pt_regs * regs )
{
struct speedtch_instance_data * instance = int_urb - > context ;
struct usbatm_data * usbatm = instance - > usbatm ;
unsigned int count = int_urb - > actual_length ;
int ret = int_urb - > status ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
/* The magic interrupt for "up state" */
2006-01-09 20:54:01 -08:00
static const unsigned char up_int [ 6 ] = { 0xa1 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 } ;
2005-05-11 20:20:40 +02:00
/* The magic interrupt for "down state" */
2006-01-09 20:54:01 -08:00
static const unsigned char down_int [ 6 ] = { 0xa1 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s entered \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
if ( ret < 0 ) {
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s: nonzero urb status %d! \n " , __func__ , ret ) ;
goto fail ;
2005-04-16 15:20:36 -07:00
}
2005-05-11 20:20:40 +02:00
if ( ( count = = 6 ) & & ! memcmp ( up_int , instance - > int_data , 6 ) ) {
del_timer ( & instance - > status_checker . timer ) ;
2005-07-25 19:54:35 -07:00
atm_info ( usbatm , " DSL line goes up \n " ) ;
2005-05-11 20:20:40 +02:00
} else if ( ( count = = 6 ) & & ! memcmp ( down_int , instance - > int_data , 6 ) ) {
2005-07-25 19:54:35 -07:00
atm_info ( usbatm , " DSL line goes down \n " ) ;
2005-05-11 20:20:40 +02:00
} else {
int i ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s: unknown interrupt packet of length %d: " , __func__ , count ) ;
for ( i = 0 ; i < count ; i + + )
printk ( " %02x " , instance - > int_data [ i ] ) ;
printk ( " \n " ) ;
goto fail ;
}
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
if ( ( int_urb = instance - > int_urb ) ) {
ret = usb_submit_urb ( int_urb , GFP_ATOMIC ) ;
schedule_work ( & instance - > status_checker ) ;
2005-04-16 15:20:36 -07:00
if ( ret < 0 ) {
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s: usb_submit_urb failed with result %d \n " , __func__ , ret ) ;
goto fail ;
2005-04-16 15:20:36 -07:00
}
}
return ;
2005-05-11 20:20:40 +02:00
fail :
if ( ( int_urb = instance - > int_urb ) )
mod_timer ( & instance - > resubmit_timer , jiffies + msecs_to_jiffies ( RESUBMIT_DELAY ) ) ;
2005-04-16 15:20:36 -07:00
}
2005-05-11 20:20:40 +02:00
static int speedtch_atm_start ( struct usbatm_data * usbatm , struct atm_dev * atm_dev )
2005-04-16 15:20:36 -07:00
{
2005-05-11 20:20:40 +02:00
struct usb_device * usb_dev = usbatm - > usb_dev ;
struct speedtch_instance_data * instance = usbatm - > driver_data ;
int i , ret ;
unsigned char mac_str [ 13 ] ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
atm_dbg ( usbatm , " %s entered \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
if ( ( ret = usb_set_interface ( usb_dev , 1 , altsetting ) ) < 0 ) {
atm_dbg ( usbatm , " %s: usb_set_interface returned %d! \n " , __func__ , ret ) ;
return ret ;
2005-04-16 15:20:36 -07:00
}
2005-05-11 20:20:40 +02:00
/* Set MAC address, it is stored in the serial number */
memset ( atm_dev - > esi , 0 , sizeof ( atm_dev - > esi ) ) ;
if ( usb_string ( usb_dev , usb_dev - > descriptor . iSerialNumber , mac_str , sizeof ( mac_str ) ) = = 12 ) {
for ( i = 0 ; i < 6 ; i + + )
atm_dev - > esi [ i ] = ( hex2int ( mac_str [ i * 2 ] ) * 16 ) + ( hex2int ( mac_str [ i * 2 + 1 ] ) ) ;
}
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
/* Start modem synchronisation */
ret = speedtch_start_synchro ( instance ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
/* Set up interrupt endpoint */
if ( instance - > int_urb ) {
ret = usb_submit_urb ( instance - > int_urb , GFP_KERNEL ) ;
if ( ret < 0 ) {
/* Doesn't matter; we'll poll anyway */
atm_dbg ( usbatm , " %s: submission of interrupt URB failed (%d)! \n " , __func__ , ret ) ;
usb_free_urb ( instance - > int_urb ) ;
instance - > int_urb = NULL ;
2005-04-16 15:20:36 -07:00
}
}
2005-05-11 20:20:40 +02:00
/* Start status polling */
mod_timer ( & instance - > status_checker . timer , jiffies + msecs_to_jiffies ( 1000 ) ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-05-11 20:20:40 +02:00
static void speedtch_atm_stop ( struct usbatm_data * usbatm , struct atm_dev * atm_dev )
2005-04-16 15:20:36 -07:00
{
2005-05-11 20:20:40 +02:00
struct speedtch_instance_data * instance = usbatm - > driver_data ;
struct urb * int_urb = instance - > int_urb ;
atm_dbg ( usbatm , " %s entered \n " , __func__ ) ;
del_timer_sync ( & instance - > status_checker . timer ) ;
/*
* Since resubmit_timer and int_urb can schedule themselves and
* each other , shutting them down correctly takes some care
*/
instance - > int_urb = NULL ; /* signal shutdown */
mb ( ) ;
usb_kill_urb ( int_urb ) ;
del_timer_sync ( & instance - > resubmit_timer ) ;
/*
* At this point , speedtch_handle_int and speedtch_resubmit_int
* can run or be running , but instance - > int_urb = = NULL means that
* they will not reschedule
*/
usb_kill_urb ( int_urb ) ;
del_timer_sync ( & instance - > resubmit_timer ) ;
usb_free_urb ( int_urb ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
flush_scheduled_work ( ) ;
}
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
/**********
* * USB * *
* * * * * * * * * */
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
static struct usb_device_id speedtch_usb_ids [ ] = {
{ USB_DEVICE ( 0x06b9 , 0x4061 ) } ,
{ }
} ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
MODULE_DEVICE_TABLE ( usb , speedtch_usb_ids ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
static int speedtch_usb_probe ( struct usb_interface * , const struct usb_device_id * ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
static struct usb_driver speedtch_usb_driver = {
. name = speedtch_driver_name ,
. probe = speedtch_usb_probe ,
. disconnect = usbatm_usb_disconnect ,
. id_table = speedtch_usb_ids
} ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
static void speedtch_release_interfaces ( struct usb_device * usb_dev , int num_interfaces ) {
struct usb_interface * cur_intf ;
int i ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
for ( i = 0 ; i < num_interfaces ; i + + )
if ( ( cur_intf = usb_ifnum_to_if ( usb_dev , i ) ) ) {
usb_set_intfdata ( cur_intf , NULL ) ;
usb_driver_release_interface ( & speedtch_usb_driver , cur_intf ) ;
}
2005-04-16 15:20:36 -07:00
}
2005-05-11 20:20:40 +02:00
static int speedtch_bind ( struct usbatm_data * usbatm ,
struct usb_interface * intf ,
2006-01-17 11:16:13 +01:00
const struct usb_device_id * id )
2005-04-16 15:20:36 -07:00
{
2005-05-11 20:20:40 +02:00
struct usb_device * usb_dev = interface_to_usbdev ( intf ) ;
struct usb_interface * cur_intf ;
struct speedtch_instance_data * instance ;
int ifnum = intf - > altsetting - > desc . bInterfaceNumber ;
int num_interfaces = usb_dev - > actconfig - > desc . bNumInterfaces ;
int i , ret ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
usb_dbg ( usbatm , " %s entered \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
2006-01-17 11:15:13 +01:00
/* sanity checks */
2005-05-11 20:20:40 +02:00
if ( usb_dev - > descriptor . bDeviceClass ! = USB_CLASS_VENDOR_SPEC ) {
2006-01-17 11:15:13 +01:00
usb_err ( usbatm , " %s: wrong device class %d \n " , __func__ , usb_dev - > descriptor . bDeviceClass ) ;
2005-04-16 15:20:36 -07:00
return - ENODEV ;
}
2005-05-11 20:20:40 +02:00
/* claim all interfaces */
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
for ( i = 0 ; i < num_interfaces ; i + + ) {
cur_intf = usb_ifnum_to_if ( usb_dev , i ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
if ( ( i ! = ifnum ) & & cur_intf ) {
ret = usb_driver_claim_interface ( & speedtch_usb_driver , cur_intf , usbatm ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
if ( ret < 0 ) {
2006-01-17 11:15:13 +01:00
usb_err ( usbatm , " %s: failed to claim interface %2d (%d)! \n " , __func__ , i , ret ) ;
2005-05-11 20:20:40 +02:00
speedtch_release_interfaces ( usb_dev , i ) ;
return ret ;
}
}
}
2005-04-16 15:20:36 -07:00
2006-01-13 09:38:22 +01:00
instance = kzalloc ( sizeof ( * instance ) , GFP_KERNEL ) ;
2005-05-11 20:20:40 +02:00
2005-04-16 15:20:36 -07:00
if ( ! instance ) {
2006-01-17 11:15:13 +01:00
usb_err ( usbatm , " %s: no memory for instance data! \n " , __func__ ) ;
2005-05-11 20:20:40 +02:00
ret = - ENOMEM ;
goto fail_release ;
2005-04-16 15:20:36 -07:00
}
2005-05-11 20:20:40 +02:00
instance - > usbatm = usbatm ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
INIT_WORK ( & instance - > status_checker , ( void * ) speedtch_check_status , instance ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
instance - > status_checker . timer . function = speedtch_status_poll ;
instance - > status_checker . timer . data = ( unsigned long ) instance ;
2005-06-23 09:37:56 +02:00
instance - > last_status = 0xff ;
2005-05-11 20:20:40 +02:00
instance - > poll_delay = MIN_POLL_DELAY ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
init_timer ( & instance - > resubmit_timer ) ;
instance - > resubmit_timer . function = speedtch_resubmit_int ;
instance - > resubmit_timer . data = ( unsigned long ) instance ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
instance - > int_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
if ( instance - > int_urb )
usb_fill_int_urb ( instance - > int_urb , usb_dev ,
usb_rcvintpipe ( usb_dev , ENDPOINT_INT ) ,
instance - > int_data , sizeof ( instance - > int_data ) ,
speedtch_handle_int , instance , 50 ) ;
else
usb_dbg ( usbatm , " %s: no memory for interrupt urb! \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
/* check whether the modem already seems to be alive */
ret = usb_control_msg ( usb_dev , usb_rcvctrlpipe ( usb_dev , 0 ) ,
0x12 , 0xc0 , 0x07 , 0x00 ,
instance - > scratch_buffer + OFFSET_7 , SIZE_7 , 500 ) ;
2005-04-16 15:20:36 -07:00
2006-01-17 11:16:13 +01:00
usbatm - > flags = ( ret = = SIZE_7 ? UDSL_SKIP_HEAVY_INIT : 0 ) ;
2005-04-16 15:20:36 -07:00
2006-01-17 11:16:13 +01:00
usb_dbg ( usbatm , " %s: firmware %s loaded \n " , __func__ , usbatm - > flags & UDSL_SKIP_HEAVY_INIT ? " already " : " not " ) ;
2005-05-11 20:20:40 +02:00
2006-01-17 11:16:13 +01:00
if ( ! ( usbatm - > flags & UDSL_SKIP_HEAVY_INIT ) )
2006-01-17 11:15:13 +01:00
if ( ( ret = usb_reset_device ( usb_dev ) ) < 0 ) {
usb_err ( usbatm , " %s: device reset failed (%d)! \n " , __func__ , ret ) ;
2005-05-11 20:20:40 +02:00
goto fail_free ;
2006-01-17 11:15:13 +01:00
}
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
usbatm - > driver_data = instance ;
2005-04-16 15:20:36 -07:00
return 0 ;
2005-05-11 20:20:40 +02:00
fail_free :
usb_free_urb ( instance - > int_urb ) ;
2005-04-16 15:20:36 -07:00
kfree ( instance ) ;
2005-05-11 20:20:40 +02:00
fail_release :
speedtch_release_interfaces ( usb_dev , num_interfaces ) ;
return ret ;
2005-04-16 15:20:36 -07:00
}
2005-05-11 20:20:40 +02:00
static void speedtch_unbind ( struct usbatm_data * usbatm , struct usb_interface * intf )
2005-04-16 15:20:36 -07:00
{
2005-05-11 20:20:40 +02:00
struct usb_device * usb_dev = interface_to_usbdev ( intf ) ;
struct speedtch_instance_data * instance = usbatm - > driver_data ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
usb_dbg ( usbatm , " %s entered \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
2005-05-11 20:20:40 +02:00
speedtch_release_interfaces ( usb_dev , usb_dev - > actconfig - > desc . bNumInterfaces ) ;
usb_free_urb ( instance - > int_urb ) ;
kfree ( instance ) ;
2005-04-16 15:20:36 -07:00
}
2005-05-11 20:20:40 +02:00
2005-04-16 15:20:36 -07:00
/***********
* * init * *
* * * * * * * * * * */
2005-05-11 20:20:40 +02:00
static struct usbatm_driver speedtch_usbatm_driver = {
. driver_name = speedtch_driver_name ,
. bind = speedtch_bind ,
. heavy_init = speedtch_heavy_init ,
. unbind = speedtch_unbind ,
. atm_start = speedtch_atm_start ,
. atm_stop = speedtch_atm_stop ,
. in = ENDPOINT_DATA ,
. out = ENDPOINT_DATA
} ;
static int speedtch_usb_probe ( struct usb_interface * intf , const struct usb_device_id * id )
{
return usbatm_usb_probe ( intf , id , & speedtch_usbatm_driver ) ;
}
2005-04-16 15:20:36 -07:00
static int __init speedtch_usb_init ( void )
{
2005-05-11 20:20:40 +02:00
dbg ( " %s: driver version %s " , __func__ , DRIVER_VERSION ) ;
2005-04-16 15:20:36 -07:00
return usb_register ( & speedtch_usb_driver ) ;
}
static void __exit speedtch_usb_cleanup ( void )
{
2005-05-11 20:20:40 +02:00
dbg ( " %s " , __func__ ) ;
2005-04-16 15:20:36 -07:00
usb_deregister ( & speedtch_usb_driver ) ;
}
module_init ( speedtch_usb_init ) ;
module_exit ( speedtch_usb_cleanup ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRIVER_VERSION ) ;