blob: c4035a12eef1a0a773be665cdb94175059604a70 [file] [log] [blame]
/*
* Copyright (c) 2013-2016 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 <qdf_time.h>
#include <qdf_lock.h>
#include <qdf_mem.h>
#include <qdf_util.h>
#include <qdf_defer.h>
#include <qdf_atomic.h>
#include <qdf_nbuf.h>
#include "qdf_net_types.h"
#include <hif_usb_internal.h>
#include <htc_services.h>
#include <hif_debug.h>
#define ATH_MODULE_NAME hif
#include <a_debug.h>
#if defined(WLAN_DEBUG) || defined(DEBUG)
static ATH_DEBUG_MASK_DESCRIPTION g_hif_debug_description[] = {
{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_hif_debug_description),
g_hif_debug_description);
#endif
#ifdef USB_ISOC_SUPPORT
unsigned int hif_usb_isoch_vo = 1;
#else
unsigned int hif_usb_isoch_vo;
#endif
unsigned int hif_usb_disable_rxdata2 = 1;
/**
* usb_hif_usb_transmit_complete() - completion routing for tx urb's
* @urb: pointer to urb for which tx completion is called
*
* Return: none
*/
static void usb_hif_usb_transmit_complete(struct urb *urb)
{
HIF_URB_CONTEXT *urb_context = (HIF_URB_CONTEXT *) urb->context;
qdf_nbuf_t buf;
HIF_USB_PIPE *pipe = urb_context->pipe;
struct hif_usb_send_context *send_context;
HIF_DBG("+%s: pipe: %d, stat:%d, len:%d", __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) {
HIF_ERROR("%s: pipe: %d, failed:%d",
__func__, pipe->logical_pipe_num, urb->status);
}
buf = urb_context->buf;
send_context = urb_context->send_context;
if (send_context->new_alloc)
qdf_mem_free(send_context);
else
qdf_nbuf_pull_head(buf, send_context->head_data_len);
urb_context->buf = NULL;
usb_hif_cleanup_transmit_urb(urb_context);
/* note: queue implements a lock */
skb_queue_tail(&pipe->io_comp_queue, buf);
HIF_USB_SCHEDULE_WORK(pipe)
HIF_DBG("-%s", __func__);
}
/**
* hif_send_internal() - HIF internal routine to prepare and submit tx urbs
* @hif_usb_device: pointer to HIF_DEVICE_USB structure
* @pipe_id: HIF pipe on which data is to be sent
* @hdr_buf: any header buf to be prepended, currently ignored
* @buf: qdf_nbuf_t containing data to be transmitted
* @nbytes: number of bytes to be transmitted
*
* Return: QDF_STATUS_SUCCESS on success and error QDF status on failure
*/
static QDF_STATUS hif_send_internal(HIF_DEVICE_USB *hif_usb_device,
uint8_t pipe_id,
qdf_nbuf_t hdr_buf,
qdf_nbuf_t buf, unsigned int nbytes)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
HIF_DEVICE_USB *device = hif_usb_device;
HIF_USB_PIPE *pipe = &device->pipes[pipe_id];
HIF_URB_CONTEXT *urb_context;
uint8_t *data;
uint32_t len;
struct urb *urb;
int usb_status;
int i;
struct hif_usb_send_context *send_context;
int frag_count = 0, head_data_len, tmp_frag_count = 0;
unsigned char *data_ptr;
HIF_DBG("+%s pipe : %d, buf:0x%p nbytes %u",
__func__, pipe_id, buf, nbytes);
frag_count = qdf_nbuf_get_num_frags(buf);
if (frag_count > 1) { /* means have extra fragment buf in skb */
/* header data length should be total sending length substract
* internal data length of netbuf
* | hif_usb_send_context | fragments except internal buffer |
* netbuf->data
*/
head_data_len = sizeof(struct hif_usb_send_context);
while (tmp_frag_count < (frag_count - 1)) {
head_data_len =
head_data_len +
qdf_nbuf_get_frag_len(buf, tmp_frag_count);
tmp_frag_count = tmp_frag_count + 1;
}
} else {
/*
* | hif_usb_send_context | netbuf->data
*/
head_data_len = sizeof(struct hif_usb_send_context);
}
/* Check whether head room is enough to save extra head data */
if (head_data_len <= qdf_nbuf_headroom(buf)) {
send_context = (struct hif_usb_send_context *)
qdf_nbuf_push_head(buf, head_data_len);
send_context->new_alloc = false;
} else {
send_context =
qdf_mem_malloc(sizeof(struct hif_usb_send_context)
+ head_data_len + nbytes);
if (send_context == NULL) {
HIF_ERROR("%s: qdf_mem_malloc failed", __func__);
status = QDF_STATUS_E_NOMEM;
goto err;
}
send_context->new_alloc = true;
}
send_context->netbuf = buf;
send_context->hif_usb_device = hif_usb_device;
send_context->transfer_id = pipe_id;
send_context->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
*/
data_ptr = (unsigned char *)send_context +
sizeof(struct hif_usb_send_context);
for (i = 0;
i < (send_context->new_alloc ? frag_count : frag_count - 1); i++) {
int frag_len = qdf_nbuf_get_frag_len(buf, i);
unsigned char *frag_addr = qdf_nbuf_get_frag_vaddr(buf, i);
qdf_mem_copy(data_ptr, frag_addr, frag_len);
data_ptr += frag_len;
}
/* Reset pData pointer and send out */
data_ptr = (unsigned char *)send_context +
sizeof(struct hif_usb_send_context);
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
*/
HIF_ERROR("%s pipe:%d no urbs left. URB Cnt : %d",
__func__, pipe_id, pipe->urb_cnt);
status = QDF_STATUS_E_RESOURCES;
goto err;
}
urb_context->send_context = send_context;
urb = urb_context->urb;
urb_context->buf = buf;
data = data_ptr;
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 */
HIF_DBG
("athusb bulk send submit:%d, 0x%X (ep:0x%2.2X), %d bytes",
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 (send_context->new_alloc)
qdf_mem_free(send_context);
else
qdf_nbuf_pull_head(buf, head_data_len);
urb_context->buf = NULL;
HIF_ERROR("athusb : usb bulk transmit failed %d",
usb_status);
usb_hif_remove_pending_transfer(urb_context);
usb_hif_cleanup_transmit_urb(urb_context);
status = QDF_STATUS_E_FAILURE;
goto err;
}
err:
if (!QDF_IS_STATUS_SUCCESS(status) &&
(status != QDF_STATUS_E_RESOURCES)) {
HIF_ERROR("athusb send failed %d", status);
}
HIF_DBG("-%s pipe : %d", __func__, pipe_id);
return status;
}
/**
* hif_send_head() - HIF routine exposed to upper layers to send data
* @scn: pointer to hif_opaque_softc structure
* @pipe_id: HIF pipe on which data is to be sent
* @transfer_id: endpoint ID on which data is to be sent
* @nbytes: number of bytes to be transmitted
* @wbuf: qdf_nbuf_t containing data to be transmitted
* @hdr_buf: any header buf to be prepended, currently ignored
* @data_attr: data_attr field from cvg_nbuf_cb of wbuf
*
* Return: QDF_STATUS_SUCCESS on success and error QDF status on failure
*/
QDF_STATUS hif_send_head(struct hif_opaque_softc *scn, uint8_t pipe_id,
uint32_t transfer_id, uint32_t nbytes,
qdf_nbuf_t wbuf, uint32_t data_attr)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(scn);
HIF_TRACE("+%s", __func__);
status = hif_send_internal(device, pipe_id, NULL, wbuf, nbytes);
HIF_TRACE("-%s", __func__);
return status;
}
/**
* hif_get_free_queue_number() - get # of free TX resources in a given HIF pipe
* @scn: pointer to hif_opaque_softc structure
* @pipe_id: HIF pipe which is being polled for free resources
*
* Return: # of free resources in pipe_id
*/
uint16_t hif_get_free_queue_number(struct hif_opaque_softc *scn, uint8_t pipe_id)
{
HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(scn);
return device->pipes[pipe_id].urb_cnt;
}
/**
* hif_post_init() - copy HTC callbacks to HIF
* @scn: pointer to hif_opaque_softc structure
* @target: pointer to HTC_TARGET structure
* @callbacks: htc callbacks
*
* Return: none
*/
void hif_post_init(struct hif_opaque_softc *scn, void *target,
struct hif_msg_callbacks *callbacks)
{
HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(scn);
qdf_mem_copy(&device->htc_callbacks, callbacks,
sizeof(device->htc_callbacks));
}
/**
* hif_detach_htc() - remove HTC callbacks from HIF
* @scn: pointer to hif_opaque_softc structure
*
* Return: none
*/
void hif_detach_htc(struct hif_opaque_softc *scn)
{
HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(scn);
usb_hif_flush_all(device);
qdf_mem_zero(&device->htc_callbacks, sizeof(device->htc_callbacks));
}
/**
* hif_usb_device_deinit() - de- init HIF_DEVICE_USB, cleanup pipe resources
* @sc: pointer to hif_usb_softc structure
*
* Return: None
*/
void hif_usb_device_deinit(struct hif_usb_softc *sc)
{
HIF_DEVICE_USB *device = &sc->hif_hdl;
HIF_TRACE("+%s", __func__);
usb_hif_cleanup_pipe_resources(device);
usb_set_intfdata(device->interface, NULL);
if (device->diag_cmd_buffer != NULL)
qdf_mem_free(device->diag_cmd_buffer);
if (device->diag_resp_buffer != NULL)
qdf_mem_free(device->diag_resp_buffer);
HIF_TRACE("-%s", __func__);
}
/**
* hif_usb_device_init() - init HIF_DEVICE_USB, setup pipe resources
* @sc: pointer to hif_usb_softc structure
*
* Return: QDF_STATUS_SUCCESS on success or a QDF error
*/
QDF_STATUS hif_usb_device_init(struct hif_usb_softc *sc)
{
int i;
HIF_DEVICE_USB *device = &sc->hif_hdl;
struct usb_interface *interface = sc->interface;
struct usb_device *dev = interface_to_usbdev(interface);
QDF_STATUS status = QDF_STATUS_SUCCESS;
HIF_USB_PIPE *pipe;
HIF_TRACE("+%s", __func__);
do {
usb_set_intfdata(interface, device);
qdf_spinlock_create(&(device->cs_lock));
qdf_spinlock_create(&(device->rx_lock));
qdf_spinlock_create(&(device->tx_lock));
device->udev = dev;
device->interface = interface;
HIF_ERROR("%s device %p device->udev %p device->interface %p",
__func__,
device,
device->udev,
device->interface);
for (i = 0; i < HIF_USB_PIPE_MAX; i++) {
pipe = &device->pipes[i];
HIF_USB_INIT_WORK(pipe);
skb_queue_head_init(&pipe->io_comp_queue);
}
device->diag_cmd_buffer =
qdf_mem_malloc(USB_CTRL_MAX_DIAG_CMD_SIZE);
if (NULL == device->diag_cmd_buffer) {
status = QDF_STATUS_E_NOMEM;
break;
}
device->diag_resp_buffer =
qdf_mem_malloc(USB_CTRL_MAX_DIAG_RESP_SIZE);
if (NULL == device->diag_resp_buffer) {
status = QDF_STATUS_E_NOMEM;
break;
}
status = usb_hif_setup_pipe_resources(device);
} while (false);
if (status != QDF_STATUS_SUCCESS)
HIF_ERROR("%s: abnormal condition", __func__);
HIF_TRACE("+%s", __func__);
return status;
}
/**
* hif_start() - Enable HIF TX and RX
* @scn: pointer to hif_opaque_softc structure
*
* Return: QDF_STATUS_SUCCESS if success else an appropriate QDF_STATUS error
*/
QDF_STATUS hif_start(struct hif_opaque_softc *scn)
{
HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(scn);
int i;
HIF_TRACE("+%s", __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;
}
HIF_TRACE("-%s", __func__);
return QDF_STATUS_SUCCESS;
}
/**
* hif_usb_stop_device() - Stop/flush all HIF communication
* @scn: pointer to hif_opaque_softc structure
*
* Return: none
*/
void hif_usb_stop_device(struct hif_softc *hif_sc)
{
HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(hif_sc);
HIF_TRACE("+%s", __func__);
usb_hif_flush_all(device);
HIF_TRACE("-%s", __func__);
}
/**
* hif_get_default_pipe() - get default pipes for HIF TX/RX
* @scn: pointer to hif_opaque_softc structure
* @ul_pipe: pointer to TX pipe
* @ul_pipe: pointer to TX pipe
*
* Return: none
*/
void hif_get_default_pipe(struct hif_opaque_softc *scn, uint8_t *ul_pipe,
uint8_t *dl_pipe)
{
*ul_pipe = HIF_TX_CTRL_PIPE;
*dl_pipe = HIF_RX_CTRL_PIPE;
}
#if defined(USB_MULTI_IN_TEST) || defined(USB_ISOC_TEST)
/**
* hif_map_service_to_pipe() - maps ul/dl pipe to service id.
* @scn: HIF context
* @svc_id: sevice index
* @ul_pipe: pointer to uplink pipe id
* @dl_pipe: pointer to down-linklink pipe id
* @ul_is_polled: if ul is polling based
* @ul_is_polled: if dl is polling based
*
* Return: QDF_STATUS_SUCCESS if success else an appropriate QDF_STATUS error
*/
int hif_map_service_to_pipe(struct hif_opaque_softc *scn, uint16_t svc_id,
uint8_t *ul_pipe, uint8_t *dl_pipe,
int *ul_is_polled, int *dl_is_polled)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
switch (svc_id) {
case HTC_CTRL_RSVD_SVC:
case WMI_CONTROL_SVC:
case HTC_RAW_STREAMS_SVC:
*ul_pipe = HIF_TX_CTRL_PIPE;
*dl_pipe = HIF_RX_DATA_PIPE;
break;
case WMI_DATA_BE_SVC:
*ul_pipe = HIF_TX_DATA_LP_PIPE;
*dl_pipe = HIF_RX_DATA_PIPE;
break;
case WMI_DATA_BK_SVC:
*ul_pipe = HIF_TX_DATA_MP_PIPE;
*dl_pipe = HIF_RX_DATA2_PIPE;
break;
case WMI_DATA_VI_SVC:
*ul_pipe = HIF_TX_DATA_HP_PIPE;
*dl_pipe = HIF_RX_DATA_PIPE;
break;
case WMI_DATA_VO_SVC:
*ul_pipe = HIF_TX_DATA_LP_PIPE;
*dl_pipe = HIF_RX_DATA_PIPE;
break;
default:
status = QDF_STATUS_E_FAILURE;
break;
}
return status;
}
#else
#ifdef QCA_TX_HTT2_SUPPORT
#define USB_TX_CHECK_HTT2_SUPPORT 1
#else
#define USB_TX_CHECK_HTT2_SUPPORT 0
#endif
/**
* hif_map_service_to_pipe() - maps ul/dl pipe to service id.
* @scn: HIF context
* @svc_id: sevice index
* @ul_pipe: pointer to uplink pipe id
* @dl_pipe: pointer to down-linklink pipe id
* @ul_is_polled: if ul is polling based
* @ul_is_polled: if dl is polling based
*
* Return: QDF_STATUS_SUCCESS if success else an appropriate QDF_STATUS error
*/
int hif_map_service_to_pipe(struct hif_opaque_softc *scn, uint16_t svc_id,
uint8_t *ul_pipe, uint8_t *dl_pipe,
int *ul_is_polled, int *dl_is_polled)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
switch (svc_id) {
case HTC_CTRL_RSVD_SVC:
case WMI_CONTROL_SVC:
*ul_pipe = HIF_TX_CTRL_PIPE;
*dl_pipe = HIF_RX_DATA_PIPE;
break;
case WMI_DATA_BE_SVC:
case WMI_DATA_BK_SVC:
*ul_pipe = HIF_TX_DATA_LP_PIPE;
if (hif_usb_disable_rxdata2)
*dl_pipe = HIF_RX_DATA_PIPE;
else
*dl_pipe = HIF_RX_DATA2_PIPE;
break;
case WMI_DATA_VI_SVC:
*ul_pipe = HIF_TX_DATA_MP_PIPE;
if (hif_usb_disable_rxdata2)
*dl_pipe = HIF_RX_DATA_PIPE;
else
*dl_pipe = HIF_RX_DATA2_PIPE;
break;
case WMI_DATA_VO_SVC:
*ul_pipe = HIF_TX_DATA_HP_PIPE;
if (hif_usb_disable_rxdata2)
*dl_pipe = HIF_RX_DATA_PIPE;
else
*dl_pipe = HIF_RX_DATA2_PIPE;
break;
case HTC_RAW_STREAMS_SVC:
*ul_pipe = HIF_TX_CTRL_PIPE;
*dl_pipe = HIF_RX_DATA_PIPE;
break;
case HTT_DATA_MSG_SVC:
*ul_pipe = HIF_TX_DATA_LP_PIPE;
if (hif_usb_disable_rxdata2)
*dl_pipe = HIF_RX_DATA_PIPE;
else
*dl_pipe = HIF_RX_DATA2_PIPE;
break;
case HTT_DATA2_MSG_SVC:
if (USB_TX_CHECK_HTT2_SUPPORT) {
*ul_pipe = HIF_TX_DATA_HP_PIPE;
if (hif_usb_disable_rxdata2)
*dl_pipe = HIF_RX_DATA_PIPE;
else
*dl_pipe = HIF_RX_DATA2_PIPE;
}
break;
default:
status = QDF_STATUS_E_FAILURE;
break;
}
return status;
}
#endif
/**
* hif_ctrl_msg_exchange() - send usb ctrl message and receive response
* @macp: pointer to HIF_DEVICE_USB
* @send_req_val: USB send message request value
* @send_msg: pointer to data to send
* @len: length in bytes of the data to send
* @response_req_val: USB response message request value
* @response_msg: pointer to response msg
* @response_len: length of the response message
*
* Return: QDF_STATUS_SUCCESS if success else an appropriate QDF_STATUS error
*/
static QDF_STATUS hif_ctrl_msg_exchange(HIF_DEVICE_USB *macp,
uint8_t send_req_val,
uint8_t *send_msg,
uint32_t len,
uint8_t response_req_val,
uint8_t *response_msg,
uint32_t *response_len)
{
QDF_STATUS status;
do {
/* send command */
status = usb_hif_submit_ctrl_out(macp, send_req_val, 0, 0,
send_msg, len);
if (!QDF_IS_STATUS_SUCCESS(status))
break;
if (NULL == response_msg) {
/* no expected response */
break;
}
/* get response */
status = usb_hif_submit_ctrl_in(macp, response_req_val, 0, 0,
response_msg, *response_len);
if (!QDF_IS_STATUS_SUCCESS(status))
break;
} while (false);
return status;
}
/**
* hif_exchange_bmi_msg() - send/recev ctrl message of type BMI_CMD/BMI_RESP
* @scn: pointer to hif_opaque_softc
* @bmi_request: pointer to data to send
* @request_length: length in bytes of the data to send
* @bmi_response: pointer to response msg
* @bmi_response_length: length of the response message
* @timeout_ms: timeout to wait for response (ignored in current implementation)
*
* Return: QDF_STATUS_SUCCESS if success else an appropriate QDF_STATUS error
*/
QDF_STATUS hif_exchange_bmi_msg(struct hif_opaque_softc *scn,
qdf_dma_addr_t cmd, qdf_dma_addr_t rsp,
uint8_t *bmi_request,
uint32_t request_length,
uint8_t *bmi_response,
uint32_t *bmi_response_lengthp,
uint32_t timeout_ms)
{
HIF_DEVICE_USB *macp = HIF_GET_USB_DEVICE(scn);
return hif_ctrl_msg_exchange(macp,
USB_CONTROL_REQ_SEND_BMI_CMD,
bmi_request,
request_length,
USB_CONTROL_REQ_RECV_BMI_RESP,
bmi_response, bmi_response_lengthp);
}
/**
* hif_diag_read_access() - Read data from target memory or register
* @scn: pointer to hif_opaque_softc
* @address: register address to read from
* @data: pointer to buffer to store the value read from the register
*
* Return: QDF_STATUS_SUCCESS if success else an appropriate QDF_STATUS error
*/
QDF_STATUS hif_diag_read_access(struct hif_opaque_softc *scn, uint32_t address,
uint32_t *data)
{
HIF_DEVICE_USB *macp = HIF_GET_USB_DEVICE(scn);
QDF_STATUS status;
USB_CTRL_DIAG_CMD_READ *cmd;
uint32_t respLength;
cmd = (USB_CTRL_DIAG_CMD_READ *) macp->diag_cmd_buffer;
qdf_mem_zero(cmd, sizeof(*cmd));
cmd->Cmd = USB_CTRL_DIAG_CC_READ;
cmd->Address = address;
respLength = sizeof(USB_CTRL_DIAG_RESP_READ);
status = hif_ctrl_msg_exchange(macp,
USB_CONTROL_REQ_DIAG_CMD,
(uint8_t *) cmd,
sizeof(*cmd),
USB_CONTROL_REQ_DIAG_RESP,
macp->diag_resp_buffer, &respLength);
if (QDF_IS_STATUS_SUCCESS(status)) {
USB_CTRL_DIAG_RESP_READ *pResp =
(USB_CTRL_DIAG_RESP_READ *) macp->diag_resp_buffer;
*data = pResp->ReadValue;
status = QDF_STATUS_SUCCESS;
} else {
status = QDF_STATUS_E_FAILURE;
}
return status;
}
/**
* hif_diag_write_access() - write data to target memory or register
* @scn: pointer to hif_opaque_softc
* @address: register address to write to
* @data: value to be written to the address
*
* Return: QDF_STATUS_SUCCESS if success else an appropriate QDF_STATUS error
*/
QDF_STATUS hif_diag_write_access(struct hif_opaque_softc *scn,
uint32_t address,
uint32_t data)
{
HIF_DEVICE_USB *macp = HIF_GET_USB_DEVICE(scn);
USB_CTRL_DIAG_CMD_WRITE *cmd;
cmd = (USB_CTRL_DIAG_CMD_WRITE *) macp->diag_cmd_buffer;
qdf_mem_zero(cmd, sizeof(*cmd));
cmd->Cmd = USB_CTRL_DIAG_CC_WRITE;
cmd->Address = address;
cmd->Value = data;
return hif_ctrl_msg_exchange(macp,
USB_CONTROL_REQ_DIAG_CMD,
(uint8_t *) cmd,
sizeof(*cmd), 0, NULL, 0);
}
/**
* hif_dump_info() - dump info about all HIF pipes and endpoints
* @scn: pointer to hif_opaque_softc
*
* Return: none
*/
void hif_dump_info(struct hif_opaque_softc *scn)
{
HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(scn);
HIF_USB_PIPE *pipe = NULL;
struct usb_host_interface *iface_desc = NULL;
struct usb_endpoint_descriptor *ep_desc;
uint8_t i = 0;
for (i = 0; i < HIF_USB_PIPE_MAX; i++) {
pipe = &device->pipes[i];
HIF_ERROR("PipeIndex : %d URB Cnt : %d PipeHandle : %x",
i, pipe->urb_cnt,
pipe->usb_pipe_handle);
if (usb_pipeisoc(pipe->usb_pipe_handle))
HIF_INFO("Pipe Type ISOC");
else if (usb_pipebulk(pipe->usb_pipe_handle))
HIF_INFO("Pipe Type BULK");
else if (usb_pipeint(pipe->usb_pipe_handle))
HIF_INFO("Pipe Type INT");
else if (usb_pipecontrol(pipe->usb_pipe_handle))
HIF_INFO("Pipe Type control");
}
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
ep_desc = &iface_desc->endpoint[i].desc;
if (ep_desc) {
HIF_INFO(
"ep_desc : %p Index : %d: DescType : %d Addr : %d Maxp : %d Atrrib : %d",
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)) {
HIF_INFO("ISOC EP Detected");
}
}
}
}
/**
* hif_flush_surprise_remove() - Cleanup residual buffers for device shutdown
* @scn: HIF context
*
* Not applicable to USB bus
*
* Return: none
*/
void hif_flush_surprise_remove(struct hif_opaque_softc *scn)
{
/* TO DO... */
}
/**
* hif_diag_read_mem() -read nbytes of data from target memory or register
* @scn: pointer to hif_opaque_softc
* @address: register address to read from
* @data: buffer to store the value read
* @nbytes: number of bytes to be read from 'address'
*
* Return: QDF_STATUS_SUCCESS if success else an appropriate QDF_STATUS error
*/
QDF_STATUS hif_diag_read_mem(struct hif_opaque_softc *scn,
uint32_t address, uint8_t *data,
int nbytes)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
HIF_TRACE("+%s", __func__);
if ((address & 0x3) || ((uintptr_t)data & 0x3))
return QDF_STATUS_E_IO;
while ((nbytes >= 4) &&
QDF_IS_STATUS_SUCCESS(status =
hif_diag_read_access(scn,
address,
(uint32_t *)data))) {
nbytes -= sizeof(uint32_t);
address += sizeof(uint32_t);
data += sizeof(uint32_t);
}
HIF_TRACE("-%s", __func__);
return status;
}
/**
* hif_diag_write_mem() -write nbytes of data to target memory or register
* @scn: pointer to hif_opaque_softc
* @address: register address to write to
* @data: buffer containing data to be written
* @nbytes: number of bytes to be written
*
* Return: QDF_STATUS_SUCCESS if success else an appropriate QDF_STATUS error
*/
QDF_STATUS hif_diag_write_mem(struct hif_opaque_softc *scn,
uint32_t address,
uint8_t *data, int nbytes)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
HIF_TRACE("+%s", __func__);
if ((address & 0x3) || ((uintptr_t)data & 0x3))
return QDF_STATUS_E_IO;
while (nbytes >= 4 &&
QDF_IS_STATUS_SUCCESS(status =
hif_diag_write_access(scn,
address,
*((uint32_t *)data)))) {
nbytes -= sizeof(uint32_t);
address += sizeof(uint32_t);
data += sizeof(uint32_t);
}
HIF_TRACE("-%s", __func__);
return status;
}
void hif_send_complete_check(struct hif_opaque_softc *scn,
uint8_t PipeID, int force)
{
/* NO-OP*/
}
/* 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
void hif_suspend_wow(struct hif_opaque_softc *scn)
{
HIF_INFO("HIFsuspendwow - TODO");
}
/**
* hif_usb_set_bundle_mode() - enable bundling and set default rx bundle cnt
* @scn: pointer to hif_opaque_softc structure
* @enabled: flag to enable/disable bundling
* @rx_bundle_cnt: bundle count to be used for RX
*
* Return: none
*/
void hif_usb_set_bundle_mode(struct hif_softc *scn,
bool enabled, int rx_bundle_cnt)
{
HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(scn);
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;
HIF_DBG("athusb bundle %s cnt %d", enabled ? "enabled" : "disabled",
rx_bundle_cnt);
}