1 /*
2  * Copyright (c) 2011, 2017-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  *
6  * Permission to use, copy, modify, and/or distribute this software for
7  * any purpose with or without fee is hereby granted, provided that the
8  * above copyright notice and this permission notice appear in all
9  * copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
12  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
15  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
16  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
17  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18  * PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <os_if_spectral_netlink.h>
22 #include <wlan_cfg80211_spectral.h>
23 #include <spectral_cmn_api_i.h>
24 #include <spectral_defs_i.h>
25 #include <wlan_nlink_srv.h>
26 #include <wlan_nlink_common.h>
27 #include <qdf_module.h>
28 #ifdef CNSS_GENL
29 #ifdef CONFIG_CNSS_OUT_OF_TREE
30 #include "cnss_nl.h"
31 #else
32 #include <net/cnss_nl.h>
33 #endif
34 #endif
35 #include <wlan_cfg80211.h>
36 
37 /**
38  * os_if_spectral_remove_nbuf_debug_entry() - Remove nbuf from nbuf debug table
39  * @nbuf: nbuf to remove from the nbuf debug table
40  *
41  * Remove nbuf from the nbuf debug hash table and decrement the nbuf count
42  *
43  * Return: None
44  */
os_if_spectral_remove_nbuf_debug_entry(qdf_nbuf_t nbuf)45 static inline void os_if_spectral_remove_nbuf_debug_entry(qdf_nbuf_t nbuf)
46 {
47 	qdf_nbuf_count_dec(nbuf);
48 	qdf_net_buf_debug_release_skb(nbuf);
49 }
50 
51 #ifndef CNSS_GENL
52 static struct sock *os_if_spectral_nl_sock;
53 static atomic_t spectral_nl_users = ATOMIC_INIT(0);
54 #endif
55 
56 #if (KERNEL_VERSION(2, 6, 31) > LINUX_VERSION_CODE)
57 void
os_if_spectral_nl_data_ready(struct sock * sk,int len)58 os_if_spectral_nl_data_ready(struct sock *sk, int len)
59 {
60 	spectral_debug("%d", __LINE__);
61 }
62 
63 #else
64 void
os_if_spectral_nl_data_ready(struct sk_buff * skb)65 os_if_spectral_nl_data_ready(struct sk_buff *skb)
66 {
67 	spectral_debug("%d", __LINE__);
68 }
69 #endif				/* VERSION */
70 
71 #ifndef CNSS_GENL
72 /**
73  * os_if_spectral_init_nl_cfg() - Initialize netlink kernel
74  * configuration parameters
75  * @cfg : Pointer to netlink_kernel_cfg
76  *
77  * Initialize netlink kernel configuration parameters required
78  * for spectral module
79  *
80  * Return: None
81  */
82 #if KERNEL_VERSION(3, 6, 0) <= LINUX_VERSION_CODE
83 static void
os_if_spectral_init_nl_cfg(struct netlink_kernel_cfg * cfg)84 os_if_spectral_init_nl_cfg(struct netlink_kernel_cfg *cfg)
85 {
86 	cfg->groups = 1;
87 	cfg->input = os_if_spectral_nl_data_ready;
88 }
89 #else
90 static void
os_if_spectral_init_nl_cfg(struct netlink_kernel_cfg * cfg)91 os_if_spectral_init_nl_cfg(struct netlink_kernel_cfg *cfg)
92 {
93 }
94 #endif
95 /**
96  * os_if_spectral_create_nl_sock() - Create Netlink socket
97  * @cfg : Pointer to netlink_kernel_cfg
98  *
99  * Create Netlink socket required for spectral module
100  *
101  * Return: None
102  */
103 #if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
104 static void
os_if_spectral_create_nl_sock(struct netlink_kernel_cfg * cfg)105 os_if_spectral_create_nl_sock(struct netlink_kernel_cfg *cfg)
106 {
107 	os_if_spectral_nl_sock =
108 	    (struct sock *)netlink_kernel_create(&init_net,
109 						 SPECTRAL_NETLINK, cfg);
110 }
111 #elif KERNEL_VERSION(3, 6, 0) <= LINUX_VERSION_CODE
112 static void
os_if_spectral_create_nl_sock(struct netlink_kernel_cfg * cfg)113 os_if_spectral_create_nl_sock(struct netlink_kernel_cfg *cfg)
114 {
115 	os_if_spectral_nl_sock =
116 	    (struct sock *)netlink_kernel_create(&init_net,
117 						 SPECTRAL_NETLINK,
118 						 THIS_MODULE, cfg);
119 }
120 #elif (KERNEL_VERSION(2, 6, 31) > LINUX_VERSION_CODE)
121 static void
os_if_spectral_create_nl_sock(struct netlink_kernel_cfg * cfg)122 os_if_spectral_create_nl_sock(struct netlink_kernel_cfg *cfg)
123 {
124 	os_if_spectral_nl_sock =
125 	    (struct sock *)netlink_kernel_create(
126 		SPECTRAL_NETLINK, 1,
127 		&os_if_spectral_nl_data_ready,
128 		THIS_MODULE);
129 }
130 #else
131 #if (KERNEL_VERSION(3, 10, 0) <= LINUX_VERSION_CODE)
132 static void
os_if_spectral_create_nl_sock(struct netlink_kernel_cfg * cfg)133 os_if_spectral_create_nl_sock(struct netlink_kernel_cfg *cfg)
134 {
135 	memset(cfg, 0, sizeof(*cfg));
136 	cfg->groups = 1;
137 	cfg->input = &os_if_spectral_nl_data_ready;
138 	os_if_spectral_nl_sock =
139 	    (struct sock *)netlink_kernel_create(&init_net,
140 						 SPECTRAL_NETLINK, cfg);
141 }
142 #else
143 static void
os_if_spectral_create_nl_sock(struct netlink_kernel_cfg * cfg)144 os_if_spectral_create_nl_sock(struct netlink_kernel_cfg *cfg)
145 {
146 	os_if_spectral_nl_sock =
147 	    (struct sock *)netlink_kernel_create(
148 		&init_net,
149 		SPECTRAL_NETLINK, 1,
150 		&os_if_spectral_nl_data_ready,
151 		NULL, THIS_MODULE);
152 }
153 #endif
154 #endif
155 
156 /**
157  * os_if_spectral_init_nl() - Initialize netlink data structures for
158  * spectral module
159  * @pdev : Pointer to pdev
160  *
161  * Return: 0 on success else failure
162  */
163 static int
os_if_spectral_init_nl(struct wlan_objmgr_pdev * pdev)164 os_if_spectral_init_nl(struct wlan_objmgr_pdev *pdev)
165 {
166 	struct pdev_spectral *ps = NULL;
167 	struct netlink_kernel_cfg cfg;
168 
169 	memset(&cfg, 0, sizeof(cfg));
170 	if (!pdev) {
171 		osif_err("PDEV is NULL!");
172 		return -EINVAL;
173 	}
174 	ps = wlan_objmgr_pdev_get_comp_private_obj(pdev,
175 						   WLAN_UMAC_COMP_SPECTRAL);
176 
177 	if (!ps) {
178 		osif_err("PDEV SPECTRAL object is NULL!");
179 		return -EINVAL;
180 	}
181 	os_if_spectral_init_nl_cfg(&cfg);
182 
183 	if (!os_if_spectral_nl_sock) {
184 		os_if_spectral_create_nl_sock(&cfg);
185 
186 		if (!os_if_spectral_nl_sock) {
187 			osif_err("NETLINK_KERNEL_CREATE FAILED");
188 			return -ENODEV;
189 		}
190 	}
191 	ps->spectral_sock = os_if_spectral_nl_sock;
192 
193 	if (!ps->spectral_sock) {
194 		osif_err("ps->spectral_sock is NULL");
195 		return -ENODEV;
196 	}
197 	atomic_inc(&spectral_nl_users);
198 
199 	return 0;
200 }
201 
202 /**
203  * os_if_spectral_destroy_netlink() - De-initialize netlink data structures for
204  * spectral module
205  * @pdev : Pointer to pdev
206  *
207  * Return: Success/Failure
208  */
209 static int
os_if_spectral_destroy_netlink(struct wlan_objmgr_pdev * pdev)210 os_if_spectral_destroy_netlink(struct wlan_objmgr_pdev *pdev)
211 {
212 	struct pdev_spectral *ps = NULL;
213 
214 	if (!pdev) {
215 		osif_err("PDEV is NULL!");
216 		return -EINVAL;
217 	}
218 	ps = wlan_objmgr_pdev_get_comp_private_obj(pdev,
219 						   WLAN_UMAC_COMP_SPECTRAL);
220 
221 	if (!ps) {
222 		osif_err("PDEV SPECTRAL object is NULL!");
223 		return -EINVAL;
224 	}
225 	ps->spectral_sock = NULL;
226 	if (atomic_dec_and_test(&spectral_nl_users)) {
227 		sock_release(os_if_spectral_nl_sock->sk_socket);
228 		os_if_spectral_nl_sock = NULL;
229 	}
230 	return 0;
231 }
232 #else
233 
234 static int
os_if_spectral_init_nl(struct wlan_objmgr_pdev * pdev)235 os_if_spectral_init_nl(struct wlan_objmgr_pdev *pdev)
236 {
237 	return 0;
238 }
239 
240 static int
os_if_spectral_destroy_netlink(struct wlan_objmgr_pdev * pdev)241 os_if_spectral_destroy_netlink(struct wlan_objmgr_pdev *pdev)
242 {
243 	return 0;
244 }
245 #endif
246 
247 void *
os_if_spectral_prep_skb(struct wlan_objmgr_pdev * pdev,enum spectral_msg_type smsg_type,enum spectral_msg_buf_type buf_type)248 os_if_spectral_prep_skb(struct wlan_objmgr_pdev *pdev,
249 			enum spectral_msg_type smsg_type,
250 			enum spectral_msg_buf_type buf_type)
251 {
252 	struct pdev_spectral *ps = NULL;
253 	struct nlmsghdr *spectral_nlh = NULL;
254 	void *buf = NULL;
255 
256 	if (!pdev) {
257 		osif_err("PDEV is NULL!");
258 		return NULL;
259 	}
260 
261 	if (smsg_type >= SPECTRAL_MSG_TYPE_MAX) {
262 		osif_err("Invalid Spectral message type %u", smsg_type);
263 		return NULL;
264 	}
265 
266 	if (buf_type >= SPECTRAL_MSG_BUF_TYPE_MAX) {
267 		osif_err("Invalid Spectral message buffer type %u",
268 			 buf_type);
269 		return NULL;
270 	}
271 
272 	ps = wlan_objmgr_pdev_get_comp_private_obj(pdev,
273 						   WLAN_UMAC_COMP_SPECTRAL);
274 
275 	if (!ps) {
276 		osif_err("PDEV SPECTRAL object is NULL!");
277 		return NULL;
278 	}
279 
280 	if (buf_type == SPECTRAL_MSG_BUF_NEW) {
281 		QDF_ASSERT(!ps->skb[smsg_type]);
282 		ps->skb[smsg_type] =
283 				qdf_nbuf_alloc(NULL, MAX_SPECTRAL_PAYLOAD,
284 					       0, 0, false);
285 
286 		if (!ps->skb[smsg_type]) {
287 			osif_err("alloc skb (len=%u, msg_type=%u) failed",
288 				 MAX_SPECTRAL_PAYLOAD, smsg_type);
289 			return NULL;
290 		}
291 
292 		qdf_nbuf_put_tail(ps->skb[smsg_type], MAX_SPECTRAL_PAYLOAD);
293 		spectral_nlh = (struct nlmsghdr *)ps->skb[smsg_type]->data;
294 
295 		qdf_mem_zero(spectral_nlh, sizeof(*spectral_nlh));
296 
297 		/*
298 		 * Possible bug that size of  struct spectral_samp_msg and
299 		 * SPECTRAL_MSG differ by 3 bytes  so we miss 3 bytes
300 		 */
301 
302 		spectral_nlh->nlmsg_len =
303 				NLMSG_SPACE(sizeof(struct spectral_samp_msg));
304 		spectral_nlh->nlmsg_pid = 0;
305 		spectral_nlh->nlmsg_flags = 0;
306 		spectral_nlh->nlmsg_type = WLAN_NL_MSG_SPECTRAL_SCAN;
307 
308 		qdf_mem_zero(NLMSG_DATA(spectral_nlh),
309 			     sizeof(struct spectral_samp_msg));
310 		buf = NLMSG_DATA(spectral_nlh);
311 	} else if (buf_type == SPECTRAL_MSG_BUF_SAVED) {
312 		QDF_ASSERT(ps->skb[smsg_type]);
313 		spectral_nlh = (struct nlmsghdr *)ps->skb[smsg_type]->data;
314 		buf = NLMSG_DATA(spectral_nlh);
315 	} else {
316 		osif_err("Failed to get spectral report buffer");
317 		buf = NULL;
318 	}
319 
320 	return buf;
321 }
322 
323 #if (KERNEL_VERSION(2, 6, 31) > LINUX_VERSION_CODE)
324 static inline void
os_if_init_spectral_skb_dst_pid(struct sk_buff * skb,struct pdev_spectral * ps)325 os_if_init_spectral_skb_dst_pid(
326 	struct sk_buff *skb,
327 	struct pdev_spectral *ps)
328 {
329 	NETLINK_CB(skb).dst_pid =
330 	    ps->spectral_pid;
331 }
332 #else
333 static inline void
os_if_init_spectral_skb_dst_pid(struct sk_buff * skb,struct pdev_spectral * ps)334 os_if_init_spectral_skb_dst_pid(
335 	struct sk_buff *skb,
336 	struct pdev_spectral *ps)
337 {
338 }
339 #endif			/* VERSION - field deprecated by newer kernels */
340 
341 #if KERNEL_VERSION(3, 7, 0) > LINUX_VERSION_CODE
342 static inline void
os_if_init_spectral_skb_pid_portid(struct sk_buff * skb)343 os_if_init_spectral_skb_pid_portid(struct sk_buff *skb)
344 {
345 	NETLINK_CB(skb).pid = 0;  /* from kernel */
346 }
347 #else
348 static inline void
os_if_init_spectral_skb_pid_portid(struct sk_buff * skb)349 os_if_init_spectral_skb_pid_portid(struct sk_buff *skb)
350 {
351 	NETLINK_CB(skb).portid = 0;  /* from kernel */
352 }
353 #endif
354 
355 
356 /**
357  * os_if_spectral_nl_unicast_msg() - Sends unicast Spectral message to user
358  * space
359  * @pdev : Pointer to pdev
360  * @smsg_type: Spectral message type
361  *
362  * Return: void
363  */
364 #ifndef CNSS_GENL
365 static int
os_if_spectral_nl_unicast_msg(struct wlan_objmgr_pdev * pdev,enum spectral_msg_type smsg_type)366 os_if_spectral_nl_unicast_msg(struct wlan_objmgr_pdev *pdev,
367 			      enum spectral_msg_type smsg_type)
368 {
369 	struct pdev_spectral *ps = NULL;
370 	int status;
371 
372 	if (!pdev) {
373 		osif_err("PDEV is NULL!");
374 		return -EINVAL;
375 	}
376 
377 	if (smsg_type >= SPECTRAL_MSG_TYPE_MAX) {
378 		osif_err("Invalid Spectral message type %u", smsg_type);
379 		return -EINVAL;
380 	}
381 
382 	ps = wlan_objmgr_pdev_get_comp_private_obj(pdev,
383 						   WLAN_UMAC_COMP_SPECTRAL);
384 	if (!ps) {
385 		osif_err("PDEV SPECTRAL object is NULL!");
386 		return -EINVAL;
387 	}
388 
389 	if (!ps->skb[smsg_type]) {
390 		osif_err("Socket buffer is null, msg_type= %u", smsg_type);
391 		return -EINVAL;
392 	}
393 
394 	if (!ps->spectral_sock) {
395 		osif_err("Spectral Socket is invalid, msg_type= %u",
396 			 smsg_type);
397 		qdf_nbuf_free(ps->skb[smsg_type]);
398 		ps->skb[smsg_type] = NULL;
399 
400 		return -EINVAL;
401 	}
402 
403 	os_if_init_spectral_skb_dst_pid(ps->skb[smsg_type], ps);
404 
405 	os_if_init_spectral_skb_pid_portid(ps->skb[smsg_type]);
406 
407 	/* to mcast group 1<<0 */
408 	NETLINK_CB(ps->skb[smsg_type]).dst_group = 0;
409 
410 	os_if_spectral_remove_nbuf_debug_entry(ps->skb[smsg_type]);
411 	status = netlink_unicast(ps->spectral_sock,
412 				 ps->skb[smsg_type],
413 				 ps->spectral_pid, MSG_DONTWAIT);
414 
415 	/* clear the local copy, free would be done by netlink layer */
416 	ps->skb[smsg_type] = NULL;
417 
418 	return status;
419 }
420 #else
421 
422 static int
os_if_spectral_nl_unicast_msg(struct wlan_objmgr_pdev * pdev,enum spectral_msg_type smsg_type)423 os_if_spectral_nl_unicast_msg(struct wlan_objmgr_pdev *pdev,
424 			      enum spectral_msg_type smsg_type)
425 {
426 	struct pdev_spectral *ps = NULL;
427 	int status;
428 
429 	if (!pdev) {
430 		osif_err("PDEV is NULL!");
431 		return -EINVAL;
432 	}
433 
434 	if (smsg_type >= SPECTRAL_MSG_TYPE_MAX) {
435 		osif_err("Invalid Spectral message type %u", smsg_type);
436 		return -EINVAL;
437 	}
438 
439 	ps = wlan_objmgr_pdev_get_comp_private_obj(pdev,
440 						   WLAN_UMAC_COMP_SPECTRAL);
441 	if (!ps) {
442 		osif_err("PDEV SPECTRAL object is NULL!");
443 		return -EINVAL;
444 	}
445 
446 	if (!ps->skb[smsg_type]) {
447 		osif_err("Socket buffer is null, msg_type= %u", smsg_type);
448 		return -EINVAL;
449 	}
450 
451 	os_if_init_spectral_skb_pid_portid(ps->skb[smsg_type]);
452 
453 	os_if_spectral_remove_nbuf_debug_entry(ps->skb[smsg_type]);
454 	status = nl_srv_ucast(ps->skb[smsg_type], ps->spectral_pid,
455 			      MSG_DONTWAIT, WLAN_NL_MSG_SPECTRAL_SCAN,
456 			      CLD80211_MCGRP_OEM_MSGS);
457 	if (status < 0)
458 		osif_err("failed to send to spectral scan app");
459 
460 	/* clear the local copy, free would be done by netlink layer */
461 	ps->skb[smsg_type] = NULL;
462 
463 	return status;
464 }
465 
466 #endif
467 
468 /**
469  * os_if_spectral_nl_bcast_msg() - Sends broadcast Spectral message to user
470  * space
471  * @pdev : Pointer to pdev
472  * @smsg_type: Spectral message type
473  *
474  * Return: void
475  */
476 static int
os_if_spectral_nl_bcast_msg(struct wlan_objmgr_pdev * pdev,enum spectral_msg_type smsg_type)477 os_if_spectral_nl_bcast_msg(struct wlan_objmgr_pdev *pdev,
478 			    enum spectral_msg_type smsg_type)
479 {
480 #if (KERNEL_VERSION(2, 6, 31) >= LINUX_VERSION_CODE)
481 	fd_set write_set;
482 #endif
483 	int status;
484 	struct pdev_spectral *ps = NULL;
485 
486 #if (KERNEL_VERSION(2, 6, 31) >= LINUX_VERSION_CODE)
487 	FD_ZERO(&write_set);
488 #endif
489 
490 	if (!pdev) {
491 		osif_err("PDEV is NULL!");
492 		return -EINVAL;
493 	}
494 
495 	if (smsg_type >= SPECTRAL_MSG_TYPE_MAX) {
496 		osif_err("Invalid Spectral message type %u", smsg_type);
497 		return -EINVAL;
498 	}
499 
500 	ps = wlan_objmgr_pdev_get_comp_private_obj(pdev,
501 						   WLAN_UMAC_COMP_SPECTRAL);
502 
503 	if (!ps) {
504 		osif_err("PDEV SPECTRAL object is NULL!");
505 		return -EINVAL;
506 	}
507 
508 	if (!ps->skb[smsg_type]) {
509 		osif_err("Socket buffer is null, msg_type= %u", smsg_type);
510 		return -EINVAL;
511 	}
512 
513 	if (!ps->spectral_sock) {
514 		qdf_nbuf_free(ps->skb[smsg_type]);
515 		ps->skb[smsg_type] = NULL;
516 
517 		return -EINVAL;
518 	}
519 
520 	os_if_spectral_remove_nbuf_debug_entry(ps->skb[smsg_type]);
521 	status = netlink_broadcast(ps->spectral_sock,
522 				   ps->skb[smsg_type],
523 				   0, 1, GFP_ATOMIC);
524 
525 	/* clear the local copy, free would be done by netlink layer */
526 	ps->skb[smsg_type] = NULL;
527 
528 	return status;
529 }
530 
531 /**
532  * os_if_spectral_free_skb() - Free spectral SAMP message skb
533  *
534  * @pdev : Pointer to pdev
535  * @smsg_type: Spectral message type
536  *
537  * Return: void
538  */
539 static void
os_if_spectral_free_skb(struct wlan_objmgr_pdev * pdev,enum spectral_msg_type smsg_type)540 os_if_spectral_free_skb(struct wlan_objmgr_pdev *pdev,
541 			enum spectral_msg_type smsg_type)
542 {
543 	struct pdev_spectral *ps = NULL;
544 
545 	if (!pdev) {
546 		osif_err("PDEV is NULL!");
547 		return;
548 	}
549 
550 	if (smsg_type >= SPECTRAL_MSG_TYPE_MAX) {
551 		osif_err("Invalid Spectral message type %u", smsg_type);
552 		return;
553 	}
554 
555 	ps = wlan_objmgr_pdev_get_comp_private_obj(pdev,
556 						   WLAN_UMAC_COMP_SPECTRAL);
557 
558 	if (!ps) {
559 		osif_err("PDEV SPECTRAL object is NULL!");
560 		return;
561 	}
562 
563 	if (!ps->skb[smsg_type]) {
564 		osif_info("Socket buffer is null, msg_type= %u", smsg_type);
565 		return;
566 	}
567 
568 	/* Free buffer */
569 	qdf_nbuf_free(ps->skb[smsg_type]);
570 
571 	/* clear the local copy */
572 	ps->skb[smsg_type] = NULL;
573 }
574 
575 void
os_if_spectral_netlink_init(struct wlan_objmgr_pdev * pdev)576 os_if_spectral_netlink_init(struct wlan_objmgr_pdev *pdev)
577 {
578 	struct spectral_nl_cb nl_cb = {0};
579 	struct spectral_context *sptrl_ctx;
580 
581 	if (!pdev) {
582 		osif_err("PDEV is NULL!");
583 		return;
584 	}
585 
586 	if (wlan_spectral_is_feature_disabled_pdev(pdev)) {
587 		osif_debug("Spectral feature is disabled");
588 		return;
589 	}
590 
591 	sptrl_ctx = spectral_get_spectral_ctx_from_pdev(pdev);
592 
593 	if (!sptrl_ctx) {
594 		osif_err("Spectral context is NULL!");
595 		return;
596 	}
597 
598 	os_if_spectral_init_nl(pdev);
599 
600 	/* Register Netlink handlers */
601 	nl_cb.get_sbuff = os_if_spectral_prep_skb;
602 	nl_cb.send_nl_bcast = os_if_spectral_nl_bcast_msg;
603 	nl_cb.send_nl_unicast = os_if_spectral_nl_unicast_msg;
604 	nl_cb.free_sbuff = os_if_spectral_free_skb;
605 	nl_cb.convert_to_phy_ch_width = wlan_spectral_get_phy_ch_width;
606 	nl_cb.convert_to_nl_ch_width = wlan_spectral_get_nl80211_chwidth;
607 
608 	if (sptrl_ctx->sptrlc_register_netlink_cb)
609 		sptrl_ctx->sptrlc_register_netlink_cb(pdev, &nl_cb);
610 }
611 qdf_export_symbol(os_if_spectral_netlink_init);
612 
os_if_spectral_netlink_deinit(struct wlan_objmgr_pdev * pdev)613 void os_if_spectral_netlink_deinit(struct wlan_objmgr_pdev *pdev)
614 {
615 	struct spectral_context *sptrl_ctx;
616 	enum spectral_msg_type msg_type = SPECTRAL_MSG_NORMAL_MODE;
617 
618 	if (!pdev) {
619 		osif_err("PDEV is NULL!");
620 		return;
621 	}
622 
623 	if (wlan_spectral_is_feature_disabled_pdev(pdev)) {
624 		osif_err("Spectral feature is disabled");
625 		return;
626 	}
627 
628 	sptrl_ctx = spectral_get_spectral_ctx_from_pdev(pdev);
629 
630 	if (!sptrl_ctx) {
631 		osif_err("Spectral context is NULL!");
632 		return;
633 	}
634 
635 	for (; msg_type < SPECTRAL_MSG_TYPE_MAX; msg_type++)
636 		os_if_spectral_free_skb(pdev, msg_type);
637 
638 	if (sptrl_ctx->sptrlc_deregister_netlink_cb)
639 		sptrl_ctx->sptrlc_deregister_netlink_cb(pdev);
640 
641 	os_if_spectral_destroy_netlink(pdev);
642 }
643 qdf_export_symbol(os_if_spectral_netlink_deinit);
644