2007-02-10 17:25:27 +03:00
/**
* This file contains functions used in USB interface module .
*/
# include <linux/delay.h>
# include <linux/firmware.h>
# include <linux/netdevice.h>
# include <linux/usb.h>
# include "host.h"
# include "sbi.h"
# include "decl.h"
# include "defs.h"
# include "dev.h"
# include "if_usb.h"
# define MESSAGE_HEADER_LEN 4
static const char usbdriver_name [ ] = " usb8xxx " ;
2007-05-25 20:01:42 +04:00
# define MAX_DEVS 5
static struct net_device * libertas_devs [ MAX_DEVS ] ;
static int libertas_found = 0 ;
2007-02-10 17:25:27 +03:00
static struct usb_device_id if_usb_table [ ] = {
/* Enter the device signature inside */
2007-05-25 08:11:58 +04:00
{ USB_DEVICE ( 0x1286 , 0x2001 ) } ,
{ USB_DEVICE ( 0x05a3 , 0x8388 ) } ,
2007-02-10 17:25:27 +03:00
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , if_usb_table ) ;
static void if_usb_receive ( struct urb * urb ) ;
static void if_usb_receive_fwload ( struct urb * urb ) ;
2007-05-25 20:01:42 +04:00
static int reset_device ( wlan_private * priv ) ;
2007-02-10 17:25:27 +03:00
/**
* @ brief call back function to handle the status of the URB
* @ param urb pointer to urb structure
* @ return N / A
*/
static void if_usb_write_bulk_callback ( struct urb * urb )
{
wlan_private * priv = ( wlan_private * ) ( urb - > context ) ;
wlan_adapter * adapter = priv - > adapter ;
struct net_device * dev = priv - > wlan_dev . netdev ;
/* handle the transmission complete validations */
if ( urb - > status ! = 0 ) {
/* print the failure status number for debug */
lbs_pr_info ( " URB in failure status \n " ) ;
} else {
2007-05-25 19:27:16 +04:00
/*
lbs_deb_usbd ( & urb - > dev - > dev , " URB status is successfull \n " ) ;
lbs_deb_usbd ( & urb - > dev - > dev , " Actual length transmitted %d \n " ,
2007-02-10 17:25:27 +03:00
urb - > actual_length ) ;
2007-05-25 19:27:16 +04:00
*/
2007-02-10 17:25:27 +03:00
priv - > wlan_dev . dnld_sent = DNLD_RES_RECEIVED ;
/* Wake main thread if commands are pending */
if ( ! adapter - > cur_cmd )
wake_up_interruptible ( & priv - > mainthread . waitq ) ;
2007-05-25 20:06:56 +04:00
if ( ( adapter - > connect_status = = libertas_connected ) ) {
2007-02-10 17:25:27 +03:00
netif_wake_queue ( dev ) ;
2007-05-25 20:06:56 +04:00
netif_wake_queue ( priv - > mesh_dev ) ;
}
2007-02-10 17:25:27 +03:00
}
return ;
}
/**
* @ brief free tx / rx urb , skb and rx buffer
* @ param cardp pointer usb_card_rec
* @ return N / A
*/
void if_usb_free ( struct usb_card_rec * cardp )
{
2007-05-25 19:27:16 +04:00
lbs_deb_enter ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
/* Unlink tx & rx urb */
usb_kill_urb ( cardp - > tx_urb ) ;
usb_kill_urb ( cardp - > rx_urb ) ;
usb_free_urb ( cardp - > tx_urb ) ;
cardp - > tx_urb = NULL ;
usb_free_urb ( cardp - > rx_urb ) ;
cardp - > rx_urb = NULL ;
kfree ( cardp - > bulk_out_buffer ) ;
cardp - > bulk_out_buffer = NULL ;
2007-05-25 19:27:16 +04:00
lbs_deb_leave ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
}
/**
* @ brief sets the configuration values
* @ param ifnum interface number
* @ param id pointer to usb_device_id
* @ return 0 on success , error code on failure
*/
static int if_usb_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
struct usb_device * udev ;
struct usb_host_interface * iface_desc ;
struct usb_endpoint_descriptor * endpoint ;
2007-05-25 19:49:19 +04:00
wlan_private * priv ;
2007-02-10 17:25:27 +03:00
struct usb_card_rec * usb_cardp ;
int i ;
udev = interface_to_usbdev ( intf ) ;
usb_cardp = kzalloc ( sizeof ( struct usb_card_rec ) , GFP_KERNEL ) ;
if ( ! usb_cardp ) {
lbs_pr_err ( " Out of memory allocating private data. \n " ) ;
goto error ;
}
usb_cardp - > udev = udev ;
iface_desc = intf - > cur_altsetting ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & udev - > dev , " bcdUSB = 0x%X bDeviceClass = 0x%X "
2007-02-10 17:25:27 +03:00
" bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X \n " ,
udev - > descriptor . bcdUSB ,
udev - > descriptor . bDeviceClass ,
udev - > descriptor . bDeviceSubClass ,
udev - > descriptor . bDeviceProtocol ) ;
for ( i = 0 ; i < iface_desc - > desc . bNumEndpoints ; + + i ) {
endpoint = & iface_desc - > endpoint [ i ] . desc ;
if ( ( endpoint - > bEndpointAddress & USB_ENDPOINT_DIR_MASK )
& & ( ( endpoint - > bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) = =
USB_ENDPOINT_XFER_BULK ) ) {
/* we found a bulk in endpoint */
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & udev - > dev , " Bulk in size is %d \n " ,
2007-02-10 17:25:27 +03:00
endpoint - > wMaxPacketSize ) ;
if ( !
( usb_cardp - > rx_urb =
usb_alloc_urb ( 0 , GFP_KERNEL ) ) ) {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & udev - > dev ,
2007-02-10 17:25:27 +03:00
" Rx URB allocation failed \n " ) ;
goto dealloc ;
}
usb_cardp - > rx_urb_recall = 0 ;
usb_cardp - > bulk_in_size =
endpoint - > wMaxPacketSize ;
usb_cardp - > bulk_in_endpointAddr =
( endpoint - >
bEndpointAddress & USB_ENDPOINT_NUMBER_MASK ) ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & udev - > dev , " in_endpoint = %d \n " ,
2007-02-10 17:25:27 +03:00
endpoint - > bEndpointAddress ) ;
}
if ( ( ( endpoint - >
bEndpointAddress & USB_ENDPOINT_DIR_MASK ) = =
USB_DIR_OUT )
& & ( ( endpoint - > bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) = =
USB_ENDPOINT_XFER_BULK ) ) {
/* We found bulk out endpoint */
if ( !
( usb_cardp - > tx_urb =
usb_alloc_urb ( 0 , GFP_KERNEL ) ) ) {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & udev - > dev ,
2007-02-10 17:25:27 +03:00
" Tx URB allocation failed \n " ) ;
goto dealloc ;
}
usb_cardp - > bulk_out_size =
endpoint - > wMaxPacketSize ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & udev - > dev ,
2007-02-10 17:25:27 +03:00
" Bulk out size is %d \n " ,
endpoint - > wMaxPacketSize ) ;
usb_cardp - > bulk_out_endpointAddr =
endpoint - > bEndpointAddress ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & udev - > dev , " out_endpoint = %d \n " ,
2007-02-10 17:25:27 +03:00
endpoint - > bEndpointAddress ) ;
usb_cardp - > bulk_out_buffer =
kmalloc ( MRVDRV_ETH_TX_PACKET_BUFFER_SIZE ,
GFP_KERNEL ) ;
if ( ! usb_cardp - > bulk_out_buffer ) {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & udev - > dev ,
2007-02-10 17:25:27 +03:00
" Could not allocate buffer \n " ) ;
goto dealloc ;
}
}
}
/* At this point wlan_add_card() will be called. Don't worry
* about keeping pwlanpriv around since it will be set on our
* usb device data in - > add ( ) - > libertas_sbi_register_dev ( ) .
*/
2007-05-25 19:49:19 +04:00
if ( ! ( priv = wlan_add_card ( usb_cardp ) ) )
goto dealloc ;
2007-05-25 20:01:42 +04:00
2007-05-25 20:06:56 +04:00
if ( wlan_add_mesh ( priv ) )
2007-05-25 20:09:13 +04:00
goto err_add_mesh ;
2007-05-25 20:06:56 +04:00
2007-05-25 20:04:31 +04:00
if ( libertas_activate_card ( priv ) )
2007-05-25 20:09:13 +04:00
goto err_activate_card ;
2007-05-25 20:04:31 +04:00
2007-05-25 20:01:42 +04:00
if ( libertas_found < MAX_DEVS ) {
libertas_devs [ libertas_found ] = priv - > wlan_dev . netdev ;
libertas_found + + ;
}
2007-02-10 17:25:27 +03:00
usb_get_dev ( udev ) ;
usb_set_intfdata ( intf , usb_cardp ) ;
/*
* return card structure , which can be got back in the
* diconnect function as the ptr
* argument .
*/
return 0 ;
2007-05-25 20:09:13 +04:00
err_activate_card :
unregister_netdev ( priv - > mesh_dev ) ;
free_netdev ( priv - > mesh_dev ) ;
err_add_mesh :
free_netdev ( priv - > wlan_dev . netdev ) ;
kfree ( priv - > adapter ) ;
2007-02-10 17:25:27 +03:00
dealloc :
if_usb_free ( usb_cardp ) ;
error :
return - ENOMEM ;
}
/**
* @ brief free resource and cleanup
* @ param udev pointer to usb_device
* @ param ptr pointer to usb_cardp
* @ return N / A
*/
static void if_usb_disconnect ( struct usb_interface * intf )
{
struct usb_card_rec * cardp = usb_get_intfdata ( intf ) ;
wlan_private * priv = ( wlan_private * ) cardp - > priv ;
wlan_adapter * adapter = NULL ;
2007-05-25 20:01:42 +04:00
int i ;
2007-02-10 17:25:27 +03:00
adapter = priv - > adapter ;
/*
* Update Surprise removed to TRUE
*/
adapter - > surpriseremoved = 1 ;
2007-05-25 20:01:42 +04:00
for ( i = 0 ; i < libertas_found ; i + + ) {
if ( libertas_devs [ i ] = = priv - > wlan_dev . netdev ) {
libertas_devs [ i ] = libertas_devs [ - - libertas_found ] ;
libertas_devs [ libertas_found ] = NULL ;
break ;
}
}
2007-02-10 17:25:27 +03:00
/* card is removed and we can call wlan_remove_card */
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev , " call remove card \n " ) ;
2007-05-25 19:49:19 +04:00
wlan_remove_mesh ( priv ) ;
2007-05-25 19:55:19 +04:00
wlan_remove_card ( priv ) ;
2007-02-10 17:25:27 +03:00
/* Unlink and free urb */
if_usb_free ( cardp ) ;
usb_set_intfdata ( intf , NULL ) ;
usb_put_dev ( interface_to_usbdev ( intf ) ) ;
return ;
}
/**
* @ brief This function download FW
* @ param priv pointer to wlan_private
* @ return 0
*/
static int if_prog_firmware ( wlan_private * priv )
{
struct usb_card_rec * cardp = priv - > wlan_dev . card ;
struct FWData * fwdata ;
struct fwheader * fwheader ;
u8 * firmware = priv - > firmware - > data ;
fwdata = kmalloc ( sizeof ( struct FWData ) , GFP_ATOMIC ) ;
if ( ! fwdata )
return - 1 ;
fwheader = & fwdata - > fwheader ;
if ( ! cardp - > CRC_OK ) {
cardp - > totalbytes = cardp - > fwlastblksent ;
cardp - > fwseqnum = cardp - > lastseqnum - 1 ;
}
2007-05-25 19:27:16 +04:00
/*
lbs_deb_usbd ( & cardp - > udev - > dev , " totalbytes = %d \n " ,
2007-02-10 17:25:27 +03:00
cardp - > totalbytes ) ;
2007-05-25 19:27:16 +04:00
*/
2007-02-10 17:25:27 +03:00
memcpy ( fwheader , & firmware [ cardp - > totalbytes ] ,
sizeof ( struct fwheader ) ) ;
cardp - > fwlastblksent = cardp - > totalbytes ;
cardp - > totalbytes + = sizeof ( struct fwheader ) ;
2007-05-25 19:27:16 +04:00
/* lbs_deb_usbd(&cardp->udev->dev,"Copy Data\n"); */
2007-02-10 17:25:27 +03:00
memcpy ( fwdata - > data , & firmware [ cardp - > totalbytes ] ,
fwdata - > fwheader . datalength ) ;
2007-05-25 19:27:16 +04:00
/*
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" Data length = %d \n " , fwdata - > fwheader . datalength ) ;
2007-05-25 19:27:16 +04:00
*/
2007-02-10 17:25:27 +03:00
cardp - > fwseqnum = cardp - > fwseqnum + 1 ;
fwdata - > seqnum = cardp - > fwseqnum ;
cardp - > lastseqnum = fwdata - > seqnum ;
cardp - > totalbytes + = fwdata - > fwheader . datalength ;
if ( fwheader - > dnldcmd = = FW_HAS_DATA_TO_RECV ) {
2007-05-25 19:27:16 +04:00
/*
lbs_deb_usbd ( & cardp - > udev - > dev , " There is data to follow \n " ) ;
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" seqnum = %d totalbytes = %d \n " , cardp - > fwseqnum ,
cardp - > totalbytes ) ;
2007-05-25 19:27:16 +04:00
*/
2007-02-10 17:25:27 +03:00
memcpy ( cardp - > bulk_out_buffer , fwheader , FW_DATA_XMIT_SIZE ) ;
usb_tx_block ( priv , cardp - > bulk_out_buffer , FW_DATA_XMIT_SIZE ) ;
} else if ( fwdata - > fwheader . dnldcmd = = FW_HAS_LAST_BLOCK ) {
2007-05-25 19:27:16 +04:00
/*
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" Host has finished FW downloading \n " ) ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" Donwloading FW JUMP BLOCK \n " ) ;
2007-05-25 19:27:16 +04:00
*/
2007-02-10 17:25:27 +03:00
memcpy ( cardp - > bulk_out_buffer , fwheader , FW_DATA_XMIT_SIZE ) ;
usb_tx_block ( priv , cardp - > bulk_out_buffer , FW_DATA_XMIT_SIZE ) ;
cardp - > fwfinalblk = 1 ;
}
2007-05-25 19:27:16 +04:00
/*
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" The firmware download is done size is %d \n " ,
cardp - > totalbytes ) ;
2007-05-25 19:27:16 +04:00
*/
2007-02-10 17:25:27 +03:00
kfree ( fwdata ) ;
return 0 ;
}
static int libertas_do_reset ( wlan_private * priv )
{
int ret ;
struct usb_card_rec * cardp = priv - > wlan_dev . card ;
2007-05-25 20:01:42 +04:00
lbs_deb_enter ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
ret = usb_reset_device ( cardp - > udev ) ;
if ( ! ret ) {
msleep ( 10 ) ;
reset_device ( priv ) ;
msleep ( 10 ) ;
}
2007-05-25 20:01:42 +04:00
lbs_deb_leave_args ( LBS_DEB_USB , " ret %d " , ret ) ;
2007-02-10 17:25:27 +03:00
return ret ;
}
/**
* @ brief This function transfer the data to the device .
* @ param priv pointer to wlan_private
* @ param payload pointer to payload data
* @ param nb data length
* @ return 0 or - 1
*/
int usb_tx_block ( wlan_private * priv , u8 * payload , u16 nb )
{
/* pointer to card structure */
struct usb_card_rec * cardp = priv - > wlan_dev . card ;
int ret = - 1 ;
/* check if device is removed */
if ( priv - > adapter - > surpriseremoved ) {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev , " Device removed \n " ) ;
2007-02-10 17:25:27 +03:00
goto tx_ret ;
}
usb_fill_bulk_urb ( cardp - > tx_urb , cardp - > udev ,
usb_sndbulkpipe ( cardp - > udev ,
cardp - > bulk_out_endpointAddr ) ,
payload , nb , if_usb_write_bulk_callback , priv ) ;
cardp - > tx_urb - > transfer_flags | = URB_ZERO_PACKET ;
if ( ( ret = usb_submit_urb ( cardp - > tx_urb , GFP_ATOMIC ) ) ) {
/* transfer failed */
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev , " usb_submit_urb failed \n " ) ;
2007-02-10 17:25:27 +03:00
ret = - 1 ;
} else {
2007-05-25 19:27:16 +04:00
/* lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb success\n"); */
2007-02-10 17:25:27 +03:00
ret = 0 ;
}
tx_ret :
return ret ;
}
static int __if_usb_submit_rx_urb ( wlan_private * priv ,
void ( * callbackfn )
( struct urb * urb ) )
{
struct usb_card_rec * cardp = priv - > wlan_dev . card ;
struct sk_buff * skb ;
struct read_cb_info * rinfo = & cardp - > rinfo ;
int ret = - 1 ;
if ( ! ( skb = dev_alloc_skb ( MRVDRV_ETH_RX_PACKET_BUFFER_SIZE ) ) ) {
lbs_pr_err ( " No free skb \n " ) ;
goto rx_ret ;
}
rinfo - > skb = skb ;
/* Fill the receive configuration URB and initialise the Rx call back */
usb_fill_bulk_urb ( cardp - > rx_urb , cardp - > udev ,
usb_rcvbulkpipe ( cardp - > udev ,
cardp - > bulk_in_endpointAddr ) ,
2007-05-11 07:10:18 +04:00
( void * ) ( skb - > tail + ( size_t ) IPFIELD_ALIGN_OFFSET ) ,
2007-02-10 17:25:27 +03:00
MRVDRV_ETH_RX_PACKET_BUFFER_SIZE , callbackfn ,
rinfo ) ;
cardp - > rx_urb - > transfer_flags | = URB_ZERO_PACKET ;
2007-05-25 19:27:16 +04:00
/* lbs_deb_usbd(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); */
2007-02-10 17:25:27 +03:00
if ( ( ret = usb_submit_urb ( cardp - > rx_urb , GFP_ATOMIC ) ) ) {
/* handle failure conditions */
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev , " Submit Rx URB failed \n " ) ;
2007-02-10 17:25:27 +03:00
ret = - 1 ;
} else {
2007-05-25 19:27:16 +04:00
/* lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB success\n"); */
2007-02-10 17:25:27 +03:00
ret = 0 ;
}
rx_ret :
return ret ;
}
static inline int if_usb_submit_rx_urb_fwload ( wlan_private * priv )
{
return __if_usb_submit_rx_urb ( priv , & if_usb_receive_fwload ) ;
}
static inline int if_usb_submit_rx_urb ( wlan_private * priv )
{
return __if_usb_submit_rx_urb ( priv , & if_usb_receive ) ;
}
static void if_usb_receive_fwload ( struct urb * urb )
{
struct read_cb_info * rinfo = ( struct read_cb_info * ) urb - > context ;
wlan_private * priv = rinfo - > priv ;
struct sk_buff * skb = rinfo - > skb ;
struct usb_card_rec * cardp = ( struct usb_card_rec * ) priv - > wlan_dev . card ;
struct fwsyncheader * syncfwheader ;
struct bootcmdrespStr bootcmdresp ;
if ( urb - > status ) {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" URB status is failed during fw load \n " ) ;
kfree_skb ( skb ) ;
return ;
}
if ( cardp - > bootcmdresp = = 0 ) {
memcpy ( & bootcmdresp , skb - > data + IPFIELD_ALIGN_OFFSET ,
sizeof ( bootcmdresp ) ) ;
if ( cardp - > udev - > descriptor . bcdDevice < 0x3106 ) {
kfree_skb ( skb ) ;
if_usb_submit_rx_urb_fwload ( priv ) ;
cardp - > bootcmdresp = 1 ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" Received valid boot command response \n " ) ;
return ;
}
if ( bootcmdresp . u32magicnumber ! = BOOT_CMD_MAGIC_NUMBER ) {
lbs_pr_info (
" boot cmd response wrong magic number (0x%x) \n " ,
bootcmdresp . u32magicnumber ) ;
} else if ( bootcmdresp . u8cmd_tag ! = BOOT_CMD_FW_BY_USB ) {
lbs_pr_info (
" boot cmd response cmd_tag error (%d) \n " ,
bootcmdresp . u8cmd_tag ) ;
} else if ( bootcmdresp . u8result ! = BOOT_CMD_RESP_OK ) {
lbs_pr_info (
" boot cmd response result error (%d) \n " ,
bootcmdresp . u8result ) ;
} else {
cardp - > bootcmdresp = 1 ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" Received valid boot command response \n " ) ;
}
kfree_skb ( skb ) ;
if_usb_submit_rx_urb_fwload ( priv ) ;
return ;
}
syncfwheader = kmalloc ( sizeof ( struct fwsyncheader ) , GFP_ATOMIC ) ;
if ( ! syncfwheader ) {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev , " Failure to allocate syncfwheader \n " ) ;
2007-02-10 17:25:27 +03:00
kfree_skb ( skb ) ;
return ;
}
memcpy ( syncfwheader , skb - > data + IPFIELD_ALIGN_OFFSET ,
sizeof ( struct fwsyncheader ) ) ;
if ( ! syncfwheader - > cmd ) {
2007-05-25 19:27:16 +04:00
/*
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" FW received Blk with correct CRC \n " ) ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" FW received Blk seqnum = %d \n " ,
syncfwheader - > seqnum ) ;
2007-05-25 19:27:16 +04:00
*/
2007-02-10 17:25:27 +03:00
cardp - > CRC_OK = 1 ;
} else {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" FW received Blk with CRC error \n " ) ;
cardp - > CRC_OK = 0 ;
}
kfree_skb ( skb ) ;
if ( cardp - > fwfinalblk ) {
cardp - > fwdnldover = 1 ;
goto exit ;
}
if_prog_firmware ( priv ) ;
if_usb_submit_rx_urb_fwload ( priv ) ;
exit :
kfree ( syncfwheader ) ;
return ;
}
# define MRVDRV_MIN_PKT_LEN 30
static inline void process_cmdtypedata ( int recvlength , struct sk_buff * skb ,
struct usb_card_rec * cardp ,
wlan_private * priv )
{
if ( recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE +
MESSAGE_HEADER_LEN | | recvlength < MRVDRV_MIN_PKT_LEN ) {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" Packet length is Invalid \n " ) ;
kfree_skb ( skb ) ;
return ;
}
skb_reserve ( skb , IPFIELD_ALIGN_OFFSET ) ;
skb_put ( skb , recvlength ) ;
skb_pull ( skb , MESSAGE_HEADER_LEN ) ;
libertas_process_rxed_packet ( priv , skb ) ;
priv - > wlan_dev . upld_len = ( recvlength - MESSAGE_HEADER_LEN ) ;
}
static inline void process_cmdrequest ( int recvlength , u8 * recvbuff ,
struct sk_buff * skb ,
struct usb_card_rec * cardp ,
wlan_private * priv )
{
u8 * cmdbuf ;
if ( recvlength > MRVDRV_SIZE_OF_CMD_BUFFER ) {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" The receive buffer is too large \n " ) ;
kfree_skb ( skb ) ;
return ;
}
if ( ! in_interrupt ( ) )
BUG ( ) ;
spin_lock ( & priv - > adapter - > driver_lock ) ;
/* take care of cur_cmd = NULL case by reading the
* data to clear the interrupt */
if ( ! priv - > adapter - > cur_cmd ) {
cmdbuf = priv - > wlan_dev . upld_buf ;
priv - > adapter - > hisregcpy & = ~ his_cmdupldrdy ;
} else
cmdbuf = priv - > adapter - > cur_cmd - > bufvirtualaddr ;
cardp - > usb_int_cause | = his_cmdupldrdy ;
priv - > wlan_dev . upld_len = ( recvlength - MESSAGE_HEADER_LEN ) ;
memcpy ( cmdbuf , recvbuff + MESSAGE_HEADER_LEN ,
priv - > wlan_dev . upld_len ) ;
kfree_skb ( skb ) ;
libertas_interrupt ( priv - > wlan_dev . netdev ) ;
spin_unlock ( & priv - > adapter - > driver_lock ) ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" Wake up main thread to handle cmd response \n " ) ;
return ;
}
/**
* @ brief This function reads of the packet into the upload buff ,
* wake up the main thread and initialise the Rx callack .
*
* @ param urb pointer to struct urb
* @ return N / A
*/
static void if_usb_receive ( struct urb * urb )
{
struct read_cb_info * rinfo = ( struct read_cb_info * ) urb - > context ;
wlan_private * priv = rinfo - > priv ;
struct sk_buff * skb = rinfo - > skb ;
struct usb_card_rec * cardp = ( struct usb_card_rec * ) priv - > wlan_dev . card ;
int recvlength = urb - > actual_length ;
u8 * recvbuff = NULL ;
u32 recvtype ;
2007-05-25 19:27:16 +04:00
lbs_deb_enter ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
if ( recvlength ) {
if ( urb - > status ) {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" URB status is failed \n " ) ;
kfree_skb ( skb ) ;
goto setup_for_next ;
}
recvbuff = skb - > data + IPFIELD_ALIGN_OFFSET ;
memcpy ( & recvtype , recvbuff , sizeof ( u32 ) ) ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" Recv length = 0x%x \n " , recvlength ) ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" Receive type = 0x%X \n " , recvtype ) ;
recvtype = le32_to_cpu ( recvtype ) ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev ,
2007-02-10 17:25:27 +03:00
" Receive type after = 0x%X \n " , recvtype ) ;
} else if ( urb - > status )
goto rx_exit ;
switch ( recvtype ) {
case CMD_TYPE_DATA :
process_cmdtypedata ( recvlength , skb , cardp , priv ) ;
break ;
case CMD_TYPE_REQUEST :
process_cmdrequest ( recvlength , recvbuff , skb , cardp , priv ) ;
break ;
case CMD_TYPE_INDICATION :
/* Event cause handling */
spin_lock ( & priv - > adapter - > driver_lock ) ;
cardp - > usb_event_cause = * ( u32 * ) ( recvbuff + MESSAGE_HEADER_LEN ) ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev , " **EVENT** 0x%X \n " ,
2007-02-10 17:25:27 +03:00
cardp - > usb_event_cause ) ;
if ( cardp - > usb_event_cause & 0xffff0000 ) {
libertas_send_tx_feedback ( priv ) ;
2007-05-11 07:08:54 +04:00
spin_unlock ( & priv - > adapter - > driver_lock ) ;
2007-02-10 17:25:27 +03:00
break ;
}
cardp - > usb_event_cause = le32_to_cpu ( cardp - > usb_event_cause ) < < 3 ;
cardp - > usb_int_cause | = his_cardevent ;
kfree_skb ( skb ) ;
libertas_interrupt ( priv - > wlan_dev . netdev ) ;
spin_unlock ( & priv - > adapter - > driver_lock ) ;
goto rx_exit ;
default :
kfree_skb ( skb ) ;
break ;
}
setup_for_next :
if_usb_submit_rx_urb ( priv ) ;
rx_exit :
2007-05-25 19:27:16 +04:00
lbs_deb_leave ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
}
/**
* @ brief This function downloads data to FW
* @ param priv pointer to wlan_private structure
* @ param type type of data
* @ param buf pointer to data buffer
* @ param len number of bytes
* @ return 0 or - 1
*/
int libertas_sbi_host_to_card ( wlan_private * priv , u8 type , u8 * payload , u16 nb )
{
int ret = - 1 ;
u32 tmp ;
struct usb_card_rec * cardp = ( struct usb_card_rec * ) priv - > wlan_dev . card ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev , " *** type = %u \n " , type ) ;
lbs_deb_usbd ( & cardp - > udev - > dev , " size after = %d \n " , nb ) ;
2007-02-10 17:25:27 +03:00
if ( type = = MVMS_CMD ) {
tmp = cpu_to_le32 ( CMD_TYPE_REQUEST ) ;
priv - > wlan_dev . dnld_sent = DNLD_CMD_SENT ;
memcpy ( cardp - > bulk_out_buffer , ( u8 * ) & tmp ,
MESSAGE_HEADER_LEN ) ;
} else {
tmp = cpu_to_le32 ( CMD_TYPE_DATA ) ;
priv - > wlan_dev . dnld_sent = DNLD_DATA_SENT ;
memcpy ( cardp - > bulk_out_buffer , ( u8 * ) & tmp ,
MESSAGE_HEADER_LEN ) ;
}
memcpy ( ( cardp - > bulk_out_buffer + MESSAGE_HEADER_LEN ) , payload , nb ) ;
ret =
usb_tx_block ( priv , cardp - > bulk_out_buffer , nb + MESSAGE_HEADER_LEN ) ;
return ret ;
}
/* called with adapter->driver_lock held */
int libertas_sbi_get_int_status ( wlan_private * priv , u8 * ireg )
{
struct usb_card_rec * cardp = priv - > wlan_dev . card ;
* ireg = cardp - > usb_int_cause ;
cardp - > usb_int_cause = 0 ;
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev , " Int cause is 0x%X \n " , * ireg ) ;
2007-02-10 17:25:27 +03:00
return 0 ;
}
int libertas_sbi_read_event_cause ( wlan_private * priv )
{
struct usb_card_rec * cardp = priv - > wlan_dev . card ;
priv - > adapter - > eventcause = cardp - > usb_event_cause ;
/* Re-submit rx urb here to avoid event lost issue */
if_usb_submit_rx_urb ( priv ) ;
return 0 ;
}
2007-05-25 20:01:42 +04:00
static int reset_device ( wlan_private * priv )
2007-02-10 17:25:27 +03:00
{
int ret ;
2007-05-25 20:01:42 +04:00
lbs_deb_enter ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
ret = libertas_prepare_and_send_command ( priv , cmd_802_11_reset ,
cmd_act_halt , 0 , 0 , NULL ) ;
msleep_interruptible ( 10 ) ;
2007-05-25 20:01:42 +04:00
lbs_deb_leave_args ( LBS_DEB_USB , " ret %d " , ret ) ;
2007-02-10 17:25:27 +03:00
return ret ;
}
int libertas_sbi_unregister_dev ( wlan_private * priv )
{
int ret = 0 ;
/* Need to send a Reset command to device before USB resources freed
* and wlan_remove_card ( ) called , then device can handle FW download
* again .
*/
if ( priv )
reset_device ( priv ) ;
return ret ;
}
/**
* @ brief This function register usb device and initialize parameter
* @ param priv pointer to wlan_private
* @ return 0 or - 1
*/
int libertas_sbi_register_dev ( wlan_private * priv )
{
struct usb_card_rec * cardp = ( struct usb_card_rec * ) priv - > wlan_dev . card ;
2007-05-25 19:27:16 +04:00
lbs_deb_enter ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
cardp - > priv = priv ;
cardp - > eth_dev = priv - > wlan_dev . netdev ;
priv - > hotplug_device = & ( cardp - > udev - > dev ) ;
SET_NETDEV_DEV ( cardp - > eth_dev , & ( cardp - > udev - > dev ) ) ;
2007-05-25 20:06:56 +04:00
SET_NETDEV_DEV ( priv - > mesh_dev , & ( cardp - > udev - > dev ) ) ;
2007-02-10 17:25:27 +03:00
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev , " udev pointer is at %p \n " ,
2007-02-10 17:25:27 +03:00
cardp - > udev ) ;
2007-05-25 19:27:16 +04:00
lbs_deb_leave ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
return 0 ;
}
int libertas_sbi_prog_firmware ( wlan_private * priv )
{
struct usb_card_rec * cardp = priv - > wlan_dev . card ;
int i = 0 ;
static int reset_count = 10 ;
2007-05-25 19:27:16 +04:00
int ret = 0 ;
2007-02-10 17:25:27 +03:00
2007-05-25 19:27:16 +04:00
lbs_deb_enter ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
cardp - > rinfo . priv = priv ;
restart :
if ( if_usb_submit_rx_urb_fwload ( priv ) < 0 ) {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev , " URB submission is failed \n " ) ;
ret = - 1 ;
goto done ;
2007-02-10 17:25:27 +03:00
}
cardp - > bootcmdresp = 0 ;
do {
int j = 0 ;
i + + ;
/* Issue Boot command = 1, Boot from Download-FW */
if_usb_issue_boot_command ( priv , BOOT_CMD_FW_BY_USB ) ;
/* wait for command response */
do {
j + + ;
msleep_interruptible ( 100 ) ;
} while ( cardp - > bootcmdresp = = 0 & & j < 10 ) ;
} while ( cardp - > bootcmdresp = = 0 & & i < 5 ) ;
if ( cardp - > bootcmdresp = = 0 ) {
if ( - - reset_count > = 0 ) {
libertas_do_reset ( priv ) ;
goto restart ;
}
return - 1 ;
}
i = 0 ;
priv - > adapter - > fw_ready = 0 ;
cardp - > totalbytes = 0 ;
cardp - > fwlastblksent = 0 ;
cardp - > CRC_OK = 1 ;
cardp - > fwdnldover = 0 ;
cardp - > fwseqnum = - 1 ;
cardp - > totalbytes = 0 ;
cardp - > fwfinalblk = 0 ;
if_prog_firmware ( priv ) ;
do {
2007-05-25 19:27:16 +04:00
lbs_deb_usbd ( & cardp - > udev - > dev , " Wlan sched timeout \n " ) ;
2007-02-10 17:25:27 +03:00
i + + ;
msleep_interruptible ( 100 ) ;
if ( priv - > adapter - > surpriseremoved | | i > = 20 )
break ;
} while ( ! cardp - > fwdnldover ) ;
if ( ! cardp - > fwdnldover ) {
lbs_pr_info ( " failed to load fw, resetting device! \n " ) ;
if ( - - reset_count > = 0 ) {
libertas_do_reset ( priv ) ;
goto restart ;
}
lbs_pr_info ( " FW download failure, time = %d ms \n " , i * 100 ) ;
2007-05-25 19:27:16 +04:00
ret = - 1 ;
goto done ;
2007-02-10 17:25:27 +03:00
}
if_usb_submit_rx_urb ( priv ) ;
/* Delay 200 ms to waiting for the FW ready */
msleep_interruptible ( 200 ) ;
priv - > adapter - > fw_ready = 1 ;
2007-05-25 19:27:16 +04:00
done :
lbs_deb_leave_args ( LBS_DEB_USB , " ret %d " , ret ) ;
return ret ;
2007-02-10 17:25:27 +03:00
}
# ifdef CONFIG_PM
static int if_usb_suspend ( struct usb_interface * intf , pm_message_t message )
{
struct usb_card_rec * cardp = usb_get_intfdata ( intf ) ;
wlan_private * priv = cardp - > priv ;
2007-05-25 19:27:16 +04:00
lbs_deb_enter ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
if ( priv - > adapter - > psstate ! = PS_STATE_FULL_POWER )
return - 1 ;
netif_device_detach ( cardp - > eth_dev ) ;
2007-05-25 20:06:56 +04:00
netif_device_detach ( priv - > mesh_dev ) ;
2007-02-10 17:25:27 +03:00
/* Unlink tx & rx urb */
usb_kill_urb ( cardp - > tx_urb ) ;
usb_kill_urb ( cardp - > rx_urb ) ;
cardp - > rx_urb_recall = 1 ;
2007-05-25 19:27:16 +04:00
lbs_deb_leave ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
return 0 ;
}
static int if_usb_resume ( struct usb_interface * intf )
{
struct usb_card_rec * cardp = usb_get_intfdata ( intf ) ;
2007-05-25 20:06:56 +04:00
wlan_private * priv = cardp - > priv ;
2007-02-10 17:25:27 +03:00
2007-05-25 19:27:16 +04:00
lbs_deb_enter ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
cardp - > rx_urb_recall = 0 ;
if_usb_submit_rx_urb ( cardp - > priv ) ;
netif_device_attach ( cardp - > eth_dev ) ;
2007-05-25 20:06:56 +04:00
netif_device_attach ( priv - > mesh_dev ) ;
2007-02-10 17:25:27 +03:00
2007-05-25 19:27:16 +04:00
lbs_deb_leave ( LBS_DEB_USB ) ;
2007-02-10 17:25:27 +03:00
return 0 ;
}
# else
# define if_usb_suspend NULL
# define if_usb_resume NULL
# endif
static struct usb_driver if_usb_driver = {
/* driver name */
. name = usbdriver_name ,
/* probe function name */
. probe = if_usb_probe ,
/* disconnect function name */
. disconnect = if_usb_disconnect ,
/* device signature table */
. id_table = if_usb_table ,
. suspend = if_usb_suspend ,
. resume = if_usb_resume ,
} ;
/**
* @ brief This function registers driver .
* @ param add pointer to add_card callback function
* @ param remove pointer to remove card callback function
* @ param arg pointer to call back function parameter
* @ return dummy success variable
*/
int libertas_sbi_register ( void )
{
/*
* API registers the Marvell USB driver
* to the USB system
*/
usb_register ( & if_usb_driver ) ;
/* Return success to wlan layer */
return 0 ;
}
/**
* @ brief This function removes usb driver .
* @ return N / A
*/
void libertas_sbi_unregister ( void )
{
2007-05-25 20:01:42 +04:00
int i ;
for ( i = 0 ; i < libertas_found ; i + + ) {
wlan_private * priv = libertas_devs [ i ] - > priv ;
reset_device ( priv ) ;
}
2007-02-10 17:25:27 +03:00
/* API unregisters the driver from USB subsystem */
usb_deregister ( & if_usb_driver ) ;
}