blob: cd0ab2985c877356d60a97c273dd66cc59bc065a [file] [log] [blame]
/*
* Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
#define ATH_MODULE_NAME hif
#include <qdf_types.h>
#include <qdf_status.h>
#include <qdf_timer.h>
#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 <athdefs.h>
#include <qdf_net_types.h>
#include <a_types.h>
#include <athdefs.h>
#include <a_osapi.h>
#include <hif.h>
#include <htc_services.h>
#include <a_debug.h>
#include "hif_sdio_internal.h"
/*
* Data structure to record required sending context data
*/
struct hif_sendContext {
bool bNewAlloc;
struct hif_sdio_device *pDev;
qdf_nbuf_t netbuf;
unsigned int transferID;
unsigned int head_data_len;
};
/**
* hif_dev_rw_completion_handler() - Completion routine
* for ALL HIF layer async I/O
* @context: hif send context
* @status: completion routine sync/async context
*
* Return: 0 for success and non-zero for failure
*/
QDF_STATUS hif_dev_rw_completion_handler(void *context, QDF_STATUS status)
{
struct hif_sendContext *send_context =
(struct hif_sendContext *)context;
unsigned int transfer_id = send_context->transferID;
struct hif_sdio_device *pdev = send_context->pDev;
qdf_nbuf_t buf = send_context->netbuf;
/* Fix Me: Do we need toeplitz_hash_result for SDIO */
uint32_t toeplitz_hash_result = 0;
if (send_context->bNewAlloc)
qdf_mem_free((void *)send_context);
else
qdf_nbuf_pull_head(buf, send_context->head_data_len);
if (pdev->hif_callbacks.txCompletionHandler)
pdev->hif_callbacks.txCompletionHandler(pdev->hif_callbacks.
Context, buf,
transfer_id, toeplitz_hash_result);
return QDF_STATUS_SUCCESS;
}
/**
* hif_dev_send_buffer() - send buffer to sdio device
* @pDev: sdio function
* @transferID: transfer id
* @pipe: ul/dl pipe
* @nbytes: no of bytes to transfer
* @buf: pointer to buffer
*
* Return: 0 for success and non-zero for failure
*/
QDF_STATUS hif_dev_send_buffer(struct hif_sdio_device *pdev,
unsigned int transfer_id,
uint8_t pipe, unsigned int nbytes,
qdf_nbuf_t buf)
{
QDF_STATUS status;
uint32_t padded_length;
int frag_count = 0, i, head_data_len;
struct hif_sendContext *send_context;
unsigned char *pData;
uint32_t request = HIF_WR_ASYNC_BLOCK_INC;
uint8_t mbox_index = hif_dev_map_pipe_to_mail_box(pdev, pipe);
if (mbox_index == INVALID_MAILBOX_NUMBER) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("pipe id(%d) invalid\n", pipe));
return QDF_STATUS_E_FAILURE;
}
padded_length = DEV_CALC_SEND_PADDED_LEN(pdev, nbytes);
A_ASSERT(padded_length - nbytes < HIF_DUMMY_SPACE_MASK + 1);
/*
* two most significant bytes to save dummy data count
* data written into the dummy space will not put into
* the final mbox FIFO.
*/
request |= ((padded_length - nbytes) << 16);
frag_count = qdf_nbuf_get_num_frags(buf);
if (frag_count > 1) {
/* header data length should be total sending length substract
* internal data length of netbuf
*/
head_data_len = sizeof(struct hif_sendContext) +
(nbytes - qdf_nbuf_get_frag_len(buf, frag_count - 1));
} else {
/*
* | hif_sendContext | netbuf->data
*/
head_data_len = sizeof(struct hif_sendContext);
}
/* Check whether head room is enough to save extra head data */
if ((head_data_len <= qdf_nbuf_headroom(buf)) &&
(qdf_nbuf_tailroom(buf) >= (padded_length - nbytes))) {
send_context =
(struct hif_sendContext *)qdf_nbuf_push_head(buf,
head_data_len);
send_context->bNewAlloc = false;
} else {
send_context =
(struct hif_sendContext *)
qdf_mem_malloc(sizeof(struct hif_sendContext) +
padded_length);
if (send_context) {
send_context->bNewAlloc = true;
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Allocate send context fail %d\n",
sizeof(struct hif_sendContext) +
padded_length));
return QDF_STATUS_E_NOMEM;
}
}
send_context->netbuf = buf;
send_context->pDev = pdev;
send_context->transferID = transfer_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
*/
pData = (unsigned char *)send_context + sizeof(struct hif_sendContext);
for (i = 0; i < (send_context->bNewAlloc ? 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);
if (frag_len > nbytes)
frag_len = nbytes;
memcpy(pData, frag_addr, frag_len);
pData += frag_len;
nbytes -= frag_len;
if (nbytes <= 0)
break;
}
/* Reset pData pointer and send_context out */
pData = (unsigned char *)send_context + sizeof(struct hif_sendContext);
status = hif_read_write(pdev->HIFDevice,
pdev->MailBoxInfo.mbox_prop[mbox_index].
extended_address, (char *)pData, padded_length,
request, (void *)send_context);
if (status == QDF_STATUS_E_PENDING)
/*
* it will return QDF_STATUS_E_PENDING in native HIF
* implementation, which should be treated as successful
* result here.
*/
status = QDF_STATUS_SUCCESS;
/* release buffer or move back data pointer when failed */
if (status != QDF_STATUS_SUCCESS) {
if (send_context->bNewAlloc)
qdf_mem_free(send_context);
else
qdf_nbuf_pull_head(buf, head_data_len);
}
return status;
}