// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2012-2014, 2018-2024 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
#include <net/mac80211.h>
#include "iwl-debug.h"
#include "iwl-io.h"
#include "iwl-prph.h"
#include "iwl-csr.h"
#include "mvm.h"
#include "fw/api/rs.h"
#include "fw/img.h"
/*
* Will return 0 even if the cmd failed when RFKILL is asserted unless
* CMD_WANT_SKB is set in cmd->flags.
*/
int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
{
int ret;
#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
if (WARN_ON(mvm->d3_test_active))
return -EIO;
#endif
/*
* Synchronous commands from this op-mode must hold
* the mutex, this ensures we don't try to send two
* (or more) synchronous commands at a time.
*/
if (!(cmd->flags & CMD_ASYNC))
lockdep_assert_held(&mvm->mutex);
ret = iwl_trans_send_cmd(mvm->trans, cmd);
/*
* If the caller wants the SKB, then don't hide any problems, the
* caller might access the response buffer which will be NULL if
* the command failed.
*/
if (cmd->flags & CMD_WANT_SKB)
return ret;
/*
* Silently ignore failures if RFKILL is asserted or
* we are in suspend\resume process
*/
if (!ret || ret == -ERFKILL || ret == -EHOSTDOWN)
return 0;
return ret;
}
int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id,
u32 flags, u16 len, const void *data)
{
struct iwl_host_cmd cmd = {
.id = id,
.len = { len, },
.data = { data, },
.flags = flags,
};
return iwl_mvm_send_cmd(mvm, &cmd);
}
/*
* We assume that the caller set the status to the success value
*/
int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
u32 *status)
{
struct iwl_rx_packet *pkt;
struct iwl_cmd_response *resp;
int ret, resp_len;
lockdep_assert_held(&mvm->mutex);
#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
if (WARN_ON(mvm->d3_test_active))
return -EIO;
#endif
/*
* Only synchronous commands can wait for status,
* we use WANT_SKB so the caller can't.
*/
if (WARN_ONCE(cmd->flags & (CMD_ASYNC | CMD_WANT_SKB),
"cmd flags %x", cmd->flags))
return -EINVAL;
cmd->flags |= CMD_WANT_SKB;
ret = iwl_trans_send_cmd(mvm->trans, cmd);
if (ret == -ERFKILL) {
/*
* The command failed because of RFKILL, don't update
* the status, leave it as success and return 0.
*/
return 0;
} else if (ret) {
return ret;
}
pkt = cmd->resp_pkt;
resp_len = iwl_rx_packet_payload_len(pkt);
if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
ret = -EIO;
goto out_free_resp;
}
resp = (void *)pkt->data;
*status = le32_to_cpu(resp->status);
out_free_resp:
iwl_free_resp(cmd);
return ret;
}
/*
* We assume that the caller set the status to the sucess value
*/
int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len,
const void *data, u32 *status)
{
struct iwl_host_cmd cmd = {
.id = id,
.len = {