Path: blob/main/sys/compat/linuxkpi/common/src/linux_80211_macops.c
102493 views
/*-1* Copyright (c) 2021-2026 The FreeBSD Foundation2*3* This software was developed by Björn Zeeb under sponsorship from4* the FreeBSD Foundation.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/param.h>29#include <sys/types.h>30#include <sys/kernel.h>31#include <sys/errno.h>3233#define LINUXKPI_NET8021134#include <net/mac80211.h>3536#include "linux_80211.h"3738/* Could be a different tracing framework later. */39#ifdef LINUXKPI_DEBUG_8021140#define LKPI_80211_TRACE_MO(fmt, ...) \41if (linuxkpi_debug_80211 & D80211_TRACE_MO) \42printf("LKPI_80211_TRACE_MO %s:%d: %d %d %lu: " fmt "\n", \43__func__, __LINE__, curcpu, curthread->td_tid, \44jiffies, ##__VA_ARGS__)45#else46#define LKPI_80211_TRACE_MO(...) do { } while(0)47#endif4849int50lkpi_80211_mo_start(struct ieee80211_hw *hw)51{52struct lkpi_hw *lhw;53int error;5455lockdep_assert_wiphy(hw->wiphy);5657lhw = HW_TO_LHW(hw);58if (lhw->ops->start == NULL) {59error = EOPNOTSUPP;60goto out;61}6263if ((lhw->sc_flags & LKPI_MAC80211_DRV_STARTED)) {64/* Trying to start twice is an error. */65error = EEXIST;66goto out;67}68LKPI_80211_TRACE_MO("hw %p", hw);69error = lhw->ops->start(hw);70if (error == 0)71lhw->sc_flags |= LKPI_MAC80211_DRV_STARTED;7273out:74return (error);75}7677void78lkpi_80211_mo_stop(struct ieee80211_hw *hw, bool suspend)79{80struct lkpi_hw *lhw;8182lhw = HW_TO_LHW(hw);83if (lhw->ops->stop == NULL)84return;8586LKPI_80211_TRACE_MO("hw %p suspend %d", hw, suspend);87lhw->ops->stop(hw, suspend);88lhw->sc_flags &= ~LKPI_MAC80211_DRV_STARTED;89}9091int92lkpi_80211_mo_get_antenna(struct ieee80211_hw *hw, u32 *txs, u32 *rxs)93{94struct lkpi_hw *lhw;95int error;9697lhw = HW_TO_LHW(hw);98if (lhw->ops->get_antenna == NULL) {99error = EOPNOTSUPP;100goto out;101}102103LKPI_80211_TRACE_MO("hw %p", hw);104LKPI_80211_TRACE_MO("TODO link/radio_idx");105error = lhw->ops->get_antenna(hw, 0, txs, rxs);106107out:108return (error);109}110111int112lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw *hw, uint32_t frag_th)113{114struct lkpi_hw *lhw;115int error;116117lhw = HW_TO_LHW(hw);118if (lhw->ops->set_frag_threshold == NULL) {119error = EOPNOTSUPP;120goto out;121}122123LKPI_80211_TRACE_MO("hw %p frag_th %u", hw, frag_th);124LKPI_80211_TRACE_MO("TODO link/radio_idx");125error = lhw->ops->set_frag_threshold(hw, 0, frag_th);126127out:128return (error);129}130131int132lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *hw, uint32_t rts_th)133{134struct lkpi_hw *lhw;135int error;136137lhw = HW_TO_LHW(hw);138if (lhw->ops->set_rts_threshold == NULL) {139error = EOPNOTSUPP;140goto out;141}142143LKPI_80211_TRACE_MO("hw %p rts_th %u", hw, rts_th);144LKPI_80211_TRACE_MO("TODO link/radio_idx");145error = lhw->ops->set_rts_threshold(hw, 0, rts_th);146147out:148return (error);149}150151152int153lkpi_80211_mo_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)154{155struct lkpi_hw *lhw;156struct lkpi_vif *lvif;157int error;158159lhw = HW_TO_LHW(hw);160if (lhw->ops->add_interface == NULL) {161error = EOPNOTSUPP;162goto out;163}164165lvif = VIF_TO_LVIF(vif);166LKPI_80211_LVIF_LOCK(lvif);167if (lvif->added_to_drv) {168LKPI_80211_LVIF_UNLOCK(lvif);169/* Trying to add twice is an error. */170error = EEXIST;171goto out;172}173LKPI_80211_LVIF_UNLOCK(lvif);174175LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);176error = lhw->ops->add_interface(hw, vif);177if (error == 0) {178LKPI_80211_LVIF_LOCK(lvif);179lvif->added_to_drv = true;180LKPI_80211_LVIF_UNLOCK(lvif);181}182183out:184return (error);185}186187void188lkpi_80211_mo_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)189{190struct lkpi_hw *lhw;191struct lkpi_vif *lvif;192193lhw = HW_TO_LHW(hw);194if (lhw->ops->remove_interface == NULL)195return;196197lvif = VIF_TO_LVIF(vif);198LKPI_80211_LVIF_LOCK(lvif);199if (!lvif->added_to_drv) {200LKPI_80211_LVIF_UNLOCK(lvif);201return;202}203LKPI_80211_LVIF_UNLOCK(lvif);204205LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);206lhw->ops->remove_interface(hw, vif);207LKPI_80211_LVIF_LOCK(lvif);208lvif->added_to_drv = false;209LKPI_80211_LVIF_UNLOCK(lvif);210}211212213int214lkpi_80211_mo_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,215struct ieee80211_scan_request *sr)216{217struct lkpi_hw *lhw;218int error;219220/*221* MUST NOT return EPERM as that is a "magic number 1" based on rtw88222* driver indicating hw_scan is not supported despite the ops call223* being available.224*/225226lhw = HW_TO_LHW(hw);227if (lhw->ops->hw_scan == NULL) {228/* Return magic number to use sw scan. */229error = 1;230goto out;231}232233LKPI_80211_TRACE_MO("CALLING hw %p vif %p sr %p", hw, vif, sr);234error = lhw->ops->hw_scan(hw, vif, sr);235LKPI_80211_TRACE_MO("RETURNING hw %p vif %p sr %p error %d", hw, vif, sr, error);236237out:238return (error);239}240241void242lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)243{244struct lkpi_hw *lhw;245246lhw = HW_TO_LHW(hw);247if (lhw->ops->cancel_hw_scan == NULL)248return;249250LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);251lhw->ops->cancel_hw_scan(hw, vif);252}253254void255lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)256{257struct lkpi_hw *lhw;258259lhw = HW_TO_LHW(hw);260if (lhw->ops->sw_scan_complete == NULL)261return;262263LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);264lhw->ops->sw_scan_complete(hw, vif);265lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;266}267268void269lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif,270const u8 *addr)271{272struct lkpi_hw *lhw;273274lhw = HW_TO_LHW(hw);275if (lhw->ops->sw_scan_start == NULL)276return;277278LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);279lhw->ops->sw_scan_start(hw, vif, addr);280}281282283/*284* We keep the Linux type here; it really is an uintptr_t.285*/286u64287lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw,288struct netdev_hw_addr_list *mc_list)289{290struct lkpi_hw *lhw;291u64 ptr;292293lhw = HW_TO_LHW(hw);294if (lhw->ops->prepare_multicast == NULL)295return (0);296297LKPI_80211_TRACE_MO("hw %p mc_list %p", hw, mc_list);298ptr = lhw->ops->prepare_multicast(hw, mc_list);299return (ptr);300}301302void303lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,304unsigned int *total_flags, u64 mc_ptr)305{306struct lkpi_hw *lhw;307308lhw = HW_TO_LHW(hw);309if (lhw->ops->configure_filter == NULL)310return;311312LKPI_80211_TRACE_MO("hw %p changed_flags %#x total_flags %p mc_ptr %ju", hw, changed_flags, total_flags, (uintmax_t)mc_ptr);313lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr);314}315316317/*318* So far we only called sta_{add,remove} as an alternative to sta_state.319* Let's keep the implementation simpler and hide sta_{add,remove} under the320* hood here calling them if state_state is not available from mo_sta_state.321*/322static int323lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,324struct ieee80211_sta *sta)325{326struct lkpi_hw *lhw;327struct lkpi_sta *lsta;328int error;329330lhw = HW_TO_LHW(hw);331if (lhw->ops->sta_add == NULL) {332error = EOPNOTSUPP;333goto out;334}335336lsta = STA_TO_LSTA(sta);337if (lsta->added_to_drv) {338error = EEXIST;339goto out;340}341342LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);343error = lhw->ops->sta_add(hw, vif, sta);344if (error == 0)345lsta->added_to_drv = true;346347out:348return error;349}350351static int352lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,353struct ieee80211_sta *sta)354{355struct lkpi_hw *lhw;356struct lkpi_sta *lsta;357int error;358359lhw = HW_TO_LHW(hw);360if (lhw->ops->sta_remove == NULL) {361error = EOPNOTSUPP;362goto out;363}364365lsta = STA_TO_LSTA(sta);366if (!lsta->added_to_drv) {367/* If we never added the sta, do not complain on cleanup. */368error = 0;369goto out;370}371372LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);373error = lhw->ops->sta_remove(hw, vif, sta);374if (error == 0)375lsta->added_to_drv = false;376377out:378return error;379}380381int382lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,383struct lkpi_sta *lsta, enum ieee80211_sta_state nstate)384{385struct lkpi_hw *lhw;386struct ieee80211_sta *sta;387int error;388389lhw = HW_TO_LHW(hw);390sta = LSTA_TO_STA(lsta);391if (lhw->ops->sta_state != NULL) {392LKPI_80211_TRACE_MO("hw %p vif %p sta %p nstate %d", hw, vif, sta, nstate);393error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate);394if (error == 0) {395if (nstate == IEEE80211_STA_NOTEXIST)396lsta->added_to_drv = false;397else398lsta->added_to_drv = true;399lsta->state = nstate;400}401goto out;402}403404/* XXX-BZ is the change state AUTH or ASSOC here? */405if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) {406error = lkpi_80211_mo_sta_add(hw, vif, sta);407if (error == 0)408lsta->added_to_drv = true;409} else if (lsta->state >= IEEE80211_STA_ASSOC &&410nstate < IEEE80211_STA_ASSOC) {411error = lkpi_80211_mo_sta_remove(hw, vif, sta);412if (error == 0)413lsta->added_to_drv = false;414} else415/* Nothing to do. */416error = 0;417if (error == 0)418lsta->state = nstate;419420out:421/* XXX-BZ should we manage state in here? */422return (error);423}424425int426lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed)427{428struct lkpi_hw *lhw;429int error;430431lhw = HW_TO_LHW(hw);432if (lhw->ops->config == NULL) {433error = EOPNOTSUPP;434goto out;435}436437LKPI_80211_TRACE_MO("hw %p changed %u", hw, changed);438LKPI_80211_TRACE_MO("TODO link/radio_idx");439error = lhw->ops->config(hw, 0, changed);440441out:442return (error);443}444445446int447lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,448struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)449{450struct lkpi_hw *lhw;451int error;452453lhw = HW_TO_LHW(hw);454if (lhw->ops->assign_vif_chanctx == NULL) {455error = EOPNOTSUPP;456goto out;457}458459LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",460hw, vif, conf, chanctx_conf);461error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf);462if (error == 0)463vif->bss_conf.chanctx_conf = chanctx_conf;464465out:466return (error);467}468469void470lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,471struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)472{473struct lkpi_hw *lhw;474475might_sleep();476lockdep_assert_wiphy(hw->wiphy);477478lhw = HW_TO_LHW(hw);479if (lhw->ops->unassign_vif_chanctx == NULL)480return;481482if (chanctx_conf == NULL)483return;484485LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",486hw, vif, conf, chanctx_conf);487lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf);488}489490491int492lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw,493struct ieee80211_chanctx_conf *chanctx_conf)494{495struct lkpi_hw *lhw;496struct lkpi_chanctx *lchanctx;497int error;498499lhw = HW_TO_LHW(hw);500if (lhw->ops->add_chanctx == NULL) {501error = EOPNOTSUPP;502goto out;503}504505LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);506error = lhw->ops->add_chanctx(hw, chanctx_conf);507if (error == 0) {508lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);509lchanctx->added_to_drv = true;510}511512out:513return (error);514}515516void517lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw,518struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed)519{520struct lkpi_hw *lhw;521522lhw = HW_TO_LHW(hw);523if (lhw->ops->change_chanctx == NULL)524return;525526LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed);527lhw->ops->change_chanctx(hw, chanctx_conf, changed);528}529530void531lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw,532struct ieee80211_chanctx_conf *chanctx_conf)533{534struct lkpi_hw *lhw;535struct lkpi_chanctx *lchanctx;536537lhw = HW_TO_LHW(hw);538if (lhw->ops->remove_chanctx == NULL)539return;540541LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);542lhw->ops->remove_chanctx(hw, chanctx_conf);543lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);544lchanctx->added_to_drv = false;545}546547void548lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,549struct ieee80211_bss_conf *conf, uint64_t changed)550{551struct lkpi_hw *lhw;552553lhw = HW_TO_LHW(hw);554if (lhw->ops->link_info_changed == NULL &&555lhw->ops->bss_info_changed == NULL)556return;557558if (changed == 0)559return;560561LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed);562if (lhw->ops->link_info_changed != NULL)563lhw->ops->link_info_changed(hw, vif, conf, changed);564else565lhw->ops->bss_info_changed(hw, vif, conf, changed);566}567568int569lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,570uint32_t link_id, uint16_t ac, const struct ieee80211_tx_queue_params *txqp)571{572struct lkpi_hw *lhw;573int error;574575lhw = HW_TO_LHW(hw);576if (lhw->ops->conf_tx == NULL) {577error = EOPNOTSUPP;578goto out;579}580581LKPI_80211_TRACE_MO("hw %p vif %p link_id %u ac %u txpq %p",582hw, vif, link_id, ac, txqp);583error = lhw->ops->conf_tx(hw, vif, link_id, ac, txqp);584585out:586return (error);587}588589void590lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,591uint32_t nqueues, bool drop)592{593struct lkpi_hw *lhw;594595lhw = HW_TO_LHW(hw);596if (lhw->ops->flush == NULL)597return;598599LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop);600lhw->ops->flush(hw, vif, nqueues, drop);601}602603void604lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,605struct ieee80211_prep_tx_info *txinfo)606{607struct lkpi_hw *lhw;608609lhw = HW_TO_LHW(hw);610if (lhw->ops->mgd_prepare_tx == NULL)611return;612613LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);614lhw->ops->mgd_prepare_tx(hw, vif, txinfo);615}616617void618lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,619struct ieee80211_prep_tx_info *txinfo)620{621struct lkpi_hw *lhw;622623lhw = HW_TO_LHW(hw);624if (lhw->ops->mgd_complete_tx == NULL)625return;626627LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);628lhw->ops->mgd_complete_tx(hw, vif, txinfo);629}630631void632lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl,633struct sk_buff *skb)634{635struct lkpi_hw *lhw;636637lhw = HW_TO_LHW(hw);638if (lhw->ops->tx == NULL)639return;640641LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb);642lhw->ops->tx(hw, txctrl, skb);643}644645void646lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq,647bool schedule)648{649struct lkpi_hw *lhw;650651lhw = HW_TO_LHW(hw);652653/* Do the schedule before the check for wake_tx_queue supported! */654if (schedule)655ieee80211_schedule_txq(hw, txq);656657if (lhw->ops->wake_tx_queue == NULL)658return;659660LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq);661lhw->ops->wake_tx_queue(hw, txq);662}663664void665lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw)666{667struct lkpi_hw *lhw;668669lhw = HW_TO_LHW(hw);670if (lhw->ops->sync_rx_queues == NULL)671return;672673LKPI_80211_TRACE_MO("hw %p", hw);674lhw->ops->sync_rx_queues(hw);675}676677void678lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw,679struct ieee80211_vif *vif, struct ieee80211_sta *sta)680{681struct lkpi_hw *lhw;682683lhw = HW_TO_LHW(hw);684if (lhw->ops->sta_pre_rcu_remove == NULL)685return;686687LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);688lhw->ops->sta_pre_rcu_remove(hw, vif, sta);689}690691int692lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,693struct ieee80211_vif *vif, struct ieee80211_sta *sta,694struct ieee80211_key_conf *kc)695{696struct lkpi_hw *lhw;697int error;698699lockdep_assert_wiphy(hw->wiphy);700701lhw = HW_TO_LHW(hw);702if (lhw->ops->set_key == NULL) {703error = EOPNOTSUPP;704goto out;705}706707LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc);708error = lhw->ops->set_key(hw, cmd, vif, sta, kc);709710out:711return (error);712}713714int715lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,716struct ieee80211_ampdu_params *params)717{718struct lkpi_hw *lhw;719int error;720721lhw = HW_TO_LHW(hw);722if (lhw->ops->ampdu_action == NULL) {723error = EOPNOTSUPP;724goto out;725}726727LKPI_80211_TRACE_MO("hw %p vif %p params %p { %p, %d, %u, %u, %u, %u, %d }",728hw, vif, params, params->sta, params->action, params->buf_size,729params->timeout, params->ssn, params->tid, params->amsdu);730error = lhw->ops->ampdu_action(hw, vif, params);731732out:733return (error);734}735736int737lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,738struct ieee80211_sta *sta, struct station_info *sinfo)739{740struct lkpi_hw *lhw;741struct lkpi_sta *lsta;742int error;743744lhw = HW_TO_LHW(hw);745if (lhw->ops->sta_statistics == NULL) {746error = EOPNOTSUPP;747goto out;748}749750lsta = STA_TO_LSTA(sta);751if (!lsta->added_to_drv) {752error = EEXIST;753goto out;754}755756lockdep_assert_wiphy(hw->wiphy);757758LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo);759lhw->ops->sta_statistics(hw, vif, sta, sinfo);760error = 0;761762out:763return (error);764}765766767