2005-05-11 22:24:03 +04:00
/******************************************************************************
* usbatm . c - Generic USB xDSL driver core
*
* Copyright ( C ) 2001 , Alcatel
* Copyright ( C ) 2003 , Duncan Sands , SolNegro , Josep Comas
* Copyright ( C ) 2004 , David Woodhouse , Roman Kagan
*
* 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 .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Written by Johan Verrept , Duncan Sands ( duncan . sands @ free . fr ) and David Woodhouse
*
* 1.7 + : - See the check - in logs
*
* 1.6 : - No longer opens a connection if the firmware is not loaded
* - Added support for the speedtouch 330
* - Removed the limit on the number of devices
* - Module now autoloads on device plugin
* - Merged relevant parts of sarlib
* - Replaced the kernel thread with a tasklet
* - New packet transmission code
* - Changed proc file contents
* - Fixed all known SMP races
* - Many fixes and cleanups
* - Various fixes by Oliver Neukum ( oliver @ neukum . name )
*
* 1.5 A : - Version for inclusion in 2.5 series kernel
* - Modifications by Richard Purdie ( rpurdie @ rpsys . net )
* - made compatible with kernel 2.5 .6 onwards by changing
* usbatm_usb_send_data_context - > urb to a pointer and adding code
* to alloc and free it
* - remove_wait_queue ( ) added to usbatm_atm_processqueue_thread ( )
*
* 1.5 : - fixed memory leak when atmsar_decode_aal5 returned NULL .
* ( reported by stephen . robinson @ zen . co . uk )
*
* 1.4 : - changed the spin_lock ( ) under interrupt to spin_lock_irqsave ( )
* - unlink all active send urbs of a vcc that is being closed .
*
* 1.3 .1 : - added the version number
*
* 1.3 : - Added multiple send urb support
* - fixed memory leak and vcc - > tx_inuse starvation bug
* when not enough memory left in vcc .
*
* 1.2 : - Fixed race condition in usbatm_usb_send_data ( )
* 1.1 : - Turned off packet debugging
*
*/
# include "usbatm.h"
# include <asm/uaccess.h>
# include <linux/crc32.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/proc_fs.h>
# include <linux/sched.h>
# include <linux/signal.h>
# include <linux/slab.h>
# include <linux/smp_lock.h>
# include <linux/stat.h>
# include <linux/timer.h>
# include <linux/wait.h>
# ifdef VERBOSE_DEBUG
static int usbatm_print_packet ( const unsigned char * data , int len ) ;
# define PACKETDEBUG(arg...) usbatm_print_packet (arg)
# define vdbg(arg...) dbg (arg)
# else
# define PACKETDEBUG(arg...)
# define vdbg(arg...)
# endif
# define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
# define DRIVER_VERSION "1.9"
# define DRIVER_DESC "Generic USB ATM / DSL I / O, version " DRIVER_VERSION
static const char usbatm_driver_name [ ] = " usbatm " ;
# define UDSL_MAX_RCV_URBS 16
# define UDSL_MAX_SND_URBS 16
# define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */
# define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */
# define UDSL_DEFAULT_RCV_URBS 4
# define UDSL_DEFAULT_SND_URBS 4
# define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */
# define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */
# define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
# define THROTTLE_MSECS 100 /* delay to recover processing after urb submission fails */
static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS ;
static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS ;
static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE ;
static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE ;
module_param ( num_rcv_urbs , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( num_rcv_urbs ,
" Number of urbs used for reception (range: 0- "
__MODULE_STRING ( UDSL_MAX_RCV_URBS ) " , default: "
__MODULE_STRING ( UDSL_DEFAULT_RCV_URBS ) " ) " ) ;
module_param ( num_snd_urbs , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( num_snd_urbs ,
" Number of urbs used for transmission (range: 0- "
__MODULE_STRING ( UDSL_MAX_SND_URBS ) " , default: "
__MODULE_STRING ( UDSL_DEFAULT_SND_URBS ) " ) " ) ;
module_param ( rcv_buf_size , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( rcv_buf_size ,
" Size of the buffers used for reception in ATM cells (range: 1- "
__MODULE_STRING ( UDSL_MAX_RCV_BUF_SIZE ) " , default: "
__MODULE_STRING ( UDSL_DEFAULT_RCV_BUF_SIZE ) " ) " ) ;
module_param ( snd_buf_size , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( snd_buf_size ,
" Size of the buffers used for transmission in ATM cells (range: 1- "
__MODULE_STRING ( UDSL_MAX_SND_BUF_SIZE ) " , default: "
__MODULE_STRING ( UDSL_DEFAULT_SND_BUF_SIZE ) " ) " ) ;
/* receive */
struct usbatm_vcc_data {
/* vpi/vci lookup */
struct list_head list ;
short vpi ;
int vci ;
struct atm_vcc * vcc ;
/* raw cell reassembly */
struct sk_buff * sarb ;
} ;
/* send */
struct usbatm_control {
struct atm_skb_data atm ;
u32 len ;
u32 crc ;
} ;
# define UDSL_SKB(x) ((struct usbatm_control *)(x)->cb)
/* ATM */
2006-01-17 13:15:13 +03:00
static void usbatm_atm_dev_close ( struct atm_dev * atm_dev ) ;
2005-05-11 22:24:03 +04:00
static int usbatm_atm_open ( struct atm_vcc * vcc ) ;
static void usbatm_atm_close ( struct atm_vcc * vcc ) ;
2006-01-17 13:15:13 +03:00
static int usbatm_atm_ioctl ( struct atm_dev * atm_dev , unsigned int cmd , void __user * arg ) ;
2005-05-11 22:24:03 +04:00
static int usbatm_atm_send ( struct atm_vcc * vcc , struct sk_buff * skb ) ;
static int usbatm_atm_proc_read ( struct atm_dev * atm_dev , loff_t * pos , char * page ) ;
static struct atmdev_ops usbatm_atm_devops = {
. dev_close = usbatm_atm_dev_close ,
. open = usbatm_atm_open ,
. close = usbatm_atm_close ,
. ioctl = usbatm_atm_ioctl ,
. send = usbatm_atm_send ,
. proc_read = usbatm_atm_proc_read ,
. owner = THIS_MODULE ,
} ;
/***********
* * misc * *
* * * * * * * * * * */
static inline unsigned int usbatm_pdu_length ( unsigned int length )
{
length + = ATM_CELL_PAYLOAD - 1 + ATM_AAL5_TRAILER ;
return length - length % ATM_CELL_PAYLOAD ;
}
static inline void usbatm_pop ( struct atm_vcc * vcc , struct sk_buff * skb )
{
if ( vcc - > pop )
vcc - > pop ( vcc , skb ) ;
else
dev_kfree_skb ( skb ) ;
}
/***********
* * urbs * *
* * * * * * * * * * * */
2006-01-15 00:20:43 +03:00
static struct urb * usbatm_pop_urb ( struct usbatm_channel * channel )
2005-05-11 22:24:03 +04:00
{
struct urb * urb ;
spin_lock_irq ( & channel - > lock ) ;
if ( list_empty ( & channel - > list ) ) {
spin_unlock_irq ( & channel - > lock ) ;
return NULL ;
}
urb = list_entry ( channel - > list . next , struct urb , urb_list ) ;
list_del ( & urb - > urb_list ) ;
spin_unlock_irq ( & channel - > lock ) ;
return urb ;
}
2006-01-15 00:20:43 +03:00
static int usbatm_submit_urb ( struct urb * urb )
2005-05-11 22:24:03 +04:00
{
struct usbatm_channel * channel = urb - > context ;
int ret ;
vdbg ( " %s: submitting urb 0x%p, size %u " ,
__func__ , urb , urb - > transfer_buffer_length ) ;
ret = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( ret ) {
2006-01-17 13:15:13 +03:00
if ( printk_ratelimit ( ) )
atm_warn ( channel - > usbatm , " %s: urb 0x%p submission failed (%d)! \n " ,
__func__ , urb , ret ) ;
2005-05-11 22:24:03 +04:00
/* consider all errors transient and return the buffer back to the queue */
urb - > status = - EAGAIN ;
spin_lock_irq ( & channel - > lock ) ;
/* must add to the front when sending; doesn't matter when receiving */
list_add ( & urb - > urb_list , & channel - > list ) ;
spin_unlock_irq ( & channel - > lock ) ;
/* make sure the channel doesn't stall */
mod_timer ( & channel - > delay , jiffies + msecs_to_jiffies ( THROTTLE_MSECS ) ) ;
}
return ret ;
}
static void usbatm_complete ( struct urb * urb , struct pt_regs * regs )
{
struct usbatm_channel * channel = urb - > context ;
unsigned long flags ;
vdbg ( " %s: urb 0x%p, status %d, actual_length %d " ,
__func__ , urb , urb - > status , urb - > actual_length ) ;
/* usually in_interrupt(), but not always */
spin_lock_irqsave ( & channel - > lock , flags ) ;
/* must add to the back when receiving; doesn't matter when sending */
list_add_tail ( & urb - > urb_list , & channel - > list ) ;
spin_unlock_irqrestore ( & channel - > lock , flags ) ;
2006-01-17 13:15:13 +03:00
if ( unlikely ( urb - > status ) ) {
if ( printk_ratelimit ( ) )
atm_warn ( channel - > usbatm , " %s: urb 0x%p failed (%d)! \n " ,
__func__ , urb , urb - > status ) ;
2005-05-11 22:24:03 +04:00
/* throttle processing in case of an error */
mod_timer ( & channel - > delay , jiffies + msecs_to_jiffies ( THROTTLE_MSECS ) ) ;
2006-01-17 13:15:13 +03:00
} else
2005-05-11 22:24:03 +04:00
tasklet_schedule ( & channel - > tasklet ) ;
}
/*************
* * decode * *
* * * * * * * * * * * * */
static inline struct usbatm_vcc_data * usbatm_find_vcc ( struct usbatm_data * instance ,
short vpi , int vci )
{
2006-01-17 13:15:13 +03:00
struct usbatm_vcc_data * vcc_data ;
2005-05-11 22:24:03 +04:00
2006-01-17 13:15:13 +03:00
list_for_each_entry ( vcc_data , & instance - > vcc_list , list )
if ( ( vcc_data - > vci = = vci ) & & ( vcc_data - > vpi = = vpi ) )
return vcc_data ;
2005-05-11 22:24:03 +04:00
return NULL ;
}
static void usbatm_extract_cells ( struct usbatm_data * instance ,
unsigned char * source , unsigned int avail_data )
{
struct usbatm_vcc_data * cached_vcc = NULL ;
struct atm_vcc * vcc ;
struct sk_buff * sarb ;
unsigned int stride = instance - > rx_channel . stride ;
int vci , cached_vci = 0 ;
short vpi , cached_vpi = 0 ;
u8 pti ;
for ( ; avail_data > = stride ; avail_data - = stride , source + = stride ) {
vpi = ( ( source [ 0 ] & 0x0f ) < < 4 ) | ( source [ 1 ] > > 4 ) ;
vci = ( ( source [ 1 ] & 0x0f ) < < 12 ) | ( source [ 2 ] < < 4 ) | ( source [ 3 ] > > 4 ) ;
pti = ( ( source [ 3 ] & 0xe ) > > 1 ) ;
vdbg ( " %s: vpi %hd, vci %d, pti %d " , __func__ , vpi , vci , pti ) ;
2005-05-26 16:32:51 +04:00
if ( ( vci ! = cached_vci ) | | ( vpi ! = cached_vpi ) ) {
2005-05-11 22:24:03 +04:00
cached_vpi = vpi ;
cached_vci = vci ;
2005-05-26 16:32:51 +04:00
cached_vcc = usbatm_find_vcc ( instance , vpi , vci ) ;
if ( ! cached_vcc )
2006-01-17 13:15:13 +03:00
atm_rldbg ( instance , " %s: unknown vpi/vci (%hd/%d)! \n " , __func__ , vpi , vci ) ;
2005-05-11 22:24:03 +04:00
}
2005-05-26 16:32:51 +04:00
if ( ! cached_vcc )
continue ;
vcc = cached_vcc - > vcc ;
2005-05-11 22:24:03 +04:00
/* OAM F5 end-to-end */
if ( pti = = ATM_PTI_E2EF5 ) {
2006-01-17 13:15:13 +03:00
if ( printk_ratelimit ( ) )
atm_warn ( instance , " %s: OAM not supported (vpi %d, vci %d)! \n " ,
__func__ , vpi , vci ) ;
2005-05-11 22:24:03 +04:00
atomic_inc ( & vcc - > stats - > rx_err ) ;
continue ;
}
2005-05-26 16:32:51 +04:00
sarb = cached_vcc - > sarb ;
2005-05-11 22:24:03 +04:00
if ( sarb - > tail + ATM_CELL_PAYLOAD > sarb - > end ) {
2006-01-17 13:15:13 +03:00
atm_rldbg ( instance , " %s: buffer overrun (sarb->len %u, vcc: 0x%p)! \n " ,
2005-05-11 22:24:03 +04:00
__func__ , sarb - > len , vcc ) ;
/* discard cells already received */
skb_trim ( sarb , 0 ) ;
UDSL_ASSERT ( sarb - > tail + ATM_CELL_PAYLOAD < = sarb - > end ) ;
}
memcpy ( sarb - > tail , source + ATM_CELL_HEADER , ATM_CELL_PAYLOAD ) ;
__skb_put ( sarb , ATM_CELL_PAYLOAD ) ;
if ( pti & 1 ) {
struct sk_buff * skb ;
unsigned int length ;
unsigned int pdu_length ;
length = ( source [ ATM_CELL_SIZE - 6 ] < < 8 ) + source [ ATM_CELL_SIZE - 5 ] ;
/* guard against overflow */
if ( length > ATM_MAX_AAL5_PDU ) {
2006-01-17 13:15:13 +03:00
atm_rldbg ( instance , " %s: bogus length %u (vcc: 0x%p)! \n " ,
2005-05-11 22:24:03 +04:00
__func__ , length , vcc ) ;
atomic_inc ( & vcc - > stats - > rx_err ) ;
goto out ;
}
pdu_length = usbatm_pdu_length ( length ) ;
if ( sarb - > len < pdu_length ) {
2006-01-17 13:15:13 +03:00
atm_rldbg ( instance , " %s: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)! \n " ,
2005-05-11 22:24:03 +04:00
__func__ , pdu_length , sarb - > len , vcc ) ;
atomic_inc ( & vcc - > stats - > rx_err ) ;
goto out ;
}
if ( crc32_be ( ~ 0 , sarb - > tail - pdu_length , pdu_length ) ! = 0xc704dd7b ) {
2006-01-17 13:15:13 +03:00
atm_rldbg ( instance , " %s: packet failed crc check (vcc: 0x%p)! \n " ,
2005-05-11 22:24:03 +04:00
__func__ , vcc ) ;
atomic_inc ( & vcc - > stats - > rx_err ) ;
goto out ;
}
vdbg ( " %s: got packet (length: %u, pdu_length: %u, vcc: 0x%p) " , __func__ , length , pdu_length , vcc ) ;
if ( ! ( skb = dev_alloc_skb ( length ) ) ) {
2006-01-17 13:15:13 +03:00
if ( printk_ratelimit ( ) )
atm_err ( instance , " %s: no memory for skb (length: %u)! \n " ,
__func__ , length ) ;
2005-05-11 22:24:03 +04:00
atomic_inc ( & vcc - > stats - > rx_drop ) ;
goto out ;
}
vdbg ( " %s: allocated new sk_buff (skb: 0x%p, skb->truesize: %u) " , __func__ , skb , skb - > truesize ) ;
if ( ! atm_charge ( vcc , skb - > truesize ) ) {
2006-01-17 13:15:13 +03:00
atm_rldbg ( instance , " %s: failed atm_charge (skb->truesize: %u)! \n " ,
__func__ , skb - > truesize ) ;
2005-05-11 22:24:03 +04:00
dev_kfree_skb ( skb ) ;
goto out ; /* atm_charge increments rx_drop */
}
memcpy ( skb - > data , sarb - > tail - pdu_length , length ) ;
__skb_put ( skb , length ) ;
vdbg ( " %s: sending skb 0x%p, skb->len %u, skb->truesize %u " ,
__func__ , skb , skb - > len , skb - > truesize ) ;
PACKETDEBUG ( skb - > data , skb - > len ) ;
vcc - > push ( vcc , skb ) ;
atomic_inc ( & vcc - > stats - > rx ) ;
out :
skb_trim ( sarb , 0 ) ;
}
}
}
/*************
* * encode * *
* * * * * * * * * * * * */
static unsigned int usbatm_write_cells ( struct usbatm_data * instance ,
struct sk_buff * skb ,
u8 * target , unsigned int avail_space )
{
struct usbatm_control * ctrl = UDSL_SKB ( skb ) ;
struct atm_vcc * vcc = ctrl - > atm . vcc ;
unsigned int num_written ;
unsigned int stride = instance - > tx_channel . stride ;
vdbg ( " %s: skb->len=%d, avail_space=%u " , __func__ , skb - > len , avail_space ) ;
UDSL_ASSERT ( ! ( avail_space % stride ) ) ;
for ( num_written = 0 ; num_written < avail_space & & ctrl - > len ;
num_written + = stride , target + = stride ) {
unsigned int data_len = min_t ( unsigned int , skb - > len , ATM_CELL_PAYLOAD ) ;
unsigned int left = ATM_CELL_PAYLOAD - data_len ;
u8 * ptr = target ;
ptr [ 0 ] = vcc - > vpi > > 4 ;
ptr [ 1 ] = ( vcc - > vpi < < 4 ) | ( vcc - > vci > > 12 ) ;
ptr [ 2 ] = vcc - > vci > > 4 ;
ptr [ 3 ] = vcc - > vci < < 4 ;
ptr [ 4 ] = 0xec ;
ptr + = ATM_CELL_HEADER ;
memcpy ( ptr , skb - > data , data_len ) ;
ptr + = data_len ;
__skb_pull ( skb , data_len ) ;
if ( ! left )
continue ;
memset ( ptr , 0 , left ) ;
if ( left > = ATM_AAL5_TRAILER ) { /* trailer will go in this cell */
u8 * trailer = target + ATM_CELL_SIZE - ATM_AAL5_TRAILER ;
/* trailer[0] = 0; UU = 0 */
/* trailer[1] = 0; CPI = 0 */
trailer [ 2 ] = ctrl - > len > > 8 ;
trailer [ 3 ] = ctrl - > len ;
ctrl - > crc = ~ crc32_be ( ctrl - > crc , ptr , left - 4 ) ;
trailer [ 4 ] = ctrl - > crc > > 24 ;
trailer [ 5 ] = ctrl - > crc > > 16 ;
trailer [ 6 ] = ctrl - > crc > > 8 ;
trailer [ 7 ] = ctrl - > crc ;
target [ 3 ] | = 0x2 ; /* adjust PTI */
ctrl - > len = 0 ; /* tag this skb finished */
}
else
ctrl - > crc = crc32_be ( ctrl - > crc , ptr , left ) ;
}
return num_written ;
}
/**************
* * receive * *
* * * * * * * * * * * * * */
static void usbatm_rx_process ( unsigned long data )
{
struct usbatm_data * instance = ( struct usbatm_data * ) data ;
struct urb * urb ;
while ( ( urb = usbatm_pop_urb ( & instance - > rx_channel ) ) ) {
vdbg ( " %s: processing urb 0x%p " , __func__ , urb ) ;
if ( usb_pipeisoc ( urb - > pipe ) ) {
int i ;
for ( i = 0 ; i < urb - > number_of_packets ; i + + )
if ( ! urb - > iso_frame_desc [ i ] . status )
usbatm_extract_cells ( instance ,
( u8 * ) urb - > transfer_buffer + urb - > iso_frame_desc [ i ] . offset ,
urb - > iso_frame_desc [ i ] . actual_length ) ;
}
else
if ( ! urb - > status )
usbatm_extract_cells ( instance , urb - > transfer_buffer , urb - > actual_length ) ;
if ( usbatm_submit_urb ( urb ) )
return ;
}
}
/***********
* * send * *
* * * * * * * * * * */
static void usbatm_tx_process ( unsigned long data )
{
struct usbatm_data * instance = ( struct usbatm_data * ) data ;
struct sk_buff * skb = instance - > current_skb ;
struct urb * urb = NULL ;
const unsigned int buf_size = instance - > tx_channel . buf_size ;
unsigned int num_written = 0 ;
u8 * buffer = NULL ;
if ( ! skb )
skb = skb_dequeue ( & instance - > sndqueue ) ;
while ( skb ) {
if ( ! urb ) {
urb = usbatm_pop_urb ( & instance - > tx_channel ) ;
if ( ! urb )
break ; /* no more senders */
buffer = urb - > transfer_buffer ;
num_written = ( urb - > status = = - EAGAIN ) ?
urb - > transfer_buffer_length : 0 ;
}
num_written + = usbatm_write_cells ( instance , skb ,
buffer + num_written ,
buf_size - num_written ) ;
vdbg ( " %s: wrote %u bytes from skb 0x%p to urb 0x%p " ,
__func__ , num_written , skb , urb ) ;
if ( ! UDSL_SKB ( skb ) - > len ) {
struct atm_vcc * vcc = UDSL_SKB ( skb ) - > atm . vcc ;
usbatm_pop ( vcc , skb ) ;
atomic_inc ( & vcc - > stats - > tx ) ;
skb = skb_dequeue ( & instance - > sndqueue ) ;
}
if ( num_written = = buf_size | | ( ! skb & & num_written ) ) {
urb - > transfer_buffer_length = num_written ;
if ( usbatm_submit_urb ( urb ) )
break ;
urb = NULL ;
}
}
instance - > current_skb = skb ;
}
static void usbatm_cancel_send ( struct usbatm_data * instance ,
struct atm_vcc * vcc )
{
struct sk_buff * skb , * n ;
atm_dbg ( instance , " %s entered \n " , __func__ ) ;
spin_lock_irq ( & instance - > sndqueue . lock ) ;
for ( skb = instance - > sndqueue . next , n = skb - > next ;
skb ! = ( struct sk_buff * ) & instance - > sndqueue ;
skb = n , n = skb - > next )
if ( UDSL_SKB ( skb ) - > atm . vcc = = vcc ) {
atm_dbg ( instance , " %s: popping skb 0x%p \n " , __func__ , skb ) ;
__skb_unlink ( skb , & instance - > sndqueue ) ;
usbatm_pop ( vcc , skb ) ;
}
spin_unlock_irq ( & instance - > sndqueue . lock ) ;
tasklet_disable ( & instance - > tx_channel . tasklet ) ;
if ( ( skb = instance - > current_skb ) & & ( UDSL_SKB ( skb ) - > atm . vcc = = vcc ) ) {
atm_dbg ( instance , " %s: popping current skb (0x%p) \n " , __func__ , skb ) ;
instance - > current_skb = NULL ;
usbatm_pop ( vcc , skb ) ;
}
tasklet_enable ( & instance - > tx_channel . tasklet ) ;
atm_dbg ( instance , " %s done \n " , __func__ ) ;
}
static int usbatm_atm_send ( struct atm_vcc * vcc , struct sk_buff * skb )
{
struct usbatm_data * instance = vcc - > dev - > dev_data ;
struct usbatm_control * ctrl = UDSL_SKB ( skb ) ;
int err ;
vdbg ( " %s called (skb 0x%p, len %u) " , __func__ , skb , skb - > len ) ;
2006-01-13 12:05:15 +03:00
/* racy disconnection check - fine */
if ( ! instance | | instance - > disconnected ) {
# ifdef DEBUG
if ( printk_ratelimit ( ) )
printk ( KERN_DEBUG " %s: %s! \n " , __func__ , instance ? " disconnected " : " NULL instance " ) ;
# endif
2005-05-11 22:24:03 +04:00
err = - ENODEV ;
goto fail ;
}
if ( vcc - > qos . aal ! = ATM_AAL5 ) {
2006-01-17 13:15:13 +03:00
atm_rldbg ( instance , " %s: unsupported ATM type %d! \n " , __func__ , vcc - > qos . aal ) ;
2005-05-11 22:24:03 +04:00
err = - EINVAL ;
goto fail ;
}
if ( skb - > len > ATM_MAX_AAL5_PDU ) {
2006-01-17 13:15:13 +03:00
atm_rldbg ( instance , " %s: packet too long (%d vs %d)! \n " ,
2005-05-11 22:24:03 +04:00
__func__ , skb - > len , ATM_MAX_AAL5_PDU ) ;
err = - EINVAL ;
goto fail ;
}
PACKETDEBUG ( skb - > data , skb - > len ) ;
/* initialize the control block */
ctrl - > atm . vcc = vcc ;
ctrl - > len = skb - > len ;
ctrl - > crc = crc32_be ( ~ 0 , skb - > data , skb - > len ) ;
skb_queue_tail ( & instance - > sndqueue , skb ) ;
tasklet_schedule ( & instance - > tx_channel . tasklet ) ;
return 0 ;
fail :
usbatm_pop ( vcc , skb ) ;
return err ;
}
/********************
* * bean counting * *
* * * * * * * * * * * * * * * * * * * */
static void usbatm_destroy_instance ( struct kref * kref )
{
struct usbatm_data * instance = container_of ( kref , struct usbatm_data , refcount ) ;
dbg ( " %s " , __func__ ) ;
tasklet_kill ( & instance - > rx_channel . tasklet ) ;
tasklet_kill ( & instance - > tx_channel . tasklet ) ;
usb_put_dev ( instance - > usb_dev ) ;
kfree ( instance ) ;
}
2005-11-21 01:56:11 +03:00
static void usbatm_get_instance ( struct usbatm_data * instance )
2005-05-11 22:24:03 +04:00
{
dbg ( " %s " , __func__ ) ;
kref_get ( & instance - > refcount ) ;
}
2005-11-21 01:56:11 +03:00
static void usbatm_put_instance ( struct usbatm_data * instance )
2005-05-11 22:24:03 +04:00
{
dbg ( " %s " , __func__ ) ;
kref_put ( & instance - > refcount , usbatm_destroy_instance ) ;
}
/**********
* * ATM * *
* * * * * * * * * */
2006-01-17 13:15:13 +03:00
static void usbatm_atm_dev_close ( struct atm_dev * atm_dev )
2005-05-11 22:24:03 +04:00
{
2006-01-17 13:15:13 +03:00
struct usbatm_data * instance = atm_dev - > dev_data ;
2005-05-11 22:24:03 +04:00
dbg ( " %s " , __func__ ) ;
if ( ! instance )
return ;
2006-01-17 13:15:13 +03:00
atm_dev - > dev_data = NULL ; /* catch bugs */
2005-05-11 22:24:03 +04:00
usbatm_put_instance ( instance ) ; /* taken in usbatm_atm_init */
}
static int usbatm_atm_proc_read ( struct atm_dev * atm_dev , loff_t * pos , char * page )
{
struct usbatm_data * instance = atm_dev - > dev_data ;
int left = * pos ;
if ( ! instance ) {
dbg ( " %s: NULL instance! " , __func__ ) ;
return - ENODEV ;
}
if ( ! left - - )
return sprintf ( page , " %s \n " , instance - > description ) ;
if ( ! left - - )
return sprintf ( page , " MAC: %02x:%02x:%02x:%02x:%02x:%02x \n " ,
atm_dev - > esi [ 0 ] , atm_dev - > esi [ 1 ] ,
atm_dev - > esi [ 2 ] , atm_dev - > esi [ 3 ] ,
atm_dev - > esi [ 4 ] , atm_dev - > esi [ 5 ] ) ;
if ( ! left - - )
return sprintf ( page ,
" AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop ) \n " ,
atomic_read ( & atm_dev - > stats . aal5 . tx ) ,
atomic_read ( & atm_dev - > stats . aal5 . tx_err ) ,
atomic_read ( & atm_dev - > stats . aal5 . rx ) ,
atomic_read ( & atm_dev - > stats . aal5 . rx_err ) ,
atomic_read ( & atm_dev - > stats . aal5 . rx_drop ) ) ;
2006-01-13 12:05:15 +03:00
if ( ! left - - ) {
if ( instance - > disconnected )
return sprintf ( page , " Disconnected \n " ) ;
else
switch ( atm_dev - > signal ) {
case ATM_PHY_SIG_FOUND :
return sprintf ( page , " Line up \n " ) ;
case ATM_PHY_SIG_LOST :
return sprintf ( page , " Line down \n " ) ;
default :
return sprintf ( page , " Line state unknown \n " ) ;
}
}
2005-05-11 22:24:03 +04:00
return 0 ;
}
static int usbatm_atm_open ( struct atm_vcc * vcc )
{
struct usbatm_data * instance = vcc - > dev - > dev_data ;
struct usbatm_vcc_data * new = NULL ;
int ret ;
int vci = vcc - > vci ;
short vpi = vcc - > vpi ;
if ( ! instance ) {
dbg ( " %s: NULL data! " , __func__ ) ;
return - ENODEV ;
}
atm_dbg ( instance , " %s: vpi %hd, vci %d \n " , __func__ , vpi , vci ) ;
/* only support AAL5 */
2006-01-17 13:15:13 +03:00
if ( ( vcc - > qos . aal ! = ATM_AAL5 ) ) {
atm_warn ( instance , " %s: unsupported ATM type %d! \n " , __func__ , vcc - > qos . aal ) ;
return - EINVAL ;
}
/* sanity checks */
if ( ( vcc - > qos . rxtp . max_sdu < 0 ) | | ( vcc - > qos . rxtp . max_sdu > ATM_MAX_AAL5_PDU ) ) {
atm_dbg ( instance , " %s: max_sdu %d out of range! \n " , __func__ , vcc - > qos . rxtp . max_sdu ) ;
2005-05-11 22:24:03 +04:00
return - EINVAL ;
}
2006-01-17 13:15:13 +03:00
down ( & instance - > serialize ) ; /* vs self, usbatm_atm_close, usbatm_usb_disconnect */
2005-05-11 22:24:03 +04:00
2006-01-13 12:05:15 +03:00
if ( instance - > disconnected ) {
atm_dbg ( instance , " %s: disconnected! \n " , __func__ ) ;
ret = - ENODEV ;
goto fail ;
}
2005-05-11 22:24:03 +04:00
if ( usbatm_find_vcc ( instance , vpi , vci ) ) {
atm_dbg ( instance , " %s: %hd/%d already in use! \n " , __func__ , vpi , vci ) ;
ret = - EADDRINUSE ;
goto fail ;
}
2006-01-13 11:38:22 +03:00
if ( ! ( new = kzalloc ( sizeof ( struct usbatm_vcc_data ) , GFP_KERNEL ) ) ) {
2006-01-17 13:15:13 +03:00
atm_err ( instance , " %s: no memory for vcc_data! \n " , __func__ ) ;
2005-05-11 22:24:03 +04:00
ret = - ENOMEM ;
goto fail ;
}
new - > vcc = vcc ;
new - > vpi = vpi ;
new - > vci = vci ;
new - > sarb = alloc_skb ( usbatm_pdu_length ( vcc - > qos . rxtp . max_sdu ) , GFP_KERNEL ) ;
if ( ! new - > sarb ) {
2006-01-17 13:15:13 +03:00
atm_err ( instance , " %s: no memory for SAR buffer! \n " , __func__ ) ;
2005-05-11 22:24:03 +04:00
ret = - ENOMEM ;
goto fail ;
}
vcc - > dev_data = new ;
tasklet_disable ( & instance - > rx_channel . tasklet ) ;
list_add ( & new - > list , & instance - > vcc_list ) ;
tasklet_enable ( & instance - > rx_channel . tasklet ) ;
set_bit ( ATM_VF_ADDR , & vcc - > flags ) ;
set_bit ( ATM_VF_PARTIAL , & vcc - > flags ) ;
set_bit ( ATM_VF_READY , & vcc - > flags ) ;
up ( & instance - > serialize ) ;
atm_dbg ( instance , " %s: allocated vcc data 0x%p \n " , __func__ , new ) ;
return 0 ;
fail :
kfree ( new ) ;
up ( & instance - > serialize ) ;
return ret ;
}
static void usbatm_atm_close ( struct atm_vcc * vcc )
{
struct usbatm_data * instance = vcc - > dev - > dev_data ;
struct usbatm_vcc_data * vcc_data = vcc - > dev_data ;
if ( ! instance | | ! vcc_data ) {
dbg ( " %s: NULL data! " , __func__ ) ;
return ;
}
atm_dbg ( instance , " %s entered \n " , __func__ ) ;
atm_dbg ( instance , " %s: deallocating vcc 0x%p with vpi %d vci %d \n " ,
__func__ , vcc_data , vcc_data - > vpi , vcc_data - > vci ) ;
usbatm_cancel_send ( instance , vcc ) ;
2006-01-17 13:15:13 +03:00
down ( & instance - > serialize ) ; /* vs self, usbatm_atm_open, usbatm_usb_disconnect */
2005-05-11 22:24:03 +04:00
tasklet_disable ( & instance - > rx_channel . tasklet ) ;
list_del ( & vcc_data - > list ) ;
tasklet_enable ( & instance - > rx_channel . tasklet ) ;
kfree_skb ( vcc_data - > sarb ) ;
vcc_data - > sarb = NULL ;
kfree ( vcc_data ) ;
vcc - > dev_data = NULL ;
vcc - > vpi = ATM_VPI_UNSPEC ;
vcc - > vci = ATM_VCI_UNSPEC ;
clear_bit ( ATM_VF_READY , & vcc - > flags ) ;
clear_bit ( ATM_VF_PARTIAL , & vcc - > flags ) ;
clear_bit ( ATM_VF_ADDR , & vcc - > flags ) ;
up ( & instance - > serialize ) ;
atm_dbg ( instance , " %s successful \n " , __func__ ) ;
}
2006-01-17 13:15:13 +03:00
static int usbatm_atm_ioctl ( struct atm_dev * atm_dev , unsigned int cmd ,
2005-05-11 22:24:03 +04:00
void __user * arg )
{
2006-01-13 12:05:15 +03:00
struct usbatm_data * instance = atm_dev - > dev_data ;
if ( ! instance | | instance - > disconnected ) {
dbg ( " %s: %s! " , __func__ , instance ? " disconnected " : " NULL instance " ) ;
return - ENODEV ;
}
2005-05-11 22:24:03 +04:00
switch ( cmd ) {
case ATM_QUERYLOOP :
return put_user ( ATM_LM_NONE , ( int __user * ) arg ) ? - EFAULT : 0 ;
default :
return - ENOIOCTLCMD ;
}
}
static int usbatm_atm_init ( struct usbatm_data * instance )
{
struct atm_dev * atm_dev ;
int ret , i ;
2006-01-17 13:15:13 +03:00
/* ATM init. The ATM initialization scheme suffers from an intrinsic race
* condition : callbacks we register can be executed at once , before we have
* initialized the struct atm_dev . To protect against this , all callbacks
* abort if atm_dev - > dev_data is NULL . */
2005-05-11 22:24:03 +04:00
atm_dev = atm_dev_register ( instance - > driver_name , & usbatm_atm_devops , - 1 , NULL ) ;
if ( ! atm_dev ) {
2006-01-17 13:15:13 +03:00
usb_err ( instance , " %s: failed to register ATM device! \n " , __func__ ) ;
2005-05-11 22:24:03 +04:00
return - 1 ;
}
instance - > atm_dev = atm_dev ;
atm_dev - > ci_range . vpi_bits = ATM_CI_MAX ;
atm_dev - > ci_range . vci_bits = ATM_CI_MAX ;
atm_dev - > signal = ATM_PHY_SIG_UNKNOWN ;
/* temp init ATM device, set to 128kbit */
atm_dev - > link_rate = 128 * 1000 / 424 ;
if ( instance - > driver - > atm_start & & ( ( ret = instance - > driver - > atm_start ( instance , atm_dev ) ) < 0 ) ) {
2006-01-17 13:15:13 +03:00
atm_err ( instance , " %s: atm_start failed: %d! \n " , __func__ , ret ) ;
2005-05-11 22:24:03 +04:00
goto fail ;
}
usbatm_get_instance ( instance ) ; /* dropped in usbatm_atm_dev_close */
2006-01-17 13:15:13 +03:00
/* ready for ATM callbacks */
2005-05-11 22:24:03 +04:00
mb ( ) ;
atm_dev - > dev_data = instance ;
/* submit all rx URBs */
for ( i = 0 ; i < num_rcv_urbs ; i + + )
usbatm_submit_urb ( instance - > urbs [ i ] ) ;
return 0 ;
fail :
instance - > atm_dev = NULL ;
2005-11-30 03:16:41 +03:00
atm_dev_deregister ( atm_dev ) ; /* usbatm_atm_dev_close will eventually be called */
2005-05-11 22:24:03 +04:00
return ret ;
}
/**********
* * USB * *
* * * * * * * * * */
static int usbatm_do_heavy_init ( void * arg )
{
struct usbatm_data * instance = arg ;
int ret ;
daemonize ( instance - > driver - > driver_name ) ;
allow_signal ( SIGTERM ) ;
complete ( & instance - > thread_started ) ;
ret = instance - > driver - > heavy_init ( instance , instance - > usb_intf ) ;
if ( ! ret )
ret = usbatm_atm_init ( instance ) ;
down ( & instance - > serialize ) ;
instance - > thread_pid = - 1 ;
up ( & instance - > serialize ) ;
complete_and_exit ( & instance - > thread_exited , ret ) ;
}
static int usbatm_heavy_init ( struct usbatm_data * instance )
{
int ret = kernel_thread ( usbatm_do_heavy_init , instance , CLONE_KERNEL ) ;
if ( ret < 0 ) {
2006-01-17 13:15:13 +03:00
usb_err ( instance , " %s: failed to create kernel_thread (%d)! \n " , __func__ , ret ) ;
2005-05-11 22:24:03 +04:00
return ret ;
}
down ( & instance - > serialize ) ;
instance - > thread_pid = ret ;
up ( & instance - > serialize ) ;
wait_for_completion ( & instance - > thread_started ) ;
return 0 ;
}
static void usbatm_tasklet_schedule ( unsigned long data )
{
tasklet_schedule ( ( struct tasklet_struct * ) data ) ;
}
static inline void usbatm_init_channel ( struct usbatm_channel * channel )
{
spin_lock_init ( & channel - > lock ) ;
INIT_LIST_HEAD ( & channel - > list ) ;
channel - > delay . function = usbatm_tasklet_schedule ;
channel - > delay . data = ( unsigned long ) & channel - > tasklet ;
init_timer ( & channel - > delay ) ;
}
int usbatm_usb_probe ( struct usb_interface * intf , const struct usb_device_id * id ,
struct usbatm_driver * driver )
{
struct device * dev = & intf - > dev ;
struct usb_device * usb_dev = interface_to_usbdev ( intf ) ;
struct usbatm_data * instance ;
char * buf ;
int error = - ENOMEM ;
int i , length ;
2006-01-17 13:15:13 +03:00
dev_dbg ( dev , " %s: trying driver %s with vendor=%04x, product=%04x, ifnum %2d \n " ,
2005-05-11 22:24:03 +04:00
__func__ , driver - > driver_name ,
le16_to_cpu ( usb_dev - > descriptor . idVendor ) ,
le16_to_cpu ( usb_dev - > descriptor . idProduct ) ,
intf - > altsetting - > desc . bInterfaceNumber ) ;
/* instance init */
2005-09-07 02:18:34 +04:00
instance = kzalloc ( sizeof ( * instance ) + sizeof ( struct urb * ) * ( num_rcv_urbs + num_snd_urbs ) , GFP_KERNEL ) ;
2005-05-11 22:24:03 +04:00
if ( ! instance ) {
2006-01-17 13:15:13 +03:00
dev_err ( dev , " %s: no memory for instance data! \n " , __func__ ) ;
2005-05-11 22:24:03 +04:00
return - ENOMEM ;
}
/* public fields */
instance - > driver = driver ;
snprintf ( instance - > driver_name , sizeof ( instance - > driver_name ) , driver - > driver_name ) ;
instance - > usb_dev = usb_dev ;
instance - > usb_intf = intf ;
buf = instance - > description ;
length = sizeof ( instance - > description ) ;
if ( ( i = usb_string ( usb_dev , usb_dev - > descriptor . iProduct , buf , length ) ) < 0 )
goto bind ;
buf + = i ;
length - = i ;
i = scnprintf ( buf , length , " ( " ) ;
buf + = i ;
length - = i ;
if ( length < = 0 | | ( i = usb_make_path ( usb_dev , buf , length ) ) < 0 )
goto bind ;
buf + = i ;
length - = i ;
snprintf ( buf , length , " ) " ) ;
bind :
2006-01-17 13:16:13 +03:00
if ( driver - > bind & & ( error = driver - > bind ( instance , intf , id ) ) < 0 ) {
2006-01-17 13:15:13 +03:00
dev_err ( dev , " %s: bind failed: %d! \n " , __func__ , error ) ;
2005-05-11 22:24:03 +04:00
goto fail_free ;
}
/* private fields */
kref_init ( & instance - > refcount ) ; /* dropped in usbatm_usb_disconnect */
init_MUTEX ( & instance - > serialize ) ;
instance - > thread_pid = - 1 ;
init_completion ( & instance - > thread_started ) ;
init_completion ( & instance - > thread_exited ) ;
INIT_LIST_HEAD ( & instance - > vcc_list ) ;
usbatm_init_channel ( & instance - > rx_channel ) ;
usbatm_init_channel ( & instance - > tx_channel ) ;
tasklet_init ( & instance - > rx_channel . tasklet , usbatm_rx_process , ( unsigned long ) instance ) ;
tasklet_init ( & instance - > tx_channel . tasklet , usbatm_tx_process , ( unsigned long ) instance ) ;
instance - > rx_channel . endpoint = usb_rcvbulkpipe ( usb_dev , driver - > in ) ;
instance - > tx_channel . endpoint = usb_sndbulkpipe ( usb_dev , driver - > out ) ;
instance - > rx_channel . stride = ATM_CELL_SIZE + driver - > rx_padding ;
instance - > tx_channel . stride = ATM_CELL_SIZE + driver - > tx_padding ;
instance - > rx_channel . buf_size = rcv_buf_size * instance - > rx_channel . stride ;
instance - > tx_channel . buf_size = snd_buf_size * instance - > tx_channel . stride ;
instance - > rx_channel . usbatm = instance - > tx_channel . usbatm = instance ;
skb_queue_head_init ( & instance - > sndqueue ) ;
for ( i = 0 ; i < num_rcv_urbs + num_snd_urbs ; i + + ) {
struct urb * urb ;
u8 * buffer ;
unsigned int iso_packets = 0 , iso_size = 0 ;
struct usbatm_channel * channel = i < num_rcv_urbs ?
& instance - > rx_channel : & instance - > tx_channel ;
if ( usb_pipeisoc ( channel - > endpoint ) ) {
/* don't expect iso out endpoints */
iso_size = usb_maxpacket ( instance - > usb_dev , channel - > endpoint , 0 ) ;
iso_size - = iso_size % channel - > stride ; /* alignment */
BUG_ON ( ! iso_size ) ;
iso_packets = ( channel - > buf_size - 1 ) / iso_size + 1 ;
}
urb = usb_alloc_urb ( iso_packets , GFP_KERNEL ) ;
if ( ! urb ) {
2006-01-17 13:15:13 +03:00
dev_err ( dev , " %s: no memory for urb %d! \n " , __func__ , i ) ;
2005-05-11 22:24:03 +04:00
goto fail_unbind ;
}
2005-05-27 12:00:08 +04:00
instance - > urbs [ i ] = urb ;
2006-01-13 11:38:22 +03:00
/* zero the tx padding to avoid leaking information */
buffer = kzalloc ( channel - > buf_size , GFP_KERNEL ) ;
2005-05-11 22:24:03 +04:00
if ( ! buffer ) {
2006-01-17 13:15:13 +03:00
dev_err ( dev , " %s: no memory for buffer %d! \n " , __func__ , i ) ;
2005-05-11 22:24:03 +04:00
goto fail_unbind ;
}
usb_fill_bulk_urb ( urb , instance - > usb_dev , channel - > endpoint ,
buffer , channel - > buf_size , usbatm_complete , channel ) ;
if ( iso_packets ) {
int j ;
urb - > interval = 1 ;
urb - > transfer_flags = URB_ISO_ASAP ;
urb - > number_of_packets = iso_packets ;
for ( j = 0 ; j < iso_packets ; j + + ) {
urb - > iso_frame_desc [ j ] . offset = iso_size * j ;
urb - > iso_frame_desc [ j ] . length = min_t ( int , iso_size ,
channel - > buf_size - urb - > iso_frame_desc [ j ] . offset ) ;
}
}
/* put all tx URBs on the list of spares */
if ( i > = num_rcv_urbs )
list_add_tail ( & urb - > urb_list , & channel - > list ) ;
vdbg ( " %s: alloced buffer 0x%p buf size %u urb 0x%p " ,
__func__ , urb - > transfer_buffer , urb - > transfer_buffer_length , urb ) ;
}
2006-01-17 13:16:13 +03:00
if ( ! ( instance - > flags & UDSL_SKIP_HEAVY_INIT ) & & driver - > heavy_init ) {
2005-05-11 22:24:03 +04:00
error = usbatm_heavy_init ( instance ) ;
} else {
complete ( & instance - > thread_exited ) ; /* pretend that heavy_init was run */
error = usbatm_atm_init ( instance ) ;
}
if ( error < 0 )
goto fail_unbind ;
usb_get_dev ( usb_dev ) ;
usb_set_intfdata ( intf , instance ) ;
return 0 ;
fail_unbind :
if ( instance - > driver - > unbind )
instance - > driver - > unbind ( instance , intf ) ;
fail_free :
for ( i = 0 ; i < num_rcv_urbs + num_snd_urbs ; i + + ) {
if ( instance - > urbs [ i ] )
kfree ( instance - > urbs [ i ] - > transfer_buffer ) ;
usb_free_urb ( instance - > urbs [ i ] ) ;
}
kfree ( instance ) ;
return error ;
}
EXPORT_SYMBOL_GPL ( usbatm_usb_probe ) ;
void usbatm_usb_disconnect ( struct usb_interface * intf )
{
struct device * dev = & intf - > dev ;
struct usbatm_data * instance = usb_get_intfdata ( intf ) ;
2006-01-13 12:05:15 +03:00
struct usbatm_vcc_data * vcc_data ;
2005-05-11 22:24:03 +04:00
int i ;
dev_dbg ( dev , " %s entered \n " , __func__ ) ;
if ( ! instance ) {
dev_dbg ( dev , " %s: NULL instance! \n " , __func__ ) ;
return ;
}
usb_set_intfdata ( intf , NULL ) ;
down ( & instance - > serialize ) ;
2006-01-13 12:05:15 +03:00
instance - > disconnected = 1 ;
2005-05-11 22:24:03 +04:00
if ( instance - > thread_pid > = 0 )
kill_proc ( instance - > thread_pid , SIGTERM , 1 ) ;
up ( & instance - > serialize ) ;
wait_for_completion ( & instance - > thread_exited ) ;
2006-01-13 12:05:15 +03:00
down ( & instance - > serialize ) ;
list_for_each_entry ( vcc_data , & instance - > vcc_list , list )
vcc_release_async ( vcc_data - > vcc , - EPIPE ) ;
up ( & instance - > serialize ) ;
2005-05-11 22:24:03 +04:00
tasklet_disable ( & instance - > rx_channel . tasklet ) ;
tasklet_disable ( & instance - > tx_channel . tasklet ) ;
for ( i = 0 ; i < num_rcv_urbs + num_snd_urbs ; i + + )
usb_kill_urb ( instance - > urbs [ i ] ) ;
del_timer_sync ( & instance - > rx_channel . delay ) ;
del_timer_sync ( & instance - > tx_channel . delay ) ;
2006-01-13 12:05:15 +03:00
/* turn usbatm_[rt]x_process into something close to a no-op */
/* no need to take the spinlock */
INIT_LIST_HEAD ( & instance - > rx_channel . list ) ;
INIT_LIST_HEAD ( & instance - > tx_channel . list ) ;
tasklet_enable ( & instance - > rx_channel . tasklet ) ;
tasklet_enable ( & instance - > tx_channel . tasklet ) ;
2005-05-11 22:24:03 +04:00
if ( instance - > atm_dev & & instance - > driver - > atm_stop )
instance - > driver - > atm_stop ( instance , instance - > atm_dev ) ;
if ( instance - > driver - > unbind )
instance - > driver - > unbind ( instance , intf ) ;
instance - > driver_data = NULL ;
for ( i = 0 ; i < num_rcv_urbs + num_snd_urbs ; i + + ) {
kfree ( instance - > urbs [ i ] - > transfer_buffer ) ;
usb_free_urb ( instance - > urbs [ i ] ) ;
}
/* ATM finalize */
if ( instance - > atm_dev )
2005-11-30 03:16:41 +03:00
atm_dev_deregister ( instance - > atm_dev ) ;
2005-05-11 22:24:03 +04:00
usbatm_put_instance ( instance ) ; /* taken in usbatm_usb_probe */
}
EXPORT_SYMBOL_GPL ( usbatm_usb_disconnect ) ;
/***********
* * init * *
* * * * * * * * * * */
static int __init usbatm_usb_init ( void )
{
dbg ( " %s: driver version %s " , __func__ , DRIVER_VERSION ) ;
if ( sizeof ( struct usbatm_control ) > sizeof ( ( ( struct sk_buff * ) 0 ) - > cb ) ) {
printk ( KERN_ERR " %s unusable with this kernel! \n " , usbatm_driver_name ) ;
return - EIO ;
}
if ( ( num_rcv_urbs > UDSL_MAX_RCV_URBS )
| | ( num_snd_urbs > UDSL_MAX_SND_URBS )
| | ( rcv_buf_size < 1 )
| | ( rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE )
| | ( snd_buf_size < 1 )
| | ( snd_buf_size > UDSL_MAX_SND_BUF_SIZE ) )
return - EINVAL ;
return 0 ;
}
module_init ( usbatm_usb_init ) ;
static void __exit usbatm_usb_exit ( void )
{
dbg ( " %s " , __func__ ) ;
}
module_exit ( usbatm_usb_exit ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRIVER_VERSION ) ;
/************
* * debug * *
* * * * * * * * * * * */
# ifdef VERBOSE_DEBUG
static int usbatm_print_packet ( const unsigned char * data , int len )
{
unsigned char buffer [ 256 ] ;
int i = 0 , j = 0 ;
for ( i = 0 ; i < len ; ) {
buffer [ 0 ] = ' \0 ' ;
sprintf ( buffer , " %.3d : " , i ) ;
for ( j = 0 ; ( j < 16 ) & & ( i < len ) ; j + + , i + + ) {
sprintf ( buffer , " %s %2.2x " , buffer , data [ i ] ) ;
}
dbg ( " %s " , buffer ) ;
}
return i ;
}
# endif