1 /* 2 * Copyright (c) 2018, The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <net/netlink.h> 18 #include <linux/firmware.h> 19 #include <net/cfg80211.h> 20 #include "wcn36xx.h" 21 22 #include "testmode.h" 23 #include "testmode_i.h" 24 #include "hal.h" 25 #include "smd.h" 26 27 static const struct nla_policy wcn36xx_tm_policy[WCN36XX_TM_ATTR_MAX + 1] = { 28 [WCN36XX_TM_ATTR_CMD] = { .type = NLA_U16 }, 29 [WCN36XX_TM_ATTR_DATA] = { .type = NLA_BINARY, 30 .len = WCN36XX_TM_DATA_MAX_LEN }, 31 }; 32 33 struct build_release_number { 34 u16 drv_major; 35 u16 drv_minor; 36 u16 drv_patch; 37 u16 drv_build; 38 u16 ptt_max; 39 u16 ptt_min; 40 u16 fw_ver; 41 } __packed; 42 43 static int wcn36xx_tm_cmd_ptt(struct wcn36xx *wcn, struct ieee80211_vif *vif, 44 struct nlattr *tb[]) 45 { 46 int ret = 0, buf_len; 47 void *buf; 48 struct ftm_rsp_msg *msg, *rsp = NULL; 49 struct sk_buff *skb; 50 51 if (!tb[WCN36XX_TM_ATTR_DATA]) 52 return -EINVAL; 53 54 buf = nla_data(tb[WCN36XX_TM_ATTR_DATA]); 55 buf_len = nla_len(tb[WCN36XX_TM_ATTR_DATA]); 56 msg = (struct ftm_rsp_msg *)buf; 57 58 wcn36xx_dbg(WCN36XX_DBG_TESTMODE, 59 "testmode cmd wmi msg_id 0x%04X msg_len %d buf %pK buf_len %d\n", 60 msg->msg_id, msg->msg_body_length, 61 buf, buf_len); 62 63 wcn36xx_dbg_dump(WCN36XX_DBG_TESTMODE_DUMP, "REQ ", buf, buf_len); 64 65 if (msg->msg_id == MSG_GET_BUILD_RELEASE_NUMBER) { 66 struct build_release_number *body = 67 (struct build_release_number *) 68 msg->msg_response; 69 70 body->drv_major = wcn->fw_major; 71 body->drv_minor = wcn->fw_minor; 72 body->drv_patch = wcn->fw_version; 73 body->drv_build = wcn->fw_revision; 74 body->ptt_max = 10; 75 body->ptt_min = 0; 76 77 rsp = msg; 78 rsp->resp_status = 0; 79 } else { 80 wcn36xx_dbg(WCN36XX_DBG_TESTMODE, 81 "PPT Request >> HAL size %d\n", 82 msg->msg_body_length); 83 84 msg->resp_status = wcn36xx_smd_process_ptt_msg(wcn, vif, msg, 85 msg->msg_body_length, (void *)(&rsp)); 86 87 wcn36xx_dbg(WCN36XX_DBG_TESTMODE, 88 "Response status = %d\n", 89 msg->resp_status); 90 if (rsp) 91 wcn36xx_dbg(WCN36XX_DBG_TESTMODE, 92 "PPT Response << HAL size %d\n", 93 rsp->msg_body_length); 94 } 95 96 if (!rsp) { 97 rsp = msg; 98 wcn36xx_warn("No response! Echoing request with response status %d\n", 99 rsp->resp_status); 100 } 101 wcn36xx_dbg_dump(WCN36XX_DBG_TESTMODE_DUMP, "RSP ", 102 rsp, rsp->msg_body_length); 103 104 skb = cfg80211_testmode_alloc_reply_skb(wcn->hw->wiphy, 105 nla_total_size(msg->msg_body_length)); 106 if (!skb) { 107 ret = -ENOMEM; 108 goto out; 109 } 110 111 ret = nla_put(skb, WCN36XX_TM_ATTR_DATA, rsp->msg_body_length, rsp); 112 if (ret) { 113 kfree_skb(skb); 114 goto out; 115 } 116 117 ret = cfg80211_testmode_reply(skb); 118 119 out: 120 if (rsp != msg) 121 kfree(rsp); 122 123 return ret; 124 } 125 126 int wcn36xx_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 127 void *data, int len) 128 { 129 struct wcn36xx *wcn = hw->priv; 130 struct nlattr *tb[WCN36XX_TM_ATTR_MAX + 1]; 131 int ret = 0; 132 unsigned short attr; 133 134 wcn36xx_dbg_dump(WCN36XX_DBG_TESTMODE_DUMP, "Data:", data, len); 135 ret = nla_parse_deprecated(tb, WCN36XX_TM_ATTR_MAX, data, len, 136 wcn36xx_tm_policy, NULL); 137 if (ret) 138 return ret; 139 140 if (!tb[WCN36XX_TM_ATTR_CMD]) 141 return -EINVAL; 142 143 attr = nla_get_u16(tb[WCN36XX_TM_ATTR_CMD]); 144 145 if (attr != WCN36XX_TM_CMD_PTT) 146 return -EOPNOTSUPP; 147 148 return wcn36xx_tm_cmd_ptt(wcn, vif, tb); 149 } 150