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, ¶ms);
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, ¶ms);
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, ¶m);
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, ¶m);
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(¶ms);
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(¶ms);
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