1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2010-2011 Monthadar Al Jaberi, TerraNet AB 5 * All rights reserved. 6 * 7 * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer, 15 * without modification. 16 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 17 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 18 * redistribution must be conditioned upon including a substantially 19 * similar Disclaimer requirement for further binary redistribution. 20 * 21 * NO WARRANTY 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 25 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 26 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 27 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 30 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 32 * THE POSSIBILITY OF SUCH DAMAGES. 33 * 34 * $FreeBSD$ 35 */ 36 #include "if_wtapvar.h" 37 #include <sys/uio.h> /* uio struct */ 38 #include <sys/jail.h> 39 #include <net/if_var.h> 40 #include <net/vnet.h> 41 42 #include <net80211/ieee80211_ratectl.h> 43 #include "if_medium.h" 44 45 /* 46 * This _requires_ vimage to be useful. 47 */ 48 #ifndef VIMAGE 49 #error if_wtap requires VIMAGE. 50 #endif /* VIMAGE */ 51 52 /* device for IOCTL and read/write for debuggin purposes */ 53 /* Function prototypes */ 54 static d_open_t wtap_node_open; 55 static d_close_t wtap_node_close; 56 static d_write_t wtap_node_write; 57 static d_ioctl_t wtap_node_ioctl; 58 59 static struct cdevsw wtap_cdevsw = { 60 .d_version = D_VERSION, 61 .d_flags = 0, 62 .d_open = wtap_node_open, 63 .d_close = wtap_node_close, 64 .d_write = wtap_node_write, 65 .d_ioctl = wtap_node_ioctl, 66 .d_name = "wtapnode", 67 }; 68 69 static int 70 wtap_node_open(struct cdev *dev, int oflags, int devtype, struct thread *p) 71 { 72 73 int err = 0; 74 uprintf("Opened device \"echo\" successfully.\n"); 75 return(err); 76 } 77 78 static int 79 wtap_node_close(struct cdev *dev, int fflag, int devtype, struct thread *p) 80 { 81 82 uprintf("Closing device \"echo.\"\n"); 83 return(0); 84 } 85 86 static int 87 wtap_node_write(struct cdev *dev, struct uio *uio, int ioflag) 88 { 89 int err = 0; 90 struct mbuf *m; 91 struct wtap_softc *sc; 92 uint8_t buf[1024]; 93 struct epoch_tracker et; 94 int buf_len; 95 96 uprintf("write device %s \"echo.\"\n", devtoname(dev)); 97 buf_len = MIN(uio->uio_iov->iov_len, 1024); 98 err = copyin(uio->uio_iov->iov_base, buf, buf_len); 99 100 if (err != 0) { 101 uprintf("Write failed: bad address!\n"); 102 return (err); 103 } 104 105 MGETHDR(m, M_NOWAIT, MT_DATA); 106 m_copyback(m, 0, buf_len, buf); 107 108 NET_EPOCH_ENTER(et); 109 110 sc = (struct wtap_softc *)dev->si_drv1; 111 printf("wtap id = %d\n", sc->id); 112 wtap_inject(sc, m); 113 114 NET_EPOCH_EXIT(et); 115 116 return(err); 117 } 118 119 int 120 wtap_node_ioctl(struct cdev *dev, u_long cmd, caddr_t data, 121 int fflag, struct thread *td) 122 { 123 int error = 0; 124 125 switch(cmd) { 126 default: 127 DWTAP_PRINTF("Unknown WTAP IOCTL\n"); 128 error = EINVAL; 129 } 130 return error; 131 } 132 133 static int wtap_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 134 const struct ieee80211_bpf_params *params); 135 136 static int 137 wtap_medium_enqueue(struct wtap_vap *avp, struct mbuf *m) 138 { 139 140 return medium_transmit(avp->av_md, avp->id, m); 141 } 142 143 /* 144 * Intercept management frames to collect beacon rssi data 145 * and to do ibss merges. 146 */ 147 static void 148 wtap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, 149 int subtype, const struct ieee80211_rx_stats *stats, int rssi, int nf) 150 { 151 struct ieee80211vap *vap = ni->ni_vap; 152 #if 0 153 DWTAP_PRINTF("[%d] %s\n", myath_id(ni), __func__); 154 #endif 155 WTAP_VAP(vap)->av_recv_mgmt(ni, m, subtype, stats, rssi, nf); 156 } 157 158 static int 159 wtap_reset_vap(struct ieee80211vap *vap, u_long cmd) 160 { 161 162 DWTAP_PRINTF("%s\n", __func__); 163 return 0; 164 } 165 166 static void 167 wtap_beacon_update(struct ieee80211vap *vap, int item) 168 { 169 struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; 170 171 DWTAP_PRINTF("%s\n", __func__); 172 setbit(bo->bo_flags, item); 173 } 174 175 /* 176 * Allocate and setup an initial beacon frame. 177 */ 178 static int 179 wtap_beacon_alloc(struct wtap_softc *sc, struct ieee80211_node *ni) 180 { 181 struct ieee80211vap *vap = ni->ni_vap; 182 struct wtap_vap *avp = WTAP_VAP(vap); 183 184 DWTAP_PRINTF("[%s] %s\n", ether_sprintf(ni->ni_macaddr), __func__); 185 186 /* 187 * NB: the beacon data buffer must be 32-bit aligned; 188 * we assume the mbuf routines will return us something 189 * with this alignment (perhaps should assert). 190 */ 191 avp->beacon = ieee80211_beacon_alloc(ni); 192 if (avp->beacon == NULL) { 193 printf("%s: cannot get mbuf\n", __func__); 194 return ENOMEM; 195 } 196 callout_init(&avp->av_swba, 0); 197 avp->bf_node = ieee80211_ref_node(ni); 198 199 return 0; 200 } 201 202 static void 203 wtap_beacon_config(struct wtap_softc *sc, struct ieee80211vap *vap) 204 { 205 206 DWTAP_PRINTF("%s\n", __func__); 207 } 208 209 static void 210 wtap_beacon_intrp(void *arg) 211 { 212 struct wtap_vap *avp = arg; 213 struct ieee80211vap *vap = arg; 214 struct mbuf *m; 215 216 if (vap->iv_state < IEEE80211_S_RUN) { 217 DWTAP_PRINTF("Skip beacon, not running, state %d", vap->iv_state); 218 return ; 219 } 220 DWTAP_PRINTF("[%d] beacon intrp\n", avp->id); //burst mode 221 /* 222 * Update dynamic beacon contents. If this returns 223 * non-zero then we need to remap the memory because 224 * the beacon frame changed size (probably because 225 * of the TIM bitmap). 226 */ 227 m = m_dup(avp->beacon, M_NOWAIT); 228 if (ieee80211_beacon_update(avp->bf_node, m, 0)) { 229 printf("%s, need to remap the memory because the beacon frame" 230 " changed size.\n",__func__); 231 } 232 233 if (ieee80211_radiotap_active_vap(vap)) 234 ieee80211_radiotap_tx(vap, m); 235 236 #if 0 237 medium_transmit(avp->av_md, avp->id, m); 238 #endif 239 wtap_medium_enqueue(avp, m); 240 callout_schedule(&avp->av_swba, avp->av_bcinterval); 241 } 242 243 static int 244 wtap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 245 { 246 struct ieee80211com *ic = vap->iv_ic; 247 struct wtap_softc *sc = ic->ic_softc; 248 struct wtap_vap *avp = WTAP_VAP(vap); 249 struct ieee80211_node *ni = NULL; 250 int error; 251 252 DWTAP_PRINTF("%s\n", __func__); 253 254 ni = ieee80211_ref_node(vap->iv_bss); 255 /* 256 * Invoke the parent method to do net80211 work. 257 */ 258 error = avp->av_newstate(vap, nstate, arg); 259 if (error != 0) 260 goto bad; 261 262 if (nstate == IEEE80211_S_RUN) { 263 /* NB: collect bss node again, it may have changed */ 264 ieee80211_free_node(ni); 265 ni = ieee80211_ref_node(vap->iv_bss); 266 switch (vap->iv_opmode) { 267 case IEEE80211_M_MBSS: 268 error = wtap_beacon_alloc(sc, ni); 269 if (error != 0) 270 goto bad; 271 wtap_beacon_config(sc, vap); 272 callout_reset(&avp->av_swba, avp->av_bcinterval, 273 wtap_beacon_intrp, vap); 274 break; 275 default: 276 goto bad; 277 } 278 } else if (nstate == IEEE80211_S_INIT) { 279 callout_stop(&avp->av_swba); 280 } 281 ieee80211_free_node(ni); 282 return 0; 283 bad: 284 printf("%s: bad\n", __func__); 285 ieee80211_free_node(ni); 286 return error; 287 } 288 289 static void 290 wtap_bmiss(struct ieee80211vap *vap) 291 { 292 struct wtap_vap *avp = (struct wtap_vap *)vap; 293 294 DWTAP_PRINTF("%s\n", __func__); 295 avp->av_bmiss(vap); 296 } 297 298 static struct ieee80211vap * 299 wtap_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], 300 int unit, enum ieee80211_opmode opmode, int flags, 301 const uint8_t bssid[IEEE80211_ADDR_LEN], 302 const uint8_t mac[IEEE80211_ADDR_LEN]) 303 { 304 struct wtap_softc *sc = ic->ic_softc; 305 struct ieee80211vap *vap; 306 struct wtap_vap *avp; 307 int error; 308 struct ieee80211_node *ni; 309 310 DWTAP_PRINTF("%s\n", __func__); 311 312 avp = malloc(sizeof(struct wtap_vap), M_80211_VAP, M_WAITOK | M_ZERO); 313 avp->id = sc->id; 314 avp->av_md = sc->sc_md; 315 avp->av_bcinterval = msecs_to_ticks(BEACON_INTRERVAL + 100*sc->id); 316 vap = (struct ieee80211vap *) avp; 317 error = ieee80211_vap_setup(ic, vap, name, unit, IEEE80211_M_MBSS, 318 flags | IEEE80211_CLONE_NOBEACONS, bssid); 319 if (error) { 320 free(avp, M_80211_VAP); 321 return (NULL); 322 } 323 324 /* override various methods */ 325 avp->av_recv_mgmt = vap->iv_recv_mgmt; 326 vap->iv_recv_mgmt = wtap_recv_mgmt; 327 vap->iv_reset = wtap_reset_vap; 328 vap->iv_update_beacon = wtap_beacon_update; 329 avp->av_newstate = vap->iv_newstate; 330 vap->iv_newstate = wtap_newstate; 331 avp->av_bmiss = vap->iv_bmiss; 332 vap->iv_bmiss = wtap_bmiss; 333 334 /* complete setup */ 335 ieee80211_vap_attach(vap, ieee80211_media_change, 336 ieee80211_media_status, mac); 337 avp->av_dev = make_dev(&wtap_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 338 "%s", (const char *)vap->iv_ifp->if_xname); 339 avp->av_dev->si_drv1 = sc; 340 341 /* TODO this is a hack to force it to choose the rate we want */ 342 ni = ieee80211_ref_node(vap->iv_bss); 343 ni->ni_txrate = 130; 344 ieee80211_free_node(ni); 345 return vap; 346 } 347 348 static void 349 wtap_vap_delete(struct ieee80211vap *vap) 350 { 351 struct wtap_vap *avp = WTAP_VAP(vap); 352 353 DWTAP_PRINTF("%s\n", __func__); 354 destroy_dev(avp->av_dev); 355 callout_stop(&avp->av_swba); 356 ieee80211_vap_detach(vap); 357 free(avp, M_80211_VAP); 358 } 359 360 static void 361 wtap_parent(struct ieee80211com *ic) 362 { 363 struct wtap_softc *sc = ic->ic_softc; 364 365 if (ic->ic_nrunning > 0) { 366 sc->up = 1; 367 ieee80211_start_all(ic); 368 } else 369 sc->up = 0; 370 } 371 372 static void 373 wtap_scan_start(struct ieee80211com *ic) 374 { 375 376 #if 0 377 DWTAP_PRINTF("%s\n", __func__); 378 #endif 379 } 380 381 static void 382 wtap_scan_end(struct ieee80211com *ic) 383 { 384 385 #if 0 386 DWTAP_PRINTF("%s\n", __func__); 387 #endif 388 } 389 390 static void 391 wtap_set_channel(struct ieee80211com *ic) 392 { 393 394 #if 0 395 DWTAP_PRINTF("%s\n", __func__); 396 #endif 397 } 398 399 static int 400 wtap_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 401 const struct ieee80211_bpf_params *params) 402 { 403 #if 0 404 DWTAP_PRINTF("%s, %p\n", __func__, m); 405 #endif 406 struct ieee80211vap *vap = ni->ni_vap; 407 struct wtap_vap *avp = WTAP_VAP(vap); 408 409 if (ieee80211_radiotap_active_vap(vap)) { 410 ieee80211_radiotap_tx(vap, m); 411 } 412 if (m->m_flags & M_TXCB) 413 ieee80211_process_callback(ni, m, 0); 414 ieee80211_free_node(ni); 415 return wtap_medium_enqueue(avp, m); 416 } 417 418 void 419 wtap_inject(struct wtap_softc *sc, struct mbuf *m) 420 { 421 struct wtap_buf *bf = (struct wtap_buf *)malloc(sizeof(struct wtap_buf), 422 M_WTAP_RXBUF, M_NOWAIT | M_ZERO); 423 KASSERT(bf != NULL, ("could not allocated a new wtap_buf\n")); 424 bf->m = m; 425 426 mtx_lock(&sc->sc_mtx); 427 STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); 428 taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); 429 mtx_unlock(&sc->sc_mtx); 430 } 431 432 static void 433 wtap_rx_proc(void *arg, int npending) 434 { 435 struct epoch_tracker et; 436 struct wtap_softc *sc = (struct wtap_softc *)arg; 437 struct ieee80211com *ic = &sc->sc_ic; 438 struct mbuf *m; 439 struct ieee80211_node *ni; 440 struct wtap_buf *bf; 441 442 #if 0 443 DWTAP_PRINTF("%s\n", __func__); 444 #endif 445 446 for(;;) { 447 mtx_lock(&sc->sc_mtx); 448 bf = STAILQ_FIRST(&sc->sc_rxbuf); 449 if (bf == NULL) { 450 mtx_unlock(&sc->sc_mtx); 451 return; 452 } 453 STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list); 454 mtx_unlock(&sc->sc_mtx); 455 KASSERT(bf != NULL, ("wtap_buf is NULL\n")); 456 m = bf->m; 457 DWTAP_PRINTF("[%d] receiving m=%p\n", sc->id, bf->m); 458 if (m == NULL) { /* NB: shouldn't happen */ 459 ic_printf(ic, "%s: no mbuf!\n", __func__); 460 free(bf, M_WTAP_RXBUF); 461 return; 462 } 463 #if 0 464 ieee80211_dump_pkt(ic, mtod(m, caddr_t), 0,0,0); 465 #endif 466 467 /* 468 * Locate the node for sender, track state, and then 469 * pass the (referenced) node up to the 802.11 layer 470 * for its use. 471 */ 472 ni = ieee80211_find_rxnode_withkey(ic, 473 mtod(m, const struct ieee80211_frame_min *), 474 IEEE80211_KEYIX_NONE); 475 NET_EPOCH_ENTER(et); 476 if (ni != NULL) { 477 /* 478 * Sending station is known, dispatch directly. 479 */ 480 ieee80211_input(ni, m, 1<<7, 10); 481 ieee80211_free_node(ni); 482 } else { 483 ieee80211_input_all(ic, m, 1<<7, 10); 484 } 485 NET_EPOCH_EXIT(et); 486 487 /* The mbufs are freed by the Net80211 stack */ 488 free(bf, M_WTAP_RXBUF); 489 } 490 } 491 492 static void 493 wtap_newassoc(struct ieee80211_node *ni, int isnew) 494 { 495 496 DWTAP_PRINTF("%s\n", __func__); 497 } 498 499 /* 500 * Callback from the 802.11 layer to update WME parameters. 501 */ 502 static int 503 wtap_wme_update(struct ieee80211com *ic) 504 { 505 506 DWTAP_PRINTF("%s\n", __func__); 507 return 0; 508 } 509 510 static void 511 wtap_update_mcast(struct ieee80211com *ic) 512 { 513 514 DWTAP_PRINTF("%s\n", __func__); 515 } 516 517 static void 518 wtap_update_promisc(struct ieee80211com *ic) 519 { 520 521 DWTAP_PRINTF("%s\n", __func__); 522 } 523 524 static int 525 wtap_transmit(struct ieee80211com *ic, struct mbuf *m) 526 { 527 struct ieee80211_node *ni = 528 (struct ieee80211_node *) m->m_pkthdr.rcvif; 529 struct ieee80211vap *vap = ni->ni_vap; 530 struct wtap_vap *avp = WTAP_VAP(vap); 531 532 if(ni == NULL){ 533 printf("m->m_pkthdr.rcvif is NULL we cant radiotap_tx\n"); 534 }else{ 535 if (ieee80211_radiotap_active_vap(vap)) 536 ieee80211_radiotap_tx(vap, m); 537 } 538 if (m->m_flags & M_TXCB) 539 ieee80211_process_callback(ni, m, 0); 540 ieee80211_free_node(ni); 541 return wtap_medium_enqueue(avp, m); 542 } 543 544 static struct ieee80211_node * 545 wtap_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 546 { 547 struct ieee80211_node *ni; 548 549 DWTAP_PRINTF("%s\n", __func__); 550 551 ni = malloc(sizeof(struct ieee80211_node), M_80211_NODE, 552 M_NOWAIT|M_ZERO); 553 if (ni == NULL) 554 return (NULL); 555 556 ni->ni_txrate = 130; 557 return ni; 558 } 559 560 static void 561 wtap_node_free(struct ieee80211_node *ni) 562 { 563 struct ieee80211com *ic = ni->ni_ic; 564 struct wtap_softc *sc = ic->ic_softc; 565 566 DWTAP_PRINTF("%s\n", __func__); 567 sc->sc_node_free(ni); 568 } 569 570 int32_t 571 wtap_attach(struct wtap_softc *sc, const uint8_t *macaddr) 572 { 573 struct ieee80211com *ic = &sc->sc_ic; 574 575 DWTAP_PRINTF("%s\n", __func__); 576 577 sc->up = 0; 578 STAILQ_INIT(&sc->sc_rxbuf); 579 sc->sc_tq = taskqueue_create("wtap_taskq", M_NOWAIT | M_ZERO, 580 taskqueue_thread_enqueue, &sc->sc_tq); 581 taskqueue_start_threads(&sc->sc_tq, 1, PI_SOFT, "%s taskQ", sc->name); 582 NET_TASK_INIT(&sc->sc_rxtask, 0, wtap_rx_proc, sc); 583 584 ic->ic_softc = sc; 585 ic->ic_name = sc->name; 586 ic->ic_phytype = IEEE80211_T_DS; 587 ic->ic_opmode = IEEE80211_M_MBSS; 588 ic->ic_caps = IEEE80211_C_MBSS; 589 590 ic->ic_max_keyix = 128; /* A value read from Atheros ATH_KEYMAX */ 591 592 ic->ic_regdomain.regdomain = SKU_ETSI; 593 ic->ic_regdomain.country = CTRY_SWEDEN; 594 ic->ic_regdomain.location = 1; /* Indoors */ 595 ic->ic_regdomain.isocc[0] = 'S'; 596 ic->ic_regdomain.isocc[1] = 'E'; 597 598 ic->ic_nchans = 1; 599 ic->ic_channels[0].ic_flags = IEEE80211_CHAN_B; 600 ic->ic_channels[0].ic_freq = 2412; 601 602 IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr); 603 ieee80211_ifattach(ic); 604 605 /* override default methods */ 606 ic->ic_newassoc = wtap_newassoc; 607 ic->ic_wme.wme_update = wtap_wme_update; 608 ic->ic_vap_create = wtap_vap_create; 609 ic->ic_vap_delete = wtap_vap_delete; 610 ic->ic_raw_xmit = wtap_raw_xmit; 611 ic->ic_update_mcast = wtap_update_mcast; 612 ic->ic_update_promisc = wtap_update_promisc; 613 ic->ic_transmit = wtap_transmit; 614 ic->ic_parent = wtap_parent; 615 616 sc->sc_node_alloc = ic->ic_node_alloc; 617 ic->ic_node_alloc = wtap_node_alloc; 618 sc->sc_node_free = ic->ic_node_free; 619 ic->ic_node_free = wtap_node_free; 620 621 ic->ic_scan_start = wtap_scan_start; 622 ic->ic_scan_end = wtap_scan_end; 623 ic->ic_set_channel = wtap_set_channel; 624 625 ieee80211_radiotap_attach(ic, 626 &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), 627 WTAP_TX_RADIOTAP_PRESENT, 628 &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), 629 WTAP_RX_RADIOTAP_PRESENT); 630 631 /* Work here, we must find a way to populate the rate table */ 632 #if 0 633 if(ic->ic_rt == NULL){ 634 printf("no table for ic_curchan\n"); 635 ic->ic_rt = ieee80211_get_ratetable(&ic->ic_channels[0]); 636 } 637 printf("ic->ic_rt =%p\n", ic->ic_rt); 638 printf("rate count %d\n", ic->ic_rt->rateCount); 639 640 uint8_t code = ic->ic_rt->info[0].dot11Rate; 641 uint8_t cix = ic->ic_rt->info[0].ctlRateIndex; 642 uint8_t ctl_rate = ic->ic_rt->info[cix].dot11Rate; 643 printf("code=%d, cix=%d, ctl_rate=%d\n", code, cix, ctl_rate); 644 645 uint8_t rix0 = ic->ic_rt->rateCodeToIndex[130]; 646 uint8_t rix1 = ic->ic_rt->rateCodeToIndex[132]; 647 uint8_t rix2 = ic->ic_rt->rateCodeToIndex[139]; 648 uint8_t rix3 = ic->ic_rt->rateCodeToIndex[150]; 649 printf("rix0 %u,rix1 %u,rix2 %u,rix3 %u\n", rix0,rix1,rix2,rix3); 650 printf("lpAckDuration=%u\n", ic->ic_rt->info[0].lpAckDuration); 651 printf("rate=%d\n", ic->ic_rt->info[0].rateKbps); 652 #endif 653 return 0; 654 } 655 656 int32_t 657 wtap_detach(struct wtap_softc *sc) 658 { 659 struct ieee80211com *ic = &sc->sc_ic; 660 661 DWTAP_PRINTF("%s\n", __func__); 662 ieee80211_ageq_drain(&ic->ic_stageq); 663 ieee80211_ifdetach(ic); 664 return 0; 665 } 666 667 void 668 wtap_resume(struct wtap_softc *sc) 669 { 670 671 DWTAP_PRINTF("%s\n", __func__); 672 } 673 674 void 675 wtap_suspend(struct wtap_softc *sc) 676 { 677 678 DWTAP_PRINTF("%s\n", __func__); 679 } 680 681 void 682 wtap_shutdown(struct wtap_softc *sc) 683 { 684 685 DWTAP_PRINTF("%s\n", __func__); 686 } 687 688 void 689 wtap_intr(struct wtap_softc *sc) 690 { 691 692 DWTAP_PRINTF("%s\n", __func__); 693 } 694