1d5c65159SKalle Valo // SPDX-License-Identifier: BSD-3-Clause-Clear 2d5c65159SKalle Valo /* 3d5c65159SKalle Valo * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. 4d5c65159SKalle Valo */ 5d5c65159SKalle Valo 6d5c65159SKalle Valo #include "testmode.h" 7d5c65159SKalle Valo #include <net/netlink.h> 8d5c65159SKalle Valo #include "debug.h" 9d5c65159SKalle Valo #include "wmi.h" 10d5c65159SKalle Valo #include "hw.h" 11d5c65159SKalle Valo #include "core.h" 12d5c65159SKalle Valo #include "testmode_i.h" 13d5c65159SKalle Valo 14d5c65159SKalle Valo static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = { 15d5c65159SKalle Valo [ATH11K_TM_ATTR_CMD] = { .type = NLA_U32 }, 16d5c65159SKalle Valo [ATH11K_TM_ATTR_DATA] = { .type = NLA_BINARY, 17d5c65159SKalle Valo .len = ATH11K_TM_DATA_MAX_LEN }, 18d5c65159SKalle Valo [ATH11K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, 19d5c65159SKalle Valo [ATH11K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, 20d5c65159SKalle Valo [ATH11K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, 21d5c65159SKalle Valo }; 22d5c65159SKalle Valo 23d5c65159SKalle Valo /* Returns true if callee consumes the skb and the skb should be discarded. 24d5c65159SKalle Valo * Returns false if skb is not used. Does not sleep. 25d5c65159SKalle Valo */ 26d5c65159SKalle Valo bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb) 27d5c65159SKalle Valo { 28d5c65159SKalle Valo struct sk_buff *nl_skb; 29d5c65159SKalle Valo bool consumed; 30d5c65159SKalle Valo int ret; 31d5c65159SKalle Valo 32d5c65159SKalle Valo ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, 33d5c65159SKalle Valo "testmode event wmi cmd_id %d skb %pK skb->len %d\n", 34d5c65159SKalle Valo cmd_id, skb, skb->len); 35d5c65159SKalle Valo 36d5c65159SKalle Valo ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len); 37d5c65159SKalle Valo 38d5c65159SKalle Valo spin_lock_bh(&ar->data_lock); 39d5c65159SKalle Valo 40d5c65159SKalle Valo consumed = true; 41d5c65159SKalle Valo 42d5c65159SKalle Valo nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy, 43d5c65159SKalle Valo 2 * sizeof(u32) + skb->len, 44d5c65159SKalle Valo GFP_ATOMIC); 45d5c65159SKalle Valo if (!nl_skb) { 46d5c65159SKalle Valo ath11k_warn(ar->ab, 47d5c65159SKalle Valo "failed to allocate skb for testmode wmi event\n"); 48d5c65159SKalle Valo goto out; 49d5c65159SKalle Valo } 50d5c65159SKalle Valo 51d5c65159SKalle Valo ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI); 52d5c65159SKalle Valo if (ret) { 53d5c65159SKalle Valo ath11k_warn(ar->ab, 54d5c65159SKalle Valo "failed to to put testmode wmi event cmd attribute: %d\n", 55d5c65159SKalle Valo ret); 56d5c65159SKalle Valo kfree_skb(nl_skb); 57d5c65159SKalle Valo goto out; 58d5c65159SKalle Valo } 59d5c65159SKalle Valo 60d5c65159SKalle Valo ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id); 61d5c65159SKalle Valo if (ret) { 62d5c65159SKalle Valo ath11k_warn(ar->ab, 63d5c65159SKalle Valo "failed to to put testmode wmi even cmd_id: %d\n", 64d5c65159SKalle Valo ret); 65d5c65159SKalle Valo kfree_skb(nl_skb); 66d5c65159SKalle Valo goto out; 67d5c65159SKalle Valo } 68d5c65159SKalle Valo 69d5c65159SKalle Valo ret = nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data); 70d5c65159SKalle Valo if (ret) { 71d5c65159SKalle Valo ath11k_warn(ar->ab, 72d5c65159SKalle Valo "failed to copy skb to testmode wmi event: %d\n", 73d5c65159SKalle Valo ret); 74d5c65159SKalle Valo kfree_skb(nl_skb); 75d5c65159SKalle Valo goto out; 76d5c65159SKalle Valo } 77d5c65159SKalle Valo 78d5c65159SKalle Valo cfg80211_testmode_event(nl_skb, GFP_ATOMIC); 79d5c65159SKalle Valo 80d5c65159SKalle Valo out: 81d5c65159SKalle Valo spin_unlock_bh(&ar->data_lock); 82d5c65159SKalle Valo 83d5c65159SKalle Valo return consumed; 84d5c65159SKalle Valo } 85d5c65159SKalle Valo 86d5c65159SKalle Valo static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[]) 87d5c65159SKalle Valo { 88d5c65159SKalle Valo struct sk_buff *skb; 89d5c65159SKalle Valo int ret; 90d5c65159SKalle Valo 91d5c65159SKalle Valo ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, 92d5c65159SKalle Valo "testmode cmd get version_major %d version_minor %d\n", 93d5c65159SKalle Valo ATH11K_TESTMODE_VERSION_MAJOR, 94d5c65159SKalle Valo ATH11K_TESTMODE_VERSION_MINOR); 95d5c65159SKalle Valo 96d5c65159SKalle Valo skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy, 97d5c65159SKalle Valo nla_total_size(sizeof(u32))); 98d5c65159SKalle Valo if (!skb) 99d5c65159SKalle Valo return -ENOMEM; 100d5c65159SKalle Valo 101d5c65159SKalle Valo ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR, 102d5c65159SKalle Valo ATH11K_TESTMODE_VERSION_MAJOR); 103d5c65159SKalle Valo if (ret) { 104d5c65159SKalle Valo kfree_skb(skb); 105d5c65159SKalle Valo return ret; 106d5c65159SKalle Valo } 107d5c65159SKalle Valo 108d5c65159SKalle Valo ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR, 109d5c65159SKalle Valo ATH11K_TESTMODE_VERSION_MINOR); 110d5c65159SKalle Valo if (ret) { 111d5c65159SKalle Valo kfree_skb(skb); 112d5c65159SKalle Valo return ret; 113d5c65159SKalle Valo } 114d5c65159SKalle Valo 115d5c65159SKalle Valo return cfg80211_testmode_reply(skb); 116d5c65159SKalle Valo } 117d5c65159SKalle Valo 118d5c65159SKalle Valo static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[]) 119d5c65159SKalle Valo { 120d5c65159SKalle Valo struct ath11k_pdev_wmi *wmi = ar->wmi; 121d5c65159SKalle Valo struct sk_buff *skb; 122d5c65159SKalle Valo u32 cmd_id, buf_len; 123d5c65159SKalle Valo int ret; 124d5c65159SKalle Valo void *buf; 125d5c65159SKalle Valo 126d5c65159SKalle Valo mutex_lock(&ar->conf_mutex); 127d5c65159SKalle Valo 128d5c65159SKalle Valo if (ar->state != ATH11K_STATE_ON) { 129d5c65159SKalle Valo ret = -ENETDOWN; 130d5c65159SKalle Valo goto out; 131d5c65159SKalle Valo } 132d5c65159SKalle Valo 133d5c65159SKalle Valo if (!tb[ATH11K_TM_ATTR_DATA]) { 134d5c65159SKalle Valo ret = -EINVAL; 135d5c65159SKalle Valo goto out; 136d5c65159SKalle Valo } 137d5c65159SKalle Valo 138d5c65159SKalle Valo if (!tb[ATH11K_TM_ATTR_WMI_CMDID]) { 139d5c65159SKalle Valo ret = -EINVAL; 140d5c65159SKalle Valo goto out; 141d5c65159SKalle Valo } 142d5c65159SKalle Valo 143d5c65159SKalle Valo buf = nla_data(tb[ATH11K_TM_ATTR_DATA]); 144d5c65159SKalle Valo buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]); 145d5c65159SKalle Valo cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]); 146d5c65159SKalle Valo 147d5c65159SKalle Valo ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, 148d5c65159SKalle Valo "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n", 149d5c65159SKalle Valo cmd_id, buf, buf_len); 150d5c65159SKalle Valo 151d5c65159SKalle Valo ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len); 152d5c65159SKalle Valo 153*6bc9d6f7SJohn Crispin skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, buf_len); 154d5c65159SKalle Valo if (!skb) { 155d5c65159SKalle Valo ret = -ENOMEM; 156d5c65159SKalle Valo goto out; 157d5c65159SKalle Valo } 158d5c65159SKalle Valo 159d5c65159SKalle Valo memcpy(skb->data, buf, buf_len); 160d5c65159SKalle Valo 161d5c65159SKalle Valo ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id); 162d5c65159SKalle Valo if (ret) { 163d5c65159SKalle Valo dev_kfree_skb(skb); 164d5c65159SKalle Valo ath11k_warn(ar->ab, "failed to transmit wmi command (testmode): %d\n", 165d5c65159SKalle Valo ret); 166d5c65159SKalle Valo goto out; 167d5c65159SKalle Valo } 168d5c65159SKalle Valo 169d5c65159SKalle Valo ret = 0; 170d5c65159SKalle Valo 171d5c65159SKalle Valo out: 172d5c65159SKalle Valo mutex_unlock(&ar->conf_mutex); 173d5c65159SKalle Valo return ret; 174d5c65159SKalle Valo } 175d5c65159SKalle Valo 176d5c65159SKalle Valo int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 177d5c65159SKalle Valo void *data, int len) 178d5c65159SKalle Valo { 179d5c65159SKalle Valo struct ath11k *ar = hw->priv; 180d5c65159SKalle Valo struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1]; 181d5c65159SKalle Valo int ret; 182d5c65159SKalle Valo 183d5c65159SKalle Valo ret = nla_parse(tb, ATH11K_TM_ATTR_MAX, data, len, ath11k_tm_policy, 184d5c65159SKalle Valo NULL); 185d5c65159SKalle Valo if (ret) 186d5c65159SKalle Valo return ret; 187d5c65159SKalle Valo 188d5c65159SKalle Valo if (!tb[ATH11K_TM_ATTR_CMD]) 189d5c65159SKalle Valo return -EINVAL; 190d5c65159SKalle Valo 191d5c65159SKalle Valo switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) { 192d5c65159SKalle Valo case ATH11K_TM_CMD_GET_VERSION: 193d5c65159SKalle Valo return ath11k_tm_cmd_get_version(ar, tb); 194d5c65159SKalle Valo case ATH11K_TM_CMD_WMI: 195d5c65159SKalle Valo return ath11k_tm_cmd_wmi(ar, tb); 196d5c65159SKalle Valo default: 197d5c65159SKalle Valo return -EOPNOTSUPP; 198d5c65159SKalle Valo } 199d5c65159SKalle Valo } 200