1 /*
2  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /**
18  * DOC: defines driver functions interfacing with linux kernel
19  */
20 #include <wmi_unified_param.h>
21 #include <wlan_osif_request_manager.h>
22 #include <osif_sync.h>
23 #include <wlan_objmgr_psoc_obj.h>
24 #include <wlan_hdd_main.h>
25 #include <wlan_coap_main.h>
26 #include <wlan_coap_ucfg_api.h>
27 #include <wlan_cfg80211_coap.h>
28 
29 #define COAP_MATCH_DATA_BYTES_MAX 16
30 #define COAP_MSG_BYTES_MAX 1152
31 #define COAP_OFFLOAD_REPLY_CACHE_EXPTIME_MS 40000
32 #define COAP_OFFLOAD_CACHE_GET_TIMEOUT_MS 2000
33 
34 #define COAP_ATTR(_name) QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ ## _name
35 
36 static const struct nla_policy
37 coap_offload_filter_policy[COAP_ATTR(FILTER_MAX) + 1] = {
38 	[COAP_ATTR(FILTER_DEST_IPV4)] = {.type = NLA_U32},
39 	[COAP_ATTR(FILTER_DEST_IPV4_IS_BC)] = {.type = NLA_FLAG},
40 	[COAP_ATTR(FILTER_DEST_PORT)] = {.type = NLA_U16},
41 	[COAP_ATTR(FILTER_MATCH_OFFSET)] = {.type = NLA_U32},
42 	[COAP_ATTR(FILTER_MATCH_DATA)] = {
43 		.type = NLA_BINARY, .len = COAP_MATCH_DATA_BYTES_MAX},
44 };
45 
46 static const struct nla_policy
47 coap_offload_tx_ipv4_policy[COAP_ATTR(TX_IPV4_MAX) + 1] = {
48 	[COAP_ATTR(TX_IPV4_SRC_ADDR)] = {.type = NLA_U32},
49 	[COAP_ATTR(TX_IPV4_SRC_PORT)] = {.type = NLA_U16},
50 	[COAP_ATTR(TX_IPV4_DEST_ADDR)] = {.type = NLA_U32},
51 	[COAP_ATTR(TX_IPV4_DEST_IS_BC)] = {.type = NLA_FLAG},
52 	[COAP_ATTR(TX_IPV4_DEST_PORT)] = {.type = NLA_U16},
53 };
54 
55 static const struct nla_policy
56 coap_offload_reply_policy[COAP_ATTR(REPLY_MAX) + 1] = {
57 	[COAP_ATTR(REPLY_SRC_IPV4)] = {.type = NLA_U32},
58 	[COAP_ATTR(REPLY_FILTER)] =
59 		VENDOR_NLA_POLICY_NESTED(coap_offload_filter_policy),
60 	[COAP_ATTR(REPLY_MSG)] = {
61 		.type = NLA_BINARY, .len = COAP_MSG_BYTES_MAX},
62 	[COAP_ATTR(REPLY_CACHE_EXPTIME)] = {.type = NLA_U32},
63 };
64 
65 static const struct nla_policy
66 coap_offload_periodic_tx_policy[COAP_ATTR(PERIODIC_TX_MAX) + 1] = {
67 	[COAP_ATTR(PERIODIC_TX_IPV4)] =
68 		VENDOR_NLA_POLICY_NESTED(coap_offload_tx_ipv4_policy),
69 	[COAP_ATTR(PERIODIC_TX_PERIOD)] = {.type = NLA_U32},
70 	[COAP_ATTR(PERIODIC_TX_MSG)] = {
71 		.type = NLA_BINARY, .len = COAP_MSG_BYTES_MAX},
72 };
73 
74 const struct nla_policy
75 coap_offload_policy[COAP_ATTR(MAX) + 1] = {
76 	[COAP_ATTR(ACTION)] = {.type = NLA_U32 },
77 	[COAP_ATTR(REQ_ID)] = {.type = NLA_U32 },
78 	[COAP_ATTR(REPLY)] =
79 		VENDOR_NLA_POLICY_NESTED(coap_offload_reply_policy),
80 	[COAP_ATTR(PERIODIC_TX)] =
81 		VENDOR_NLA_POLICY_NESTED(coap_offload_periodic_tx_policy),
82 };
83 
84 /**
85  * wlan_cfg80211_coap_offload_reply_fill_filter() - fill filter for CoAP
86  * offload reply.
87  * @attr_filter: pointer to filter attribute
88  * @params: pointer to parameters for CoAP offload reply
89  *
90  * Return: 0 on success; error number otherwise
91  */
92 static int
wlan_cfg80211_coap_offload_reply_fill_filter(struct nlattr * attr_filter,struct coap_offload_reply_param * params)93 wlan_cfg80211_coap_offload_reply_fill_filter(struct nlattr *attr_filter,
94 	struct coap_offload_reply_param *params)
95 {
96 	struct nlattr *tb[COAP_ATTR(FILTER_MAX) + 1];
97 
98 	if (!attr_filter) {
99 		coap_err("No ATTR filter");
100 		return -EINVAL;
101 	}
102 
103 	if (!nla_data(attr_filter)) {
104 		coap_err("Invalid filter");
105 		return -EINVAL;
106 	}
107 
108 	if (wlan_cfg80211_nla_parse_nested(tb, COAP_ATTR(FILTER_MAX),
109 					   attr_filter,
110 					   coap_offload_filter_policy)) {
111 		coap_err("Invalid ATTR");
112 		return -EINVAL;
113 	}
114 
115 	if (!tb[COAP_ATTR(FILTER_DEST_IPV4)]) {
116 		coap_err("no ATTR dest IPv4");
117 		return -EINVAL;
118 	}
119 
120 	params->dest_ip_v4 = nla_get_u32(tb[COAP_ATTR(FILTER_DEST_IPV4)]);
121 	params->dest_ip_v4_is_bc =
122 		nla_get_flag(tb[COAP_ATTR(FILTER_DEST_IPV4_IS_BC)]);
123 
124 	if (!tb[COAP_ATTR(FILTER_DEST_PORT)]) {
125 		coap_err("no ATTR dest IPv4 port");
126 		return -EINVAL;
127 	}
128 
129 	params->dest_udp_port = nla_get_u16(tb[COAP_ATTR(FILTER_DEST_PORT)]);
130 
131 	if (!tb[COAP_ATTR(FILTER_MATCH_OFFSET)]) {
132 		coap_err("no ATTR match offset");
133 		return -EINVAL;
134 	}
135 
136 	params->verify_offset =
137 		nla_get_u32(tb[COAP_ATTR(FILTER_MATCH_OFFSET)]);
138 
139 	if (!tb[COAP_ATTR(FILTER_MATCH_DATA)]) {
140 		coap_err("no ATTR match data");
141 		return -EINVAL;
142 	}
143 
144 	params->verify_len = nla_len(tb[COAP_ATTR(FILTER_MATCH_DATA)]);
145 	if (!params->verify_len) {
146 		coap_err("invalid match data len");
147 		return -EINVAL;
148 	}
149 
150 	params->verify = nla_data(tb[COAP_ATTR(FILTER_MATCH_DATA)]);
151 	return 0;
152 }
153 
154 /**
155  * wlan_cfg80211_coap_offload_reply_enable() - enable CoAP offload reply
156  * @vdev: pointer to vdev object
157  * @req_id: request id
158  * @attr_reply: pointer to CoAP offload reply attribute
159  *
160  * Return: 0 on success; error number otherwise
161  */
162 static int
wlan_cfg80211_coap_offload_reply_enable(struct wlan_objmgr_vdev * vdev,uint32_t req_id,struct nlattr * attr_reply)163 wlan_cfg80211_coap_offload_reply_enable(struct wlan_objmgr_vdev *vdev,
164 					uint32_t req_id,
165 					struct nlattr *attr_reply)
166 {
167 	struct nlattr *tb[COAP_ATTR(REPLY_MAX) + 1];
168 	struct coap_offload_reply_param params = {0};
169 	struct nlattr *attr;
170 	QDF_STATUS status;
171 	int ret;
172 
173 	if (!attr_reply) {
174 		coap_err("No ATTR reply");
175 		return -EINVAL;
176 	}
177 
178 	if (wlan_cfg80211_nla_parse_nested(tb, COAP_ATTR(REPLY_MAX),
179 					   attr_reply,
180 					   coap_offload_reply_policy)) {
181 		coap_err("Invalid ATTR");
182 		return -EINVAL;
183 	}
184 
185 	attr = tb[COAP_ATTR(REPLY_SRC_IPV4)];
186 	if (!attr) {
187 		coap_err("No ATTR IPv4");
188 		return -EINVAL;
189 	}
190 
191 	params.pattern_id = req_id;
192 	params.vdev_id = wlan_vdev_get_id(vdev);
193 	params.src_ip_v4 = nla_get_u32(attr);
194 
195 	attr = tb[COAP_ATTR(REPLY_FILTER)];
196 	ret = wlan_cfg80211_coap_offload_reply_fill_filter(attr, &params);
197 	if (ret)
198 		return ret;
199 
200 	attr = tb[COAP_ATTR(REPLY_MSG)];
201 	if (!attr) {
202 		coap_err("No ATTR msg");
203 		return -EINVAL;
204 	}
205 
206 	params.coapmsg_len = nla_len(attr);
207 	params.coapmsg = nla_data(attr);
208 
209 	attr = tb[COAP_ATTR(REPLY_CACHE_EXPTIME)];
210 	if (!attr)
211 		params.cache_timeout = COAP_OFFLOAD_REPLY_CACHE_EXPTIME_MS;
212 	else
213 		params.cache_timeout = nla_get_u32(attr);
214 
215 	status = ucfg_coap_offload_reply_enable(vdev, &params);
216 	ret = qdf_status_to_os_return(status);
217 	return ret;
218 }
219 
220 /**
221  * wlan_cfg80211_coap_offload_fill_tx_ipv4() - fill IPv4 source/destination
222  * address/port for offload transmitting.
223  * @attr_ipv4: pointer to TX IPv4 attribute
224  * @params: pointer to parameters for CoAP offload reply
225  *
226  * Return: 0 on success; error number otherwise
227  */
228 static int
wlan_cfg80211_coap_offload_fill_tx_ipv4(struct nlattr * attr_ipv4,struct coap_offload_periodic_tx_param * params)229 wlan_cfg80211_coap_offload_fill_tx_ipv4(struct nlattr *attr_ipv4,
230 			struct coap_offload_periodic_tx_param *params)
231 {
232 	struct nlattr *tb[COAP_ATTR(TX_IPV4_MAX) + 1];
233 
234 	if (!attr_ipv4) {
235 		coap_err("No ATTR TX IPv4");
236 		return -EINVAL;
237 	}
238 
239 	if (wlan_cfg80211_nla_parse_nested(tb, COAP_ATTR(TX_IPV4_MAX),
240 					   attr_ipv4,
241 					   coap_offload_tx_ipv4_policy)) {
242 		coap_err("Invalid ATTR");
243 		return -EINVAL;
244 	}
245 
246 	if (!tb[COAP_ATTR(TX_IPV4_SRC_ADDR)]) {
247 		coap_err("no ATTR src addr");
248 		return -EINVAL;
249 	}
250 
251 	params->src_ip_v4 = nla_get_u32(tb[COAP_ATTR(TX_IPV4_SRC_ADDR)]);
252 	if (tb[COAP_ATTR(TX_IPV4_SRC_PORT)])
253 		params->src_udp_port =
254 			nla_get_u32(tb[COAP_ATTR(TX_IPV4_SRC_PORT)]);
255 
256 	if (!tb[COAP_ATTR(TX_IPV4_DEST_ADDR)]) {
257 		coap_err("no ATTR IPv4 dest addr");
258 		return -EINVAL;
259 	}
260 
261 	params->dest_ip_v4 = nla_get_u32(tb[COAP_ATTR(TX_IPV4_DEST_ADDR)]);
262 	params->dest_ip_v4_is_bc =
263 		nla_get_flag(tb[COAP_ATTR(TX_IPV4_DEST_IS_BC)]);
264 
265 	if (!tb[COAP_ATTR(TX_IPV4_DEST_PORT)]) {
266 		coap_err("no ATTR dest IPv4 port");
267 		return -EINVAL;
268 	}
269 
270 	params->dest_udp_port =
271 		nla_get_u32(tb[COAP_ATTR(TX_IPV4_DEST_PORT)]);
272 	return 0;
273 }
274 
275 /**
276  * wlan_cfg80211_coap_offload_periodic_tx_enable() - enable CoAP offload
277  * periodic transmitting
278  * @vdev: pointer to vdev object
279  * @req_id: request id
280  * @attr_periodic_tx: pointer to CoAP offload periodic TX attribute
281  *
282  * Return: 0 on success; error number otherwise
283  */
284 static int
wlan_cfg80211_coap_offload_periodic_tx_enable(struct wlan_objmgr_vdev * vdev,uint32_t req_id,struct nlattr * attr_periodic_tx)285 wlan_cfg80211_coap_offload_periodic_tx_enable(struct wlan_objmgr_vdev *vdev,
286 					      uint32_t req_id,
287 					      struct nlattr *attr_periodic_tx)
288 {
289 	struct nlattr *tb[COAP_ATTR(PERIODIC_TX_MAX) + 1];
290 	struct coap_offload_periodic_tx_param param = {0};
291 	struct nlattr *attr_ipv4;
292 	QDF_STATUS status;
293 	int ret;
294 
295 	if (!attr_periodic_tx) {
296 		coap_err("No ATTR periodic tx");
297 		return -EINVAL;
298 	}
299 
300 	if (wlan_cfg80211_nla_parse_nested(tb, COAP_ATTR(PERIODIC_TX_MAX),
301 					   attr_periodic_tx,
302 					   coap_offload_periodic_tx_policy)) {
303 		coap_err("Invalid ATTR");
304 		return -EINVAL;
305 	}
306 
307 	if (!tb[COAP_ATTR(PERIODIC_TX_PERIOD)]) {
308 		coap_err("no ATTR period");
309 		return -EINVAL;
310 	}
311 
312 	param.timeout = nla_get_u32(tb[COAP_ATTR(PERIODIC_TX_PERIOD)]);
313 	attr_ipv4 = tb[COAP_ATTR(PERIODIC_TX_IPV4)];
314 	ret = wlan_cfg80211_coap_offload_fill_tx_ipv4(attr_ipv4, &param);
315 	if (ret)
316 		return ret;
317 
318 	param.vdev_id = wlan_vdev_get_id(vdev);
319 	param.pattern_id = req_id;
320 	if (!tb[COAP_ATTR(PERIODIC_TX_MSG)]) {
321 		coap_err("no ATTR msg");
322 		return -EINVAL;
323 	}
324 
325 	param.coapmsg_len = nla_len(tb[COAP_ATTR(PERIODIC_TX_MSG)]);
326 	param.coapmsg = nla_data(tb[COAP_ATTR(PERIODIC_TX_MSG)]);
327 	status = ucfg_coap_offload_periodic_tx_enable(vdev, &param);
328 	return qdf_status_to_os_return(status);
329 }
330 
331 /**
332  * wlan_cfg80211_coap_offload_periodic_tx_disable() - disable CoAP offload
333  * periodic transmitting
334  * @vdev: pointer to vdev object
335  * @req_id: request id
336  *
337  * Return: 0 on success; error number otherwise
338  */
339 static int
wlan_cfg80211_coap_offload_periodic_tx_disable(struct wlan_objmgr_vdev * vdev,uint32_t req_id)340 wlan_cfg80211_coap_offload_periodic_tx_disable(struct wlan_objmgr_vdev *vdev,
341 					       uint32_t req_id)
342 {
343 	QDF_STATUS status;
344 
345 	status = ucfg_coap_offload_periodic_tx_disable(vdev, req_id);
346 	return qdf_status_to_os_return(status);
347 }
348 
349 /**
350  * wlan_cfg80211_dealloc_coap_buf_info() - Callback to free priv
351  * allocations for CoAP buffer info
352  * @priv: Pointer to priv data statucture
353  *
354  * Return: None
355  */
wlan_cfg80211_dealloc_coap_buf_info(void * priv)356 static void wlan_cfg80211_dealloc_coap_buf_info(void *priv)
357 {
358 	struct coap_buf_info *info = priv;
359 	struct coap_buf_node *cur, *next;
360 
361 	if (!info)
362 		return;
363 
364 	qdf_list_for_each_del(&info->info_list, cur, next, node) {
365 		qdf_list_remove_node(&info->info_list, &cur->node);
366 		qdf_mem_free(cur->payload);
367 		qdf_mem_free(cur);
368 	}
369 
370 	qdf_list_destroy(&info->info_list);
371 }
372 
373 static void
wlan_cfg80211_coap_cache_get_cbk(void * context,struct coap_buf_info * info)374 wlan_cfg80211_coap_cache_get_cbk(void *context, struct coap_buf_info *info)
375 {
376 	struct osif_request *request;
377 	struct coap_buf_info *priv_info;
378 
379 	if (!context || !info)
380 		return;
381 
382 	request = osif_request_get(context);
383 	if (!request)
384 		return;
385 
386 	priv_info = osif_request_priv(request);
387 	if (info->req_id == priv_info->req_id) {
388 		qdf_list_join(&priv_info->info_list, &info->info_list);
389 		if (!info->more_info)
390 			osif_request_complete(request);
391 	}
392 
393 	osif_request_put(request);
394 }
395 
396 /**
397  * wlan_cfg80211_coap_fill_buf_info() - Fill cache get response buffer
398  * @reply_skb : pointer to reply_skb
399  * @info : information of cached CoAP messages
400  * @index : attribute type index for nla_next_start()
401  *
402  * Return : 0 on success and errno on failure
403  */
404 static int
wlan_cfg80211_coap_fill_buf_info(struct sk_buff * reply_skb,struct coap_buf_node * info,int index)405 wlan_cfg80211_coap_fill_buf_info(struct sk_buff *reply_skb,
406 				 struct coap_buf_node *info, int index)
407 {
408 	struct nlattr *attr;
409 
410 	attr = nla_nest_start(reply_skb, index);
411 	if (!attr) {
412 		coap_err("nla_nest_start failed");
413 		return -EINVAL;
414 	}
415 
416 	if (hdd_wlan_nla_put_u64(reply_skb, COAP_ATTR(CACHE_INFO_TS),
417 				 info->tsf) ||
418 	    nla_put_u32(reply_skb, COAP_ATTR(CACHE_INFO_SRC_IPV4),
419 			info->src_ip) ||
420 	    nla_put(reply_skb, COAP_ATTR(CACHE_INFO_MSG),
421 		    info->len, info->payload)) {
422 		coap_err("nla_put failed");
423 		return -EINVAL;
424 	}
425 
426 	nla_nest_end(reply_skb, attr);
427 	return 0;
428 }
429 
430 /**
431  * wlan_cfg80211_coap_offload_cache_deliver() - deliver cached CoAP messages
432  * @wiphy: pointer to wireless wiphy structure.
433  * @cache_list: list of cached CoAP messages
434  *
435  * Return: 0 on success; error number otherwise
436  */
437 static int
wlan_cfg80211_coap_offload_cache_deliver(struct wiphy * wiphy,qdf_list_t * cache_list)438 wlan_cfg80211_coap_offload_cache_deliver(struct wiphy *wiphy,
439 					 qdf_list_t *cache_list)
440 {
441 	struct sk_buff *skb;
442 	uint32_t skb_len = NLMSG_HDRLEN;
443 	struct coap_buf_node *cur, *next;
444 	struct nlattr *attr;
445 	int i = 0, ret;
446 
447 	/* QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHES */
448 	skb_len += nla_total_size(0);
449 	qdf_list_for_each_del(cache_list, cur, next, node) {
450 		if (!cur->len || !cur->payload)
451 			continue;
452 
453 		/* nest attribute */
454 		skb_len += nla_total_size(0);
455 
456 		/* QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_TS */
457 		skb_len += nla_total_size(sizeof(uint64_t));
458 
459 		/* QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV4 */
460 		skb_len += nla_total_size(sizeof(uint32_t));
461 
462 		/* QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_MSG */
463 		skb_len += nla_total_size(cur->len);
464 	}
465 
466 	skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(wiphy, skb_len);
467 	attr = nla_nest_start(skb, COAP_ATTR(CACHES));
468 	if (!attr) {
469 		hdd_err("nla_nest_start failed");
470 		wlan_cfg80211_vendor_free_skb(skb);
471 		return -EINVAL;
472 	}
473 
474 	qdf_list_for_each_del(cache_list, cur, next, node) {
475 		if (!cur->len || !cur->payload)
476 			continue;
477 
478 		qdf_list_remove_node(cache_list, &cur->node);
479 		ret = wlan_cfg80211_coap_fill_buf_info(skb, cur, i++);
480 		if (ret) {
481 			wlan_cfg80211_vendor_free_skb(skb);
482 			return -EINVAL;
483 		}
484 
485 		qdf_mem_free(cur->payload);
486 		qdf_mem_free(cur);
487 	}
488 
489 	nla_nest_end(skb, attr);
490 	return wlan_cfg80211_vendor_cmd_reply(skb);
491 }
492 
493 /**
494  * wlan_cfg80211_coap_offload_cache_get() - get cached CoAP messages
495  * @wiphy: pointer to wireless wiphy structure.
496  * @vdev: pointer to vdev object
497  * @req_id: request id
498  *
499  * Return: 0 on success; error number otherwise
500  */
501 static int
wlan_cfg80211_coap_offload_cache_get(struct wiphy * wiphy,struct wlan_objmgr_vdev * vdev,uint32_t req_id)502 wlan_cfg80211_coap_offload_cache_get(struct wiphy *wiphy,
503 				     struct wlan_objmgr_vdev *vdev,
504 				     uint32_t req_id)
505 {
506 	void *cookie;
507 	QDF_STATUS status;
508 	struct osif_request *request;
509 	struct coap_buf_info *buf_info;
510 	int ret;
511 	static const struct osif_request_params params = {
512 		.priv_size = sizeof(*buf_info),
513 		.timeout_ms = COAP_OFFLOAD_CACHE_GET_TIMEOUT_MS,
514 		.dealloc = wlan_cfg80211_dealloc_coap_buf_info,
515 	};
516 
517 	request = osif_request_alloc(&params);
518 	if (!request) {
519 		coap_err("Request allocation failure");
520 		status = QDF_STATUS_E_NOMEM;
521 		goto out;
522 	}
523 
524 	buf_info = osif_request_priv(request);
525 	qdf_list_create(&buf_info->info_list, 0);
526 	buf_info->req_id = req_id;
527 	buf_info->vdev_id = wlan_vdev_get_id(vdev);
528 
529 	cookie = osif_request_cookie(request);
530 	status = ucfg_coap_offload_cache_get(vdev, req_id,
531 					     wlan_cfg80211_coap_cache_get_cbk,
532 					     cookie);
533 	if (QDF_IS_STATUS_ERROR(status)) {
534 		coap_err("Unable to get cache");
535 		goto out;
536 	}
537 
538 	ret = osif_request_wait_for_response(request);
539 	if (ret) {
540 		coap_err("Target response timed out");
541 		status = qdf_status_from_os_return(ret);
542 		goto out;
543 	}
544 
545 	ret = wlan_cfg80211_coap_offload_cache_deliver(wiphy,
546 						       &buf_info->info_list);
547 	if (ret) {
548 		coap_err("Failed to deliver buf info");
549 		status = qdf_status_from_os_return(ret);
550 		goto out;
551 	}
552 
553 out:
554 	if (request)
555 		osif_request_put(request);
556 	return qdf_status_to_os_return(status);
557 }
558 
559 /**
560  * wlan_cfg80211_coap_offload_reply_disable() - disable CoAP offload reply
561  * @wiphy: pointer to wireless wiphy structure.
562  * @vdev: pointer to vdev object
563  * @req_id: request id
564  *
565  * Return: 0 on success; error number otherwise
566  */
567 static int
wlan_cfg80211_coap_offload_reply_disable(struct wiphy * wiphy,struct wlan_objmgr_vdev * vdev,uint32_t req_id)568 wlan_cfg80211_coap_offload_reply_disable(struct wiphy *wiphy,
569 					 struct wlan_objmgr_vdev *vdev,
570 					 uint32_t req_id)
571 {
572 	void *cookie;
573 	QDF_STATUS status;
574 	struct osif_request *request;
575 	struct coap_buf_info *buf_info;
576 	int ret;
577 	static const struct osif_request_params params = {
578 		.priv_size = sizeof(*buf_info),
579 		.timeout_ms = COAP_OFFLOAD_CACHE_GET_TIMEOUT_MS,
580 		.dealloc = wlan_cfg80211_dealloc_coap_buf_info,
581 	};
582 
583 	request = osif_request_alloc(&params);
584 	if (!request) {
585 		coap_err("Request allocation failure");
586 		status = QDF_STATUS_E_NOMEM;
587 		goto out;
588 	}
589 
590 	buf_info = osif_request_priv(request);
591 	qdf_list_create(&buf_info->info_list, 0);
592 	buf_info->req_id = req_id;
593 	buf_info->vdev_id = wlan_vdev_get_id(vdev);
594 
595 	cookie = osif_request_cookie(request);
596 	status = ucfg_coap_offload_reply_disable(vdev, req_id,
597 			wlan_cfg80211_coap_cache_get_cbk, cookie);
598 	if (QDF_IS_STATUS_ERROR(status)) {
599 		coap_err("Failed to disable offload reply");
600 		goto out;
601 	}
602 
603 	ret = osif_request_wait_for_response(request);
604 	if (ret) {
605 		coap_err("Target response timed out");
606 		status = qdf_status_from_os_return(ret);
607 		goto out;
608 	}
609 
610 	ret = wlan_cfg80211_coap_offload_cache_deliver(wiphy,
611 						       &buf_info->info_list);
612 	if (ret) {
613 		coap_err("Failed to deliver buf info");
614 		status = qdf_status_from_os_return(ret);
615 		goto out;
616 	}
617 
618 out:
619 	if (request)
620 		osif_request_put(request);
621 	return qdf_status_to_os_return(status);
622 }
623 
624 int
wlan_cfg80211_coap_offload(struct wiphy * wiphy,struct wlan_objmgr_vdev * vdev,const void * data,int data_len)625 wlan_cfg80211_coap_offload(struct wiphy *wiphy, struct wlan_objmgr_vdev *vdev,
626 			   const void *data, int data_len)
627 {
628 	struct nlattr *tb[COAP_ATTR(MAX) + 1];
629 	struct nlattr *attr;
630 	uint32_t action, req_id;
631 	int ret;
632 
633 	if (wlan_cfg80211_nla_parse(tb, COAP_ATTR(MAX),
634 				    data, data_len, coap_offload_policy)) {
635 		coap_err("Invalid ATTR");
636 		return -EINVAL;
637 	}
638 
639 	if (!tb[COAP_ATTR(ACTION)]) {
640 		coap_err("no attr action");
641 		return -EINVAL;
642 	}
643 
644 	if (!tb[COAP_ATTR(REQ_ID)]) {
645 		coap_err("no attr req id");
646 		return -EINVAL;
647 	}
648 
649 	action = nla_get_u32(tb[COAP_ATTR(ACTION)]);
650 	req_id = nla_get_u32(tb[COAP_ATTR(REQ_ID)]);
651 	switch (action) {
652 	case QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_ENABLE:
653 		attr = tb[COAP_ATTR(REPLY)];
654 		ret = wlan_cfg80211_coap_offload_reply_enable(vdev, req_id,
655 							      attr);
656 		break;
657 	case QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_DISABLE:
658 		ret = wlan_cfg80211_coap_offload_reply_disable(wiphy, vdev,
659 							       req_id);
660 		break;
661 	case QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_ENABLE:
662 		attr = tb[COAP_ATTR(PERIODIC_TX)];
663 		ret = wlan_cfg80211_coap_offload_periodic_tx_enable(vdev,
664 								    req_id,
665 								    attr);
666 		break;
667 	case QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_DISABLE:
668 		ret = wlan_cfg80211_coap_offload_periodic_tx_disable(vdev,
669 								     req_id);
670 		break;
671 	case QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_CACHE_GET:
672 		ret = wlan_cfg80211_coap_offload_cache_get(wiphy, vdev,
673 							   req_id);
674 		break;
675 	default:
676 		ret = -EINVAL;
677 		break;
678 	}
679 
680 	coap_debug("vdev_id %u action %u req id %u ret %d",
681 		   wlan_vdev_get_id(vdev), action, req_id, ret);
682 	return ret;
683 }
684