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:
Mohit Khanna
2016-05-11 11:09:21 -07:00
committed by Vishwajith Upendra
szülő 5693051379
commit bda5d43e70
7 fájl változott, egészen pontosan 4553 új sor hozzáadva és 0 régi sor törölve

918
hif/src/usb/hif_usb.c Normal file
Fájl megtekintése

@@ -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);
}

Fájl megtekintése

@@ -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
Fájl megtekintése

@@ -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
Fájl megtekintése

@@ -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__ */

Fájl megtekintése

@@ -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

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

A különbségek nem kerülnek megjelenítésre, mivel a fájl túl nagy Load Diff