Merge tag 'nds32-for-linus-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / net / wireless / ath / wcn36xx / testmode.c
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(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 }