2006-03-26 13:38:34 +04:00
/*
* USB driver for Gigaset 307 x base via direct USB connection .
*
* Copyright ( c ) 2001 by Hansjoerg Lipp < hjlipp @ web . de > ,
* Tilman Schmidt < tilman @ imap . cc > ,
2006-04-11 09:55:14 +04:00
* Stefan Eilers .
2006-03-26 13:38:34 +04:00
*
* Based on usb - gigaset . c .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* 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 .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# include "gigaset.h"
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/timer.h>
# include <linux/usb.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
/* Version Information */
2006-04-11 09:55:14 +04:00
# define DRIVER_AUTHOR "Tilman Schmidt <tilman@imap.cc>, Hansjoerg Lipp <hjlipp@web.de>, Stefan Eilers"
2006-03-26 13:38:34 +04:00
# define DRIVER_DESC "USB Driver for Gigaset 307x"
/* Module parameters */
static int startmode = SM_ISDN ;
static int cidmode = 1 ;
module_param ( startmode , int , S_IRUGO ) ;
module_param ( cidmode , int , S_IRUGO ) ;
MODULE_PARM_DESC ( startmode , " start in isdn4linux mode " ) ;
MODULE_PARM_DESC ( cidmode , " Call-ID mode " ) ;
# define GIGASET_MINORS 1
# define GIGASET_MINOR 16
# define GIGASET_MODULENAME "bas_gigaset"
# define GIGASET_DEVFSNAME "gig / bas / "
# define GIGASET_DEVNAME "ttyGB"
# define IF_WRITEBUF 256 //FIXME
/* Values for the Gigaset 307x */
# define USB_GIGA_VENDOR_ID 0x0681
# define USB_GIGA_PRODUCT_ID 0x0001
# define USB_4175_PRODUCT_ID 0x0002
# define USB_SX303_PRODUCT_ID 0x0021
# define USB_SX353_PRODUCT_ID 0x0022
/* table of devices that work with this driver */
static struct usb_device_id gigaset_table [ ] = {
{ USB_DEVICE ( USB_GIGA_VENDOR_ID , USB_GIGA_PRODUCT_ID ) } ,
{ USB_DEVICE ( USB_GIGA_VENDOR_ID , USB_4175_PRODUCT_ID ) } ,
{ USB_DEVICE ( USB_GIGA_VENDOR_ID , USB_SX303_PRODUCT_ID ) } ,
{ USB_DEVICE ( USB_GIGA_VENDOR_ID , USB_SX353_PRODUCT_ID ) } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , gigaset_table ) ;
/*======================= local function prototypes =============================*/
/* This function is called if a new device is connected to the USB port. It
* checks whether this new device belongs to this driver .
*/
static int gigaset_probe ( struct usb_interface * interface ,
const struct usb_device_id * id ) ;
/* Function will be called if the device is unplugged */
static void gigaset_disconnect ( struct usb_interface * interface ) ;
/*==============================================================================*/
struct bas_cardstate {
2006-04-11 09:55:04 +04:00
struct usb_device * udev ; /* USB device pointer */
struct usb_interface * interface ; /* interface for this device */
2006-03-26 13:38:34 +04:00
unsigned char minor ; /* starting minor number */
2006-04-11 09:55:04 +04:00
struct urb * urb_ctrl ; /* control pipe default URB */
2006-03-26 13:38:34 +04:00
struct usb_ctrlrequest dr_ctrl ;
struct timer_list timer_ctrl ; /* control request timeout */
struct timer_list timer_atrdy ; /* AT command ready timeout */
2006-04-11 09:55:04 +04:00
struct urb * urb_cmd_out ; /* for sending AT commands */
2006-03-26 13:38:34 +04:00
struct usb_ctrlrequest dr_cmd_out ;
int retry_cmd_out ;
2006-04-11 09:55:04 +04:00
struct urb * urb_cmd_in ; /* for receiving AT replies */
2006-03-26 13:38:34 +04:00
struct usb_ctrlrequest dr_cmd_in ;
struct timer_list timer_cmd_in ; /* receive request timeout */
2006-04-11 09:55:04 +04:00
unsigned char * rcvbuf ; /* AT reply receive buffer */
2006-03-26 13:38:34 +04:00
2006-04-11 09:55:04 +04:00
struct urb * urb_int_in ; /* URB for interrupt pipe */
2006-03-26 13:38:34 +04:00
unsigned char int_in_buf [ 3 ] ;
spinlock_t lock ; /* locks all following */
atomic_t basstate ; /* bitmap (BS_*) */
int pending ; /* uncompleted base request */
int rcvbuf_size ; /* size of AT receive buffer */
/* 0: no receive in progress */
int retry_cmd_in ; /* receive req retry count */
} ;
/* status of direct USB connection to 307x base (bits in basstate) */
# define BS_ATOPEN 0x001
# define BS_B1OPEN 0x002
# define BS_B2OPEN 0x004
# define BS_ATREADY 0x008
# define BS_INIT 0x010
# define BS_ATTIMER 0x020
static struct gigaset_driver * driver = NULL ;
static struct cardstate * cardstate = NULL ;
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver gigaset_usb_driver = {
. name = GIGASET_MODULENAME ,
. probe = gigaset_probe ,
. disconnect = gigaset_disconnect ,
. id_table = gigaset_table ,
} ;
/* get message text for USB status code
*/
static char * get_usb_statmsg ( int status )
{
static char unkmsg [ 28 ] ;
switch ( status ) {
case 0 :
return " success " ;
case - ENOENT :
return " canceled " ;
case - ECONNRESET :
return " canceled (async) " ;
case - EINPROGRESS :
return " pending " ;
case - EPROTO :
return " bit stuffing or unknown USB error " ;
case - EILSEQ :
return " Illegal byte sequence (CRC mismatch) " ;
case - EPIPE :
return " babble detect or endpoint stalled " ;
case - ENOSR :
return " buffer error " ;
case - ETIMEDOUT :
return " timed out " ;
case - ENODEV :
return " device not present " ;
case - EREMOTEIO :
return " short packet detected " ;
case - EXDEV :
return " partial isochronous transfer " ;
case - EINVAL :
return " invalid argument " ;
case - ENXIO :
return " URB already queued " ;
case - EAGAIN :
return " isochronous start frame too early or too much scheduled " ;
case - EFBIG :
return " too many isochronous frames requested " ;
case - EMSGSIZE :
return " endpoint message size zero " ;
case - ESHUTDOWN :
return " endpoint shutdown " ;
case - EBUSY :
return " another request pending " ;
default :
snprintf ( unkmsg , sizeof ( unkmsg ) , " unknown error %d " , status ) ;
return unkmsg ;
}
}
/* usb_pipetype_str
* retrieve string representation of USB pipe type
*/
static inline char * usb_pipetype_str ( int pipe )
{
if ( usb_pipeisoc ( pipe ) )
return " Isoc " ;
if ( usb_pipeint ( pipe ) )
return " Int " ;
if ( usb_pipecontrol ( pipe ) )
return " Ctrl " ;
if ( usb_pipebulk ( pipe ) )
return " Bulk " ;
return " ? " ;
}
/* dump_urb
* write content of URB to syslog for debugging
*/
static inline void dump_urb ( enum debuglevel level , const char * tag ,
2006-04-11 09:55:04 +04:00
struct urb * urb )
2006-03-26 13:38:34 +04:00
{
# ifdef CONFIG_GIGASET_DEBUG
int i ;
2006-04-11 09:55:04 +04:00
gig_dbg ( level , " %s urb(0x%08lx)->{ " , tag , ( unsigned long ) urb ) ;
2006-03-26 13:38:34 +04:00
if ( urb ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( level ,
" dev=0x%08lx, pipe=%s:EP%d/DV%d:%s, "
" status=%d, hcpriv=0x%08lx, transfer_flags=0x%x, " ,
( unsigned long ) urb - > dev ,
usb_pipetype_str ( urb - > pipe ) ,
usb_pipeendpoint ( urb - > pipe ) , usb_pipedevice ( urb - > pipe ) ,
usb_pipein ( urb - > pipe ) ? " in " : " out " ,
urb - > status , ( unsigned long ) urb - > hcpriv ,
urb - > transfer_flags ) ;
gig_dbg ( level ,
" transfer_buffer=0x%08lx[%d], actual_length=%d, "
" bandwidth=%d, setup_packet=0x%08lx, " ,
( unsigned long ) urb - > transfer_buffer ,
urb - > transfer_buffer_length , urb - > actual_length ,
urb - > bandwidth , ( unsigned long ) urb - > setup_packet ) ;
gig_dbg ( level ,
" start_frame=%d, number_of_packets=%d, interval=%d, "
" error_count=%d, " ,
urb - > start_frame , urb - > number_of_packets , urb - > interval ,
urb - > error_count ) ;
gig_dbg ( level ,
" context=0x%08lx, complete=0x%08lx, "
" iso_frame_desc[]={ " ,
( unsigned long ) urb - > context ,
( unsigned long ) urb - > complete ) ;
2006-03-26 13:38:34 +04:00
for ( i = 0 ; i < urb - > number_of_packets ; i + + ) {
2006-04-11 09:55:00 +04:00
struct usb_iso_packet_descriptor * pifd
= & urb - > iso_frame_desc [ i ] ;
2006-04-11 09:55:04 +04:00
gig_dbg ( level ,
" {offset=%u, length=%u, actual_length=%u, "
" status=%u} " ,
pifd - > offset , pifd - > length , pifd - > actual_length ,
pifd - > status ) ;
2006-03-26 13:38:34 +04:00
}
}
2006-04-11 09:55:04 +04:00
gig_dbg ( level , " }} " ) ;
2006-03-26 13:38:34 +04:00
# endif
}
/* read/set modem control bits etc. (m10x only) */
static int gigaset_set_modem_ctrl ( struct cardstate * cs , unsigned old_state ,
2006-04-11 09:55:04 +04:00
unsigned new_state )
2006-03-26 13:38:34 +04:00
{
return - EINVAL ;
}
static int gigaset_baud_rate ( struct cardstate * cs , unsigned cflag )
{
return - EINVAL ;
}
static int gigaset_set_line_ctrl ( struct cardstate * cs , unsigned cflag )
{
return - EINVAL ;
}
/* error_hangup
* hang up any existing connection because of an unrecoverable error
* This function may be called from any context and takes care of scheduling
* the necessary actions for execution outside of interrupt context .
* argument :
* B channel control structure
*/
static inline void error_hangup ( struct bc_state * bcs )
{
struct cardstate * cs = bcs - > cs ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY , " %s: scheduling HUP for channel %d " ,
__func__ , bcs - > channel ) ;
2006-03-26 13:38:34 +04:00
if ( ! gigaset_add_event ( cs , & bcs - > at_state , EV_HUP , NULL , 0 , NULL ) ) {
//FIXME what should we do?
return ;
}
gigaset_schedule_event ( cs ) ;
}
/* error_reset
* reset Gigaset device because of an unrecoverable error
* This function may be called from any context and takes care of scheduling
* the necessary actions for execution outside of interrupt context .
* argument :
* controller state structure
*/
static inline void error_reset ( struct cardstate * cs )
{
//FIXME try to recover without bothering the user
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev ,
" unrecoverable error - please disconnect Gigaset base to reset \n " ) ;
2006-03-26 13:38:34 +04:00
}
/* check_pending
* check for completion of pending control request
* parameter :
2006-04-11 09:55:04 +04:00
* ucs hardware specific controller state structure
2006-03-26 13:38:34 +04:00
*/
static void check_pending ( struct bas_cardstate * ucs )
{
unsigned long flags ;
spin_lock_irqsave ( & ucs - > lock , flags ) ;
switch ( ucs - > pending ) {
case 0 :
break ;
case HD_OPEN_ATCHANNEL :
if ( atomic_read ( & ucs - > basstate ) & BS_ATOPEN )
ucs - > pending = 0 ;
break ;
case HD_OPEN_B1CHANNEL :
if ( atomic_read ( & ucs - > basstate ) & BS_B1OPEN )
ucs - > pending = 0 ;
break ;
case HD_OPEN_B2CHANNEL :
if ( atomic_read ( & ucs - > basstate ) & BS_B2OPEN )
ucs - > pending = 0 ;
break ;
case HD_CLOSE_ATCHANNEL :
if ( ! ( atomic_read ( & ucs - > basstate ) & BS_ATOPEN ) )
ucs - > pending = 0 ;
break ;
case HD_CLOSE_B1CHANNEL :
if ( ! ( atomic_read ( & ucs - > basstate ) & BS_B1OPEN ) )
ucs - > pending = 0 ;
break ;
case HD_CLOSE_B2CHANNEL :
if ( ! ( atomic_read ( & ucs - > basstate ) & BS_B2OPEN ) )
ucs - > pending = 0 ;
break ;
case HD_DEVICE_INIT_ACK : /* no reply expected */
ucs - > pending = 0 ;
break ;
/* HD_READ_ATMESSAGE, HD_WRITE_ATMESSAGE, HD_RESET_INTERRUPTPIPE
* are handled separately and should never end up here
*/
default :
2006-04-11 09:55:04 +04:00
dev_warn ( & ucs - > interface - > dev ,
" unknown pending request 0x%02x cleared \n " ,
ucs - > pending ) ;
2006-03-26 13:38:34 +04:00
ucs - > pending = 0 ;
}
if ( ! ucs - > pending )
del_timer ( & ucs - > timer_ctrl ) ;
spin_unlock_irqrestore ( & ucs - > lock , flags ) ;
}
/* cmd_in_timeout
* timeout routine for command input request
* argument :
* controller state structure
*/
static void cmd_in_timeout ( unsigned long data )
{
struct cardstate * cs = ( struct cardstate * ) data ;
2006-04-11 09:55:08 +04:00
struct bas_cardstate * ucs = cs - > hw . bas ;
2006-03-26 13:38:34 +04:00
unsigned long flags ;
spin_lock_irqsave ( & cs - > lock , flags ) ;
2006-04-11 09:55:04 +04:00
if ( unlikely ( ! atomic_read ( & cs - > connected ) ) ) {
gig_dbg ( DEBUG_USBREQ , " %s: disconnected " , __func__ ) ;
2006-03-26 13:38:34 +04:00
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
return ;
}
if ( ! ucs - > rcvbuf_size ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " %s: no receive in progress " , __func__ ) ;
2006-03-26 13:38:34 +04:00
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
return ;
}
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " timeout reading AT response \n " ) ;
2006-03-26 13:38:34 +04:00
error_reset ( cs ) ; //FIXME retry?
}
static void read_ctrl_callback ( struct urb * urb , struct pt_regs * regs ) ;
/* atread_submit
* submit an HD_READ_ATMESSAGE command URB
* parameters :
* cs controller state structure
* timeout timeout in 1 / 10 sec . , 0 : none
* return value :
* 0 on success
* - EINVAL if a NULL pointer is encountered somewhere
* - EBUSY if another request is pending
* any URB submission error code
*/
static int atread_submit ( struct cardstate * cs , int timeout )
{
2006-04-11 09:55:08 +04:00
struct bas_cardstate * ucs = cs - > hw . bas ;
2006-03-26 13:38:34 +04:00
int ret ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " -------> HD_READ_ATMESSAGE (%d) " ,
ucs - > rcvbuf_size ) ;
2006-03-26 13:38:34 +04:00
if ( ucs - > urb_cmd_in - > status = = - EINPROGRESS ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev ,
" could not submit HD_READ_ATMESSAGE: URB busy \n " ) ;
2006-03-26 13:38:34 +04:00
return - EBUSY ;
}
ucs - > dr_cmd_in . bRequestType = IN_VENDOR_REQ ;
ucs - > dr_cmd_in . bRequest = HD_READ_ATMESSAGE ;
ucs - > dr_cmd_in . wValue = 0 ;
ucs - > dr_cmd_in . wIndex = 0 ;
ucs - > dr_cmd_in . wLength = cpu_to_le16 ( ucs - > rcvbuf_size ) ;
usb_fill_control_urb ( ucs - > urb_cmd_in , ucs - > udev ,
2006-04-11 09:55:04 +04:00
usb_rcvctrlpipe ( ucs - > udev , 0 ) ,
( unsigned char * ) & ucs - > dr_cmd_in ,
ucs - > rcvbuf , ucs - > rcvbuf_size ,
read_ctrl_callback , cs - > inbuf ) ;
2006-03-26 13:38:34 +04:00
if ( ( ret = usb_submit_urb ( ucs - > urb_cmd_in , SLAB_ATOMIC ) ) ! = 0 ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " could not submit HD_READ_ATMESSAGE: %s \n " ,
get_usb_statmsg ( ret ) ) ;
2006-03-26 13:38:34 +04:00
return ret ;
}
if ( timeout > 0 ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " setting timeout of %d/10 secs " , timeout ) ;
2006-03-26 13:38:34 +04:00
ucs - > timer_cmd_in . expires = jiffies + timeout * HZ / 10 ;
ucs - > timer_cmd_in . data = ( unsigned long ) cs ;
ucs - > timer_cmd_in . function = cmd_in_timeout ;
add_timer ( & ucs - > timer_cmd_in ) ;
}
return 0 ;
}
static void stopurbs ( struct bas_bc_state * ) ;
static int start_cbsend ( struct cardstate * ) ;
/* set/clear bits in base connection state
*/
inline static void update_basstate ( struct bas_cardstate * ucs ,
int set , int clear )
{
unsigned long flags ;
int state ;
spin_lock_irqsave ( & ucs - > lock , flags ) ;
state = atomic_read ( & ucs - > basstate ) ;
state & = ~ clear ;
state | = set ;
atomic_set ( & ucs - > basstate , state ) ;
spin_unlock_irqrestore ( & ucs - > lock , flags ) ;
}
/* read_int_callback
* USB completion handler for interrupt pipe input
* called by the USB subsystem in interrupt context
* parameter :
* urb USB request block
* urb - > context = controller state structure
*/
static void read_int_callback ( struct urb * urb , struct pt_regs * regs )
{
2006-04-11 09:55:08 +04:00
struct cardstate * cs = urb - > context ;
struct bas_cardstate * ucs = cs - > hw . bas ;
2006-03-26 13:38:34 +04:00
struct bc_state * bcs ;
unsigned long flags ;
int status ;
unsigned l ;
int channel ;
if ( unlikely ( ! atomic_read ( & cs - > connected ) ) ) {
warn ( " %s: disconnected " , __func__ ) ;
return ;
}
switch ( urb - > status ) {
case 0 : /* success */
break ;
case - ENOENT : /* canceled */
case - ECONNRESET : /* canceled (async) */
case - EINPROGRESS : /* pending */
/* ignore silently */
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " %s: %s " ,
__func__ , get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
return ;
default : /* severe trouble */
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev , " interrupt read: %s \n " ,
get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
//FIXME corrective action? resubmission always ok?
goto resubmit ;
}
l = ( unsigned ) ucs - > int_in_buf [ 1 ] +
( ( ( unsigned ) ucs - > int_in_buf [ 2 ] ) < < 8 ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " <-------%d: 0x%02x (%u [0x%02x 0x%02x]) " ,
urb - > actual_length , ( int ) ucs - > int_in_buf [ 0 ] , l ,
( int ) ucs - > int_in_buf [ 1 ] , ( int ) ucs - > int_in_buf [ 2 ] ) ;
2006-03-26 13:38:34 +04:00
channel = 0 ;
switch ( ucs - > int_in_buf [ 0 ] ) {
case HD_DEVICE_INIT_OK :
update_basstate ( ucs , BS_INIT , 0 ) ;
break ;
case HD_READY_SEND_ATDATA :
del_timer ( & ucs - > timer_atrdy ) ;
update_basstate ( ucs , BS_ATREADY , BS_ATTIMER ) ;
start_cbsend ( cs ) ;
break ;
case HD_OPEN_B2CHANNEL_ACK :
+ + channel ;
case HD_OPEN_B1CHANNEL_ACK :
bcs = cs - > bcs + channel ;
update_basstate ( ucs , BS_B1OPEN < < channel , 0 ) ;
gigaset_bchannel_up ( bcs ) ;
break ;
case HD_OPEN_ATCHANNEL_ACK :
update_basstate ( ucs , BS_ATOPEN , 0 ) ;
start_cbsend ( cs ) ;
break ;
case HD_CLOSE_B2CHANNEL_ACK :
+ + channel ;
case HD_CLOSE_B1CHANNEL_ACK :
bcs = cs - > bcs + channel ;
update_basstate ( ucs , 0 , BS_B1OPEN < < channel ) ;
stopurbs ( bcs - > hw . bas ) ;
gigaset_bchannel_down ( bcs ) ;
break ;
case HD_CLOSE_ATCHANNEL_ACK :
update_basstate ( ucs , 0 , BS_ATOPEN ) ;
break ;
case HD_B2_FLOW_CONTROL :
+ + channel ;
case HD_B1_FLOW_CONTROL :
bcs = cs - > bcs + channel ;
atomic_add ( ( l - BAS_NORMFRAME ) * BAS_CORRFRAMES ,
2006-04-11 09:55:04 +04:00
& bcs - > hw . bas - > corrbytes ) ;
gig_dbg ( DEBUG_ISO ,
" Flow control (channel %d, sub %d): 0x%02x => %d " ,
channel , bcs - > hw . bas - > numsub , l ,
atomic_read ( & bcs - > hw . bas - > corrbytes ) ) ;
2006-03-26 13:38:34 +04:00
break ;
case HD_RECEIVEATDATA_ACK : /* AT response ready to be received */
if ( ! l ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" HD_RECEIVEATDATA_ACK with length 0 ignored \n " ) ;
2006-03-26 13:38:34 +04:00
break ;
}
spin_lock_irqsave ( & cs - > lock , flags ) ;
if ( ucs - > rcvbuf_size ) {
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev ,
" receive AT data overrun, %d bytes lost \n " , l ) ;
2006-03-26 13:38:34 +04:00
error_reset ( cs ) ; //FIXME reschedule
break ;
}
if ( ( ucs - > rcvbuf = kmalloc ( l , GFP_ATOMIC ) ) = = NULL ) {
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " out of memory, %d bytes lost \n " , l ) ;
2006-03-26 13:38:34 +04:00
error_reset ( cs ) ; //FIXME reschedule
break ;
}
ucs - > rcvbuf_size = l ;
ucs - > retry_cmd_in = 0 ;
if ( ( status = atread_submit ( cs , BAS_TIMEOUT ) ) < 0 ) {
kfree ( ucs - > rcvbuf ) ;
ucs - > rcvbuf = NULL ;
ucs - > rcvbuf_size = 0 ;
error_reset ( cs ) ; //FIXME reschedule
}
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
break ;
case HD_RESET_INTERRUPT_PIPE_ACK :
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " HD_RESET_INTERRUPT_PIPE_ACK " ) ;
2006-03-26 13:38:34 +04:00
break ;
case HD_SUSPEND_END :
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " HD_SUSPEND_END " ) ;
2006-03-26 13:38:34 +04:00
break ;
default :
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" unknown Gigaset signal 0x%02x (%u) ignored \n " ,
( int ) ucs - > int_in_buf [ 0 ] , l ) ;
2006-03-26 13:38:34 +04:00
}
check_pending ( ucs ) ;
resubmit :
status = usb_submit_urb ( urb , SLAB_ATOMIC ) ;
if ( unlikely ( status ) ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " could not resubmit interrupt URB: %s \n " ,
get_usb_statmsg ( status ) ) ;
2006-03-26 13:38:34 +04:00
error_reset ( cs ) ;
}
}
/* read_ctrl_callback
* USB completion handler for control pipe input
* called by the USB subsystem in interrupt context
* parameter :
* urb USB request block
* urb - > context = inbuf structure for controller state
*/
static void read_ctrl_callback ( struct urb * urb , struct pt_regs * regs )
{
2006-04-11 09:55:08 +04:00
struct inbuf_t * inbuf = urb - > context ;
struct cardstate * cs = inbuf - > cs ;
struct bas_cardstate * ucs = cs - > hw . bas ;
int have_data = 0 ;
2006-03-26 13:38:34 +04:00
unsigned numbytes ;
unsigned long flags ;
spin_lock_irqsave ( & cs - > lock , flags ) ;
2006-04-11 09:55:04 +04:00
if ( unlikely ( ! atomic_read ( & cs - > connected ) ) ) {
2006-03-26 13:38:34 +04:00
warn ( " %s: disconnected " , __func__ ) ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
return ;
}
if ( ! ucs - > rcvbuf_size ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev , " %s: no receive in progress \n " , __func__ ) ;
2006-03-26 13:38:34 +04:00
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
return ;
}
del_timer ( & ucs - > timer_cmd_in ) ;
switch ( urb - > status ) {
case 0 : /* normal completion */
numbytes = urb - > actual_length ;
if ( unlikely ( numbytes = = 0 ) ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" control read: empty block received \n " ) ;
2006-03-26 13:38:34 +04:00
goto retry ;
}
if ( unlikely ( numbytes ! = ucs - > rcvbuf_size ) ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" control read: received %d chars, expected %d \n " ,
numbytes , ucs - > rcvbuf_size ) ;
2006-03-26 13:38:34 +04:00
if ( numbytes > ucs - > rcvbuf_size )
numbytes = ucs - > rcvbuf_size ;
}
/* copy received bytes to inbuf */
have_data = gigaset_fill_inbuf ( inbuf , ucs - > rcvbuf , numbytes ) ;
if ( unlikely ( numbytes < ucs - > rcvbuf_size ) ) {
/* incomplete - resubmit for remaining bytes */
ucs - > rcvbuf_size - = numbytes ;
ucs - > retry_cmd_in = 0 ;
goto retry ;
}
break ;
case - ENOENT : /* canceled */
case - ECONNRESET : /* canceled (async) */
case - EINPROGRESS : /* pending */
/* no action necessary */
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " %s: %s " ,
__func__ , get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
break ;
default : /* severe trouble */
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev , " control read: %s \n " ,
get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
retry :
if ( ucs - > retry_cmd_in + + < BAS_RETRY ) {
2006-04-11 09:55:04 +04:00
dev_notice ( cs - > dev , " control read: retry %d \n " ,
ucs - > retry_cmd_in ) ;
2006-03-26 13:38:34 +04:00
if ( atread_submit ( cs , BAS_TIMEOUT ) > = 0 ) {
/* resubmitted - bypass regular exit block */
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
return ;
}
} else {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev ,
" control read: giving up after %d tries \n " ,
ucs - > retry_cmd_in ) ;
2006-03-26 13:38:34 +04:00
}
error_reset ( cs ) ;
}
kfree ( ucs - > rcvbuf ) ;
ucs - > rcvbuf = NULL ;
ucs - > rcvbuf_size = 0 ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
if ( have_data ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INTR , " %s-->BH " , __func__ ) ;
2006-03-26 13:38:34 +04:00
gigaset_schedule_event ( cs ) ;
}
}
/* read_iso_callback
* USB completion handler for B channel isochronous input
* called by the USB subsystem in interrupt context
* parameter :
* urb USB request block of completed request
* urb - > context = bc_state structure
*/
static void read_iso_callback ( struct urb * urb , struct pt_regs * regs )
{
struct bc_state * bcs ;
struct bas_bc_state * ubc ;
unsigned long flags ;
int i , rc ;
/* status codes not worth bothering the tasklet with */
if ( unlikely ( urb - > status = = - ENOENT | | urb - > status = = - ECONNRESET | |
2006-04-11 09:55:04 +04:00
urb - > status = = - EINPROGRESS ) ) {
gig_dbg ( DEBUG_ISO , " %s: %s " ,
__func__ , get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
return ;
}
2006-04-11 09:55:08 +04:00
bcs = urb - > context ;
2006-03-26 13:38:34 +04:00
ubc = bcs - > hw . bas ;
spin_lock_irqsave ( & ubc - > isoinlock , flags ) ;
if ( likely ( ubc - > isoindone = = NULL ) ) {
/* pass URB to tasklet */
ubc - > isoindone = urb ;
tasklet_schedule ( & ubc - > rcvd_tasklet ) ;
} else {
/* tasklet still busy, drop data and resubmit URB */
ubc - > loststatus = urb - > status ;
for ( i = 0 ; i < BAS_NUMFRAMES ; i + + ) {
ubc - > isoinlost + = urb - > iso_frame_desc [ i ] . actual_length ;
if ( unlikely ( urb - > iso_frame_desc [ i ] . status ! = 0 & &
urb - > iso_frame_desc [ i ] . status ! = - EINPROGRESS ) ) {
ubc - > loststatus = urb - > iso_frame_desc [ i ] . status ;
}
urb - > iso_frame_desc [ i ] . status = 0 ;
urb - > iso_frame_desc [ i ] . actual_length = 0 ;
}
if ( likely ( atomic_read ( & ubc - > running ) ) ) {
2006-04-11 09:55:04 +04:00
/* urb->dev is clobbered by USB subsystem */
urb - > dev = bcs - > cs - > hw . bas - > udev ;
2006-03-26 13:38:34 +04:00
urb - > transfer_flags = URB_ISO_ASAP ;
urb - > number_of_packets = BAS_NUMFRAMES ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO , " %s: isoc read overrun/resubmit " ,
__func__ ) ;
2006-03-26 13:38:34 +04:00
rc = usb_submit_urb ( urb , SLAB_ATOMIC ) ;
if ( unlikely ( rc ! = 0 ) ) {
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev ,
" could not resubmit isochronous read "
" URB: %s \n " , get_usb_statmsg ( rc ) ) ;
2006-03-26 13:38:34 +04:00
dump_urb ( DEBUG_ISO , " isoc read " , urb ) ;
error_hangup ( bcs ) ;
}
}
}
spin_unlock_irqrestore ( & ubc - > isoinlock , flags ) ;
}
/* write_iso_callback
* USB completion handler for B channel isochronous output
* called by the USB subsystem in interrupt context
* parameter :
* urb USB request block of completed request
* urb - > context = isow_urbctx_t structure
*/
static void write_iso_callback ( struct urb * urb , struct pt_regs * regs )
{
struct isow_urbctx_t * ucx ;
struct bas_bc_state * ubc ;
unsigned long flags ;
/* status codes not worth bothering the tasklet with */
if ( unlikely ( urb - > status = = - ENOENT | | urb - > status = = - ECONNRESET | |
2006-04-11 09:55:04 +04:00
urb - > status = = - EINPROGRESS ) ) {
gig_dbg ( DEBUG_ISO , " %s: %s " ,
__func__ , get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
return ;
}
/* pass URB context to tasklet */
2006-04-11 09:55:08 +04:00
ucx = urb - > context ;
2006-03-26 13:38:34 +04:00
ubc = ucx - > bcs - > hw . bas ;
spin_lock_irqsave ( & ubc - > isooutlock , flags ) ;
ubc - > isooutovfl = ubc - > isooutdone ;
ubc - > isooutdone = ucx ;
spin_unlock_irqrestore ( & ubc - > isooutlock , flags ) ;
tasklet_schedule ( & ubc - > sent_tasklet ) ;
}
/* starturbs
* prepare and submit USB request blocks for isochronous input and output
* argument :
* B channel control structure
* return value :
* 0 on success
* < 0 on error ( no URBs submitted )
*/
static int starturbs ( struct bc_state * bcs )
{
2006-04-11 09:55:08 +04:00
struct bas_bc_state * ubc = bcs - > hw . bas ;
2006-03-26 13:38:34 +04:00
struct urb * urb ;
int j , k ;
int rc ;
/* initialize L2 reception */
if ( bcs - > proto2 = = ISDN_PROTO_L2_HDLC )
bcs - > inputstate | = INS_flag_hunt ;
/* submit all isochronous input URBs */
atomic_set ( & ubc - > running , 1 ) ;
for ( k = 0 ; k < BAS_INURBS ; k + + ) {
urb = ubc - > isoinurbs [ k ] ;
if ( ! urb ) {
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev , " isoinurbs[%d]==NULL \n " , k ) ;
2006-03-26 13:38:34 +04:00
rc = - EFAULT ;
goto error ;
}
urb - > dev = bcs - > cs - > hw . bas - > udev ;
urb - > pipe = usb_rcvisocpipe ( urb - > dev , 3 + 2 * bcs - > channel ) ;
urb - > transfer_flags = URB_ISO_ASAP ;
urb - > transfer_buffer = ubc - > isoinbuf + k * BAS_INBUFSIZE ;
urb - > transfer_buffer_length = BAS_INBUFSIZE ;
urb - > number_of_packets = BAS_NUMFRAMES ;
urb - > interval = BAS_FRAMETIME ;
urb - > complete = read_iso_callback ;
urb - > context = bcs ;
for ( j = 0 ; j < BAS_NUMFRAMES ; j + + ) {
urb - > iso_frame_desc [ j ] . offset = j * BAS_MAXFRAME ;
urb - > iso_frame_desc [ j ] . length = BAS_MAXFRAME ;
urb - > iso_frame_desc [ j ] . status = 0 ;
urb - > iso_frame_desc [ j ] . actual_length = 0 ;
}
dump_urb ( DEBUG_ISO , " Initial isoc read " , urb ) ;
if ( ( rc = usb_submit_urb ( urb , SLAB_ATOMIC ) ) ! = 0 ) {
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev ,
" could not submit isochronous read URB %d: %s \n " ,
k , get_usb_statmsg ( rc ) ) ;
2006-03-26 13:38:34 +04:00
goto error ;
}
}
/* initialize L2 transmission */
gigaset_isowbuf_init ( ubc - > isooutbuf , PPP_FLAG ) ;
/* set up isochronous output URBs for flag idling */
for ( k = 0 ; k < BAS_OUTURBS ; + + k ) {
urb = ubc - > isoouturbs [ k ] . urb ;
if ( ! urb ) {
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev , " isoouturbs[%d].urb==NULL \n " , k ) ;
2006-03-26 13:38:34 +04:00
rc = - EFAULT ;
goto error ;
}
urb - > dev = bcs - > cs - > hw . bas - > udev ;
urb - > pipe = usb_sndisocpipe ( urb - > dev , 4 + 2 * bcs - > channel ) ;
urb - > transfer_flags = URB_ISO_ASAP ;
urb - > transfer_buffer = ubc - > isooutbuf - > data ;
urb - > transfer_buffer_length = sizeof ( ubc - > isooutbuf - > data ) ;
urb - > number_of_packets = BAS_NUMFRAMES ;
urb - > interval = BAS_FRAMETIME ;
urb - > complete = write_iso_callback ;
urb - > context = & ubc - > isoouturbs [ k ] ;
for ( j = 0 ; j < BAS_NUMFRAMES ; + + j ) {
urb - > iso_frame_desc [ j ] . offset = BAS_OUTBUFSIZE ;
urb - > iso_frame_desc [ j ] . length = BAS_NORMFRAME ;
urb - > iso_frame_desc [ j ] . status = 0 ;
urb - > iso_frame_desc [ j ] . actual_length = 0 ;
}
ubc - > isoouturbs [ k ] . limit = - 1 ;
}
/* submit two URBs, keep third one */
for ( k = 0 ; k < 2 ; + + k ) {
dump_urb ( DEBUG_ISO , " Initial isoc write " , urb ) ;
rc = usb_submit_urb ( ubc - > isoouturbs [ k ] . urb , SLAB_ATOMIC ) ;
if ( rc ! = 0 ) {
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev ,
" could not submit isochronous write URB %d: %s \n " ,
k , get_usb_statmsg ( rc ) ) ;
2006-03-26 13:38:34 +04:00
goto error ;
}
}
dump_urb ( DEBUG_ISO , " Initial isoc write (free) " , urb ) ;
ubc - > isooutfree = & ubc - > isoouturbs [ 2 ] ;
ubc - > isooutdone = ubc - > isooutovfl = NULL ;
return 0 ;
error :
stopurbs ( ubc ) ;
return rc ;
}
/* stopurbs
* cancel the USB request blocks for isochronous input and output
* errors are silently ignored
* argument :
* B channel control structure
*/
static void stopurbs ( struct bas_bc_state * ubc )
{
int k , rc ;
atomic_set ( & ubc - > running , 0 ) ;
for ( k = 0 ; k < BAS_INURBS ; + + k ) {
rc = usb_unlink_urb ( ubc - > isoinurbs [ k ] ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO ,
" %s: isoc input URB %d unlinked, result = %d " ,
__func__ , k , rc ) ;
2006-03-26 13:38:34 +04:00
}
for ( k = 0 ; k < BAS_OUTURBS ; + + k ) {
rc = usb_unlink_urb ( ubc - > isoouturbs [ k ] . urb ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO ,
" %s: isoc output URB %d unlinked, result = %d " ,
__func__ , k , rc ) ;
2006-03-26 13:38:34 +04:00
}
}
/* Isochronous Write - Bottom Half */
/* =============================== */
/* submit_iso_write_urb
* fill and submit the next isochronous write URB
* parameters :
* bcs B channel state structure
* return value :
* number of frames submitted in URB
* 0 if URB not submitted because no data available ( isooutbuf busy )
* error code < 0 on error
*/
static int submit_iso_write_urb ( struct isow_urbctx_t * ucx )
{
2006-04-11 09:55:08 +04:00
struct urb * urb = ucx - > urb ;
struct bas_bc_state * ubc = ucx - > bcs - > hw . bas ;
2006-03-26 13:38:34 +04:00
struct usb_iso_packet_descriptor * ifd ;
int corrbytes , nframe , rc ;
2006-04-11 09:55:04 +04:00
/* urb->dev is clobbered by USB subsystem */
urb - > dev = ucx - > bcs - > cs - > hw . bas - > udev ;
2006-03-26 13:38:34 +04:00
urb - > transfer_flags = URB_ISO_ASAP ;
urb - > transfer_buffer = ubc - > isooutbuf - > data ;
urb - > transfer_buffer_length = sizeof ( ubc - > isooutbuf - > data ) ;
for ( nframe = 0 ; nframe < BAS_NUMFRAMES ; nframe + + ) {
ifd = & urb - > iso_frame_desc [ nframe ] ;
/* compute frame length according to flow control */
ifd - > length = BAS_NORMFRAME ;
if ( ( corrbytes = atomic_read ( & ubc - > corrbytes ) ) ! = 0 ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO , " %s: corrbytes=%d " ,
__func__ , corrbytes ) ;
2006-03-26 13:38:34 +04:00
if ( corrbytes > BAS_HIGHFRAME - BAS_NORMFRAME )
corrbytes = BAS_HIGHFRAME - BAS_NORMFRAME ;
else if ( corrbytes < BAS_LOWFRAME - BAS_NORMFRAME )
corrbytes = BAS_LOWFRAME - BAS_NORMFRAME ;
ifd - > length + = corrbytes ;
atomic_add ( - corrbytes , & ubc - > corrbytes ) ;
}
/* retrieve block of data to send */
2006-04-11 09:55:00 +04:00
ifd - > offset = gigaset_isowbuf_getbytes ( ubc - > isooutbuf ,
ifd - > length ) ;
2006-03-26 13:38:34 +04:00
if ( ifd - > offset < 0 ) {
if ( ifd - > offset = = - EBUSY ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO ,
" %s: buffer busy at frame %d " ,
__func__ , nframe ) ;
/* tasklet will be restarted from
gigaset_send_skb ( ) */
2006-03-26 13:38:34 +04:00
} else {
2006-04-11 09:55:04 +04:00
dev_err ( ucx - > bcs - > cs - > dev ,
" %s: buffer error %d at frame %d \n " ,
__func__ , ifd - > offset , nframe ) ;
2006-03-26 13:38:34 +04:00
return ifd - > offset ;
}
break ;
}
ucx - > limit = atomic_read ( & ubc - > isooutbuf - > nextread ) ;
ifd - > status = 0 ;
ifd - > actual_length = 0 ;
}
if ( ( urb - > number_of_packets = nframe ) > 0 ) {
if ( ( rc = usb_submit_urb ( urb , SLAB_ATOMIC ) ) ! = 0 ) {
2006-04-11 09:55:04 +04:00
dev_err ( ucx - > bcs - > cs - > dev ,
" could not submit isochronous write URB: %s \n " ,
get_usb_statmsg ( rc ) ) ;
2006-03-26 13:38:34 +04:00
dump_urb ( DEBUG_ISO , " isoc write " , urb ) ;
return rc ;
}
+ + ubc - > numsub ;
}
return nframe ;
}
/* write_iso_tasklet
* tasklet scheduled when an isochronous output URB from the Gigaset device
* has completed
* parameter :
* data B channel state structure
*/
static void write_iso_tasklet ( unsigned long data )
{
2006-04-11 09:55:08 +04:00
struct bc_state * bcs = ( struct bc_state * ) data ;
struct bas_bc_state * ubc = bcs - > hw . bas ;
struct cardstate * cs = bcs - > cs ;
2006-03-26 13:38:34 +04:00
struct isow_urbctx_t * done , * next , * ovfl ;
struct urb * urb ;
struct usb_iso_packet_descriptor * ifd ;
int offset ;
unsigned long flags ;
int i ;
struct sk_buff * skb ;
int len ;
/* loop while completed URBs arrive in time */
for ( ; ; ) {
if ( unlikely ( ! atomic_read ( & cs - > connected ) ) ) {
warn ( " %s: disconnected " , __func__ ) ;
return ;
}
if ( unlikely ( ! ( atomic_read ( & ubc - > running ) ) ) ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO , " %s: not running " , __func__ ) ;
2006-03-26 13:38:34 +04:00
return ;
}
/* retrieve completed URBs */
spin_lock_irqsave ( & ubc - > isooutlock , flags ) ;
done = ubc - > isooutdone ;
ubc - > isooutdone = NULL ;
ovfl = ubc - > isooutovfl ;
ubc - > isooutovfl = NULL ;
spin_unlock_irqrestore ( & ubc - > isooutlock , flags ) ;
if ( ovfl ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " isochronous write buffer underrun \n " ) ;
2006-03-26 13:38:34 +04:00
error_hangup ( bcs ) ;
break ;
}
if ( ! done )
break ;
/* submit free URB if available */
spin_lock_irqsave ( & ubc - > isooutlock , flags ) ;
next = ubc - > isooutfree ;
ubc - > isooutfree = NULL ;
spin_unlock_irqrestore ( & ubc - > isooutlock , flags ) ;
if ( next ) {
if ( submit_iso_write_urb ( next ) < = 0 ) {
/* could not submit URB, put it back */
spin_lock_irqsave ( & ubc - > isooutlock , flags ) ;
if ( ubc - > isooutfree = = NULL ) {
ubc - > isooutfree = next ;
next = NULL ;
}
spin_unlock_irqrestore ( & ubc - > isooutlock , flags ) ;
if ( next ) {
/* couldn't put it back */
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev ,
" losing isochronous write URB \n " ) ;
2006-03-26 13:38:34 +04:00
error_hangup ( bcs ) ;
}
}
}
/* process completed URB */
urb = done - > urb ;
switch ( urb - > status ) {
case 0 : /* normal completion */
break ;
case - EXDEV : /* inspect individual frames */
/* assumptions (for lack of documentation):
2006-04-11 09:55:00 +04:00
* - actual_length bytes of the frame in error are
* successfully sent
2006-03-26 13:38:34 +04:00
* - all following frames are not sent at all
*/
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO , " %s: URB partially completed " ,
__func__ ) ;
2006-03-26 13:38:34 +04:00
offset = done - > limit ; /* just in case */
for ( i = 0 ; i < BAS_NUMFRAMES ; i + + ) {
ifd = & urb - > iso_frame_desc [ i ] ;
if ( ifd - > status | |
ifd - > actual_length ! = ifd - > length ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" isochronous write: frame %d: %s, "
" only %d of %d bytes sent \n " ,
2006-03-26 13:38:34 +04:00
i , get_usb_statmsg ( ifd - > status ) ,
ifd - > actual_length , ifd - > length ) ;
offset = ( ifd - > offset +
2006-04-11 09:55:04 +04:00
ifd - > actual_length )
% BAS_OUTBUFSIZE ;
2006-03-26 13:38:34 +04:00
break ;
}
}
# ifdef CONFIG_GIGASET_DEBUG
/* check assumption on remaining frames */
for ( ; i < BAS_NUMFRAMES ; i + + ) {
ifd = & urb - > iso_frame_desc [ i ] ;
if ( ifd - > status ! = - EINPROGRESS
| | ifd - > actual_length ! = 0 ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" isochronous write: frame %d: %s, "
" %d of %d bytes sent \n " ,
2006-03-26 13:38:34 +04:00
i , get_usb_statmsg ( ifd - > status ) ,
ifd - > actual_length , ifd - > length ) ;
offset = ( ifd - > offset +
2006-04-11 09:55:04 +04:00
ifd - > actual_length )
% BAS_OUTBUFSIZE ;
2006-03-26 13:38:34 +04:00
break ;
}
}
# endif
break ;
2006-04-11 09:55:04 +04:00
case - EPIPE : //FIXME is this the code for "underrun"?
dev_err ( cs - > dev , " isochronous write stalled \n " ) ;
2006-03-26 13:38:34 +04:00
error_hangup ( bcs ) ;
break ;
default : /* severe trouble */
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev , " isochronous write: %s \n " ,
get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
}
/* mark the write buffer area covered by this URB as free */
if ( done - > limit > = 0 )
atomic_set ( & ubc - > isooutbuf - > read , done - > limit ) ;
/* mark URB as free */
spin_lock_irqsave ( & ubc - > isooutlock , flags ) ;
next = ubc - > isooutfree ;
ubc - > isooutfree = done ;
spin_unlock_irqrestore ( & ubc - > isooutlock , flags ) ;
if ( next ) {
/* only one URB still active - resubmit one */
if ( submit_iso_write_urb ( next ) < = 0 ) {
/* couldn't submit */
error_hangup ( bcs ) ;
}
}
}
/* process queued SKBs */
while ( ( skb = skb_dequeue ( & bcs - > squeue ) ) ) {
/* copy to output buffer, doing L2 encapsulation */
len = skb - > len ;
if ( gigaset_isoc_buildframe ( bcs , skb - > data , len ) = = - EAGAIN ) {
/* insufficient buffer space, push back onto queue */
skb_queue_head ( & bcs - > squeue , skb ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO , " %s: skb requeued, qlen=%d " ,
__func__ , skb_queue_len ( & bcs - > squeue ) ) ;
2006-03-26 13:38:34 +04:00
break ;
}
skb_pull ( skb , len ) ;
gigaset_skb_sent ( bcs , skb ) ;
dev_kfree_skb_any ( skb ) ;
}
}
/* Isochronous Read - Bottom Half */
/* ============================== */
/* read_iso_tasklet
* tasklet scheduled when an isochronous input URB from the Gigaset device
* has completed
* parameter :
* data B channel state structure
*/
static void read_iso_tasklet ( unsigned long data )
{
2006-04-11 09:55:08 +04:00
struct bc_state * bcs = ( struct bc_state * ) data ;
struct bas_bc_state * ubc = bcs - > hw . bas ;
struct cardstate * cs = bcs - > cs ;
2006-03-26 13:38:34 +04:00
struct urb * urb ;
char * rcvbuf ;
unsigned long flags ;
int totleft , numbytes , offset , frame , rc ;
/* loop while more completed URBs arrive in the meantime */
for ( ; ; ) {
2006-04-11 09:55:04 +04:00
if ( unlikely ( ! atomic_read ( & cs - > connected ) ) ) {
2006-03-26 13:38:34 +04:00
warn ( " %s: disconnected " , __func__ ) ;
return ;
}
/* retrieve URB */
spin_lock_irqsave ( & ubc - > isoinlock , flags ) ;
if ( ! ( urb = ubc - > isoindone ) ) {
spin_unlock_irqrestore ( & ubc - > isoinlock , flags ) ;
return ;
}
ubc - > isoindone = NULL ;
if ( unlikely ( ubc - > loststatus ! = - EINPROGRESS ) ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" isochronous read overrun, "
" dropped URB with status: %s, %d bytes lost \n " ,
get_usb_statmsg ( ubc - > loststatus ) ,
ubc - > isoinlost ) ;
2006-03-26 13:38:34 +04:00
ubc - > loststatus = - EINPROGRESS ;
}
spin_unlock_irqrestore ( & ubc - > isoinlock , flags ) ;
if ( unlikely ( ! ( atomic_read ( & ubc - > running ) ) ) ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO ,
" %s: channel not running, "
" dropped URB with status: %s " ,
__func__ , get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
return ;
}
switch ( urb - > status ) {
case 0 : /* normal completion */
break ;
2006-04-11 09:55:00 +04:00
case - EXDEV : /* inspect individual frames
( we do that anyway ) */
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO , " %s: URB partially completed " ,
__func__ ) ;
2006-03-26 13:38:34 +04:00
break ;
case - ENOENT :
case - ECONNRESET :
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO , " %s: URB canceled " , __func__ ) ;
2006-03-26 13:38:34 +04:00
continue ; /* -> skip */
case - EINPROGRESS : /* huh? */
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ISO , " %s: URB still pending " , __func__ ) ;
2006-03-26 13:38:34 +04:00
continue ; /* -> skip */
case - EPIPE :
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " isochronous read stalled \n " ) ;
2006-03-26 13:38:34 +04:00
error_hangup ( bcs ) ;
continue ; /* -> skip */
default : /* severe trouble */
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev , " isochronous read: %s \n " ,
get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
goto error ;
}
rcvbuf = urb - > transfer_buffer ;
totleft = urb - > actual_length ;
for ( frame = 0 ; totleft > 0 & & frame < BAS_NUMFRAMES ; frame + + ) {
if ( unlikely ( urb - > iso_frame_desc [ frame ] . status ) ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" isochronous read: frame %d: %s \n " ,
frame ,
get_usb_statmsg (
urb - > iso_frame_desc [ frame ] . status ) ) ;
2006-03-26 13:38:34 +04:00
break ;
}
numbytes = urb - > iso_frame_desc [ frame ] . actual_length ;
if ( unlikely ( numbytes > BAS_MAXFRAME ) ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" isochronous read: frame %d: "
" numbytes (%d) > BAS_MAXFRAME \n " ,
frame , numbytes ) ;
2006-03-26 13:38:34 +04:00
break ;
}
if ( unlikely ( numbytes > totleft ) ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" isochronous read: frame %d: "
" numbytes (%d) > totleft (%d) \n " ,
frame , numbytes , totleft ) ;
2006-03-26 13:38:34 +04:00
break ;
}
offset = urb - > iso_frame_desc [ frame ] . offset ;
if ( unlikely ( offset + numbytes > BAS_INBUFSIZE ) ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" isochronous read: frame %d: "
" offset (%d) + numbytes (%d) "
" > BAS_INBUFSIZE \n " ,
frame , offset , numbytes ) ;
2006-03-26 13:38:34 +04:00
break ;
}
gigaset_isoc_receive ( rcvbuf + offset , numbytes , bcs ) ;
totleft - = numbytes ;
}
if ( unlikely ( totleft > 0 ) )
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" isochronous read: %d data bytes missing \n " ,
totleft ) ;
2006-03-26 13:38:34 +04:00
error :
/* URB processed, resubmit */
for ( frame = 0 ; frame < BAS_NUMFRAMES ; frame + + ) {
urb - > iso_frame_desc [ frame ] . status = 0 ;
urb - > iso_frame_desc [ frame ] . actual_length = 0 ;
}
2006-04-11 09:55:04 +04:00
/* urb->dev is clobbered by USB subsystem */
urb - > dev = bcs - > cs - > hw . bas - > udev ;
2006-03-26 13:38:34 +04:00
urb - > transfer_flags = URB_ISO_ASAP ;
urb - > number_of_packets = BAS_NUMFRAMES ;
if ( ( rc = usb_submit_urb ( urb , SLAB_ATOMIC ) ) ! = 0 ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev ,
" could not resubmit isochronous read URB: %s \n " ,
get_usb_statmsg ( rc ) ) ;
2006-03-26 13:38:34 +04:00
dump_urb ( DEBUG_ISO , " resubmit iso read " , urb ) ;
error_hangup ( bcs ) ;
}
}
}
/* Channel Operations */
/* ================== */
/* req_timeout
* timeout routine for control output request
* argument :
* B channel control structure
*/
static void req_timeout ( unsigned long data )
{
struct bc_state * bcs = ( struct bc_state * ) data ;
2006-04-11 09:55:08 +04:00
struct bas_cardstate * ucs = bcs - > cs - > hw . bas ;
2006-03-26 13:38:34 +04:00
int pending ;
unsigned long flags ;
check_pending ( ucs ) ;
spin_lock_irqsave ( & ucs - > lock , flags ) ;
pending = ucs - > pending ;
ucs - > pending = 0 ;
spin_unlock_irqrestore ( & ucs - > lock , flags ) ;
switch ( pending ) {
case 0 : /* no pending request */
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " %s: no request pending " , __func__ ) ;
2006-03-26 13:38:34 +04:00
break ;
case HD_OPEN_ATCHANNEL :
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev , " timeout opening AT channel \n " ) ;
2006-03-26 13:38:34 +04:00
error_reset ( bcs - > cs ) ;
break ;
case HD_OPEN_B2CHANNEL :
case HD_OPEN_B1CHANNEL :
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev , " timeout opening channel %d \n " ,
bcs - > channel + 1 ) ;
2006-03-26 13:38:34 +04:00
error_hangup ( bcs ) ;
break ;
case HD_CLOSE_ATCHANNEL :
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev , " timeout closing AT channel \n " ) ;
2006-03-26 13:38:34 +04:00
break ;
case HD_CLOSE_B2CHANNEL :
case HD_CLOSE_B1CHANNEL :
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev , " timeout closing channel %d \n " ,
bcs - > channel + 1 ) ;
2006-03-26 13:38:34 +04:00
break ;
default :
2006-04-11 09:55:04 +04:00
dev_warn ( bcs - > cs - > dev , " request 0x%02x timed out, clearing \n " ,
pending ) ;
2006-03-26 13:38:34 +04:00
}
}
/* write_ctrl_callback
* USB completion handler for control pipe output
* called by the USB subsystem in interrupt context
* parameter :
* urb USB request block of completed request
* urb - > context = hardware specific controller state structure
*/
static void write_ctrl_callback ( struct urb * urb , struct pt_regs * regs )
{
2006-04-11 09:55:08 +04:00
struct bas_cardstate * ucs = urb - > context ;
2006-03-26 13:38:34 +04:00
unsigned long flags ;
spin_lock_irqsave ( & ucs - > lock , flags ) ;
if ( urb - > status & & ucs - > pending ) {
2006-04-11 09:55:04 +04:00
dev_err ( & ucs - > interface - > dev ,
" control request 0x%02x failed: %s \n " ,
ucs - > pending , get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
del_timer ( & ucs - > timer_ctrl ) ;
ucs - > pending = 0 ;
}
/* individual handling of specific request types */
switch ( ucs - > pending ) {
case HD_DEVICE_INIT_ACK : /* no reply expected */
ucs - > pending = 0 ;
break ;
}
spin_unlock_irqrestore ( & ucs - > lock , flags ) ;
}
/* req_submit
* submit a control output request without message buffer to the Gigaset base
* and optionally start a timeout
* parameters :
* bcs B channel control structure
* req control request code ( HD_ * )
* val control request parameter value ( set to 0 if unused )
* timeout timeout in seconds ( 0 : no timeout )
* return value :
* 0 on success
* - EINVAL if a NULL pointer is encountered somewhere
* - EBUSY if another request is pending
* any URB submission error code
*/
static int req_submit ( struct bc_state * bcs , int req , int val , int timeout )
{
2006-04-11 09:55:08 +04:00
struct bas_cardstate * ucs = bcs - > cs - > hw . bas ;
2006-03-26 13:38:34 +04:00
int ret ;
unsigned long flags ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " -------> 0x%02x (%d) " , req , val ) ;
2006-03-26 13:38:34 +04:00
spin_lock_irqsave ( & ucs - > lock , flags ) ;
if ( ucs - > pending ) {
spin_unlock_irqrestore ( & ucs - > lock , flags ) ;
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev ,
" submission of request 0x%02x failed: "
" request 0x%02x still pending \n " ,
req , ucs - > pending ) ;
2006-03-26 13:38:34 +04:00
return - EBUSY ;
}
if ( ucs - > urb_ctrl - > status = = - EINPROGRESS ) {
spin_unlock_irqrestore ( & ucs - > lock , flags ) ;
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev ,
" could not submit request 0x%02x: URB busy \n " , req ) ;
2006-03-26 13:38:34 +04:00
return - EBUSY ;
}
ucs - > dr_ctrl . bRequestType = OUT_VENDOR_REQ ;
ucs - > dr_ctrl . bRequest = req ;
ucs - > dr_ctrl . wValue = cpu_to_le16 ( val ) ;
ucs - > dr_ctrl . wIndex = 0 ;
ucs - > dr_ctrl . wLength = 0 ;
usb_fill_control_urb ( ucs - > urb_ctrl , ucs - > udev ,
2006-04-11 09:55:04 +04:00
usb_sndctrlpipe ( ucs - > udev , 0 ) ,
( unsigned char * ) & ucs - > dr_ctrl , NULL , 0 ,
write_ctrl_callback , ucs ) ;
2006-03-26 13:38:34 +04:00
if ( ( ret = usb_submit_urb ( ucs - > urb_ctrl , SLAB_ATOMIC ) ) ! = 0 ) {
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev , " could not submit request 0x%02x: %s \n " ,
req , get_usb_statmsg ( ret ) ) ;
2006-03-26 13:38:34 +04:00
spin_unlock_irqrestore ( & ucs - > lock , flags ) ;
return ret ;
}
ucs - > pending = req ;
if ( timeout > 0 ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " setting timeout of %d/10 secs " , timeout ) ;
2006-03-26 13:38:34 +04:00
ucs - > timer_ctrl . expires = jiffies + timeout * HZ / 10 ;
ucs - > timer_ctrl . data = ( unsigned long ) bcs ;
ucs - > timer_ctrl . function = req_timeout ;
add_timer ( & ucs - > timer_ctrl ) ;
}
spin_unlock_irqrestore ( & ucs - > lock , flags ) ;
return 0 ;
}
/* gigaset_init_bchannel
* called by common . c to connect a B channel
* initialize isochronous I / O and tell the Gigaset base to open the channel
* argument :
* B channel control structure
* return value :
* 0 on success , error code < 0 on error
*/
static int gigaset_init_bchannel ( struct bc_state * bcs )
{
int req , ret ;
if ( ( ret = starturbs ( bcs ) ) < 0 ) {
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev ,
" could not start isochronous I/O for channel %d \n " ,
bcs - > channel + 1 ) ;
2006-03-26 13:38:34 +04:00
error_hangup ( bcs ) ;
return ret ;
}
req = bcs - > channel ? HD_OPEN_B2CHANNEL : HD_OPEN_B1CHANNEL ;
if ( ( ret = req_submit ( bcs , req , 0 , BAS_TIMEOUT ) ) < 0 ) {
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev , " could not open channel %d: %s \n " ,
bcs - > channel + 1 , get_usb_statmsg ( ret ) ) ;
2006-03-26 13:38:34 +04:00
stopurbs ( bcs - > hw . bas ) ;
error_hangup ( bcs ) ;
}
return ret ;
}
/* gigaset_close_bchannel
* called by common . c to disconnect a B channel
* tell the Gigaset base to close the channel
* stopping isochronous I / O and LL notification will be done when the
* acknowledgement for the close arrives
* argument :
* B channel control structure
* return value :
* 0 on success , error code < 0 on error
*/
static int gigaset_close_bchannel ( struct bc_state * bcs )
{
int req , ret ;
if ( ! ( atomic_read ( & bcs - > cs - > hw . bas - > basstate ) &
( bcs - > channel ? BS_B2OPEN : BS_B1OPEN ) ) ) {
/* channel not running: just signal common.c */
gigaset_bchannel_down ( bcs ) ;
return 0 ;
}
req = bcs - > channel ? HD_CLOSE_B2CHANNEL : HD_CLOSE_B1CHANNEL ;
if ( ( ret = req_submit ( bcs , req , 0 , BAS_TIMEOUT ) ) < 0 )
2006-04-11 09:55:04 +04:00
dev_err ( bcs - > cs - > dev ,
" could not submit HD_CLOSE_BxCHANNEL request: %s \n " ,
get_usb_statmsg ( ret ) ) ;
2006-03-26 13:38:34 +04:00
return ret ;
}
/* Device Operations */
/* ================= */
/* complete_cb
* unqueue first command buffer from queue , waking any sleepers
* must be called with cs - > cmdlock held
* parameter :
* cs controller state structure
*/
static void complete_cb ( struct cardstate * cs )
{
2006-04-11 09:55:08 +04:00
struct cmdbuf_t * cb = cs - > cmdbuf ;
2006-03-26 13:38:34 +04:00
/* unqueue completed buffer */
cs - > cmdbytes - = cs - > curlen ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_TRANSCMD | DEBUG_LOCKCMD ,
" write_command: sent %u bytes, %u left " ,
cs - > curlen , cs - > cmdbytes ) ;
2006-03-26 13:38:34 +04:00
if ( ( cs - > cmdbuf = cb - > next ) ! = NULL ) {
cs - > cmdbuf - > prev = NULL ;
cs - > curlen = cs - > cmdbuf - > len ;
} else {
cs - > lastcmdbuf = NULL ;
cs - > curlen = 0 ;
}
if ( cb - > wake_tasklet )
tasklet_schedule ( cb - > wake_tasklet ) ;
kfree ( cb ) ;
}
static int atwrite_submit ( struct cardstate * cs , unsigned char * buf , int len ) ;
/* write_command_callback
* USB completion handler for AT command transmission
* called by the USB subsystem in interrupt context
* parameter :
* urb USB request block of completed request
* urb - > context = controller state structure
*/
static void write_command_callback ( struct urb * urb , struct pt_regs * regs )
{
2006-04-11 09:55:08 +04:00
struct cardstate * cs = urb - > context ;
struct bas_cardstate * ucs = cs - > hw . bas ;
2006-03-26 13:38:34 +04:00
unsigned long flags ;
/* check status */
switch ( urb - > status ) {
case 0 : /* normal completion */
break ;
case - ENOENT : /* canceled */
case - ECONNRESET : /* canceled (async) */
case - EINPROGRESS : /* pending */
/* ignore silently */
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " %s: %s " ,
__func__ , get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
return ;
default : /* any failure */
if ( + + ucs - > retry_cmd_out > BAS_RETRY ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" command write: %s, "
" giving up after %d retries \n " ,
get_usb_statmsg ( urb - > status ) ,
ucs - > retry_cmd_out ) ;
2006-03-26 13:38:34 +04:00
break ;
}
if ( cs - > cmdbuf = = NULL ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev ,
" command write: %s, "
" cannot retry - cmdbuf gone \n " ,
get_usb_statmsg ( urb - > status ) ) ;
2006-03-26 13:38:34 +04:00
break ;
}
2006-04-11 09:55:04 +04:00
dev_notice ( cs - > dev , " command write: %s, retry %d \n " ,
get_usb_statmsg ( urb - > status ) , ucs - > retry_cmd_out ) ;
2006-03-26 13:38:34 +04:00
if ( atwrite_submit ( cs , cs - > cmdbuf - > buf , cs - > cmdbuf - > len ) > = 0 )
/* resubmitted - bypass regular exit block */
return ;
/* command send failed, assume base still waiting */
update_basstate ( ucs , BS_ATREADY , 0 ) ;
}
spin_lock_irqsave ( & cs - > cmdlock , flags ) ;
if ( cs - > cmdbuf ! = NULL )
complete_cb ( cs ) ;
spin_unlock_irqrestore ( & cs - > cmdlock , flags ) ;
}
/* atrdy_timeout
* timeout routine for AT command transmission
* argument :
* controller state structure
*/
static void atrdy_timeout ( unsigned long data )
{
struct cardstate * cs = ( struct cardstate * ) data ;
2006-04-11 09:55:08 +04:00
struct bas_cardstate * ucs = cs - > hw . bas ;
2006-03-26 13:38:34 +04:00
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev , " timeout waiting for HD_READY_SEND_ATDATA \n " ) ;
2006-03-26 13:38:34 +04:00
/* fake the missing signal - what else can I do? */
update_basstate ( ucs , BS_ATREADY , BS_ATTIMER ) ;
start_cbsend ( cs ) ;
}
/* atwrite_submit
* submit an HD_WRITE_ATMESSAGE command URB
* parameters :
* cs controller state structure
* buf buffer containing command to send
* len length of command to send
* return value :
* 0 on success
* - EFAULT if a NULL pointer is encountered somewhere
* - EBUSY if another request is pending
* any URB submission error code
*/
static int atwrite_submit ( struct cardstate * cs , unsigned char * buf , int len )
{
2006-04-11 09:55:08 +04:00
struct bas_cardstate * ucs = cs - > hw . bas ;
2006-03-26 13:38:34 +04:00
int ret ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_USBREQ , " -------> HD_WRITE_ATMESSAGE (%d) " , len ) ;
2006-03-26 13:38:34 +04:00
if ( ucs - > urb_cmd_out - > status = = - EINPROGRESS ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev ,
" could not submit HD_WRITE_ATMESSAGE: URB busy \n " ) ;
2006-03-26 13:38:34 +04:00
return - EBUSY ;
}
ucs - > dr_cmd_out . bRequestType = OUT_VENDOR_REQ ;
ucs - > dr_cmd_out . bRequest = HD_WRITE_ATMESSAGE ;
ucs - > dr_cmd_out . wValue = 0 ;
ucs - > dr_cmd_out . wIndex = 0 ;
ucs - > dr_cmd_out . wLength = cpu_to_le16 ( len ) ;
usb_fill_control_urb ( ucs - > urb_cmd_out , ucs - > udev ,
usb_sndctrlpipe ( ucs - > udev , 0 ) ,
( unsigned char * ) & ucs - > dr_cmd_out , buf , len ,
write_command_callback , cs ) ;
if ( ( ret = usb_submit_urb ( ucs - > urb_cmd_out , SLAB_ATOMIC ) ) ! = 0 ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " could not submit HD_WRITE_ATMESSAGE: %s \n " ,
get_usb_statmsg ( ret ) ) ;
2006-03-26 13:38:34 +04:00
return ret ;
}
/* submitted successfully */
update_basstate ( ucs , 0 , BS_ATREADY ) ;
/* start timeout if necessary */
if ( ! ( atomic_read ( & ucs - > basstate ) & BS_ATTIMER ) ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_OUTPUT , " setting ATREADY timeout of %d/10 secs " ,
ATRDY_TIMEOUT ) ;
2006-03-26 13:38:34 +04:00
ucs - > timer_atrdy . expires = jiffies + ATRDY_TIMEOUT * HZ / 10 ;
ucs - > timer_atrdy . data = ( unsigned long ) cs ;
ucs - > timer_atrdy . function = atrdy_timeout ;
add_timer ( & ucs - > timer_atrdy ) ;
update_basstate ( ucs , BS_ATTIMER , 0 ) ;
}
return 0 ;
}
/* start_cbsend
* start transmission of AT command queue if necessary
* parameter :
* cs controller state structure
* return value :
* 0 on success
* error code < 0 on error
*/
static int start_cbsend ( struct cardstate * cs )
{
struct cmdbuf_t * cb ;
2006-04-11 09:55:08 +04:00
struct bas_cardstate * ucs = cs - > hw . bas ;
2006-03-26 13:38:34 +04:00
unsigned long flags ;
int rc ;
int retval = 0 ;
/* check if AT channel is open */
if ( ! ( atomic_read ( & ucs - > basstate ) & BS_ATOPEN ) ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_TRANSCMD | DEBUG_LOCKCMD , " AT channel not open " ) ;
2006-03-26 13:38:34 +04:00
rc = req_submit ( cs - > bcs , HD_OPEN_ATCHANNEL , 0 , BAS_TIMEOUT ) ;
if ( rc < 0 ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " could not open AT channel \n " ) ;
2006-03-26 13:38:34 +04:00
/* flush command queue */
spin_lock_irqsave ( & cs - > cmdlock , flags ) ;
while ( cs - > cmdbuf ! = NULL )
complete_cb ( cs ) ;
spin_unlock_irqrestore ( & cs - > cmdlock , flags ) ;
}
return rc ;
}
/* try to send first command in queue */
spin_lock_irqsave ( & cs - > cmdlock , flags ) ;
while ( ( cb = cs - > cmdbuf ) ! = NULL & &
atomic_read ( & ucs - > basstate ) & BS_ATREADY ) {
ucs - > retry_cmd_out = 0 ;
rc = atwrite_submit ( cs , cb - > buf , cb - > len ) ;
if ( unlikely ( rc ) ) {
retval = rc ;
complete_cb ( cs ) ;
}
}
spin_unlock_irqrestore ( & cs - > cmdlock , flags ) ;
return retval ;
}
/* gigaset_write_cmd
* This function is called by the device independent part of the driver
* to transmit an AT command string to the Gigaset device .
* It encapsulates the device specific method for transmission over the
* direct USB connection to the base .
* The command string is added to the queue of commands to send , and
* USB transmission is started if necessary .
* parameters :
* cs controller state structure
* buf command string to send
* len number of bytes to send ( max . IF_WRITEBUF )
2006-04-11 09:55:00 +04:00
* wake_tasklet tasklet to run when transmission is completed
* ( NULL if none )
2006-03-26 13:38:34 +04:00
* return value :
* number of bytes queued on success
* error code < 0 on error
*/
static int gigaset_write_cmd ( struct cardstate * cs ,
2006-04-11 09:55:04 +04:00
const unsigned char * buf , int len ,
struct tasklet_struct * wake_tasklet )
2006-03-26 13:38:34 +04:00
{
struct cmdbuf_t * cb ;
unsigned long flags ;
int status ;
gigaset_dbg_buffer ( atomic_read ( & cs - > mstate ) ! = MS_LOCKED ?
2006-04-11 09:55:04 +04:00
DEBUG_TRANSCMD : DEBUG_LOCKCMD ,
2006-04-11 09:55:11 +04:00
" CMD Transmit " , len , buf ) ;
2006-03-26 13:38:34 +04:00
2006-04-11 09:55:04 +04:00
if ( unlikely ( ! atomic_read ( & cs - > connected ) ) ) {
err ( " %s: disconnected " , __func__ ) ;
2006-03-26 13:38:34 +04:00
return - ENODEV ;
}
if ( len < = 0 )
return 0 ; /* nothing to do */
if ( len > IF_WRITEBUF )
len = IF_WRITEBUF ;
if ( ! ( cb = kmalloc ( sizeof ( struct cmdbuf_t ) + len , GFP_ATOMIC ) ) ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " %s: out of memory \n " , __func__ ) ;
2006-03-26 13:38:34 +04:00
return - ENOMEM ;
}
memcpy ( cb - > buf , buf , len ) ;
cb - > len = len ;
cb - > offset = 0 ;
cb - > next = NULL ;
cb - > wake_tasklet = wake_tasklet ;
spin_lock_irqsave ( & cs - > cmdlock , flags ) ;
cb - > prev = cs - > lastcmdbuf ;
if ( cs - > lastcmdbuf )
cs - > lastcmdbuf - > next = cb ;
else {
cs - > cmdbuf = cb ;
cs - > curlen = len ;
}
cs - > cmdbytes + = len ;
cs - > lastcmdbuf = cb ;
spin_unlock_irqrestore ( & cs - > cmdlock , flags ) ;
status = start_cbsend ( cs ) ;
return status < 0 ? status : len ;
}
/* gigaset_write_room
* tty_driver . write_room interface routine
2006-04-11 09:55:00 +04:00
* return number of characters the driver will accept to be written via
* gigaset_write_cmd
2006-03-26 13:38:34 +04:00
* parameter :
* controller state structure
* return value :
* number of characters
*/
static int gigaset_write_room ( struct cardstate * cs )
{
return IF_WRITEBUF ;
}
/* gigaset_chars_in_buffer
* tty_driver . chars_in_buffer interface routine
* return number of characters waiting to be sent
* parameter :
* controller state structure
* return value :
* number of characters
*/
static int gigaset_chars_in_buffer ( struct cardstate * cs )
{
unsigned long flags ;
unsigned bytes ;
spin_lock_irqsave ( & cs - > cmdlock , flags ) ;
bytes = cs - > cmdbytes ;
spin_unlock_irqrestore ( & cs - > cmdlock , flags ) ;
return bytes ;
}
/* gigaset_brkchars
* implementation of ioctl ( GIGASET_BRKCHARS )
* parameter :
* controller state structure
* return value :
* - EINVAL ( unimplemented function )
*/
static int gigaset_brkchars ( struct cardstate * cs , const unsigned char buf [ 6 ] )
{
return - EINVAL ;
}
/* Device Initialization/Shutdown */
/* ============================== */
/* Free hardware dependent part of the B channel structure
* parameter :
* bcs B channel structure
* return value :
* ! = 0 on success
*/
static int gigaset_freebcshw ( struct bc_state * bcs )
{
if ( ! bcs - > hw . bas )
return 0 ;
if ( bcs - > hw . bas - > isooutbuf )
kfree ( bcs - > hw . bas - > isooutbuf ) ;
kfree ( bcs - > hw . bas ) ;
bcs - > hw . bas = NULL ;
return 1 ;
}
/* Initialize hardware dependent part of the B channel structure
* parameter :
* bcs B channel structure
* return value :
* ! = 0 on success
*/
static int gigaset_initbcshw ( struct bc_state * bcs )
{
int i ;
struct bas_bc_state * ubc ;
bcs - > hw . bas = ubc = kmalloc ( sizeof ( struct bas_bc_state ) , GFP_KERNEL ) ;
if ( ! ubc ) {
err ( " could not allocate bas_bc_state " ) ;
return 0 ;
}
atomic_set ( & ubc - > running , 0 ) ;
atomic_set ( & ubc - > corrbytes , 0 ) ;
spin_lock_init ( & ubc - > isooutlock ) ;
for ( i = 0 ; i < BAS_OUTURBS ; + + i ) {
ubc - > isoouturbs [ i ] . urb = NULL ;
ubc - > isoouturbs [ i ] . bcs = bcs ;
}
ubc - > isooutdone = ubc - > isooutfree = ubc - > isooutovfl = NULL ;
ubc - > numsub = 0 ;
if ( ! ( ubc - > isooutbuf = kmalloc ( sizeof ( struct isowbuf_t ) , GFP_KERNEL ) ) ) {
err ( " could not allocate isochronous output buffer " ) ;
kfree ( ubc ) ;
bcs - > hw . bas = NULL ;
return 0 ;
}
tasklet_init ( & ubc - > sent_tasklet ,
2006-04-11 09:55:04 +04:00
& write_iso_tasklet , ( unsigned long ) bcs ) ;
2006-03-26 13:38:34 +04:00
spin_lock_init ( & ubc - > isoinlock ) ;
for ( i = 0 ; i < BAS_INURBS ; + + i )
ubc - > isoinurbs [ i ] = NULL ;
ubc - > isoindone = NULL ;
ubc - > loststatus = - EINPROGRESS ;
ubc - > isoinlost = 0 ;
ubc - > seqlen = 0 ;
ubc - > inbyte = 0 ;
ubc - > inbits = 0 ;
ubc - > goodbytes = 0 ;
ubc - > alignerrs = 0 ;
ubc - > fcserrs = 0 ;
ubc - > frameerrs = 0 ;
ubc - > giants = 0 ;
ubc - > runts = 0 ;
ubc - > aborts = 0 ;
ubc - > shared0s = 0 ;
ubc - > stolen0s = 0 ;
tasklet_init ( & ubc - > rcvd_tasklet ,
2006-04-11 09:55:04 +04:00
& read_iso_tasklet , ( unsigned long ) bcs ) ;
2006-03-26 13:38:34 +04:00
return 1 ;
}
static void gigaset_reinitbcshw ( struct bc_state * bcs )
{
struct bas_bc_state * ubc = bcs - > hw . bas ;
atomic_set ( & bcs - > hw . bas - > running , 0 ) ;
atomic_set ( & bcs - > hw . bas - > corrbytes , 0 ) ;
bcs - > hw . bas - > numsub = 0 ;
spin_lock_init ( & ubc - > isooutlock ) ;
spin_lock_init ( & ubc - > isoinlock ) ;
ubc - > loststatus = - EINPROGRESS ;
}
static void gigaset_freecshw ( struct cardstate * cs )
{
struct bas_cardstate * ucs = cs - > hw . bas ;
del_timer ( & ucs - > timer_ctrl ) ;
del_timer ( & ucs - > timer_atrdy ) ;
del_timer ( & ucs - > timer_cmd_in ) ;
kfree ( cs - > hw . bas ) ;
}
static int gigaset_initcshw ( struct cardstate * cs )
{
struct bas_cardstate * ucs ;
cs - > hw . bas = ucs = kmalloc ( sizeof * ucs , GFP_KERNEL ) ;
if ( ! ucs )
return 0 ;
ucs - > urb_cmd_in = NULL ;
ucs - > urb_cmd_out = NULL ;
ucs - > rcvbuf = NULL ;
ucs - > rcvbuf_size = 0 ;
spin_lock_init ( & ucs - > lock ) ;
ucs - > pending = 0 ;
atomic_set ( & ucs - > basstate , 0 ) ;
init_timer ( & ucs - > timer_ctrl ) ;
init_timer ( & ucs - > timer_atrdy ) ;
init_timer ( & ucs - > timer_cmd_in ) ;
return 1 ;
}
/* freeurbs
* unlink and deallocate all URBs unconditionally
* caller must make sure that no commands are still in progress
* parameter :
* cs controller state structure
*/
static void freeurbs ( struct cardstate * cs )
{
2006-04-11 09:55:08 +04:00
struct bas_cardstate * ucs = cs - > hw . bas ;
2006-03-26 13:38:34 +04:00
struct bas_bc_state * ubc ;
int i , j ;
for ( j = 0 ; j < 2 ; + + j ) {
ubc = cs - > bcs [ j ] . hw . bas ;
for ( i = 0 ; i < BAS_OUTURBS ; + + i )
if ( ubc - > isoouturbs [ i ] . urb ) {
usb_kill_urb ( ubc - > isoouturbs [ i ] . urb ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT ,
" %s: isoc output URB %d/%d unlinked " ,
__func__ , j , i ) ;
2006-03-26 13:38:34 +04:00
usb_free_urb ( ubc - > isoouturbs [ i ] . urb ) ;
ubc - > isoouturbs [ i ] . urb = NULL ;
}
for ( i = 0 ; i < BAS_INURBS ; + + i )
if ( ubc - > isoinurbs [ i ] ) {
usb_kill_urb ( ubc - > isoinurbs [ i ] ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT ,
" %s: isoc input URB %d/%d unlinked " ,
__func__ , j , i ) ;
2006-03-26 13:38:34 +04:00
usb_free_urb ( ubc - > isoinurbs [ i ] ) ;
ubc - > isoinurbs [ i ] = NULL ;
}
}
if ( ucs - > urb_int_in ) {
usb_kill_urb ( ucs - > urb_int_in ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " %s: interrupt input URB unlinked " ,
__func__ ) ;
2006-03-26 13:38:34 +04:00
usb_free_urb ( ucs - > urb_int_in ) ;
ucs - > urb_int_in = NULL ;
}
if ( ucs - > urb_cmd_out ) {
usb_kill_urb ( ucs - > urb_cmd_out ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " %s: command output URB unlinked " ,
__func__ ) ;
2006-03-26 13:38:34 +04:00
usb_free_urb ( ucs - > urb_cmd_out ) ;
ucs - > urb_cmd_out = NULL ;
}
if ( ucs - > urb_cmd_in ) {
usb_kill_urb ( ucs - > urb_cmd_in ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " %s: command input URB unlinked " ,
__func__ ) ;
2006-03-26 13:38:34 +04:00
usb_free_urb ( ucs - > urb_cmd_in ) ;
ucs - > urb_cmd_in = NULL ;
}
if ( ucs - > urb_ctrl ) {
usb_kill_urb ( ucs - > urb_ctrl ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " %s: control output URB unlinked " ,
__func__ ) ;
2006-03-26 13:38:34 +04:00
usb_free_urb ( ucs - > urb_ctrl ) ;
ucs - > urb_ctrl = NULL ;
}
}
/* gigaset_probe
* This function is called when a new USB device is connected .
* It checks whether the new device is handled by this driver .
*/
static int gigaset_probe ( struct usb_interface * interface ,
const struct usb_device_id * id )
{
struct usb_host_interface * hostif ;
struct usb_device * udev = interface_to_usbdev ( interface ) ;
struct cardstate * cs = NULL ;
struct bas_cardstate * ucs = NULL ;
struct bas_bc_state * ubc ;
struct usb_endpoint_descriptor * endpoint ;
int i , j ;
int ret ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY ,
" %s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x) " ,
__func__ , le16_to_cpu ( udev - > descriptor . idVendor ) ,
le16_to_cpu ( udev - > descriptor . idProduct ) ) ;
2006-03-26 13:38:34 +04:00
/* See if the device offered us matches what we can accept */
if ( ( le16_to_cpu ( udev - > descriptor . idVendor ) ! = USB_GIGA_VENDOR_ID ) | |
( le16_to_cpu ( udev - > descriptor . idProduct ) ! = USB_GIGA_PRODUCT_ID & &
le16_to_cpu ( udev - > descriptor . idProduct ) ! = USB_4175_PRODUCT_ID & &
le16_to_cpu ( udev - > descriptor . idProduct ) ! = USB_SX303_PRODUCT_ID & &
le16_to_cpu ( udev - > descriptor . idProduct ) ! = USB_SX353_PRODUCT_ID ) ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY , " %s: unmatched ID - exiting " , __func__ ) ;
2006-03-26 13:38:34 +04:00
return - ENODEV ;
}
/* set required alternate setting */
hostif = interface - > cur_altsetting ;
if ( hostif - > desc . bAlternateSetting ! = 3 ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY ,
" %s: wrong alternate setting %d - trying to switch " ,
__func__ , hostif - > desc . bAlternateSetting ) ;
2006-03-26 13:38:34 +04:00
if ( usb_set_interface ( udev , hostif - > desc . bInterfaceNumber , 3 ) < 0 ) {
2006-04-11 09:55:04 +04:00
dev_warn ( & udev - > dev , " usb_set_interface failed, "
" device %d interface %d altsetting %d \n " ,
udev - > devnum , hostif - > desc . bInterfaceNumber ,
hostif - > desc . bAlternateSetting ) ;
2006-03-26 13:38:34 +04:00
return - ENODEV ;
}
hostif = interface - > cur_altsetting ;
}
/* Reject application specific interfaces
*/
if ( hostif - > desc . bInterfaceClass ! = 255 ) {
2006-04-11 09:55:04 +04:00
dev_warn ( & udev - > dev , " %s: bInterfaceClass == %d \n " ,
__func__ , hostif - > desc . bInterfaceClass ) ;
2006-03-26 13:38:34 +04:00
return - ENODEV ;
}
2006-04-11 09:55:04 +04:00
dev_info ( & udev - > dev ,
" %s: Device matched (Vendor: 0x%x, Product: 0x%x) \n " ,
__func__ , le16_to_cpu ( udev - > descriptor . idVendor ) ,
le16_to_cpu ( udev - > descriptor . idProduct ) ) ;
2006-03-26 13:38:34 +04:00
cs = gigaset_getunassignedcs ( driver ) ;
if ( ! cs ) {
2006-04-11 09:55:04 +04:00
dev_err ( & udev - > dev , " no free cardstate \n " ) ;
2006-03-26 13:38:34 +04:00
return - ENODEV ;
}
ucs = cs - > hw . bas ;
2006-04-11 09:55:04 +04:00
/* save off device structure ptrs for later use */
usb_get_dev ( udev ) ;
2006-03-26 13:38:34 +04:00
ucs - > udev = udev ;
ucs - > interface = interface ;
2006-04-11 09:55:07 +04:00
cs - > dev = & interface - > dev ;
2006-03-26 13:38:34 +04:00
/* allocate URBs:
* - one for the interrupt pipe
* - three for the different uses of the default control pipe
* - three for each isochronous pipe
*/
ucs - > urb_int_in = usb_alloc_urb ( 0 , SLAB_KERNEL ) ;
if ( ! ucs - > urb_int_in ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " no free urbs available \n " ) ;
2006-03-26 13:38:34 +04:00
goto error ;
}
ucs - > urb_cmd_in = usb_alloc_urb ( 0 , SLAB_KERNEL ) ;
if ( ! ucs - > urb_cmd_in ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " no free urbs available \n " ) ;
2006-03-26 13:38:34 +04:00
goto error ;
}
ucs - > urb_cmd_out = usb_alloc_urb ( 0 , SLAB_KERNEL ) ;
if ( ! ucs - > urb_cmd_out ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " no free urbs available \n " ) ;
2006-03-26 13:38:34 +04:00
goto error ;
}
ucs - > urb_ctrl = usb_alloc_urb ( 0 , SLAB_KERNEL ) ;
if ( ! ucs - > urb_ctrl ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " no free urbs available \n " ) ;
2006-03-26 13:38:34 +04:00
goto error ;
}
for ( j = 0 ; j < 2 ; + + j ) {
ubc = cs - > bcs [ j ] . hw . bas ;
for ( i = 0 ; i < BAS_OUTURBS ; + + i ) {
ubc - > isoouturbs [ i ] . urb =
usb_alloc_urb ( BAS_NUMFRAMES , SLAB_KERNEL ) ;
if ( ! ubc - > isoouturbs [ i ] . urb ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " no free urbs available \n " ) ;
2006-03-26 13:38:34 +04:00
goto error ;
}
}
for ( i = 0 ; i < BAS_INURBS ; + + i ) {
ubc - > isoinurbs [ i ] =
usb_alloc_urb ( BAS_NUMFRAMES , SLAB_KERNEL ) ;
if ( ! ubc - > isoinurbs [ i ] ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " no free urbs available \n " ) ;
2006-03-26 13:38:34 +04:00
goto error ;
}
}
}
ucs - > rcvbuf = NULL ;
ucs - > rcvbuf_size = 0 ;
/* Fill the interrupt urb and send it to the core */
endpoint = & hostif - > endpoint [ 0 ] . desc ;
usb_fill_int_urb ( ucs - > urb_int_in , udev ,
2006-04-11 09:55:04 +04:00
usb_rcvintpipe ( udev ,
( endpoint - > bEndpointAddress ) & 0x0f ) ,
ucs - > int_in_buf , 3 , read_int_callback , cs ,
endpoint - > bInterval ) ;
2006-03-26 13:38:34 +04:00
ret = usb_submit_urb ( ucs - > urb_int_in , SLAB_KERNEL ) ;
if ( ret ) {
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " could not submit interrupt URB: %s \n " ,
get_usb_statmsg ( ret ) ) ;
2006-03-26 13:38:34 +04:00
goto error ;
}
/* tell the device that the driver is ready */
if ( ( ret = req_submit ( cs - > bcs , HD_DEVICE_INIT_ACK , 0 , 0 ) ) ! = 0 )
goto error ;
/* tell common part that the device is ready */
if ( startmode = = SM_LOCKED )
atomic_set ( & cs - > mstate , MS_LOCKED ) ;
/* save address of controller structure */
usb_set_intfdata ( interface , cs ) ;
2006-04-11 09:55:07 +04:00
if ( ! gigaset_start ( cs ) )
goto error ;
2006-03-26 13:38:34 +04:00
return 0 ;
error :
freeurbs ( cs ) ;
gigaset_unassign ( cs ) ;
return - ENODEV ;
}
/* gigaset_disconnect
* This function is called when the Gigaset base is unplugged .
*/
static void gigaset_disconnect ( struct usb_interface * interface )
{
struct cardstate * cs ;
struct bas_cardstate * ucs ;
cs = usb_get_intfdata ( interface ) ;
ucs = cs - > hw . bas ;
2006-04-11 09:55:07 +04:00
dev_info ( cs - > dev , " disconnecting Gigaset base \n " ) ;
2006-03-26 13:38:34 +04:00
gigaset_stop ( cs ) ;
freeurbs ( cs ) ;
2006-04-11 09:55:07 +04:00
usb_set_intfdata ( interface , NULL ) ;
2006-03-26 13:38:34 +04:00
kfree ( ucs - > rcvbuf ) ;
ucs - > rcvbuf = NULL ;
ucs - > rcvbuf_size = 0 ;
atomic_set ( & ucs - > basstate , 0 ) ;
2006-04-11 09:55:07 +04:00
usb_put_dev ( ucs - > udev ) ;
ucs - > interface = NULL ;
ucs - > udev = NULL ;
cs - > dev = NULL ;
2006-03-26 13:38:34 +04:00
gigaset_unassign ( cs ) ;
}
static struct gigaset_ops gigops = {
gigaset_write_cmd ,
gigaset_write_room ,
gigaset_chars_in_buffer ,
gigaset_brkchars ,
gigaset_init_bchannel ,
gigaset_close_bchannel ,
gigaset_initbcshw ,
gigaset_freebcshw ,
gigaset_reinitbcshw ,
gigaset_initcshw ,
gigaset_freecshw ,
gigaset_set_modem_ctrl ,
gigaset_baud_rate ,
gigaset_set_line_ctrl ,
gigaset_isoc_send_skb ,
gigaset_isoc_input ,
} ;
/* bas_gigaset_init
* This function is called after the kernel module is loaded .
*/
static int __init bas_gigaset_init ( void )
{
int result ;
/* allocate memory for our driver state and intialize it */
if ( ( driver = gigaset_initdriver ( GIGASET_MINOR , GIGASET_MINORS ,
2006-04-11 09:55:04 +04:00
GIGASET_MODULENAME , GIGASET_DEVNAME ,
GIGASET_DEVFSNAME , & gigops ,
THIS_MODULE ) ) = = NULL )
2006-03-26 13:38:34 +04:00
goto error ;
/* allocate memory for our device state and intialize it */
2006-04-11 09:55:00 +04:00
cardstate = gigaset_initcs ( driver , 2 , 0 , 0 , cidmode ,
GIGASET_MODULENAME ) ;
2006-03-26 13:38:34 +04:00
if ( ! cardstate )
goto error ;
/* register this driver with the USB subsystem */
result = usb_register ( & gigaset_usb_driver ) ;
if ( result < 0 ) {
err ( " usb_register failed (error %d) " , - result ) ;
goto error ;
}
info ( DRIVER_AUTHOR ) ;
info ( DRIVER_DESC ) ;
return 0 ;
error : if ( cardstate )
gigaset_freecs ( cardstate ) ;
cardstate = NULL ;
if ( driver )
gigaset_freedriver ( driver ) ;
driver = NULL ;
return - 1 ;
}
/* bas_gigaset_exit
* This function is called before the kernel module is unloaded .
*/
static void __exit bas_gigaset_exit ( void )
{
gigaset_blockdriver ( driver ) ; /* => probe will fail
2006-04-11 09:55:04 +04:00
* = > no gigaset_start any more
*/
2006-03-26 13:38:34 +04:00
gigaset_shutdown ( cardstate ) ;
/* from now on, no isdn callback should be possible */
if ( atomic_read ( & cardstate - > hw . bas - > basstate ) & BS_ATOPEN ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY , " closing AT channel " ) ;
2006-03-26 13:38:34 +04:00
if ( req_submit ( cardstate - > bcs ,
2006-04-11 09:55:04 +04:00
HD_CLOSE_ATCHANNEL , 0 , BAS_TIMEOUT ) > = 0 ) {
/* successfully submitted */
//FIXME wait for completion?
2006-03-26 13:38:34 +04:00
}
}
/* deregister this driver with the USB subsystem */
usb_deregister ( & gigaset_usb_driver ) ;
/* this will call the disconnect-callback */
/* from now on, no disconnect/probe callback should be running */
gigaset_freecs ( cardstate ) ;
cardstate = NULL ;
gigaset_freedriver ( driver ) ;
driver = NULL ;
}
module_init ( bas_gigaset_init ) ;
module_exit ( bas_gigaset_exit ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;