blob: 0e16fdf3c11c9de8032da965aa7c4cb631a8bf72 [file] [log] [blame]
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +05301/*
2 * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
3 *
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 Kukadee547a482018-05-22 16:43:30 +0530224 struct sir_apf_set_offload *apf_set_offload;
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 apf_set_offload = qdf_mem_malloc(sizeof(*apf_set_offload));
238 if (!apf_set_offload) {
239 hdd_err("qdf_mem_malloc failed for apf_set_offload");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530240 return -ENOMEM;
241 }
242
Nachiket Kukadee547a482018-05-22 16:43:30 +0530243 /* Parse and fetch apf packet size */
244 if (!tb[APF_PACKET_SIZE]) {
245 hdd_err("attr apf packet size failed");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530246 ret = -EINVAL;
247 goto fail;
248 }
Nachiket Kukadee547a482018-05-22 16:43:30 +0530249 apf_set_offload->total_length = nla_get_u32(tb[APF_PACKET_SIZE]);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530250
Nachiket Kukadee547a482018-05-22 16:43:30 +0530251 if (!apf_set_offload->total_length) {
252 hdd_debug("APF reset packet filter received");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530253 goto post_sme;
254 }
255
Nachiket Kukadee547a482018-05-22 16:43:30 +0530256 /* Parse and fetch apf program */
257 if (!tb[APF_PROGRAM]) {
258 hdd_err("attr apf program failed");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530259 ret = -EINVAL;
260 goto fail;
261 }
262
Nachiket Kukadee547a482018-05-22 16:43:30 +0530263 prog_len = nla_len(tb[APF_PROGRAM]);
264 apf_set_offload->program = qdf_mem_malloc(sizeof(uint8_t) * prog_len);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530265
Nachiket Kukadee547a482018-05-22 16:43:30 +0530266 if (!apf_set_offload->program) {
267 hdd_err("qdf_mem_malloc failed for apf offload program");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530268 ret = -ENOMEM;
269 goto fail;
270 }
271
Nachiket Kukadee547a482018-05-22 16:43:30 +0530272 apf_set_offload->current_length = prog_len;
273 nla_memcpy(apf_set_offload->program, tb[APF_PROGRAM], prog_len);
274 apf_set_offload->session_id = adapter->session_id;
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530275
Nachiket Kukadee547a482018-05-22 16:43:30 +0530276 hdd_debug("APF set instructions");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530277 QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
Nachiket Kukadee547a482018-05-22 16:43:30 +0530278 apf_set_offload->program, prog_len);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530279
280 /* Parse and fetch filter Id */
Nachiket Kukadee547a482018-05-22 16:43:30 +0530281 if (!tb[APF_FILTER_ID]) {
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530282 hdd_err("attr filter id failed");
283 ret = -EINVAL;
284 goto fail;
285 }
Nachiket Kukadee547a482018-05-22 16:43:30 +0530286 apf_set_offload->filter_id = nla_get_u32(tb[APF_FILTER_ID]);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530287
288 /* Parse and fetch current offset */
Nachiket Kukadee547a482018-05-22 16:43:30 +0530289 if (!tb[APF_CURRENT_OFFSET]) {
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530290 hdd_err("attr current offset failed");
291 ret = -EINVAL;
292 goto fail;
293 }
Nachiket Kukadee547a482018-05-22 16:43:30 +0530294 apf_set_offload->current_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530295
296post_sme:
Nachiket Kukadee547a482018-05-22 16:43:30 +0530297 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",
298 apf_set_offload->session_id, apf_set_offload->version,
299 apf_set_offload->filter_id, apf_set_offload->total_length,
300 apf_set_offload->current_length,
301 apf_set_offload->current_offset);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530302
Jeff Johnsonc6162232018-06-20 13:33:07 -0700303 status = sme_set_apf_instructions(hdd_ctx->mac_handle, apf_set_offload);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530304 if (!QDF_IS_STATUS_SUCCESS(status)) {
Nachiket Kukadee547a482018-05-22 16:43:30 +0530305 hdd_err("sme_set_apf_instructions failed(err=%d)", status);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530306 ret = -EINVAL;
307 goto fail;
308 }
309 hdd_exit();
310
311fail:
Nachiket Kukadee547a482018-05-22 16:43:30 +0530312 if (apf_set_offload->current_length)
313 qdf_mem_free(apf_set_offload->program);
314 qdf_mem_free(apf_set_offload);
Rajeev Kumar Sirasanagandla85f8b022018-03-12 12:52:59 +0530315
316 if (!ret)
317 hdd_ctx->apf_enabled_v2 = true;
318
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530319 return ret;
320}
321
322/**
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530323 * hdd_enable_disable_apf - Enable or Disable the APF interpreter
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530324 * @adapter: HDD Adapter
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530325 * @apf_enable: true: Enable APF Int., false: disable APF Int.
326 *
327 * Return: 0 on success, errno on failure
328 */
329static int
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530330hdd_enable_disable_apf(struct hdd_adapter *adapter, bool apf_enable)
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530331{
332 QDF_STATUS status;
333
334 hdd_enter();
335
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530336 status = sme_set_apf_enable_disable(hdd_adapter_get_mac_handle(adapter),
337 adapter->session_id, apf_enable);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530338 if (!QDF_IS_STATUS_SUCCESS(status)) {
339 hdd_err("Unable to post sme apf enable/disable message (status-%d)",
340 status);
341 return -EINVAL;
342 }
343
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530344 adapter->apf_context.apf_enabled = apf_enable;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530345
346 hdd_exit();
347 return 0;
348}
349
350/**
351 * hdd_apf_write_memory - Write into the apf work memory
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530352 * @adapter: HDD Adapter
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530353 * @tb: list of attributes
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530354 *
355 * This function writes code/data into the APF work memory and
356 * provides program length that is passed on to the interpreter.
357 *
358 * Return: 0 on success, errno on failure
359 */
360static int
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530361hdd_apf_write_memory(struct hdd_adapter *adapter, struct nlattr **tb)
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530362{
363 struct wmi_apf_write_memory_params write_mem_params = {0};
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530364 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530365 QDF_STATUS status;
366 int ret = 0;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530367
368 hdd_enter();
369
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530370
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530371 write_mem_params.vdev_id = adapter->session_id;
372 if (adapter->apf_context.apf_enabled) {
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530373 hdd_err("Cannot get/set when APF interpreter is enabled");
374 return -EINVAL;
375 }
376
377 /* Read program length */
378 if (!tb[APF_PROG_LEN]) {
379 hdd_err("attr program length failed");
380 return -EINVAL;
381 }
382 write_mem_params.program_len = nla_get_u32(tb[APF_PROG_LEN]);
383
384 /* Read APF work memory offset */
385 if (!tb[APF_CURRENT_OFFSET]) {
386 hdd_err("attr apf packet size failed");
387 return -EINVAL;
388 }
389 write_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
390
391 /* Parse and fetch apf program */
392 if (!tb[APF_PROGRAM]) {
393 hdd_err("attr apf program failed");
394 return -EINVAL;
395 }
396
397 write_mem_params.length = nla_len(tb[APF_PROGRAM]);
398 if (!write_mem_params.length) {
399 hdd_err("Program attr with empty data");
400 return -EINVAL;
401 }
402
403 write_mem_params.buf = qdf_mem_malloc(sizeof(uint8_t)
404 * write_mem_params.length);
405 if (write_mem_params.buf == NULL) {
406 hdd_err("failed to alloc mem for apf write mem operation");
407 return -EINVAL;
408 }
409 nla_memcpy(write_mem_params.buf, tb[APF_PROGRAM],
410 write_mem_params.length);
411
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530412 write_mem_params.apf_version = hdd_ctx->apf_version;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530413
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530414 status = sme_apf_write_work_memory(hdd_adapter_get_mac_handle(adapter),
Jeff Johnsonc6162232018-06-20 13:33:07 -0700415 &write_mem_params);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530416 if (!QDF_IS_STATUS_SUCCESS(status)) {
417 hdd_err("Unable to retrieve APF caps");
418 ret = -EINVAL;
419 }
420
421 if (write_mem_params.buf)
422 qdf_mem_free(write_mem_params.buf);
423
424 hdd_exit();
425 return ret;
426}
427
428/**
429 * hdd_apf_read_memory_callback - HDD Callback for the APF read memory
430 * operation
431 * @context: Hdd context
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530432 * @evt: APF read memory event response parameters
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530433 *
434 * Return: 0 on success, errno on failure
435 */
436static void
437hdd_apf_read_memory_callback(void *hdd_context,
438 struct wmi_apf_read_memory_resp_event_params *evt)
439{
440 struct hdd_context *hdd_ctx = hdd_context;
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530441 struct hdd_adapter *adapter;
442 struct hdd_apf_context *context;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530443 uint8_t *buf_ptr;
444 uint32_t pkt_offset;
445
446 hdd_enter();
447
448 if (wlan_hdd_validate_context(hdd_ctx) || !evt) {
449 hdd_err("HDD context is invalid or event buf(%pK) is null",
450 evt);
451 return;
452 }
453
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530454 adapter = hdd_get_adapter_by_vdev(hdd_ctx, evt->vdev_id);
455 context = &adapter->apf_context;
456
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530457 if (context->magic != APF_CONTEXT_MAGIC) {
458 /* The caller presumably timed out, nothing to do */
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530459 hdd_err("Caller timed out or corrupt magic, simply return");
460 return;
461 }
462
463 if (evt->offset < context->offset) {
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530464 hdd_err("Offset in read event(%d) smaller than offset in request(%d)!",
465 evt->offset, context->offset);
466 return;
467 }
468
469 /*
470 * offset in the event is relative to the APF work memory.
471 * Calculate the packet offset, which gives us the relative
472 * location in the buffer to start copy into.
473 */
474 pkt_offset = evt->offset - context->offset;
475
476 if (context->buf_len < pkt_offset + evt->length) {
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530477 hdd_err("Read chunk exceeding allocated space");
478 return;
479 }
480 buf_ptr = context->buf + pkt_offset;
481
482 qdf_mem_copy(buf_ptr, evt->data, evt->length);
483
484 if (!evt->more_data) {
485 /* Release the caller after last event, clear magic */
486 context->magic = 0;
487 qdf_event_set(&context->qdf_apf_event);
488 }
489
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530490 hdd_exit();
491}
492
493/**
494 * hdd_apf_read_memory - Read part of the apf work memory
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530495 * @adapter: HDD Adapter
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530496 * @tb: list of attributes
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530497 *
498 * Return: 0 on success, errno on failure
499 */
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530500static int hdd_apf_read_memory(struct hdd_adapter *adapter, struct nlattr **tb)
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530501{
502 struct wmi_apf_read_memory_params read_mem_params = {0};
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530503 struct hdd_apf_context *context = &adapter->apf_context;
504 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530505 QDF_STATUS status;
506 unsigned long nl_buf_len = NLMSG_HDRLEN;
507 int ret = 0;
508 struct sk_buff *skb = NULL;
509 uint8_t *bufptr;
510
511 hdd_enter();
512
Rajeev Kumar75fc36e2018-09-04 11:41:10 -0700513 if (context->apf_enabled) {
514 hdd_err("Cannot get/set while interpreter is enabled");
515 return -EINVAL;
516 }
517
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530518 read_mem_params.vdev_id = adapter->session_id;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530519
520 /* Read APF work memory offset */
521 if (!tb[APF_CURRENT_OFFSET]) {
522 hdd_err("attr apf memory offset failed");
523 return -EINVAL;
524 }
525 read_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
526
527 /* Read length */
528 if (!tb[APF_PACKET_SIZE]) {
529 hdd_err("attr apf packet size failed");
530 return -EINVAL;
531 }
532 read_mem_params.length = nla_get_u32(tb[APF_PACKET_SIZE]);
533 if (!read_mem_params.length) {
534 hdd_err("apf read length cannot be zero!");
535 return -EINVAL;
536 }
537 bufptr = qdf_mem_malloc(read_mem_params.length);
538 if (bufptr == NULL) {
539 hdd_err("alloc failed for cumulative event buffer");
540 return -ENOMEM;
541 }
542
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530543 qdf_event_reset(&context->qdf_apf_event);
544 context->offset = read_mem_params.addr_offset;
545
546 context->buf = bufptr;
547 context->buf_len = read_mem_params.length;
548 context->magic = APF_CONTEXT_MAGIC;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530549
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530550 status = sme_apf_read_work_memory(hdd_adapter_get_mac_handle(adapter),
Jeff Johnsonc6162232018-06-20 13:33:07 -0700551 &read_mem_params,
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530552 hdd_apf_read_memory_callback);
553 if (QDF_IS_STATUS_ERROR(status)) {
554 hdd_err("Unable to post sme APF read memory message (status-%d)",
555 status);
556 ret = -EINVAL;
557 goto fail;
558 }
559
560 /* request was sent -- wait for the response */
561 status = qdf_wait_for_event_completion(&context->qdf_apf_event,
562 WLAN_WAIT_TIME_APF_READ_MEM);
563 if (QDF_IS_STATUS_ERROR(status)) {
564 hdd_err("Target response timed out");
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530565 context->magic = 0;
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530566 ret = -ETIMEDOUT;
567 goto fail;
568 }
569
570 nl_buf_len += sizeof(uint32_t) + NLA_HDRLEN;
571 nl_buf_len += context->buf_len + NLA_HDRLEN;
572
573 skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
574 if (!skb) {
575 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
576 ret = -ENOMEM;
577 goto fail;
578 }
579
580 if (nla_put_u32(skb, APF_SUBCMD, QCA_WLAN_READ_PACKET_FILTER) ||
581 nla_put(skb, APF_PROGRAM, read_mem_params.length, context->buf)) {
582 hdd_err("put fail");
583 kfree_skb(skb);
584 ret = -EINVAL;
585 goto fail;
586 }
587
588 cfg80211_vendor_cmd_reply(skb);
589fail:
590 if (context->buf) {
591 qdf_mem_free(context->buf);
592 context->buf = NULL;
593 }
594
595 hdd_exit();
596 return ret;
597}
598
599/**
Nachiket Kukadee547a482018-05-22 16:43:30 +0530600 * wlan_hdd_cfg80211_apf_offload() - Set/Reset to APF Offload
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530601 * @wiphy: wiphy structure pointer
602 * @wdev: Wireless device structure pointer
603 * @data: Pointer to the data received
604 * @data_len: Length of @data
605 *
606 * Return: 0 on success; errno on failure
607 */
608static int
Nachiket Kukadee547a482018-05-22 16:43:30 +0530609__wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy,
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530610 struct wireless_dev *wdev,
611 const void *data, int data_len)
612{
613 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
614 struct net_device *dev = wdev->netdev;
615 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
Nachiket Kukadee547a482018-05-22 16:43:30 +0530616 struct nlattr *tb[APF_MAX + 1];
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530617 int ret_val = 0, apf_subcmd;
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530618 struct hdd_apf_context *context;
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530619
620 hdd_enter();
621
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530622 if (!adapter) {
623 hdd_err("Adapter is null");
624 return -EINVAL;
625 }
626
627 context = &adapter->apf_context;
628
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530629 ret_val = wlan_hdd_validate_context(hdd_ctx);
630 if (ret_val)
631 return ret_val;
632
633 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
634 hdd_err("Command not allowed in FTM mode");
635 return -EINVAL;
636 }
637
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530638 if (!hdd_ctx->apf_supported) {
639 hdd_err("APF is not supported or disabled through INI");
640 return -ENOTSUPP;
641 }
642
643 if (!(adapter->device_mode == QDF_STA_MODE ||
644 adapter->device_mode == QDF_P2P_CLIENT_MODE)) {
645 hdd_err("APF only supported in STA or P2P CLI modes!");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530646 return -ENOTSUPP;
647 }
648
Nachiket Kukadee547a482018-05-22 16:43:30 +0530649 if (wlan_cfg80211_nla_parse(tb, APF_MAX, data, data_len,
650 wlan_hdd_apf_offload_policy)) {
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530651 hdd_err("Invalid ATTR");
652 return -EINVAL;
653 }
654
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530655 if (!tb[APF_SUBCMD]) {
656 hdd_err("attr apf sub-command failed");
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530657 return -EINVAL;
658 }
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530659 apf_subcmd = nla_get_u32(tb[APF_SUBCMD]);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530660
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530661 /* Do not allow simultaneous new APF commands on the same adapter */
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530662 qdf_spin_lock(&context->lock);
663 if (context->cmd_in_progress) {
664 qdf_spin_unlock(&context->lock);
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530665 hdd_err("Another cmd in progress for same session!");
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530666 return -EAGAIN;
667 }
668 context->cmd_in_progress = true;
669 qdf_spin_unlock(&context->lock);
670
671 switch (apf_subcmd) {
672 /* Legacy APF sub-commands */
673 case QCA_WLAN_SET_PACKET_FILTER:
674 ret_val = hdd_set_reset_apf_offload(hdd_ctx, tb,
675 adapter);
676 break;
677 case QCA_WLAN_GET_PACKET_FILTER:
678 ret_val = hdd_get_apf_capabilities(hdd_ctx);
679 break;
680
681 /* APF 3.0 sub-commands */
682 case QCA_WLAN_WRITE_PACKET_FILTER:
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530683 ret_val = hdd_apf_write_memory(adapter, tb);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530684 break;
685 case QCA_WLAN_READ_PACKET_FILTER:
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530686 ret_val = hdd_apf_read_memory(adapter, tb);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530687 break;
688 case QCA_WLAN_ENABLE_PACKET_FILTER:
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530689 ret_val = hdd_enable_disable_apf(adapter, true);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530690 break;
691 case QCA_WLAN_DISABLE_PACKET_FILTER:
Nachiket Kukade5f0ce4f2018-06-15 19:47:37 +0530692 ret_val = hdd_enable_disable_apf(adapter, false);
Nachiket Kukade177b5b02018-05-22 20:52:17 +0530693 break;
694 default:
695 hdd_err("Unknown APF Sub-command: %d", apf_subcmd);
696 ret_val = -ENOTSUPP;
697 }
698
699 qdf_spin_lock(&context->lock);
700 context->cmd_in_progress = false;
701 qdf_spin_unlock(&context->lock);
702
703 return ret_val;
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530704}
705
Nachiket Kukadee547a482018-05-22 16:43:30 +0530706int
707wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy, struct wireless_dev *wdev,
708 const void *data, int data_len)
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530709{
710 int ret;
711
712 cds_ssr_protect(__func__);
Nachiket Kukadee547a482018-05-22 16:43:30 +0530713 ret = __wlan_hdd_cfg80211_apf_offload(wiphy, wdev, data, data_len);
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +0530714 cds_ssr_unprotect(__func__);
715
716 return ret;
717}
718