qcacmn: Add USB bus support (HIF USB)
Add HIF changes for USB bus support. Change-Id: I06609db68d537ba7c7716f2926589e0dbe738e58 CRs-Fixed: 1023663
This commit is contained in:

committed by
Vishwajith Upendra

szülő
5693051379
commit
bda5d43e70
918
hif/src/usb/hif_usb.c
Normal file
918
hif/src/usb/hif_usb.c
Normal file
@@ -0,0 +1,918 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#ifdef 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_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_set_bundle_mode(struct hif_opaque_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);
|
||||
}
|
127
hif/src/usb/hif_usb_internal.h
Normal file
127
hif/src/usb/hif_usb_internal.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _HIF_USB_INTERNAL_H
|
||||
#define _HIF_USB_INTERNAL_H
|
||||
|
||||
#include <qdf_nbuf.h>
|
||||
#include "a_types.h"
|
||||
#include "athdefs.h"
|
||||
#include "a_osapi.h"
|
||||
#include "a_usb_defs.h"
|
||||
#include <ol_if_athvar.h>
|
||||
#include <linux/usb.h>
|
||||
#include "hif.h"
|
||||
#include "if_usb.h"
|
||||
|
||||
#define TX_URB_COUNT 32
|
||||
#define RX_URB_COUNT 32
|
||||
|
||||
#define HIF_USB_RX_BUFFER_SIZE (1792 + 8)
|
||||
#define HIF_USB_RX_BUNDLE_ONE_PKT_SIZE (1792 + 8)
|
||||
|
||||
#ifdef HIF_USB_TASKLET
|
||||
#define HIF_USB_SCHEDULE_WORK(pipe)\
|
||||
tasklet_schedule(&pipe->io_complete_tasklet);
|
||||
|
||||
#define HIF_USB_INIT_WORK(pipe)\
|
||||
tasklet_init(&pipe->io_complete_tasklet,\
|
||||
usb_hif_io_comp_tasklet,\
|
||||
(long unsigned int)pipe);
|
||||
|
||||
#define HIF_USB_FLUSH_WORK(pipe) flush_work(&pipe->io_complete_work);
|
||||
#else
|
||||
#define HIF_USB_SCHEDULE_WORK(pipe) schedule_work(&pipe->io_complete_work);
|
||||
#define HIF_USB_INIT_WORK(pipe)\
|
||||
INIT_WORK(&pipe->io_complete_work,\
|
||||
usb_hif_io_comp_work);
|
||||
#define HIF_USB_FLUSH_WORK(pipe)
|
||||
#endif
|
||||
|
||||
/* debug masks */
|
||||
#define USB_HIF_DEBUG_CTRL_TRANS ATH_DEBUG_MAKE_MODULE_MASK(0)
|
||||
#define USB_HIF_DEBUG_BULK_IN ATH_DEBUG_MAKE_MODULE_MASK(1)
|
||||
#define USB_HIF_DEBUG_BULK_OUT ATH_DEBUG_MAKE_MODULE_MASK(2)
|
||||
#define USB_HIF_DEBUG_ENUM ATH_DEBUG_MAKE_MODULE_MASK(3)
|
||||
#define USB_HIF_DEBUG_DUMP_DATA ATH_DEBUG_MAKE_MODULE_MASK(4)
|
||||
#define USB_HIF_SUSPEND ATH_DEBUG_MAKE_MODULE_MASK(5)
|
||||
#define USB_HIF_ISOC_SUPPORT ATH_DEBUG_MAKE_MODULE_MASK(6)
|
||||
|
||||
struct _HIF_USB_PIPE;
|
||||
|
||||
typedef struct _HIF_URB_CONTEXT {
|
||||
DL_LIST link;
|
||||
struct _HIF_USB_PIPE *pipe;
|
||||
qdf_nbuf_t buf;
|
||||
struct urb *urb;
|
||||
struct hif_usb_send_context *send_context;
|
||||
} HIF_URB_CONTEXT;
|
||||
|
||||
#define HIF_USB_PIPE_FLAG_TX (1 << 0)
|
||||
|
||||
/*
|
||||
* Data structure to record required sending context data
|
||||
*/
|
||||
struct hif_usb_send_context {
|
||||
A_BOOL new_alloc;
|
||||
HIF_DEVICE_USB *hif_usb_device;
|
||||
qdf_nbuf_t netbuf;
|
||||
unsigned int transfer_id;
|
||||
unsigned int head_data_len;
|
||||
};
|
||||
|
||||
extern unsigned int hif_usb_disable_rxdata2;
|
||||
|
||||
extern QDF_STATUS usb_hif_submit_ctrl_in(HIF_DEVICE_USB *macp,
|
||||
uint8_t req,
|
||||
uint16_t value,
|
||||
uint16_t index,
|
||||
void *data, uint32_t size);
|
||||
|
||||
extern QDF_STATUS usb_hif_submit_ctrl_out(HIF_DEVICE_USB *macp,
|
||||
uint8_t req,
|
||||
uint16_t value,
|
||||
uint16_t index,
|
||||
void *data, uint32_t size);
|
||||
|
||||
QDF_STATUS usb_hif_setup_pipe_resources(HIF_DEVICE_USB *device);
|
||||
void usb_hif_cleanup_pipe_resources(HIF_DEVICE_USB *device);
|
||||
void usb_hif_prestart_recv_pipes(HIF_DEVICE_USB *device);
|
||||
void usb_hif_start_recv_pipes(HIF_DEVICE_USB *device);
|
||||
void usb_hif_flush_all(HIF_DEVICE_USB *device);
|
||||
void usb_hif_cleanup_transmit_urb(HIF_URB_CONTEXT *urb_context);
|
||||
void usb_hif_enqueue_pending_transfer(HIF_USB_PIPE *pipe,
|
||||
HIF_URB_CONTEXT *urb_context);
|
||||
void usb_hif_remove_pending_transfer(HIF_URB_CONTEXT *urb_context);
|
||||
HIF_URB_CONTEXT *usb_hif_alloc_urb_from_pipe(HIF_USB_PIPE *pipe);
|
||||
#ifdef HIF_USB_TASKLET
|
||||
void usb_hif_io_comp_tasklet(long unsigned int context);
|
||||
#else
|
||||
void usb_hif_io_comp_work(struct work_struct *work);
|
||||
#endif
|
||||
QDF_STATUS hif_diag_write_warm_reset(struct usb_interface *interface,
|
||||
uint32_t address, uint32_t data);
|
||||
#endif
|
716
hif/src/usb/if_usb.c
Normal file
716
hif/src/usb/if_usb.c
Normal file
@@ -0,0 +1,716 @@
|
||||
/*
|
||||
* 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 <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include "if_usb.h"
|
||||
#include "hif_usb_internal.h"
|
||||
#include "bmi_msg.h" /* TARGET_TYPE_ */
|
||||
#include "regtable_usb.h"
|
||||
#include "ol_fw.h"
|
||||
#include "hif_debug.h"
|
||||
#include "epping_main.h"
|
||||
#include "hif_main.h"
|
||||
#include "qwlan_version.h"
|
||||
|
||||
#define DELAY_FOR_TARGET_READY 200 /* 200ms */
|
||||
|
||||
/* Save memory addresses where we save FW ram dump, and then we could obtain
|
||||
* them by symbol table.
|
||||
*/
|
||||
uint32_t fw_stack_addr;
|
||||
void *fw_ram_seg_addr[FW_RAM_SEG_CNT];
|
||||
|
||||
|
||||
|
||||
static int hif_usb_unload_dev_num = -1;
|
||||
struct hif_usb_softc *g_usb_sc = NULL;
|
||||
|
||||
void hif_usb_device_deinit(struct hif_usb_softc *sc);
|
||||
QDF_STATUS hif_usb_device_init(struct hif_usb_softc *sc);
|
||||
|
||||
/**
|
||||
* hif_usb_diag_write_cold_reset() - reset SOC by sending a diag command
|
||||
* @scn: pointer to ol_softc structure
|
||||
*
|
||||
* Return: QDF_STATUS_SUCCESS if success else an appropriate QDF_STATUS error
|
||||
*/
|
||||
static inline QDF_STATUS
|
||||
hif_usb_diag_write_cold_reset(struct hif_softc *scn)
|
||||
{
|
||||
struct hif_opaque_softc *hif_hdl = GET_HIF_OPAQUE_HDL(scn);
|
||||
|
||||
|
||||
HIF_DBG("%s: resetting SOC", __func__);
|
||||
|
||||
return hif_diag_write_access(hif_hdl,
|
||||
(ROME_USB_SOC_RESET_CONTROL_COLD_RST_LSB |
|
||||
ROME_USB_RTC_SOC_BASE_ADDRESS),
|
||||
SOC_RESET_CONTROL_COLD_RST_SET(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_procfs_init() - create init procfs
|
||||
* @scn: pointer to hif_usb_softc structure
|
||||
*
|
||||
* Return: int 0 if success else an appropriate error number
|
||||
*/
|
||||
static int
|
||||
hif_usb_procfs_init(struct hif_softc *scn)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
HIF_ENTER();
|
||||
|
||||
if (athdiag_procfs_init(scn) != 0) {
|
||||
HIF_ERROR("athdiag_procfs_init failed");
|
||||
ret = A_ERROR;
|
||||
}
|
||||
|
||||
scn->athdiag_procfs_inited = true;
|
||||
|
||||
HIF_EXIT();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_nointrs(): disable IRQ
|
||||
* @scn: pointer to struct hif_softc
|
||||
*
|
||||
* This function stops interrupt(s)
|
||||
*
|
||||
* Return: none
|
||||
*/
|
||||
void hif_usb_nointrs(struct hif_softc *scn)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_reboot() - called at reboot time to reset WLAN SOC
|
||||
* @nb: pointer to notifier_block registered during register_reboot_notifier
|
||||
* @val: code indicating reboot reason
|
||||
* @v: unused pointer
|
||||
*
|
||||
* Return: int 0 if success else an appropriate error number
|
||||
*/
|
||||
static int hif_usb_reboot(struct notifier_block *nb, unsigned long val,
|
||||
void *v)
|
||||
{
|
||||
struct hif_usb_softc *sc;
|
||||
|
||||
HIF_ENTER();
|
||||
sc = container_of(nb, struct hif_usb_softc, reboot_notifier);
|
||||
/* do cold reset */
|
||||
hif_usb_diag_write_cold_reset(HIF_GET_SOFTC(sc));
|
||||
HIF_EXIT();
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_disable_lpm() - Disable lpm feature of usb2.0
|
||||
* @udev: pointer to usb_device for which LPM is to be disabled
|
||||
*
|
||||
* LPM needs to be disabled to avoid usb2.0 probe timeout
|
||||
*
|
||||
* Return: int 0 if success else an appropriate error number
|
||||
*/
|
||||
static int hif_usb_disable_lpm(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int ret = -EPERM;
|
||||
|
||||
HIF_ENTER();
|
||||
|
||||
if (!udev || !udev->bus) {
|
||||
HIF_ERROR("Invalid input parameters");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
if (udev->usb2_hw_lpm_enabled) {
|
||||
if (hcd->driver->set_usb2_hw_lpm) {
|
||||
ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, false);
|
||||
if (!ret) {
|
||||
udev->usb2_hw_lpm_enabled = false;
|
||||
udev->usb2_hw_lpm_capable = false;
|
||||
HIF_TRACE("%s: LPM is disabled", __func__);
|
||||
} else {
|
||||
HIF_TRACE("%s: Fail to disable LPM",
|
||||
__func__);
|
||||
}
|
||||
} else {
|
||||
HIF_TRACE("%s: hcd doesn't support LPM",
|
||||
__func__);
|
||||
}
|
||||
} else {
|
||||
HIF_TRACE("%s: LPM isn't enabled", __func__);
|
||||
}
|
||||
exit:
|
||||
HIF_EXIT();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_enable_bus() - enable usb bus
|
||||
* @ol_sc: hif_softc struct
|
||||
* @dev: device pointer
|
||||
* @bdev: bus dev pointer
|
||||
* @bid: bus id pointer
|
||||
* @type: enum hif_enable_type such as HIF_ENABLE_TYPE_PROBE
|
||||
*
|
||||
* Return: QDF_STATUS_SUCCESS on success and error QDF status on failure
|
||||
*/
|
||||
QDF_STATUS hif_usb_enable_bus(struct hif_softc *scn,
|
||||
struct device *dev, void *bdev,
|
||||
const hif_bus_id *bid,
|
||||
enum hif_enable_type type)
|
||||
|
||||
{
|
||||
struct usb_interface *interface = (struct usb_interface *)bdev;
|
||||
struct usb_device_id *id = (struct usb_device_id *)bid;
|
||||
int ret = 0;
|
||||
struct hif_usb_softc *sc;
|
||||
struct usb_device *usbdev = interface_to_usbdev(interface);
|
||||
int vendor_id, product_id;
|
||||
|
||||
usb_get_dev(usbdev);
|
||||
|
||||
if (!scn) {
|
||||
HIF_ERROR("%s: hif_ctx is NULL", __func__);
|
||||
goto err_usb;
|
||||
}
|
||||
|
||||
sc = HIF_GET_USB_SOFTC(scn);
|
||||
|
||||
HIF_INFO("%s hif_softc %p usbdev %p interface %p\n",
|
||||
__func__,
|
||||
scn,
|
||||
usbdev,
|
||||
interface);
|
||||
|
||||
vendor_id = qdf_le16_to_cpu(usbdev->descriptor.idVendor);
|
||||
product_id = qdf_le16_to_cpu(usbdev->descriptor.idProduct);
|
||||
|
||||
HIF_ERROR("%s: con_mode = 0x%x, vendor_id = 0x%x product_id = 0x%x",
|
||||
__func__, hif_get_conparam(scn), vendor_id, product_id);
|
||||
|
||||
sc->pdev = (void *)usbdev;
|
||||
sc->dev = &usbdev->dev;
|
||||
sc->devid = id->idProduct;
|
||||
|
||||
if ((usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
|
||||
USB_REQ_SET_CONFIGURATION, 0, 1, 0, NULL, 0,
|
||||
HZ)) < 0) {
|
||||
HIF_ERROR("%s[%d]", __func__, __LINE__);
|
||||
goto err_usb;
|
||||
}
|
||||
|
||||
usb_set_interface(usbdev, 0, 0);
|
||||
/* disable lpm to avoid usb2.0 probe timeout */
|
||||
hif_usb_disable_lpm(usbdev);
|
||||
|
||||
/* params need to be added - TO DO
|
||||
scn->enableuartprint = 1;
|
||||
scn->enablefwlog = 0;
|
||||
scn->max_no_of_peers = 1; */
|
||||
|
||||
sc->interface = interface;
|
||||
sc->reboot_notifier.notifier_call = hif_usb_reboot;
|
||||
register_reboot_notifier(&sc->reboot_notifier);
|
||||
|
||||
if (hif_usb_device_init(sc) != QDF_STATUS_SUCCESS) {
|
||||
HIF_ERROR("ath: %s: hif_usb_device_init failed", __func__);
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
if (hif_usb_procfs_init(scn))
|
||||
goto err_reset;
|
||||
|
||||
hif_usb_unload_dev_num = usbdev->devnum;
|
||||
g_usb_sc = sc;
|
||||
HIF_EXIT();
|
||||
return 0;
|
||||
|
||||
err_reset:
|
||||
hif_usb_diag_write_cold_reset(scn);
|
||||
g_usb_sc = NULL;
|
||||
hif_usb_unload_dev_num = -1;
|
||||
unregister_reboot_notifier(&sc->reboot_notifier);
|
||||
err_usb:
|
||||
ret = QDF_STATUS_E_FAILURE;
|
||||
usb_put_dev(usbdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hif_usb_close(): close bus, delete hif_sc
|
||||
* @ol_sc: soft_sc struct
|
||||
*
|
||||
* Return: none
|
||||
*/
|
||||
void hif_usb_close(struct hif_softc *scn)
|
||||
{
|
||||
g_usb_sc = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_disable_bus(): This function disables usb bus
|
||||
* @hif_ctx: pointer to struct hif_softc
|
||||
*
|
||||
* Return: none
|
||||
*/
|
||||
void hif_usb_disable_bus(struct hif_softc *hif_ctx)
|
||||
{
|
||||
struct hif_usb_softc *sc = HIF_GET_USB_SOFTC(hif_ctx);
|
||||
struct usb_interface *interface = sc->interface;
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
|
||||
HIF_TRACE("%s: trying to remove hif_usb!", __func__);
|
||||
|
||||
/* disable lpm to avoid following cold reset will
|
||||
* cause xHCI U1/U2 timeout
|
||||
*/
|
||||
usb_disable_lpm(udev);
|
||||
|
||||
/* wait for disable lpm */
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(msecs_to_jiffies(DELAY_FOR_TARGET_READY));
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
/* do cold reset */
|
||||
hif_usb_diag_write_cold_reset(hif_ctx);
|
||||
|
||||
if (g_usb_sc->suspend_state)
|
||||
hif_bus_resume(GET_HIF_OPAQUE_HDL(hif_ctx));
|
||||
|
||||
unregister_reboot_notifier(&sc->reboot_notifier);
|
||||
usb_put_dev(interface_to_usbdev(interface));
|
||||
|
||||
hif_usb_device_deinit(sc);
|
||||
|
||||
HIF_TRACE("%s hif_usb removed !!!!!!", __func__);
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_bus_suspend() - suspend the bus
|
||||
* @hif_ctx: hif_ctx
|
||||
*
|
||||
* This function suspends the bus, but usb doesn't need to suspend.
|
||||
* Therefore just remove all the pending urb transactions
|
||||
*
|
||||
* Return: 0 for success and non-zero for failure
|
||||
*/
|
||||
int hif_usb_bus_suspend(struct hif_softc *hif_ctx)
|
||||
{
|
||||
struct hif_usb_softc *sc = HIF_GET_USB_SOFTC(hif_ctx);
|
||||
HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(hif_ctx);
|
||||
|
||||
HIF_ENTER();
|
||||
sc->suspend_state = 1;
|
||||
usb_hif_flush_all(device);
|
||||
HIF_EXIT();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_bus_resume() - hif resume API
|
||||
* @hif_ctx: struct hif_opaque_softc
|
||||
*
|
||||
* This function resumes the bus. but usb doesn't need to resume.
|
||||
* Post recv urbs for RX data pipe
|
||||
*
|
||||
* Return: 0 for success and non-zero for failure
|
||||
*/
|
||||
int hif_usb_bus_resume(struct hif_softc *hif_ctx)
|
||||
{
|
||||
struct hif_usb_softc *sc = HIF_GET_USB_SOFTC(hif_ctx);
|
||||
HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(hif_ctx);
|
||||
|
||||
HIF_ENTER();
|
||||
sc->suspend_state = 0;
|
||||
usb_hif_start_recv_pipes(device);
|
||||
|
||||
HIF_EXIT();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_bus_reset_resume() - resume the bus after reset
|
||||
* @scn: struct hif_opaque_softc
|
||||
*
|
||||
* This function is called to tell the driver that USB device has been resumed
|
||||
* and it has also been reset. The driver should redo any necessary
|
||||
* initialization. This function resets WLAN SOC.
|
||||
*
|
||||
* Return: int 0 for success, non zero for failure
|
||||
*/
|
||||
int hif_bus_reset_resume(struct hif_opaque_softc *scn)
|
||||
{
|
||||
int ret = 0;
|
||||
struct hif_softc *hif_ctx = HIF_GET_SOFTC(scn);
|
||||
HIF_ENTER();
|
||||
if (hif_usb_diag_write_cold_reset(hif_ctx) != QDF_STATUS_SUCCESS)
|
||||
ret = 1;
|
||||
|
||||
HIF_EXIT();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_open()- initialization routine for usb bus
|
||||
* @ol_sc: ol_sc
|
||||
* @bus_type: bus type
|
||||
*
|
||||
* Return: QDF_STATUS_SUCCESS on success and error QDF status on failure
|
||||
*/
|
||||
QDF_STATUS hif_usb_open(struct hif_softc *hif_ctx,
|
||||
enum qdf_bus_type bus_type)
|
||||
{
|
||||
hif_ctx->bus_type = bus_type;
|
||||
return QDF_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_disable_isr(): disable isr
|
||||
* @hif_ctx: struct hif_softc
|
||||
*
|
||||
* Return: void
|
||||
*/
|
||||
void hif_usb_disable_isr(struct hif_softc *hif_ctx)
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_reg_tbl_attach()- attach hif, target register tables
|
||||
* @scn: pointer to ol_softc structure
|
||||
*
|
||||
* Attach host and target register tables based on target_type, target_version
|
||||
*
|
||||
* Return: none
|
||||
*/
|
||||
void hif_usb_reg_tbl_attach(struct hif_softc *scn)
|
||||
{
|
||||
u_int32_t hif_type, target_type;
|
||||
int32_t ret = 0;
|
||||
uint32_t chip_id;
|
||||
QDF_STATUS rv;
|
||||
struct hif_target_info *tgt_info = &scn->target_info;
|
||||
struct hif_opaque_softc *hif_hdl = GET_HIF_OPAQUE_HDL(scn);
|
||||
|
||||
if (scn->hostdef == NULL && scn->targetdef == NULL) {
|
||||
switch (tgt_info->target_type) {
|
||||
case TARGET_TYPE_AR6320:
|
||||
switch (tgt_info->target_version) {
|
||||
case AR6320_REV1_VERSION:
|
||||
case AR6320_REV1_1_VERSION:
|
||||
case AR6320_REV1_3_VERSION:
|
||||
hif_type = HIF_TYPE_AR6320;
|
||||
target_type = TARGET_TYPE_AR6320;
|
||||
break;
|
||||
case AR6320_REV2_1_VERSION:
|
||||
case AR6320_REV3_VERSION:
|
||||
case QCA9377_REV1_1_VERSION:
|
||||
hif_type = HIF_TYPE_AR6320V2;
|
||||
target_type = TARGET_TYPE_AR6320V2;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
/* assign target register table if we find
|
||||
corresponding type */
|
||||
hif_register_tbl_attach(scn, hif_type);
|
||||
target_register_tbl_attach(scn, target_type);
|
||||
/* read the chip revision*/
|
||||
rv = hif_diag_read_access(hif_hdl,
|
||||
(CHIP_ID_ADDRESS |
|
||||
RTC_SOC_BASE_ADDRESS),
|
||||
&chip_id);
|
||||
if (rv != QDF_STATUS_SUCCESS) {
|
||||
HIF_ERROR("%s: get chip id val (%d)", __func__,
|
||||
rv);
|
||||
}
|
||||
tgt_info->target_revision =
|
||||
CHIP_ID_REVISION_GET(chip_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_get_hw_info()- attach register table for USB
|
||||
* @hif_ctx: pointer to hif_softc structure
|
||||
|
||||
* This function is used to attach the host and target register tables.
|
||||
* Ideally, we should not attach register tables as a part of this function.
|
||||
* There is scope of cleanup to move register table attach during
|
||||
* initialization for USB bus.
|
||||
*
|
||||
* The reason we are doing register table attach for USB here is that, it relies
|
||||
* on target_info->target_type and target_info->target_version,
|
||||
* which get populated during bmi_firmware_download. "hif_get_fw_info" is the
|
||||
* only initialization related call into HIF there after.
|
||||
*
|
||||
* To fix this, we can move the "get target info, functionality currently in
|
||||
* bmi_firmware_download into hif initialization functions. This change will
|
||||
* affect all buses. Can be taken up as a part of convergence.
|
||||
*
|
||||
* Return: none
|
||||
*/
|
||||
void hif_usb_get_hw_info(struct hif_softc *hif_ctx)
|
||||
{
|
||||
hif_usb_reg_tbl_attach(hif_ctx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hif_bus_configure() - configure the bus
|
||||
* @scn: pointer to the hif context.
|
||||
*
|
||||
* return: 0 for success. nonzero for failure.
|
||||
*/
|
||||
int hif_usb_bus_configure(struct hif_softc *scn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_irq_enable() - hif_usb_irq_enable
|
||||
* @scn: hif_softc
|
||||
* @ce_id: ce_id
|
||||
*
|
||||
* Return: void
|
||||
*/
|
||||
void hif_usb_irq_enable(struct hif_softc *scn, int ce_id)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_irq_disable() - hif_usb_irq_disable
|
||||
* @scn: hif_softc
|
||||
* @ce_id: ce_id
|
||||
*
|
||||
* Return: void
|
||||
*/
|
||||
void hif_usb_irq_disable(struct hif_softc *scn, int ce_id)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_usb_shutdown_bus_device() - This function shuts down the device
|
||||
* @scn: hif opaque pointer
|
||||
*
|
||||
* Return: void
|
||||
*/
|
||||
void hif_usb_shutdown_bus_device(struct hif_softc *scn)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_trigger_dump() - trigger various dump cmd
|
||||
* @scn: struct hif_opaque_softc
|
||||
* @cmd_id: dump command id
|
||||
* @start: start/stop dump
|
||||
*
|
||||
* Return: None
|
||||
*/
|
||||
void hif_trigger_dump(struct hif_opaque_softc *scn, uint8_t cmd_id, bool start)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_wlan_disable() - call the platform driver to disable wlan
|
||||
* @scn: scn
|
||||
*
|
||||
* Return: void
|
||||
*/
|
||||
void hif_wlan_disable(struct hif_softc *scn)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_fw_assert_ramdump_pattern() - handle firmware assert with ramdump pattern
|
||||
* @sc: pointer to hif_usb_softc structure
|
||||
*
|
||||
* Return: void
|
||||
*/
|
||||
|
||||
void hif_fw_assert_ramdump_pattern(struct hif_usb_softc *sc)
|
||||
{
|
||||
uint32_t *reg, pattern, i = 0;
|
||||
uint32_t len;
|
||||
uint8_t *data;
|
||||
uint8_t *ram_ptr = NULL;
|
||||
char *fw_ram_seg_name[FW_RAM_SEG_CNT] = {"DRAM", "IRAM", "AXI"};
|
||||
|
||||
data = sc->fw_data;
|
||||
len = sc->fw_data_len;
|
||||
pattern = *((A_UINT32 *) data);
|
||||
|
||||
qdf_assert(sc->ramdump_index < FW_RAM_SEG_CNT);
|
||||
i = sc->ramdump_index;
|
||||
reg = (uint32_t *) (data + 4);
|
||||
if (sc->fw_ram_dumping == 0) {
|
||||
sc->fw_ram_dumping = 1;
|
||||
HIF_ERROR("Firmware %s dump:\n", fw_ram_seg_name[i]);
|
||||
sc->ramdump[i] =
|
||||
qdf_mem_malloc(sizeof(struct fw_ramdump) +
|
||||
FW_RAMDUMP_SEG_SIZE);
|
||||
if (!sc->ramdump[i]) {
|
||||
pr_err("Fail to allocate memory for ram dump");
|
||||
QDF_BUG(0);
|
||||
}
|
||||
(sc->ramdump[i])->mem = (uint8_t *) (sc->ramdump[i] + 1);
|
||||
fw_ram_seg_addr[i] = (sc->ramdump[i])->mem;
|
||||
HIF_ERROR("FW %s start addr = %#08x\n",
|
||||
fw_ram_seg_name[i], *reg);
|
||||
HIF_ERROR("Memory addr for %s = %p\n",
|
||||
fw_ram_seg_name[i],
|
||||
(sc->ramdump[i])->mem);
|
||||
(sc->ramdump[i])->start_addr = *reg;
|
||||
(sc->ramdump[i])->length = 0;
|
||||
}
|
||||
reg++;
|
||||
ram_ptr = (sc->ramdump[i])->mem + (sc->ramdump[i])->length;
|
||||
(sc->ramdump[i])->length += (len - 8);
|
||||
qdf_mem_copy(ram_ptr, (A_UINT8 *) reg, len - 8);
|
||||
|
||||
if (pattern == FW_RAMDUMP_END_PATTERN) {
|
||||
HIF_ERROR("%s memory size = %d\n", fw_ram_seg_name[i],
|
||||
(sc->ramdump[i])->length);
|
||||
if (i == (FW_RAM_SEG_CNT - 1))
|
||||
QDF_BUG(0);
|
||||
|
||||
sc->ramdump_index++;
|
||||
sc->fw_ram_dumping = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_ramdump_handler(): dump bus debug registers
|
||||
* @scn: struct hif_opaque_softc
|
||||
*
|
||||
* This function is to receive information of firmware crash dump, and
|
||||
* save it in host memory. It consists of 5 parts: registers, call stack,
|
||||
* DRAM dump, IRAM dump, and AXI dump, and they are reported to host in order.
|
||||
*
|
||||
* registers: wrapped in a USB packet by starting as FW_ASSERT_PATTERN and
|
||||
* 60 registers.
|
||||
* call stack: wrapped in multiple USB packets, and each of them starts as
|
||||
* FW_REG_PATTERN and contains multiple double-words. The tail
|
||||
* of the last packet is FW_REG_END_PATTERN.
|
||||
* DRAM dump: wrapped in multiple USB pakcets, and each of them start as
|
||||
* FW_RAMDUMP_PATTERN and contains multiple double-wors. The tail
|
||||
* of the last packet is FW_RAMDUMP_END_PATTERN;
|
||||
* IRAM dump and AXI dump are with the same format as DRAM dump.
|
||||
*
|
||||
* Return: 0 for success or error code
|
||||
*/
|
||||
|
||||
void hif_ramdump_handler(struct hif_opaque_softc *scn)
|
||||
{
|
||||
uint32_t *reg, pattern, i, start_addr = 0;
|
||||
uint32_t len;
|
||||
uint8_t *data;
|
||||
uint8_t str_buf[128];
|
||||
uint32_t remaining;
|
||||
struct hif_usb_softc *sc = HIF_GET_USB_SOFTC(scn);
|
||||
struct hif_softc *hif_ctx = HIF_GET_SOFTC(scn);
|
||||
struct hif_target_info *tgt_info = &hif_ctx->target_info;
|
||||
|
||||
data = sc->fw_data;
|
||||
len = sc->fw_data_len;
|
||||
pattern = *((A_UINT32 *) data);
|
||||
|
||||
if (pattern == FW_ASSERT_PATTERN) {
|
||||
HIF_ERROR("Firmware crash detected...\n");
|
||||
HIF_ERROR("Host SW version: %s\n", QWLAN_VERSIONSTR);
|
||||
HIF_ERROR("target_type: %d.target_version %d. target_revision%d.",
|
||||
tgt_info->target_type,
|
||||
tgt_info->target_version,
|
||||
tgt_info->target_revision);
|
||||
|
||||
reg = (uint32_t *) (data + 4);
|
||||
print_hex_dump(KERN_DEBUG, " ", DUMP_PREFIX_OFFSET, 16, 4, reg,
|
||||
min_t(A_UINT32, len - 4, FW_REG_DUMP_CNT * 4),
|
||||
false);
|
||||
sc->fw_ram_dumping = 0;
|
||||
|
||||
} else if (pattern == FW_REG_PATTERN) {
|
||||
reg = (uint32_t *) (data + 4);
|
||||
start_addr = *reg++;
|
||||
if (sc->fw_ram_dumping == 0) {
|
||||
pr_err("Firmware stack dump:");
|
||||
sc->fw_ram_dumping = 1;
|
||||
fw_stack_addr = start_addr;
|
||||
}
|
||||
remaining = len - 8;
|
||||
/* len is in byte, but it's printed in double-word. */
|
||||
for (i = 0; i < (len - 8); i += 16) {
|
||||
if ((*reg == FW_REG_END_PATTERN) && (i == len - 12)) {
|
||||
sc->fw_ram_dumping = 0;
|
||||
pr_err("Stack start address = %#08x\n",
|
||||
fw_stack_addr);
|
||||
break;
|
||||
}
|
||||
hex_dump_to_buffer(reg, remaining, 16, 4, str_buf,
|
||||
sizeof(str_buf), false);
|
||||
pr_err("%#08x: %s\n", start_addr + i, str_buf);
|
||||
remaining -= 16;
|
||||
reg += 4;
|
||||
}
|
||||
} else if ((!sc->enable_self_recovery) &&
|
||||
((pattern & FW_RAMDUMP_PATTERN_MASK) ==
|
||||
FW_RAMDUMP_PATTERN)) {
|
||||
hif_fw_assert_ramdump_pattern(sc);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef QCA_WIFI_3_0
|
||||
/**
|
||||
* hif_check_fw_reg(): hif_check_fw_reg
|
||||
* @scn: scn
|
||||
* @state:
|
||||
*
|
||||
* Return: int
|
||||
*/
|
||||
int hif_check_fw_reg(struct hif_opaque_softc *scn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
160
hif/src/usb/if_usb.h
Normal file
160
hif/src/usb/if_usb.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef __ATH_USB_H__
|
||||
#define __ATH_USB_H__
|
||||
|
||||
#include <linux/reboot.h>
|
||||
|
||||
/*
|
||||
* There may be some pending tx frames during platform suspend.
|
||||
* Suspend operation should be delayed until those tx frames are
|
||||
* transfered from the host to target. This macro specifies how
|
||||
* long suspend thread has to sleep before checking pending tx
|
||||
* frame count.
|
||||
*/
|
||||
#define OL_ATH_TX_DRAIN_WAIT_DELAY 50 /* ms */
|
||||
/*
|
||||
* Wait time (in unit of OL_ATH_TX_DRAIN_WAIT_DELAY) for pending
|
||||
* tx frame completion before suspend. Refer: hif_pci_suspend()
|
||||
*/
|
||||
#define OL_ATH_TX_DRAIN_WAIT_CNT 10
|
||||
|
||||
#define CONFIG_COPY_ENGINE_SUPPORT /* TBDXXX: here for now */
|
||||
#define ATH_DBG_DEFAULT 0
|
||||
#include <osdep.h>
|
||||
#include <ol_if_athvar.h>
|
||||
#include <athdefs.h>
|
||||
#include "osapi_linux.h"
|
||||
#include "hif_main.h"
|
||||
#include "hif.h"
|
||||
|
||||
#define FW_REG_DUMP_CNT 60
|
||||
|
||||
/* Magic patterns for FW to report crash information (Rome USB) */
|
||||
#define FW_ASSERT_PATTERN 0x0000c600
|
||||
#define FW_REG_PATTERN 0x0000d600
|
||||
#define FW_REG_END_PATTERN 0x0000e600
|
||||
#define FW_RAMDUMP_PATTERN 0x0000f600
|
||||
#define FW_RAMDUMP_END_PATTERN 0x0000f601
|
||||
#define FW_RAMDUMP_PATTERN_MASK 0xfffffff0
|
||||
|
||||
/* FW RAM segments (Rome USB) */
|
||||
enum {
|
||||
FW_RAM_SEG_DRAM,
|
||||
FW_RAM_SEG_IRAM,
|
||||
FW_RAM_SEG_AXI,
|
||||
FW_RAM_SEG_CNT
|
||||
};
|
||||
|
||||
/* Allocate 384K memory to save each segment of ram dump */
|
||||
#define FW_RAMDUMP_SEG_SIZE 393216
|
||||
|
||||
/* structure to save RAM dump information */
|
||||
struct fw_ramdump {
|
||||
uint32_t start_addr;
|
||||
uint32_t length;
|
||||
uint8_t *mem;
|
||||
};
|
||||
|
||||
/* USB Endpoint definition */
|
||||
typedef enum {
|
||||
HIF_TX_CTRL_PIPE = 0,
|
||||
HIF_TX_DATA_LP_PIPE,
|
||||
HIF_TX_DATA_MP_PIPE,
|
||||
HIF_TX_DATA_HP_PIPE,
|
||||
HIF_RX_CTRL_PIPE,
|
||||
HIF_RX_DATA_PIPE,
|
||||
HIF_RX_DATA2_PIPE,
|
||||
HIF_RX_INT_PIPE,
|
||||
HIF_USB_PIPE_MAX
|
||||
} HIF_USB_PIPE_ID;
|
||||
|
||||
#define HIF_USB_PIPE_INVALID HIF_USB_PIPE_MAX
|
||||
|
||||
typedef struct _HIF_USB_PIPE {
|
||||
DL_LIST urb_list_head;
|
||||
DL_LIST urb_pending_list;
|
||||
int32_t urb_alloc;
|
||||
int32_t urb_cnt;
|
||||
int32_t urb_cnt_thresh;
|
||||
unsigned int usb_pipe_handle;
|
||||
uint32_t flags;
|
||||
uint8_t ep_address;
|
||||
uint8_t logical_pipe_num;
|
||||
struct _HIF_DEVICE_USB *device;
|
||||
uint16_t max_packet_size;
|
||||
#ifdef HIF_USB_TASKLET
|
||||
struct tasklet_struct io_complete_tasklet;
|
||||
#else
|
||||
struct work_struct io_complete_work;
|
||||
#endif
|
||||
struct sk_buff_head io_comp_queue;
|
||||
struct usb_endpoint_descriptor *ep_desc;
|
||||
int32_t urb_prestart_cnt;
|
||||
} HIF_USB_PIPE;
|
||||
|
||||
typedef struct _HIF_DEVICE_USB {
|
||||
struct hif_softc ol_sc;
|
||||
qdf_spinlock_t cs_lock;
|
||||
qdf_spinlock_t tx_lock;
|
||||
qdf_spinlock_t rx_lock;
|
||||
struct hif_msg_callbacks htc_callbacks;
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *interface;
|
||||
HIF_USB_PIPE pipes[HIF_USB_PIPE_MAX];
|
||||
uint8_t *diag_cmd_buffer;
|
||||
uint8_t *diag_resp_buffer;
|
||||
void *claimed_context;
|
||||
A_BOOL is_bundle_enabled;
|
||||
uint16_t rx_bundle_cnt;
|
||||
uint32_t rx_bundle_buf_len;
|
||||
} HIF_DEVICE_USB;
|
||||
|
||||
|
||||
struct hif_usb_softc {
|
||||
struct _HIF_DEVICE_USB hif_hdl;
|
||||
/* For efficiency, should be first in struct */
|
||||
struct device *dev;
|
||||
struct usb_dev *pdev;
|
||||
/*
|
||||
* Guard changes to Target HW state and to software
|
||||
* structures that track hardware state.
|
||||
*/
|
||||
u16 devid;
|
||||
struct usb_interface *interface;
|
||||
struct notifier_block reboot_notifier; /* default mode before reboot */
|
||||
u8 suspend_state;
|
||||
u8 *fw_data;
|
||||
u32 fw_data_len;
|
||||
/* structure to save FW RAM dump (Rome USB) */
|
||||
struct fw_ramdump *ramdump[FW_RAM_SEG_CNT];
|
||||
uint8_t ramdump_index;
|
||||
bool fw_ram_dumping;
|
||||
/* enable FW self-recovery for Rome USB */
|
||||
bool enable_self_recovery;
|
||||
};
|
||||
#endif /* __ATH_USB_H__ */
|
70
hif/src/usb/regtable_usb.c
Normal file
70
hif/src/usb/regtable_usb.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2014-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 "bmi_msg.h"
|
||||
#include "targaddrs.h"
|
||||
#include "regtable_usb.h"
|
||||
#include "ar9888def.h"
|
||||
#include "ar6320def.h"
|
||||
#include "ar6320v2def.h"
|
||||
#include "hif_debug.h"
|
||||
|
||||
void target_register_tbl_attach(struct hif_softc *scn,
|
||||
uint32_t target_type)
|
||||
{
|
||||
switch (target_type) {
|
||||
case TARGET_TYPE_AR9888:
|
||||
scn->targetdef = &ar9888_targetdef;
|
||||
break;
|
||||
case TARGET_TYPE_AR6320:
|
||||
scn->targetdef = &ar6320_targetdef;
|
||||
break;
|
||||
case TARGET_TYPE_AR6320V2:
|
||||
scn->targetdef = &ar6320v2_targetdef;
|
||||
break;
|
||||
default:
|
||||
HIF_ERROR("%s: unknown target_type %u", __func__, target_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void hif_register_tbl_attach(struct hif_softc *scn, uint32_t hif_type)
|
||||
{
|
||||
switch (hif_type) {
|
||||
case HIF_TYPE_AR9888:
|
||||
scn->hostdef = &ar9888_hostdef;
|
||||
break;
|
||||
case HIF_TYPE_AR6320:
|
||||
scn->hostdef = &ar6320_hostdef;
|
||||
break;
|
||||
case HIF_TYPE_AR6320V2:
|
||||
scn->hostdef = &ar6320v2_hostdef;
|
||||
break;
|
||||
default:
|
||||
HIF_ERROR("%s: unknown hif_type %u", __func__, hif_type);
|
||||
break;
|
||||
}
|
||||
}
|
1295
hif/src/usb/regtable_usb.h
Normal file
1295
hif/src/usb/regtable_usb.h
Normal file
A különbségek nem kerülnek megjelenítésre, mivel a fájl túl nagy
Load Diff
1267
hif/src/usb/usbdrv.c
Normal file
1267
hif/src/usb/usbdrv.c
Normal file
A különbségek nem kerülnek megjelenítésre, mivel a fájl túl nagy
Load Diff
Reference in New Issue
Block a user