xref: /haiku/src/libs/compat/freebsd_wlan/net80211/ieee80211_haiku.cpp (revision 1026b0a1a76dc88927bb8175c470f638dc5464ee)
1 /*
2  * Copyright 2009, Colin Günther, coling@gmx.de.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 
7 /*-
8  * Copyright (c) 2003-2009 Sam Leffler, Errno Consulting
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 
33 /*
34  * IEEE 802.11 support (Haiku-specific code)
35  */
36 
37 
38 #include "ieee80211_haiku.h"
39 
40 extern "C" {
41 #	include <sys/kernel.h>
42 #	include <sys/mbuf.h>
43 #	include <sys/bus.h>
44 #	include <sys/sockio.h>
45 
46 #	include <net/if.h>
47 #	include <net/if_media.h>
48 #	include <net/if_types.h>
49 #	include <net/if_var.h>
50 
51 #	include "ieee80211_var.h"
52 };
53 
54 #include <SupportDefs.h>
55 
56 #include <util/KMessage.h>
57 
58 #include <ether_driver.h>
59 #include <bosii_driver.h>
60 #include <net_notifications.h>
61 
62 #include <shared.h>
63 
64 
65 #define TRACE_WLAN
66 #ifdef TRACE_WLAN
67 #	define TRACE(x...) dprintf(x);
68 #else
69 #	define TRACE(x...) ;
70 #endif
71 
72 
73 #define	MC_ALIGN(m, len)									\
74 do {														\
75 	(m)->m_data += (MCLBYTES - (len)) &~ (sizeof(long) - 1);\
76 } while (/* CONSTCOND */ 0)
77 
78 
79 static net_notifications_module_info* sNotificationModule;
80 
81 
82 static struct ifnet*
83 get_ifnet(device_t device, int& i)
84 {
85 	int unit = device_get_unit(device);
86 
87 	for (i = 0; i < MAX_DEVICES; i++) {
88 		if (gDevices[i] != NULL && gDevices[i]->if_dunit == unit)
89 			return gDevices[i];
90 	}
91 
92 	return NULL;
93 }
94 
95 
96 status_t
97 init_wlan_stack(void)
98 {
99 	ieee80211_phy_init();
100 	ieee80211_auth_setup();
101 	ieee80211_ht_init();
102 
103 	get_module(NET_NOTIFICATIONS_MODULE_NAME,
104 		(module_info**)&sNotificationModule);
105 
106 	return B_OK;
107 }
108 
109 
110 void
111 uninit_wlan_stack(void)
112 {
113 	if (sNotificationModule != NULL)
114 		put_module(NET_NOTIFICATIONS_MODULE_NAME);
115 }
116 
117 
118 status_t
119 start_wlan(device_t device)
120 {
121 	int i;
122 	struct ifnet* ifp = get_ifnet(device, i);
123 	if (ifp == NULL)
124 		return B_BAD_VALUE;
125 
126 // TODO: review this and find a cleaner solution!
127 	// This ensures that the cloned device gets
128 	// the same index assigned as the base device
129 	// Resulting in the same device name
130 	// e.g.: /dev/net/atheros/0 instead of
131 	//       /dev/net/atheros/1
132 	gDevices[i] = NULL;
133 
134 	struct ieee80211com* ic = (ieee80211com*)ifp->if_l2com;
135 
136 	struct ieee80211vap* vap = ic->ic_vap_create(ic, "wlan",
137 		device_get_unit(device),
138 		IEEE80211_M_STA,		// mode
139 		0,						// flags
140 		NULL,					// BSSID
141 		IF_LLADDR(ifp));		// MAC address
142 
143 	if (vap == NULL) {
144 		gDevices[i] = ifp;
145 		return B_ERROR;
146 	}
147 
148 	// ic_vap_create() established that gDevices[i] links to vap->iv_ifp now
149 	KASSERT(gDevices[i] == vap->iv_ifp,
150 		("start_wlan: gDevices[i] != vap->iv_ifp"));
151 
152 	vap->iv_ifp->scan_done_sem = create_sem(0, "wlan scan done");
153 
154 	// We aren't connected to a WLAN, yet.
155 	if_link_state_change(vap->iv_ifp, LINK_STATE_DOWN);
156 
157 	dprintf("%s: wlan started.\n", __func__);
158 
159 	return B_OK;
160 }
161 
162 
163 status_t
164 stop_wlan(device_t device)
165 {
166 	int i;
167 	struct ifnet* ifp = get_ifnet(device, i);
168 	if (ifp == NULL)
169 		return B_BAD_VALUE;
170 
171 	if (ifp->if_type == IFT_IEEE80211) {
172 		// This happens when there was an error in starting the wlan before,
173 		// resulting in never creating a clone device
174 		return B_OK;
175 	}
176 
177 	delete_sem(ifp->scan_done_sem);
178 
179 	struct ieee80211vap* vap = (ieee80211vap*)ifp->if_softc;
180 	struct ieee80211com* ic = vap->iv_ic;
181 
182 	ic->ic_vap_delete(vap);
183 
184 	// ic_vap_delete freed gDevices[i]
185 	KASSERT(gDevices[i] == NULL, ("stop_wlan: gDevices[i] != NULL"));
186 
187 	// assign the base device ifp again
188 	gDevices[i] = ic->ic_ifp;
189 
190 	return B_OK;
191 }
192 
193 
194 status_t
195 wlan_control(void* cookie, uint32 op, void* arg, size_t length)
196 {
197 	struct ifnet* ifp = (struct ifnet*)cookie;
198 
199 	switch (op) {
200 		case BOSII_DEVICE:
201 			return B_OK;
202 
203 		case BOSII_DETECT_NETWORKS:
204 		{
205 			struct ieee80211req request;
206 			struct ieee80211_scan_req scanRequest;
207 
208 			if_printf(ifp, "%s: BOSII_DETECT_NETWORKS\n", __func__);
209 			memset(&scanRequest, 0, sizeof(scanRequest));
210 			scanRequest.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
211 				| IEEE80211_IOC_SCAN_NOPICK
212 				| IEEE80211_IOC_SCAN_ONCE;
213 			scanRequest.sr_duration = 10000; // 10 s
214 			scanRequest.sr_nssid = 0;
215 
216 			memset(&request, 0, sizeof(request));
217 			request.i_type = IEEE80211_IOC_SCAN_REQ;
218 			request.i_data = &scanRequest;
219 			request.i_len = sizeof(scanRequest);
220 
221 			ifp->if_ioctl(ifp, SIOCS80211, (caddr_t)&request);
222 
223 			acquire_sem_etc(ifp->scan_done_sem, 1, B_RELATIVE_TIMEOUT,
224 				10000000); // 10 s
225 
226 			return B_OK;
227 		}
228 
229 		case BOSII_GET_DETECTED_NETWORKS:
230 		{
231 			struct ieee80211req request;
232 			struct ifreq ifRequest;
233 			struct route_entry* networkRequest = &ifRequest.ifr_route;
234 
235 			if_printf(ifp, "%s: BOSII_GET_DETECTED_NETWORKS\n", __func__);
236 
237 			if (length < sizeof(struct ieee80211req_scan_result))
238 				return B_BAD_VALUE;
239 
240 			if (user_memcpy(&ifRequest, arg, sizeof(ifRequest)) < B_OK)
241 				return B_BAD_ADDRESS;
242 
243 			memset(&request, 0, sizeof(request));
244 			request.i_type = IEEE80211_IOC_SCAN_RESULTS;
245 			request.i_len = length;
246 			request.i_data = networkRequest->destination;
247 
248 			// After return value of request.i_data is copied into user
249 			// space, already.
250 			if (ifp->if_ioctl(ifp, SIOCG80211, (caddr_t)&request) < B_OK)
251 				return B_BAD_ADDRESS;
252 
253 			// Tell the user space how much data was copied
254 			networkRequest->mtu = request.i_len;
255 			if (user_memcpy(&((struct ifreq*)arg)->ifr_route.mtu,
256 				&networkRequest->mtu, sizeof(networkRequest->mtu)) < B_OK)
257 				return B_BAD_ADDRESS;
258 
259 			return B_OK;
260 		}
261 
262 		case BOSII_JOIN_NETWORK:
263 		{
264 			struct ieee80211req request;
265 			struct ifreq ifRequest;
266 			struct route_entry* networkRequest = &ifRequest.ifr_route;
267 			struct ieee80211req_scan_result network;
268 
269 			if_printf(ifp, "%s: BOSII_JOIN_NETWORK\n", __func__);
270 
271 			if (length < sizeof(struct ifreq))
272 				return B_BAD_VALUE;
273 
274 			if (user_memcpy(&ifRequest, arg, sizeof(ifRequest)) != B_OK
275 				|| user_memcpy(&network, networkRequest->source,
276 						sizeof(ieee80211req_scan_result)) != B_OK)
277 				return B_BAD_ADDRESS;
278 
279 			memset(&request, 0, sizeof(ieee80211req));
280 
281 			request.i_type = IEEE80211_IOC_SSID;
282 			request.i_val = 0;
283 			request.i_len = network.isr_ssid_len;
284 			request.i_data = (uint8*)networkRequest->source
285 				+ network.isr_ie_off;
286 			if (ifp->if_ioctl(ifp, SIOCS80211, (caddr_t)&request) < B_OK)
287 				return B_ERROR;
288 
289 			// wait for network join
290 
291 			return B_OK;
292 		}
293 
294 		case BOSII_GET_ASSOCIATED_NETWORK:
295 		{
296 			struct ieee80211req request;
297 			struct ifreq ifRequest;
298 			struct route_entry* networkRequest = &ifRequest.ifr_route;
299 
300 			if_printf(ifp, "%s: BOSII_GET_ASSOCIATED_NETWORK\n", __func__);
301 
302 			if (length < sizeof(struct ieee80211req_sta_req))
303 				return B_BAD_VALUE;
304 
305 			if (user_memcpy(&ifRequest, arg, sizeof(ifRequest)) < B_OK)
306 				return B_BAD_ADDRESS;
307 
308 			// Only want station information about associated network.
309 			memset(&request, 0, sizeof(request));
310 			request.i_type = IEEE80211_IOC_BSSID;
311 			request.i_len = IEEE80211_ADDR_LEN;
312 			request.i_data = ((struct ieee80211req_sta_req*)networkRequest->
313 					destination)->is_u.macaddr;
314 			if (ifp->if_ioctl(ifp, SIOCG80211, (caddr_t)&request) < B_OK)
315 				return B_BAD_ADDRESS;
316 
317 			request.i_type = IEEE80211_IOC_STA_INFO;
318 			request.i_len = length;
319 			request.i_data = networkRequest->destination;
320 
321 			// After return value of request.i_data is copied into user
322 			// space, already.
323 			if (ifp->if_ioctl(ifp, SIOCG80211, (caddr_t)&request) < B_OK)
324 				return B_BAD_ADDRESS;
325 
326 			// Tell the user space how much data was copied
327 			networkRequest->mtu = request.i_len;
328 			if (user_memcpy(&((struct ifreq*)arg)->ifr_route.mtu,
329 					&networkRequest->mtu, sizeof(networkRequest->mtu)) != B_OK)
330 				return B_BAD_ADDRESS;
331 
332 			return B_OK;
333 		}
334 
335 		case SIOCG80211:
336 		case SIOCS80211:
337 		{
338 			// Allowing FreeBSD based WLAN ioctls to pass, as those will become
339 			// the future Haiku WLAN ioctls anyway.
340 
341 			// FreeBSD drivers assume that the request structure has already
342 			// been copied into kernel space
343 			struct ieee80211req request;
344 			if (user_memcpy(&request, arg, sizeof(struct ieee80211req)) != B_OK)
345 				return B_BAD_ADDRESS;
346 
347 			TRACE("wlan_control: %ld, %d\n", op, request.i_type);
348 			status_t status = ifp->if_ioctl(ifp, op, (caddr_t)&request);
349 			if (status != B_OK)
350 				return status;
351 
352 			if (op == SIOCG80211 && user_memcpy(arg, &request,
353 					sizeof(struct ieee80211req)) != B_OK)
354 				return B_BAD_ADDRESS;
355 			return B_OK;
356 		}
357 
358 		case SIOCSIFFLAGS:
359 		case SIOCSIFMEDIA:
360 		case SIOCGIFMEDIA:
361 		case SIOCSIFMTU:
362 			// Requests that make it here always come from the kernel
363 			return ifp->if_ioctl(ifp, op, (caddr_t)arg);
364 	}
365 
366 	return B_BAD_VALUE;
367 }
368 
369 
370 status_t
371 wlan_close(void* cookie)
372 {
373 	struct ifnet* ifp = (struct ifnet*)cookie;
374 
375 	ifp->if_flags &= ~IFF_UP;
376 	ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
377 
378 	return release_sem_etc(ifp->scan_done_sem, 1, B_RELEASE_ALL);
379 }
380 
381 
382 status_t
383 wlan_if_l2com_alloc(void* data)
384 {
385 	struct ifnet* ifp = (struct ifnet*)data;
386 
387 	ifp->if_l2com = _kernel_malloc(sizeof(struct ieee80211com), M_ZERO);
388 	if (ifp->if_l2com == NULL)
389 		return B_NO_MEMORY;
390 	((struct ieee80211com*)(ifp->if_l2com))->ic_ifp = ifp;
391 	return B_OK;
392 }
393 
394 
395 void
396 get_random_bytes(void* p, size_t n)
397 {
398 	uint8_t* dp = (uint8_t*)p;
399 
400 	while (n > 0) {
401 		uint32_t v = arc4random();
402 		size_t nb = n > sizeof(uint32_t) ? sizeof(uint32_t) : n;
403 		bcopy(&v, dp, n > sizeof(uint32_t) ? sizeof(uint32_t) : n);
404 		dp += sizeof(uint32_t), n -= nb;
405 	}
406 }
407 
408 
409 struct mbuf*
410 ieee80211_getmgtframe(uint8_t** frm, int headroom, int pktlen)
411 {
412 	struct mbuf* m;
413 	u_int len;
414 
415 	len = roundup2(headroom + pktlen, 4);
416 	KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len));
417 	if (len < MINCLSIZE) {
418 		m = m_gethdr(M_NOWAIT, MT_DATA);
419 		if (m != NULL)
420 			MH_ALIGN(m, len);
421 	} else {
422 		m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
423 		if (m != NULL)
424 			MC_ALIGN(m, len);
425 	}
426 	if (m != NULL) {
427 		m->m_data += headroom;
428 		*frm = (uint8_t*)m->m_data;
429 	}
430 	return m;
431 }
432 
433 
434 /*
435  * Decrements the reference-counter and
436  * tests whether it became zero.
437  *
438  * @return 1 reference-counter became zero
439  * @return 0 reference-counter didn't became zero
440  */
441 int
442 ieee80211_node_dectestref(struct ieee80211_node* ni)
443 {
444 	// atomic_add returns old value
445 	return atomic_add((vint32*)&ni->ni_refcnt, -1) == 1;
446 }
447 
448 
449 void
450 ieee80211_drain_ifq(struct ifqueue* ifq)
451 {
452 	struct ieee80211_node* ni;
453 	struct mbuf* m;
454 
455 	for (;;) {
456 		IF_DEQUEUE(ifq, m);
457 		if (m == NULL)
458 			break;
459 
460 		ni = (struct ieee80211_node*)m->m_pkthdr.rcvif;
461 		KASSERT(ni != NULL, ("frame w/o node"));
462 		ieee80211_free_node(ni);
463 		m->m_pkthdr.rcvif = NULL;
464 
465 		m_freem(m);
466 	}
467 }
468 
469 
470 void
471 ieee80211_flush_ifq(struct ifqueue* ifq, struct ieee80211vap* vap)
472 {
473 	struct ieee80211_node* ni;
474 	struct mbuf* m;
475 	struct mbuf** mprev;
476 
477 	IF_LOCK(ifq);
478 	mprev = &ifq->ifq_head;
479 	while ((m = *mprev) != NULL) {
480 		ni = (struct ieee80211_node*)m->m_pkthdr.rcvif;
481 		if (ni != NULL && ni->ni_vap == vap) {
482 			*mprev = m->m_nextpkt;
483 				// remove from list
484 			ifq->ifq_len--;
485 
486 			m_freem(m);
487 			ieee80211_free_node(ni);
488 				// reclaim ref
489 		} else
490 			mprev = &m->m_nextpkt;
491 	}
492 	// recalculate tail ptr
493 	m = ifq->ifq_head;
494 	for (; m != NULL && m->m_nextpkt != NULL; m = m->m_nextpkt);
495 	ifq->ifq_tail = m;
496 	IF_UNLOCK(ifq);
497 }
498 
499 
500 int
501 ieee80211_add_callback(struct mbuf* m,
502 	void (*func)(struct ieee80211_node*, void*, int), void* arg)
503 {
504 	struct m_tag* mtag;
505 	struct ieee80211_cb* cb;
506 
507 	mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_CALLBACK,
508 		sizeof(struct ieee80211_cb), M_NOWAIT);
509 	if (mtag == NULL)
510 		return 0;
511 
512 	cb = (struct ieee80211_cb*)(mtag+1);
513 	cb->func = func;
514 	cb->arg = arg;
515 	m_tag_prepend(m, mtag);
516 	m->m_flags |= M_TXCB;
517 	return 1;
518 }
519 
520 
521 void
522 ieee80211_process_callback(struct ieee80211_node* ni, struct mbuf* m,
523 	int status)
524 {
525 	struct m_tag* mtag;
526 
527 	mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, NULL);
528 	if (mtag != NULL) {
529 		struct ieee80211_cb* cb = (struct ieee80211_cb*)(mtag+1);
530 		cb->func(ni, cb->arg, status);
531 	}
532 }
533 
534 
535 void
536 ieee80211_sysctl_vattach(struct ieee80211vap* vap)
537 {
538 	vap->iv_debug = IEEE80211_MSG_XRATE
539 		| IEEE80211_MSG_NODE
540 		| IEEE80211_MSG_ASSOC
541 		| IEEE80211_MSG_AUTH
542 		| IEEE80211_MSG_STATE
543 		| IEEE80211_MSG_POWER
544 		| IEEE80211_MSG_WME
545 		| IEEE80211_MSG_DOTH
546 		| IEEE80211_MSG_INACT
547 		| IEEE80211_MSG_ROAM
548 		| IEEE80211_MSG_RATECTL;
549 }
550 
551 
552 void
553 ieee80211_sysctl_vdetach(struct ieee80211vap* vap)
554 {
555 	dprintf("%s not implemented, yet.\n", __func__);
556 }
557 
558 
559 void
560 ieee80211_vap_destroy(struct ieee80211vap* vap)
561 {
562 	struct ieee80211com* ic = vap->iv_ic;
563 
564 	ic->ic_vap_delete(vap);
565 	dprintf("%s: done.\n", __func__);
566 }
567 
568 
569 void
570 ieee80211_load_module(const char* modname)
571 {
572 	dprintf("%s not implemented, yet: modname %s\n", __func__, modname);
573 }
574 
575 
576 void
577 ieee80211_notify_node_join(struct ieee80211_node* ni, int newassoc)
578 {
579 	struct ieee80211vap* vap = ni->ni_vap;
580 	struct ifnet* ifp = vap->iv_ifp;
581 
582 	if (ni == vap->iv_bss)
583 		if_link_state_change(ifp, LINK_STATE_UP);
584 
585 	TRACE("%s\n", __FUNCTION__);
586 
587 	if (sNotificationModule != NULL) {
588 		char messageBuffer[512];
589 		KMessage message;
590 		message.SetTo(messageBuffer, sizeof(messageBuffer), B_NETWORK_MONITOR);
591 		message.AddInt32("opcode", B_NETWORK_WLAN_JOINED);
592 		message.AddString("interface", ifp->device_name);
593 		// TODO: add data about the node
594 
595 		sNotificationModule->send_notification(&message);
596 	}
597 }
598 
599 
600 void
601 ieee80211_notify_node_leave(struct ieee80211_node* ni)
602 {
603 	struct ieee80211vap* vap = ni->ni_vap;
604 	struct ifnet* ifp = vap->iv_ifp;
605 
606 	if (ni == vap->iv_bss)
607 		if_link_state_change(ifp, LINK_STATE_DOWN);
608 
609 	TRACE("%s\n", __FUNCTION__);
610 
611 	if (sNotificationModule != NULL) {
612 		char messageBuffer[512];
613 		KMessage message;
614 		message.SetTo(messageBuffer, sizeof(messageBuffer), B_NETWORK_MONITOR);
615 		message.AddInt32("opcode", B_NETWORK_WLAN_LEFT);
616 		message.AddString("interface", ifp->device_name);
617 		// TODO: add data about the node
618 
619 		sNotificationModule->send_notification(&message);
620 	}
621 }
622 
623 
624 void
625 ieee80211_notify_scan_done(struct ieee80211vap* vap)
626 {
627 	release_sem_etc(vap->iv_ifp->scan_done_sem, 1,
628 		B_DO_NOT_RESCHEDULE | B_RELEASE_ALL);
629 
630 	TRACE("%s\n", __FUNCTION__);
631 
632 	if (sNotificationModule != NULL) {
633 		char messageBuffer[512];
634 		KMessage message;
635 		message.SetTo(messageBuffer, sizeof(messageBuffer), B_NETWORK_MONITOR);
636 		message.AddInt32("opcode", B_NETWORK_WLAN_SCANNED);
637 		message.AddString("interface", vap->iv_ifp->device_name);
638 
639 		sNotificationModule->send_notification(&message);
640 	}
641 }
642 
643 
644 void
645 ieee80211_notify_replay_failure(struct ieee80211vap* vap,
646 	const struct ieee80211_frame* wh, const struct ieee80211_key* k,
647 	u_int64_t rsc, int tid)
648 {
649 	dprintf("%s not implemented, yet.\n", __func__);
650 }
651 
652 
653 void
654 ieee80211_notify_michael_failure(struct ieee80211vap* vap,
655 	const struct ieee80211_frame* wh, u_int keyix)
656 {
657 	dprintf("%s not implemented, yet.\n", __func__);
658 }
659 
660 
661 void
662 ieee80211_notify_wds_discover(struct ieee80211_node* ni)
663 {
664 	dprintf("%s not implemented, yet.\n", __func__);
665 }
666 
667 
668 void
669 ieee80211_notify_csa(struct ieee80211com* ic,
670 	const struct ieee80211_channel* c, int mode, int count)
671 {
672 	dprintf("%s not implemented, yet.\n", __func__);
673 }
674 
675 
676 void
677 ieee80211_notify_radar(struct ieee80211com* ic,
678 	const struct ieee80211_channel* c)
679 {
680 	dprintf("%s not implemented, yet.\n", __func__);
681 }
682 
683 
684 void
685 ieee80211_notify_cac(struct ieee80211com* ic,
686 	const struct ieee80211_channel* c, enum ieee80211_notify_cac_event type)
687 {
688 	dprintf("%s not implemented, yet.\n", __func__);
689 }
690 
691 
692 void
693 ieee80211_notify_node_deauth(struct ieee80211_node* ni)
694 {
695 	dprintf("%s not implemented, yet.\n", __func__);
696 }
697 
698 
699 void
700 ieee80211_notify_node_auth(struct ieee80211_node* ni)
701 {
702 	dprintf("%s not implemented, yet.\n", __func__);
703 }
704 
705 
706 void
707 ieee80211_notify_country(struct ieee80211vap* vap,
708 	const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t cc[2])
709 {
710 	dprintf("%s not implemented, yet.\n", __func__);
711 }
712 
713 
714 void
715 ieee80211_notify_radio(struct ieee80211com* ic, int state)
716 {
717 	dprintf("%s not implemented, yet.\n", __func__);
718 }
719 
720 
721 void
722 ieee80211_sysctl_attach(struct ieee80211com* ic)
723 {
724 	dprintf("%s not implemented, yet.\n", __func__);
725 }
726 
727 
728 void
729 ieee80211_sysctl_detach(struct ieee80211com* ic)
730 {
731 	dprintf("%s not implemented, yet.\n", __func__);
732 }
733