xref: /wlan-driver/qcacld-3.0/components/pmo/core/src/wlan_pmo_ns.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1 /*
2  * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /**
21  * DOC: Implements ns offload feature API's
22  */
23 
24 #include "wlan_pmo_ns.h"
25 #include "wlan_pmo_tgt_api.h"
26 #include "wlan_pmo_main.h"
27 #include "wlan_pmo_obj_mgmt_public_struct.h"
28 
pmo_core_fill_ns_addr(struct pmo_ns_offload_params * request,struct pmo_ns_req * ns_req)29 static void pmo_core_fill_ns_addr(struct pmo_ns_offload_params *request,
30 				  struct pmo_ns_req *ns_req)
31 {
32 	int i;
33 
34 	for (i = 0; i < ns_req->count; i++) {
35 		/*
36 		 * Filling up the request structure
37 		 * Filling the selfIPv6Addr with solicited address
38 		 * A Solicited-Node multicast address is created by
39 		 * taking the last 24 bits of a unicast or anycast
40 		 * address and appending them to the prefix
41 		 *
42 		 * FF02:0000:0000:0000:0000:0001:FFXX:XXXX
43 		 *
44 		 * here XX is the unicast/anycast bits
45 		 */
46 		request->self_ipv6_addr[i][0] = 0xFF;
47 		request->self_ipv6_addr[i][1] = 0x02;
48 		request->self_ipv6_addr[i][11] = 0x01;
49 		request->self_ipv6_addr[i][12] = 0xFF;
50 		request->self_ipv6_addr[i][13] =
51 					ns_req->ipv6_addr[i][13];
52 		request->self_ipv6_addr[i][14] =
53 					ns_req->ipv6_addr[i][14];
54 		request->self_ipv6_addr[i][15] =
55 					ns_req->ipv6_addr[i][15];
56 		request->slot_idx = i;
57 		qdf_mem_copy(&request->target_ipv6_addr[i],
58 			&ns_req->ipv6_addr[i][0], QDF_IPV6_ADDR_SIZE);
59 
60 		request->target_ipv6_addr_valid[i] =
61 			PMO_IPV6_ADDR_VALID;
62 		request->target_ipv6_addr_ac_type[i] =
63 			ns_req->ipv6_addr_type[i];
64 
65 		request->scope[i] = ns_req->scope[i];
66 
67 		pmo_debug("NSoffload solicitIp: %pI6 targetIp: %pI6 Index: %d",
68 			&request->self_ipv6_addr[i],
69 			&request->target_ipv6_addr[i], i);
70 	}
71 }
72 
pmo_core_cache_ns_in_vdev_priv(struct pmo_ns_req * ns_req,struct wlan_objmgr_vdev * vdev)73 static QDF_STATUS pmo_core_cache_ns_in_vdev_priv(
74 			struct pmo_ns_req *ns_req,
75 			struct wlan_objmgr_vdev *vdev)
76 {
77 	QDF_STATUS status = QDF_STATUS_SUCCESS;
78 	struct pmo_vdev_priv_obj *vdev_ctx;
79 	struct pmo_ns_offload_params *request;
80 	struct wlan_objmgr_peer *peer;
81 	uint8_t *self_addr, *peer_addr;
82 
83 	vdev_ctx = pmo_vdev_get_priv(vdev);
84 
85 	request = qdf_mem_malloc(sizeof(*request));
86 	if (!request) {
87 		pmo_err("malloc failed for offload params");
88 		return QDF_STATUS_E_NOMEM;
89 	}
90 
91 	pmo_core_fill_ns_addr(request, ns_req);
92 
93 	request->enable = PMO_OFFLOAD_ENABLE;
94 	request->is_offload_applied = false;
95 
96 	/* set number of ns offload address count */
97 	request->num_ns_offload_count = ns_req->count;
98 
99 	peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_PMO_ID);
100 	if (!peer) {
101 		pmo_err("peer is null");
102 		status = QDF_STATUS_E_INVAL;
103 		goto out;
104 	}
105 
106 	if (wlan_vdev_mlme_is_mlo_vdev(vdev)) {
107 		self_addr = wlan_vdev_mlme_get_mldaddr(vdev);
108 		peer_addr = wlan_peer_mlme_get_mldaddr(peer);
109 	} else {
110 		self_addr = wlan_vdev_mlme_get_macaddr(vdev);
111 		peer_addr = wlan_peer_get_macaddr(peer);
112 	}
113 
114 	pmo_debug("vdev self mac addr: "QDF_MAC_ADDR_FMT" bss peer mac addr: "QDF_MAC_ADDR_FMT,
115 		QDF_MAC_ADDR_REF(self_addr),
116 		QDF_MAC_ADDR_REF(peer_addr));
117 
118 	qdf_mem_copy(&request->self_macaddr.bytes, self_addr,
119 		     QDF_MAC_ADDR_SIZE);
120 	/* get peer and peer mac accdress aka ap mac address */
121 	qdf_mem_copy(&request->bssid, peer_addr,
122 		     QDF_MAC_ADDR_SIZE);
123 	wlan_objmgr_peer_release_ref(peer, WLAN_PMO_ID);
124 	/* cache ns request */
125 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
126 	qdf_mem_copy(&vdev_ctx->vdev_ns_req, request,
127 		     sizeof(vdev_ctx->vdev_ns_req));
128 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
129 out:
130 	qdf_mem_free(request);
131 	return status;
132 }
133 
pmo_core_flush_ns_from_vdev_priv(struct wlan_objmgr_vdev * vdev)134 static QDF_STATUS pmo_core_flush_ns_from_vdev_priv(
135 		struct wlan_objmgr_vdev *vdev)
136 {
137 	struct pmo_vdev_priv_obj *vdev_ctx;
138 
139 	pmo_enter();
140 
141 	vdev_ctx = pmo_vdev_get_priv(vdev);
142 
143 	/* clear ns request */
144 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
145 	qdf_mem_zero(&vdev_ctx->vdev_ns_req, sizeof(vdev_ctx->vdev_ns_req));
146 	vdev_ctx->vdev_ns_req.enable = PMO_OFFLOAD_DISABLE;
147 	vdev_ctx->vdev_ns_req.is_offload_applied = false;
148 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
149 
150 	pmo_exit();
151 
152 	return QDF_STATUS_SUCCESS;
153 }
154 
pmo_core_do_enable_ns_offload(struct wlan_objmgr_vdev * vdev,uint8_t vdev_id,enum pmo_offload_trigger trigger)155 static QDF_STATUS pmo_core_do_enable_ns_offload(struct wlan_objmgr_vdev *vdev,
156 		uint8_t vdev_id, enum pmo_offload_trigger trigger)
157 {
158 	QDF_STATUS status = QDF_STATUS_SUCCESS;
159 	struct pmo_psoc_priv_obj *psoc_ctx;
160 	struct pmo_vdev_priv_obj *vdev_ctx;
161 
162 	vdev_ctx = pmo_vdev_get_priv(vdev);
163 
164 	psoc_ctx = vdev_ctx->pmo_psoc_ctx;
165 	if (!psoc_ctx) {
166 		pmo_err("psoc_ctx is NULL");
167 		status = QDF_STATUS_E_INVAL;
168 		goto out;
169 	}
170 
171 	switch (trigger) {
172 	case pmo_ipv6_change_notify:
173 	case pmo_ns_offload_dynamic_update:
174 		if (!psoc_ctx->psoc_cfg.active_mode_offload) {
175 			pmo_debug("active offload is disabled, skip in mode:%d",
176 				  trigger);
177 			goto out;
178 		}
179 		/* enable arp when active offload is true (ipv6 notifier) */
180 		status = pmo_tgt_enable_ns_offload_req(vdev, vdev_id);
181 		break;
182 	case pmo_apps_suspend:
183 	case pmo_arp_ns_offload_dynamic_update:
184 		/* enable arp when active offload is false (apps suspend) */
185 		status = pmo_tgt_enable_ns_offload_req(vdev, vdev_id);
186 		break;
187 	default:
188 		status = QDF_STATUS_E_INVAL;
189 		pmo_err("invalid pmo trigger");
190 		break;
191 	}
192 out:
193 
194 	return status;
195 }
196 
pmo_core_do_disable_ns_offload(struct wlan_objmgr_vdev * vdev,uint8_t vdev_id,enum pmo_offload_trigger trigger)197 static QDF_STATUS pmo_core_do_disable_ns_offload(struct wlan_objmgr_vdev *vdev,
198 		uint8_t vdev_id, enum pmo_offload_trigger trigger)
199 {
200 	QDF_STATUS status = QDF_STATUS_SUCCESS;
201 	struct pmo_psoc_priv_obj *psoc_ctx;
202 
203 	pmo_enter();
204 
205 	psoc_ctx = pmo_vdev_get_psoc_priv(vdev);
206 
207 	switch (trigger) {
208 	case pmo_ipv6_change_notify:
209 	case pmo_ns_offload_dynamic_update:
210 		if (!psoc_ctx->psoc_cfg.active_mode_offload) {
211 			pmo_debug("active offload is disabled, skip in mode:%d",
212 				  trigger);
213 			goto out;
214 		}
215 		/* config ns when active offload is enable */
216 		status = pmo_tgt_disable_ns_offload_req(vdev, vdev_id);
217 		break;
218 	case pmo_apps_resume:
219 	case pmo_arp_ns_offload_dynamic_update:
220 		status = pmo_tgt_disable_ns_offload_req(vdev, vdev_id);
221 		break;
222 	default:
223 		status = QDF_STATUS_E_INVAL;
224 		pmo_err("invalid pmo trigger");
225 		break;
226 	}
227 out:
228 	pmo_exit();
229 
230 	return status;
231 }
232 
233 
pmo_core_ns_offload_sanity(struct wlan_objmgr_vdev * vdev)234 static QDF_STATUS pmo_core_ns_offload_sanity(struct wlan_objmgr_vdev *vdev)
235 {
236 	struct pmo_vdev_priv_obj *vdev_ctx;
237 
238 	vdev_ctx = pmo_vdev_get_priv(vdev);
239 
240 	if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.ns_offload_enable_static) {
241 		pmo_debug("ns offload statically disable");
242 		return QDF_STATUS_E_INVAL;
243 	}
244 
245 	if (!pmo_core_is_vdev_supports_offload(vdev)) {
246 		pmo_debug("vdev in invalid opmode for ns offload %d",
247 			pmo_get_vdev_opmode(vdev));
248 		return QDF_STATUS_E_INVAL;
249 	}
250 
251 	if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS)
252 		return QDF_STATUS_E_INVAL;
253 
254 	return QDF_STATUS_SUCCESS;
255 }
256 
pmo_core_ns_check_offload(struct wlan_objmgr_psoc * psoc,enum pmo_offload_trigger trigger,uint8_t vdev_id)257 QDF_STATUS pmo_core_ns_check_offload(struct wlan_objmgr_psoc *psoc,
258 				     enum pmo_offload_trigger trigger,
259 				     uint8_t vdev_id)
260 {
261 	QDF_STATUS status = QDF_STATUS_SUCCESS;
262 	struct pmo_psoc_priv_obj *psoc_ctx;
263 	struct pmo_vdev_priv_obj *vdev_ctx;
264 	struct wlan_objmgr_vdev *vdev;
265 	bool active_offload_cond, is_applied_cond;
266 	enum QDF_OPMODE opmode;
267 
268 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, WLAN_PMO_ID);
269 	if (!vdev) {
270 		pmo_err("vdev is NULL");
271 		status = QDF_STATUS_E_INVAL;
272 		goto out;
273 	}
274 
275 	opmode = pmo_get_vdev_opmode(vdev);
276 	if (opmode == QDF_NDI_MODE) {
277 		pmo_debug("NS offload not supported in NaN mode");
278 		wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
279 		return QDF_STATUS_E_INVAL;
280 	}
281 
282 	if (wlan_vdev_mlme_get_is_mlo_link(psoc, vdev_id)) {
283 		pmo_debug("NS offload not supported for MLO partner link "
284 			  "with vdev_id[%d]", vdev_id);
285 		wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
286 		return QDF_STATUS_E_INVAL;
287 	}
288 
289 	vdev_ctx = pmo_vdev_get_priv(vdev);
290 	psoc_ctx = vdev_ctx->pmo_psoc_ctx;
291 
292 	if (trigger == pmo_apps_suspend || trigger == pmo_apps_resume) {
293 		active_offload_cond = psoc_ctx->psoc_cfg.active_mode_offload;
294 
295 		qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
296 		is_applied_cond = vdev_ctx->vdev_ns_req.enable &&
297 			       vdev_ctx->vdev_ns_req.is_offload_applied;
298 		qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
299 
300 		if (active_offload_cond && is_applied_cond) {
301 			pmo_debug("active offload is enabled and offload already sent");
302 			wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
303 			return QDF_STATUS_E_INVAL;
304 		}
305 	}
306 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
307 out:
308 	return status;
309 }
310 
pmo_core_cache_ns_offload_req(struct pmo_ns_req * ns_req)311 QDF_STATUS pmo_core_cache_ns_offload_req(struct pmo_ns_req *ns_req)
312 {
313 	QDF_STATUS status;
314 	struct wlan_objmgr_vdev *vdev;
315 
316 	if (!ns_req) {
317 		pmo_err("ns is NULL");
318 		status = QDF_STATUS_E_INVAL;
319 		goto out;
320 	}
321 
322 	if (!ns_req->psoc) {
323 		pmo_err("psoc is NULL");
324 		status = QDF_STATUS_E_INVAL;
325 		goto out;
326 	}
327 
328 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(ns_req->psoc,
329 						    ns_req->vdev_id,
330 						    WLAN_PMO_ID);
331 	if (!vdev) {
332 		pmo_err("vdev is NULL");
333 		status = QDF_STATUS_E_INVAL;
334 		goto out;
335 	}
336 
337 	status = pmo_core_ns_offload_sanity(vdev);
338 	if (status != QDF_STATUS_SUCCESS)
339 		goto dec_ref;
340 
341 	if (ns_req->count == 0) {
342 		pmo_debug("skip ns offload caching as ns count is 0");
343 		status = QDF_STATUS_E_INVAL;
344 		goto dec_ref;
345 	}
346 
347 	status = pmo_core_cache_ns_in_vdev_priv(ns_req, vdev);
348 dec_ref:
349 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
350 out:
351 
352 	return status;
353 }
354 
pmo_core_flush_ns_offload_req(struct wlan_objmgr_vdev * vdev)355 QDF_STATUS pmo_core_flush_ns_offload_req(struct wlan_objmgr_vdev *vdev)
356 {
357 	QDF_STATUS status;
358 	uint8_t vdev_id;
359 
360 	pmo_enter();
361 	if (!vdev) {
362 		pmo_err("vdev is NULL");
363 		status = QDF_STATUS_E_INVAL;
364 		goto out;
365 	}
366 
367 	status = pmo_vdev_get_ref(vdev);
368 	if (status != QDF_STATUS_SUCCESS)
369 		goto out;
370 
371 	status = pmo_core_ns_offload_sanity(vdev);
372 	if (status != QDF_STATUS_SUCCESS)
373 		goto dec_ref;
374 
375 	vdev_id = pmo_vdev_get_id(vdev);
376 	pmo_debug("Flush ns offload on vdev id: %d vdev: %pK", vdev_id, vdev);
377 
378 	status = pmo_core_flush_ns_from_vdev_priv(vdev);
379 dec_ref:
380 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
381 out:
382 	pmo_exit();
383 
384 	return status;
385 }
386 
pmo_core_enable_ns_offload_in_fwr(struct wlan_objmgr_vdev * vdev,enum pmo_offload_trigger trigger)387 QDF_STATUS pmo_core_enable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev,
388 		enum pmo_offload_trigger trigger)
389 {
390 	QDF_STATUS status;
391 	struct pmo_vdev_priv_obj *vdev_ctx;
392 	struct pmo_psoc_priv_obj *pmo_psoc_ctx;
393 	uint8_t vdev_id;
394 
395 	if (!vdev) {
396 		pmo_err("vdev is NULL");
397 		status = QDF_STATUS_E_INVAL;
398 		goto out;
399 	}
400 
401 	status = pmo_vdev_get_ref(vdev);
402 	if (status != QDF_STATUS_SUCCESS)
403 		goto out;
404 
405 	vdev_ctx = pmo_vdev_get_priv(vdev);
406 	pmo_psoc_ctx = vdev_ctx->pmo_psoc_ctx;
407 
408 	status = pmo_core_ns_offload_sanity(vdev);
409 	if (status != QDF_STATUS_SUCCESS)
410 		goto dec_ref;
411 
412 	if (trigger == pmo_ns_offload_dynamic_update) {
413 		/*
414 		 * user enable ns offload using ioctl/vendor cmd dynamically.
415 		 */
416 		pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic = true;
417 		goto skip_ns_dynamic_check;
418 	}
419 
420 	if (!pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic) {
421 		pmo_debug("ns offload dynamically disable");
422 		status = QDF_STATUS_E_INVAL;
423 		goto dec_ref;
424 	}
425 
426 skip_ns_dynamic_check:
427 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
428 	if (vdev_ctx->vdev_ns_req.num_ns_offload_count == 0) {
429 		qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
430 		pmo_debug("skip ns offload enable as ns count is 0");
431 		status = QDF_STATUS_E_INVAL;
432 		goto dec_ref;
433 	}
434 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
435 
436 	vdev_id = pmo_vdev_get_id(vdev);
437 	pmo_debug("Enable ns offload in fwr vdev id: %d vdev: %pK trigger: %d",
438 		vdev_id, vdev, trigger);
439 	status = pmo_core_do_enable_ns_offload(vdev, vdev_id, trigger);
440 dec_ref:
441 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
442 out:
443 
444 	return status;
445 }
446 
pmo_core_disable_ns_offload_in_fwr(struct wlan_objmgr_vdev * vdev,enum pmo_offload_trigger trigger)447 QDF_STATUS pmo_core_disable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev,
448 		enum pmo_offload_trigger trigger)
449 {
450 	QDF_STATUS status;
451 	uint8_t vdev_id;
452 	struct pmo_vdev_priv_obj *vdev_ctx;
453 	struct pmo_psoc_priv_obj *pmo_psoc_ctx;
454 
455 	pmo_enter();
456 	if (!vdev) {
457 		pmo_err("vdev is NULL");
458 		status = QDF_STATUS_E_INVAL;
459 		goto out;
460 	}
461 
462 	status = pmo_vdev_get_ref(vdev);
463 	if (status != QDF_STATUS_SUCCESS)
464 		goto out;
465 
466 	vdev_ctx = pmo_vdev_get_priv(vdev);
467 	pmo_psoc_ctx = vdev_ctx->pmo_psoc_ctx;
468 
469 	status = pmo_core_ns_offload_sanity(vdev);
470 	if (status != QDF_STATUS_SUCCESS)
471 		goto dec_ref;
472 
473 	if (trigger == pmo_ns_offload_dynamic_update) {
474 		/*
475 		 * user disable ns offload using ioctl/vendor cmd dynamically.
476 		 */
477 		if (!pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic) {
478 			/* Already disabled, skip to end */
479 			status = QDF_STATUS_SUCCESS;
480 			goto dec_ref;
481 		}
482 
483 		pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic = false;
484 		goto skip_ns_dynamic_check;
485 	}
486 
487 	if (!pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic) {
488 		pmo_debug("ns offload dynamically disable");
489 		status = QDF_STATUS_E_INVAL;
490 		goto dec_ref;
491 	}
492 
493 skip_ns_dynamic_check:
494 	vdev_id = pmo_vdev_get_id(vdev);
495 	pmo_debug("disable ns offload in fwr vdev id: %d vdev: %pK trigger: %d",
496 		vdev_id, vdev, trigger);
497 
498 	status = pmo_core_do_disable_ns_offload(vdev, vdev_id, trigger);
499 dec_ref:
500 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
501 out:
502 	pmo_exit();
503 
504 	return status;
505 }
506 
507 QDF_STATUS
pmo_core_get_ns_offload_params(struct wlan_objmgr_vdev * vdev,struct pmo_ns_offload_params * params)508 pmo_core_get_ns_offload_params(struct wlan_objmgr_vdev *vdev,
509 			       struct pmo_ns_offload_params *params)
510 {
511 	QDF_STATUS status;
512 	struct pmo_vdev_priv_obj *vdev_ctx;
513 
514 	pmo_enter();
515 
516 	if (!params)
517 		return QDF_STATUS_E_INVAL;
518 
519 	qdf_mem_zero(params, sizeof(*params));
520 
521 	if (!vdev) {
522 		pmo_err("vdev is NULL");
523 		status = QDF_STATUS_E_INVAL;
524 		goto out;
525 	}
526 
527 	status = pmo_vdev_get_ref(vdev);
528 	if (status != QDF_STATUS_SUCCESS)
529 		goto out;
530 
531 	vdev_ctx = pmo_vdev_get_priv(vdev);
532 
533 	status = pmo_core_ns_offload_sanity(vdev);
534 	if (status != QDF_STATUS_SUCCESS)
535 		goto dec_ref;
536 
537 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
538 	*params = vdev_ctx->vdev_ns_req;
539 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
540 
541 dec_ref:
542 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
543 out:
544 	pmo_exit();
545 
546 	return status;
547 }
548