blob: c7eecb700518602290e7625dfc90d5e045afa9bf [file] [log] [blame]
/*
* Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
#include <adf_os_types.h>
#include <adf_os_dma.h>
#include <adf_os_timer.h>
#include <adf_os_time.h>
#include <adf_os_lock.h>
#include <adf_os_io.h>
#include <adf_os_mem.h>
#include <adf_os_module.h>
#include <adf_os_util.h>
#include <adf_os_stdtypes.h>
#include <adf_os_defer.h>
#include <adf_os_atomic.h>
#include <adf_nbuf.h>
#include "adf_net_types.h"
#include <athdefs.h>
#include "a_types.h"
#include "athdefs.h"
#include "a_osapi.h"
#include <linux/usb.h>
#include "hif_usb_internal.h"
#include <hif.h>
#include <htc_services.h>
#include <ol_if_athvar.h>
#include <if_usb.h>
#define ATH_MODULE_NAME hif
#include <a_debug.h>
#define USB_HIF_USE_SINGLE_PIPE_FOR_DATA
#define USB_HIF_TARGET_CREDIT_SIZE 1664
#ifdef WLAN_DEBUG
static ATH_DEBUG_MASK_DESCRIPTION g_HIFDebugDescription[] = {
{USB_HIF_DEBUG_CTRL_TRANS, "Control Transfers"},
{USB_HIF_DEBUG_BULK_IN, "BULK In Transfers"},
{USB_HIF_DEBUG_BULK_OUT, "BULK Out Transfers"},
{USB_HIF_DEBUG_DUMP_DATA, "Dump data"},
{USB_HIF_DEBUG_ENUM, "Enumeration"},
};
ATH_DEBUG_INSTANTIATE_MODULE_VAR(hif,
"hif",
"USB Host Interface",
ATH_DEBUG_MASK_DEFAULTS | ATH_DEBUG_INFO |
USB_HIF_DEBUG_ENUM,
ATH_DEBUG_DESCRIPTION_COUNT
(g_HIFDebugDescription),
g_HIFDebugDescription);
#endif
/* use credit flow control over HTC */
unsigned int htc_credit_flow;
module_param(htc_credit_flow, uint, 0644);
#ifdef USB_ISOC_SUPPORT
unsigned int hif_usb_isoch_vo = 1;
#else
unsigned int hif_usb_isoch_vo;
#endif
module_param(hif_usb_isoch_vo, uint, 0644);
unsigned int hif_usb_disable_rxdata2 = 1;
module_param(hif_usb_disable_rxdata2, uint, 0644);
unsigned int hif_usbaudioclass;
module_param(hif_usbaudioclass, uint, 0644);
static void usb_hif_destroy(HIF_DEVICE_USB *device);
static HIF_DEVICE_USB *usb_hif_create(struct usb_interface *interface);
OSDRV_CALLBACKS osDrvcallback;
int notifyDeviceInsertedHandler(void *hHIF)
{
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
osDrvcallback.deviceInsertedHandler(osDrvcallback.context, hHIF);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
complete_and_exit(NULL, 0);
return 0;
}
int notifyDeviceSurprisedRemovedHandler(void *context)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) context;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
osDrvcallback.deviceRemovedHandler(device->claimed_context, device);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
complete_and_exit(NULL, 0);
return 0;
}
int HIF_USBDeviceInserted(struct usb_interface *interface, hif_handle_t hif_hdl)
{
HIF_DEVICE_USB *device = NULL;
int retval = -1;
struct hif_usb_softc *sc = hif_hdl;
struct usb_device *udev = interface_to_usbdev(interface);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
AR_DEBUG_PRINTF(ATH_DEBUG_INFO, ("BCDDevice : %x\n",
udev->descriptor.bcdDevice));
do {
device = usb_hif_create(interface);
if (NULL == device) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("device is NULL\n"));
break;
}
device->sc = sc;
sc->hif_device = (HIF_DEVICE *) device;
retval = 0;
} while (FALSE);
if ((device != NULL) && (retval < 0)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("abnormal condition\n"));
usb_hif_destroy(device);
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
return retval;
}
void HIF_USBDeviceDetached(struct usb_interface *interface,
a_uint8_t surprise_removed)
{
HIF_DEVICE_USB *device;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
do {
device = (HIF_DEVICE_USB *) usb_get_intfdata(interface);
if (NULL == device) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("device is NULL\n"));
break;
}
device->surpriseRemoved = surprise_removed;
/* inform upper layer if it is still interested */
if (surprise_removed
&& (osDrvcallback.deviceRemovedHandler != NULL)
&& (device->claimed_context != NULL)) {
osDrvcallback.deviceRemovedHandler(device->
claimed_context,
device);
}
usb_hif_destroy(device);
athdiag_procfs_remove();
} while (FALSE);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
}
static void usb_hif_usb_transmit_complete(struct urb *urb)
{
HIF_URB_CONTEXT *urb_context = (HIF_URB_CONTEXT *) urb->context;
adf_nbuf_t buf;
HIF_USB_PIPE *pipe = urb_context->pipe;
struct HIFSendContext *pSendContext;
AR_DEBUG_PRINTF(USB_HIF_DEBUG_BULK_OUT,
("+%s: pipe: %d, stat:%d, len:%d\n", __func__,
pipe->logical_pipe_num, urb->status,
urb->actual_length));
/* this urb is not pending anymore */
usb_hif_remove_pending_transfer(urb_context);
if (urb->status != 0) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: pipe: %d, failed:%d\n",
__func__,
pipe->logical_pipe_num,
urb->status));
}
a_mem_trace(urb_context->buf);
buf = urb_context->buf;
pSendContext = urb_context->pSendContext;
if (pSendContext->bNewAlloc) {
adf_os_mem_free((void *)pSendContext);
} else {
adf_nbuf_pull_head(buf, pSendContext->head_data_len);
}
urb_context->buf = NULL;
usb_hif_cleanup_transmit_urb(urb_context);
#if 0
if (pipe->urbcnt >= pipe->urb_cnt_thresh)
/* TBD */
#endif
/* note: queue implements a lock */
skb_queue_tail(&pipe->io_comp_queue, buf);
#ifdef HIF_USB_TASKLET
tasklet_schedule(&pipe->io_complete_tasklet);
#else
schedule_work(&pipe->io_complete_work);
#endif
AR_DEBUG_PRINTF(USB_HIF_DEBUG_BULK_OUT, ("-%s\n", __func__));
}
static A_STATUS HIFSend_internal(HIF_DEVICE *hifDevice, a_uint8_t PipeID,
adf_nbuf_t hdr_buf, adf_nbuf_t buf, unsigned int nbytes)
{
A_STATUS status = A_OK;
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hifDevice;
HIF_USB_PIPE *pipe = &device->pipes[PipeID];
HIF_URB_CONTEXT *urb_context;
A_UINT8 *data;
A_UINT32 len;
struct urb *urb;
int usb_status;
int i;
struct HIFSendContext *pSendContext;
uint8_t frag_count;
uint32_t head_data_len, tmp_frag_count = 0;
unsigned char *pData;
AR_DEBUG_PRINTF(USB_HIF_DEBUG_BULK_OUT, ("+%s pipe : %d, buf:0x%pK\n",
__func__, PipeID, buf));
a_mem_trace(buf);
frag_count = adf_nbuf_get_num_frags(buf);
if (frag_count == 1) {
/*
* | HIFSendContext | netbuf->data
*/
head_data_len = sizeof(struct HIFSendContext);
} else if ((frag_count - 1) <= CVG_NBUF_MAX_EXTRA_FRAGS) {
/*
* means have extra fragment buf in skb
* header data length should be total sending length substract
* internal data length of netbuf
* | HIFSendContext | fragments except internal buffer |
* netbuf->data
*/
head_data_len = sizeof(struct HIFSendContext);
while (tmp_frag_count < (frag_count - 1)) {
head_data_len =
head_data_len + adf_nbuf_get_frag_len(buf,
tmp_frag_count);
tmp_frag_count = tmp_frag_count + 1;
}
} else {
/* Extra fragments overflow */
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (
"%s Extra fragments count overflow : %d\n",
__func__, frag_count));
status = A_ERROR;
goto exit;
}
/* Check whether head room is enough to save extra head data */
if (head_data_len <= adf_nbuf_headroom(buf)) {
pSendContext = (struct HIFSendContext *)adf_nbuf_push_head(buf,
head_data_len);
pSendContext->bNewAlloc = FALSE;
} else {
pSendContext = adf_os_mem_alloc(NULL,
sizeof
(struct
HIFSendContext)
+
head_data_len
+
nbytes);
pSendContext->bNewAlloc = TRUE;
}
pSendContext->netbuf = buf;
pSendContext->pDev = hifDevice;
pSendContext->transferID = PipeID;
pSendContext->head_data_len = head_data_len;
/*
* Copy data to head part of netbuf or head of allocated buffer.
* if buffer is new allocated, the last buffer should be copied also.
* It assume last fragment is internal buffer of netbuf
* sometime total length of fragments larger than nbytes
*/
pData = (unsigned char *)pSendContext + sizeof(struct HIFSendContext);
for (i = 0; i < (pSendContext->bNewAlloc ? frag_count : frag_count - 1);
i++) {
int frag_len = adf_nbuf_get_frag_len(buf, i);
unsigned char *frag_addr = adf_nbuf_get_frag_vaddr(buf, i);
memcpy(pData, frag_addr, frag_len);
pData += frag_len;
}
/* Reset pData pointer and send out */
pData = (unsigned char *)pSendContext + sizeof(struct HIFSendContext);
do {
urb_context = usb_hif_alloc_urb_from_pipe(pipe);
if (NULL == urb_context) {
/* TODO : note, it is possible to run out of urbs if 2
* endpoints map to the same pipe ID
*/
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (
"%s pipe:%d no urbs left. URB Cnt : %d\n",
__func__, PipeID, pipe->urb_cnt));
status = A_NO_RESOURCE;
break;
}
urb_context->pSendContext = pSendContext;
urb = urb_context->urb;
urb_context->buf = buf;
data = pData;
len = nbytes;
usb_fill_bulk_urb(urb,
device->udev,
pipe->usb_pipe_handle,
data,
(len % pipe->max_packet_size) ==
0 ? (len + 1) : len,
usb_hif_usb_transmit_complete, urb_context);
if ((len % pipe->max_packet_size) == 0) {
/* hit a max packet boundary on this pipe */
/* urb->transfer_flags |= URB_ZERO_PACKET; */
}
AR_DEBUG_PRINTF(USB_HIF_DEBUG_BULK_OUT, (
"athusb bulk send submit:%d, 0x%X (ep:0x%2.2X), %d bytes\n",
pipe->logical_pipe_num, pipe->usb_pipe_handle,
pipe->ep_address, nbytes));
usb_hif_enqueue_pending_transfer(pipe, urb_context);
usb_status = usb_submit_urb(urb, GFP_ATOMIC);
if (usb_status) {
if (pSendContext->bNewAlloc)
adf_os_mem_free(pSendContext);
else
adf_nbuf_pull_head(buf, head_data_len);
urb_context->buf = NULL;
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (
"athusb : usb bulk transmit failed %d\n",
usb_status));
usb_hif_remove_pending_transfer(urb_context);
usb_hif_cleanup_transmit_urb(urb_context);
status = A_ECOMM;
break;
}
} while (FALSE);
exit:
if (A_FAILED(status) && (status != A_NO_RESOURCE)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("athusb send failed %d\n", status));
}
AR_DEBUG_PRINTF(USB_HIF_DEBUG_BULK_OUT,
("-%s pipe : %d\n", __func__, PipeID));
return status;
}
/* Send the entire buffer */
A_STATUS HIFSend(HIF_DEVICE *hif_device, a_uint8_t pipe, adf_nbuf_t hdr_buf,
adf_nbuf_t netbuf)
{
return HIFSend_head(hif_device, pipe, 0, adf_nbuf_len(netbuf), netbuf);
}
A_STATUS
HIFSend_head(HIF_DEVICE *hif_device,
a_uint8_t pipe, unsigned int transfer_id, unsigned int nbytes,
adf_nbuf_t nbuf)
{
A_STATUS status = EOK;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
status = HIFSend_internal(hif_device, pipe, NULL, nbuf, nbytes);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
return status;
}
a_uint16_t HIFGetFreeQueueNumber(HIF_DEVICE *hifDevice, a_uint8_t PipeID)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hifDevice;
return device->pipes[PipeID].urb_cnt;
}
a_uint16_t HIFGetMaxQueueNumber(HIF_DEVICE *hifDevice, a_uint8_t PipeID)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hifDevice;
return device->pipes[PipeID].urb_alloc;
}
void HIFPostInit(HIF_DEVICE *hifDevice, void *unused,
MSG_BASED_HIF_CALLBACKS *callbacks)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hifDevice;
A_MEMCPY(&device->htcCallbacks.Context, callbacks,
sizeof(MSG_BASED_HIF_CALLBACKS));
}
void HIFDetachHTC(HIF_DEVICE *hifDevice)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hifDevice;
usb_hif_flush_all(device);
A_MEMZERO(&device->htcCallbacks, sizeof(MSG_BASED_HIF_CALLBACKS));
}
static void usb_hif_destroy(HIF_DEVICE_USB *device)
{
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
usb_hif_flush_all(device);
usb_hif_cleanup_pipe_resources(device);
usb_set_intfdata(device->interface, NULL);
if (device->diag_cmd_buffer != NULL)
adf_os_mem_free(device->diag_cmd_buffer);
if (device->diag_resp_buffer != NULL)
adf_os_mem_free(device->diag_resp_buffer);
adf_os_mem_free(device);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
}
static HIF_DEVICE_USB *usb_hif_create(struct usb_interface *interface)
{
HIF_DEVICE_USB *device = NULL;
struct usb_device *dev = interface_to_usbdev(interface);
A_STATUS status = A_OK;
int i;
HIF_USB_PIPE *pipe;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
do {
device = adf_os_mem_alloc(NULL, sizeof(*device));
if (NULL == device) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("device is NULL\n"));
break;
}
adf_os_mem_zero(device, sizeof(*device));
usb_set_intfdata(interface, device);
spin_lock_init(&(device->cs_lock));
spin_lock_init(&(device->rx_lock));
spin_lock_init(&(device->tx_lock));
spin_lock_init(&(device->rx_prestart_lock));
device->udev = dev;
device->interface = interface;
for (i = 0; i < HIF_USB_PIPE_MAX; i++) {
pipe = &device->pipes[i];
#ifdef HIF_USB_TASKLET
tasklet_init(&pipe->io_complete_tasklet, usb_hif_io_comp_tasklet,
(long unsigned int)pipe);
#else
INIT_WORK(&pipe->io_complete_work,
usb_hif_io_comp_work);
#endif
skb_queue_head_init(&pipe->io_comp_queue);
}
device->diag_cmd_buffer = adf_os_mem_alloc(NULL,
USB_CTRL_MAX_DIAG_CMD_SIZE);
if (NULL == device->diag_cmd_buffer) {
status = A_NO_MEMORY;
break;
}
device->diag_resp_buffer = adf_os_mem_alloc(NULL,
USB_CTRL_MAX_DIAG_RESP_SIZE);
if (NULL == device->diag_resp_buffer) {
status = A_NO_MEMORY;
break;
}
status = usb_hif_setup_pipe_resources(device);
} while (FALSE);
if (A_FAILED(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("abnormal condition\n"));
usb_hif_destroy(device);
device = NULL;
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
return device;
}
A_STATUS HIFStart(HIF_DEVICE *hifDevice)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hifDevice;
int i;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
usb_hif_prestart_recv_pipes(device);
/* set the TX resource avail threshold for each TX pipe */
for (i = HIF_TX_CTRL_PIPE; i <= HIF_TX_DATA_HP_PIPE; i++) {
device->pipes[i].urb_cnt_thresh =
device->pipes[i].urb_alloc / 2;
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
return A_OK;
}
void HIFStop(HIF_DEVICE *hifDevice)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hifDevice;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
usb_hif_flush_all(device);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
}
void HIFGetDefaultPipe(HIF_DEVICE *hifDevice, a_uint8_t *ULPipe,
a_uint8_t *DLPipe)
{
*ULPipe = HIF_TX_CTRL_PIPE;
*DLPipe = HIF_RX_CTRL_PIPE;
}
#if defined(USB_MULTI_IN_TEST) || defined(USB_ISOC_TEST)
int
HIFMapServiceToPipe(HIF_DEVICE *hif_device, a_uint16_t ServiceId,
a_uint8_t *ULPipe, a_uint8_t *DLPipe, int *ul_is_polled,
int *dl_is_polled)
{
A_STATUS status = A_OK;
switch (ServiceId) {
case HTC_CTRL_RSVD_SVC:
case WMI_CONTROL_SVC:
case HTC_RAW_STREAMS_SVC:
*ULPipe = HIF_TX_CTRL_PIPE;
*DLPipe = HIF_RX_DATA_PIPE;
break;
case WMI_DATA_BE_SVC:
*ULPipe = HIF_TX_DATA_LP_PIPE;
*DLPipe = HIF_RX_DATA_PIPE;
break;
case WMI_DATA_BK_SVC:
*ULPipe = HIF_TX_DATA_MP_PIPE;
*DLPipe = HIF_RX_DATA2_PIPE;
break;
case WMI_DATA_VI_SVC:
*ULPipe = HIF_TX_DATA_HP_PIPE;
*DLPipe = HIF_RX_DATA_PIPE;
break;
case WMI_DATA_VO_SVC:
*ULPipe = HIF_TX_DATA_LP_PIPE;
*DLPipe = HIF_RX_DATA_PIPE;
break;
default:
status = A_ENOTSUP;
break;
}
return status;
}
#else
int
HIFMapServiceToPipe(HIF_DEVICE *hif_device, a_uint16_t ServiceId,
a_uint8_t *ULPipe, a_uint8_t *DLPipe, int *ul_is_polled,
int *dl_is_polled)
{
A_STATUS status = A_OK;
switch (ServiceId) {
case HTC_CTRL_RSVD_SVC:
case WMI_CONTROL_SVC:
*ULPipe = HIF_TX_CTRL_PIPE;
*DLPipe = HIF_RX_DATA_PIPE;
break;
case WMI_DATA_BE_SVC:
case WMI_DATA_BK_SVC:
*ULPipe = HIF_TX_DATA_LP_PIPE;
if (hif_usb_disable_rxdata2)
*DLPipe = HIF_RX_DATA_PIPE;
else
*DLPipe = HIF_RX_DATA2_PIPE;
break;
case WMI_DATA_VI_SVC:
#ifdef USB_HIF_USE_SINGLE_PIPE_FOR_DATA
*ULPipe = HIF_TX_DATA_LP_PIPE;
#else
*ULPipe = HIF_TX_DATA_MP_PIPE;
#endif
if (hif_usb_disable_rxdata2)
*DLPipe = HIF_RX_DATA_PIPE;
else
*DLPipe = HIF_RX_DATA2_PIPE;
break;
case WMI_DATA_VO_SVC:
#ifdef USB_HIF_USE_SINGLE_PIPE_FOR_DATA
*ULPipe = HIF_TX_DATA_LP_PIPE;
#else
*ULPipe = HIF_TX_DATA_HP_PIPE;
#endif
if (hif_usb_disable_rxdata2)
*DLPipe = HIF_RX_DATA_PIPE;
else
*DLPipe = HIF_RX_DATA2_PIPE;
#ifdef USB_HIF_TEST_INTERRUPT_IN
*DLPipe = HIF_RX_INT_PIPE;
#endif
break;
case HTC_RAW_STREAMS_SVC:
*ULPipe = HIF_TX_CTRL_PIPE;
*DLPipe = HIF_RX_DATA_PIPE;
break;
case HTT_DATA_MSG_SVC:
*ULPipe = HIF_TX_DATA_LP_PIPE;
if (hif_usb_disable_rxdata2)
*DLPipe = HIF_RX_DATA_PIPE;
else
*DLPipe = HIF_RX_DATA2_PIPE;
break;
#ifdef QCA_TX_HTT2_SUPPORT
case HTT_DATA2_MSG_SVC:
*ULPipe = HIF_TX_DATA_HP_PIPE;
if (hif_usb_disable_rxdata2)
*DLPipe = HIF_RX_DATA_PIPE;
else
*DLPipe = HIF_RX_DATA2_PIPE;
break;
#endif /* QCA_TX_HTT2_SUPPORT */
default:
status = A_ENOTSUP;
break;
}
return status;
}
#endif
static A_STATUS HIFCtrlMsgExchange(HIF_DEVICE_USB *macp,
A_UINT8 SendReqVal,
A_UINT8 *pSendMessage,
A_UINT32 Length,
A_UINT8 ResponseReqVal,
A_UINT8 *pResponseMessage,
A_UINT32 *pResponseLength)
{
A_STATUS status;
do {
/* send command */
status = usb_hif_submit_ctrl_out(macp, SendReqVal, 0, 0,
pSendMessage, Length);
if (A_FAILED(status))
break;
if (NULL == pResponseMessage) {
/* no expected response */
break;
}
/* get response */
status = usb_hif_submit_ctrl_in(macp, ResponseReqVal, 0, 0,
pResponseMessage,
*pResponseLength);
if (A_FAILED(status))
break;
} while (FALSE);
return status;
}
A_STATUS HIFExchangeBMIMsg(HIF_DEVICE *device,
A_UINT8 *pSendMessage,
A_UINT32 Length,
A_UINT8 *pResponseMessage,
A_UINT32 *pResponseLength, A_UINT32 TimeoutMS)
{
HIF_DEVICE_USB *macp = (HIF_DEVICE_USB *) device;
return HIFCtrlMsgExchange(macp,
USB_CONTROL_REQ_SEND_BMI_CMD,
pSendMessage,
Length,
USB_CONTROL_REQ_RECV_BMI_RESP,
pResponseMessage, pResponseLength);
}
A_STATUS HIFConfigureDevice(HIF_DEVICE *hif, HIF_DEVICE_CONFIG_OPCODE opcode,
void *config, A_UINT32 configLen)
{
A_STATUS status = A_OK;
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hif;
switch (opcode) {
case HIF_DEVICE_GET_OS_DEVICE:
{
HIF_DEVICE_OS_DEVICE_INFO *info = config;
info->pOSDevice = &device->udev->dev;
}
break;
case HIF_DEVICE_GET_MBOX_BLOCK_SIZE:
/* provide fake block sizes for mailboxes to satisfy upper layer
* software
*/
((A_UINT32 *) config)[0] = 16;
((A_UINT32 *) config)[1] = 16;
((A_UINT32 *) config)[2] = 16;
((A_UINT32 *) config)[3] = 16;
break;
default:
status = A_ENOTSUP;
break;
}
return status;
}
A_STATUS hifWaitForPendingRecv(HIF_DEVICE *device)
{
return A_OK;
}
A_STATUS HIFDiagReadAccess(HIF_DEVICE *hifDevice, A_UINT32 address,
A_UINT32 *data)
{
HIF_DEVICE_USB *macp = (HIF_DEVICE_USB *) hifDevice;
A_STATUS status;
USB_CTRL_DIAG_CMD_READ *cmd;
A_UINT32 respLength;
cmd = (USB_CTRL_DIAG_CMD_READ *) macp->diag_cmd_buffer;
A_MEMZERO(cmd, sizeof(*cmd));
cmd->Cmd = USB_CTRL_DIAG_CC_READ;
cmd->Address = address;
respLength = sizeof(USB_CTRL_DIAG_RESP_READ);
status = HIFCtrlMsgExchange(macp,
USB_CONTROL_REQ_DIAG_CMD,
(A_UINT8 *) cmd,
sizeof(*cmd),
USB_CONTROL_REQ_DIAG_RESP,
macp->diag_resp_buffer, &respLength);
if (A_SUCCESS(status)) {
USB_CTRL_DIAG_RESP_READ *pResp =
(USB_CTRL_DIAG_RESP_READ *) macp->diag_resp_buffer;
*data = pResp->ReadValue;
}
return status;
}
A_STATUS HIFDiagWriteAccess(HIF_DEVICE *hifDevice, A_UINT32 address,
A_UINT32 data)
{
HIF_DEVICE_USB *macp = (HIF_DEVICE_USB *) hifDevice;
USB_CTRL_DIAG_CMD_WRITE *cmd;
cmd = (USB_CTRL_DIAG_CMD_WRITE *) macp->diag_cmd_buffer;
A_MEMZERO(cmd, sizeof(*cmd));
cmd->Cmd = USB_CTRL_DIAG_CC_WRITE;
cmd->Address = address;
cmd->Value = data;
return HIFCtrlMsgExchange(macp,
USB_CONTROL_REQ_DIAG_CMD,
(A_UINT8 *) cmd,
sizeof(*cmd), 0, NULL, 0);
}
void HIFShutDownDevice(HIF_DEVICE *hif)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hif;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
if (NULL == hif) {
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("full shutdown\n"));
/* this is a full driver shutdown */
} else {
/* perform any actions to shutdown specific device */
usb_hif_flush_all(device);
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
}
void HIFReleaseDevice(HIF_DEVICE *hif)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hif;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
device->claimed_context = NULL;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
}
void HIFClaimDevice(HIF_DEVICE *hif, void *claimedContext)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hif;
device->claimed_context = claimedContext;
}
A_STATUS HIFInit(OSDRV_CALLBACKS *callbacks)
{
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HIFInit\n"));
A_MEMZERO(&osDrvcallback, sizeof(osDrvcallback));
A_REGISTER_MODULE_DEBUG_INFO(hif);
osDrvcallback.deviceInsertedHandler = callbacks->deviceInsertedHandler;
osDrvcallback.deviceRemovedHandler = callbacks->deviceRemovedHandler;
osDrvcallback.deviceSuspendHandler = callbacks->deviceSuspendHandler;
osDrvcallback.deviceResumeHandler = callbacks->deviceResumeHandler;
osDrvcallback.deviceWakeupHandler = callbacks->deviceWakeupHandler;
osDrvcallback.context = callbacks->context;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HIFInit\n"));
return A_OK;
}
#ifdef ATH_BUS_PM
void HIFDeviceSuspend(HIF_DEVICE *dev)
{
HIF_DEVICE_USB *macp = (HIF_DEVICE_USB *) dev;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
if (osDrvcallback.deviceSuspendHandler)
osDrvcallback.deviceSuspendHandler(macp->claimed_context);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
}
void HIFDeviceResume(HIF_DEVICE *dev)
{
HIF_DEVICE_USB *macp = (HIF_DEVICE_USB *) dev;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
if (osDrvcallback.deviceResumeHandler)
osDrvcallback.deviceResumeHandler(macp->claimed_context);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
}
#endif
void HIFDumpInfo(HIF_DEVICE *hif)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hif;
HIF_USB_PIPE *pipe = NULL;
struct usb_host_interface *iface_desc = NULL;
struct usb_endpoint_descriptor *ep_desc;
A_UINT8 i = 0;
for (i = 0; i < HIF_USB_PIPE_MAX; i++) {
pipe = &device->pipes[i];
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (
"PipeIndex : %d URB Cnt : %d PipeHandle : %x\n",
i, pipe->urb_cnt,
pipe->usb_pipe_handle));
if (usb_pipeisoc(pipe->usb_pipe_handle))
pr_info("Pipe Type ISOC\n");
else if (usb_pipebulk(pipe->usb_pipe_handle))
pr_info("Pipe Type BULK\n");
else if (usb_pipeint(pipe->usb_pipe_handle))
pr_info("Pipe Type INT\n");
else if (usb_pipecontrol(pipe->usb_pipe_handle))
pr_info("Pipe Type control\n");
}
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
ep_desc = &iface_desc->endpoint[i].desc;
if (ep_desc) {
pr_info(
"ep_desc : %pK Index : %d: DescType : %d Addr : %d Maxp : %d Atrrib : %d\n",
ep_desc, i,
ep_desc->bDescriptorType,
ep_desc->bEndpointAddress, ep_desc->wMaxPacketSize,
ep_desc->bmAttributes);
if ((ep_desc)
&& (usb_endpoint_type(ep_desc) ==
USB_ENDPOINT_XFER_ISOC)) {
pr_info("ISOC EP Detected\n");
}
}
}
}
void HIFDump(HIF_DEVICE *hif_device, u_int8_t cmd_id, bool start)
{
/* TO DO... */
}
void HIFFlushSurpriseRemove(HIF_DEVICE *hif_device)
{
/* TO DO... */
}
A_STATUS
HIFDiagReadMem(HIF_DEVICE *hif_device, A_UINT32 address, A_UINT8 *data,
int nbytes)
{
A_STATUS status = EOK;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
if ((address & 0x3) || ((uintptr_t)data & 0x3)) {
return (-EIO);
}
while ((nbytes >= 4) &&
(A_OK == (status = HIFDiagReadAccess(hif_device, address,
(A_UINT32*)data)))) {
nbytes -= sizeof(A_UINT32);
address += sizeof(A_UINT32);
data += sizeof(A_UINT32);
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
return status;
}
A_STATUS
HIFDiagWriteMem(HIF_DEVICE *hif_device, A_UINT32 address, A_UINT8 *data, int nbytes)
{
A_STATUS status = EOK;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
if ((address & 0x3) || ((uintptr_t)data & 0x3)) {
return (-EIO);
}
while ((nbytes >= 4) &&
(A_OK == (status = HIFDiagWriteAccess(hif_device, address,
*((A_UINT32*)data))))) {
nbytes -= sizeof(A_UINT32);
address += sizeof(A_UINT32);
data += sizeof(A_UINT32);
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
return status;
}
void *hif_get_targetdef(HIF_DEVICE *hif_device)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hif_device;
struct hif_usb_softc *sc = device->sc;
return sc->targetdef;
}
void HIFSendCompleteCheck(HIF_DEVICE *hif_device, a_uint8_t pipe, int force)
{
}
void HIFCancelDeferredTargetSleep(HIF_DEVICE *hif_device)
{
}
/* diagnostic command defnitions */
#define USB_CTRL_DIAG_CC_READ 0
#define USB_CTRL_DIAG_CC_WRITE 1
#define USB_CTRL_DIAG_CC_WARM_RESET 2
A_STATUS HIFDiagWriteWARMRESET(struct usb_interface *interface,
A_UINT32 address, A_UINT32 data)
{
HIF_DEVICE_USB *hHIF_DEV;
A_STATUS status = EOK;
USB_CTRL_DIAG_CMD_WRITE *cmd;
hHIF_DEV = (HIF_DEVICE_USB *) usb_get_intfdata(interface);
cmd = (USB_CTRL_DIAG_CMD_WRITE *) hHIF_DEV->diag_cmd_buffer;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+%s\n", __func__));
A_MEMZERO(cmd, sizeof(USB_CTRL_DIAG_CMD_WRITE));
cmd->Cmd = USB_CTRL_DIAG_CC_WARM_RESET;
cmd->Address = address;
cmd->Value = data;
status = HIFCtrlMsgExchange(hHIF_DEV,
USB_CONTROL_REQ_DIAG_CMD,
(A_UINT8 *) cmd,
sizeof(USB_CTRL_DIAG_CMD_WRITE),
0, NULL, 0);
if (A_FAILED(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("-%s HIFCtrlMsgExchange fail\n",
__func__));
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-%s\n", __func__));
return status;
}
void HIFsuspendwow(HIF_DEVICE *hif_device)
{
printk(KERN_INFO "HIFsuspendwow TODO\n");
}
void HIFSetBundleMode(HIF_DEVICE *hif_device, bool enabled, int rx_bundle_cnt)
{
HIF_DEVICE_USB *device = (HIF_DEVICE_USB *) hif_device;
device->is_bundle_enabled = enabled;
device->rx_bundle_cnt = rx_bundle_cnt;
if (device->is_bundle_enabled && (device->rx_bundle_cnt == 0)) {
device->rx_bundle_cnt = 1;
}
device->rx_bundle_buf_len = device->rx_bundle_cnt *
HIF_USB_RX_BUNDLE_ONE_PKT_SIZE;
AR_DEBUG_PRINTF(USB_HIF_DEBUG_BULK_IN,
("athusb bundle %s cnt %d\n",
enabled ? "enabled" : "disabled",
rx_bundle_cnt));
}
/**
* hif_is_80211_fw_wow_required() - API to check if target suspend is needed
*
* API determines if fw can be suspended and returns true/false to the caller.
* Caller will call WMA WoW API's to suspend.
* This API returns true only for SDIO bus types, for others it's a false.
*
* Return: bool
*/
bool hif_is_80211_fw_wow_required(void)
{
return true;
}