#include <sys/param.h>
#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#define LINUXKPI_NET80211
#include <net/mac80211.h>
#include "linux_80211.h"
#ifdef LINUXKPI_DEBUG_80211
#define LKPI_80211_TRACE_MO(fmt, ...) \
if (linuxkpi_debug_80211 & D80211_TRACE_MO) \
printf("LKPI_80211_TRACE_MO %s:%d: %d %d %lu: " fmt "\n", \
__func__, __LINE__, curcpu, curthread->td_tid, \
jiffies, ##__VA_ARGS__)
#else
#define LKPI_80211_TRACE_MO(...) do { } while(0)
#endif
int
lkpi_80211_mo_start(struct ieee80211_hw *hw)
{
struct lkpi_hw *lhw;
int error;
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->start == NULL) {
error = EOPNOTSUPP;
goto out;
}
if ((lhw->sc_flags & LKPI_MAC80211_DRV_STARTED)) {
error = EEXIST;
goto out;
}
LKPI_80211_TRACE_MO("hw %p", hw);
error = lhw->ops->start(hw);
if (error == 0)
lhw->sc_flags |= LKPI_MAC80211_DRV_STARTED;
out:
return (error);
}
void
lkpi_80211_mo_stop(struct ieee80211_hw *hw, bool suspend)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (lhw->ops->stop == NULL)
return;
LKPI_80211_TRACE_MO("hw %p suspend %d", hw, suspend);
lhw->ops->stop(hw, suspend);
lhw->sc_flags &= ~LKPI_MAC80211_DRV_STARTED;
}
int
lkpi_80211_mo_get_antenna(struct ieee80211_hw *hw, u32 *txs, u32 *rxs)
{
struct lkpi_hw *lhw;
int error;
lhw = HW_TO_LHW(hw);
if (lhw->ops->get_antenna == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p", hw);
LKPI_80211_TRACE_MO("TODO link/radio_idx");
error = lhw->ops->get_antenna(hw, 0, txs, rxs);
out:
return (error);
}
int
lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw *hw, uint32_t frag_th)
{
struct lkpi_hw *lhw;
int error;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->set_frag_threshold == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p frag_th %u", hw, frag_th);
LKPI_80211_TRACE_MO("TODO link/radio_idx");
error = lhw->ops->set_frag_threshold(hw, 0, frag_th);
out:
return (error);
}
int
lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *hw, uint32_t rts_th)
{
struct lkpi_hw *lhw;
int error;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->set_rts_threshold == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p rts_th %u", hw, rts_th);
LKPI_80211_TRACE_MO("TODO link/radio_idx");
error = lhw->ops->set_rts_threshold(hw, 0, rts_th);
out:
return (error);
}
int
lkpi_80211_mo_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct lkpi_hw *lhw;
struct lkpi_vif *lvif;
int error;
lhw = HW_TO_LHW(hw);
if (lhw->ops->add_interface == NULL) {
error = EOPNOTSUPP;
goto out;
}
lvif = VIF_TO_LVIF(vif);
LKPI_80211_LVIF_LOCK(lvif);
if (lvif->added_to_drv) {
LKPI_80211_LVIF_UNLOCK(lvif);
error = EEXIST;
goto out;
}
LKPI_80211_LVIF_UNLOCK(lvif);
LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
error = lhw->ops->add_interface(hw, vif);
if (error == 0) {
LKPI_80211_LVIF_LOCK(lvif);
lvif->added_to_drv = true;
LKPI_80211_LVIF_UNLOCK(lvif);
}
out:
return (error);
}
void
lkpi_80211_mo_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct lkpi_hw *lhw;
struct lkpi_vif *lvif;
lhw = HW_TO_LHW(hw);
if (lhw->ops->remove_interface == NULL)
return;
lvif = VIF_TO_LVIF(vif);
LKPI_80211_LVIF_LOCK(lvif);
if (!lvif->added_to_drv) {
LKPI_80211_LVIF_UNLOCK(lvif);
return;
}
LKPI_80211_LVIF_UNLOCK(lvif);
LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
lhw->ops->remove_interface(hw, vif);
LKPI_80211_LVIF_LOCK(lvif);
lvif->added_to_drv = false;
LKPI_80211_LVIF_UNLOCK(lvif);
}
int
lkpi_80211_mo_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_scan_request *sr)
{
struct lkpi_hw *lhw;
int error;
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->hw_scan == NULL) {
error = 1;
goto out;
}
LKPI_80211_TRACE_MO("CALLING hw %p vif %p sr %p", hw, vif, sr);
error = lhw->ops->hw_scan(hw, vif, sr);
LKPI_80211_TRACE_MO("RETURNING hw %p vif %p sr %p error %d", hw, vif, sr, error);
out:
return (error);
}
void
lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct lkpi_hw *lhw;
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->cancel_hw_scan == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
lhw->ops->cancel_hw_scan(hw, vif);
}
void
lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (lhw->ops->sw_scan_complete == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
lhw->ops->sw_scan_complete(hw, vif);
lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
}
void
lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
const u8 *addr)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (lhw->ops->sw_scan_start == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
lhw->ops->sw_scan_start(hw, vif, addr);
}
u64
lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw,
struct netdev_hw_addr_list *mc_list)
{
struct lkpi_hw *lhw;
u64 ptr;
lhw = HW_TO_LHW(hw);
if (lhw->ops->prepare_multicast == NULL)
return (0);
LKPI_80211_TRACE_MO("hw %p mc_list %p", hw, mc_list);
ptr = lhw->ops->prepare_multicast(hw, mc_list);
return (ptr);
}
void
lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
unsigned int *total_flags, u64 mc_ptr)
{
struct lkpi_hw *lhw;
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->configure_filter == NULL)
return;
LKPI_80211_TRACE_MO("hw %p changed_flags %#x total_flags %p mc_ptr %ju", hw, changed_flags, total_flags, (uintmax_t)mc_ptr);
lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr);
}
static int
lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct lkpi_hw *lhw;
struct lkpi_sta *lsta;
int error;
lhw = HW_TO_LHW(hw);
if (lhw->ops->sta_add == NULL) {
error = EOPNOTSUPP;
goto out;
}
lsta = STA_TO_LSTA(sta);
if (lsta->added_to_drv) {
error = EEXIST;
goto out;
}
LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
error = lhw->ops->sta_add(hw, vif, sta);
if (error == 0)
lsta->added_to_drv = true;
out:
return error;
}
static int
lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct lkpi_hw *lhw;
struct lkpi_sta *lsta;
int error;
lhw = HW_TO_LHW(hw);
if (lhw->ops->sta_remove == NULL) {
error = EOPNOTSUPP;
goto out;
}
lsta = STA_TO_LSTA(sta);
if (!lsta->added_to_drv) {
error = 0;
goto out;
}
LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
error = lhw->ops->sta_remove(hw, vif, sta);
if (error == 0)
lsta->added_to_drv = false;
out:
return error;
}
int
lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct lkpi_sta *lsta, enum ieee80211_sta_state nstate)
{
struct lkpi_hw *lhw;
struct ieee80211_sta *sta;
int error;
lhw = HW_TO_LHW(hw);
sta = LSTA_TO_STA(lsta);
if (lhw->ops->sta_state != NULL) {
LKPI_80211_TRACE_MO("hw %p vif %p sta %p nstate %d", hw, vif, sta, nstate);
error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate);
if (error == 0) {
if (nstate == IEEE80211_STA_NOTEXIST)
lsta->added_to_drv = false;
else
lsta->added_to_drv = true;
lsta->state = nstate;
}
goto out;
}
if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) {
error = lkpi_80211_mo_sta_add(hw, vif, sta);
if (error == 0)
lsta->added_to_drv = true;
} else if (lsta->state >= IEEE80211_STA_ASSOC &&
nstate < IEEE80211_STA_ASSOC) {
error = lkpi_80211_mo_sta_remove(hw, vif, sta);
if (error == 0)
lsta->added_to_drv = false;
} else
error = 0;
if (error == 0)
lsta->state = nstate;
out:
return (error);
}
int
lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed)
{
struct lkpi_hw *lhw;
int error;
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->config == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p changed %u", hw, changed);
LKPI_80211_TRACE_MO("TODO link/radio_idx");
error = lhw->ops->config(hw, 0, changed);
out:
return (error);
}
int
lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
{
struct lkpi_hw *lhw;
int error;
lhw = HW_TO_LHW(hw);
if (lhw->ops->assign_vif_chanctx == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
hw, vif, conf, chanctx_conf);
error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf);
if (error == 0)
vif->bss_conf.chanctx_conf = chanctx_conf;
out:
return (error);
}
void
lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
{
struct lkpi_hw *lhw;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->unassign_vif_chanctx == NULL)
return;
if (chanctx_conf == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
hw, vif, conf, chanctx_conf);
lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf);
}
int
lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *chanctx_conf)
{
struct lkpi_hw *lhw;
struct lkpi_chanctx *lchanctx;
int error;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->add_chanctx == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
error = lhw->ops->add_chanctx(hw, chanctx_conf);
if (error == 0) {
lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
lchanctx->added_to_drv = true;
}
out:
return (error);
}
void
lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed)
{
struct lkpi_hw *lhw;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->change_chanctx == NULL)
return;
LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed);
lhw->ops->change_chanctx(hw, chanctx_conf, changed);
}
void
lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *chanctx_conf)
{
struct lkpi_hw *lhw;
struct lkpi_chanctx *lchanctx;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->remove_chanctx == NULL)
return;
LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
lhw->ops->remove_chanctx(hw, chanctx_conf);
lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
lchanctx->added_to_drv = false;
}
void
lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
uint64_t vif_cfg_bits, bool fallback)
{
struct lkpi_hw *lhw;
might_sleep();
lhw = HW_TO_LHW(hw);
if (lhw->ops->vif_cfg_changed == NULL &&
lhw->ops->bss_info_changed == NULL)
return;
if (vif_cfg_bits == 0)
return;
LKPI_80211_TRACE_MO("hw %p vif %p vif_cfg_bits %#jx", hw, vif, (uintmax_t)vif_cfg_bits);
if (lhw->ops->link_info_changed != NULL)
lhw->ops->vif_cfg_changed(hw, vif, vif_cfg_bits);
else if (fallback)
lhw->ops->bss_info_changed(hw, vif, &vif->bss_conf, vif_cfg_bits);
}
void
lkpi_80211_mo_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *conf, uint64_t link_info_bits, uint8_t link_id,
bool fallback)
{
struct lkpi_hw *lhw;
might_sleep();
lhw = HW_TO_LHW(hw);
if (lhw->ops->link_info_changed == NULL &&
lhw->ops->bss_info_changed == NULL)
return;
if (link_info_bits == 0)
return;
if (!ieee80211_vif_link_active(vif, link_id))
return;
LKPI_80211_TRACE_MO("hw %p vif %p conf %p link_info_bits %#jx", hw, vif, conf, (uintmax_t)link_info_bits);
if (lhw->ops->link_info_changed != NULL)
lhw->ops->link_info_changed(hw, vif, conf, link_info_bits);
else if (fallback)
lhw->ops->bss_info_changed(hw, vif, conf, link_info_bits);
}
void
lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *conf, uint64_t bss_changed)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (lhw->ops->bss_info_changed == NULL)
return;
if (bss_changed == 0)
return;
LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)bss_changed);
lhw->ops->bss_info_changed(hw, vif, conf, bss_changed);
}
int
lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
uint32_t link_id, uint16_t ac, const struct ieee80211_tx_queue_params *txqp)
{
struct lkpi_hw *lhw;
int error;
lhw = HW_TO_LHW(hw);
if (lhw->ops->conf_tx == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p vif %p link_id %u ac %u txpq %p",
hw, vif, link_id, ac, txqp);
error = lhw->ops->conf_tx(hw, vif, link_id, ac, txqp);
out:
return (error);
}
void
lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
uint32_t nqueues, bool drop)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (lhw->ops->flush == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop);
lhw->ops->flush(hw, vif, nqueues, drop);
}
void
lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_prep_tx_info *txinfo)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (lhw->ops->mgd_prepare_tx == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
lhw->ops->mgd_prepare_tx(hw, vif, txinfo);
}
void
lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_prep_tx_info *txinfo)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (lhw->ops->mgd_complete_tx == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
lhw->ops->mgd_complete_tx(hw, vif, txinfo);
}
void
lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl,
struct sk_buff *skb)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (lhw->ops->tx == NULL)
return;
LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb);
lhw->ops->tx(hw, txctrl, skb);
}
void
lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
bool schedule)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (schedule)
ieee80211_schedule_txq(hw, txq);
if (lhw->ops->wake_tx_queue == NULL)
return;
LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq);
lhw->ops->wake_tx_queue(hw, txq);
}
void
lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (lhw->ops->sync_rx_queues == NULL)
return;
LKPI_80211_TRACE_MO("hw %p", hw);
lhw->ops->sync_rx_queues(hw);
}
void
lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_sta *sta)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (lhw->ops->sta_pre_rcu_remove == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
lhw->ops->sta_pre_rcu_remove(hw, vif, sta);
}
void
lkpi_80211_mo_link_sta_rc_update(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta,
enum ieee80211_rate_control_changed_flags rc_changed)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
if (lhw->ops->link_sta_rc_update == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p link_sta %p rc_changed %#010x",
hw, vif, link_sta, rc_changed);
lhw->ops->link_sta_rc_update(hw, vif, link_sta, rc_changed);
}
int
lkpi_80211_mo_set_bitrate_mask(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *br_mask)
{
struct lkpi_hw *lhw;
int error;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->set_bitrate_mask == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p vif %p br_mask %p",
hw, vif, br_mask);
error = lhw->ops->set_bitrate_mask(hw, vif, br_mask);
out:
return (error);
}
int
lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
struct ieee80211_key_conf *kc)
{
struct lkpi_hw *lhw;
int error;
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->set_key == NULL) {
error = EOPNOTSUPP;
goto out;
}
MPASS(kc->_k != NULL);
if (is_broadcast_ether_addr(kc->_k->wk_macaddr))
sta = NULL;
LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc);
error = lhw->ops->set_key(hw, cmd, vif, sta, kc);
out:
return (error);
}
void
lkpi_80211_mo_sta_set_decap_offload(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
bool enable)
{
struct lkpi_hw *lhw;
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->sta_set_decap_offload == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p sta %p enable %d", hw, vif, sta, enable);
lhw->ops->sta_set_decap_offload(hw, vif, sta, enable);
}
int
lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_ampdu_params *params)
{
struct lkpi_hw *lhw;
int error;
lhw = HW_TO_LHW(hw);
if (lhw->ops->ampdu_action == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p vif %p params %p { %p, %d, %u, %u, %u, %u, %d }",
hw, vif, params, params->sta, params->action, params->buf_size,
params->timeout, params->ssn, params->tid, params->amsdu);
error = lhw->ops->ampdu_action(hw, vif, params);
out:
return (error);
}
int
lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct station_info *sinfo)
{
struct lkpi_hw *lhw;
struct lkpi_sta *lsta;
int error;
lhw = HW_TO_LHW(hw);
if (lhw->ops->sta_statistics == NULL) {
error = EOPNOTSUPP;
goto out;
}
lsta = STA_TO_LSTA(sta);
if (!lsta->added_to_drv) {
error = EEXIST;
goto out;
}
lockdep_assert_wiphy(hw->wiphy);
LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo);
lhw->ops->sta_statistics(hw, vif, sta, sinfo);
error = 0;
out:
return (error);
}
int
lkpi_80211_mo_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
{
struct lkpi_hw *lhw;
int error;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->suspend == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p wowlan %p", hw, wowlan);
error = lhw->ops->suspend(hw, wowlan);
out:
return (error);
}
int
lkpi_80211_mo_resume(struct ieee80211_hw *hw)
{
struct lkpi_hw *lhw;
int error;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->resume == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p", hw);
error = lhw->ops->resume(hw);
out:
return (error);
}
int
lkpi_80211_mo_set_wakeup(struct ieee80211_hw *hw, bool enable)
{
struct lkpi_hw *lhw;
int error;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->set_wakeup == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p enable %d", hw, enable);
lhw->ops->set_wakeup(hw, enable);
error = 0;
out:
return (error);
}
int
lkpi_80211_mo_set_rekey_data(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct cfg80211_gtk_rekey_data *grd)
{
struct lkpi_hw *lhw;
int error;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->set_rekey_data == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p vif %p grd %p", hw, vif, grd);
lhw->ops->set_rekey_data(hw, vif, grd);
error = 0;
out:
return (error);
}
int
lkpi_80211_mo_set_default_unicast_key(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, int idx)
{
struct lkpi_hw *lhw;
int error;
might_sleep();
lockdep_assert_wiphy(hw->wiphy);
lhw = HW_TO_LHW(hw);
if (lhw->ops->set_default_unicast_key == NULL) {
error = EOPNOTSUPP;
goto out;
}
LKPI_80211_TRACE_MO("hw %p vif %p idx %d", hw, vif, idx);
lhw->ops->set_default_unicast_key(hw, vif, idx);
error = 0;
out:
return (error);
}