mirror of
https://github.com/nxp-imx/mwifiex.git
synced 2024-11-14 11:05:34 +00:00
5cda905576
Driver Bug Fixes: ----------------- 1. LF-6972: Updated the License to GPL-2.0 2. Incremented the driver version Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
2109 lines
58 KiB
C
2109 lines
58 KiB
C
/** @file moal_usb.c
|
|
*
|
|
* @brief This file contains the interfaceing to USB bus
|
|
* driver.
|
|
*
|
|
*
|
|
* Copyright 2008-2021 NXP
|
|
*
|
|
* This software file (the File) is distributed by NXP
|
|
* under the terms of the GNU General Public License Version 2, June 1991
|
|
* (the License). You may use, redistribute and/or modify the File in
|
|
* accordance with the terms and conditions of the License, a copy of which
|
|
* is available by writing to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
|
|
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
|
|
*
|
|
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
|
|
* this warranty disclaimer.
|
|
*
|
|
*/
|
|
|
|
/********************************************************
|
|
Change log:
|
|
10/21/2008: initial version
|
|
********************************************************/
|
|
|
|
#include "moal_main.h"
|
|
#include "moal_usb.h"
|
|
extern struct semaphore AddRemoveCardSem;
|
|
|
|
/********************************************************
|
|
Local Variables
|
|
********************************************************/
|
|
|
|
#if defined(USB8997) || defined(USB9098) || defined(USB9097) || \
|
|
defined(USB8978) || defined(USBNW62X)
|
|
/** Card-type detection frame response */
|
|
typedef struct {
|
|
/** 32-bit ACK+WINNER field */
|
|
t_u32 ack_winner;
|
|
/** 32-bit Sequence number */
|
|
t_u32 seq;
|
|
/** 32-bit extend */
|
|
t_u32 extend;
|
|
/** 32-bit chip-revision code */
|
|
t_u32 chip_rev;
|
|
/** 32-bit strap setting */
|
|
t_u32 strap;
|
|
} usb_ack_pkt;
|
|
#endif
|
|
|
|
/** NXP USB device */
|
|
#define NXP_USB_DEVICE(vid, pid, name) \
|
|
USB_DEVICE(vid, pid), .driver_info = (t_ptr)name
|
|
|
|
/** Name of the USB driver */
|
|
static const char usbdriver_name[] = "usbxxx";
|
|
|
|
/** This structure contains the device signature */
|
|
static struct usb_device_id woal_usb_table[] = {
|
|
/* Enter the device signature inside */
|
|
#ifdef USB8801
|
|
{NXP_USB_DEVICE(USB8801_VID_1, USB8801_PID_1, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8801_VID_1, USB8801_PID_2, "NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USB8897
|
|
{NXP_USB_DEVICE(USB8897_VID_1, USB8897_PID_1, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8897_VID_1, USB8897_PID_2, "NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USB8997
|
|
{NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_1, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8997_VID_1, USB8997V2_PID_1,
|
|
"NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_2, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_3, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_4, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_5, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_6, "NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USB8978
|
|
{NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_1, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_1_BT,
|
|
"NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2_BT,
|
|
"NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USB9098
|
|
{NXP_USB_DEVICE(USB9098_VID_1, USB9098_PID_1, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB9098_VID_1, USB9098_PID_2, "NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USB9097
|
|
{NXP_USB_DEVICE(USB9097_VID_1, USB9097_PID_1, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB9097_VID_1, USB9097_PID_2, "NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USBNW62X
|
|
{NXP_USB_DEVICE(USBNW62X_VID_1, USBNW62X_PID_1,
|
|
"NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USBNW62X_VID_1, USBNW62X_PID_2,
|
|
"NXP WLAN USB Adapter")},
|
|
#endif
|
|
/* Terminating entry */
|
|
{},
|
|
};
|
|
|
|
/** This structure contains the device signature */
|
|
static struct usb_device_id woal_usb_table_skip_fwdnld[] = {
|
|
/* Enter the device signature inside */
|
|
#ifdef USB8801
|
|
{NXP_USB_DEVICE(USB8801_VID_1, USB8801_PID_2, "NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USB8897
|
|
{NXP_USB_DEVICE(USB8897_VID_1, USB8897_PID_2, "NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USB8997
|
|
{NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_2, "NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USB8978
|
|
{NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2, "NXP WLAN USB Adapter")},
|
|
{NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2_BT,
|
|
"NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USB9098
|
|
{NXP_USB_DEVICE(USB9098_VID_1, USB9098_PID_2, "NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USB9097
|
|
{NXP_USB_DEVICE(USB9097_VID_1, USB9097_PID_2, "NXP WLAN USB Adapter")},
|
|
#endif
|
|
#ifdef USBNW62X
|
|
{NXP_USB_DEVICE(USBNW62X_VID_1, USBNW62X_PID_2,
|
|
"NXP WLAN USB Adapter")},
|
|
#endif
|
|
/* Terminating entry */
|
|
{},
|
|
};
|
|
|
|
static mlan_status woal_usb_submit_rx_urb(urb_context *ctx, int size);
|
|
static int woal_usb_probe(struct usb_interface *intf,
|
|
const struct usb_device_id *id);
|
|
static void woal_usb_disconnect(struct usb_interface *intf);
|
|
static mlan_status woal_usb_write_data_sync(moal_handle *handle,
|
|
mlan_buffer *pmbuf, t_u32 endpoint,
|
|
t_u32 timeout);
|
|
static mlan_status woal_usb_read_data_sync(moal_handle *handle,
|
|
mlan_buffer *pmbuf, t_u32 endpoint,
|
|
t_u32 timeout);
|
|
#ifdef CONFIG_PM
|
|
static int woal_usb_suspend(struct usb_interface *intf, pm_message_t message);
|
|
static int woal_usb_resume(struct usb_interface *intf);
|
|
#endif /* CONFIG_PM */
|
|
|
|
/** woal_usb_driver */
|
|
static struct usb_driver REFDATA woal_usb_driver = {
|
|
/* Driver name */
|
|
.name = usbdriver_name,
|
|
|
|
/* Probe function name */
|
|
.probe = woal_usb_probe,
|
|
|
|
/* Disconnect function name */
|
|
.disconnect = woal_usb_disconnect,
|
|
|
|
/* Device signature table */
|
|
.id_table = woal_usb_table,
|
|
#ifdef CONFIG_PM
|
|
/* Suspend function name */
|
|
.suspend = woal_usb_suspend,
|
|
|
|
/* Resume function name */
|
|
.resume = woal_usb_resume,
|
|
|
|
/* Reset resume function name */
|
|
.reset_resume = woal_usb_resume,
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
|
|
/* Driver supports autosuspend */
|
|
.supports_autosuspend = 1,
|
|
#endif
|
|
#endif /* CONFIG_PM */
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(usb, woal_usb_table);
|
|
MODULE_DEVICE_TABLE(usb, woal_usb_table_skip_fwdnld);
|
|
|
|
/* moal interface ops */
|
|
static moal_if_ops usb_ops;
|
|
|
|
/********************************************************
|
|
Global Variables
|
|
********************************************************/
|
|
|
|
/********************************************************
|
|
Local Functions
|
|
********************************************************/
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
|
|
/**
|
|
* @brief This function receive packet of the data/cmd/event packet
|
|
* and pass to MLAN
|
|
*
|
|
* @param urb Pointer to struct urb
|
|
* @param regs Registers
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void woal_usb_receive(struct urb *urb, struct pt_regs *regs)
|
|
#else
|
|
/**
|
|
* @brief This function receive packet of the data/cmd/event packet
|
|
* and pass to MLAN
|
|
*
|
|
* @param urb Pointer to struct urb
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void woal_usb_receive(struct urb *urb)
|
|
#endif
|
|
{
|
|
urb_context *context = NULL;
|
|
moal_handle *handle = NULL;
|
|
mlan_buffer *pmbuf = NULL;
|
|
struct usb_card_rec *cardp = NULL;
|
|
int recv_length;
|
|
int size;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (!urb || !urb->context) {
|
|
PRINTM(MERROR, "URB or URB context is not valid in USB Rx\n");
|
|
LEAVE();
|
|
return;
|
|
}
|
|
context = (urb_context *)urb->context;
|
|
handle = context->handle;
|
|
pmbuf = context->pmbuf;
|
|
recv_length = urb->actual_length;
|
|
|
|
if (!handle || !handle->card || !pmbuf) {
|
|
PRINTM(MERROR,
|
|
"moal handle, card structure or mlan_buffer is not valid in USB Rx\n");
|
|
LEAVE();
|
|
return;
|
|
}
|
|
cardp = (struct usb_card_rec *)handle->card;
|
|
if (cardp->rx_cmd_ep == context->ep)
|
|
atomic_dec(&cardp->rx_cmd_urb_pending);
|
|
else
|
|
atomic_dec(&cardp->rx_data_urb_pending);
|
|
|
|
if (recv_length) {
|
|
if (urb->status || (handle->surprise_removed == MTRUE)) {
|
|
if (handle->surprise_removed || handle->is_suspended) {
|
|
woal_free_mlan_buffer(handle, pmbuf);
|
|
context->pmbuf = NULL;
|
|
goto rx_exit;
|
|
} else {
|
|
PRINTM(MERROR,
|
|
"EP %d Rx URB status failure: %d\n",
|
|
context->ep, urb->status);
|
|
/* Do not free mlan_buffer in case of command ep
|
|
*/
|
|
if (cardp->rx_cmd_ep != context->ep)
|
|
woal_free_mlan_buffer(handle, pmbuf);
|
|
goto setup_for_next;
|
|
}
|
|
}
|
|
pmbuf->data_len = recv_length;
|
|
pmbuf->flags |= MLAN_BUF_FLAG_RX_DEAGGR;
|
|
/* Send packet to MLAN */
|
|
atomic_inc(&handle->rx_pending);
|
|
status = mlan_recv(handle->pmlan_adapter, pmbuf, context->ep);
|
|
PRINTM(MINFO, "Receive length = 0x%x, status=%d\n", recv_length,
|
|
status);
|
|
if (status == MLAN_STATUS_PENDING) {
|
|
queue_work(handle->workqueue, &handle->main_work);
|
|
/* urb for data_ep is re-submitted now, unless we reach
|
|
* USB_HIGH_RX_PENDING */
|
|
/* urb for cmd_ep will be re-submitted in callback
|
|
* moal_recv_complete */
|
|
if (cardp->rx_cmd_ep == context->ep)
|
|
goto rx_exit;
|
|
else if (atomic_read(&handle->rx_pending) >=
|
|
USB_HIGH_RX_PENDING) {
|
|
context->pmbuf = NULL;
|
|
goto rx_exit;
|
|
}
|
|
} else {
|
|
atomic_dec(&handle->rx_pending);
|
|
if (status == MLAN_STATUS_FAILURE) {
|
|
PRINTM(MERROR,
|
|
"MLAN fail to process the receive data\n");
|
|
} else if ((status == MLAN_STATUS_SUCCESS) &&
|
|
(pmbuf->flags &
|
|
MLAN_BUF_FLAG_SLEEPCFM_RESP)) {
|
|
pmbuf->flags &= ~MLAN_BUF_FLAG_SLEEPCFM_RESP;
|
|
queue_work(handle->workqueue,
|
|
&handle->main_work);
|
|
}
|
|
/* Do not free mlan_buffer in case of command ep */
|
|
if (cardp->rx_cmd_ep != context->ep)
|
|
woal_free_mlan_buffer(handle, pmbuf);
|
|
}
|
|
} else if (urb->status) {
|
|
if (!((cardp->rx_data_ep == context->ep) &&
|
|
(cardp->resubmit_urbs == 1))) {
|
|
if (!handle->is_suspended) {
|
|
PRINTM(MMSG, "Card is removed: %d\n",
|
|
urb->status);
|
|
handle->surprise_removed = MTRUE;
|
|
}
|
|
}
|
|
woal_free_mlan_buffer(handle, pmbuf);
|
|
context->pmbuf = NULL;
|
|
goto rx_exit;
|
|
} else {
|
|
/* Do not free mlan_buffer in case of command ep */
|
|
if (cardp->rx_cmd_ep != context->ep)
|
|
woal_free_mlan_buffer(handle, pmbuf);
|
|
goto setup_for_next;
|
|
}
|
|
|
|
setup_for_next:
|
|
if (cardp->rx_cmd_ep == context->ep) {
|
|
size = MLAN_RX_CMD_BUF_SIZE;
|
|
} else {
|
|
if (cardp->rx_deaggr_ctrl.enable) {
|
|
size = cardp->rx_deaggr_ctrl.aggr_max;
|
|
if (cardp->rx_deaggr_ctrl.aggr_mode ==
|
|
MLAN_USB_AGGR_MODE_NUM) {
|
|
size *= MAX(MLAN_USB_MAX_PKT_SIZE,
|
|
cardp->rx_deaggr_ctrl.aggr_align);
|
|
size = MAX(size, MLAN_RX_DATA_BUF_SIZE);
|
|
}
|
|
} else
|
|
size = MLAN_RX_DATA_BUF_SIZE;
|
|
}
|
|
woal_usb_submit_rx_urb(context, size);
|
|
|
|
rx_exit:
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
|
|
/**
|
|
* @brief Call back function to handle the status of the Tx data URB
|
|
*
|
|
* @param urb Pointer to urb structure
|
|
* @param regs Registers
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void woal_usb_tx_complete(struct urb *urb, struct pt_regs *regs)
|
|
#else
|
|
/**
|
|
* @brief Call back function to handle the status of the Tx data URB
|
|
*
|
|
* @param urb Pointer to urb structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void woal_usb_tx_complete(struct urb *urb)
|
|
#endif
|
|
{
|
|
urb_context *context = NULL;
|
|
moal_handle *handle = NULL;
|
|
struct usb_card_rec *cardp = NULL;
|
|
|
|
ENTER();
|
|
|
|
if (!urb || !urb->context) {
|
|
PRINTM(MERROR,
|
|
"URB or URB context is not valid in USB Tx complete\n");
|
|
LEAVE();
|
|
return;
|
|
}
|
|
context = (urb_context *)urb->context;
|
|
handle = context->handle;
|
|
|
|
if (!handle || !handle->card || !context->pmbuf) {
|
|
PRINTM(MERROR,
|
|
"moal handle, card structure or mlan_buffer is not valid in USB Tx complete\n");
|
|
LEAVE();
|
|
return;
|
|
}
|
|
cardp = handle->card;
|
|
|
|
/* Handle the transmission complete validations */
|
|
if (urb->status) {
|
|
PRINTM(MERROR, "EP %d Tx URB status failure: %d\n", context->ep,
|
|
urb->status);
|
|
mlan_write_data_async_complete(handle->pmlan_adapter,
|
|
context->pmbuf, context->ep,
|
|
MLAN_STATUS_FAILURE);
|
|
} else {
|
|
mlan_write_data_async_complete(handle->pmlan_adapter,
|
|
context->pmbuf, context->ep,
|
|
MLAN_STATUS_SUCCESS);
|
|
}
|
|
|
|
/* Decrease pending URB counter */
|
|
if (context->ep == cardp->tx_cmd_ep)
|
|
atomic_dec(&cardp->tx_cmd_urb_pending);
|
|
else if (context->ep == cardp->tx_data_ep)
|
|
atomic_dec(&cardp->tx_data_urb_pending);
|
|
|
|
queue_work(handle->workqueue, &handle->main_work);
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function sets up the data to receive
|
|
*
|
|
* @param ctx Pointer to urb_context structure
|
|
* @param size Skb size
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
static mlan_status woal_usb_submit_rx_urb(urb_context *ctx, int size)
|
|
{
|
|
moal_handle *handle = ctx->handle;
|
|
struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card;
|
|
mlan_status ret = MLAN_STATUS_FAILURE;
|
|
t_u8 *data = NULL;
|
|
|
|
ENTER();
|
|
|
|
if (handle->surprise_removed || handle->is_suspended) {
|
|
if ((cardp->rx_cmd_ep == ctx->ep) && ctx->pmbuf) {
|
|
woal_free_mlan_buffer(handle, ctx->pmbuf);
|
|
ctx->pmbuf = NULL;
|
|
}
|
|
PRINTM(MERROR,
|
|
"Card removed/suspended, EP %d Rx URB submit skipped\n",
|
|
ctx->ep);
|
|
goto rx_ret;
|
|
}
|
|
|
|
if (cardp->rx_cmd_ep != ctx->ep) {
|
|
ctx->pmbuf = woal_alloc_mlan_buffer(handle, size);
|
|
if (!ctx->pmbuf) {
|
|
PRINTM(MERROR,
|
|
"Fail to submit Rx URB due to no memory/skb\n");
|
|
goto rx_ret;
|
|
}
|
|
ctx->pmbuf->data_offset = MLAN_RX_HEADER_LEN;
|
|
data = ctx->pmbuf->pbuf + ctx->pmbuf->data_offset;
|
|
} else {
|
|
ctx->pmbuf->data_offset = 0;
|
|
data = ctx->pmbuf->pbuf + ctx->pmbuf->data_offset;
|
|
}
|
|
|
|
if (cardp->rx_cmd_ep == ctx->ep &&
|
|
cardp->rx_cmd_ep_type == USB_ENDPOINT_XFER_INT)
|
|
usb_fill_int_urb(ctx->urb, cardp->udev,
|
|
usb_rcvintpipe(cardp->udev, ctx->ep), data,
|
|
size - ctx->pmbuf->data_offset,
|
|
woal_usb_receive, (void *)ctx,
|
|
cardp->rx_cmd_interval);
|
|
else
|
|
usb_fill_bulk_urb(ctx->urb, cardp->udev,
|
|
usb_rcvbulkpipe(cardp->udev, ctx->ep), data,
|
|
size - ctx->pmbuf->data_offset,
|
|
woal_usb_receive, (void *)ctx);
|
|
if (cardp->rx_cmd_ep == ctx->ep)
|
|
atomic_inc(&cardp->rx_cmd_urb_pending);
|
|
else
|
|
atomic_inc(&cardp->rx_data_urb_pending);
|
|
if (usb_submit_urb(ctx->urb, GFP_ATOMIC)) {
|
|
/* Submit URB failure */
|
|
PRINTM(MERROR, "Submit EP %d Rx URB failed: %d\n", ctx->ep,
|
|
ret);
|
|
woal_free_mlan_buffer(handle, ctx->pmbuf);
|
|
if (cardp->rx_cmd_ep == ctx->ep)
|
|
atomic_dec(&cardp->rx_cmd_urb_pending);
|
|
else
|
|
atomic_dec(&cardp->rx_data_urb_pending);
|
|
ctx->pmbuf = NULL;
|
|
ret = MLAN_STATUS_FAILURE;
|
|
} else {
|
|
ret = MLAN_STATUS_SUCCESS;
|
|
}
|
|
rx_ret:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/********************************************************
|
|
Global Functions
|
|
********************************************************/
|
|
|
|
#if defined(USB8997) || defined(USB9098) || defined(USB9097) || \
|
|
defined(USB8978) || defined(USBNW62X)
|
|
/**
|
|
* @brief Check chip revision
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
* @param usb_chip_rev A pointer to usb_chip_rev variable
|
|
* @param usb_strap A pointer to usb_strap
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
static mlan_status woal_check_chip_revision(moal_handle *handle,
|
|
t_u32 *usb_chip_rev,
|
|
t_u32 *usb_strap)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_buffer mbuf;
|
|
t_u8 *tx_buff = NULL;
|
|
t_u8 *recv_buff = NULL;
|
|
usb_ack_pkt ack_pkt;
|
|
t_u32 extend_ver;
|
|
t_u8 tx_size = CHIP_REV_TX_BUF_SIZE;
|
|
struct usb_card_rec *cardp = handle->card;
|
|
|
|
ENTER();
|
|
|
|
/* Allocate memory for transmit */
|
|
tx_buff = kzalloc(tx_size, GFP_ATOMIC | GFP_DMA);
|
|
if (tx_buff == NULL) {
|
|
PRINTM(MERROR,
|
|
"Could not allocate buffer for chip revision check frame transmission\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Allocate memory for receive */
|
|
recv_buff = kzalloc(CHIP_REV_RX_BUF_SIZE, GFP_ATOMIC | GFP_DMA);
|
|
if (recv_buff == NULL) {
|
|
PRINTM(MERROR,
|
|
"Could not allocate buffer for chip revision check frame response\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* The struct is initialised to all zero */
|
|
memset(&ack_pkt, 0, sizeof(usb_ack_pkt));
|
|
|
|
/* Send pseudo data to check winner status first */
|
|
memset(&mbuf, 0, sizeof(mlan_buffer));
|
|
mbuf.pbuf = (t_u8 *)tx_buff;
|
|
mbuf.data_len = tx_size;
|
|
|
|
/* Send the chip revision check frame */
|
|
ret = woal_usb_write_data_sync(handle, &mbuf, cardp->tx_cmd_ep,
|
|
MLAN_USB_BULK_MSG_TIMEOUT);
|
|
if (ret != MLAN_STATUS_SUCCESS) {
|
|
PRINTM(MERROR,
|
|
"Chip revision check frame dnld: write_data failed, ret %d\n",
|
|
ret);
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto cleanup;
|
|
}
|
|
|
|
memset(&mbuf, 0, sizeof(mlan_buffer));
|
|
mbuf.pbuf = (t_u8 *)recv_buff;
|
|
mbuf.data_len = CHIP_REV_RX_BUF_SIZE;
|
|
|
|
/* Receive the chip revision check frame response */
|
|
ret = woal_usb_read_data_sync(handle, &mbuf, cardp->rx_cmd_ep,
|
|
MLAN_USB_BULK_MSG_TIMEOUT);
|
|
if (ret != MLAN_STATUS_SUCCESS) {
|
|
PRINTM(MERROR,
|
|
"Chip revision check frame response: read_data failed, ret %d\n",
|
|
ret);
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto cleanup;
|
|
}
|
|
moal_memcpy_ext(handle, &ack_pkt, recv_buff, sizeof(usb_ack_pkt),
|
|
sizeof(ack_pkt));
|
|
ack_pkt.ack_winner = woal_le32_to_cpu(ack_pkt.ack_winner);
|
|
ack_pkt.seq = woal_le32_to_cpu(ack_pkt.seq);
|
|
ack_pkt.extend = woal_le32_to_cpu(ack_pkt.extend);
|
|
ack_pkt.chip_rev = woal_le32_to_cpu(ack_pkt.chip_rev);
|
|
ack_pkt.strap = woal_le32_to_cpu(ack_pkt.strap);
|
|
|
|
if ((ack_pkt.extend & 0xffff0000) == EXTEND_HDR) {
|
|
extend_ver = ack_pkt.extend & 0x0000ffff;
|
|
*usb_chip_rev = ack_pkt.chip_rev & 0x000000ff;
|
|
if (extend_ver >= EXTEND_V2) {
|
|
PRINTM(MINFO, "chip_rev=0x%x, strap=0x%x\n",
|
|
*usb_chip_rev, ack_pkt.strap);
|
|
*usb_strap = ack_pkt.strap & 0x7;
|
|
} else
|
|
PRINTM(MINFO, "chip_rev=0x%x\n", *usb_chip_rev);
|
|
}
|
|
cleanup:
|
|
kfree(recv_buff);
|
|
kfree(tx_buff);
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief This function unlink urb
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
* @return N/A
|
|
*/
|
|
static void woal_usb_unlink_urb(void *card_desc)
|
|
{
|
|
struct usb_card_rec *cardp = (struct usb_card_rec *)card_desc;
|
|
int i;
|
|
ENTER();
|
|
if (cardp) {
|
|
/* Unlink Rx cmd URB */
|
|
if (atomic_read(&cardp->rx_cmd_urb_pending) &&
|
|
cardp->rx_cmd.urb) {
|
|
usb_kill_urb(cardp->rx_cmd.urb);
|
|
}
|
|
/* Unlink Rx data URBs */
|
|
if (atomic_read(&cardp->rx_data_urb_pending)) {
|
|
for (i = 0; i < MVUSB_RX_DATA_URB; i++) {
|
|
if (cardp->rx_data_list[i].urb)
|
|
usb_kill_urb(
|
|
cardp->rx_data_list[i].urb);
|
|
}
|
|
}
|
|
/* Unlink Tx cmd URB */
|
|
if (atomic_read(&cardp->tx_cmd_urb_pending) &&
|
|
cardp->tx_cmd.urb) {
|
|
usb_kill_urb(cardp->tx_cmd.urb);
|
|
}
|
|
/* Unlink Tx data URBs */
|
|
if (atomic_read(&cardp->tx_data_urb_pending)) {
|
|
for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) {
|
|
if (cardp->tx_data_list[i].urb) {
|
|
usb_kill_urb(
|
|
cardp->tx_data_list[i].urb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief Free Tx/Rx urb, skb and Rx buffer
|
|
*
|
|
* @param cardp Pointer usb_card_rec
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void woal_usb_free(struct usb_card_rec *cardp)
|
|
{
|
|
int i;
|
|
|
|
ENTER();
|
|
|
|
woal_usb_unlink_urb(cardp);
|
|
/* Free Rx data URBs */
|
|
for (i = 0; i < MVUSB_RX_DATA_URB; i++) {
|
|
if (cardp->rx_data_list[i].urb) {
|
|
usb_free_urb(cardp->rx_data_list[i].urb);
|
|
cardp->rx_data_list[i].urb = NULL;
|
|
}
|
|
}
|
|
/* Free Rx cmd URB */
|
|
if (cardp->rx_cmd.urb) {
|
|
usb_free_urb(cardp->rx_cmd.urb);
|
|
cardp->rx_cmd.urb = NULL;
|
|
}
|
|
|
|
/* Free Tx data URBs */
|
|
for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) {
|
|
if (cardp->tx_data_list[i].urb) {
|
|
usb_free_urb(cardp->tx_data_list[i].urb);
|
|
cardp->tx_data_list[i].urb = NULL;
|
|
}
|
|
}
|
|
/* Free Tx cmd URB */
|
|
if (cardp->tx_cmd.urb) {
|
|
usb_free_urb(cardp->tx_cmd.urb);
|
|
cardp->tx_cmd.urb = NULL;
|
|
}
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
static t_u16 woal_update_card_type(t_void *card)
|
|
{
|
|
struct usb_card_rec *cardp_usb = (struct usb_card_rec *)card;
|
|
t_u16 card_type = 0;
|
|
|
|
/* Update card type */
|
|
#ifdef USB8801
|
|
if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8801_PID_1 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8801_PID_2) {
|
|
card_type = CARD_TYPE_USB8801;
|
|
moal_memcpy_ext(NULL, driver_version, CARD_USB8801,
|
|
strlen(CARD_USB8801), strlen(driver_version));
|
|
moal_memcpy_ext(NULL,
|
|
driver_version + strlen(INTF_CARDTYPE) +
|
|
strlen(KERN_VERSION),
|
|
V14, strlen(V14),
|
|
strlen(driver_version) - strlen(INTF_CARDTYPE) -
|
|
strlen(KERN_VERSION));
|
|
}
|
|
#endif
|
|
#ifdef USB8897
|
|
if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8897_PID_1 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8897_PID_2) {
|
|
card_type = CARD_TYPE_USB8897;
|
|
moal_memcpy_ext(NULL, driver_version, CARD_USB8897,
|
|
strlen(CARD_USB8897), strlen(driver_version));
|
|
moal_memcpy_ext(NULL,
|
|
driver_version + strlen(INTF_CARDTYPE) +
|
|
strlen(KERN_VERSION),
|
|
V15, strlen(V15),
|
|
strlen(driver_version) - strlen(INTF_CARDTYPE) -
|
|
strlen(KERN_VERSION));
|
|
}
|
|
#endif
|
|
#ifdef USB8997
|
|
if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8997_PID_1 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8997_PID_2 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8997_PID_3 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8997_PID_4 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8997_PID_5 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8997_PID_6 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8997V2_PID_1) {
|
|
card_type = CARD_TYPE_USB8997;
|
|
moal_memcpy_ext(NULL, driver_version, CARD_USB8997,
|
|
strlen(CARD_USB8997), strlen(driver_version));
|
|
moal_memcpy_ext(NULL,
|
|
driver_version + strlen(INTF_CARDTYPE) +
|
|
strlen(KERN_VERSION),
|
|
V16, strlen(V16),
|
|
strlen(driver_version) - strlen(INTF_CARDTYPE) -
|
|
strlen(KERN_VERSION));
|
|
}
|
|
#endif
|
|
#ifdef USB8978
|
|
if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8978_PID_1 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB8978_PID_2) {
|
|
card_type = CARD_TYPE_USB8978;
|
|
moal_memcpy_ext(NULL, driver_version, "USBIW416",
|
|
strlen("USBIW416"), strlen(driver_version));
|
|
moal_memcpy_ext(NULL,
|
|
driver_version + strlen(INTF_CARDTYPE) +
|
|
strlen(KERN_VERSION),
|
|
V16, strlen(V16),
|
|
strlen(driver_version) - strlen(INTF_CARDTYPE) -
|
|
strlen(KERN_VERSION));
|
|
}
|
|
#endif
|
|
#ifdef USB9098
|
|
if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB9098_PID_1 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB9098_PID_2) {
|
|
card_type = CARD_TYPE_USB9098;
|
|
moal_memcpy_ext(NULL, driver_version, CARD_USB9098,
|
|
strlen(CARD_USB9098), strlen(driver_version));
|
|
moal_memcpy_ext(NULL,
|
|
driver_version + strlen(INTF_CARDTYPE) +
|
|
strlen(KERN_VERSION),
|
|
V17, strlen(V17),
|
|
strlen(driver_version) - strlen(INTF_CARDTYPE) -
|
|
strlen(KERN_VERSION));
|
|
}
|
|
#endif
|
|
#ifdef USB9097
|
|
if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB9097_PID_1 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USB9097_PID_2) {
|
|
card_type = CARD_TYPE_USB9097;
|
|
moal_memcpy_ext(NULL, driver_version, CARD_USB9097,
|
|
strlen(CARD_USB9097), strlen(driver_version));
|
|
moal_memcpy_ext(NULL,
|
|
driver_version + strlen(INTF_CARDTYPE) +
|
|
strlen(KERN_VERSION),
|
|
V17, strlen(V17),
|
|
strlen(driver_version) - strlen(INTF_CARDTYPE) -
|
|
strlen(KERN_VERSION));
|
|
}
|
|
#endif
|
|
#ifdef USBNW62X
|
|
if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USBNW62X_PID_1 ||
|
|
woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) ==
|
|
(__force __le16)USBNW62X_PID_2) {
|
|
card_type = CARD_TYPE_USBNW62X;
|
|
moal_memcpy_ext(NULL, driver_version, CARD_USBNW62X,
|
|
strlen(CARD_USBNW62X), strlen(driver_version));
|
|
moal_memcpy_ext(NULL,
|
|
driver_version + strlen(INTF_CARDTYPE) +
|
|
strlen(KERN_VERSION),
|
|
V17, strlen(V17),
|
|
strlen(driver_version) - strlen(INTF_CARDTYPE) -
|
|
strlen(KERN_VERSION));
|
|
}
|
|
#endif
|
|
return card_type;
|
|
}
|
|
|
|
/**
|
|
* @brief Sets the configuration values
|
|
*
|
|
* @param intf Pointer to usb_interface
|
|
* @param id Pointer to usb_device_id
|
|
*
|
|
* @return Address of variable usb_cardp, error code otherwise
|
|
*/
|
|
static int woal_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;
|
|
int i;
|
|
struct usb_card_rec *usb_cardp = NULL;
|
|
t_u16 card_type = 0;
|
|
|
|
ENTER();
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
PRINTM(MMSG,
|
|
"USB probe: idVendor=%x idProduct=%x bInterfaceNumber=%d\n",
|
|
id->idVendor, id->idProduct, id->bInterfaceNumber);
|
|
#endif
|
|
|
|
udev = interface_to_usbdev(intf);
|
|
usb_cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL);
|
|
if (!usb_cardp) {
|
|
LEAVE();
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Check probe is for our device */
|
|
for (i = 0; woal_usb_table[i].idVendor; i++) {
|
|
if (woal_cpu_to_le16(udev->descriptor.idVendor) ==
|
|
(__force __le16)woal_usb_table[i].idVendor &&
|
|
woal_cpu_to_le16(udev->descriptor.idProduct) ==
|
|
(__force __le16)woal_usb_table[i].idProduct) {
|
|
PRINTM(MMSG, "VID/PID = %X/%X, Boot2 version = %X\n",
|
|
woal_cpu_to_le16(udev->descriptor.idVendor),
|
|
woal_cpu_to_le16(udev->descriptor.idProduct),
|
|
woal_cpu_to_le16(udev->descriptor.bcdDevice));
|
|
switch (woal_cpu_to_le16(udev->descriptor.idProduct)) {
|
|
#ifdef USB8801
|
|
case (__force __le16)USB8801_PID_1:
|
|
#endif /* USB8801 */
|
|
#ifdef USB8897
|
|
case (__force __le16)USB8897_PID_1:
|
|
#endif /* USB8897 */
|
|
#ifdef USB8997
|
|
case (__force __le16)USB8997_PID_1:
|
|
case (__force __le16)USB8997V2_PID_1:
|
|
#endif /* USB8997 */
|
|
#ifdef USB8978
|
|
case (__force __le16)USB8978_PID_1:
|
|
case (__force __le16)USB8978_PID_1_BT:
|
|
#endif /* USB8978 */
|
|
#ifdef USB9098
|
|
case (__force __le16)USB9098_PID_1:
|
|
#endif /* USB9098 */
|
|
#ifdef USB9097
|
|
case (__force __le16)USB9097_PID_1:
|
|
#endif /* USB9097 */
|
|
#ifdef USBNW62X
|
|
case (__force __le16)USBNW62X_PID_1:
|
|
#endif /* USBNW62X */
|
|
|
|
/* If skip FW is set, we must return error so
|
|
* the next driver can download the FW */
|
|
if (skip_fwdnld)
|
|
goto error;
|
|
else
|
|
usb_cardp->boot_state = USB_FW_DNLD;
|
|
break;
|
|
#ifdef USB8801
|
|
case (__force __le16)USB8801_PID_2:
|
|
#endif /* USB8801 */
|
|
#ifdef USB8897
|
|
case (__force __le16)USB8897_PID_2:
|
|
#endif /* USB8897 */
|
|
#ifdef USB8997
|
|
case (__force __le16)USB8997_PID_2:
|
|
#endif /* USB8997 */
|
|
#ifdef USB8978
|
|
case (__force __le16)USB8978_PID_2:
|
|
case (__force __le16)USB8978_PID_2_BT:
|
|
#endif /* USB8978 */
|
|
#ifdef USB9098
|
|
case (__force __le16)USB9098_PID_2:
|
|
#endif /* USB9098 */
|
|
#ifdef USB9097
|
|
case (__force __le16)USB9097_PID_2:
|
|
#endif /* USB9097 */
|
|
#ifdef USBNW62X
|
|
case (__force __le16)USBNW62X_PID_2:
|
|
#endif /* USBNW62X */
|
|
|
|
usb_cardp->boot_state = USB_FW_READY;
|
|
break;
|
|
}
|
|
/*To do, get card type*/
|
|
/* if
|
|
(woal_cpu_to_le16(udev->descriptor.idProduct) ==
|
|
USB8897_PID_2) usb_cardp->card_type =
|
|
CARD_TYPE_USB8897; else if
|
|
(woal_cpu_to_le16(udev->descriptor.idProduct) ==
|
|
USB8997_PID_2) usb_cardp->card_type =
|
|
CARD_TYPE_USB997;
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (woal_usb_table[i].idVendor) {
|
|
usb_cardp->udev = udev;
|
|
iface_desc = intf->cur_altsetting;
|
|
usb_cardp->intf = intf;
|
|
|
|
PRINTM(MINFO,
|
|
"bcdUSB = 0x%X bDeviceClass = 0x%X"
|
|
" bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n",
|
|
woal_cpu_to_le16(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 ((usb_endpoint_is_bulk_in(endpoint) ||
|
|
usb_endpoint_is_int_in(endpoint)) &&
|
|
(usb_endpoint_num(endpoint) ==
|
|
MLAN_USB_EP_CMD_EVENT ||
|
|
usb_endpoint_num(endpoint) ==
|
|
MLAN_USB_EP_CMD_EVENT_IF2)) {
|
|
usb_cardp->rx_cmd_ep_type =
|
|
usb_endpoint_type(endpoint);
|
|
usb_cardp->rx_cmd_interval =
|
|
endpoint->bInterval;
|
|
/* We found a bulk in command/event endpoint */
|
|
PRINTM(MCMND,
|
|
"Rx CMD/EVT: max packet size = %d, address = %d ep_type=%d\n",
|
|
woal_le16_to_cpu(
|
|
endpoint->wMaxPacketSize),
|
|
endpoint->bEndpointAddress,
|
|
usb_cardp->rx_cmd_ep_type);
|
|
usb_cardp->rx_cmd_ep =
|
|
(endpoint->bEndpointAddress &
|
|
USB_ENDPOINT_NUMBER_MASK);
|
|
|
|
atomic_set(&usb_cardp->rx_cmd_urb_pending, 0);
|
|
if (usb_endpoint_num(endpoint) ==
|
|
MLAN_USB_EP_CMD_EVENT_IF2)
|
|
usb_cardp->second_mac = MTRUE;
|
|
}
|
|
if (usb_endpoint_is_bulk_in(endpoint) &&
|
|
(usb_endpoint_num(endpoint) == MLAN_USB_EP_DATA ||
|
|
usb_endpoint_num(endpoint) ==
|
|
MLAN_USB_EP_DATA_IF2)) {
|
|
/* We found a bulk in data endpoint */
|
|
PRINTM(MINFO,
|
|
"Bulk IN: max packet size = %d, address = %d\n",
|
|
woal_le16_to_cpu(
|
|
endpoint->wMaxPacketSize),
|
|
endpoint->bEndpointAddress);
|
|
usb_cardp->rx_data_ep =
|
|
(endpoint->bEndpointAddress &
|
|
USB_ENDPOINT_NUMBER_MASK);
|
|
atomic_set(&usb_cardp->rx_data_urb_pending, 0);
|
|
}
|
|
if (usb_endpoint_is_bulk_out(endpoint) &&
|
|
(usb_endpoint_num(endpoint) == MLAN_USB_EP_DATA ||
|
|
usb_endpoint_num(endpoint) ==
|
|
MLAN_USB_EP_DATA_IF2)) {
|
|
/* We found a bulk out data endpoint */
|
|
PRINTM(MCMND,
|
|
"Bulk OUT: max packet size = %d, address = %d\n",
|
|
woal_le16_to_cpu(
|
|
endpoint->wMaxPacketSize),
|
|
endpoint->bEndpointAddress);
|
|
usb_cardp->tx_data_ep =
|
|
endpoint->bEndpointAddress;
|
|
atomic_set(&usb_cardp->tx_data_urb_pending, 0);
|
|
usb_cardp->tx_data_maxpktsize =
|
|
(__force int)woal_le16_to_cpu(
|
|
endpoint->wMaxPacketSize);
|
|
}
|
|
|
|
if ((usb_endpoint_is_bulk_out(endpoint) ||
|
|
usb_endpoint_is_int_out(endpoint)) &&
|
|
(usb_endpoint_num(endpoint) ==
|
|
MLAN_USB_EP_CMD_EVENT ||
|
|
usb_endpoint_num(endpoint) ==
|
|
MLAN_USB_EP_CMD_EVENT_IF2)) {
|
|
usb_cardp->tx_cmd_ep_type =
|
|
usb_endpoint_type(endpoint);
|
|
usb_cardp->tx_cmd_interval =
|
|
endpoint->bInterval;
|
|
/* We found a bulk out command/event endpoint */
|
|
PRINTM(MCMND,
|
|
"Tx CMD: max packet size = %d, address = %d ep_type=%d\n",
|
|
woal_le16_to_cpu(
|
|
endpoint->wMaxPacketSize),
|
|
endpoint->bEndpointAddress,
|
|
usb_cardp->tx_cmd_ep_type);
|
|
usb_cardp->tx_cmd_ep =
|
|
endpoint->bEndpointAddress;
|
|
atomic_set(&usb_cardp->tx_cmd_urb_pending, 0);
|
|
usb_cardp->tx_cmd_maxpktsize =
|
|
(__force int)woal_le16_to_cpu(
|
|
endpoint->wMaxPacketSize);
|
|
}
|
|
}
|
|
|
|
if (usb_cardp->boot_state == USB_FW_DNLD) {
|
|
if (!usb_cardp->tx_cmd_ep || !usb_cardp->rx_cmd_ep)
|
|
goto error;
|
|
} else if (usb_cardp->boot_state == USB_FW_READY) {
|
|
if (!usb_cardp->tx_cmd_ep || !usb_cardp->tx_data_ep ||
|
|
!usb_cardp->rx_cmd_ep || !usb_cardp->rx_data_ep) {
|
|
PRINTM(MERROR,
|
|
"%s: invalid endpoint assignment\n",
|
|
__FUNCTION__);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
usb_cardp->tx_aggr_ctrl.enable = MFALSE;
|
|
usb_cardp->tx_aggr_ctrl.aggr_mode = MLAN_USB_AGGR_MODE_NUM;
|
|
usb_cardp->tx_aggr_ctrl.aggr_align =
|
|
MAX(max_tx_buf, MLAN_USB_TX_AGGR_ALIGN);
|
|
usb_cardp->tx_aggr_ctrl.aggr_max = MLAN_USB_TX_MAX_AGGR_NUM;
|
|
usb_cardp->tx_aggr_ctrl.aggr_tmo =
|
|
MLAN_USB_TX_AGGR_TIMEOUT_MSEC * 1000;
|
|
usb_cardp->rx_deaggr_ctrl.enable = MFALSE;
|
|
usb_cardp->rx_deaggr_ctrl.aggr_mode = MLAN_USB_AGGR_MODE_NUM;
|
|
usb_cardp->rx_deaggr_ctrl.aggr_align = MLAN_USB_RX_ALIGN_SIZE;
|
|
usb_cardp->rx_deaggr_ctrl.aggr_max = MLAN_USB_RX_MAX_AGGR_NUM;
|
|
usb_cardp->rx_deaggr_ctrl.aggr_tmo =
|
|
MLAN_USB_RX_DEAGGR_TIMEOUT_USEC;
|
|
usb_set_intfdata(intf, usb_cardp);
|
|
|
|
card_type = woal_update_card_type(usb_cardp);
|
|
if (!card_type) {
|
|
PRINTM(MERROR,
|
|
"usb probe: woal_update_card_type() failed\n");
|
|
goto error;
|
|
}
|
|
|
|
/* At this point wlan_add_card() will be called */
|
|
if (!(woal_add_card(usb_cardp, &usb_cardp->udev->dev, &usb_ops,
|
|
card_type))) {
|
|
PRINTM(MERROR, "%s: woal_add_card failed\n",
|
|
__FUNCTION__);
|
|
goto error;
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
|
|
/* Note: From 2.6.34.2 onwards, remote wakeup is NOT enabled by
|
|
* default. So drivers wanting remote wakeup will have to enable
|
|
* this using -
|
|
* device_set_wakeup_enable(&udev->dev, 1);
|
|
* It has been observed that some cards having device attr =
|
|
* 0xa0 do not support remote wakeup. These cards come
|
|
* immediately out of the suspend when power/wakeup file is set
|
|
* to 'enabled'. To support all types of cards i.e. with/without
|
|
* remote wakeup, we are NOT setting the 'power/wakeup' file
|
|
* from here. Also in principle, we are not supposed to change
|
|
* the wakeup policy, which is purely a userspace decision.
|
|
*/
|
|
/* if (udev->actconfig->desc.bmAttributes &
|
|
USB_CONFIG_ATT_WAKEUP) intf->needs_remote_wakeup = 1; */
|
|
#endif
|
|
usb_get_dev(udev);
|
|
LEAVE();
|
|
return 0;
|
|
} else {
|
|
PRINTM(MINFO, "Discard the Probe request\n");
|
|
PRINTM(MINFO, "VID = 0x%X PID = 0x%X\n",
|
|
woal_cpu_to_le16(udev->descriptor.idVendor),
|
|
woal_cpu_to_le16(udev->descriptor.idProduct));
|
|
}
|
|
error:
|
|
kfree(usb_cardp);
|
|
usb_cardp = NULL;
|
|
LEAVE();
|
|
return -ENXIO;
|
|
}
|
|
|
|
/**
|
|
* @brief Free resource and cleanup
|
|
*
|
|
* @param intf Pointer to usb_interface
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void woal_usb_disconnect(struct usb_interface *intf)
|
|
{
|
|
struct usb_card_rec *cardp = usb_get_intfdata(intf);
|
|
moal_handle *phandle = NULL;
|
|
ENTER();
|
|
if (!cardp || !cardp->phandle) {
|
|
PRINTM(MERROR, "Card or phandle is not valid\n");
|
|
LEAVE();
|
|
return;
|
|
}
|
|
phandle = (moal_handle *)cardp->phandle;
|
|
|
|
/*
|
|
* Update Surprise removed to TRUE
|
|
* Free all the URB's allocated
|
|
*/
|
|
phandle->surprise_removed = MTRUE;
|
|
|
|
/* Card is removed and we can call wlan_remove_card */
|
|
PRINTM(MINFO, "Call remove card\n");
|
|
woal_remove_card(cardp);
|
|
|
|
usb_set_intfdata(intf, NULL);
|
|
usb_put_dev(interface_to_usbdev(intf));
|
|
kfree(cardp);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief killall pending urbs
|
|
*
|
|
* @param handle Pointer to moal_handle
|
|
*
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void woal_kill_urbs(moal_handle *handle)
|
|
{
|
|
ENTER();
|
|
handle->is_suspended = MTRUE;
|
|
woal_usb_unlink_urb(handle->card);
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief resubmit urbs
|
|
*
|
|
* @param handle Pointer to moal_handle
|
|
*
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void woal_resubmit_urbs(moal_handle *handle)
|
|
{
|
|
struct usb_card_rec *cardp = handle->card;
|
|
|
|
ENTER();
|
|
handle->is_suspended = MFALSE;
|
|
|
|
if (!atomic_read(&cardp->rx_data_urb_pending)) {
|
|
/* Submit multiple Rx data URBs */
|
|
woal_usb_submit_rx_data_urbs(handle);
|
|
}
|
|
if (!atomic_read(&cardp->rx_cmd_urb_pending)) {
|
|
cardp->rx_cmd.pmbuf =
|
|
woal_alloc_mlan_buffer(handle, MLAN_RX_CMD_BUF_SIZE);
|
|
if (cardp->rx_cmd.pmbuf)
|
|
woal_usb_submit_rx_urb(&cardp->rx_cmd,
|
|
MLAN_RX_CMD_BUF_SIZE);
|
|
}
|
|
LEAVE();
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
/**
|
|
* @brief Handle suspend
|
|
*
|
|
* @param intf Pointer to usb_interface
|
|
* @param message Pointer to pm_message_t structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
static int woal_usb_suspend(struct usb_interface *intf, pm_message_t message)
|
|
{
|
|
struct usb_card_rec *cardp = usb_get_intfdata(intf);
|
|
moal_handle *handle = NULL;
|
|
int i;
|
|
int ret = 0;
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MCMND, "<--- Enter woal_usb_suspend --->\n");
|
|
if (!cardp || !cardp->phandle) {
|
|
PRINTM(MERROR, "Card or moal_handle structure is not valid\n");
|
|
ret = 0;
|
|
goto done;
|
|
}
|
|
handle = cardp->phandle;
|
|
if (handle->is_suspended == MTRUE) {
|
|
PRINTM(MWARN, "Device already suspended\n");
|
|
ret = 0;
|
|
goto done;
|
|
}
|
|
#ifdef STA_SUPPORT
|
|
for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) {
|
|
if (handle->priv[i] &&
|
|
(GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA))
|
|
woal_cancel_scan(handle->priv[i], MOAL_IOCTL_WAIT);
|
|
}
|
|
#endif
|
|
/* Enable Host Sleep */
|
|
woal_enable_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY));
|
|
|
|
/* Indicate device suspended */
|
|
/* The flag must be set here before the usb_kill_urb() calls.
|
|
* Reason: In the complete handlers, urb->status(= -ENOENT) and
|
|
* 'is_suspended' flag is used in combination to distinguish
|
|
* between a suspended state and a 'disconnect' one.
|
|
*/
|
|
handle->is_suspended = MTRUE;
|
|
for (i = 0; i < handle->priv_num; i++)
|
|
netif_carrier_off(handle->priv[i]->netdev);
|
|
|
|
/* Unlink Rx cmd URB */
|
|
if (atomic_read(&cardp->rx_cmd_urb_pending) && cardp->rx_cmd.urb) {
|
|
usb_kill_urb(cardp->rx_cmd.urb);
|
|
}
|
|
/* Unlink Rx data URBs */
|
|
if (atomic_read(&cardp->rx_data_urb_pending)) {
|
|
for (i = 0; i < MVUSB_RX_DATA_URB; i++) {
|
|
if (cardp->rx_data_list[i].urb) {
|
|
usb_kill_urb(cardp->rx_data_list[i].urb);
|
|
usb_init_urb(cardp->rx_data_list[i].urb);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Unlink Tx data URBs */
|
|
for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) {
|
|
if (cardp->tx_data_list[i].urb) {
|
|
usb_kill_urb(cardp->tx_data_list[i].urb);
|
|
}
|
|
}
|
|
/* Unlink Tx cmd URB */
|
|
if (cardp->tx_cmd.urb) {
|
|
usb_kill_urb(cardp->tx_cmd.urb);
|
|
}
|
|
|
|
handle->suspend_wait_q_woken = MTRUE;
|
|
wake_up_interruptible(&handle->suspend_wait_q);
|
|
|
|
done:
|
|
PRINTM(MCMND, "<--- Leave woal_usb_suspend --->\n");
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Handle resume
|
|
*
|
|
* @param intf Pointer to usb_interface
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
static int woal_usb_resume(struct usb_interface *intf)
|
|
{
|
|
struct usb_card_rec *cardp = usb_get_intfdata(intf);
|
|
moal_handle *handle = NULL;
|
|
int i;
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MCMND, "<--- Enter woal_usb_resume --->\n");
|
|
if (!cardp || !cardp->phandle) {
|
|
PRINTM(MERROR, "Card or adapter structure is not valid\n");
|
|
goto done;
|
|
}
|
|
handle = cardp->phandle;
|
|
|
|
if (handle->is_suspended == MFALSE) {
|
|
PRINTM(MWARN, "Device already resumed\n");
|
|
goto done;
|
|
}
|
|
|
|
/* Indicate device resumed.
|
|
* The netdev queue will be resumed only after the urbs
|
|
* have been resubmitted */
|
|
handle->is_suspended = MFALSE;
|
|
|
|
if (!atomic_read(&cardp->rx_data_urb_pending)) {
|
|
/* Submit multiple Rx data URBs */
|
|
woal_usb_submit_rx_data_urbs(handle);
|
|
}
|
|
if (!atomic_read(&cardp->rx_cmd_urb_pending)) {
|
|
cardp->rx_cmd.pmbuf =
|
|
woal_alloc_mlan_buffer(handle, MLAN_RX_CMD_BUF_SIZE);
|
|
if (cardp->rx_cmd.pmbuf)
|
|
woal_usb_submit_rx_urb(&cardp->rx_cmd,
|
|
MLAN_RX_CMD_BUF_SIZE);
|
|
}
|
|
|
|
for (i = 0; i < handle->priv_num; i++)
|
|
if (handle->priv[i]->media_connected == MTRUE)
|
|
netif_carrier_on(handle->priv[i]->netdev);
|
|
|
|
/* Disable Host Sleep */
|
|
if (handle->hs_activated)
|
|
woal_cancel_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY),
|
|
MOAL_NO_WAIT);
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
|
|
/* Resume handler may be called due to remote wakeup,
|
|
force to exit suspend anyway */
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
|
|
cardp->udev->autosuspend_disabled = 1;
|
|
#else
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
cardp->udev->dev.power.runtime_auto = 0;
|
|
#endif
|
|
#endif /* < 2.6.35 */
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
|
|
cardp->udev->autoresume_disabled = 0;
|
|
#endif /* < 2.6.33 */
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
atomic_inc(&(cardp->udev)->dev.power.usage_count);
|
|
#endif
|
|
#endif /* >= 2.6.34 */
|
|
#endif /* >= 2.6.24 */
|
|
|
|
done:
|
|
PRINTM(MCMND, "<--- Leave woal_usb_resume --->\n");
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_PM */
|
|
|
|
/**
|
|
* @brief This function initialize the tx URBs
|
|
*
|
|
* @param handle Pointer to moal_handle structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
mlan_status woal_usb_tx_init(moal_handle *handle)
|
|
{
|
|
struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card;
|
|
int i;
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
cardp->tx_cmd.handle = handle;
|
|
cardp->tx_cmd.ep = cardp->tx_cmd_ep;
|
|
/* Allocate URB for command */
|
|
cardp->tx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
if (!cardp->tx_cmd.urb) {
|
|
PRINTM(MERROR, "Tx command URB allocation failed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto init_exit;
|
|
}
|
|
|
|
cardp->tx_data_ix = 0;
|
|
for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) {
|
|
cardp->tx_data_list[i].handle = handle;
|
|
cardp->tx_data_list[i].ep = cardp->tx_data_ep;
|
|
/* Allocate URB for data */
|
|
cardp->tx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
if (!cardp->tx_data_list[i].urb) {
|
|
PRINTM(MERROR, "Tx data URB allocation failed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto init_exit;
|
|
}
|
|
}
|
|
|
|
init_exit:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function submits the rx data URBs
|
|
*
|
|
* @param handle Pointer to moal_handle structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
mlan_status woal_usb_submit_rx_data_urbs(moal_handle *handle)
|
|
{
|
|
struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card;
|
|
int i;
|
|
mlan_status ret = MLAN_STATUS_FAILURE;
|
|
t_u32 buffer_len = MLAN_RX_DATA_BUF_SIZE;
|
|
|
|
ENTER();
|
|
|
|
if (cardp->rx_deaggr_ctrl.enable) {
|
|
buffer_len = cardp->rx_deaggr_ctrl.aggr_max;
|
|
if (cardp->rx_deaggr_ctrl.aggr_mode == MLAN_USB_AGGR_MODE_NUM) {
|
|
buffer_len *= MAX(MLAN_USB_MAX_PKT_SIZE,
|
|
cardp->rx_deaggr_ctrl.aggr_align);
|
|
buffer_len = MAX(buffer_len, MLAN_RX_DATA_BUF_SIZE);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MVUSB_RX_DATA_URB; i++) {
|
|
/* Submit Rx data URB */
|
|
if (!cardp->rx_data_list[i].pmbuf) {
|
|
if (woal_usb_submit_rx_urb(&cardp->rx_data_list[i],
|
|
buffer_len))
|
|
continue;
|
|
}
|
|
ret = MLAN_STATUS_SUCCESS;
|
|
}
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function initialize the rx URBs and submit them
|
|
*
|
|
* @param handle Pointer to moal_handle structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
mlan_status woal_usb_rx_init(moal_handle *handle)
|
|
{
|
|
struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card;
|
|
int i;
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
cardp->rx_cmd.handle = handle;
|
|
cardp->rx_cmd.ep = cardp->rx_cmd_ep;
|
|
/* Allocate URB for command/event */
|
|
cardp->rx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
if (!cardp->rx_cmd.urb) {
|
|
PRINTM(MERROR, "Rx command URB allocation failed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto init_exit;
|
|
}
|
|
|
|
cardp->rx_cmd.pmbuf =
|
|
woal_alloc_mlan_buffer(handle, MLAN_RX_CMD_BUF_SIZE);
|
|
if (cardp->rx_cmd.pmbuf) {
|
|
/* Submit Rx command URB */
|
|
if (woal_usb_submit_rx_urb(&cardp->rx_cmd,
|
|
MLAN_RX_CMD_BUF_SIZE)) {
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto init_exit;
|
|
}
|
|
}
|
|
for (i = 0; i < MVUSB_RX_DATA_URB; i++) {
|
|
cardp->rx_data_list[i].handle = handle;
|
|
cardp->rx_data_list[i].ep = cardp->rx_data_ep;
|
|
/* Allocate URB for data */
|
|
cardp->rx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
if (!cardp->rx_data_list[i].urb) {
|
|
PRINTM(MERROR, "Rx data URB allocation failed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto init_exit;
|
|
}
|
|
}
|
|
ret = woal_usb_submit_rx_data_urbs(handle);
|
|
if (ret) {
|
|
PRINTM(MERROR, "Rx data URB submission failed\n");
|
|
goto init_exit;
|
|
}
|
|
|
|
init_exit:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function downloads data blocks to device
|
|
*
|
|
* @param handle Pointer to moal_handle structure
|
|
* @param pmbuf Pointer to mlan_buffer structure
|
|
* @param ep Endpoint to send
|
|
* @param timeout Timeout value in milliseconds (if 0 the wait is forever)
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
static mlan_status woal_usb_write_data_sync(moal_handle *handle,
|
|
mlan_buffer *pmbuf, t_u32 endpoint,
|
|
t_u32 timeout)
|
|
{
|
|
struct usb_card_rec *cardp = handle->card;
|
|
t_u8 *data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset);
|
|
t_u8 ep = endpoint;
|
|
t_u32 length = pmbuf->data_len;
|
|
int actual_length;
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
int bulk_out_maxpktsize = 512;
|
|
|
|
if (ep == cardp->tx_cmd_ep)
|
|
bulk_out_maxpktsize = cardp->tx_cmd_maxpktsize;
|
|
else if (ep == cardp->tx_data_ep)
|
|
bulk_out_maxpktsize = cardp->tx_data_maxpktsize;
|
|
|
|
if (length % bulk_out_maxpktsize == 0)
|
|
length++;
|
|
|
|
/* Send the data block */
|
|
ret = usb_bulk_msg(cardp->udev, usb_sndbulkpipe(cardp->udev, ep),
|
|
(t_u8 *)data, length, &actual_length, timeout);
|
|
if (ret) {
|
|
PRINTM(MERROR, "usb_blk_msg for send failed, ret %d\n", ret);
|
|
ret = MLAN_STATUS_FAILURE;
|
|
}
|
|
|
|
pmbuf->data_len = actual_length;
|
|
DBG_HEXDUMP(MIF_D, "write sync", data, actual_length);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function read data blocks to device
|
|
*
|
|
* @param handle Pointer to moal_handle structure
|
|
* @param pmbuf Pointer to mlan_buffer structure
|
|
* @param ep Endpoint to receive
|
|
* @param timeout Timeout value in milliseconds (if 0 the wait is forever)
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
static mlan_status woal_usb_read_data_sync(moal_handle *handle,
|
|
mlan_buffer *pmbuf, t_u32 endpoint,
|
|
t_u32 timeout)
|
|
{
|
|
struct usb_card_rec *cardp = handle->card;
|
|
t_u8 *data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset);
|
|
t_u8 ep = endpoint;
|
|
t_u32 buf_len = pmbuf->data_len;
|
|
int actual_length;
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
ENTER();
|
|
/* Receive the data response */
|
|
ret = usb_bulk_msg(cardp->udev, usb_rcvbulkpipe(cardp->udev, ep), data,
|
|
buf_len, &actual_length, timeout);
|
|
if (ret) {
|
|
PRINTM(MERROR, "usb_bulk_msg failed: %d\n", ret);
|
|
ret = MLAN_STATUS_FAILURE;
|
|
}
|
|
pmbuf->data_len = actual_length;
|
|
DBG_HEXDUMP(MIF_D, "read sync", data, actual_length);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function downloads data/command packet to device
|
|
*
|
|
* @param handle Pointer to moal_handle structure
|
|
* @param pmbuf Pointer to mlan_buffer structure
|
|
* @param ep Endpoint to send
|
|
*
|
|
* @return MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE or
|
|
* MLAN_STATUS_RESOURCE
|
|
*/
|
|
mlan_status woal_write_data_async(moal_handle *handle, mlan_buffer *pmbuf,
|
|
t_u8 ep)
|
|
{
|
|
struct usb_card_rec *cardp = handle->card;
|
|
urb_context *context = NULL;
|
|
t_u8 *data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset);
|
|
struct urb *tx_urb = NULL;
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
t_u32 data_len = pmbuf->data_len;
|
|
int bulk_out_maxpktsize = 512;
|
|
|
|
ENTER();
|
|
|
|
/* Check if device is removed */
|
|
if (handle->surprise_removed) {
|
|
PRINTM(MERROR, "Device removed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto tx_ret;
|
|
}
|
|
|
|
if ((ep == cardp->tx_data_ep) &&
|
|
(atomic_read(&cardp->tx_data_urb_pending) >= MVUSB_TX_HIGH_WMARK)) {
|
|
ret = MLAN_STATUS_RESOURCE;
|
|
goto tx_ret;
|
|
}
|
|
PRINTM(MINFO, "woal_write_data_async: ep=%d\n", ep);
|
|
|
|
if (ep == cardp->tx_cmd_ep) {
|
|
context = &cardp->tx_cmd;
|
|
bulk_out_maxpktsize = cardp->tx_cmd_maxpktsize;
|
|
} else {
|
|
if (ep == cardp->tx_data_ep) {
|
|
bulk_out_maxpktsize = cardp->tx_data_maxpktsize;
|
|
if (cardp->tx_data_ix >= MVUSB_TX_HIGH_WMARK)
|
|
cardp->tx_data_ix = 0;
|
|
context = &cardp->tx_data_list[cardp->tx_data_ix++];
|
|
}
|
|
}
|
|
|
|
if (!context) {
|
|
PRINTM(MERROR, "Cannot allocate Tx urb_context\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto tx_ret;
|
|
}
|
|
context->handle = handle;
|
|
context->ep = ep;
|
|
context->pmbuf = pmbuf;
|
|
|
|
tx_urb = context->urb;
|
|
|
|
if (data_len % bulk_out_maxpktsize == 0)
|
|
data_len++;
|
|
|
|
/*
|
|
* Use USB API usb_fill_bulk_urb() to set the
|
|
* configuration information of the Tx bulk URB
|
|
* and initialize the Tx callback
|
|
*/
|
|
|
|
if (ep == cardp->tx_cmd_ep &&
|
|
cardp->tx_cmd_ep_type == USB_ENDPOINT_XFER_INT) {
|
|
usb_fill_int_urb(tx_urb, cardp->udev,
|
|
usb_sndintpipe(cardp->udev, ep), data,
|
|
data_len, woal_usb_tx_complete,
|
|
(void *)context, cardp->tx_cmd_interval);
|
|
} else
|
|
usb_fill_bulk_urb(tx_urb, cardp->udev,
|
|
usb_sndbulkpipe(cardp->udev, ep), data,
|
|
data_len, woal_usb_tx_complete,
|
|
(void *)context);
|
|
/* We find on Ubuntu 12.10 this flag does not work */
|
|
// tx_urb->transfer_flags |= URB_ZERO_PACKET;
|
|
|
|
if (ep == cardp->tx_cmd_ep)
|
|
atomic_inc(&cardp->tx_cmd_urb_pending);
|
|
else if (ep == cardp->tx_data_ep)
|
|
atomic_inc(&cardp->tx_data_urb_pending);
|
|
if (usb_submit_urb(tx_urb, GFP_ATOMIC)) {
|
|
/* Submit URB failure */
|
|
PRINTM(MERROR, "Submit EP %d Tx URB failed: %d\n", ep, ret);
|
|
if (ep == cardp->tx_cmd_ep)
|
|
atomic_dec(&cardp->tx_cmd_urb_pending);
|
|
else {
|
|
if (ep == cardp->tx_data_ep) {
|
|
atomic_dec(&cardp->tx_data_urb_pending);
|
|
if (cardp->tx_data_ix)
|
|
cardp->tx_data_ix--;
|
|
else
|
|
cardp->tx_data_ix = MVUSB_TX_HIGH_WMARK;
|
|
}
|
|
}
|
|
ret = MLAN_STATUS_FAILURE;
|
|
} else {
|
|
if (ep == cardp->tx_data_ep &&
|
|
(atomic_read(&cardp->tx_data_urb_pending) ==
|
|
MVUSB_TX_HIGH_WMARK))
|
|
ret = MLAN_STATUS_PRESOURCE;
|
|
else
|
|
ret = MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
tx_ret:
|
|
|
|
if (!ret)
|
|
ret = MLAN_STATUS_PENDING;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function register usb device and initialize parameter
|
|
*
|
|
* @param handle Pointer to moal_handle structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
static mlan_status woal_usb_register_dev(moal_handle *handle)
|
|
{
|
|
struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card;
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
cardp->phandle = handle;
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
static void woal_usb_unregister_dev(moal_handle *handle)
|
|
{
|
|
struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card;
|
|
PRINTM(MMSG, "USB: unregister device\n");
|
|
woal_usb_free(cardp);
|
|
cardp->phandle = NULL;
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function registers driver.
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
mlan_status woal_usb_bus_register(void)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
ENTER();
|
|
|
|
if (skip_fwdnld) {
|
|
woal_usb_driver.id_table = woal_usb_table_skip_fwdnld;
|
|
}
|
|
/*
|
|
* API registers the NXP USB driver
|
|
* to the USB system
|
|
*/
|
|
if (usb_register(&woal_usb_driver)) {
|
|
PRINTM(MFATAL, "USB Driver Registration Failed \n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
}
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function removes usb driver.
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void woal_usb_bus_unregister(void)
|
|
{
|
|
ENTER();
|
|
/* API unregisters the driver from USB subsystem */
|
|
usb_deregister(&woal_usb_driver);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function check if this is second mac
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
* @return MTRUE/MFALSE
|
|
*
|
|
*/
|
|
static t_u8 woal_usb_is_second_mac(moal_handle *handle)
|
|
{
|
|
return ((struct usb_card_rec *)(handle->card))->second_mac;
|
|
}
|
|
|
|
#ifdef CONFIG_USB_SUSPEND
|
|
/**
|
|
* @brief This function makes USB device to suspend.
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
*
|
|
* @return 0 --success, otherwise fail
|
|
*/
|
|
int woal_enter_usb_suspend(moal_handle *handle)
|
|
{
|
|
struct usb_device *udev = ((struct usb_card_rec *)(handle->card))->udev;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
|
|
struct usb_interface *intf =
|
|
((struct usb_card_rec *)(handle->card))->intf;
|
|
#endif /* < 2.6.34 */
|
|
#endif /* >= 2.6.24 */
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MIOCTL, "USB suspend ioctl (state %d)\n", udev->state);
|
|
|
|
if (handle->is_suspended == MTRUE) {
|
|
PRINTM(MERROR, "Device already suspended\n");
|
|
LEAVE();
|
|
return -EFAULT;
|
|
}
|
|
|
|
handle->suspend_wait_q_woken = MFALSE;
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
|
|
/* Enter into USB suspend */
|
|
usb_lock_device(udev);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
|
|
udev->autosuspend_delay = 0; /* Autosuspend delay in jiffies */
|
|
#else
|
|
pm_runtime_set_autosuspend_delay(&udev->dev, 0); /* Autosuspend delay in
|
|
jiffies */
|
|
#endif /* < 2.6.38 */
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
|
|
udev->autosuspend_disabled = 0; /* /sys/bus/usb/devices/.../power/level
|
|
< auto */
|
|
#endif /* < 2.6.34 */
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
|
|
udev->autoresume_disabled = 0;
|
|
#endif /* < 2.6.33 */
|
|
usb_unlock_device(udev);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
|
|
intf->pm_usage_cnt = 1;
|
|
#else
|
|
atomic_set(&intf->pm_usage_cnt, 1);
|
|
#endif /* < 2.6.32 */
|
|
usb_autopm_put_interface(intf);
|
|
#else
|
|
usb_lock_device(udev);
|
|
atomic_set(&udev->dev.power.usage_count, 1);
|
|
usb_enable_autosuspend(udev);
|
|
usb_unlock_device(udev);
|
|
#endif /* < 2.6.34 */
|
|
#endif /* >= 2.6.24 */
|
|
|
|
/* Wait for suspend to complete */
|
|
wait_event_interruptible(handle->suspend_wait_q,
|
|
handle->suspend_wait_q_woken);
|
|
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief This function makes USB device to resume.
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
*
|
|
* @return 0 --success, otherwise fail
|
|
*/
|
|
int woal_exit_usb_suspend(moal_handle *handle)
|
|
{
|
|
struct usb_device *udev = ((struct usb_card_rec *)(handle->card))->udev;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
|
|
struct usb_interface *intf =
|
|
((struct usb_card_rec *)(handle->card))->intf;
|
|
#endif /* < 2.6.34 */
|
|
#endif /* >= 2.6.24 */
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MIOCTL, "USB resume ioctl (state %d)\n", udev->state);
|
|
|
|
if (handle->is_suspended == MFALSE ||
|
|
udev->state != USB_STATE_SUSPENDED) {
|
|
PRINTM(MERROR, "Device already resumed\n");
|
|
LEAVE();
|
|
return -EFAULT;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
|
|
/* Exit from USB suspend */
|
|
usb_lock_device(udev);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
|
|
udev->autosuspend_disabled = 1; /* /sys/bus/usb/devices/.../power/level
|
|
< on */
|
|
#endif /* < 2.6.34 */
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
|
|
udev->autoresume_disabled = 0;
|
|
#endif /* < 2.6.33 */
|
|
usb_unlock_device(udev);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
|
|
intf->pm_usage_cnt = 0;
|
|
#else
|
|
atomic_set(&intf->pm_usage_cnt, 0);
|
|
#endif /* < 2.6.32 */
|
|
usb_autopm_get_interface(intf);
|
|
#else
|
|
usb_lock_device(udev);
|
|
atomic_set(&udev->dev.power.usage_count, 0);
|
|
usb_disable_autosuspend(udev);
|
|
usb_unlock_device(udev);
|
|
#endif /* < 2.6.34 */
|
|
#endif /* >= 2.6.24 */
|
|
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_USB_SUSPEND */
|
|
|
|
/**
|
|
* @brief This function will submit rx urb.
|
|
*
|
|
* @param handle Pointer to moal_handle
|
|
* @param ep Endpoint to re-submit urb
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void woal_submit_rx_urb(moal_handle *handle, t_u8 ep)
|
|
{
|
|
struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card;
|
|
|
|
ENTER();
|
|
|
|
if ((ep == cardp->rx_cmd_ep) &&
|
|
(!atomic_read(&cardp->rx_cmd_urb_pending))) {
|
|
woal_usb_submit_rx_urb(&cardp->rx_cmd, MLAN_RX_CMD_BUF_SIZE);
|
|
}
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function dump firmware memory to file
|
|
*
|
|
* @param phandle A pointer to moal_handle
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void woal_usb_dump_fw_info(moal_handle *phandle)
|
|
{
|
|
moal_private *priv = NULL;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_misc_cfg *pcfg_misc = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
priv = woal_get_priv(phandle, MLAN_BSS_ROLE_ANY);
|
|
if (!priv) {
|
|
PRINTM(MERROR, "woal_usb_dump_fw_info: priv is NULL!\n");
|
|
goto done;
|
|
}
|
|
/* Allocate an IOCTL request buffer */
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
|
|
if (req == NULL) {
|
|
PRINTM(MERROR, "woal_usb_dump_fw_info: alloc req fail!\n");
|
|
goto done;
|
|
}
|
|
|
|
/* Fill request buffer */
|
|
pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf;
|
|
pcfg_misc->sub_command = MLAN_OID_MISC_FW_DUMP_EVENT;
|
|
req->req_id = MLAN_IOCTL_MISC_CFG;
|
|
req->action = MLAN_ACT_GET;
|
|
|
|
/* Send IOCTL request to MLAN */
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
phandle->is_fw_dump_timer_set = MTRUE;
|
|
woal_mod_timer(&phandle->fw_dump_timer, MOAL_TIMER_5S);
|
|
|
|
done:
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
static mlan_status woal_usb_get_fw_name(moal_handle *handle)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
#if defined(USB8997) || defined(USB9098) || defined(USB9097) || \
|
|
defined(USB8978) || defined(USBNW62X)
|
|
t_u32 revision_id = 0;
|
|
t_u32 strap = 0;
|
|
#endif
|
|
struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card;
|
|
#if defined(USB9098)
|
|
moal_handle *ref_handle = NULL;
|
|
#endif
|
|
|
|
ENTER();
|
|
if (handle->params.fw_name)
|
|
goto done;
|
|
if (cardp->boot_state == USB_FW_READY)
|
|
goto done;
|
|
#ifdef USB8801
|
|
if (IS_USB8801(handle->card_type))
|
|
goto done;
|
|
#endif
|
|
|
|
#if defined(USB8997) || defined(USB9098) || defined(USB9097) || \
|
|
defined(USB8978) || defined(USBNW62X)
|
|
ret = woal_check_chip_revision(handle, &revision_id, &strap);
|
|
if (ret != MLAN_STATUS_SUCCESS) {
|
|
PRINTM(MFATAL, "Chip revision check failure!\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
PRINTM(MCMND, "revision=0x%x, strap=0x%x\n", revision_id, strap);
|
|
#endif
|
|
|
|
#ifdef USB8997
|
|
if (IS_USB8997(handle->card_type)) {
|
|
if (strap == CARD_TYPE_USB_UART)
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUART8997_DEFAULT_COMBO_FW_NAME);
|
|
else if (strap != 0)
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUSB8997_DEFAULT_COMBO_FW_NAME);
|
|
}
|
|
#endif
|
|
|
|
#ifdef USB8978
|
|
if (IS_USB8978(handle->card_type)) {
|
|
if (strap == CARD_TYPE_USB_UART)
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUART8978_DEFAULT_COMBO_FW_NAME);
|
|
else if (strap != 0)
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUSB8978_DEFAULT_COMBO_FW_NAME);
|
|
}
|
|
#endif
|
|
|
|
#ifdef USB9098
|
|
if (IS_USB9098(handle->card_type)) {
|
|
if (cardp->second_mac) {
|
|
ref_handle = (moal_handle *)handle->pref_mac;
|
|
if (ref_handle) {
|
|
strcpy(handle->card_info->fw_name,
|
|
ref_handle->card_info->fw_name);
|
|
strcpy(handle->card_info->fw_name_wlan,
|
|
ref_handle->card_info->fw_name_wlan);
|
|
}
|
|
goto done;
|
|
}
|
|
switch (revision_id) {
|
|
case USB9098_Z1Z2:
|
|
if (strap != 0) {
|
|
if (strap == CARD_TYPE_USB_UART)
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUART9098_DEFAULT_COMBO_FW_NAME);
|
|
else
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUSB9098_DEFAULT_COMBO_FW_NAME);
|
|
}
|
|
strcpy(handle->card_info->fw_name_wlan,
|
|
USB9098_DEFAULT_WLAN_FW_NAME);
|
|
break;
|
|
case USB9098_A0:
|
|
case USB9098_A1:
|
|
case USB9098_A2:
|
|
if (strap != 0) {
|
|
if (strap == CARD_TYPE_USB_UART)
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUART9098_COMBO_V1_FW_NAME);
|
|
else
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUSB9098_COMBO_V1_FW_NAME);
|
|
}
|
|
strcpy(handle->card_info->fw_name_wlan,
|
|
USB9098_WLAN_V1_FW_NAME);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef USB9097
|
|
if (IS_USB9097(handle->card_type)) {
|
|
switch (revision_id) {
|
|
case USB9097_B0:
|
|
case USB9097_B1:
|
|
if (strap != 0) {
|
|
if (strap == CARD_TYPE_USB_UART)
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUART9097_COMBO_V1_FW_NAME);
|
|
else
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUSB9097_COMBO_V1_FW_NAME);
|
|
}
|
|
strcpy(handle->card_info->fw_name_wlan,
|
|
USB9097_WLAN_V1_FW_NAME);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef USBNW62X
|
|
if (IS_USBNW62X(handle->card_type)) {
|
|
if (strap == CARD_TYPE_USB_UART)
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUARTNW62X_COMBO_FW_NAME);
|
|
else
|
|
strcpy(handle->card_info->fw_name,
|
|
USBUSBNW62X_COMBO_FW_NAME);
|
|
}
|
|
#endif
|
|
|
|
done:
|
|
PRINTM(MCMND, "combo fw:%s wlan fw:%s \n", handle->card_info->fw_name,
|
|
handle->card_info->fw_name_wlan);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
static moal_if_ops usb_ops = {
|
|
.register_dev = woal_usb_register_dev,
|
|
.unregister_dev = woal_usb_unregister_dev,
|
|
.read_data_sync = woal_usb_read_data_sync,
|
|
.write_data_sync = woal_usb_write_data_sync,
|
|
.get_fw_name = woal_usb_get_fw_name,
|
|
.dump_fw_info = woal_usb_dump_fw_info,
|
|
.is_second_mac = woal_usb_is_second_mac,
|
|
};
|