xref: /wlan-driver/qcacld-3.0/core/hdd/src/wlan_hdd_apf.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1*5113495bSYour Name /*
2*5113495bSYour Name  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
3*5113495bSYour Name  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4*5113495bSYour Name  *
5*5113495bSYour Name  * Permission to use, copy, modify, and/or distribute this software for
6*5113495bSYour Name  * any purpose with or without fee is hereby granted, provided that the
7*5113495bSYour Name  * above copyright notice and this permission notice appear in all
8*5113495bSYour Name  * copies.
9*5113495bSYour Name  *
10*5113495bSYour Name  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11*5113495bSYour Name  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12*5113495bSYour Name  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13*5113495bSYour Name  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14*5113495bSYour Name  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15*5113495bSYour Name  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16*5113495bSYour Name  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*5113495bSYour Name  * PERFORMANCE OF THIS SOFTWARE.
18*5113495bSYour Name  */
19*5113495bSYour Name 
20*5113495bSYour Name /**
21*5113495bSYour Name  * DOC: wlan_hdd_apf.c
22*5113495bSYour Name  *
23*5113495bSYour Name  * Android Packet Filter support and implementation
24*5113495bSYour Name  */
25*5113495bSYour Name 
26*5113495bSYour Name #include "wlan_hdd_apf.h"
27*5113495bSYour Name #include "osif_sync.h"
28*5113495bSYour Name #include "qca_vendor.h"
29*5113495bSYour Name #include "wlan_osif_request_manager.h"
30*5113495bSYour Name 
31*5113495bSYour Name /*
32*5113495bSYour Name  * define short names for the global vendor params
33*5113495bSYour Name  * used by __wlan_hdd_cfg80211_apf_offload()
34*5113495bSYour Name  */
35*5113495bSYour Name #define APF_INVALID \
36*5113495bSYour Name 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID
37*5113495bSYour Name #define APF_SUBCMD \
38*5113495bSYour Name 	QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER
39*5113495bSYour Name #define APF_VERSION \
40*5113495bSYour Name 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION
41*5113495bSYour Name #define APF_FILTER_ID \
42*5113495bSYour Name 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID
43*5113495bSYour Name #define APF_PACKET_SIZE \
44*5113495bSYour Name 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE
45*5113495bSYour Name #define APF_CURRENT_OFFSET \
46*5113495bSYour Name 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET
47*5113495bSYour Name #define APF_PROGRAM \
48*5113495bSYour Name 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM
49*5113495bSYour Name #define APF_PROG_LEN \
50*5113495bSYour Name 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH
51*5113495bSYour Name #define APF_MAX \
52*5113495bSYour Name 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX
53*5113495bSYour Name 
54*5113495bSYour Name const struct nla_policy wlan_hdd_apf_offload_policy[APF_MAX + 1] = {
55*5113495bSYour Name 	[APF_SUBCMD] = {.type = NLA_U32},
56*5113495bSYour Name 	[APF_VERSION] = {.type = NLA_U32},
57*5113495bSYour Name 	[APF_FILTER_ID] = {.type = NLA_U32},
58*5113495bSYour Name 	[APF_PACKET_SIZE] = {.type = NLA_U32},
59*5113495bSYour Name 	[APF_CURRENT_OFFSET] = {.type = NLA_U32},
60*5113495bSYour Name 	[APF_PROGRAM] = {.type = NLA_BINARY,
61*5113495bSYour Name 			 .len = MAX_APF_MEMORY_LEN},
62*5113495bSYour Name 	[APF_PROG_LEN] = {.type = NLA_U32},
63*5113495bSYour Name };
64*5113495bSYour Name 
hdd_apf_context_init(struct hdd_adapter * adapter)65*5113495bSYour Name void hdd_apf_context_init(struct hdd_adapter *adapter)
66*5113495bSYour Name {
67*5113495bSYour Name 	qdf_event_create(&adapter->apf_context.qdf_apf_event);
68*5113495bSYour Name 	qdf_spinlock_create(&adapter->apf_context.lock);
69*5113495bSYour Name 	adapter->apf_context.apf_enabled = true;
70*5113495bSYour Name }
71*5113495bSYour Name 
hdd_apf_context_destroy(struct hdd_adapter * adapter)72*5113495bSYour Name void hdd_apf_context_destroy(struct hdd_adapter *adapter)
73*5113495bSYour Name {
74*5113495bSYour Name 	qdf_event_destroy(&adapter->apf_context.qdf_apf_event);
75*5113495bSYour Name 	qdf_spinlock_destroy(&adapter->apf_context.lock);
76*5113495bSYour Name 	qdf_mem_zero(&adapter->apf_context,
77*5113495bSYour Name 		     sizeof(struct hdd_apf_context));
78*5113495bSYour Name }
79*5113495bSYour Name 
80*5113495bSYour Name struct apf_offload_priv {
81*5113495bSYour Name 	struct sir_apf_get_offload apf_get_offload;
82*5113495bSYour Name };
83*5113495bSYour Name 
hdd_get_apf_capabilities_cb(void * context,struct sir_apf_get_offload * data)84*5113495bSYour Name void hdd_get_apf_capabilities_cb(void *context,
85*5113495bSYour Name 				 struct sir_apf_get_offload *data)
86*5113495bSYour Name {
87*5113495bSYour Name 	struct osif_request *request;
88*5113495bSYour Name 	struct apf_offload_priv *priv;
89*5113495bSYour Name 
90*5113495bSYour Name 	hdd_enter();
91*5113495bSYour Name 
92*5113495bSYour Name 	request = osif_request_get(context);
93*5113495bSYour Name 	if (!request) {
94*5113495bSYour Name 		hdd_err("Obsolete request");
95*5113495bSYour Name 		return;
96*5113495bSYour Name 	}
97*5113495bSYour Name 
98*5113495bSYour Name 	priv = osif_request_priv(request);
99*5113495bSYour Name 	priv->apf_get_offload = *data;
100*5113495bSYour Name 	osif_request_complete(request);
101*5113495bSYour Name 	osif_request_put(request);
102*5113495bSYour Name 
103*5113495bSYour Name 	hdd_exit();
104*5113495bSYour Name }
105*5113495bSYour Name 
106*5113495bSYour Name /**
107*5113495bSYour Name  * hdd_post_get_apf_capabilities_rsp() - Callback function to APF Offload
108*5113495bSYour Name  * @hdd_ctx: hdd_context
109*5113495bSYour Name  * @apf_get_offload: struct for get offload
110*5113495bSYour Name  *
111*5113495bSYour Name  * Return: 0 on success, error number otherwise.
112*5113495bSYour Name  */
113*5113495bSYour Name static int
hdd_post_get_apf_capabilities_rsp(struct hdd_context * hdd_ctx,struct sir_apf_get_offload * apf_get_offload)114*5113495bSYour Name hdd_post_get_apf_capabilities_rsp(struct hdd_context *hdd_ctx,
115*5113495bSYour Name 				  struct sir_apf_get_offload *apf_get_offload)
116*5113495bSYour Name {
117*5113495bSYour Name 	struct sk_buff *skb;
118*5113495bSYour Name 	uint32_t nl_buf_len;
119*5113495bSYour Name 
120*5113495bSYour Name 	hdd_enter();
121*5113495bSYour Name 
122*5113495bSYour Name 	nl_buf_len = NLMSG_HDRLEN;
123*5113495bSYour Name 	nl_buf_len +=
124*5113495bSYour Name 		(sizeof(apf_get_offload->max_bytes_for_apf_inst) + NLA_HDRLEN) +
125*5113495bSYour Name 		(sizeof(apf_get_offload->apf_version) + NLA_HDRLEN);
126*5113495bSYour Name 	skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
127*5113495bSYour Name 						       nl_buf_len);
128*5113495bSYour Name 	if (!skb) {
129*5113495bSYour Name 		hdd_err("wlan_cfg80211_vendor_cmd_alloc_reply_skb failed");
130*5113495bSYour Name 		return -ENOMEM;
131*5113495bSYour Name 	}
132*5113495bSYour Name 
133*5113495bSYour Name 	hdd_ctx->apf_version = apf_get_offload->apf_version;
134*5113495bSYour Name 	hdd_debug("APF Version: %u APF max bytes: %u",
135*5113495bSYour Name 		  apf_get_offload->apf_version,
136*5113495bSYour Name 		  apf_get_offload->max_bytes_for_apf_inst);
137*5113495bSYour Name 
138*5113495bSYour Name 	if (nla_put_u32(skb, APF_PACKET_SIZE,
139*5113495bSYour Name 			apf_get_offload->max_bytes_for_apf_inst) ||
140*5113495bSYour Name 	    nla_put_u32(skb, APF_VERSION, apf_get_offload->apf_version)) {
141*5113495bSYour Name 		hdd_err("nla put failure");
142*5113495bSYour Name 		goto nla_put_failure;
143*5113495bSYour Name 	}
144*5113495bSYour Name 
145*5113495bSYour Name 	wlan_cfg80211_vendor_cmd_reply(skb);
146*5113495bSYour Name 	hdd_exit();
147*5113495bSYour Name 	return 0;
148*5113495bSYour Name 
149*5113495bSYour Name nla_put_failure:
150*5113495bSYour Name 	wlan_cfg80211_vendor_free_skb(skb);
151*5113495bSYour Name 	return -EINVAL;
152*5113495bSYour Name }
153*5113495bSYour Name 
154*5113495bSYour Name /**
155*5113495bSYour Name  * hdd_get_apf_capabilities - Get APF offload Capabilities
156*5113495bSYour Name  * @hdd_ctx: Hdd context
157*5113495bSYour Name  *
158*5113495bSYour Name  * Return: 0 on success, errno on failure
159*5113495bSYour Name  */
hdd_get_apf_capabilities(struct hdd_context * hdd_ctx)160*5113495bSYour Name static int hdd_get_apf_capabilities(struct hdd_context *hdd_ctx)
161*5113495bSYour Name {
162*5113495bSYour Name 	QDF_STATUS status;
163*5113495bSYour Name 	int ret;
164*5113495bSYour Name 	void *cookie;
165*5113495bSYour Name 	struct osif_request *request;
166*5113495bSYour Name 	struct apf_offload_priv *priv;
167*5113495bSYour Name 	static const struct osif_request_params params = {
168*5113495bSYour Name 		.priv_size = sizeof(*priv),
169*5113495bSYour Name 		.timeout_ms = WLAN_WAIT_TIME_APF,
170*5113495bSYour Name 	};
171*5113495bSYour Name 
172*5113495bSYour Name 	hdd_enter();
173*5113495bSYour Name 
174*5113495bSYour Name 	request = osif_request_alloc(&params);
175*5113495bSYour Name 	if (!request) {
176*5113495bSYour Name 		hdd_err("Unable to allocate request");
177*5113495bSYour Name 		return -EINVAL;
178*5113495bSYour Name 	}
179*5113495bSYour Name 	cookie = osif_request_cookie(request);
180*5113495bSYour Name 
181*5113495bSYour Name 	status = sme_get_apf_capabilities(hdd_ctx->mac_handle,
182*5113495bSYour Name 					  hdd_get_apf_capabilities_cb,
183*5113495bSYour Name 					  cookie);
184*5113495bSYour Name 	if (!QDF_IS_STATUS_SUCCESS(status)) {
185*5113495bSYour Name 		hdd_err("Unable to retrieve APF caps");
186*5113495bSYour Name 		ret = qdf_status_to_os_return(status);
187*5113495bSYour Name 		goto cleanup;
188*5113495bSYour Name 	}
189*5113495bSYour Name 	ret = osif_request_wait_for_response(request);
190*5113495bSYour Name 	if (ret) {
191*5113495bSYour Name 		hdd_err("Target response timed out");
192*5113495bSYour Name 		goto cleanup;
193*5113495bSYour Name 	}
194*5113495bSYour Name 	priv = osif_request_priv(request);
195*5113495bSYour Name 	ret = hdd_post_get_apf_capabilities_rsp(hdd_ctx,
196*5113495bSYour Name 						&priv->apf_get_offload);
197*5113495bSYour Name 	if (ret)
198*5113495bSYour Name 		hdd_err("Failed to post get apf capabilities");
199*5113495bSYour Name 
200*5113495bSYour Name cleanup:
201*5113495bSYour Name 	/*
202*5113495bSYour Name 	 * either we never sent a request to SME, we sent a request to
203*5113495bSYour Name 	 * SME and timed out, or we sent a request to SME, received a
204*5113495bSYour Name 	 * response from SME, and posted the response to userspace.
205*5113495bSYour Name 	 * regardless we are done with the request.
206*5113495bSYour Name 	 */
207*5113495bSYour Name 	osif_request_put(request);
208*5113495bSYour Name 	hdd_exit();
209*5113495bSYour Name 
210*5113495bSYour Name 	return ret;
211*5113495bSYour Name }
212*5113495bSYour Name 
213*5113495bSYour Name /**
214*5113495bSYour Name  * hdd_set_reset_apf_offload - Post set/reset apf to SME
215*5113495bSYour Name  * @hdd_ctx: Hdd context
216*5113495bSYour Name  * @tb: Length of @data
217*5113495bSYour Name  * @adapter: pointer to adapter struct
218*5113495bSYour Name  *
219*5113495bSYour Name  * Return: 0 on success; errno on failure
220*5113495bSYour Name  */
hdd_set_reset_apf_offload(struct hdd_context * hdd_ctx,struct nlattr ** tb,struct hdd_adapter * adapter)221*5113495bSYour Name static int hdd_set_reset_apf_offload(struct hdd_context *hdd_ctx,
222*5113495bSYour Name 				     struct nlattr **tb,
223*5113495bSYour Name 				     struct hdd_adapter *adapter)
224*5113495bSYour Name {
225*5113495bSYour Name 	struct sir_apf_set_offload apf_set_offload = {0};
226*5113495bSYour Name 	QDF_STATUS status;
227*5113495bSYour Name 	int prog_len;
228*5113495bSYour Name 	int ret = 0;
229*5113495bSYour Name 
230*5113495bSYour Name 	if (!hdd_cm_is_vdev_associated(adapter->deflink)) {
231*5113495bSYour Name 		hdd_err("Not in Connected state!");
232*5113495bSYour Name 		return -ENOTSUPP;
233*5113495bSYour Name 	}
234*5113495bSYour Name 
235*5113495bSYour Name 	/* Parse and fetch apf packet size */
236*5113495bSYour Name 	if (!tb[APF_PACKET_SIZE]) {
237*5113495bSYour Name 		hdd_err("attr apf packet size failed");
238*5113495bSYour Name 		ret = -EINVAL;
239*5113495bSYour Name 		goto fail;
240*5113495bSYour Name 	}
241*5113495bSYour Name 
242*5113495bSYour Name 	apf_set_offload.session_id = adapter->deflink->vdev_id;
243*5113495bSYour Name 	apf_set_offload.total_length = nla_get_u32(tb[APF_PACKET_SIZE]);
244*5113495bSYour Name 
245*5113495bSYour Name 	if (!apf_set_offload.total_length) {
246*5113495bSYour Name 		hdd_debug("APF reset packet");
247*5113495bSYour Name 		goto post_sme;
248*5113495bSYour Name 	}
249*5113495bSYour Name 
250*5113495bSYour Name 	/* Parse and fetch apf program */
251*5113495bSYour Name 	if (!tb[APF_PROGRAM]) {
252*5113495bSYour Name 		hdd_err("attr apf program failed");
253*5113495bSYour Name 		ret = -EINVAL;
254*5113495bSYour Name 		goto fail;
255*5113495bSYour Name 	}
256*5113495bSYour Name 
257*5113495bSYour Name 	prog_len = nla_len(tb[APF_PROGRAM]);
258*5113495bSYour Name 	apf_set_offload.program = qdf_mem_malloc(sizeof(uint8_t) * prog_len);
259*5113495bSYour Name 
260*5113495bSYour Name 	if (!apf_set_offload.program) {
261*5113495bSYour Name 		ret = -ENOMEM;
262*5113495bSYour Name 		goto fail;
263*5113495bSYour Name 	}
264*5113495bSYour Name 
265*5113495bSYour Name 	apf_set_offload.current_length = prog_len;
266*5113495bSYour Name 	nla_memcpy(apf_set_offload.program, tb[APF_PROGRAM], prog_len);
267*5113495bSYour Name 
268*5113495bSYour Name 	/* Parse and fetch filter Id */
269*5113495bSYour Name 	if (!tb[APF_FILTER_ID]) {
270*5113495bSYour Name 		hdd_err("attr filter id failed");
271*5113495bSYour Name 		ret = -EINVAL;
272*5113495bSYour Name 		goto fail;
273*5113495bSYour Name 	}
274*5113495bSYour Name 	apf_set_offload.filter_id = nla_get_u32(tb[APF_FILTER_ID]);
275*5113495bSYour Name 
276*5113495bSYour Name 	/* Parse and fetch current offset */
277*5113495bSYour Name 	if (!tb[APF_CURRENT_OFFSET]) {
278*5113495bSYour Name 		hdd_err("attr current offset failed");
279*5113495bSYour Name 		ret = -EINVAL;
280*5113495bSYour Name 		goto fail;
281*5113495bSYour Name 	}
282*5113495bSYour Name 	apf_set_offload.current_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
283*5113495bSYour Name 
284*5113495bSYour Name post_sme:
285*5113495bSYour Name 	hdd_debug("Posting, session_id: %d APF Version: %d filter ID: %d total_len: %d current_len: %d offset: %d",
286*5113495bSYour Name 		  apf_set_offload.session_id, apf_set_offload.version,
287*5113495bSYour Name 		  apf_set_offload.filter_id, apf_set_offload.total_length,
288*5113495bSYour Name 		  apf_set_offload.current_length,
289*5113495bSYour Name 		  apf_set_offload.current_offset);
290*5113495bSYour Name 
291*5113495bSYour Name 	status = sme_set_apf_instructions(hdd_ctx->mac_handle,
292*5113495bSYour Name 					  &apf_set_offload);
293*5113495bSYour Name 	if (!QDF_IS_STATUS_SUCCESS(status)) {
294*5113495bSYour Name 		hdd_err("sme_set_apf_instructions failed(err=%d)", status);
295*5113495bSYour Name 		ret = -EINVAL;
296*5113495bSYour Name 		goto fail;
297*5113495bSYour Name 	}
298*5113495bSYour Name 	hdd_exit();
299*5113495bSYour Name 
300*5113495bSYour Name fail:
301*5113495bSYour Name 	if (apf_set_offload.current_length)
302*5113495bSYour Name 		qdf_mem_free(apf_set_offload.program);
303*5113495bSYour Name 
304*5113495bSYour Name 	if (!ret)
305*5113495bSYour Name 		hdd_ctx->apf_enabled_v2 = true;
306*5113495bSYour Name 
307*5113495bSYour Name 	return ret;
308*5113495bSYour Name }
309*5113495bSYour Name 
310*5113495bSYour Name /**
311*5113495bSYour Name  * hdd_enable_disable_apf - Enable or Disable the APF interpreter
312*5113495bSYour Name  * @adapter: HDD Adapter
313*5113495bSYour Name  * @apf_enable: true: Enable APF Int., false: disable APF Int.
314*5113495bSYour Name  *
315*5113495bSYour Name  * Return: 0 on success, errno on failure
316*5113495bSYour Name  */
317*5113495bSYour Name static int
hdd_enable_disable_apf(struct hdd_adapter * adapter,bool apf_enable)318*5113495bSYour Name hdd_enable_disable_apf(struct hdd_adapter *adapter, bool apf_enable)
319*5113495bSYour Name {
320*5113495bSYour Name 	QDF_STATUS status;
321*5113495bSYour Name 
322*5113495bSYour Name 	status = sme_set_apf_enable_disable(hdd_adapter_get_mac_handle(adapter),
323*5113495bSYour Name 					    adapter->deflink->vdev_id,
324*5113495bSYour Name 					    apf_enable);
325*5113495bSYour Name 	if (!QDF_IS_STATUS_SUCCESS(status)) {
326*5113495bSYour Name 		hdd_err("Unable to post sme apf enable/disable message (status-%d)",
327*5113495bSYour Name 				status);
328*5113495bSYour Name 		return -EINVAL;
329*5113495bSYour Name 	}
330*5113495bSYour Name 
331*5113495bSYour Name 	adapter->apf_context.apf_enabled = apf_enable;
332*5113495bSYour Name 
333*5113495bSYour Name 	return 0;
334*5113495bSYour Name }
335*5113495bSYour Name 
336*5113495bSYour Name /**
337*5113495bSYour Name  * hdd_apf_write_memory - Write into the apf work memory
338*5113495bSYour Name  * @adapter: HDD Adapter
339*5113495bSYour Name  * @tb: list of attributes
340*5113495bSYour Name  *
341*5113495bSYour Name  * This function writes code/data into the APF work memory and
342*5113495bSYour Name  * provides program length that is passed on to the interpreter.
343*5113495bSYour Name  *
344*5113495bSYour Name  * Return: 0 on success, errno on failure
345*5113495bSYour Name  */
346*5113495bSYour Name static int
hdd_apf_write_memory(struct hdd_adapter * adapter,struct nlattr ** tb)347*5113495bSYour Name hdd_apf_write_memory(struct hdd_adapter *adapter, struct nlattr **tb)
348*5113495bSYour Name {
349*5113495bSYour Name 	struct wmi_apf_write_memory_params write_mem_params = {0};
350*5113495bSYour Name 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
351*5113495bSYour Name 	QDF_STATUS status;
352*5113495bSYour Name 	int ret = 0;
353*5113495bSYour Name 
354*5113495bSYour Name 	write_mem_params.vdev_id = adapter->deflink->vdev_id;
355*5113495bSYour Name 	if (adapter->apf_context.apf_enabled) {
356*5113495bSYour Name 		hdd_err("Cannot get/set when APF interpreter is enabled");
357*5113495bSYour Name 		return -EINVAL;
358*5113495bSYour Name 	}
359*5113495bSYour Name 
360*5113495bSYour Name 	/* Read program length */
361*5113495bSYour Name 	if (!tb[APF_PROG_LEN]) {
362*5113495bSYour Name 		hdd_err("attr program length failed");
363*5113495bSYour Name 		return -EINVAL;
364*5113495bSYour Name 	}
365*5113495bSYour Name 	write_mem_params.program_len = nla_get_u32(tb[APF_PROG_LEN]);
366*5113495bSYour Name 
367*5113495bSYour Name 	/* Read APF work memory offset */
368*5113495bSYour Name 	if (!tb[APF_CURRENT_OFFSET]) {
369*5113495bSYour Name 		hdd_err("attr apf packet size failed");
370*5113495bSYour Name 		return -EINVAL;
371*5113495bSYour Name 	}
372*5113495bSYour Name 	write_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
373*5113495bSYour Name 
374*5113495bSYour Name 	/* Parse and fetch apf program */
375*5113495bSYour Name 	if (!tb[APF_PROGRAM]) {
376*5113495bSYour Name 		hdd_err("attr apf program failed");
377*5113495bSYour Name 		return -EINVAL;
378*5113495bSYour Name 	}
379*5113495bSYour Name 
380*5113495bSYour Name 	write_mem_params.length = nla_len(tb[APF_PROGRAM]);
381*5113495bSYour Name 	if (!write_mem_params.length) {
382*5113495bSYour Name 		hdd_err("Program attr with empty data");
383*5113495bSYour Name 		return -EINVAL;
384*5113495bSYour Name 	}
385*5113495bSYour Name 
386*5113495bSYour Name 	write_mem_params.buf = qdf_mem_malloc(sizeof(uint8_t)
387*5113495bSYour Name 						* write_mem_params.length);
388*5113495bSYour Name 	if (!write_mem_params.buf)
389*5113495bSYour Name 		return -EINVAL;
390*5113495bSYour Name 	nla_memcpy(write_mem_params.buf, tb[APF_PROGRAM],
391*5113495bSYour Name 		   write_mem_params.length);
392*5113495bSYour Name 
393*5113495bSYour Name 	write_mem_params.apf_version = hdd_ctx->apf_version;
394*5113495bSYour Name 
395*5113495bSYour Name 	status = sme_apf_write_work_memory(hdd_adapter_get_mac_handle(adapter),
396*5113495bSYour Name 					   &write_mem_params);
397*5113495bSYour Name 	if (!QDF_IS_STATUS_SUCCESS(status)) {
398*5113495bSYour Name 		hdd_err("Unable to retrieve APF caps");
399*5113495bSYour Name 		ret = -EINVAL;
400*5113495bSYour Name 	}
401*5113495bSYour Name 
402*5113495bSYour Name 	hdd_debug("Writing successful into APF work memory from offset 0x%X:",
403*5113495bSYour Name 		  write_mem_params.addr_offset);
404*5113495bSYour Name 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
405*5113495bSYour Name 			   write_mem_params.buf, write_mem_params.length);
406*5113495bSYour Name 
407*5113495bSYour Name 	if (write_mem_params.buf)
408*5113495bSYour Name 		qdf_mem_free(write_mem_params.buf);
409*5113495bSYour Name 
410*5113495bSYour Name 	return ret;
411*5113495bSYour Name }
412*5113495bSYour Name 
413*5113495bSYour Name /**
414*5113495bSYour Name  * hdd_apf_read_memory_callback - HDD Callback for the APF read memory
415*5113495bSYour Name  *	operation
416*5113495bSYour Name  * @hdd_context: Hdd context
417*5113495bSYour Name  * @evt: APF read memory event response parameters
418*5113495bSYour Name  *
419*5113495bSYour Name  * Return: 0 on success, errno on failure
420*5113495bSYour Name  */
421*5113495bSYour Name static void
hdd_apf_read_memory_callback(void * hdd_context,struct wmi_apf_read_memory_resp_event_params * evt)422*5113495bSYour Name hdd_apf_read_memory_callback(void *hdd_context,
423*5113495bSYour Name 			     struct wmi_apf_read_memory_resp_event_params *evt)
424*5113495bSYour Name {
425*5113495bSYour Name 	struct hdd_context *hdd_ctx = hdd_context;
426*5113495bSYour Name 	struct hdd_apf_context *context;
427*5113495bSYour Name 	uint8_t *buf_ptr;
428*5113495bSYour Name 	uint32_t pkt_offset;
429*5113495bSYour Name 	struct wlan_hdd_link_info *link_info;
430*5113495bSYour Name 
431*5113495bSYour Name 	hdd_enter();
432*5113495bSYour Name 
433*5113495bSYour Name 	if (wlan_hdd_validate_context(hdd_ctx) || !evt) {
434*5113495bSYour Name 		hdd_err("HDD context is invalid or event buf(%pK) is null",
435*5113495bSYour Name 			evt);
436*5113495bSYour Name 		return;
437*5113495bSYour Name 	}
438*5113495bSYour Name 
439*5113495bSYour Name 	link_info = hdd_get_link_info_by_vdev(hdd_ctx, evt->vdev_id);
440*5113495bSYour Name 	if (!link_info || hdd_validate_adapter(link_info->adapter))
441*5113495bSYour Name 		return;
442*5113495bSYour Name 
443*5113495bSYour Name 	context = &link_info->adapter->apf_context;
444*5113495bSYour Name 
445*5113495bSYour Name 	if (context->magic != APF_CONTEXT_MAGIC) {
446*5113495bSYour Name 		/* The caller presumably timed out, nothing to do */
447*5113495bSYour Name 		hdd_err("Caller timed out or corrupt magic, simply return");
448*5113495bSYour Name 		return;
449*5113495bSYour Name 	}
450*5113495bSYour Name 
451*5113495bSYour Name 	if (evt->offset <  context->offset) {
452*5113495bSYour Name 		hdd_err("Offset in read event(%d) smaller than offset in request(%d)!",
453*5113495bSYour Name 					evt->offset, context->offset);
454*5113495bSYour Name 		return;
455*5113495bSYour Name 	}
456*5113495bSYour Name 
457*5113495bSYour Name 	/*
458*5113495bSYour Name 	 * offset in the event is relative to the APF work memory.
459*5113495bSYour Name 	 * Calculate the packet offset, which gives us the relative
460*5113495bSYour Name 	 * location in the buffer to start copy into.
461*5113495bSYour Name 	 */
462*5113495bSYour Name 	pkt_offset = evt->offset - context->offset;
463*5113495bSYour Name 
464*5113495bSYour Name 	if ((pkt_offset > context->buf_len) ||
465*5113495bSYour Name 	    (context->buf_len - pkt_offset < evt->length)) {
466*5113495bSYour Name 		hdd_err("Read chunk exceeding allocated space");
467*5113495bSYour Name 		return;
468*5113495bSYour Name 	}
469*5113495bSYour Name 	buf_ptr = context->buf + pkt_offset;
470*5113495bSYour Name 
471*5113495bSYour Name 	qdf_mem_copy(buf_ptr, evt->data, evt->length);
472*5113495bSYour Name 
473*5113495bSYour Name 	if (!evt->more_data) {
474*5113495bSYour Name 		/* Release the caller after last event, clear magic */
475*5113495bSYour Name 		context->magic = 0;
476*5113495bSYour Name 		qdf_event_set(&context->qdf_apf_event);
477*5113495bSYour Name 	}
478*5113495bSYour Name 
479*5113495bSYour Name 	hdd_exit();
480*5113495bSYour Name }
481*5113495bSYour Name 
482*5113495bSYour Name /**
483*5113495bSYour Name  * hdd_apf_read_memory - Read part of the apf work memory
484*5113495bSYour Name  * @adapter: HDD Adapter
485*5113495bSYour Name  * @tb: list of attributes
486*5113495bSYour Name  *
487*5113495bSYour Name  * Return: 0 on success, errno on failure
488*5113495bSYour Name  */
hdd_apf_read_memory(struct hdd_adapter * adapter,struct nlattr ** tb)489*5113495bSYour Name static int hdd_apf_read_memory(struct hdd_adapter *adapter, struct nlattr **tb)
490*5113495bSYour Name {
491*5113495bSYour Name 	struct wmi_apf_read_memory_params read_mem_params = {0};
492*5113495bSYour Name 	struct hdd_apf_context *context = &adapter->apf_context;
493*5113495bSYour Name 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
494*5113495bSYour Name 	QDF_STATUS status;
495*5113495bSYour Name 	unsigned long nl_buf_len = NLMSG_HDRLEN;
496*5113495bSYour Name 	int ret = 0;
497*5113495bSYour Name 	struct sk_buff *skb = NULL;
498*5113495bSYour Name 	uint8_t *bufptr;
499*5113495bSYour Name 	mac_handle_t mac_handle;
500*5113495bSYour Name 
501*5113495bSYour Name 	mac_handle = hdd_adapter_get_mac_handle(adapter);
502*5113495bSYour Name 	if (!mac_handle) {
503*5113495bSYour Name 		hdd_debug("mac ctx NULL");
504*5113495bSYour Name 		return -EINVAL;
505*5113495bSYour Name 	}
506*5113495bSYour Name 
507*5113495bSYour Name 	if (context->apf_enabled) {
508*5113495bSYour Name 		hdd_err("Cannot get/set while interpreter is enabled");
509*5113495bSYour Name 		return -EINVAL;
510*5113495bSYour Name 	}
511*5113495bSYour Name 
512*5113495bSYour Name 	read_mem_params.vdev_id = adapter->deflink->vdev_id;
513*5113495bSYour Name 
514*5113495bSYour Name 	/* Read APF work memory offset */
515*5113495bSYour Name 	if (!tb[APF_CURRENT_OFFSET]) {
516*5113495bSYour Name 		hdd_err("attr apf memory offset failed");
517*5113495bSYour Name 		return -EINVAL;
518*5113495bSYour Name 	}
519*5113495bSYour Name 	read_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
520*5113495bSYour Name 	if (read_mem_params.addr_offset > MAX_APF_MEMORY_LEN) {
521*5113495bSYour Name 		hdd_err("attr apf memory offset should be less than %d",
522*5113495bSYour Name 			MAX_APF_MEMORY_LEN);
523*5113495bSYour Name 		return -EINVAL;
524*5113495bSYour Name 	}
525*5113495bSYour Name 
526*5113495bSYour Name 	/* Read length */
527*5113495bSYour Name 	if (!tb[APF_PACKET_SIZE]) {
528*5113495bSYour Name 		hdd_err("attr apf packet size failed");
529*5113495bSYour Name 		return -EINVAL;
530*5113495bSYour Name 	}
531*5113495bSYour Name 	read_mem_params.length = nla_get_u32(tb[APF_PACKET_SIZE]);
532*5113495bSYour Name 	if (!read_mem_params.length) {
533*5113495bSYour Name 		hdd_err("apf read length cannot be zero!");
534*5113495bSYour Name 		return -EINVAL;
535*5113495bSYour Name 	}
536*5113495bSYour Name 	bufptr = qdf_mem_malloc(read_mem_params.length);
537*5113495bSYour Name 	if (!bufptr)
538*5113495bSYour Name 		return -ENOMEM;
539*5113495bSYour Name 
540*5113495bSYour Name 	qdf_event_reset(&context->qdf_apf_event);
541*5113495bSYour Name 	context->offset = read_mem_params.addr_offset;
542*5113495bSYour Name 
543*5113495bSYour Name 	context->buf = bufptr;
544*5113495bSYour Name 	context->buf_len = read_mem_params.length;
545*5113495bSYour Name 	context->magic = APF_CONTEXT_MAGIC;
546*5113495bSYour Name 
547*5113495bSYour Name 	status = sme_apf_read_work_memory(mac_handle, &read_mem_params,
548*5113495bSYour Name 					  hdd_apf_read_memory_callback);
549*5113495bSYour Name 	if (QDF_IS_STATUS_ERROR(status)) {
550*5113495bSYour Name 		hdd_err("Unable to post sme APF read memory message (status-%d)",
551*5113495bSYour Name 				status);
552*5113495bSYour Name 		ret = -EINVAL;
553*5113495bSYour Name 		goto fail;
554*5113495bSYour Name 	}
555*5113495bSYour Name 
556*5113495bSYour Name 	/* request was sent -- wait for the response */
557*5113495bSYour Name 	status = qdf_wait_for_event_completion(&context->qdf_apf_event,
558*5113495bSYour Name 					       WLAN_WAIT_TIME_APF_READ_MEM);
559*5113495bSYour Name 	if (QDF_IS_STATUS_ERROR(status)) {
560*5113495bSYour Name 		hdd_err("Target response timed out");
561*5113495bSYour Name 		context->magic = 0;
562*5113495bSYour Name 		ret = -ETIMEDOUT;
563*5113495bSYour Name 		goto fail;
564*5113495bSYour Name 	}
565*5113495bSYour Name 
566*5113495bSYour Name 	nl_buf_len += sizeof(uint32_t) + NLA_HDRLEN;
567*5113495bSYour Name 	nl_buf_len += context->buf_len + NLA_HDRLEN;
568*5113495bSYour Name 	skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
569*5113495bSYour Name 						       nl_buf_len);
570*5113495bSYour Name 	if (!skb) {
571*5113495bSYour Name 		hdd_err("wlan_cfg80211_vendor_cmd_alloc_reply_skb failed");
572*5113495bSYour Name 		ret = -ENOMEM;
573*5113495bSYour Name 		goto fail;
574*5113495bSYour Name 	}
575*5113495bSYour Name 
576*5113495bSYour Name 	if (nla_put_u32(skb, APF_SUBCMD, QCA_WLAN_READ_PACKET_FILTER) ||
577*5113495bSYour Name 	    nla_put(skb, APF_PROGRAM, read_mem_params.length, context->buf)) {
578*5113495bSYour Name 		hdd_err("put fail");
579*5113495bSYour Name 		wlan_cfg80211_vendor_free_skb(skb);
580*5113495bSYour Name 		ret = -EINVAL;
581*5113495bSYour Name 		goto fail;
582*5113495bSYour Name 	}
583*5113495bSYour Name 
584*5113495bSYour Name 	wlan_cfg80211_vendor_cmd_reply(skb);
585*5113495bSYour Name 
586*5113495bSYour Name 	hdd_debug("Reading APF work memory from offset 0x%X:",
587*5113495bSYour Name 		  read_mem_params.addr_offset);
588*5113495bSYour Name 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
589*5113495bSYour Name 			   context->buf, read_mem_params.length);
590*5113495bSYour Name fail:
591*5113495bSYour Name 	if (context->buf) {
592*5113495bSYour Name 		qdf_mem_free(context->buf);
593*5113495bSYour Name 		context->buf = NULL;
594*5113495bSYour Name 	}
595*5113495bSYour Name 
596*5113495bSYour Name 	return ret;
597*5113495bSYour Name }
598*5113495bSYour Name 
599*5113495bSYour Name /**
600*5113495bSYour Name  * __wlan_hdd_cfg80211_apf_offload() - Set/Reset to APF Offload
601*5113495bSYour Name  * @wiphy:    wiphy structure pointer
602*5113495bSYour Name  * @wdev:     Wireless device structure pointer
603*5113495bSYour Name  * @data:     Pointer to the data received
604*5113495bSYour Name  * @data_len: Length of @data
605*5113495bSYour Name  *
606*5113495bSYour Name  * Return: 0 on success; errno on failure
607*5113495bSYour Name  */
608*5113495bSYour Name static int
__wlan_hdd_cfg80211_apf_offload(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)609*5113495bSYour Name __wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy,
610*5113495bSYour Name 				struct wireless_dev *wdev,
611*5113495bSYour Name 				const void *data, int data_len)
612*5113495bSYour Name {
613*5113495bSYour Name 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
614*5113495bSYour Name 	struct net_device *dev = wdev->netdev;
615*5113495bSYour Name 	struct hdd_adapter *adapter =  WLAN_HDD_GET_PRIV_PTR(dev);
616*5113495bSYour Name 	struct nlattr *tb[APF_MAX + 1];
617*5113495bSYour Name 	int ret_val = 0, apf_subcmd;
618*5113495bSYour Name 	struct hdd_apf_context *context;
619*5113495bSYour Name 
620*5113495bSYour Name 	hdd_enter_dev(dev);
621*5113495bSYour Name 
622*5113495bSYour Name 	if (!adapter) {
623*5113495bSYour Name 		hdd_err("Adapter is null");
624*5113495bSYour Name 		return -EINVAL;
625*5113495bSYour Name 	}
626*5113495bSYour Name 
627*5113495bSYour Name 	context = &adapter->apf_context;
628*5113495bSYour Name 
629*5113495bSYour Name 	ret_val = wlan_hdd_validate_context(hdd_ctx);
630*5113495bSYour Name 	if (ret_val)
631*5113495bSYour Name 		return ret_val;
632*5113495bSYour Name 
633*5113495bSYour Name 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
634*5113495bSYour Name 		hdd_err("Command not allowed in FTM mode");
635*5113495bSYour Name 		return -EINVAL;
636*5113495bSYour Name 	}
637*5113495bSYour Name 
638*5113495bSYour Name 	if (!ucfg_pmo_is_apf_enabled(hdd_ctx->psoc)) {
639*5113495bSYour Name 		hdd_err("APF is not supported or disabled through INI");
640*5113495bSYour Name 		return -ENOTSUPP;
641*5113495bSYour Name 	}
642*5113495bSYour Name 
643*5113495bSYour Name 	if (!(adapter->device_mode == QDF_STA_MODE ||
644*5113495bSYour Name 	      adapter->device_mode == QDF_P2P_CLIENT_MODE)) {
645*5113495bSYour Name 		hdd_err("APF only supported in STA or P2P CLI modes!");
646*5113495bSYour Name 		return -ENOTSUPP;
647*5113495bSYour Name 	}
648*5113495bSYour Name 
649*5113495bSYour Name 	if (wlan_cfg80211_nla_parse(tb, APF_MAX, data, data_len,
650*5113495bSYour Name 				    wlan_hdd_apf_offload_policy)) {
651*5113495bSYour Name 		hdd_err("Invalid ATTR");
652*5113495bSYour Name 		return -EINVAL;
653*5113495bSYour Name 	}
654*5113495bSYour Name 
655*5113495bSYour Name 	if (!tb[APF_SUBCMD]) {
656*5113495bSYour Name 		hdd_err("attr apf sub-command failed");
657*5113495bSYour Name 		return -EINVAL;
658*5113495bSYour Name 	}
659*5113495bSYour Name 	apf_subcmd = nla_get_u32(tb[APF_SUBCMD]);
660*5113495bSYour Name 
661*5113495bSYour Name 	/* Do not allow simultaneous new APF commands on the same adapter */
662*5113495bSYour Name 	qdf_spin_lock(&context->lock);
663*5113495bSYour Name 	if (context->cmd_in_progress) {
664*5113495bSYour Name 		qdf_spin_unlock(&context->lock);
665*5113495bSYour Name 		hdd_err("Another cmd in progress for same session!");
666*5113495bSYour Name 		return -EAGAIN;
667*5113495bSYour Name 	}
668*5113495bSYour Name 	context->cmd_in_progress = true;
669*5113495bSYour Name 	qdf_spin_unlock(&context->lock);
670*5113495bSYour Name 
671*5113495bSYour Name 	switch (apf_subcmd) {
672*5113495bSYour Name 	/* Legacy APF sub-commands */
673*5113495bSYour Name 	case QCA_WLAN_SET_PACKET_FILTER:
674*5113495bSYour Name 		ret_val = hdd_set_reset_apf_offload(hdd_ctx, tb,
675*5113495bSYour Name 						    adapter);
676*5113495bSYour Name 		break;
677*5113495bSYour Name 	case QCA_WLAN_GET_PACKET_FILTER:
678*5113495bSYour Name 		ret_val = hdd_get_apf_capabilities(hdd_ctx);
679*5113495bSYour Name 		break;
680*5113495bSYour Name 
681*5113495bSYour Name 	/* APF 3.0 sub-commands */
682*5113495bSYour Name 	case QCA_WLAN_WRITE_PACKET_FILTER:
683*5113495bSYour Name 		ret_val = hdd_apf_write_memory(adapter, tb);
684*5113495bSYour Name 		break;
685*5113495bSYour Name 	case QCA_WLAN_READ_PACKET_FILTER:
686*5113495bSYour Name 		ret_val = hdd_apf_read_memory(adapter, tb);
687*5113495bSYour Name 		break;
688*5113495bSYour Name 	case QCA_WLAN_ENABLE_PACKET_FILTER:
689*5113495bSYour Name 		ret_val = hdd_enable_disable_apf(adapter, true);
690*5113495bSYour Name 		break;
691*5113495bSYour Name 	case QCA_WLAN_DISABLE_PACKET_FILTER:
692*5113495bSYour Name 		ret_val = hdd_enable_disable_apf(adapter, false);
693*5113495bSYour Name 		break;
694*5113495bSYour Name 	default:
695*5113495bSYour Name 		hdd_err("Unknown APF Sub-command: %d", apf_subcmd);
696*5113495bSYour Name 		ret_val = -ENOTSUPP;
697*5113495bSYour Name 	}
698*5113495bSYour Name 
699*5113495bSYour Name 	qdf_spin_lock(&context->lock);
700*5113495bSYour Name 	context->cmd_in_progress = false;
701*5113495bSYour Name 	qdf_spin_unlock(&context->lock);
702*5113495bSYour Name 
703*5113495bSYour Name 	return ret_val;
704*5113495bSYour Name }
705*5113495bSYour Name 
706*5113495bSYour Name int
wlan_hdd_cfg80211_apf_offload(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)707*5113495bSYour Name wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy, struct wireless_dev *wdev,
708*5113495bSYour Name 			      const void *data, int data_len)
709*5113495bSYour Name {
710*5113495bSYour Name 	int errno;
711*5113495bSYour Name 	struct osif_vdev_sync *vdev_sync;
712*5113495bSYour Name 
713*5113495bSYour Name 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
714*5113495bSYour Name 	if (errno)
715*5113495bSYour Name 		return errno;
716*5113495bSYour Name 
717*5113495bSYour Name 	errno = __wlan_hdd_cfg80211_apf_offload(wiphy, wdev, data, data_len);
718*5113495bSYour Name 
719*5113495bSYour Name 	osif_vdev_sync_op_stop(vdev_sync);
720*5113495bSYour Name 
721*5113495bSYour Name 	return errno;
722*5113495bSYour Name }
723*5113495bSYour Name 
724