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(nla: tb[WCN36XX_TM_ATTR_DATA]); |
55 | buf_len = nla_len(nla: tb[WCN36XX_TM_ATTR_DATA]); |
56 | 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, ptt_msg: msg, |
85 | len: msg->msg_body_length, ptt_rsp_msg: (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(wiphy: wcn->hw->wiphy, |
105 | approxlen: nla_total_size(payload: msg->msg_body_length)); |
106 | if (!skb) { |
107 | ret = -ENOMEM; |
108 | goto out; |
109 | } |
110 | |
111 | ret = nla_put(skb, attrtype: WCN36XX_TM_ATTR_DATA, attrlen: rsp->msg_body_length, data: 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(objp: 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, maxtype: WCN36XX_TM_ATTR_MAX, head: data, len, |
136 | policy: 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(nla: 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 | |