blob: faffbb1bbf5d3330893b4bab8c451769e55ff2c6 [file] [log] [blame]
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +05301/*
Nachiket Kukade338547b2019-01-03 18:50:46 +05302 * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved.
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +05303 *
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/**
20 * DOC: wlan_hdd_apf.c
21 *
22 * Android Packet Filter support and implementation
23 */
24
25#include "wlan_hdd_apf.h"
26#include "qca_vendor.h"
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -070027#include "wlan_osif_request_manager.h"
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053028
29/*
30 * define short names for the global vendor params
Nachiket Kukadee547a482018-05-22 16:43:30 +053031 * used by __wlan_hdd_cfg80211_apf_offload()
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053032 */
Nachiket Kukadee547a482018-05-22 16:43:30 +053033#define APF_INVALID \
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053034 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID
Nachiket Kukade177b5b02018-05-22 20:52:17 +053035#define APF_SUBCMD \
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053036 QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER
Nachiket Kukadee547a482018-05-22 16:43:30 +053037#define APF_VERSION \
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053038 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION
Nachiket Kukadee547a482018-05-22 16:43:30 +053039#define APF_FILTER_ID \
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053040 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID
Nachiket Kukadee547a482018-05-22 16:43:30 +053041#define APF_PACKET_SIZE \
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053042 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE
Nachiket Kukadee547a482018-05-22 16:43:30 +053043#define APF_CURRENT_OFFSET \
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053044 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET
Nachiket Kukadee547a482018-05-22 16:43:30 +053045#define APF_PROGRAM \
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053046 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM
Nachiket Kukade177b5b02018-05-22 20:52:17 +053047#define APF_PROG_LEN \
48 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH
Nachiket Kukadee547a482018-05-22 16:43:30 +053049#define APF_MAX \
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053050 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX
51
52static const struct nla_policy
Nachiket Kukadee547a482018-05-22 16:43:30 +053053wlan_hdd_apf_offload_policy[APF_MAX + 1] = {
Nachiket Kukade177b5b02018-05-22 20:52:17 +053054 [APF_SUBCMD] = {.type = NLA_U32},
Nachiket Kukadee547a482018-05-22 16:43:30 +053055 [APF_VERSION] = {.type = NLA_U32},
56 [APF_FILTER_ID] = {.type = NLA_U32},
57 [APF_PACKET_SIZE] = {.type = NLA_U32},
58 [APF_CURRENT_OFFSET] = {.type = NLA_U32},
Nachiket Kukade177b5b02018-05-22 20:52:17 +053059 [APF_PROGRAM] = {.type = NLA_BINARY,
60 .len = MAX_APF_MEMORY_LEN},
61 [APF_PROG_LEN] = {.type = NLA_U32},
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053062};
63
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +053064void hdd_apf_context_init(struct hdd_adapter *adapter)
Nachiket Kukade177b5b02018-05-22 20:52:17 +053065{
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +053066 qdf_event_create(&adapter->apf_context.qdf_apf_event);
67 qdf_spinlock_create(&adapter->apf_context.lock);
68 adapter->apf_context.apf_enabled = true;
Nachiket Kukade177b5b02018-05-22 20:52:17 +053069}
70
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +053071void hdd_apf_context_destroy(struct hdd_adapter *adapter)
Nachiket Kukade177b5b02018-05-22 20:52:17 +053072{
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +053073 qdf_event_destroy(&adapter->apf_context.qdf_apf_event);
74 qdf_spinlock_destroy(&adapter->apf_context.lock);
75 qdf_mem_zero(&adapter->apf_context,
76 sizeof(struct hdd_apf_context));
Nachiket Kukade177b5b02018-05-22 20:52:17 +053077}
78
Nachiket Kukadee547a482018-05-22 16:43:30 +053079struct apf_offload_priv {
80 struct sir_apf_get_offload apf_get_offload;
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053081};
82
Nachiket Kukade177b5b02018-05-22 20:52:17 +053083void hdd_get_apf_capabilities_cb(void *context,
84 struct sir_apf_get_offload *data)
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053085{
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -070086 struct osif_request *request;
Nachiket Kukadee547a482018-05-22 16:43:30 +053087 struct apf_offload_priv *priv;
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053088
89 hdd_enter();
90
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -070091 request = osif_request_get(context);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +053092 if (!request) {
93 hdd_err("Obsolete request");
94 return;
95 }
96
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -070097 priv = osif_request_priv(request);
Nachiket Kukadee547a482018-05-22 16:43:30 +053098 priv->apf_get_offload = *data;
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -070099 osif_request_complete(request);
100 osif_request_put(request);
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530101
102 hdd_exit();
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530103}
104
105/**
Nachiket Kukadee547a482018-05-22 16:43:30 +0530106 * hdd_post_get_apf_capabilities_rsp() - Callback function to APF Offload
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530107 * @hdd_context: hdd_context
Nachiket Kukadee547a482018-05-22 16:43:30 +0530108 * @apf_get_offload: struct for get offload
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530109 *
110 * Return: 0 on success, error number otherwise.
111 */
Nachiket Kukadee547a482018-05-22 16:43:30 +0530112static int
113hdd_post_get_apf_capabilities_rsp(struct hdd_context *hdd_ctx,
114 struct sir_apf_get_offload *apf_get_offload)
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530115{
116 struct sk_buff *skb;
117 uint32_t nl_buf_len;
118
119 hdd_enter();
120
121 nl_buf_len = NLMSG_HDRLEN;
122 nl_buf_len +=
Nachiket Kukadee547a482018-05-22 16:43:30 +0530123 (sizeof(apf_get_offload->max_bytes_for_apf_inst) + NLA_HDRLEN) +
124 (sizeof(apf_get_offload->apf_version) + NLA_HDRLEN);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530125
126 skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
127 if (!skb) {
128 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
129 return -ENOMEM;
130 }
131
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530132 hdd_ctx->apf_version = apf_get_offload->apf_version;
Nachiket Kukadee547a482018-05-22 16:43:30 +0530133 hdd_debug("APF Version: %u APF max bytes: %u",
134 apf_get_offload->apf_version,
135 apf_get_offload->max_bytes_for_apf_inst);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530136
Nachiket Kukadee547a482018-05-22 16:43:30 +0530137 if (nla_put_u32(skb, APF_PACKET_SIZE,
138 apf_get_offload->max_bytes_for_apf_inst) ||
139 nla_put_u32(skb, APF_VERSION, apf_get_offload->apf_version)) {
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530140 hdd_err("nla put failure");
141 goto nla_put_failure;
142 }
143
144 cfg80211_vendor_cmd_reply(skb);
145 hdd_exit();
146 return 0;
147
148nla_put_failure:
149 kfree_skb(skb);
150 return -EINVAL;
151}
152
153/**
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530154 * hdd_get_apf_capabilities - Get APF offload Capabilities
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530155 * @hdd_ctx: Hdd context
156 *
157 * Return: 0 on success, errno on failure
158 */
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530159static int hdd_get_apf_capabilities(struct hdd_context *hdd_ctx)
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530160{
161 QDF_STATUS status;
162 int ret;
163 void *cookie;
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -0700164 struct osif_request *request;
Nachiket Kukadee547a482018-05-22 16:43:30 +0530165 struct apf_offload_priv *priv;
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -0700166 static const struct osif_request_params params = {
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530167 .priv_size = sizeof(*priv),
Nachiket Kukadee547a482018-05-22 16:43:30 +0530168 .timeout_ms = WLAN_WAIT_TIME_APF,
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530169 };
170
171 hdd_enter();
172
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -0700173 request = osif_request_alloc(&params);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530174 if (!request) {
175 hdd_err("Unable to allocate request");
176 return -EINVAL;
177 }
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -0700178 cookie = osif_request_cookie(request);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530179
Jeff Johnsonc6162232018-06-20 13:33:07 -0700180 status = sme_get_apf_capabilities(hdd_ctx->mac_handle,
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530181 hdd_get_apf_capabilities_cb,
182 cookie);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530183 if (!QDF_IS_STATUS_SUCCESS(status)) {
Nachiket Kukadee547a482018-05-22 16:43:30 +0530184 hdd_err("Unable to retrieve APF caps");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530185 ret = qdf_status_to_os_return(status);
186 goto cleanup;
187 }
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -0700188 ret = osif_request_wait_for_response(request);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530189 if (ret) {
190 hdd_err("Target response timed out");
191 goto cleanup;
192 }
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -0700193 priv = osif_request_priv(request);
Nachiket Kukadee547a482018-05-22 16:43:30 +0530194 ret = hdd_post_get_apf_capabilities_rsp(hdd_ctx,
195 &priv->apf_get_offload);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530196 if (ret)
Nachiket Kukadee547a482018-05-22 16:43:30 +0530197 hdd_err("Failed to post get apf capabilities");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530198
199cleanup:
200 /*
201 * either we never sent a request to SME, we sent a request to
202 * SME and timed out, or we sent a request to SME, received a
203 * response from SME, and posted the response to userspace.
204 * regardless we are done with the request.
205 */
Jeff Johnsonf1a99ea2018-06-28 13:27:01 -0700206 osif_request_put(request);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530207 hdd_exit();
208
209 return ret;
210}
211
212/**
Nachiket Kukadee547a482018-05-22 16:43:30 +0530213 * hdd_set_reset_apf_offload - Post set/reset apf to SME
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530214 * @hdd_ctx: Hdd context
215 * @tb: Length of @data
216 * @adapter: pointer to adapter struct
217 *
218 * Return: 0 on success; errno on failure
219 */
Nachiket Kukadee547a482018-05-22 16:43:30 +0530220static int hdd_set_reset_apf_offload(struct hdd_context *hdd_ctx,
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530221 struct nlattr **tb,
222 struct hdd_adapter *adapter)
223{
Nachiket Kukade4f686582018-11-19 19:18:27 +0530224 struct sir_apf_set_offload apf_set_offload = {0};
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530225 QDF_STATUS status;
226 int prog_len;
227 int ret = 0;
228
229 hdd_enter();
230
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530231 if (!hdd_conn_is_connected(
232 WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
233 hdd_err("Not in Connected state!");
234 return -ENOTSUPP;
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530235 }
236
Nachiket Kukadee547a482018-05-22 16:43:30 +0530237 /* Parse and fetch apf packet size */
238 if (!tb[APF_PACKET_SIZE]) {
239 hdd_err("attr apf packet size failed");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530240 ret = -EINVAL;
241 goto fail;
242 }
Nachiket Kukade4f686582018-11-19 19:18:27 +0530243 apf_set_offload.total_length = nla_get_u32(tb[APF_PACKET_SIZE]);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530244
Nachiket Kukade4f686582018-11-19 19:18:27 +0530245 if (!apf_set_offload.total_length) {
Nachiket Kukadee547a482018-05-22 16:43:30 +0530246 hdd_debug("APF reset packet filter received");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530247 goto post_sme;
248 }
249
Nachiket Kukadee547a482018-05-22 16:43:30 +0530250 /* Parse and fetch apf program */
251 if (!tb[APF_PROGRAM]) {
252 hdd_err("attr apf program failed");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530253 ret = -EINVAL;
254 goto fail;
255 }
256
Nachiket Kukadee547a482018-05-22 16:43:30 +0530257 prog_len = nla_len(tb[APF_PROGRAM]);
Nachiket Kukade4f686582018-11-19 19:18:27 +0530258 apf_set_offload.program = qdf_mem_malloc(sizeof(uint8_t) * prog_len);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530259
Nachiket Kukade4f686582018-11-19 19:18:27 +0530260 if (!apf_set_offload.program) {
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530261 ret = -ENOMEM;
262 goto fail;
263 }
264
Nachiket Kukade4f686582018-11-19 19:18:27 +0530265 apf_set_offload.current_length = prog_len;
266 nla_memcpy(apf_set_offload.program, tb[APF_PROGRAM], prog_len);
267 apf_set_offload.session_id = adapter->session_id;
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530268
Nachiket Kukadee547a482018-05-22 16:43:30 +0530269 hdd_debug("APF set instructions");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530270 QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
Nachiket Kukade4f686582018-11-19 19:18:27 +0530271 apf_set_offload.program, prog_len);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530272
273 /* Parse and fetch filter Id */
Nachiket Kukadee547a482018-05-22 16:43:30 +0530274 if (!tb[APF_FILTER_ID]) {
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530275 hdd_err("attr filter id failed");
276 ret = -EINVAL;
277 goto fail;
278 }
Nachiket Kukade4f686582018-11-19 19:18:27 +0530279 apf_set_offload.filter_id = nla_get_u32(tb[APF_FILTER_ID]);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530280
281 /* Parse and fetch current offset */
Nachiket Kukadee547a482018-05-22 16:43:30 +0530282 if (!tb[APF_CURRENT_OFFSET]) {
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530283 hdd_err("attr current offset failed");
284 ret = -EINVAL;
285 goto fail;
286 }
Nachiket Kukade4f686582018-11-19 19:18:27 +0530287 apf_set_offload.current_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530288
289post_sme:
Nachiket Kukadee547a482018-05-22 16:43:30 +0530290 hdd_debug("Posting APF SET/RESET to SME, session_id: %d APF Version: %d filter ID: %d total_length: %d current_length: %d current offset: %d",
Nachiket Kukade4f686582018-11-19 19:18:27 +0530291 apf_set_offload.session_id, apf_set_offload.version,
292 apf_set_offload.filter_id, apf_set_offload.total_length,
293 apf_set_offload.current_length,
294 apf_set_offload.current_offset);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530295
Nachiket Kukade4f686582018-11-19 19:18:27 +0530296 status = sme_set_apf_instructions(hdd_ctx->mac_handle,
297 &apf_set_offload);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530298 if (!QDF_IS_STATUS_SUCCESS(status)) {
Nachiket Kukadee547a482018-05-22 16:43:30 +0530299 hdd_err("sme_set_apf_instructions failed(err=%d)", status);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530300 ret = -EINVAL;
301 goto fail;
302 }
303 hdd_exit();
304
305fail:
Nachiket Kukade4f686582018-11-19 19:18:27 +0530306 if (apf_set_offload.current_length)
307 qdf_mem_free(apf_set_offload.program);
Rajeev Kumar Sirasanagandla85f8b022018-03-12 12:52:59 +0530308
309 if (!ret)
310 hdd_ctx->apf_enabled_v2 = true;
311
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530312 return ret;
313}
314
315/**
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530316 * hdd_enable_disable_apf - Enable or Disable the APF interpreter
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530317 * @adapter: HDD Adapter
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530318 * @apf_enable: true: Enable APF Int., false: disable APF Int.
319 *
320 * Return: 0 on success, errno on failure
321 */
322static int
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530323hdd_enable_disable_apf(struct hdd_adapter *adapter, bool apf_enable)
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530324{
325 QDF_STATUS status;
326
327 hdd_enter();
328
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530329 status = sme_set_apf_enable_disable(hdd_adapter_get_mac_handle(adapter),
330 adapter->session_id, apf_enable);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530331 if (!QDF_IS_STATUS_SUCCESS(status)) {
332 hdd_err("Unable to post sme apf enable/disable message (status-%d)",
333 status);
334 return -EINVAL;
335 }
336
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530337 adapter->apf_context.apf_enabled = apf_enable;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530338
339 hdd_exit();
340 return 0;
341}
342
343/**
344 * hdd_apf_write_memory - Write into the apf work memory
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530345 * @adapter: HDD Adapter
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530346 * @tb: list of attributes
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530347 *
348 * This function writes code/data into the APF work memory and
349 * provides program length that is passed on to the interpreter.
350 *
351 * Return: 0 on success, errno on failure
352 */
353static int
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530354hdd_apf_write_memory(struct hdd_adapter *adapter, struct nlattr **tb)
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530355{
356 struct wmi_apf_write_memory_params write_mem_params = {0};
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530357 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530358 QDF_STATUS status;
359 int ret = 0;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530360
361 hdd_enter();
362
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530363
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530364 write_mem_params.vdev_id = adapter->session_id;
365 if (adapter->apf_context.apf_enabled) {
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530366 hdd_err("Cannot get/set when APF interpreter is enabled");
367 return -EINVAL;
368 }
369
370 /* Read program length */
371 if (!tb[APF_PROG_LEN]) {
372 hdd_err("attr program length failed");
373 return -EINVAL;
374 }
375 write_mem_params.program_len = nla_get_u32(tb[APF_PROG_LEN]);
376
377 /* Read APF work memory offset */
378 if (!tb[APF_CURRENT_OFFSET]) {
379 hdd_err("attr apf packet size failed");
380 return -EINVAL;
381 }
382 write_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
383
384 /* Parse and fetch apf program */
385 if (!tb[APF_PROGRAM]) {
386 hdd_err("attr apf program failed");
387 return -EINVAL;
388 }
389
390 write_mem_params.length = nla_len(tb[APF_PROGRAM]);
391 if (!write_mem_params.length) {
392 hdd_err("Program attr with empty data");
393 return -EINVAL;
394 }
395
396 write_mem_params.buf = qdf_mem_malloc(sizeof(uint8_t)
397 * write_mem_params.length);
Min Liu74a1a502018-10-10 19:59:07 +0800398 if (!write_mem_params.buf)
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530399 return -EINVAL;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530400 nla_memcpy(write_mem_params.buf, tb[APF_PROGRAM],
401 write_mem_params.length);
402
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530403 write_mem_params.apf_version = hdd_ctx->apf_version;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530404
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530405 status = sme_apf_write_work_memory(hdd_adapter_get_mac_handle(adapter),
Jeff Johnsonc6162232018-06-20 13:33:07 -0700406 &write_mem_params);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530407 if (!QDF_IS_STATUS_SUCCESS(status)) {
408 hdd_err("Unable to retrieve APF caps");
409 ret = -EINVAL;
410 }
411
Nachiket Kukade338547b2019-01-03 18:50:46 +0530412 hdd_debug("Writing successful into APF work memory from offset 0x%X:",
413 write_mem_params.addr_offset);
414 QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
415 write_mem_params.buf, write_mem_params.length);
416
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530417 if (write_mem_params.buf)
418 qdf_mem_free(write_mem_params.buf);
419
420 hdd_exit();
421 return ret;
422}
423
424/**
425 * hdd_apf_read_memory_callback - HDD Callback for the APF read memory
426 * operation
427 * @context: Hdd context
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530428 * @evt: APF read memory event response parameters
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530429 *
430 * Return: 0 on success, errno on failure
431 */
432static void
433hdd_apf_read_memory_callback(void *hdd_context,
434 struct wmi_apf_read_memory_resp_event_params *evt)
435{
436 struct hdd_context *hdd_ctx = hdd_context;
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530437 struct hdd_adapter *adapter;
438 struct hdd_apf_context *context;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530439 uint8_t *buf_ptr;
440 uint32_t pkt_offset;
441
442 hdd_enter();
443
444 if (wlan_hdd_validate_context(hdd_ctx) || !evt) {
445 hdd_err("HDD context is invalid or event buf(%pK) is null",
446 evt);
447 return;
448 }
449
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530450 adapter = hdd_get_adapter_by_vdev(hdd_ctx, evt->vdev_id);
Ashish Kumar Dhanotiya04cc7c22018-05-11 14:43:10 +0530451 if (hdd_validate_adapter(adapter))
452 return;
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530453 context = &adapter->apf_context;
454
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530455 if (context->magic != APF_CONTEXT_MAGIC) {
456 /* The caller presumably timed out, nothing to do */
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530457 hdd_err("Caller timed out or corrupt magic, simply return");
458 return;
459 }
460
461 if (evt->offset < context->offset) {
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530462 hdd_err("Offset in read event(%d) smaller than offset in request(%d)!",
463 evt->offset, context->offset);
464 return;
465 }
466
467 /*
468 * offset in the event is relative to the APF work memory.
469 * Calculate the packet offset, which gives us the relative
470 * location in the buffer to start copy into.
471 */
472 pkt_offset = evt->offset - context->offset;
473
474 if (context->buf_len < pkt_offset + evt->length) {
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530475 hdd_err("Read chunk exceeding allocated space");
476 return;
477 }
478 buf_ptr = context->buf + pkt_offset;
479
480 qdf_mem_copy(buf_ptr, evt->data, evt->length);
481
482 if (!evt->more_data) {
483 /* Release the caller after last event, clear magic */
484 context->magic = 0;
485 qdf_event_set(&context->qdf_apf_event);
486 }
487
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530488 hdd_exit();
489}
490
491/**
492 * hdd_apf_read_memory - Read part of the apf work memory
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530493 * @adapter: HDD Adapter
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530494 * @tb: list of attributes
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530495 *
496 * Return: 0 on success, errno on failure
497 */
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530498static int hdd_apf_read_memory(struct hdd_adapter *adapter, struct nlattr **tb)
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530499{
500 struct wmi_apf_read_memory_params read_mem_params = {0};
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530501 struct hdd_apf_context *context = &adapter->apf_context;
502 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530503 QDF_STATUS status;
504 unsigned long nl_buf_len = NLMSG_HDRLEN;
505 int ret = 0;
506 struct sk_buff *skb = NULL;
507 uint8_t *bufptr;
508
509 hdd_enter();
510
Rajeev Kumar75fc36e2018-09-04 11:41:10 -0700511 if (context->apf_enabled) {
512 hdd_err("Cannot get/set while interpreter is enabled");
513 return -EINVAL;
514 }
515
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530516 read_mem_params.vdev_id = adapter->session_id;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530517
518 /* Read APF work memory offset */
519 if (!tb[APF_CURRENT_OFFSET]) {
520 hdd_err("attr apf memory offset failed");
521 return -EINVAL;
522 }
523 read_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
524
525 /* Read length */
526 if (!tb[APF_PACKET_SIZE]) {
527 hdd_err("attr apf packet size failed");
528 return -EINVAL;
529 }
530 read_mem_params.length = nla_get_u32(tb[APF_PACKET_SIZE]);
531 if (!read_mem_params.length) {
532 hdd_err("apf read length cannot be zero!");
533 return -EINVAL;
534 }
535 bufptr = qdf_mem_malloc(read_mem_params.length);
Min Liu74a1a502018-10-10 19:59:07 +0800536 if (!bufptr)
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530537 return -ENOMEM;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530538
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530539 qdf_event_reset(&context->qdf_apf_event);
540 context->offset = read_mem_params.addr_offset;
541
542 context->buf = bufptr;
543 context->buf_len = read_mem_params.length;
544 context->magic = APF_CONTEXT_MAGIC;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530545
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530546 status = sme_apf_read_work_memory(hdd_adapter_get_mac_handle(adapter),
Jeff Johnsonc6162232018-06-20 13:33:07 -0700547 &read_mem_params,
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530548 hdd_apf_read_memory_callback);
549 if (QDF_IS_STATUS_ERROR(status)) {
550 hdd_err("Unable to post sme APF read memory message (status-%d)",
551 status);
552 ret = -EINVAL;
553 goto fail;
554 }
555
556 /* request was sent -- wait for the response */
557 status = qdf_wait_for_event_completion(&context->qdf_apf_event,
558 WLAN_WAIT_TIME_APF_READ_MEM);
559 if (QDF_IS_STATUS_ERROR(status)) {
560 hdd_err("Target response timed out");
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530561 context->magic = 0;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530562 ret = -ETIMEDOUT;
563 goto fail;
564 }
565
566 nl_buf_len += sizeof(uint32_t) + NLA_HDRLEN;
567 nl_buf_len += context->buf_len + NLA_HDRLEN;
568
569 skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
570 if (!skb) {
571 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
572 ret = -ENOMEM;
573 goto fail;
574 }
575
576 if (nla_put_u32(skb, APF_SUBCMD, QCA_WLAN_READ_PACKET_FILTER) ||
577 nla_put(skb, APF_PROGRAM, read_mem_params.length, context->buf)) {
578 hdd_err("put fail");
579 kfree_skb(skb);
580 ret = -EINVAL;
581 goto fail;
582 }
583
584 cfg80211_vendor_cmd_reply(skb);
Nachiket Kukade338547b2019-01-03 18:50:46 +0530585
586 hdd_debug("Reading APF work memory from offset 0x%X:",
587 read_mem_params.addr_offset);
588 QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
589 context->buf, read_mem_params.length);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530590fail:
591 if (context->buf) {
592 qdf_mem_free(context->buf);
593 context->buf = NULL;
594 }
595
596 hdd_exit();
597 return ret;
598}
599
600/**
Nachiket Kukadee547a482018-05-22 16:43:30 +0530601 * wlan_hdd_cfg80211_apf_offload() - Set/Reset to APF Offload
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530602 * @wiphy: wiphy structure pointer
603 * @wdev: Wireless device structure pointer
604 * @data: Pointer to the data received
605 * @data_len: Length of @data
606 *
607 * Return: 0 on success; errno on failure
608 */
609static int
Nachiket Kukadee547a482018-05-22 16:43:30 +0530610__wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy,
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530611 struct wireless_dev *wdev,
612 const void *data, int data_len)
613{
614 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
615 struct net_device *dev = wdev->netdev;
616 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
Nachiket Kukadee547a482018-05-22 16:43:30 +0530617 struct nlattr *tb[APF_MAX + 1];
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530618 int ret_val = 0, apf_subcmd;
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530619 struct hdd_apf_context *context;
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530620
621 hdd_enter();
622
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530623 if (!adapter) {
624 hdd_err("Adapter is null");
625 return -EINVAL;
626 }
627
628 context = &adapter->apf_context;
629
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530630 ret_val = wlan_hdd_validate_context(hdd_ctx);
631 if (ret_val)
632 return ret_val;
633
634 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
635 hdd_err("Command not allowed in FTM mode");
636 return -EINVAL;
637 }
638
Wu Gao66454f12018-09-26 19:55:41 +0800639 if (!ucfg_pmo_is_apf_enabled(hdd_ctx->psoc)) {
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530640 hdd_err("APF is not supported or disabled through INI");
641 return -ENOTSUPP;
642 }
643
644 if (!(adapter->device_mode == QDF_STA_MODE ||
645 adapter->device_mode == QDF_P2P_CLIENT_MODE)) {
646 hdd_err("APF only supported in STA or P2P CLI modes!");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530647 return -ENOTSUPP;
648 }
649
Nachiket Kukadee547a482018-05-22 16:43:30 +0530650 if (wlan_cfg80211_nla_parse(tb, APF_MAX, data, data_len,
651 wlan_hdd_apf_offload_policy)) {
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530652 hdd_err("Invalid ATTR");
653 return -EINVAL;
654 }
655
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530656 if (!tb[APF_SUBCMD]) {
657 hdd_err("attr apf sub-command failed");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530658 return -EINVAL;
659 }
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530660 apf_subcmd = nla_get_u32(tb[APF_SUBCMD]);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530661
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530662 /* Do not allow simultaneous new APF commands on the same adapter */
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530663 qdf_spin_lock(&context->lock);
664 if (context->cmd_in_progress) {
665 qdf_spin_unlock(&context->lock);
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530666 hdd_err("Another cmd in progress for same session!");
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530667 return -EAGAIN;
668 }
669 context->cmd_in_progress = true;
670 qdf_spin_unlock(&context->lock);
671
672 switch (apf_subcmd) {
673 /* Legacy APF sub-commands */
674 case QCA_WLAN_SET_PACKET_FILTER:
675 ret_val = hdd_set_reset_apf_offload(hdd_ctx, tb,
676 adapter);
677 break;
678 case QCA_WLAN_GET_PACKET_FILTER:
679 ret_val = hdd_get_apf_capabilities(hdd_ctx);
680 break;
681
682 /* APF 3.0 sub-commands */
683 case QCA_WLAN_WRITE_PACKET_FILTER:
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530684 ret_val = hdd_apf_write_memory(adapter, tb);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530685 break;
686 case QCA_WLAN_READ_PACKET_FILTER:
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530687 ret_val = hdd_apf_read_memory(adapter, tb);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530688 break;
689 case QCA_WLAN_ENABLE_PACKET_FILTER:
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530690 ret_val = hdd_enable_disable_apf(adapter, true);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530691 break;
692 case QCA_WLAN_DISABLE_PACKET_FILTER:
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530693 ret_val = hdd_enable_disable_apf(adapter, false);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530694 break;
695 default:
696 hdd_err("Unknown APF Sub-command: %d", apf_subcmd);
697 ret_val = -ENOTSUPP;
698 }
699
700 qdf_spin_lock(&context->lock);
701 context->cmd_in_progress = false;
702 qdf_spin_unlock(&context->lock);
703
704 return ret_val;
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530705}
706
Nachiket Kukadee547a482018-05-22 16:43:30 +0530707int
708wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy, struct wireless_dev *wdev,
709 const void *data, int data_len)
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530710{
711 int ret;
712
713 cds_ssr_protect(__func__);
Nachiket Kukadee547a482018-05-22 16:43:30 +0530714 ret = __wlan_hdd_cfg80211_apf_offload(wiphy, wdev, data, data_len);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530715 cds_ssr_unprotect(__func__);
716
717 return ret;
718}
719