2005-04-16 15:20:36 -07:00
/*
* Driver for ST5481 USB ISDN modem
*
* Author Frode Isaksen
* Copyright 2001 by Frode Isaksen < fisaksen @ bewan . com >
* 2001 by Kai Germaschewski < kai . germaschewski @ gmx . de >
*
* This software may be used and distributed according to the terms
* of the GNU General Public License , incorporated herein by reference .
*
*/
# include <linux/init.h>
# include <linux/usb.h>
# include <linux/slab.h>
# include "st5481.h"
2005-06-25 14:59:18 -07:00
static int st5481_isoc_flatten ( struct urb * urb ) ;
2005-04-16 15:20:36 -07:00
/* ======================================================================
* control pipe
*/
/*
* Send the next endpoint 0 request stored in the FIFO .
* Called either by the completion or by usb_ctrl_msg .
*/
static void usb_next_ctrl_msg ( struct urb * urb ,
struct st5481_adapter * adapter )
{
struct st5481_ctrl * ctrl = & adapter - > ctrl ;
int r_index ;
if ( test_and_set_bit ( 0 , & ctrl - > busy ) ) {
return ;
}
if ( ( r_index = fifo_remove ( & ctrl - > msg_fifo . f ) ) < 0 ) {
test_and_clear_bit ( 0 , & ctrl - > busy ) ;
return ;
}
urb - > setup_packet =
( unsigned char * ) & ctrl - > msg_fifo . data [ r_index ] ;
DBG ( 1 , " request=0x%02x,value=0x%04x,index=%x " ,
( ( struct ctrl_msg * ) urb - > setup_packet ) - > dr . bRequest ,
( ( struct ctrl_msg * ) urb - > setup_packet ) - > dr . wValue ,
( ( struct ctrl_msg * ) urb - > setup_packet ) - > dr . wIndex ) ;
// Prepare the URB
urb - > dev = adapter - > usb_dev ;
SUBMIT_URB ( urb , GFP_ATOMIC ) ;
}
/*
* Asynchronous endpoint 0 request ( async version of usb_control_msg ) .
* The request will be queued up in a FIFO if the endpoint is busy .
*/
2005-06-25 14:59:18 -07:00
static void usb_ctrl_msg ( struct st5481_adapter * adapter ,
u8 request , u8 requesttype , u16 value , u16 index ,
ctrl_complete_t complete , void * context )
2005-04-16 15:20:36 -07:00
{
struct st5481_ctrl * ctrl = & adapter - > ctrl ;
int w_index ;
struct ctrl_msg * ctrl_msg ;
if ( ( w_index = fifo_add ( & ctrl - > msg_fifo . f ) ) < 0 ) {
WARN ( " control msg FIFO full " ) ;
return ;
}
ctrl_msg = & ctrl - > msg_fifo . data [ w_index ] ;
ctrl_msg - > dr . bRequestType = requesttype ;
ctrl_msg - > dr . bRequest = request ;
ctrl_msg - > dr . wValue = cpu_to_le16p ( & value ) ;
ctrl_msg - > dr . wIndex = cpu_to_le16p ( & index ) ;
ctrl_msg - > dr . wLength = 0 ;
ctrl_msg - > complete = complete ;
ctrl_msg - > context = context ;
usb_next_ctrl_msg ( ctrl - > urb , adapter ) ;
}
/*
* Asynchronous endpoint 0 device request .
*/
void st5481_usb_device_ctrl_msg ( struct st5481_adapter * adapter ,
u8 request , u16 value ,
ctrl_complete_t complete , void * context )
{
usb_ctrl_msg ( adapter , request ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
value , 0 , complete , context ) ;
}
/*
* Asynchronous pipe reset ( async version of usb_clear_halt ) .
*/
void st5481_usb_pipe_reset ( struct st5481_adapter * adapter ,
u_char pipe ,
ctrl_complete_t complete , void * context )
{
DBG ( 1 , " pipe=%02x " , pipe ) ;
usb_ctrl_msg ( adapter ,
USB_REQ_CLEAR_FEATURE , USB_DIR_OUT | USB_RECIP_ENDPOINT ,
0 , pipe , complete , context ) ;
}
/*
Physical level functions
*/
void st5481_ph_command ( struct st5481_adapter * adapter , unsigned int command )
{
DBG ( 8 , " command=%s " , ST5481_CMD_string ( command ) ) ;
st5481_usb_device_ctrl_msg ( adapter , TXCI , command , NULL , NULL ) ;
}
/*
* The request on endpoint 0 has completed .
* Call the user provided completion routine and try
* to send the next request .
*/
static void usb_ctrl_complete ( struct urb * urb , struct pt_regs * regs )
{
struct st5481_adapter * adapter = urb - > context ;
struct st5481_ctrl * ctrl = & adapter - > ctrl ;
struct ctrl_msg * ctrl_msg ;
if ( unlikely ( urb - > status < 0 ) ) {
2005-09-17 23:52:42 +02:00
switch ( urb - > status ) {
case - ENOENT :
case - ESHUTDOWN :
case - ECONNRESET :
DBG ( 1 , " urb killed status %d " , urb - > status ) ;
return ; // Give up
default :
WARN ( " urb status %d " , urb - > status ) ;
break ;
2005-04-16 15:20:36 -07:00
}
}
ctrl_msg = ( struct ctrl_msg * ) urb - > setup_packet ;
if ( ctrl_msg - > dr . bRequest = = USB_REQ_CLEAR_FEATURE ) {
/* Special case handling for pipe reset */
le16_to_cpus ( & ctrl_msg - > dr . wIndex ) ;
/* toggle is reset on clear */
usb_settoggle ( adapter - > usb_dev ,
ctrl_msg - > dr . wIndex & ~ USB_DIR_IN ,
( ctrl_msg - > dr . wIndex & USB_DIR_IN ) = = 0 ,
0 ) ;
}
if ( ctrl_msg - > complete )
ctrl_msg - > complete ( ctrl_msg - > context ) ;
clear_bit ( 0 , & ctrl - > busy ) ;
// Try to send next control message
usb_next_ctrl_msg ( urb , adapter ) ;
return ;
}
/* ======================================================================
* interrupt pipe
*/
/*
* The interrupt endpoint will be called when any
* of the 6 registers changes state ( depending on masks ) .
* Decode the register values and schedule a private event .
* Called at interrupt .
*/
static void usb_int_complete ( struct urb * urb , struct pt_regs * regs )
{
u8 * data = urb - > transfer_buffer ;
u8 irqbyte ;
struct st5481_adapter * adapter = urb - > context ;
int j ;
int status ;
switch ( urb - > status ) {
2005-09-17 23:52:42 +02:00
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
/* this urb is terminated, clean up */
DBG ( 2 , " urb shutting down with status: %d " , urb - > status ) ;
return ;
default :
WARN ( " nonzero urb status received: %d " , urb - > status ) ;
goto exit ;
2005-04-16 15:20:36 -07:00
}
2005-09-17 23:52:42 +02:00
DBG_PACKET ( 2 , data , INT_PKT_SIZE ) ;
2005-04-16 15:20:36 -07:00
if ( urb - > actual_length = = 0 ) {
goto exit ;
}
irqbyte = data [ MPINT ] ;
if ( irqbyte & DEN_INT )
FsmEvent ( & adapter - > d_out . fsm , EV_DOUT_DEN , NULL ) ;
if ( irqbyte & DCOLL_INT )
FsmEvent ( & adapter - > d_out . fsm , EV_DOUT_COLL , NULL ) ;
irqbyte = data [ FFINT_D ] ;
if ( irqbyte & OUT_UNDERRUN )
FsmEvent ( & adapter - > d_out . fsm , EV_DOUT_UNDERRUN , NULL ) ;
if ( irqbyte & OUT_DOWN )
; // printk("OUT_DOWN\n");
irqbyte = data [ MPINT ] ;
if ( irqbyte & RXCI_INT )
FsmEvent ( & adapter - > l1m , data [ CCIST ] & 0x0f , NULL ) ;
for ( j = 0 ; j < 2 ; j + + )
adapter - > bcs [ j ] . b_out . flow_event | = data [ FFINT_B1 + j ] ;
urb - > actual_length = 0 ;
exit :
status = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( status )
WARN ( " usb_submit_urb failed with result %d " , status ) ;
}
/* ======================================================================
* initialization
*/
int st5481_setup_usb ( struct st5481_adapter * adapter )
{
struct usb_device * dev = adapter - > usb_dev ;
struct st5481_ctrl * ctrl = & adapter - > ctrl ;
struct st5481_intr * intr = & adapter - > intr ;
struct usb_interface * intf ;
struct usb_host_interface * altsetting = NULL ;
struct usb_host_endpoint * endpoint ;
int status ;
struct urb * urb ;
u8 * buf ;
2005-09-17 23:52:42 +02:00
DBG ( 2 , " " ) ;
2005-04-16 15:20:36 -07:00
if ( ( status = usb_reset_configuration ( dev ) ) < 0 ) {
WARN ( " reset_configuration failed,status=%d " , status ) ;
return status ;
}
intf = usb_ifnum_to_if ( dev , 0 ) ;
if ( intf )
altsetting = usb_altnum_to_altsetting ( intf , 3 ) ;
if ( ! altsetting )
return - ENXIO ;
// Check if the config is sane
if ( altsetting - > desc . bNumEndpoints ! = 7 ) {
WARN ( " expecting 7 got %d endpoints! " , altsetting - > desc . bNumEndpoints ) ;
return - EINVAL ;
}
// The descriptor is wrong for some early samples of the ST5481 chip
altsetting - > endpoint [ 3 ] . desc . wMaxPacketSize = __constant_cpu_to_le16 ( 32 ) ;
altsetting - > endpoint [ 4 ] . desc . wMaxPacketSize = __constant_cpu_to_le16 ( 32 ) ;
// Use alternative setting 3 on interface 0 to have 2B+D
if ( ( status = usb_set_interface ( dev , 0 , 3 ) ) < 0 ) {
WARN ( " usb_set_interface failed,status=%d " , status ) ;
return status ;
}
// Allocate URB for control endpoint
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! urb ) {
return - ENOMEM ;
}
ctrl - > urb = urb ;
// Fill the control URB
usb_fill_control_urb ( urb , dev ,
usb_sndctrlpipe ( dev , 0 ) ,
NULL , NULL , 0 , usb_ctrl_complete , adapter ) ;
fifo_init ( & ctrl - > msg_fifo . f , ARRAY_SIZE ( ctrl - > msg_fifo . data ) ) ;
// Allocate URBs and buffers for interrupt endpoint
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! urb ) {
return - ENOMEM ;
}
intr - > urb = urb ;
buf = kmalloc ( INT_PKT_SIZE , GFP_KERNEL ) ;
if ( ! buf ) {
return - ENOMEM ;
}
endpoint = & altsetting - > endpoint [ EP_INT - 1 ] ;
// Fill the interrupt URB
usb_fill_int_urb ( urb , dev ,
usb_rcvintpipe ( dev , endpoint - > desc . bEndpointAddress ) ,
buf , INT_PKT_SIZE ,
usb_int_complete , adapter ,
endpoint - > desc . bInterval ) ;
return 0 ;
}
/*
* Release buffers and URBs for the interrupt and control
* endpoint .
*/
void st5481_release_usb ( struct st5481_adapter * adapter )
{
struct st5481_intr * intr = & adapter - > intr ;
struct st5481_ctrl * ctrl = & adapter - > ctrl ;
DBG ( 1 , " " ) ;
// Stop and free Control and Interrupt URBs
2005-09-17 23:52:42 +02:00
usb_kill_urb ( ctrl - > urb ) ;
2005-11-07 01:01:29 -08:00
kfree ( ctrl - > urb - > transfer_buffer ) ;
2005-04-16 15:20:36 -07:00
usb_free_urb ( ctrl - > urb ) ;
2005-09-17 23:52:42 +02:00
ctrl - > urb = NULL ;
2005-04-16 15:20:36 -07:00
2005-09-17 23:52:42 +02:00
usb_kill_urb ( intr - > urb ) ;
2005-11-07 01:01:29 -08:00
kfree ( intr - > urb - > transfer_buffer ) ;
2005-04-16 15:20:36 -07:00
usb_free_urb ( intr - > urb ) ;
2005-09-17 23:52:42 +02:00
ctrl - > urb = NULL ;
2005-04-16 15:20:36 -07:00
}
/*
* Initialize the adapter .
*/
void st5481_start ( struct st5481_adapter * adapter )
{
static const u8 init_cmd_table [ ] = {
SET_DEFAULT , 0 ,
STT , 0 ,
SDA_MIN , 0x0d ,
SDA_MAX , 0x29 ,
SDELAY_VALUE , 0x14 ,
GPIO_DIR , 0x01 ,
GPIO_OUT , RED_LED ,
// FFCTRL_OUT_D,4,
// FFCTRH_OUT_D,12,
FFCTRL_OUT_B1 , 6 ,
FFCTRH_OUT_B1 , 20 ,
FFCTRL_OUT_B2 , 6 ,
FFCTRH_OUT_B2 , 20 ,
MPMSK , RXCI_INT + DEN_INT + DCOLL_INT ,
0
} ;
struct st5481_intr * intr = & adapter - > intr ;
int i = 0 ;
u8 request , value ;
DBG ( 8 , " " ) ;
adapter - > leds = RED_LED ;
// Start receiving on the interrupt endpoint
SUBMIT_URB ( intr - > urb , GFP_KERNEL ) ;
while ( ( request = init_cmd_table [ i + + ] ) ) {
value = init_cmd_table [ i + + ] ;
st5481_usb_device_ctrl_msg ( adapter , request , value , NULL , NULL ) ;
}
st5481_ph_command ( adapter , ST5481_CMD_PUP ) ;
}
/*
* Reset the adapter to default values .
*/
void st5481_stop ( struct st5481_adapter * adapter )
{
DBG ( 8 , " " ) ;
st5481_usb_device_ctrl_msg ( adapter , SET_DEFAULT , 0 , NULL , NULL ) ;
}
/* ======================================================================
* isochronous USB helpers
*/
static void
fill_isoc_urb ( struct urb * urb , struct usb_device * dev ,
unsigned int pipe , void * buf , int num_packets ,
int packet_size , usb_complete_t complete ,
void * context )
{
int k ;
spin_lock_init ( & urb - > lock ) ;
urb - > dev = dev ;
urb - > pipe = pipe ;
2005-09-17 23:52:42 +02:00
urb - > interval = 1 ;
2005-04-16 15:20:36 -07:00
urb - > transfer_buffer = buf ;
urb - > number_of_packets = num_packets ;
urb - > transfer_buffer_length = num_packets * packet_size ;
urb - > actual_length = 0 ;
urb - > complete = complete ;
urb - > context = context ;
urb - > transfer_flags = URB_ISO_ASAP ;
for ( k = 0 ; k < num_packets ; k + + ) {
urb - > iso_frame_desc [ k ] . offset = packet_size * k ;
urb - > iso_frame_desc [ k ] . length = packet_size ;
urb - > iso_frame_desc [ k ] . actual_length = 0 ;
}
}
int
st5481_setup_isocpipes ( struct urb * urb [ 2 ] , struct usb_device * dev ,
unsigned int pipe , int num_packets ,
int packet_size , int buf_size ,
usb_complete_t complete , void * context )
{
int j , retval ;
unsigned char * buf ;
for ( j = 0 ; j < 2 ; j + + ) {
retval = - ENOMEM ;
urb [ j ] = usb_alloc_urb ( num_packets , GFP_KERNEL ) ;
if ( ! urb [ j ] )
goto err ;
// Allocate memory for 2000bytes/sec (16Kb/s)
buf = kmalloc ( buf_size , GFP_KERNEL ) ;
if ( ! buf )
goto err ;
// Fill the isochronous URB
fill_isoc_urb ( urb [ j ] , dev , pipe , buf ,
num_packets , packet_size , complete ,
context ) ;
}
return 0 ;
err :
for ( j = 0 ; j < 2 ; j + + ) {
if ( urb [ j ] ) {
2005-11-07 01:01:29 -08:00
kfree ( urb [ j ] - > transfer_buffer ) ;
2005-09-17 23:52:42 +02:00
urb [ j ] - > transfer_buffer = NULL ;
2005-04-16 15:20:36 -07:00
usb_free_urb ( urb [ j ] ) ;
2005-09-17 23:52:42 +02:00
urb [ j ] = NULL ;
2005-04-16 15:20:36 -07:00
}
}
return retval ;
}
void st5481_release_isocpipes ( struct urb * urb [ 2 ] )
{
int j ;
for ( j = 0 ; j < 2 ; j + + ) {
2005-09-17 23:52:42 +02:00
usb_kill_urb ( urb [ j ] ) ;
2005-11-07 01:01:29 -08:00
kfree ( urb [ j ] - > transfer_buffer ) ;
2005-04-16 15:20:36 -07:00
usb_free_urb ( urb [ j ] ) ;
2005-09-17 23:52:42 +02:00
urb [ j ] = NULL ;
2005-04-16 15:20:36 -07:00
}
}
/*
* Decode frames received on the B / D channel .
* Note that this function will be called continously
* with 64 Kbit / s / 16 Kbit / s of data and hence it will be
* called 50 times per second with 20 ISOC descriptors .
* Called at interrupt .
*/
static void usb_in_complete ( struct urb * urb , struct pt_regs * regs )
{
struct st5481_in * in = urb - > context ;
unsigned char * ptr ;
struct sk_buff * skb ;
int len , count , status ;
if ( unlikely ( urb - > status < 0 ) ) {
2005-09-17 23:52:42 +02:00
switch ( urb - > status ) {
case - ENOENT :
case - ESHUTDOWN :
case - ECONNRESET :
DBG ( 1 , " urb killed status %d " , urb - > status ) ;
return ; // Give up
default :
WARN ( " urb status %d " , urb - > status ) ;
break ;
2005-04-16 15:20:36 -07:00
}
}
DBG_ISO_PACKET ( 0x80 , urb ) ;
len = st5481_isoc_flatten ( urb ) ;
ptr = urb - > transfer_buffer ;
while ( len > 0 ) {
if ( in - > mode = = L1_MODE_TRANS ) {
memcpy ( in - > rcvbuf , ptr , len ) ;
status = len ;
len = 0 ;
} else {
status = isdnhdlc_decode ( & in - > hdlc_state , ptr , len , & count ,
in - > rcvbuf , in - > bufsize ) ;
ptr + = count ;
len - = count ;
}
if ( status > 0 ) {
// Good frame received
DBG ( 4 , " count=%d " , status ) ;
DBG_PACKET ( 0x400 , in - > rcvbuf , status ) ;
if ( ! ( skb = dev_alloc_skb ( status ) ) ) {
WARN ( " receive out of memory \n " ) ;
break ;
}
memcpy ( skb_put ( skb , status ) , in - > rcvbuf , status ) ;
in - > hisax_if - > l1l2 ( in - > hisax_if , PH_DATA | INDICATION , skb ) ;
} else if ( status = = - HDLC_CRC_ERROR ) {
INFO ( " CRC error " ) ;
} else if ( status = = - HDLC_FRAMING_ERROR ) {
INFO ( " framing error " ) ;
} else if ( status = = - HDLC_LENGTH_ERROR ) {
INFO ( " length error " ) ;
}
}
// Prepare URB for next transfer
urb - > dev = in - > adapter - > usb_dev ;
urb - > actual_length = 0 ;
SUBMIT_URB ( urb , GFP_ATOMIC ) ;
}
int st5481_setup_in ( struct st5481_in * in )
{
struct usb_device * dev = in - > adapter - > usb_dev ;
int retval ;
DBG ( 4 , " " ) ;
in - > rcvbuf = kmalloc ( in - > bufsize , GFP_KERNEL ) ;
retval = - ENOMEM ;
if ( ! in - > rcvbuf )
goto err ;
retval = st5481_setup_isocpipes ( in - > urb , dev ,
usb_rcvisocpipe ( dev , in - > ep ) ,
in - > num_packets , in - > packet_size ,
in - > num_packets * in - > packet_size ,
usb_in_complete , in ) ;
if ( retval )
goto err_free ;
return 0 ;
err_free :
kfree ( in - > rcvbuf ) ;
err :
return retval ;
}
void st5481_release_in ( struct st5481_in * in )
{
DBG ( 2 , " " ) ;
st5481_release_isocpipes ( in - > urb ) ;
}
/*
* Make the transfer_buffer contiguous by
* copying from the iso descriptors if necessary .
*/
2005-06-25 14:59:18 -07:00
static int st5481_isoc_flatten ( struct urb * urb )
2005-04-16 15:20:36 -07:00
{
struct usb_iso_packet_descriptor * pipd , * pend ;
unsigned char * src , * dst ;
unsigned int len ;
if ( urb - > status < 0 ) {
return urb - > status ;
}
for ( pipd = & urb - > iso_frame_desc [ 0 ] ,
pend = & urb - > iso_frame_desc [ urb - > number_of_packets ] ,
dst = urb - > transfer_buffer ;
pipd < pend ;
pipd + + ) {
if ( pipd - > status < 0 ) {
return ( pipd - > status ) ;
}
len = pipd - > actual_length ;
pipd - > actual_length = 0 ;
src = urb - > transfer_buffer + pipd - > offset ;
if ( src ! = dst ) {
// Need to copy since isoc buffers not full
while ( len - - ) {
* dst + + = * src + + ;
}
} else {
// No need to copy, just update destination buffer
dst + = len ;
}
}
// Return size of flattened buffer
return ( dst - ( unsigned char * ) urb - > transfer_buffer ) ;
}
static void st5481_start_rcv ( void * context )
{
struct st5481_in * in = context ;
struct st5481_adapter * adapter = in - > adapter ;
DBG ( 4 , " " ) ;
in - > urb [ 0 ] - > dev = adapter - > usb_dev ;
SUBMIT_URB ( in - > urb [ 0 ] , GFP_KERNEL ) ;
in - > urb [ 1 ] - > dev = adapter - > usb_dev ;
SUBMIT_URB ( in - > urb [ 1 ] , GFP_KERNEL ) ;
}
void st5481_in_mode ( struct st5481_in * in , int mode )
{
if ( in - > mode = = mode )
return ;
in - > mode = mode ;
usb_unlink_urb ( in - > urb [ 0 ] ) ;
usb_unlink_urb ( in - > urb [ 1 ] ) ;
if ( in - > mode ! = L1_MODE_NULL ) {
if ( in - > mode ! = L1_MODE_TRANS )
isdnhdlc_rcv_init ( & in - > hdlc_state ,
in - > mode = = L1_MODE_HDLC_56K ) ;
st5481_usb_pipe_reset ( in - > adapter , in - > ep , NULL , NULL ) ;
st5481_usb_device_ctrl_msg ( in - > adapter , in - > counter ,
in - > packet_size ,
NULL , NULL ) ;
st5481_start_rcv ( in ) ;
} else {
st5481_usb_device_ctrl_msg ( in - > adapter , in - > counter ,
0 , NULL , NULL ) ;
}
}