blob: 9eb0c70a9f5b0ed494bdd876e2006a5d9c94e93f [file] [log] [blame]
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*
* 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.
*/
/*
* nss_tx_msg_sync.c
* NSS Tx msg sync core APIs
*/
#include "nss_tx_rx_common.h"
/*
* nss_tx_msg_sync_callback()
* Internal callback used to handle the message response.
*/
static void nss_tx_msg_sync_callback(void *app_data, struct nss_cmn_msg *ncm)
{
uint32_t resp_offset;
/*
* Per-message sync data was used as app_data.
* Retrieve the address of the original message from it.
*/
struct nss_tx_msg_sync_cmn_data *sync_data = (struct nss_tx_msg_sync_cmn_data *)app_data;
struct nss_cmn_msg *original_msg = (struct nss_cmn_msg *)sync_data->original_msg;
/*
* Set TX status. And Copy back ncm->error and ncm->response if it is NACK.
*/
sync_data->status = NSS_TX_SUCCESS;
if (ncm->response != NSS_CMN_RESPONSE_ACK) {
nss_warning("Tx msg sync error response %d\n", ncm->response);
sync_data->status = NSS_TX_FAILURE_SYNC_FW_ERR;
original_msg->error = ncm->error;
original_msg->response = ncm->response;
}
/*
* ncm is the return message containing message response.
* It is different from the original message caller built.
* Because the return message is only visible in this callback context,
* we copy back message response by specifying offset and length to
* the return message. So the caller can use response in their context
* once wake up instead of calling a passed-in user callback here.
*/
resp_offset = sync_data->resp_offset + sizeof(struct nss_cmn_msg);
if (sync_data->copy_len > 0)
memcpy((uint8_t *)((nss_ptr_t)original_msg + resp_offset),
(uint8_t *)((nss_ptr_t)ncm + resp_offset),
sync_data->copy_len);
/*
* Wake up the caller
*/
complete(&sync_data->complete);
}
/*
* nss_tx_msg_sync_internal()
* Internal call for sending messages to FW synchronously.
*/
static nss_tx_status_t nss_tx_msg_sync_internal(struct nss_ctx_instance *nss_ctx,
nss_tx_msg_sync_subsys_async_t tx_msg_async,
nss_tx_msg_sync_subsys_async_with_size_t tx_msg_async_with_size,
uint32_t msg_buf_size,
struct nss_tx_msg_sync_cmn_data *sync_data,
struct nss_cmn_msg *ncm,
uint32_t timeout)
{
nss_tx_status_t status;
int ret;
/*
* Per-msg sync data is used as app_data.
* A generic callback is used to handle the return message.
*/
ncm->cb = (nss_ptr_t)nss_tx_msg_sync_callback;
ncm->app_data = (nss_ptr_t)sync_data;
BUG_ON(!tx_msg_async && !tx_msg_async_with_size);
/*
* Per-subsystem asynchronous call to send down the message.
*/
if (tx_msg_async)
status = tx_msg_async(nss_ctx, ncm);
else
status = tx_msg_async_with_size(nss_ctx, ncm, msg_buf_size);
if (status != NSS_TX_SUCCESS) {
nss_warning("%px: Tx msg async failed\n", nss_ctx);
return status;
}
/*
* Sleep. Wake up either by notification or timeout.
*/
ret = wait_for_completion_timeout(&sync_data->complete, msecs_to_jiffies(timeout));
if (!ret) {
nss_warning("%px: Tx msg sync timeout\n", nss_ctx);
return NSS_TX_FAILURE_SYNC_TIMEOUT;
}
/*
* Wake up. Message response has been received within timeout.
*/
return sync_data->status;
}
/*
* nss_tx_msg_sync()
* Send messages to FW synchronously with default message buffer size.
*
* tx_msg_async specifies the per-subsystem asynchronous call.
* timeout specifies the maximum sleep time for the completion.
* ncm is the original message the caller built.
* Since the caller cannot access the return message containing message response,
* we copy back message response from return message.
* resp_offset and copy_len specify the part of return message it'll copy.
*/
nss_tx_status_t nss_tx_msg_sync(struct nss_ctx_instance *nss_ctx,
nss_tx_msg_sync_subsys_async_t tx_msg_async,
uint32_t timeout, struct nss_cmn_msg *ncm,
uint32_t resp_offset, uint32_t copy_len)
{
struct nss_tx_msg_sync_cmn_data sync_data;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
/*
* Check Tx msg async API
*/
if (!unlikely(tx_msg_async)) {
nss_warning("%px: missing Tx msg async API\n", nss_ctx);
return NSS_TX_FAILURE_SYNC_BAD_PARAM;
}
/*
* Initialize the per-message sync data.
*/
init_completion(&sync_data.complete);
sync_data.status = NSS_TX_FAILURE;
sync_data.original_msg = (void *)ncm;
sync_data.resp_offset = resp_offset;
sync_data.copy_len = copy_len;
return nss_tx_msg_sync_internal(nss_ctx, tx_msg_async, NULL, 0, &sync_data, ncm, timeout);
}
EXPORT_SYMBOL(nss_tx_msg_sync);
/*
* nss_tx_msg_sync_with_size()
* Send messages to FW synchronously with specified message buffer size.
*/
nss_tx_status_t nss_tx_msg_sync_with_size(struct nss_ctx_instance *nss_ctx,
nss_tx_msg_sync_subsys_async_with_size_t tx_msg_async_with_size,
uint32_t msg_buf_size, uint32_t timeout,
struct nss_cmn_msg *ncm, uint32_t resp_offset, uint32_t copy_len)
{
struct nss_tx_msg_sync_cmn_data sync_data;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
/*
* Check Tx msg async API
*/
if (!unlikely(tx_msg_async_with_size)) {
nss_warning("%px: missing Tx msg async API\n", nss_ctx);
return NSS_TX_FAILURE_SYNC_BAD_PARAM;
}
/*
* Initialize the per-message sync data.
*/
init_completion(&sync_data.complete);
sync_data.status = NSS_TX_FAILURE;
sync_data.original_msg = (void *)ncm;
sync_data.resp_offset = resp_offset;
sync_data.copy_len = copy_len;
return nss_tx_msg_sync_internal(nss_ctx, NULL, tx_msg_async_with_size,
msg_buf_size, &sync_data, ncm, timeout);
}
EXPORT_SYMBOL(nss_tx_msg_sync_with_size);