1/*- 2 * Copyright (c) 2021-2022 The FreeBSD Foundation 3 * 4 * This software was developed by Bj��rn Zeeb under sponsorship from 5 * the FreeBSD Foundation. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/param.h> 30#include <sys/types.h> 31#include <sys/kernel.h> 32#include <sys/errno.h> 33 34#define LINUXKPI_NET80211 35#include <net/mac80211.h> 36 37#include "linux_80211.h" 38 39/* Could be a different tracing framework later. */ 40#ifdef LINUXKPI_DEBUG_80211 41#define LKPI_80211_TRACE_MO(fmt, ...) \ 42 if (linuxkpi_debug_80211 & D80211_TRACE_MO) \ 43 printf("LKPI_80211_TRACE_MO %s:%d: %d %d %u_" fmt "\n", \ 44 __func__, __LINE__, curcpu, curthread->td_tid, \ 45 (unsigned int)ticks, __VA_ARGS__) 46#else 47#define LKPI_80211_TRACE_MO(...) do { } while(0) 48#endif 49 50int 51lkpi_80211_mo_start(struct ieee80211_hw *hw) 52{ 53 struct lkpi_hw *lhw; 54 int error; 55 56 lhw = HW_TO_LHW(hw); 57 if (lhw->ops->start == NULL) { 58 error = EOPNOTSUPP; 59 goto out; 60 } 61 62 if ((lhw->sc_flags & LKPI_MAC80211_DRV_STARTED)) { 63 /* Trying to start twice is an error. */ 64 error = EEXIST; 65 goto out; 66 } 67 LKPI_80211_TRACE_MO("hw %p", hw); 68 error = lhw->ops->start(hw); 69 if (error == 0) 70 lhw->sc_flags |= LKPI_MAC80211_DRV_STARTED; 71 72out: 73 return (error); 74} 75 76void 77lkpi_80211_mo_stop(struct ieee80211_hw *hw) 78{ 79 struct lkpi_hw *lhw; 80 81 lhw = HW_TO_LHW(hw); 82 if (lhw->ops->stop == NULL) 83 return; 84 85 LKPI_80211_TRACE_MO("hw %p", hw); 86 lhw->ops->stop(hw); 87 lhw->sc_flags &= ~LKPI_MAC80211_DRV_STARTED; 88} 89 90int 91lkpi_80211_mo_get_antenna(struct ieee80211_hw *hw, u32 *txs, u32 *rxs) 92{ 93 struct lkpi_hw *lhw; 94 int error; 95 96 lhw = HW_TO_LHW(hw); 97 if (lhw->ops->get_antenna == NULL) { 98 error = EOPNOTSUPP; 99 goto out; 100 } 101 102 LKPI_80211_TRACE_MO("hw %p", hw); 103 error = lhw->ops->get_antenna(hw, txs, rxs); 104 105out: 106 return (error); 107} 108 109int 110lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw *hw, uint32_t frag_th) 111{ 112 struct lkpi_hw *lhw; 113 int error; 114 115 lhw = HW_TO_LHW(hw); 116 if (lhw->ops->set_frag_threshold == NULL) { 117 error = EOPNOTSUPP; 118 goto out; 119 } 120 121 LKPI_80211_TRACE_MO("hw %p frag_th %u", hw, frag_th); 122 error = lhw->ops->set_frag_threshold(hw, frag_th); 123 124out: 125 return (error); 126} 127 128int 129lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *hw, uint32_t rts_th) 130{ 131 struct lkpi_hw *lhw; 132 int error; 133 134 lhw = HW_TO_LHW(hw); 135 if (lhw->ops->set_rts_threshold == NULL) { 136 error = EOPNOTSUPP; 137 goto out; 138 } 139 140 LKPI_80211_TRACE_MO("hw %p rts_th %u", hw, rts_th); 141 error = lhw->ops->set_rts_threshold(hw, rts_th); 142 143out: 144 return (error); 145} 146 147 148int 149lkpi_80211_mo_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 150{ 151 struct lkpi_hw *lhw; 152 struct lkpi_vif *lvif; 153 int error; 154 155 lhw = HW_TO_LHW(hw); 156 if (lhw->ops->add_interface == NULL) { 157 error = EOPNOTSUPP; 158 goto out; 159 } 160 161 lvif = VIF_TO_LVIF(vif); 162 LKPI_80211_LVIF_LOCK(lvif); 163 if (lvif->added_to_drv) { 164 LKPI_80211_LVIF_UNLOCK(lvif); 165 /* Trying to add twice is an error. */ 166 error = EEXIST; 167 goto out; 168 } 169 LKPI_80211_LVIF_UNLOCK(lvif); 170 171 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 172 error = lhw->ops->add_interface(hw, vif); 173 if (error == 0) { 174 LKPI_80211_LVIF_LOCK(lvif); 175 lvif->added_to_drv = true; 176 LKPI_80211_LVIF_UNLOCK(lvif); 177 } 178 179out: 180 return (error); 181} 182 183void 184lkpi_80211_mo_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 185{ 186 struct lkpi_hw *lhw; 187 struct lkpi_vif *lvif; 188 189 lhw = HW_TO_LHW(hw); 190 if (lhw->ops->remove_interface == NULL) 191 return; 192 193 lvif = VIF_TO_LVIF(vif); 194 LKPI_80211_LVIF_LOCK(lvif); 195 if (!lvif->added_to_drv) { 196 LKPI_80211_LVIF_UNLOCK(lvif); 197 return; 198 } 199 LKPI_80211_LVIF_UNLOCK(lvif); 200 201 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 202 lhw->ops->remove_interface(hw, vif); 203 LKPI_80211_LVIF_LOCK(lvif); 204 lvif->added_to_drv = false; 205 LKPI_80211_LVIF_UNLOCK(lvif); 206} 207 208 209int 210lkpi_80211_mo_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 211 struct ieee80211_scan_request *sr) 212{ 213 struct lkpi_hw *lhw; 214 int error; 215 216 /* 217 * MUST NOT return EPERM as that is a "magic number 1" based on rtw88 218 * driver indicating hw_scan is not supported despite the ops call 219 * being available. 220 */ 221 222 lhw = HW_TO_LHW(hw); 223 if (lhw->ops->hw_scan == NULL) { 224 /* Return magic number to use sw scan. */ 225 error = 1; 226 goto out; 227 } 228 229 LKPI_80211_TRACE_MO("CALLING hw %p vif %p sr %p", hw, vif, sr); 230 error = lhw->ops->hw_scan(hw, vif, sr); 231 LKPI_80211_TRACE_MO("RETURNING hw %p vif %p sr %p error %d", hw, vif, sr, error); 232 233out: 234 return (error); 235} 236 237void 238lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 239{ 240 struct lkpi_hw *lhw; 241 242 lhw = HW_TO_LHW(hw); 243 if (lhw->ops->cancel_hw_scan == NULL) 244 return; 245 246 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 247 lhw->ops->cancel_hw_scan(hw, vif); 248} 249 250void 251lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 252{ 253 struct lkpi_hw *lhw; 254 255 lhw = HW_TO_LHW(hw); 256 if (lhw->ops->sw_scan_complete == NULL) 257 return; 258 259 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 260 lhw->ops->sw_scan_complete(hw, vif); 261 lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING; 262} 263 264void 265lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 266 const u8 *addr) 267{ 268 struct lkpi_hw *lhw; 269 270 lhw = HW_TO_LHW(hw); 271 if (lhw->ops->sw_scan_start == NULL) 272 return; 273 274 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 275 lhw->ops->sw_scan_start(hw, vif, addr); 276} 277 278 279/* 280 * We keep the Linux type here; it really is an uintptr_t. 281 */ 282u64 283lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw, 284 struct netdev_hw_addr_list *mc_list) 285{ 286 struct lkpi_hw *lhw; 287 u64 ptr; 288 289 lhw = HW_TO_LHW(hw); 290 if (lhw->ops->prepare_multicast == NULL) 291 return (0); 292 293 LKPI_80211_TRACE_MO("hw %p mc_list %p", hw, mc_list); 294 ptr = lhw->ops->prepare_multicast(hw, mc_list); 295 return (ptr); 296} 297 298void 299lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, 300 unsigned int *total_flags, u64 mc_ptr) 301{ 302 struct lkpi_hw *lhw; 303 304 lhw = HW_TO_LHW(hw); 305 if (lhw->ops->configure_filter == NULL) 306 return; 307 308 if (mc_ptr == 0) 309 return; 310 311 LKPI_80211_TRACE_MO("hw %p changed_flags %#x total_flags %p mc_ptr %ju", hw, changed_flags, total_flags, (uintmax_t)mc_ptr); 312 lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr); 313} 314 315 316/* 317 * So far we only called sta_{add,remove} as an alternative to sta_state. 318 * Let's keep the implementation simpler and hide sta_{add,remove} under the 319 * hood here calling them if state_state is not available from mo_sta_state. 320 */ 321static int 322lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 323 struct ieee80211_sta *sta) 324{ 325 struct lkpi_hw *lhw; 326 struct lkpi_sta *lsta; 327 int error; 328 329 lhw = HW_TO_LHW(hw); 330 if (lhw->ops->sta_add == NULL) { 331 error = EOPNOTSUPP; 332 goto out; 333 } 334 335 lsta = STA_TO_LSTA(sta); 336 if (lsta->added_to_drv) { 337 error = EEXIST; 338 goto out; 339 } 340 341 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta); 342 error = lhw->ops->sta_add(hw, vif, sta); 343 if (error == 0) 344 lsta->added_to_drv = true; 345 346out: 347 return error; 348} 349 350static int 351lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 352 struct ieee80211_sta *sta) 353{ 354 struct lkpi_hw *lhw; 355 struct lkpi_sta *lsta; 356 int error; 357 358 lhw = HW_TO_LHW(hw); 359 if (lhw->ops->sta_remove == NULL) { 360 error = EOPNOTSUPP; 361 goto out; 362 } 363 364 lsta = STA_TO_LSTA(sta); 365 if (!lsta->added_to_drv) { 366 /* If we never added the sta, do not complain on cleanup. */ 367 error = 0; 368 goto out; 369 } 370 371 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta); 372 error = lhw->ops->sta_remove(hw, vif, sta); 373 if (error == 0) 374 lsta->added_to_drv = false; 375 376out: 377 return error; 378} 379 380int 381lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 382 struct lkpi_sta *lsta, enum ieee80211_sta_state nstate) 383{ 384 struct lkpi_hw *lhw; 385 struct ieee80211_sta *sta; 386 int error; 387 388 lhw = HW_TO_LHW(hw); 389 sta = LSTA_TO_STA(lsta); 390 if (lhw->ops->sta_state != NULL) { 391 LKPI_80211_TRACE_MO("hw %p vif %p sta %p nstate %d", hw, vif, sta, nstate); 392 error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate); 393 if (error == 0) { 394 if (nstate == IEEE80211_STA_NOTEXIST) 395 lsta->added_to_drv = false; 396 else 397 lsta->added_to_drv = true; 398 lsta->state = nstate; 399 } 400 goto out; 401 } 402 403 /* XXX-BZ is the change state AUTH or ASSOC here? */ 404 if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) { 405 error = lkpi_80211_mo_sta_add(hw, vif, sta); 406 if (error == 0) 407 lsta->added_to_drv = true; 408 } else if (lsta->state >= IEEE80211_STA_ASSOC && 409 nstate < IEEE80211_STA_ASSOC) { 410 error = lkpi_80211_mo_sta_remove(hw, vif, sta); 411 if (error == 0) 412 lsta->added_to_drv = false; 413 } else 414 /* Nothing to do. */ 415 error = 0; 416 if (error == 0) 417 lsta->state = nstate; 418 419out: 420 /* XXX-BZ should we manage state in here? */ 421 return (error); 422} 423 424int 425lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed) 426{ 427 struct lkpi_hw *lhw; 428 int error; 429 430 lhw = HW_TO_LHW(hw); 431 if (lhw->ops->config == NULL) { 432 error = EOPNOTSUPP; 433 goto out; 434 } 435 436 LKPI_80211_TRACE_MO("hw %p changed %u", hw, changed); 437 error = lhw->ops->config(hw, changed); 438 439out: 440 return (error); 441} 442 443 444int 445lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 446 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf) 447{ 448 struct lkpi_hw *lhw; 449 int error; 450 451 lhw = HW_TO_LHW(hw); 452 if (lhw->ops->assign_vif_chanctx == NULL) { 453 error = EOPNOTSUPP; 454 goto out; 455 } 456 457 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p", 458 hw, vif, conf, chanctx_conf); 459 error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf); 460 if (error == 0) 461 vif->chanctx_conf = chanctx_conf; 462 463out: 464 return (error); 465} 466 467void 468lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 469 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf **chanctx_conf) 470{ 471 struct lkpi_hw *lhw; 472 473 lhw = HW_TO_LHW(hw); 474 if (lhw->ops->unassign_vif_chanctx == NULL) 475 return; 476 477 if (*chanctx_conf == NULL) 478 return; 479 480 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p", 481 hw, vif, conf, *chanctx_conf); 482 lhw->ops->unassign_vif_chanctx(hw, vif, conf, *chanctx_conf); 483 *chanctx_conf = NULL; 484} 485 486 487int 488lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw, 489 struct ieee80211_chanctx_conf *chanctx_conf) 490{ 491 struct lkpi_hw *lhw; 492 struct lkpi_chanctx *lchanctx; 493 int error; 494 495 lhw = HW_TO_LHW(hw); 496 if (lhw->ops->add_chanctx == NULL) { 497 error = EOPNOTSUPP; 498 goto out; 499 } 500 501 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf); 502 error = lhw->ops->add_chanctx(hw, chanctx_conf); 503 if (error == 0) { 504 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); 505 lchanctx->added_to_drv = true; 506 } 507 508out: 509 return (error); 510} 511 512void 513lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw, 514 struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed) 515{ 516 struct lkpi_hw *lhw; 517 518 lhw = HW_TO_LHW(hw); 519 if (lhw->ops->change_chanctx == NULL) 520 return; 521 522 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed); 523 lhw->ops->change_chanctx(hw, chanctx_conf, changed); 524} 525 526void 527lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw, 528 struct ieee80211_chanctx_conf *chanctx_conf) 529{ 530 struct lkpi_hw *lhw; 531 struct lkpi_chanctx *lchanctx; 532 533 lhw = HW_TO_LHW(hw); 534 if (lhw->ops->remove_chanctx == NULL) 535 return; 536 537 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf); 538 lhw->ops->remove_chanctx(hw, chanctx_conf); 539 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); 540 lchanctx->added_to_drv = false; 541} 542 543void 544lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 545 struct ieee80211_bss_conf *conf, uint64_t changed) 546{ 547 struct lkpi_hw *lhw; 548 549 lhw = HW_TO_LHW(hw); 550 if (lhw->ops->link_info_changed == NULL && 551 lhw->ops->bss_info_changed == NULL) 552 return; 553 554 LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed); 555 if (lhw->ops->link_info_changed != NULL) 556 lhw->ops->link_info_changed(hw, vif, conf, changed); 557 else 558 lhw->ops->bss_info_changed(hw, vif, conf, changed); 559} 560 561int 562lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 563 uint32_t link_id, uint16_t ac, const struct ieee80211_tx_queue_params *txqp) 564{ 565 struct lkpi_hw *lhw; 566 int error; 567 568 lhw = HW_TO_LHW(hw); 569 if (lhw->ops->conf_tx == NULL) { 570 error = EOPNOTSUPP; 571 goto out; 572 } 573 574 LKPI_80211_TRACE_MO("hw %p vif %p link_id %u ac %u txpq %p", 575 hw, vif, link_id, ac, txqp); 576 error = lhw->ops->conf_tx(hw, vif, link_id, ac, txqp); 577 578out: 579 return (error); 580} 581 582void 583lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 584 uint32_t nqueues, bool drop) 585{ 586 struct lkpi_hw *lhw; 587 588 lhw = HW_TO_LHW(hw); 589 if (lhw->ops->flush == NULL) 590 return; 591 592 LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop); 593 lhw->ops->flush(hw, vif, nqueues, drop); 594} 595 596void 597lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 598 struct ieee80211_prep_tx_info *txinfo) 599{ 600 struct lkpi_hw *lhw; 601 602 lhw = HW_TO_LHW(hw); 603 if (lhw->ops->mgd_prepare_tx == NULL) 604 return; 605 606 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo); 607 lhw->ops->mgd_prepare_tx(hw, vif, txinfo); 608} 609 610void 611lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 612 struct ieee80211_prep_tx_info *txinfo) 613{ 614 struct lkpi_hw *lhw; 615 616 lhw = HW_TO_LHW(hw); 617 if (lhw->ops->mgd_complete_tx == NULL) 618 return; 619 620 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo); 621 lhw->ops->mgd_complete_tx(hw, vif, txinfo); 622} 623 624void 625lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl, 626 struct sk_buff *skb) 627{ 628 struct lkpi_hw *lhw; 629 630 lhw = HW_TO_LHW(hw); 631 if (lhw->ops->tx == NULL) 632 return; 633 634 LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb); 635 lhw->ops->tx(hw, txctrl, skb); 636} 637 638void 639lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) 640{ 641 struct lkpi_hw *lhw; 642 643 lhw = HW_TO_LHW(hw); 644 if (lhw->ops->wake_tx_queue == NULL) 645 return; 646 647 LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq); 648 lhw->ops->wake_tx_queue(hw, txq); 649} 650 651void 652lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw) 653{ 654 struct lkpi_hw *lhw; 655 656 lhw = HW_TO_LHW(hw); 657 if (lhw->ops->sync_rx_queues == NULL) 658 return; 659 660 LKPI_80211_TRACE_MO("hw %p", hw); 661 lhw->ops->sync_rx_queues(hw); 662} 663 664void 665lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw, 666 struct ieee80211_vif *vif, struct ieee80211_sta *sta) 667{ 668 struct lkpi_hw *lhw; 669 670 lhw = HW_TO_LHW(hw); 671 if (lhw->ops->sta_pre_rcu_remove == NULL) 672 return; 673 674 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta); 675 lhw->ops->sta_pre_rcu_remove(hw, vif, sta); 676} 677 678int 679lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 680 struct ieee80211_vif *vif, struct ieee80211_sta *sta, 681 struct ieee80211_key_conf *kc) 682{ 683 struct lkpi_hw *lhw; 684 int error; 685 686 lhw = HW_TO_LHW(hw); 687 if (lhw->ops->set_key == NULL) { 688 error = EOPNOTSUPP; 689 goto out; 690 } 691 692 LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc); 693 error = lhw->ops->set_key(hw, cmd, vif, sta, kc); 694 695out: 696 return (error); 697} 698 699int 700lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 701 struct ieee80211_ampdu_params *params) 702{ 703 struct lkpi_hw *lhw; 704 int error; 705 706 lhw = HW_TO_LHW(hw); 707 if (lhw->ops->ampdu_action == NULL) { 708 error = EOPNOTSUPP; 709 goto out; 710 } 711 712 LKPI_80211_TRACE_MO("hw %p vif %p params %p { %p, %d, %u, %u, %u, %u, %d }", 713 hw, vif, params, params->sta, params->action, params->buf_size, 714 params->timeout, params->ssn, params->tid, params->amsdu); 715 error = lhw->ops->ampdu_action(hw, vif, params); 716 717out: 718 return (error); 719} 720