This relatively large batch of changes is comprised of the
following: * large mac80211-hwsim changes from Ben, Jukka and a bit myself * OCB/WAVE/11p support from Rostislav on behalf of the Czech Technical University in Prague and Volkswagen Group Research * minstrel VHT work from Karl * more CSA work from Luca * WMM admission control support in mac80211 (myself) * various smaller fixes, spelling corrections, and minor API additions -----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJUWMs/AAoJEDBSmw7B7bqrmBQQAIbfAe7wH1WifRtOnhw3zWQQ K36+Edf3HlQ+EIkSs63QousRj2e7pGDOyhzMWLaqsmeTLteUtlGbr7qwiJO1QZdf Ml2V5O2s+b8hUIClDBVQF2L6+GGUmRUdQqvDDhkN1guoxD/Nk8cNtsRkSdiXWJWy R48NzvYDflBhc8uqPtR8jDb10eM3c00YP9HB+w9hYAfizD+FRue7UNp4MQIqwp9V HdKRT6L2n/6QA+Mzse0rMDes5qI7nIUNgj+hjqgJSnhITPMgGR5j/pitnVHrr81M ngOipBFG3svsQrwZh8nM4Llp0cM4Gs+GlgCieu9+TJpr2sY00Z3kYcp0pxtDoSxz Wblqz9n/bnW9mrkEfl12XqwwT5vguchwHoZ9cXhejDxSawWXoTRx20uW4ahO8ArA kWwwjTBVsQ5WMCtOBiqggzNKghwCc2ILmcZnjGdg9aNXcWsmQ4vyeCfG2QxBz/UB Grv/f9NSy6mzKQ34yv+lyR7rFZ8XcT03EVAnZSYz8X0ZZGxwtFupRp1RrBh1KPtD TJoe6Q71FfHKYRJ2xgygYkQFo+r9d0BKBeerq+Vu2hBeaqyi4aUwSj7d1sUaaq6N tL8fmAUqFjVOOUFeH1g07Xke5QD+yrEC7sJKkeRMfcRGB+dEa+2m3I5p4WDz9bWM AEvFSsYr/I9KI4d1huXD =6GIj -----END PGP SIGNATURE----- Merge tag 'mac80211-next-for-john-2014-11-04' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next Johannes Berg <johannes@sipsolutions.net> says: "This relatively large batch of changes is comprised of the following: * large mac80211-hwsim changes from Ben, Jukka and a bit myself * OCB/WAVE/11p support from Rostislav on behalf of the Czech Technical University in Prague and Volkswagen Group Research * minstrel VHT work from Karl * more CSA work from Luca * WMM admission control support in mac80211 (myself) * various smaller fixes, spelling corrections, and minor API additions" Conflicts: drivers/net/wireless/ath/wil6210/cfg80211.c Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
commit
bf515fb11a
62 changed files with 2758 additions and 453 deletions
|
@ -338,7 +338,7 @@ enum ath10k_state {
|
||||||
* stopped in ath10k_core_restart() work holding conf_mutex. The state
|
* stopped in ath10k_core_restart() work holding conf_mutex. The state
|
||||||
* RESTARTED means that the device is up and mac80211 has started hw
|
* RESTARTED means that the device is up and mac80211 has started hw
|
||||||
* reconfiguration. Once mac80211 is done with the reconfiguration we
|
* reconfiguration. Once mac80211 is done with the reconfiguration we
|
||||||
* set the state to STATE_ON in restart_complete(). */
|
* set the state to STATE_ON in reconfig_complete(). */
|
||||||
ATH10K_STATE_RESTARTING,
|
ATH10K_STATE_RESTARTING,
|
||||||
ATH10K_STATE_RESTARTED,
|
ATH10K_STATE_RESTARTED,
|
||||||
|
|
||||||
|
|
|
@ -3994,10 +3994,14 @@ exit:
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void ath10k_restart_complete(struct ieee80211_hw *hw)
|
static void ath10k_reconfig_complete(struct ieee80211_hw *hw,
|
||||||
|
enum ieee80211_reconfig_type reconfig_type)
|
||||||
{
|
{
|
||||||
struct ath10k *ar = hw->priv;
|
struct ath10k *ar = hw->priv;
|
||||||
|
|
||||||
|
if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART)
|
||||||
|
return;
|
||||||
|
|
||||||
mutex_lock(&ar->conf_mutex);
|
mutex_lock(&ar->conf_mutex);
|
||||||
|
|
||||||
/* If device failed to restart it will be in a different state, e.g.
|
/* If device failed to restart it will be in a different state, e.g.
|
||||||
|
@ -4515,7 +4519,7 @@ static const struct ieee80211_ops ath10k_ops = {
|
||||||
.tx_last_beacon = ath10k_tx_last_beacon,
|
.tx_last_beacon = ath10k_tx_last_beacon,
|
||||||
.set_antenna = ath10k_set_antenna,
|
.set_antenna = ath10k_set_antenna,
|
||||||
.get_antenna = ath10k_get_antenna,
|
.get_antenna = ath10k_get_antenna,
|
||||||
.restart_complete = ath10k_restart_complete,
|
.reconfig_complete = ath10k_reconfig_complete,
|
||||||
.get_survey = ath10k_get_survey,
|
.get_survey = ath10k_get_survey,
|
||||||
.set_bitrate_mask = ath10k_set_bitrate_mask,
|
.set_bitrate_mask = ath10k_set_bitrate_mask,
|
||||||
.sta_rc_update = ath10k_sta_rc_update,
|
.sta_rc_update = ath10k_sta_rc_update,
|
||||||
|
|
|
@ -2976,11 +2976,11 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
||||||
static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||||
|
|
||||||
static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
|
static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
|
||||||
const u8 *mac)
|
struct station_del_parameters *params)
|
||||||
{
|
{
|
||||||
struct ath6kl *ar = ath6kl_priv(dev);
|
struct ath6kl *ar = ath6kl_priv(dev);
|
||||||
struct ath6kl_vif *vif = netdev_priv(dev);
|
struct ath6kl_vif *vif = netdev_priv(dev);
|
||||||
const u8 *addr = mac ? mac : bcast_addr;
|
const u8 *addr = params->mac ? params->mac : bcast_addr;
|
||||||
|
|
||||||
return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
|
return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
|
||||||
addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
|
addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
|
||||||
|
|
|
@ -792,12 +792,13 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wil_cfg80211_del_station(struct wiphy *wiphy,
|
static int wil_cfg80211_del_station(struct wiphy *wiphy,
|
||||||
struct net_device *dev, const u8 *mac)
|
struct net_device *dev,
|
||||||
|
struct station_del_parameters *params)
|
||||||
{
|
{
|
||||||
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
||||||
|
|
||||||
mutex_lock(&wil->mutex);
|
mutex_lock(&wil->mutex);
|
||||||
wil6210_disconnect(wil, mac, false);
|
wil6210_disconnect(wil, params->mac, false);
|
||||||
mutex_unlock(&wil->mutex);
|
mutex_unlock(&wil->mutex);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -4045,24 +4045,24 @@ brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
|
||||||
|
|
||||||
static int
|
static int
|
||||||
brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
|
brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
|
||||||
const u8 *mac)
|
struct station_del_parameters *params)
|
||||||
{
|
{
|
||||||
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
|
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
|
||||||
struct brcmf_scb_val_le scbval;
|
struct brcmf_scb_val_le scbval;
|
||||||
struct brcmf_if *ifp = netdev_priv(ndev);
|
struct brcmf_if *ifp = netdev_priv(ndev);
|
||||||
s32 err;
|
s32 err;
|
||||||
|
|
||||||
if (!mac)
|
if (!params->mac)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
brcmf_dbg(TRACE, "Enter %pM\n", mac);
|
brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
|
||||||
|
|
||||||
if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
|
if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
|
||||||
ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
|
ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
|
||||||
if (!check_vif_up(ifp->vif))
|
if (!check_vif_up(ifp->vif))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
memcpy(&scbval.ea, mac, ETH_ALEN);
|
memcpy(&scbval.ea, params->mac, ETH_ALEN);
|
||||||
scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
|
scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
|
||||||
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
|
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
|
||||||
&scbval, sizeof(scbval));
|
&scbval, sizeof(scbval));
|
||||||
|
|
|
@ -6063,7 +6063,7 @@ il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
il4965_mac_channel_switch(struct ieee80211_hw *hw,
|
il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||||
struct ieee80211_channel_switch *ch_switch)
|
struct ieee80211_channel_switch *ch_switch)
|
||||||
{
|
{
|
||||||
struct il_priv *il = hw->priv;
|
struct il_priv *il = hw->priv;
|
||||||
|
|
|
@ -187,8 +187,9 @@ int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||||
u8 buf_size);
|
u8 buf_size);
|
||||||
int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||||
struct ieee80211_sta *sta);
|
struct ieee80211_sta *sta);
|
||||||
void il4965_mac_channel_switch(struct ieee80211_hw *hw,
|
void
|
||||||
struct ieee80211_channel_switch *ch_switch);
|
il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||||
|
struct ieee80211_channel_switch *ch_switch);
|
||||||
|
|
||||||
void il4965_led_enable(struct il_priv *il);
|
void il4965_led_enable(struct il_priv *il);
|
||||||
|
|
||||||
|
|
|
@ -941,6 +941,7 @@ static int iwlagn_mac_sta_state(struct ieee80211_hw *hw,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
|
static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
|
||||||
|
struct ieee80211_vif *vif,
|
||||||
struct ieee80211_channel_switch *ch_switch)
|
struct ieee80211_channel_switch *ch_switch)
|
||||||
{
|
{
|
||||||
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
|
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
|
||||||
|
|
|
@ -895,9 +895,8 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
|
static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
|
||||||
{
|
{
|
||||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&mvm->mutex);
|
mutex_lock(&mvm->mutex);
|
||||||
|
@ -915,6 +914,21 @@ static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
|
||||||
mutex_unlock(&mvm->mutex);
|
mutex_unlock(&mvm->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
|
||||||
|
enum ieee80211_reconfig_type reconfig_type)
|
||||||
|
{
|
||||||
|
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||||
|
|
||||||
|
switch (reconfig_type) {
|
||||||
|
case IEEE80211_RECONFIG_TYPE_RESTART:
|
||||||
|
iwl_mvm_restart_complete(mvm);
|
||||||
|
break;
|
||||||
|
case IEEE80211_RECONFIG_TYPE_SUSPEND:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
|
void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
|
||||||
{
|
{
|
||||||
lockdep_assert_held(&mvm->mutex);
|
lockdep_assert_held(&mvm->mutex);
|
||||||
|
@ -3058,7 +3072,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
|
||||||
.tx = iwl_mvm_mac_tx,
|
.tx = iwl_mvm_mac_tx,
|
||||||
.ampdu_action = iwl_mvm_mac_ampdu_action,
|
.ampdu_action = iwl_mvm_mac_ampdu_action,
|
||||||
.start = iwl_mvm_mac_start,
|
.start = iwl_mvm_mac_start,
|
||||||
.restart_complete = iwl_mvm_mac_restart_complete,
|
.reconfig_complete = iwl_mvm_mac_reconfig_complete,
|
||||||
.stop = iwl_mvm_mac_stop,
|
.stop = iwl_mvm_mac_stop,
|
||||||
.add_interface = iwl_mvm_mac_add_interface,
|
.add_interface = iwl_mvm_mac_add_interface,
|
||||||
.remove_interface = iwl_mvm_mac_remove_interface,
|
.remove_interface = iwl_mvm_mac_remove_interface,
|
||||||
|
|
|
@ -412,6 +412,9 @@ struct mac80211_hwsim_data {
|
||||||
struct mac_address addresses[2];
|
struct mac_address addresses[2];
|
||||||
int channels, idx;
|
int channels, idx;
|
||||||
bool use_chanctx;
|
bool use_chanctx;
|
||||||
|
bool destroy_on_close;
|
||||||
|
struct work_struct destroy_work;
|
||||||
|
u32 portid;
|
||||||
|
|
||||||
struct ieee80211_channel *tmp_chan;
|
struct ieee80211_channel *tmp_chan;
|
||||||
struct delayed_work roc_done;
|
struct delayed_work roc_done;
|
||||||
|
@ -436,7 +439,7 @@ struct mac80211_hwsim_data {
|
||||||
/*
|
/*
|
||||||
* Only radios in the same group can communicate together (the
|
* Only radios in the same group can communicate together (the
|
||||||
* channel has to match too). Each bit represents a group. A
|
* channel has to match too). Each bit represents a group. A
|
||||||
* radio can be in more then one group.
|
* radio can be in more than one group.
|
||||||
*/
|
*/
|
||||||
u64 group;
|
u64 group;
|
||||||
|
|
||||||
|
@ -447,6 +450,14 @@ struct mac80211_hwsim_data {
|
||||||
s64 bcn_delta;
|
s64 bcn_delta;
|
||||||
/* absolute beacon transmission time. Used to cover up "tx" delay. */
|
/* absolute beacon transmission time. Used to cover up "tx" delay. */
|
||||||
u64 abs_bcn_ts;
|
u64 abs_bcn_ts;
|
||||||
|
|
||||||
|
/* Stats */
|
||||||
|
u64 tx_pkts;
|
||||||
|
u64 rx_pkts;
|
||||||
|
u64 tx_bytes;
|
||||||
|
u64 rx_bytes;
|
||||||
|
u64 tx_dropped;
|
||||||
|
u64 tx_failed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -476,6 +487,14 @@ static struct genl_family hwsim_genl_family = {
|
||||||
.maxattr = HWSIM_ATTR_MAX,
|
.maxattr = HWSIM_ATTR_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum hwsim_multicast_groups {
|
||||||
|
HWSIM_MCGRP_CONFIG,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct genl_multicast_group hwsim_mcgrps[] = {
|
||||||
|
[HWSIM_MCGRP_CONFIG] = { .name = "config", },
|
||||||
|
};
|
||||||
|
|
||||||
/* MAC80211_HWSIM netlink policy */
|
/* MAC80211_HWSIM netlink policy */
|
||||||
|
|
||||||
static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
|
static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
|
||||||
|
@ -496,6 +515,10 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
|
||||||
[HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 },
|
[HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 },
|
||||||
[HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG },
|
[HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG },
|
||||||
[HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG },
|
[HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG },
|
||||||
|
[HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE] = { .type = NLA_FLAG },
|
||||||
|
[HWSIM_ATTR_RADIO_NAME] = { .type = NLA_STRING },
|
||||||
|
[HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG },
|
||||||
|
[HWSIM_ATTR_FREQ] = { .type = NLA_U32 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
|
static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
|
||||||
|
@ -861,8 +884,10 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
|
||||||
/* If the queue contains MAX_QUEUE skb's drop some */
|
/* If the queue contains MAX_QUEUE skb's drop some */
|
||||||
if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
|
if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
|
||||||
/* Droping until WARN_QUEUE level */
|
/* Droping until WARN_QUEUE level */
|
||||||
while (skb_queue_len(&data->pending) >= WARN_QUEUE)
|
while (skb_queue_len(&data->pending) >= WARN_QUEUE) {
|
||||||
skb_dequeue(&data->pending);
|
ieee80211_free_txskb(hw, skb_dequeue(&data->pending));
|
||||||
|
data->tx_dropped++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
|
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
|
||||||
|
@ -896,6 +921,9 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
|
||||||
if (nla_put_u32(skb, HWSIM_ATTR_FLAGS, hwsim_flags))
|
if (nla_put_u32(skb, HWSIM_ATTR_FLAGS, hwsim_flags))
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
||||||
|
if (nla_put_u32(skb, HWSIM_ATTR_FREQ, data->channel->center_freq))
|
||||||
|
goto nla_put_failure;
|
||||||
|
|
||||||
/* We get the tx control (rate and retries) info*/
|
/* We get the tx control (rate and retries) info*/
|
||||||
|
|
||||||
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
|
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
|
||||||
|
@ -917,10 +945,14 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
|
||||||
|
|
||||||
/* Enqueue the packet */
|
/* Enqueue the packet */
|
||||||
skb_queue_tail(&data->pending, my_skb);
|
skb_queue_tail(&data->pending, my_skb);
|
||||||
|
data->tx_pkts++;
|
||||||
|
data->tx_bytes += my_skb->len;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nla_put_failure:
|
nla_put_failure:
|
||||||
printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
|
printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
|
||||||
|
ieee80211_free_txskb(hw, my_skb);
|
||||||
|
data->tx_failed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hwsim_chans_compat(struct ieee80211_channel *c1,
|
static bool hwsim_chans_compat(struct ieee80211_channel *c1,
|
||||||
|
@ -1066,6 +1098,8 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
|
||||||
rx_status.mactime = now + data2->tsf_offset;
|
rx_status.mactime = now + data2->tsf_offset;
|
||||||
|
|
||||||
memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
|
memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
|
||||||
|
data2->rx_pkts++;
|
||||||
|
data2->rx_bytes += nskb->len;
|
||||||
ieee80211_rx_irqsafe(data2->hw, nskb);
|
ieee80211_rx_irqsafe(data2->hw, nskb);
|
||||||
}
|
}
|
||||||
spin_unlock(&hwsim_radio_lock);
|
spin_unlock(&hwsim_radio_lock);
|
||||||
|
@ -1133,6 +1167,8 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
|
||||||
return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
|
return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
|
||||||
|
|
||||||
/* NO wmediumd detected, perfect medium simulation */
|
/* NO wmediumd detected, perfect medium simulation */
|
||||||
|
data->tx_pkts++;
|
||||||
|
data->tx_bytes += skb->len;
|
||||||
ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel);
|
ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel);
|
||||||
|
|
||||||
if (ack && skb->len >= 16) {
|
if (ack && skb->len >= 16) {
|
||||||
|
@ -1916,6 +1952,57 @@ static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw,
|
||||||
hwsim_check_chanctx_magic(ctx);
|
hwsim_check_chanctx_magic(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char mac80211_hwsim_gstrings_stats[][ETH_GSTRING_LEN] = {
|
||||||
|
"tx_pkts_nic",
|
||||||
|
"tx_bytes_nic",
|
||||||
|
"rx_pkts_nic",
|
||||||
|
"rx_bytes_nic",
|
||||||
|
"d_tx_dropped",
|
||||||
|
"d_tx_failed",
|
||||||
|
"d_ps_mode",
|
||||||
|
"d_group",
|
||||||
|
"d_tx_power",
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAC80211_HWSIM_SSTATS_LEN ARRAY_SIZE(mac80211_hwsim_gstrings_stats)
|
||||||
|
|
||||||
|
static void mac80211_hwsim_get_et_strings(struct ieee80211_hw *hw,
|
||||||
|
struct ieee80211_vif *vif,
|
||||||
|
u32 sset, u8 *data)
|
||||||
|
{
|
||||||
|
if (sset == ETH_SS_STATS)
|
||||||
|
memcpy(data, *mac80211_hwsim_gstrings_stats,
|
||||||
|
sizeof(mac80211_hwsim_gstrings_stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mac80211_hwsim_get_et_sset_count(struct ieee80211_hw *hw,
|
||||||
|
struct ieee80211_vif *vif, int sset)
|
||||||
|
{
|
||||||
|
if (sset == ETH_SS_STATS)
|
||||||
|
return MAC80211_HWSIM_SSTATS_LEN;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mac80211_hwsim_get_et_stats(struct ieee80211_hw *hw,
|
||||||
|
struct ieee80211_vif *vif,
|
||||||
|
struct ethtool_stats *stats, u64 *data)
|
||||||
|
{
|
||||||
|
struct mac80211_hwsim_data *ar = hw->priv;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
data[i++] = ar->tx_pkts;
|
||||||
|
data[i++] = ar->tx_bytes;
|
||||||
|
data[i++] = ar->rx_pkts;
|
||||||
|
data[i++] = ar->rx_bytes;
|
||||||
|
data[i++] = ar->tx_dropped;
|
||||||
|
data[i++] = ar->tx_failed;
|
||||||
|
data[i++] = ar->ps;
|
||||||
|
data[i++] = ar->group;
|
||||||
|
data[i++] = ar->power_level;
|
||||||
|
|
||||||
|
WARN_ON(i != MAC80211_HWSIM_SSTATS_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct ieee80211_ops mac80211_hwsim_ops = {
|
static const struct ieee80211_ops mac80211_hwsim_ops = {
|
||||||
.tx = mac80211_hwsim_tx,
|
.tx = mac80211_hwsim_tx,
|
||||||
.start = mac80211_hwsim_start,
|
.start = mac80211_hwsim_start,
|
||||||
|
@ -1939,14 +2026,131 @@ static const struct ieee80211_ops mac80211_hwsim_ops = {
|
||||||
.flush = mac80211_hwsim_flush,
|
.flush = mac80211_hwsim_flush,
|
||||||
.get_tsf = mac80211_hwsim_get_tsf,
|
.get_tsf = mac80211_hwsim_get_tsf,
|
||||||
.set_tsf = mac80211_hwsim_set_tsf,
|
.set_tsf = mac80211_hwsim_set_tsf,
|
||||||
|
.get_et_sset_count = mac80211_hwsim_get_et_sset_count,
|
||||||
|
.get_et_stats = mac80211_hwsim_get_et_stats,
|
||||||
|
.get_et_strings = mac80211_hwsim_get_et_strings,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct ieee80211_ops mac80211_hwsim_mchan_ops;
|
static struct ieee80211_ops mac80211_hwsim_mchan_ops;
|
||||||
|
|
||||||
static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
struct hwsim_new_radio_params {
|
||||||
const struct ieee80211_regdomain *regd,
|
unsigned int channels;
|
||||||
bool reg_strict, bool p2p_device,
|
const char *reg_alpha2;
|
||||||
bool use_chanctx)
|
const struct ieee80211_regdomain *regd;
|
||||||
|
bool reg_strict;
|
||||||
|
bool p2p_device;
|
||||||
|
bool use_chanctx;
|
||||||
|
bool destroy_on_close;
|
||||||
|
const char *hwname;
|
||||||
|
bool no_vif;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
|
||||||
|
struct genl_info *info)
|
||||||
|
{
|
||||||
|
if (info)
|
||||||
|
genl_notify(&hwsim_genl_family, mcast_skb,
|
||||||
|
genl_info_net(info), info->snd_portid,
|
||||||
|
HWSIM_MCGRP_CONFIG, info->nlhdr, GFP_KERNEL);
|
||||||
|
else
|
||||||
|
genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0,
|
||||||
|
HWSIM_MCGRP_CONFIG, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sk_buff *build_radio_msg(int cmd, int id,
|
||||||
|
struct hwsim_new_radio_params *param)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
void *data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||||
|
if (!skb)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0, cmd);
|
||||||
|
if (!data)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (param->channels) {
|
||||||
|
ret = nla_put_u32(skb, HWSIM_ATTR_CHANNELS, param->channels);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param->reg_alpha2) {
|
||||||
|
ret = nla_put(skb, HWSIM_ATTR_REG_HINT_ALPHA2, 2,
|
||||||
|
param->reg_alpha2);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param->regd) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; hwsim_world_regdom_custom[i] != param->regd &&
|
||||||
|
i < ARRAY_SIZE(hwsim_world_regdom_custom); i++)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (i < ARRAY_SIZE(hwsim_world_regdom_custom)) {
|
||||||
|
ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param->reg_strict) {
|
||||||
|
ret = nla_put_flag(skb, HWSIM_ATTR_REG_STRICT_REG);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param->p2p_device) {
|
||||||
|
ret = nla_put_flag(skb, HWSIM_ATTR_SUPPORT_P2P_DEVICE);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param->use_chanctx) {
|
||||||
|
ret = nla_put_flag(skb, HWSIM_ATTR_USE_CHANCTX);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param->hwname) {
|
||||||
|
ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME,
|
||||||
|
strlen(param->hwname), param->hwname);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
genlmsg_end(skb, data);
|
||||||
|
|
||||||
|
return skb;
|
||||||
|
|
||||||
|
error:
|
||||||
|
nlmsg_free(skb);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hswim_mcast_new_radio(int id, struct genl_info *info,
|
||||||
|
struct hwsim_new_radio_params *param)
|
||||||
|
{
|
||||||
|
struct sk_buff *mcast_skb;
|
||||||
|
|
||||||
|
mcast_skb = build_radio_msg(HWSIM_CMD_NEW_RADIO, id, param);
|
||||||
|
if (!mcast_skb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hwsim_mcast_config_msg(mcast_skb, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||||
|
struct hwsim_new_radio_params *param)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
u8 addr[ETH_ALEN];
|
u8 addr[ETH_ALEN];
|
||||||
|
@ -1956,16 +2160,16 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||||
const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
|
const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
|
||||||
int idx;
|
int idx;
|
||||||
|
|
||||||
if (WARN_ON(channels > 1 && !use_chanctx))
|
if (WARN_ON(param->channels > 1 && !param->use_chanctx))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
spin_lock_bh(&hwsim_radio_lock);
|
spin_lock_bh(&hwsim_radio_lock);
|
||||||
idx = hwsim_radio_idx++;
|
idx = hwsim_radio_idx++;
|
||||||
spin_unlock_bh(&hwsim_radio_lock);
|
spin_unlock_bh(&hwsim_radio_lock);
|
||||||
|
|
||||||
if (use_chanctx)
|
if (param->use_chanctx)
|
||||||
ops = &mac80211_hwsim_mchan_ops;
|
ops = &mac80211_hwsim_mchan_ops;
|
||||||
hw = ieee80211_alloc_hw(sizeof(*data), ops);
|
hw = ieee80211_alloc_hw_nm(sizeof(*data), ops, param->hwname);
|
||||||
if (!hw) {
|
if (!hw) {
|
||||||
printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n");
|
printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n");
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
@ -2003,9 +2207,12 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||||
hw->wiphy->n_addresses = 2;
|
hw->wiphy->n_addresses = 2;
|
||||||
hw->wiphy->addresses = data->addresses;
|
hw->wiphy->addresses = data->addresses;
|
||||||
|
|
||||||
data->channels = channels;
|
data->channels = param->channels;
|
||||||
data->use_chanctx = use_chanctx;
|
data->use_chanctx = param->use_chanctx;
|
||||||
data->idx = idx;
|
data->idx = idx;
|
||||||
|
data->destroy_on_close = param->destroy_on_close;
|
||||||
|
if (info)
|
||||||
|
data->portid = info->snd_portid;
|
||||||
|
|
||||||
if (data->use_chanctx) {
|
if (data->use_chanctx) {
|
||||||
hw->wiphy->max_scan_ssids = 255;
|
hw->wiphy->max_scan_ssids = 255;
|
||||||
|
@ -2014,12 +2221,12 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||||
/* For channels > 1 DFS is not allowed */
|
/* For channels > 1 DFS is not allowed */
|
||||||
hw->wiphy->n_iface_combinations = 1;
|
hw->wiphy->n_iface_combinations = 1;
|
||||||
hw->wiphy->iface_combinations = &data->if_combination;
|
hw->wiphy->iface_combinations = &data->if_combination;
|
||||||
if (p2p_device)
|
if (param->p2p_device)
|
||||||
data->if_combination = hwsim_if_comb_p2p_dev[0];
|
data->if_combination = hwsim_if_comb_p2p_dev[0];
|
||||||
else
|
else
|
||||||
data->if_combination = hwsim_if_comb[0];
|
data->if_combination = hwsim_if_comb[0];
|
||||||
data->if_combination.num_different_channels = data->channels;
|
data->if_combination.num_different_channels = data->channels;
|
||||||
} else if (p2p_device) {
|
} else if (param->p2p_device) {
|
||||||
hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev;
|
hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev;
|
||||||
hw->wiphy->n_iface_combinations =
|
hw->wiphy->n_iface_combinations =
|
||||||
ARRAY_SIZE(hwsim_if_comb_p2p_dev);
|
ARRAY_SIZE(hwsim_if_comb_p2p_dev);
|
||||||
|
@ -2040,7 +2247,7 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||||
BIT(NL80211_IFTYPE_ADHOC) |
|
BIT(NL80211_IFTYPE_ADHOC) |
|
||||||
BIT(NL80211_IFTYPE_MESH_POINT);
|
BIT(NL80211_IFTYPE_MESH_POINT);
|
||||||
|
|
||||||
if (p2p_device)
|
if (param->p2p_device)
|
||||||
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
|
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
|
||||||
|
|
||||||
hw->flags = IEEE80211_HW_MFP_CAPABLE |
|
hw->flags = IEEE80211_HW_MFP_CAPABLE |
|
||||||
|
@ -2095,6 +2302,7 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||||
sband->ht_cap.ht_supported = true;
|
sband->ht_cap.ht_supported = true;
|
||||||
sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
||||||
IEEE80211_HT_CAP_GRN_FLD |
|
IEEE80211_HT_CAP_GRN_FLD |
|
||||||
|
IEEE80211_HT_CAP_SGI_20 |
|
||||||
IEEE80211_HT_CAP_SGI_40 |
|
IEEE80211_HT_CAP_SGI_40 |
|
||||||
IEEE80211_HT_CAP_DSSSCCK40;
|
IEEE80211_HT_CAP_DSSSCCK40;
|
||||||
sband->ht_cap.ampdu_factor = 0x3;
|
sband->ht_cap.ampdu_factor = 0x3;
|
||||||
|
@ -2142,15 +2350,18 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||||
hw->max_rates = 4;
|
hw->max_rates = 4;
|
||||||
hw->max_rate_tries = 11;
|
hw->max_rate_tries = 11;
|
||||||
|
|
||||||
if (reg_strict)
|
if (param->reg_strict)
|
||||||
hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
|
hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
|
||||||
if (regd) {
|
if (param->regd) {
|
||||||
hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
|
hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
|
||||||
wiphy_apply_custom_regulatory(hw->wiphy, regd);
|
wiphy_apply_custom_regulatory(hw->wiphy, param->regd);
|
||||||
/* give the regulatory workqueue a chance to run */
|
/* give the regulatory workqueue a chance to run */
|
||||||
schedule_timeout_interruptible(1);
|
schedule_timeout_interruptible(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (param->no_vif)
|
||||||
|
hw->flags |= IEEE80211_HW_NO_AUTO_VIF;
|
||||||
|
|
||||||
err = ieee80211_register_hw(hw);
|
err = ieee80211_register_hw(hw);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n",
|
printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n",
|
||||||
|
@ -2160,8 +2371,8 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||||
|
|
||||||
wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr);
|
wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr);
|
||||||
|
|
||||||
if (reg_alpha2)
|
if (param->reg_alpha2)
|
||||||
regulatory_hint(hw->wiphy, reg_alpha2);
|
regulatory_hint(hw->wiphy, param->reg_alpha2);
|
||||||
|
|
||||||
data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir);
|
data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir);
|
||||||
debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps);
|
debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps);
|
||||||
|
@ -2180,6 +2391,9 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||||
list_add_tail(&data->list, &hwsim_radios);
|
list_add_tail(&data->list, &hwsim_radios);
|
||||||
spin_unlock_bh(&hwsim_radio_lock);
|
spin_unlock_bh(&hwsim_radio_lock);
|
||||||
|
|
||||||
|
if (idx > 0)
|
||||||
|
hswim_mcast_new_radio(idx, info, param);
|
||||||
|
|
||||||
return idx;
|
return idx;
|
||||||
|
|
||||||
failed_hw:
|
failed_hw:
|
||||||
|
@ -2190,8 +2404,48 @@ failed:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data)
|
static void hwsim_mcast_del_radio(int id, const char *hwname,
|
||||||
|
struct genl_info *info)
|
||||||
{
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
void *data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||||
|
if (!skb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
|
||||||
|
HWSIM_CMD_DEL_RADIO);
|
||||||
|
if (!data)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (hwname) {
|
||||||
|
ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME, strlen(hwname),
|
||||||
|
hwname);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
genlmsg_end(skb, data);
|
||||||
|
|
||||||
|
hwsim_mcast_config_msg(skb, info);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
error:
|
||||||
|
nlmsg_free(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mac80211_hwsim_del_radio(struct mac80211_hwsim_data *data,
|
||||||
|
const char *hwname,
|
||||||
|
struct genl_info *info)
|
||||||
|
{
|
||||||
|
hwsim_mcast_del_radio(data->idx, hwname, info);
|
||||||
debugfs_remove_recursive(data->debugfs);
|
debugfs_remove_recursive(data->debugfs);
|
||||||
ieee80211_unregister_hw(data->hw);
|
ieee80211_unregister_hw(data->hw);
|
||||||
device_release_driver(data->dev);
|
device_release_driver(data->dev);
|
||||||
|
@ -2209,7 +2463,7 @@ static void mac80211_hwsim_free(void)
|
||||||
list))) {
|
list))) {
|
||||||
list_del(&data->list);
|
list_del(&data->list);
|
||||||
spin_unlock_bh(&hwsim_radio_lock);
|
spin_unlock_bh(&hwsim_radio_lock);
|
||||||
mac80211_hwsim_destroy_radio(data);
|
mac80211_hwsim_del_radio(data, NULL, NULL);
|
||||||
spin_lock_bh(&hwsim_radio_lock);
|
spin_lock_bh(&hwsim_radio_lock);
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&hwsim_radio_lock);
|
spin_unlock_bh(&hwsim_radio_lock);
|
||||||
|
@ -2337,7 +2591,6 @@ out:
|
||||||
static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
||||||
struct genl_info *info)
|
struct genl_info *info)
|
||||||
{
|
{
|
||||||
|
|
||||||
struct mac80211_hwsim_data *data2;
|
struct mac80211_hwsim_data *data2;
|
||||||
struct ieee80211_rx_status rx_status;
|
struct ieee80211_rx_status rx_status;
|
||||||
const u8 *dst;
|
const u8 *dst;
|
||||||
|
@ -2380,18 +2633,22 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
||||||
|
|
||||||
/* A frame is received from user space */
|
/* A frame is received from user space */
|
||||||
memset(&rx_status, 0, sizeof(rx_status));
|
memset(&rx_status, 0, sizeof(rx_status));
|
||||||
|
/* TODO: Check ATTR_FREQ if it exists, and maybe throw away off-channel
|
||||||
|
* packets?
|
||||||
|
*/
|
||||||
rx_status.freq = data2->channel->center_freq;
|
rx_status.freq = data2->channel->center_freq;
|
||||||
rx_status.band = data2->channel->band;
|
rx_status.band = data2->channel->band;
|
||||||
rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
|
rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
|
||||||
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
|
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
|
||||||
|
|
||||||
memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
|
memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
|
||||||
|
data2->rx_pkts++;
|
||||||
|
data2->rx_bytes += skb->len;
|
||||||
ieee80211_rx_irqsafe(data2->hw, skb);
|
ieee80211_rx_irqsafe(data2->hw, skb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err:
|
||||||
printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
|
printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
|
||||||
goto out;
|
|
||||||
out:
|
out:
|
||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -2427,54 +2684,72 @@ static int hwsim_register_received_nl(struct sk_buff *skb_2,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||||
{
|
{
|
||||||
unsigned int chans = channels;
|
struct hwsim_new_radio_params param = { 0 };
|
||||||
const char *alpha2 = NULL;
|
|
||||||
const struct ieee80211_regdomain *regd = NULL;
|
param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
|
||||||
bool reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
|
param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
|
||||||
bool p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
|
param.channels = channels;
|
||||||
bool use_chanctx;
|
param.destroy_on_close =
|
||||||
|
info->attrs[HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE];
|
||||||
|
|
||||||
if (info->attrs[HWSIM_ATTR_CHANNELS])
|
if (info->attrs[HWSIM_ATTR_CHANNELS])
|
||||||
chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
|
param.channels = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
|
||||||
|
|
||||||
|
if (info->attrs[HWSIM_ATTR_NO_VIF])
|
||||||
|
param.no_vif = true;
|
||||||
|
|
||||||
|
if (info->attrs[HWSIM_ATTR_RADIO_NAME])
|
||||||
|
param.hwname = nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
|
||||||
|
|
||||||
if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
|
if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
|
||||||
use_chanctx = true;
|
param.use_chanctx = true;
|
||||||
else
|
else
|
||||||
use_chanctx = (chans > 1);
|
param.use_chanctx = (param.channels > 1);
|
||||||
|
|
||||||
if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
|
if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
|
||||||
alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
|
param.reg_alpha2 =
|
||||||
|
nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
|
||||||
|
|
||||||
if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
|
if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
|
||||||
u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
|
u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
|
||||||
|
|
||||||
if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
|
if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
regd = hwsim_world_regdom_custom[idx];
|
param.regd = hwsim_world_regdom_custom[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict,
|
return mac80211_hwsim_new_radio(info, ¶m);
|
||||||
p2p_device, use_chanctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||||
{
|
{
|
||||||
struct mac80211_hwsim_data *data;
|
struct mac80211_hwsim_data *data;
|
||||||
int idx;
|
s64 idx = -1;
|
||||||
|
const char *hwname = NULL;
|
||||||
|
|
||||||
if (!info->attrs[HWSIM_ATTR_RADIO_ID])
|
if (info->attrs[HWSIM_ATTR_RADIO_ID])
|
||||||
|
idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
|
||||||
|
else if (info->attrs[HWSIM_ATTR_RADIO_NAME])
|
||||||
|
hwname = (void *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
|
||||||
|
else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
|
|
||||||
|
|
||||||
spin_lock_bh(&hwsim_radio_lock);
|
spin_lock_bh(&hwsim_radio_lock);
|
||||||
list_for_each_entry(data, &hwsim_radios, list) {
|
list_for_each_entry(data, &hwsim_radios, list) {
|
||||||
if (data->idx != idx)
|
if (idx >= 0) {
|
||||||
continue;
|
if (data->idx != idx)
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (hwname &&
|
||||||
|
strcmp(hwname, wiphy_name(data->hw->wiphy)))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
list_del(&data->list);
|
list_del(&data->list);
|
||||||
spin_unlock_bh(&hwsim_radio_lock);
|
spin_unlock_bh(&hwsim_radio_lock);
|
||||||
mac80211_hwsim_destroy_radio(data);
|
mac80211_hwsim_del_radio(data, hwname, info);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&hwsim_radio_lock);
|
spin_unlock_bh(&hwsim_radio_lock);
|
||||||
|
@ -2501,19 +2776,42 @@ static const struct genl_ops hwsim_ops[] = {
|
||||||
.doit = hwsim_tx_info_frame_received_nl,
|
.doit = hwsim_tx_info_frame_received_nl,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.cmd = HWSIM_CMD_CREATE_RADIO,
|
.cmd = HWSIM_CMD_NEW_RADIO,
|
||||||
.policy = hwsim_genl_policy,
|
.policy = hwsim_genl_policy,
|
||||||
.doit = hwsim_create_radio_nl,
|
.doit = hwsim_new_radio_nl,
|
||||||
.flags = GENL_ADMIN_PERM,
|
.flags = GENL_ADMIN_PERM,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.cmd = HWSIM_CMD_DESTROY_RADIO,
|
.cmd = HWSIM_CMD_DEL_RADIO,
|
||||||
.policy = hwsim_genl_policy,
|
.policy = hwsim_genl_policy,
|
||||||
.doit = hwsim_destroy_radio_nl,
|
.doit = hwsim_del_radio_nl,
|
||||||
.flags = GENL_ADMIN_PERM,
|
.flags = GENL_ADMIN_PERM,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void destroy_radio(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct mac80211_hwsim_data *data =
|
||||||
|
container_of(work, struct mac80211_hwsim_data, destroy_work);
|
||||||
|
|
||||||
|
mac80211_hwsim_del_radio(data, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_user_radios(u32 portid)
|
||||||
|
{
|
||||||
|
struct mac80211_hwsim_data *entry, *tmp;
|
||||||
|
|
||||||
|
spin_lock_bh(&hwsim_radio_lock);
|
||||||
|
list_for_each_entry_safe(entry, tmp, &hwsim_radios, list) {
|
||||||
|
if (entry->destroy_on_close && entry->portid == portid) {
|
||||||
|
list_del(&entry->list);
|
||||||
|
INIT_WORK(&entry->destroy_work, destroy_radio);
|
||||||
|
schedule_work(&entry->destroy_work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock_bh(&hwsim_radio_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
|
static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
|
||||||
unsigned long state,
|
unsigned long state,
|
||||||
void *_notify)
|
void *_notify)
|
||||||
|
@ -2523,6 +2821,8 @@ static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
|
||||||
if (state != NETLINK_URELEASE)
|
if (state != NETLINK_URELEASE)
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
|
remove_user_radios(notify->portid);
|
||||||
|
|
||||||
if (notify->portid == wmediumd_portid) {
|
if (notify->portid == wmediumd_portid) {
|
||||||
printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
|
printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
|
||||||
" socket, switching to perfect channel medium\n");
|
" socket, switching to perfect channel medium\n");
|
||||||
|
@ -2542,7 +2842,9 @@ static int hwsim_init_netlink(void)
|
||||||
|
|
||||||
printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
|
printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
|
||||||
|
|
||||||
rc = genl_register_family_with_ops(&hwsim_genl_family, hwsim_ops);
|
rc = genl_register_family_with_ops_groups(&hwsim_genl_family,
|
||||||
|
hwsim_ops,
|
||||||
|
hwsim_mcgrps);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto failure;
|
goto failure;
|
||||||
|
|
||||||
|
@ -2603,69 +2905,73 @@ static int __init init_mac80211_hwsim(void)
|
||||||
goto out_unregister_driver;
|
goto out_unregister_driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = hwsim_init_netlink();
|
||||||
|
if (err < 0)
|
||||||
|
goto out_unregister_driver;
|
||||||
|
|
||||||
for (i = 0; i < radios; i++) {
|
for (i = 0; i < radios; i++) {
|
||||||
const char *reg_alpha2 = NULL;
|
struct hwsim_new_radio_params param = { 0 };
|
||||||
const struct ieee80211_regdomain *regd = NULL;
|
|
||||||
bool reg_strict = false;
|
param.channels = channels;
|
||||||
|
|
||||||
switch (regtest) {
|
switch (regtest) {
|
||||||
case HWSIM_REGTEST_DIFF_COUNTRY:
|
case HWSIM_REGTEST_DIFF_COUNTRY:
|
||||||
if (i < ARRAY_SIZE(hwsim_alpha2s))
|
if (i < ARRAY_SIZE(hwsim_alpha2s))
|
||||||
reg_alpha2 = hwsim_alpha2s[i];
|
param.reg_alpha2 = hwsim_alpha2s[i];
|
||||||
break;
|
break;
|
||||||
case HWSIM_REGTEST_DRIVER_REG_FOLLOW:
|
case HWSIM_REGTEST_DRIVER_REG_FOLLOW:
|
||||||
if (!i)
|
if (!i)
|
||||||
reg_alpha2 = hwsim_alpha2s[0];
|
param.reg_alpha2 = hwsim_alpha2s[0];
|
||||||
break;
|
break;
|
||||||
case HWSIM_REGTEST_STRICT_ALL:
|
case HWSIM_REGTEST_STRICT_ALL:
|
||||||
reg_strict = true;
|
param.reg_strict = true;
|
||||||
case HWSIM_REGTEST_DRIVER_REG_ALL:
|
case HWSIM_REGTEST_DRIVER_REG_ALL:
|
||||||
reg_alpha2 = hwsim_alpha2s[0];
|
param.reg_alpha2 = hwsim_alpha2s[0];
|
||||||
break;
|
break;
|
||||||
case HWSIM_REGTEST_WORLD_ROAM:
|
case HWSIM_REGTEST_WORLD_ROAM:
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
regd = &hwsim_world_regdom_custom_01;
|
param.regd = &hwsim_world_regdom_custom_01;
|
||||||
break;
|
break;
|
||||||
case HWSIM_REGTEST_CUSTOM_WORLD:
|
case HWSIM_REGTEST_CUSTOM_WORLD:
|
||||||
regd = &hwsim_world_regdom_custom_01;
|
param.regd = &hwsim_world_regdom_custom_01;
|
||||||
break;
|
break;
|
||||||
case HWSIM_REGTEST_CUSTOM_WORLD_2:
|
case HWSIM_REGTEST_CUSTOM_WORLD_2:
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
regd = &hwsim_world_regdom_custom_01;
|
param.regd = &hwsim_world_regdom_custom_01;
|
||||||
else if (i == 1)
|
else if (i == 1)
|
||||||
regd = &hwsim_world_regdom_custom_02;
|
param.regd = &hwsim_world_regdom_custom_02;
|
||||||
break;
|
break;
|
||||||
case HWSIM_REGTEST_STRICT_FOLLOW:
|
case HWSIM_REGTEST_STRICT_FOLLOW:
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
reg_strict = true;
|
param.reg_strict = true;
|
||||||
reg_alpha2 = hwsim_alpha2s[0];
|
param.reg_alpha2 = hwsim_alpha2s[0];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
|
case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
reg_strict = true;
|
param.reg_strict = true;
|
||||||
reg_alpha2 = hwsim_alpha2s[0];
|
param.reg_alpha2 = hwsim_alpha2s[0];
|
||||||
} else if (i == 1) {
|
} else if (i == 1) {
|
||||||
reg_alpha2 = hwsim_alpha2s[1];
|
param.reg_alpha2 = hwsim_alpha2s[1];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HWSIM_REGTEST_ALL:
|
case HWSIM_REGTEST_ALL:
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 0:
|
case 0:
|
||||||
regd = &hwsim_world_regdom_custom_01;
|
param.regd = &hwsim_world_regdom_custom_01;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
regd = &hwsim_world_regdom_custom_02;
|
param.regd = &hwsim_world_regdom_custom_02;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
reg_alpha2 = hwsim_alpha2s[0];
|
param.reg_alpha2 = hwsim_alpha2s[0];
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
reg_alpha2 = hwsim_alpha2s[1];
|
param.reg_alpha2 = hwsim_alpha2s[1];
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
reg_strict = true;
|
param.reg_strict = true;
|
||||||
reg_alpha2 = hwsim_alpha2s[2];
|
param.reg_alpha2 = hwsim_alpha2s[2];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2673,10 +2979,10 @@ static int __init init_mac80211_hwsim(void)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mac80211_hwsim_create_radio(channels, reg_alpha2,
|
param.p2p_device = support_p2p_device;
|
||||||
regd, reg_strict,
|
param.use_chanctx = channels > 1;
|
||||||
support_p2p_device,
|
|
||||||
channels > 1);
|
err = mac80211_hwsim_new_radio(NULL, ¶m);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto out_free_radios;
|
goto out_free_radios;
|
||||||
}
|
}
|
||||||
|
@ -2702,10 +3008,6 @@ static int __init init_mac80211_hwsim(void)
|
||||||
}
|
}
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
|
|
||||||
err = hwsim_init_netlink();
|
|
||||||
if (err < 0)
|
|
||||||
goto out_free_mon;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free_mon:
|
out_free_mon:
|
||||||
|
|
|
@ -60,14 +60,15 @@ enum hwsim_tx_control_flags {
|
||||||
* space, uses:
|
* space, uses:
|
||||||
* %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
|
* %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
|
||||||
* %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
|
* %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
|
||||||
* %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
|
* %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE, %HWSIM_ATTR_FREQ (optional)
|
||||||
* @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
|
* @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
|
||||||
* kernel, uses:
|
* kernel, uses:
|
||||||
* %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
|
* %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
|
||||||
* %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
|
* %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
|
||||||
* @HWSIM_CMD_CREATE_RADIO: create a new radio with the given parameters,
|
* @HWSIM_CMD_NEW_RADIO: create a new radio with the given parameters,
|
||||||
* returns the radio ID (>= 0) or negative on errors
|
* returns the radio ID (>= 0) or negative on errors, if successful
|
||||||
* @HWSIM_CMD_DESTROY_RADIO: destroy a radio
|
* then multicast the result
|
||||||
|
* @HWSIM_CMD_DEL_RADIO: destroy a radio, reply is multicasted
|
||||||
* @__HWSIM_CMD_MAX: enum limit
|
* @__HWSIM_CMD_MAX: enum limit
|
||||||
*/
|
*/
|
||||||
enum {
|
enum {
|
||||||
|
@ -75,12 +76,15 @@ enum {
|
||||||
HWSIM_CMD_REGISTER,
|
HWSIM_CMD_REGISTER,
|
||||||
HWSIM_CMD_FRAME,
|
HWSIM_CMD_FRAME,
|
||||||
HWSIM_CMD_TX_INFO_FRAME,
|
HWSIM_CMD_TX_INFO_FRAME,
|
||||||
HWSIM_CMD_CREATE_RADIO,
|
HWSIM_CMD_NEW_RADIO,
|
||||||
HWSIM_CMD_DESTROY_RADIO,
|
HWSIM_CMD_DEL_RADIO,
|
||||||
__HWSIM_CMD_MAX,
|
__HWSIM_CMD_MAX,
|
||||||
};
|
};
|
||||||
#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
|
#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
|
||||||
|
|
||||||
|
#define HWSIM_CMD_CREATE_RADIO HWSIM_CMD_NEW_RADIO
|
||||||
|
#define HWSIM_CMD_DESTROY_RADIO HWSIM_CMD_DEL_RADIO
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum hwsim_attrs - hwsim netlink attributes
|
* enum hwsim_attrs - hwsim netlink attributes
|
||||||
*
|
*
|
||||||
|
@ -111,6 +115,11 @@ enum {
|
||||||
* @HWSIM_ATTR_USE_CHANCTX: used with the %HWSIM_CMD_CREATE_RADIO
|
* @HWSIM_ATTR_USE_CHANCTX: used with the %HWSIM_CMD_CREATE_RADIO
|
||||||
* command to force use of channel contexts even when only a
|
* command to force use of channel contexts even when only a
|
||||||
* single channel is supported
|
* single channel is supported
|
||||||
|
* @HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE: used with the %HWSIM_CMD_CREATE_RADIO
|
||||||
|
* command to force radio removal when process that created the radio dies
|
||||||
|
* @HWSIM_ATTR_RADIO_NAME: Name of radio, e.g. phy666
|
||||||
|
* @HWSIM_ATTR_NO_VIF: Do not create vif (wlanX) when creating radio.
|
||||||
|
* @HWSIM_ATTR_FREQ: Frequency at which packet is transmitted or received.
|
||||||
* @__HWSIM_ATTR_MAX: enum limit
|
* @__HWSIM_ATTR_MAX: enum limit
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -132,6 +141,10 @@ enum {
|
||||||
HWSIM_ATTR_REG_STRICT_REG,
|
HWSIM_ATTR_REG_STRICT_REG,
|
||||||
HWSIM_ATTR_SUPPORT_P2P_DEVICE,
|
HWSIM_ATTR_SUPPORT_P2P_DEVICE,
|
||||||
HWSIM_ATTR_USE_CHANCTX,
|
HWSIM_ATTR_USE_CHANCTX,
|
||||||
|
HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE,
|
||||||
|
HWSIM_ATTR_RADIO_NAME,
|
||||||
|
HWSIM_ATTR_NO_VIF,
|
||||||
|
HWSIM_ATTR_FREQ,
|
||||||
__HWSIM_ATTR_MAX,
|
__HWSIM_ATTR_MAX,
|
||||||
};
|
};
|
||||||
#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
|
#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
|
||||||
|
|
|
@ -1285,7 +1285,7 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy,
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
|
mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
|
||||||
const u8 *mac)
|
struct station_del_parameters *params)
|
||||||
{
|
{
|
||||||
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
|
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
|
||||||
struct mwifiex_sta_node *sta_node;
|
struct mwifiex_sta_node *sta_node;
|
||||||
|
@ -1294,7 +1294,7 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
|
||||||
if (list_empty(&priv->sta_list) || !priv->bss_started)
|
if (list_empty(&priv->sta_list) || !priv->bss_started)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!mac || is_broadcast_ether_addr(mac)) {
|
if (!params->mac || is_broadcast_ether_addr(params->mac)) {
|
||||||
wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
|
wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
|
||||||
list_for_each_entry(sta_node, &priv->sta_list, list) {
|
list_for_each_entry(sta_node, &priv->sta_list, list) {
|
||||||
if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
|
if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
|
||||||
|
@ -1304,9 +1304,10 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
|
||||||
mwifiex_uap_del_sta_data(priv, sta_node);
|
mwifiex_uap_del_sta_data(priv, sta_node);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, mac);
|
wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__,
|
||||||
|
params->mac);
|
||||||
spin_lock_irqsave(&priv->sta_list_spinlock, flags);
|
spin_lock_irqsave(&priv->sta_list_spinlock, flags);
|
||||||
sta_node = mwifiex_get_sta_entry(priv, mac);
|
sta_node = mwifiex_get_sta_entry(priv, params->mac);
|
||||||
spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
|
spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
|
||||||
if (sta_node) {
|
if (sta_node) {
|
||||||
if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
|
if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
|
||||||
|
|
|
@ -5177,10 +5177,11 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
|
static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
|
||||||
|
struct ieee80211_vif *vif,
|
||||||
struct ieee80211_channel_switch *ch_switch)
|
struct ieee80211_channel_switch *ch_switch)
|
||||||
{
|
{
|
||||||
struct wl1271 *wl = hw->priv;
|
struct wl1271 *wl = hw->priv;
|
||||||
struct wl12xx_vif *wlvif;
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
|
wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
|
||||||
|
@ -5190,14 +5191,8 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
|
||||||
mutex_lock(&wl->mutex);
|
mutex_lock(&wl->mutex);
|
||||||
|
|
||||||
if (unlikely(wl->state == WLCORE_STATE_OFF)) {
|
if (unlikely(wl->state == WLCORE_STATE_OFF)) {
|
||||||
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
||||||
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
|
|
||||||
|
|
||||||
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ieee80211_chswitch_done(vif, false);
|
ieee80211_chswitch_done(vif, false);
|
||||||
}
|
|
||||||
goto out;
|
goto out;
|
||||||
} else if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
} else if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -5208,11 +5203,9 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* TODO: change mac80211 to pass vif as param */
|
/* TODO: change mac80211 to pass vif as param */
|
||||||
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
|
||||||
unsigned long delay_usec;
|
|
||||||
|
|
||||||
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
|
||||||
continue;
|
unsigned long delay_usec;
|
||||||
|
|
||||||
ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
|
ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -5222,10 +5215,10 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
|
||||||
|
|
||||||
/* indicate failure 5 seconds after channel switch time */
|
/* indicate failure 5 seconds after channel switch time */
|
||||||
delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) *
|
delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) *
|
||||||
ch_switch->count;
|
ch_switch->count;
|
||||||
ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work,
|
ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work,
|
||||||
usecs_to_jiffies(delay_usec) +
|
usecs_to_jiffies(delay_usec) +
|
||||||
msecs_to_jiffies(5000));
|
msecs_to_jiffies(5000));
|
||||||
}
|
}
|
||||||
|
|
||||||
out_sleep:
|
out_sleep:
|
||||||
|
|
|
@ -2856,8 +2856,10 @@ static int cfg80211_rtw_add_station(struct wiphy *wiphy,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cfg80211_rtw_del_station(struct wiphy *wiphy,
|
static int cfg80211_rtw_del_station(struct wiphy *wiphy,
|
||||||
struct net_device *ndev, const u8 *mac)
|
struct net_device *ndev,
|
||||||
|
struct station_del_parameters *params)
|
||||||
{
|
{
|
||||||
|
const u8 *mac = params->mac;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct list_head *phead, *plist, *ptmp;
|
struct list_head *phead, *plist, *ptmp;
|
||||||
u8 updated = 0;
|
u8 updated = 0;
|
||||||
|
|
|
@ -1274,7 +1274,7 @@ struct ieee80211_ht_cap {
|
||||||
#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2
|
#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Maximum length of AMPDU that the STA can receive.
|
* Maximum length of AMPDU that the STA can receive in high-throughput (HT).
|
||||||
* Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
|
* Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
|
||||||
*/
|
*/
|
||||||
enum ieee80211_max_ampdu_length_exp {
|
enum ieee80211_max_ampdu_length_exp {
|
||||||
|
@ -1284,6 +1284,21 @@ enum ieee80211_max_ampdu_length_exp {
|
||||||
IEEE80211_HT_MAX_AMPDU_64K = 3
|
IEEE80211_HT_MAX_AMPDU_64K = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maximum length of AMPDU that the STA can receive in VHT.
|
||||||
|
* Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
|
||||||
|
*/
|
||||||
|
enum ieee80211_vht_max_ampdu_length_exp {
|
||||||
|
IEEE80211_VHT_MAX_AMPDU_8K = 0,
|
||||||
|
IEEE80211_VHT_MAX_AMPDU_16K = 1,
|
||||||
|
IEEE80211_VHT_MAX_AMPDU_32K = 2,
|
||||||
|
IEEE80211_VHT_MAX_AMPDU_64K = 3,
|
||||||
|
IEEE80211_VHT_MAX_AMPDU_128K = 4,
|
||||||
|
IEEE80211_VHT_MAX_AMPDU_256K = 5,
|
||||||
|
IEEE80211_VHT_MAX_AMPDU_512K = 6,
|
||||||
|
IEEE80211_VHT_MAX_AMPDU_1024K = 7
|
||||||
|
};
|
||||||
|
|
||||||
#define IEEE80211_HT_MAX_AMPDU_FACTOR 13
|
#define IEEE80211_HT_MAX_AMPDU_FACTOR 13
|
||||||
|
|
||||||
/* Minimum MPDU start spacing */
|
/* Minimum MPDU start spacing */
|
||||||
|
@ -1998,6 +2013,11 @@ enum ieee80211_tdls_actioncode {
|
||||||
WLAN_TDLS_DISCOVERY_REQUEST = 10,
|
WLAN_TDLS_DISCOVERY_REQUEST = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Extended Channel Switching capability to be set in the 1st byte of
|
||||||
|
* the @WLAN_EID_EXT_CAPABILITY information element
|
||||||
|
*/
|
||||||
|
#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING BIT(2)
|
||||||
|
|
||||||
/* Interworking capabilities are set in 7th bit of 4th byte of the
|
/* Interworking capabilities are set in 7th bit of 4th byte of the
|
||||||
* @WLAN_EID_EXT_CAPABILITY information element
|
* @WLAN_EID_EXT_CAPABILITY information element
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -319,9 +319,12 @@ struct ieee80211_supported_band {
|
||||||
/**
|
/**
|
||||||
* struct vif_params - describes virtual interface parameters
|
* struct vif_params - describes virtual interface parameters
|
||||||
* @use_4addr: use 4-address frames
|
* @use_4addr: use 4-address frames
|
||||||
* @macaddr: address to use for this virtual interface. This will only
|
* @macaddr: address to use for this virtual interface.
|
||||||
* be used for non-netdevice interfaces. If this parameter is set
|
* If this parameter is set to zero address the driver may
|
||||||
* to zero address the driver may determine the address as needed.
|
* determine the address as needed.
|
||||||
|
* This feature is only fully supported by drivers that enable the
|
||||||
|
* %NL80211_FEATURE_MAC_ON_CREATE flag. Others may support creating
|
||||||
|
** only p2p devices with specified MAC.
|
||||||
*/
|
*/
|
||||||
struct vif_params {
|
struct vif_params {
|
||||||
int use_4addr;
|
int use_4addr;
|
||||||
|
@ -798,6 +801,22 @@ struct station_parameters {
|
||||||
bool opmode_notif_used;
|
bool opmode_notif_used;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct station_del_parameters - station deletion parameters
|
||||||
|
*
|
||||||
|
* Used to delete a station entry (or all stations).
|
||||||
|
*
|
||||||
|
* @mac: MAC address of the station to remove or NULL to remove all stations
|
||||||
|
* @subtype: Management frame subtype to use for indicating removal
|
||||||
|
* (10 = Disassociation, 12 = Deauthentication)
|
||||||
|
* @reason_code: Reason code for the Disassociation/Deauthentication frame
|
||||||
|
*/
|
||||||
|
struct station_del_parameters {
|
||||||
|
const u8 *mac;
|
||||||
|
u8 subtype;
|
||||||
|
u16 reason_code;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum cfg80211_station_type - the type of station being modified
|
* enum cfg80211_station_type - the type of station being modified
|
||||||
* @CFG80211_STA_AP_CLIENT: client of an AP interface
|
* @CFG80211_STA_AP_CLIENT: client of an AP interface
|
||||||
|
@ -1339,6 +1358,16 @@ struct mesh_setup {
|
||||||
u32 basic_rates;
|
u32 basic_rates;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ocb_setup - 802.11p OCB mode setup configuration
|
||||||
|
* @chandef: defines the channel to use
|
||||||
|
*
|
||||||
|
* These parameters are fixed when connecting to the network
|
||||||
|
*/
|
||||||
|
struct ocb_setup {
|
||||||
|
struct cfg80211_chan_def chandef;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ieee80211_txq_params - TX queue parameters
|
* struct ieee80211_txq_params - TX queue parameters
|
||||||
* @ac: AC identifier
|
* @ac: AC identifier
|
||||||
|
@ -2132,7 +2161,7 @@ struct cfg80211_qos_map {
|
||||||
* @stop_ap: Stop being an AP, including stopping beaconing.
|
* @stop_ap: Stop being an AP, including stopping beaconing.
|
||||||
*
|
*
|
||||||
* @add_station: Add a new station.
|
* @add_station: Add a new station.
|
||||||
* @del_station: Remove a station; @mac may be NULL to remove all stations.
|
* @del_station: Remove a station
|
||||||
* @change_station: Modify a given station. Note that flags changes are not much
|
* @change_station: Modify a given station. Note that flags changes are not much
|
||||||
* validated in cfg80211, in particular the auth/assoc/authorized flags
|
* validated in cfg80211, in particular the auth/assoc/authorized flags
|
||||||
* might come to the driver in invalid combinations -- make sure to check
|
* might come to the driver in invalid combinations -- make sure to check
|
||||||
|
@ -2146,6 +2175,8 @@ struct cfg80211_qos_map {
|
||||||
* @change_mpath: change a given mesh path
|
* @change_mpath: change a given mesh path
|
||||||
* @get_mpath: get a mesh path for the given parameters
|
* @get_mpath: get a mesh path for the given parameters
|
||||||
* @dump_mpath: dump mesh path callback -- resume dump at index @idx
|
* @dump_mpath: dump mesh path callback -- resume dump at index @idx
|
||||||
|
* @get_mpp: get a mesh proxy path for the given parameters
|
||||||
|
* @dump_mpp: dump mesh proxy path callback -- resume dump at index @idx
|
||||||
* @join_mesh: join the mesh network with the specified parameters
|
* @join_mesh: join the mesh network with the specified parameters
|
||||||
* (invoked with the wireless_dev mutex held)
|
* (invoked with the wireless_dev mutex held)
|
||||||
* @leave_mesh: leave the current mesh network
|
* @leave_mesh: leave the current mesh network
|
||||||
|
@ -2331,6 +2362,11 @@ struct cfg80211_qos_map {
|
||||||
* with the peer followed by immediate teardown when the addition is later
|
* with the peer followed by immediate teardown when the addition is later
|
||||||
* rejected)
|
* rejected)
|
||||||
* @del_tx_ts: remove an existing TX TS
|
* @del_tx_ts: remove an existing TX TS
|
||||||
|
*
|
||||||
|
* @join_ocb: join the OCB network with the specified parameters
|
||||||
|
* (invoked with the wireless_dev mutex held)
|
||||||
|
* @leave_ocb: leave the current OCB network
|
||||||
|
* (invoked with the wireless_dev mutex held)
|
||||||
*/
|
*/
|
||||||
struct cfg80211_ops {
|
struct cfg80211_ops {
|
||||||
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
|
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
|
||||||
|
@ -2376,7 +2412,7 @@ struct cfg80211_ops {
|
||||||
const u8 *mac,
|
const u8 *mac,
|
||||||
struct station_parameters *params);
|
struct station_parameters *params);
|
||||||
int (*del_station)(struct wiphy *wiphy, struct net_device *dev,
|
int (*del_station)(struct wiphy *wiphy, struct net_device *dev,
|
||||||
const u8 *mac);
|
struct station_del_parameters *params);
|
||||||
int (*change_station)(struct wiphy *wiphy, struct net_device *dev,
|
int (*change_station)(struct wiphy *wiphy, struct net_device *dev,
|
||||||
const u8 *mac,
|
const u8 *mac,
|
||||||
struct station_parameters *params);
|
struct station_parameters *params);
|
||||||
|
@ -2396,6 +2432,11 @@ struct cfg80211_ops {
|
||||||
int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
|
int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
|
||||||
int idx, u8 *dst, u8 *next_hop,
|
int idx, u8 *dst, u8 *next_hop,
|
||||||
struct mpath_info *pinfo);
|
struct mpath_info *pinfo);
|
||||||
|
int (*get_mpp)(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
u8 *dst, u8 *mpp, struct mpath_info *pinfo);
|
||||||
|
int (*dump_mpp)(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
int idx, u8 *dst, u8 *mpp,
|
||||||
|
struct mpath_info *pinfo);
|
||||||
int (*get_mesh_config)(struct wiphy *wiphy,
|
int (*get_mesh_config)(struct wiphy *wiphy,
|
||||||
struct net_device *dev,
|
struct net_device *dev,
|
||||||
struct mesh_config *conf);
|
struct mesh_config *conf);
|
||||||
|
@ -2407,6 +2448,10 @@ struct cfg80211_ops {
|
||||||
const struct mesh_setup *setup);
|
const struct mesh_setup *setup);
|
||||||
int (*leave_mesh)(struct wiphy *wiphy, struct net_device *dev);
|
int (*leave_mesh)(struct wiphy *wiphy, struct net_device *dev);
|
||||||
|
|
||||||
|
int (*join_ocb)(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
struct ocb_setup *setup);
|
||||||
|
int (*leave_ocb)(struct wiphy *wiphy, struct net_device *dev);
|
||||||
|
|
||||||
int (*change_bss)(struct wiphy *wiphy, struct net_device *dev,
|
int (*change_bss)(struct wiphy *wiphy, struct net_device *dev,
|
||||||
struct bss_parameters *params);
|
struct bss_parameters *params);
|
||||||
|
|
||||||
|
@ -2623,13 +2668,9 @@ struct cfg80211_ops {
|
||||||
* @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
|
* @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
|
||||||
* @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
|
* @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
|
||||||
* beaconing mode (AP, IBSS, Mesh, ...).
|
* beaconing mode (AP, IBSS, Mesh, ...).
|
||||||
* @WIPHY_FLAG_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
|
|
||||||
* TSPEC sessions (TID aka TSID 0-7) with the NL80211_CMD_ADD_TX_TS
|
|
||||||
* command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
|
|
||||||
* needs to be able to handle Block-Ack agreements and other things.
|
|
||||||
*/
|
*/
|
||||||
enum wiphy_flags {
|
enum wiphy_flags {
|
||||||
WIPHY_FLAG_SUPPORTS_WMM_ADMISSION = BIT(0),
|
/* use hole at 0 */
|
||||||
/* use hole at 1 */
|
/* use hole at 1 */
|
||||||
/* use hole at 2 */
|
/* use hole at 2 */
|
||||||
WIPHY_FLAG_NETNS_OK = BIT(3),
|
WIPHY_FLAG_NETNS_OK = BIT(3),
|
||||||
|
@ -3165,6 +3206,23 @@ static inline const char *wiphy_name(const struct wiphy *wiphy)
|
||||||
return dev_name(&wiphy->dev);
|
return dev_name(&wiphy->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wiphy_new_nm - create a new wiphy for use with cfg80211
|
||||||
|
*
|
||||||
|
* @ops: The configuration operations for this device
|
||||||
|
* @sizeof_priv: The size of the private area to allocate
|
||||||
|
* @requested_name: Request a particular name.
|
||||||
|
* NULL is valid value, and means use the default phy%d naming.
|
||||||
|
*
|
||||||
|
* Create a new wiphy and associate the given operations with it.
|
||||||
|
* @sizeof_priv bytes are allocated for private use.
|
||||||
|
*
|
||||||
|
* Return: A pointer to the new wiphy. This pointer must be
|
||||||
|
* assigned to each netdev's ieee80211_ptr for proper operation.
|
||||||
|
*/
|
||||||
|
struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
|
||||||
|
const char *requested_name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wiphy_new - create a new wiphy for use with cfg80211
|
* wiphy_new - create a new wiphy for use with cfg80211
|
||||||
*
|
*
|
||||||
|
@ -3177,7 +3235,11 @@ static inline const char *wiphy_name(const struct wiphy *wiphy)
|
||||||
* Return: A pointer to the new wiphy. This pointer must be
|
* Return: A pointer to the new wiphy. This pointer must be
|
||||||
* assigned to each netdev's ieee80211_ptr for proper operation.
|
* assigned to each netdev's ieee80211_ptr for proper operation.
|
||||||
*/
|
*/
|
||||||
struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv);
|
static inline struct wiphy *wiphy_new(const struct cfg80211_ops *ops,
|
||||||
|
int sizeof_priv)
|
||||||
|
{
|
||||||
|
return wiphy_new_nm(ops, sizeof_priv, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wiphy_register - register a wiphy with cfg80211
|
* wiphy_register - register a wiphy with cfg80211
|
||||||
|
|
|
@ -263,6 +263,7 @@ struct ieee80211_vif_chanctx_switch {
|
||||||
* @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
|
* @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
|
||||||
* note that this is only called when it changes after the channel
|
* note that this is only called when it changes after the channel
|
||||||
* context had been assigned.
|
* context had been assigned.
|
||||||
|
* @BSS_CHANGED_OCB: OCB join status changed
|
||||||
*/
|
*/
|
||||||
enum ieee80211_bss_change {
|
enum ieee80211_bss_change {
|
||||||
BSS_CHANGED_ASSOC = 1<<0,
|
BSS_CHANGED_ASSOC = 1<<0,
|
||||||
|
@ -287,6 +288,7 @@ enum ieee80211_bss_change {
|
||||||
BSS_CHANGED_P2P_PS = 1<<19,
|
BSS_CHANGED_P2P_PS = 1<<19,
|
||||||
BSS_CHANGED_BEACON_INFO = 1<<20,
|
BSS_CHANGED_BEACON_INFO = 1<<20,
|
||||||
BSS_CHANGED_BANDWIDTH = 1<<21,
|
BSS_CHANGED_BANDWIDTH = 1<<21,
|
||||||
|
BSS_CHANGED_OCB = 1<<22,
|
||||||
|
|
||||||
/* when adding here, make sure to change ieee80211_reconfig */
|
/* when adding here, make sure to change ieee80211_reconfig */
|
||||||
};
|
};
|
||||||
|
@ -739,7 +741,8 @@ struct ieee80211_tx_info {
|
||||||
u8 ampdu_ack_len;
|
u8 ampdu_ack_len;
|
||||||
u8 ampdu_len;
|
u8 ampdu_len;
|
||||||
u8 antenna;
|
u8 antenna;
|
||||||
void *status_driver_data[21 / sizeof(void *)];
|
u16 tx_time;
|
||||||
|
void *status_driver_data[19 / sizeof(void *)];
|
||||||
} status;
|
} status;
|
||||||
struct {
|
struct {
|
||||||
struct ieee80211_tx_rate driver_rates[
|
struct ieee80211_tx_rate driver_rates[
|
||||||
|
@ -1117,6 +1120,8 @@ struct ieee80211_conf {
|
||||||
* Function (TSF) timer when the frame containing the channel switch
|
* Function (TSF) timer when the frame containing the channel switch
|
||||||
* announcement was received. This is simply the rx.mactime parameter
|
* announcement was received. This is simply the rx.mactime parameter
|
||||||
* the driver passed into mac80211.
|
* the driver passed into mac80211.
|
||||||
|
* @device_timestamp: arbitrary timestamp for the device, this is the
|
||||||
|
* rx.device_timestamp parameter the driver passed to mac80211.
|
||||||
* @block_tx: Indicates whether transmission must be blocked before the
|
* @block_tx: Indicates whether transmission must be blocked before the
|
||||||
* scheduled channel switch, as indicated by the AP.
|
* scheduled channel switch, as indicated by the AP.
|
||||||
* @chandef: the new channel to switch to
|
* @chandef: the new channel to switch to
|
||||||
|
@ -1124,6 +1129,7 @@ struct ieee80211_conf {
|
||||||
*/
|
*/
|
||||||
struct ieee80211_channel_switch {
|
struct ieee80211_channel_switch {
|
||||||
u64 timestamp;
|
u64 timestamp;
|
||||||
|
u32 device_timestamp;
|
||||||
bool block_tx;
|
bool block_tx;
|
||||||
struct cfg80211_chan_def chandef;
|
struct cfg80211_chan_def chandef;
|
||||||
u8 count;
|
u8 count;
|
||||||
|
@ -1423,6 +1429,8 @@ struct ieee80211_sta_rates {
|
||||||
* @smps_mode: current SMPS mode (off, static or dynamic)
|
* @smps_mode: current SMPS mode (off, static or dynamic)
|
||||||
* @rates: rate control selection table
|
* @rates: rate control selection table
|
||||||
* @tdls: indicates whether the STA is a TDLS peer
|
* @tdls: indicates whether the STA is a TDLS peer
|
||||||
|
* @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
|
||||||
|
* valid if the STA is a TDLS peer in the first place.
|
||||||
*/
|
*/
|
||||||
struct ieee80211_sta {
|
struct ieee80211_sta {
|
||||||
u32 supp_rates[IEEE80211_NUM_BANDS];
|
u32 supp_rates[IEEE80211_NUM_BANDS];
|
||||||
|
@ -1438,6 +1446,7 @@ struct ieee80211_sta {
|
||||||
enum ieee80211_smps_mode smps_mode;
|
enum ieee80211_smps_mode smps_mode;
|
||||||
struct ieee80211_sta_rates __rcu *rates;
|
struct ieee80211_sta_rates __rcu *rates;
|
||||||
bool tdls;
|
bool tdls;
|
||||||
|
bool tdls_initiator;
|
||||||
|
|
||||||
/* must be last */
|
/* must be last */
|
||||||
u8 drv_priv[0] __aligned(sizeof(void *));
|
u8 drv_priv[0] __aligned(sizeof(void *));
|
||||||
|
@ -1576,6 +1585,10 @@ struct ieee80211_tx_control {
|
||||||
* a virtual monitor interface when monitor interfaces are the only
|
* a virtual monitor interface when monitor interfaces are the only
|
||||||
* active interfaces.
|
* active interfaces.
|
||||||
*
|
*
|
||||||
|
* @IEEE80211_HW_NO_AUTO_VIF: The driver would like for no wlanX to
|
||||||
|
* be created. It is expected user-space will create vifs as
|
||||||
|
* desired (and thus have them named as desired).
|
||||||
|
*
|
||||||
* @IEEE80211_HW_QUEUE_CONTROL: The driver wants to control per-interface
|
* @IEEE80211_HW_QUEUE_CONTROL: The driver wants to control per-interface
|
||||||
* queue mapping in order to use different queues (not just one per AC)
|
* queue mapping in order to use different queues (not just one per AC)
|
||||||
* for different virtual interfaces. See the doc section on HW queue
|
* for different virtual interfaces. See the doc section on HW queue
|
||||||
|
@ -1622,7 +1635,8 @@ enum ieee80211_hw_flags {
|
||||||
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
|
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
|
||||||
IEEE80211_HW_MFP_CAPABLE = 1<<13,
|
IEEE80211_HW_MFP_CAPABLE = 1<<13,
|
||||||
IEEE80211_HW_WANT_MONITOR_VIF = 1<<14,
|
IEEE80211_HW_WANT_MONITOR_VIF = 1<<14,
|
||||||
/* free slots */
|
IEEE80211_HW_NO_AUTO_VIF = 1<<15,
|
||||||
|
/* free slot */
|
||||||
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
|
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
|
||||||
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
|
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
|
||||||
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
|
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
|
||||||
|
@ -2374,6 +2388,22 @@ enum ieee80211_roc_type {
|
||||||
IEEE80211_ROC_TYPE_MGMT_TX,
|
IEEE80211_ROC_TYPE_MGMT_TX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ieee80211_reconfig_complete_type - reconfig type
|
||||||
|
*
|
||||||
|
* This enum is used by the reconfig_complete() callback to indicate what
|
||||||
|
* reconfiguration type was completed.
|
||||||
|
*
|
||||||
|
* @IEEE80211_RECONFIG_TYPE_RESTART: hw restart type
|
||||||
|
* (also due to resume() callback returning 1)
|
||||||
|
* @IEEE80211_RECONFIG_TYPE_SUSPEND: suspend type (regardless
|
||||||
|
* of wowlan configuration)
|
||||||
|
*/
|
||||||
|
enum ieee80211_reconfig_type {
|
||||||
|
IEEE80211_RECONFIG_TYPE_RESTART,
|
||||||
|
IEEE80211_RECONFIG_TYPE_SUSPEND,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ieee80211_ops - callbacks from mac80211 to the driver
|
* struct ieee80211_ops - callbacks from mac80211 to the driver
|
||||||
*
|
*
|
||||||
|
@ -2809,11 +2839,11 @@ enum ieee80211_roc_type {
|
||||||
* disabled/enabled via @bss_info_changed.
|
* disabled/enabled via @bss_info_changed.
|
||||||
* @stop_ap: Stop operation on the AP interface.
|
* @stop_ap: Stop operation on the AP interface.
|
||||||
*
|
*
|
||||||
* @restart_complete: Called after a call to ieee80211_restart_hw(), when the
|
* @reconfig_complete: Called after a call to ieee80211_restart_hw() and
|
||||||
* reconfiguration has completed. This can help the driver implement the
|
* during resume, when the reconfiguration has completed.
|
||||||
* reconfiguration step. Also called when reconfiguring because the
|
* This can help the driver implement the reconfiguration step (and
|
||||||
* driver's resume function returned 1, as this is just like an "inline"
|
* indicate mac80211 is ready to receive frames).
|
||||||
* hardware restart. This callback may sleep.
|
* This callback may sleep.
|
||||||
*
|
*
|
||||||
* @ipv6_addr_change: IPv6 address assignment on the given interface changed.
|
* @ipv6_addr_change: IPv6 address assignment on the given interface changed.
|
||||||
* Currently, this is only called for managed or P2P client interfaces.
|
* Currently, this is only called for managed or P2P client interfaces.
|
||||||
|
@ -2829,6 +2859,13 @@ enum ieee80211_roc_type {
|
||||||
* transmitted and then call ieee80211_csa_finish().
|
* transmitted and then call ieee80211_csa_finish().
|
||||||
* If the CSA count starts as zero or 1, this function will not be called,
|
* If the CSA count starts as zero or 1, this function will not be called,
|
||||||
* since there won't be any time to beacon before the switch anyway.
|
* since there won't be any time to beacon before the switch anyway.
|
||||||
|
* @pre_channel_switch: This is an optional callback that is called
|
||||||
|
* before a channel switch procedure is started (ie. when a STA
|
||||||
|
* gets a CSA or an userspace initiated channel-switch), allowing
|
||||||
|
* the driver to prepare for the channel switch.
|
||||||
|
* @post_channel_switch: This is an optional callback that is called
|
||||||
|
* after a channel switch procedure is completed, allowing the
|
||||||
|
* driver to go back to a normal configuration.
|
||||||
*
|
*
|
||||||
* @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
|
* @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
|
||||||
* information in bss_conf is set up and the beacon can be retrieved. A
|
* information in bss_conf is set up and the beacon can be retrieved. A
|
||||||
|
@ -2838,6 +2875,9 @@ enum ieee80211_roc_type {
|
||||||
* @get_expected_throughput: extract the expected throughput towards the
|
* @get_expected_throughput: extract the expected throughput towards the
|
||||||
* specified station. The returned value is expressed in Kbps. It returns 0
|
* specified station. The returned value is expressed in Kbps. It returns 0
|
||||||
* if the RC algorithm does not have proper data to provide.
|
* if the RC algorithm does not have proper data to provide.
|
||||||
|
*
|
||||||
|
* @get_txpower: get current maximum tx power (in dBm) based on configuration
|
||||||
|
* and hardware limits.
|
||||||
*/
|
*/
|
||||||
struct ieee80211_ops {
|
struct ieee80211_ops {
|
||||||
void (*tx)(struct ieee80211_hw *hw,
|
void (*tx)(struct ieee80211_hw *hw,
|
||||||
|
@ -2959,6 +2999,7 @@ struct ieee80211_ops {
|
||||||
void (*flush)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
void (*flush)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||||
u32 queues, bool drop);
|
u32 queues, bool drop);
|
||||||
void (*channel_switch)(struct ieee80211_hw *hw,
|
void (*channel_switch)(struct ieee80211_hw *hw,
|
||||||
|
struct ieee80211_vif *vif,
|
||||||
struct ieee80211_channel_switch *ch_switch);
|
struct ieee80211_channel_switch *ch_switch);
|
||||||
int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
|
int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
|
||||||
int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
|
int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
|
||||||
|
@ -3025,7 +3066,8 @@ struct ieee80211_ops {
|
||||||
int n_vifs,
|
int n_vifs,
|
||||||
enum ieee80211_chanctx_switch_mode mode);
|
enum ieee80211_chanctx_switch_mode mode);
|
||||||
|
|
||||||
void (*restart_complete)(struct ieee80211_hw *hw);
|
void (*reconfig_complete)(struct ieee80211_hw *hw,
|
||||||
|
enum ieee80211_reconfig_type reconfig_type);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
void (*ipv6_addr_change)(struct ieee80211_hw *hw,
|
void (*ipv6_addr_change)(struct ieee80211_hw *hw,
|
||||||
|
@ -3035,14 +3077,42 @@ struct ieee80211_ops {
|
||||||
void (*channel_switch_beacon)(struct ieee80211_hw *hw,
|
void (*channel_switch_beacon)(struct ieee80211_hw *hw,
|
||||||
struct ieee80211_vif *vif,
|
struct ieee80211_vif *vif,
|
||||||
struct cfg80211_chan_def *chandef);
|
struct cfg80211_chan_def *chandef);
|
||||||
|
int (*pre_channel_switch)(struct ieee80211_hw *hw,
|
||||||
|
struct ieee80211_vif *vif,
|
||||||
|
struct ieee80211_channel_switch *ch_switch);
|
||||||
|
|
||||||
|
int (*post_channel_switch)(struct ieee80211_hw *hw,
|
||||||
|
struct ieee80211_vif *vif);
|
||||||
|
|
||||||
int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||||
void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||||
u32 (*get_expected_throughput)(struct ieee80211_sta *sta);
|
u32 (*get_expected_throughput)(struct ieee80211_sta *sta);
|
||||||
|
int (*get_txpower)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||||
|
int *dbm);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_alloc_hw - Allocate a new hardware device
|
* ieee80211_alloc_hw_nm - Allocate a new hardware device
|
||||||
|
*
|
||||||
|
* This must be called once for each hardware device. The returned pointer
|
||||||
|
* must be used to refer to this device when calling other functions.
|
||||||
|
* mac80211 allocates a private data area for the driver pointed to by
|
||||||
|
* @priv in &struct ieee80211_hw, the size of this area is given as
|
||||||
|
* @priv_data_len.
|
||||||
|
*
|
||||||
|
* @priv_data_len: length of private data
|
||||||
|
* @ops: callbacks for this device
|
||||||
|
* @requested_name: Requested name for this device.
|
||||||
|
* NULL is valid value, and means use the default naming (phy%d)
|
||||||
|
*
|
||||||
|
* Return: A pointer to the new hardware device, or %NULL on error.
|
||||||
|
*/
|
||||||
|
struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
||||||
|
const struct ieee80211_ops *ops,
|
||||||
|
const char *requested_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ieee80211_alloc_hw - Allocate a new hardware device
|
||||||
*
|
*
|
||||||
* This must be called once for each hardware device. The returned pointer
|
* This must be called once for each hardware device. The returned pointer
|
||||||
* must be used to refer to this device when calling other functions.
|
* must be used to refer to this device when calling other functions.
|
||||||
|
@ -3055,8 +3125,12 @@ struct ieee80211_ops {
|
||||||
*
|
*
|
||||||
* Return: A pointer to the new hardware device, or %NULL on error.
|
* Return: A pointer to the new hardware device, or %NULL on error.
|
||||||
*/
|
*/
|
||||||
|
static inline
|
||||||
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||||
const struct ieee80211_ops *ops);
|
const struct ieee80211_ops *ops)
|
||||||
|
{
|
||||||
|
return ieee80211_alloc_hw_nm(priv_data_len, ops, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_register_hw - Register hardware device
|
* ieee80211_register_hw - Register hardware device
|
||||||
|
@ -4171,6 +4245,22 @@ void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw,
|
||||||
struct ieee80211_vif *vif),
|
struct ieee80211_vif *vif),
|
||||||
void *data);
|
void *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ieee80211_iterate_stations_atomic - iterate stations
|
||||||
|
*
|
||||||
|
* This function iterates over all stations associated with a given
|
||||||
|
* hardware that are currently uploaded to the driver and calls the callback
|
||||||
|
* function for them.
|
||||||
|
* This function requires the iterator callback function to be atomic,
|
||||||
|
*
|
||||||
|
* @hw: the hardware struct of which the interfaces should be iterated over
|
||||||
|
* @iterator: the iterator function to call, cannot sleep
|
||||||
|
* @data: first argument of the iterator function
|
||||||
|
*/
|
||||||
|
void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
|
||||||
|
void (*iterator)(void *data,
|
||||||
|
struct ieee80211_sta *sta),
|
||||||
|
void *data);
|
||||||
/**
|
/**
|
||||||
* ieee80211_queue_work - add work onto the mac80211 workqueue
|
* ieee80211_queue_work - add work onto the mac80211 workqueue
|
||||||
*
|
*
|
||||||
|
@ -4888,4 +4978,32 @@ void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf);
|
||||||
void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
|
void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
|
||||||
enum nl80211_tdls_operation oper,
|
enum nl80211_tdls_operation oper,
|
||||||
u16 reason_code, gfp_t gfp);
|
u16 reason_code, gfp_t gfp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ieee80211_ie_split - split an IE buffer according to ordering
|
||||||
|
*
|
||||||
|
* @ies: the IE buffer
|
||||||
|
* @ielen: the length of the IE buffer
|
||||||
|
* @ids: an array with element IDs that are allowed before
|
||||||
|
* the split
|
||||||
|
* @n_ids: the size of the element ID array
|
||||||
|
* @offset: offset where to start splitting in the buffer
|
||||||
|
*
|
||||||
|
* This function splits an IE buffer by updating the @offset
|
||||||
|
* variable to point to the location where the buffer should be
|
||||||
|
* split.
|
||||||
|
*
|
||||||
|
* It assumes that the given IE buffer is well-formed, this
|
||||||
|
* has to be guaranteed by the caller!
|
||||||
|
*
|
||||||
|
* It also assumes that the IEs in the buffer are ordered
|
||||||
|
* correctly, if not the result of using this function will not
|
||||||
|
* be ordered correctly either, i.e. it does no reordering.
|
||||||
|
*
|
||||||
|
* The function returns the offset where the next part of the
|
||||||
|
* buffer starts, which may be @ielen if the entire (remainder)
|
||||||
|
* of the buffer should be used.
|
||||||
|
*/
|
||||||
|
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
|
||||||
|
const u8 *ids, int n_ids, size_t offset);
|
||||||
#endif /* MAC80211_H */
|
#endif /* MAC80211_H */
|
||||||
|
|
|
@ -227,7 +227,11 @@
|
||||||
* the interface identified by %NL80211_ATTR_IFINDEX.
|
* the interface identified by %NL80211_ATTR_IFINDEX.
|
||||||
* @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
|
* @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
|
||||||
* or, if no MAC address given, all stations, on the interface identified
|
* or, if no MAC address given, all stations, on the interface identified
|
||||||
* by %NL80211_ATTR_IFINDEX.
|
* by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and
|
||||||
|
* %NL80211_ATTR_REASON_CODE can optionally be used to specify which type
|
||||||
|
* of disconnection indication should be sent to the station
|
||||||
|
* (Deauthentication or Disassociation frame and reason code for that
|
||||||
|
* frame).
|
||||||
*
|
*
|
||||||
* @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
|
* @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
|
||||||
* destination %NL80211_ATTR_MAC on the interface identified by
|
* destination %NL80211_ATTR_MAC on the interface identified by
|
||||||
|
@ -738,6 +742,15 @@
|
||||||
* before removing a station entry entirely, or before disassociating
|
* before removing a station entry entirely, or before disassociating
|
||||||
* or similar, cleanup will happen in the driver/device in this case.
|
* or similar, cleanup will happen in the driver/device in this case.
|
||||||
*
|
*
|
||||||
|
* @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to
|
||||||
|
* destination %NL80211_ATTR_MAC on the interface identified by
|
||||||
|
* %NL80211_ATTR_IFINDEX.
|
||||||
|
*
|
||||||
|
* @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and
|
||||||
|
* bandwidth of a channel must be given.
|
||||||
|
* @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
|
||||||
|
* network is determined by the network interface.
|
||||||
|
*
|
||||||
* @NL80211_CMD_MAX: highest used command number
|
* @NL80211_CMD_MAX: highest used command number
|
||||||
* @__NL80211_CMD_AFTER_LAST: internal use
|
* @__NL80211_CMD_AFTER_LAST: internal use
|
||||||
*/
|
*/
|
||||||
|
@ -912,6 +925,11 @@ enum nl80211_commands {
|
||||||
NL80211_CMD_ADD_TX_TS,
|
NL80211_CMD_ADD_TX_TS,
|
||||||
NL80211_CMD_DEL_TX_TS,
|
NL80211_CMD_DEL_TX_TS,
|
||||||
|
|
||||||
|
NL80211_CMD_GET_MPP,
|
||||||
|
|
||||||
|
NL80211_CMD_JOIN_OCB,
|
||||||
|
NL80211_CMD_LEAVE_OCB,
|
||||||
|
|
||||||
/* add new commands above here */
|
/* add new commands above here */
|
||||||
|
|
||||||
/* used to define NL80211_CMD_MAX below */
|
/* used to define NL80211_CMD_MAX below */
|
||||||
|
@ -2064,6 +2082,8 @@ enum nl80211_attrs {
|
||||||
* and therefore can't be created in the normal ways, use the
|
* and therefore can't be created in the normal ways, use the
|
||||||
* %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
|
* %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
|
||||||
* commands to create and destroy one
|
* commands to create and destroy one
|
||||||
|
* @NL80211_IF_TYPE_OCB: Outside Context of a BSS
|
||||||
|
* This mode corresponds to the MIB variable dot11OCBActivated=true
|
||||||
* @NL80211_IFTYPE_MAX: highest interface type number currently defined
|
* @NL80211_IFTYPE_MAX: highest interface type number currently defined
|
||||||
* @NUM_NL80211_IFTYPES: number of defined interface types
|
* @NUM_NL80211_IFTYPES: number of defined interface types
|
||||||
*
|
*
|
||||||
|
@ -2083,6 +2103,7 @@ enum nl80211_iftype {
|
||||||
NL80211_IFTYPE_P2P_CLIENT,
|
NL80211_IFTYPE_P2P_CLIENT,
|
||||||
NL80211_IFTYPE_P2P_GO,
|
NL80211_IFTYPE_P2P_GO,
|
||||||
NL80211_IFTYPE_P2P_DEVICE,
|
NL80211_IFTYPE_P2P_DEVICE,
|
||||||
|
NL80211_IFTYPE_OCB,
|
||||||
|
|
||||||
/* keep last */
|
/* keep last */
|
||||||
NUM_NL80211_IFTYPES,
|
NUM_NL80211_IFTYPES,
|
||||||
|
@ -4042,6 +4063,13 @@ enum nl80211_ap_sme_features {
|
||||||
* multiplexing powersave, ie. can turn off all but one chain
|
* multiplexing powersave, ie. can turn off all but one chain
|
||||||
* and then wake the rest up as required after, for example,
|
* and then wake the rest up as required after, for example,
|
||||||
* rts/cts handshake.
|
* rts/cts handshake.
|
||||||
|
* @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
|
||||||
|
* TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS
|
||||||
|
* command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
|
||||||
|
* needs to be able to handle Block-Ack agreements and other things.
|
||||||
|
* @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring
|
||||||
|
* the vif's MAC address upon creation.
|
||||||
|
* See 'macaddr' field in the vif_params (cfg80211.h).
|
||||||
*/
|
*/
|
||||||
enum nl80211_feature_flags {
|
enum nl80211_feature_flags {
|
||||||
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
|
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
|
||||||
|
@ -4070,6 +4098,8 @@ enum nl80211_feature_flags {
|
||||||
NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23,
|
NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23,
|
||||||
NL80211_FEATURE_STATIC_SMPS = 1 << 24,
|
NL80211_FEATURE_STATIC_SMPS = 1 << 24,
|
||||||
NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25,
|
NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25,
|
||||||
|
NL80211_FEATURE_SUPPORTS_WMM_ADMISSION = 1 << 26,
|
||||||
|
NL80211_FEATURE_MAC_ON_CREATE = 1 << 27,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -33,6 +33,13 @@ config MAC80211_RC_MINSTREL_HT
|
||||||
---help---
|
---help---
|
||||||
This option enables the 'minstrel_ht' TX rate control algorithm
|
This option enables the 'minstrel_ht' TX rate control algorithm
|
||||||
|
|
||||||
|
config MAC80211_RC_MINSTREL_VHT
|
||||||
|
bool "Minstrel 802.11ac support" if EXPERT
|
||||||
|
depends on MAC80211_RC_MINSTREL_HT
|
||||||
|
default n
|
||||||
|
---help---
|
||||||
|
This option enables VHT in the 'minstrel_ht' TX rate control algorithm
|
||||||
|
|
||||||
choice
|
choice
|
||||||
prompt "Default rate control algorithm"
|
prompt "Default rate control algorithm"
|
||||||
depends on MAC80211_HAS_RC
|
depends on MAC80211_HAS_RC
|
||||||
|
@ -169,6 +176,17 @@ config MAC80211_HT_DEBUG
|
||||||
|
|
||||||
Do not select this option.
|
Do not select this option.
|
||||||
|
|
||||||
|
config MAC80211_OCB_DEBUG
|
||||||
|
bool "Verbose OCB debugging"
|
||||||
|
depends on MAC80211_DEBUG_MENU
|
||||||
|
---help---
|
||||||
|
Selecting this option causes mac80211 to print out
|
||||||
|
very verbose OCB debugging messages. It should not
|
||||||
|
be selected on production systems as those messages
|
||||||
|
are remotely triggerable.
|
||||||
|
|
||||||
|
Do not select this option.
|
||||||
|
|
||||||
config MAC80211_IBSS_DEBUG
|
config MAC80211_IBSS_DEBUG
|
||||||
bool "Verbose IBSS debugging"
|
bool "Verbose IBSS debugging"
|
||||||
depends on MAC80211_DEBUG_MENU
|
depends on MAC80211_DEBUG_MENU
|
||||||
|
|
|
@ -27,7 +27,8 @@ mac80211-y := \
|
||||||
event.o \
|
event.o \
|
||||||
chan.o \
|
chan.o \
|
||||||
trace.o mlme.o \
|
trace.o mlme.o \
|
||||||
tdls.o
|
tdls.o \
|
||||||
|
ocb.o
|
||||||
|
|
||||||
mac80211-$(CONFIG_MAC80211_LEDS) += led.o
|
mac80211-$(CONFIG_MAC80211_LEDS) += led.o
|
||||||
mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
|
mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
|
||||||
|
|
|
@ -149,11 +149,6 @@ void ieee80211_assign_tid_tx(struct sta_info *sta, int tid,
|
||||||
rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
|
rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int ieee80211_ac_from_tid(int tid)
|
|
||||||
{
|
|
||||||
return ieee802_1d_to_ac[tid & 7];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When multiple aggregation sessions on multiple stations
|
* When multiple aggregation sessions on multiple stations
|
||||||
* are being created/destroyed simultaneously, we need to
|
* are being created/destroyed simultaneously, we need to
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "cfg.h"
|
#include "cfg.h"
|
||||||
#include "rate.h"
|
#include "rate.h"
|
||||||
#include "mesh.h"
|
#include "mesh.h"
|
||||||
|
#include "wme.h"
|
||||||
|
|
||||||
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
|
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
|
||||||
const char *name,
|
const char *name,
|
||||||
|
@ -190,7 +191,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
|
||||||
* receive the key. When wpa_supplicant has roamed
|
* receive the key. When wpa_supplicant has roamed
|
||||||
* using FT, it attempts to set the key before
|
* using FT, it attempts to set the key before
|
||||||
* association has completed, this rejects that attempt
|
* association has completed, this rejects that attempt
|
||||||
* so it will set the key again after assocation.
|
* so it will set the key again after association.
|
||||||
*
|
*
|
||||||
* TODO: accept the key if we have a station entry and
|
* TODO: accept the key if we have a station entry and
|
||||||
* add it to the device after the station.
|
* add it to the device after the station.
|
||||||
|
@ -229,6 +230,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
|
||||||
case NUM_NL80211_IFTYPES:
|
case NUM_NL80211_IFTYPES:
|
||||||
case NL80211_IFTYPE_P2P_CLIENT:
|
case NL80211_IFTYPE_P2P_CLIENT:
|
||||||
case NL80211_IFTYPE_P2P_GO:
|
case NL80211_IFTYPE_P2P_GO:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
/* shouldn't happen */
|
/* shouldn't happen */
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
break;
|
break;
|
||||||
|
@ -1225,14 +1227,14 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
|
static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
|
||||||
const u8 *mac)
|
struct station_del_parameters *params)
|
||||||
{
|
{
|
||||||
struct ieee80211_sub_if_data *sdata;
|
struct ieee80211_sub_if_data *sdata;
|
||||||
|
|
||||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
|
|
||||||
if (mac)
|
if (params->mac)
|
||||||
return sta_info_destroy_addr_bss(sdata, mac);
|
return sta_info_destroy_addr_bss(sdata, params->mac);
|
||||||
|
|
||||||
sta_info_flush(sdata);
|
sta_info_flush(sdata);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1516,6 +1518,57 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mpp_set_pinfo(struct mesh_path *mpath, u8 *mpp,
|
||||||
|
struct mpath_info *pinfo)
|
||||||
|
{
|
||||||
|
memset(pinfo, 0, sizeof(*pinfo));
|
||||||
|
memcpy(mpp, mpath->mpp, ETH_ALEN);
|
||||||
|
|
||||||
|
pinfo->generation = mpp_paths_generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
u8 *dst, u8 *mpp, struct mpath_info *pinfo)
|
||||||
|
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata;
|
||||||
|
struct mesh_path *mpath;
|
||||||
|
|
||||||
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
mpath = mpp_path_lookup(sdata, dst);
|
||||||
|
if (!mpath) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
memcpy(dst, mpath->dst, ETH_ALEN);
|
||||||
|
mpp_set_pinfo(mpath, mpp, pinfo);
|
||||||
|
rcu_read_unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ieee80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
int idx, u8 *dst, u8 *mpp,
|
||||||
|
struct mpath_info *pinfo)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata;
|
||||||
|
struct mesh_path *mpath;
|
||||||
|
|
||||||
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
mpath = mpp_path_lookup_by_idx(sdata, idx);
|
||||||
|
if (!mpath) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
memcpy(dst, mpath->dst, ETH_ALEN);
|
||||||
|
mpp_set_pinfo(mpath, mpp, pinfo);
|
||||||
|
rcu_read_unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int ieee80211_get_mesh_config(struct wiphy *wiphy,
|
static int ieee80211_get_mesh_config(struct wiphy *wiphy,
|
||||||
struct net_device *dev,
|
struct net_device *dev,
|
||||||
struct mesh_config *conf)
|
struct mesh_config *conf)
|
||||||
|
@ -1966,6 +2019,17 @@ static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
|
||||||
return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
|
return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ieee80211_join_ocb(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
struct ocb_setup *setup)
|
||||||
|
{
|
||||||
|
return ieee80211_ocb_join(IEEE80211_DEV_TO_SUB_IF(dev), setup);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ieee80211_leave_ocb(struct wiphy *wiphy, struct net_device *dev)
|
||||||
|
{
|
||||||
|
return ieee80211_ocb_leave(IEEE80211_DEV_TO_SUB_IF(dev));
|
||||||
|
}
|
||||||
|
|
||||||
static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
|
static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
|
||||||
int rate[IEEE80211_NUM_BANDS])
|
int rate[IEEE80211_NUM_BANDS])
|
||||||
{
|
{
|
||||||
|
@ -2081,6 +2145,9 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy,
|
||||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||||
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
||||||
|
|
||||||
|
if (local->ops->get_txpower)
|
||||||
|
return drv_get_txpower(local, sdata, dbm);
|
||||||
|
|
||||||
if (!local->use_chanctx)
|
if (!local->use_chanctx)
|
||||||
*dbm = local->hw.conf.power_level;
|
*dbm = local->hw.conf.power_level;
|
||||||
else
|
else
|
||||||
|
@ -2850,11 +2917,7 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
|
||||||
if (sdata->reserved_ready)
|
if (sdata->reserved_ready)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err = ieee80211_vif_use_reserved_context(sdata);
|
return ieee80211_vif_use_reserved_context(sdata);
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
|
if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
|
||||||
|
@ -2868,7 +2931,6 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
ieee80211_bss_info_change_notify(sdata, changed);
|
ieee80211_bss_info_change_notify(sdata, changed);
|
||||||
cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
|
|
||||||
|
|
||||||
if (sdata->csa_block_tx) {
|
if (sdata->csa_block_tx) {
|
||||||
ieee80211_wake_vif_queues(local, sdata,
|
ieee80211_wake_vif_queues(local, sdata,
|
||||||
|
@ -2876,6 +2938,12 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
|
||||||
sdata->csa_block_tx = false;
|
sdata->csa_block_tx = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = drv_post_channel_switch(sdata);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3053,9 +3121,11 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||||
{
|
{
|
||||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
struct ieee80211_local *local = sdata->local;
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_channel_switch ch_switch;
|
||||||
struct ieee80211_chanctx_conf *conf;
|
struct ieee80211_chanctx_conf *conf;
|
||||||
struct ieee80211_chanctx *chanctx;
|
struct ieee80211_chanctx *chanctx;
|
||||||
int err, changed = 0;
|
u32 changed = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
sdata_assert_lock(sdata);
|
sdata_assert_lock(sdata);
|
||||||
lockdep_assert_held(&local->mtx);
|
lockdep_assert_held(&local->mtx);
|
||||||
|
@ -3088,6 +3158,10 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = drv_pre_channel_switch(sdata, &ch_switch);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
err = ieee80211_vif_reserve_chanctx(sdata, ¶ms->chandef,
|
err = ieee80211_vif_reserve_chanctx(sdata, ¶ms->chandef,
|
||||||
chanctx->mode,
|
chanctx->mode,
|
||||||
params->radar_required);
|
params->radar_required);
|
||||||
|
@ -3101,6 +3175,12 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ch_switch.timestamp = 0;
|
||||||
|
ch_switch.device_timestamp = 0;
|
||||||
|
ch_switch.block_tx = params->block_tx;
|
||||||
|
ch_switch.chandef = params->chandef;
|
||||||
|
ch_switch.count = params->count;
|
||||||
|
|
||||||
err = ieee80211_set_csa_beacon(sdata, params, &changed);
|
err = ieee80211_set_csa_beacon(sdata, params, &changed);
|
||||||
if (err) {
|
if (err) {
|
||||||
ieee80211_vif_unreserve_chanctx(sdata);
|
ieee80211_vif_unreserve_chanctx(sdata);
|
||||||
|
@ -3458,7 +3538,7 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||||
if (chanctx_conf) {
|
if (chanctx_conf) {
|
||||||
*chandef = chanctx_conf->def;
|
*chandef = sdata->vif.bss_conf.chandef;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else if (local->open_count > 0 &&
|
} else if (local->open_count > 0 &&
|
||||||
local->open_count == local->monitors &&
|
local->open_count == local->monitors &&
|
||||||
|
@ -3521,6 +3601,76 @@ static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ieee80211_add_tx_ts(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
u8 tsid, const u8 *peer, u8 up,
|
||||||
|
u16 admitted_time)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||||
|
int ac = ieee802_1d_to_ac[up];
|
||||||
|
|
||||||
|
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (!(sdata->wmm_acm & BIT(up)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (ifmgd->tx_tspec[ac].admitted_time)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
if (admitted_time) {
|
||||||
|
ifmgd->tx_tspec[ac].admitted_time = 32 * admitted_time;
|
||||||
|
ifmgd->tx_tspec[ac].tsid = tsid;
|
||||||
|
ifmgd->tx_tspec[ac].up = up;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ieee80211_del_tx_ts(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
u8 tsid, const u8 *peer)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||||
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||||
|
int ac;
|
||||||
|
|
||||||
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||||
|
struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
|
||||||
|
|
||||||
|
/* skip unused entries */
|
||||||
|
if (!tx_tspec->admitted_time)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (tx_tspec->tsid != tsid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* due to this new packets will be reassigned to non-ACM ACs */
|
||||||
|
tx_tspec->up = -1;
|
||||||
|
|
||||||
|
/* Make sure that all packets have been sent to avoid to
|
||||||
|
* restore the QoS params on packets that are still on the
|
||||||
|
* queues.
|
||||||
|
*/
|
||||||
|
synchronize_net();
|
||||||
|
ieee80211_flush_queues(local, sdata);
|
||||||
|
|
||||||
|
/* restore the normal QoS parameters
|
||||||
|
* (unconditionally to avoid races)
|
||||||
|
*/
|
||||||
|
tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
|
||||||
|
tx_tspec->downgraded = false;
|
||||||
|
ieee80211_sta_handle_tspec_ac_params(sdata);
|
||||||
|
|
||||||
|
/* finally clear all the data */
|
||||||
|
memset(tx_tspec, 0, sizeof(*tx_tspec));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
const struct cfg80211_ops mac80211_config_ops = {
|
const struct cfg80211_ops mac80211_config_ops = {
|
||||||
.add_virtual_intf = ieee80211_add_iface,
|
.add_virtual_intf = ieee80211_add_iface,
|
||||||
.del_virtual_intf = ieee80211_del_iface,
|
.del_virtual_intf = ieee80211_del_iface,
|
||||||
|
@ -3547,11 +3697,15 @@ const struct cfg80211_ops mac80211_config_ops = {
|
||||||
.change_mpath = ieee80211_change_mpath,
|
.change_mpath = ieee80211_change_mpath,
|
||||||
.get_mpath = ieee80211_get_mpath,
|
.get_mpath = ieee80211_get_mpath,
|
||||||
.dump_mpath = ieee80211_dump_mpath,
|
.dump_mpath = ieee80211_dump_mpath,
|
||||||
|
.get_mpp = ieee80211_get_mpp,
|
||||||
|
.dump_mpp = ieee80211_dump_mpp,
|
||||||
.update_mesh_config = ieee80211_update_mesh_config,
|
.update_mesh_config = ieee80211_update_mesh_config,
|
||||||
.get_mesh_config = ieee80211_get_mesh_config,
|
.get_mesh_config = ieee80211_get_mesh_config,
|
||||||
.join_mesh = ieee80211_join_mesh,
|
.join_mesh = ieee80211_join_mesh,
|
||||||
.leave_mesh = ieee80211_leave_mesh,
|
.leave_mesh = ieee80211_leave_mesh,
|
||||||
#endif
|
#endif
|
||||||
|
.join_ocb = ieee80211_join_ocb,
|
||||||
|
.leave_ocb = ieee80211_leave_ocb,
|
||||||
.change_bss = ieee80211_change_bss,
|
.change_bss = ieee80211_change_bss,
|
||||||
.set_txq_params = ieee80211_set_txq_params,
|
.set_txq_params = ieee80211_set_txq_params,
|
||||||
.set_monitor_channel = ieee80211_set_monitor_channel,
|
.set_monitor_channel = ieee80211_set_monitor_channel,
|
||||||
|
@ -3597,4 +3751,6 @@ const struct cfg80211_ops mac80211_config_ops = {
|
||||||
.channel_switch = ieee80211_channel_switch,
|
.channel_switch = ieee80211_channel_switch,
|
||||||
.set_qos_map = ieee80211_set_qos_map,
|
.set_qos_map = ieee80211_set_qos_map,
|
||||||
.set_ap_chanwidth = ieee80211_set_ap_chanwidth,
|
.set_ap_chanwidth = ieee80211_set_ap_chanwidth,
|
||||||
|
.add_tx_ts = ieee80211_add_tx_ts,
|
||||||
|
.del_tx_ts = ieee80211_del_tx_ts,
|
||||||
};
|
};
|
||||||
|
|
|
@ -270,6 +270,7 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
case NL80211_IFTYPE_WDS:
|
case NL80211_IFTYPE_WDS:
|
||||||
case NL80211_IFTYPE_MESH_POINT:
|
case NL80211_IFTYPE_MESH_POINT:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
width = vif->bss_conf.chandef.width;
|
width = vif->bss_conf.chandef.width;
|
||||||
break;
|
break;
|
||||||
case NL80211_IFTYPE_UNSPECIFIED:
|
case NL80211_IFTYPE_UNSPECIFIED:
|
||||||
|
@ -674,6 +675,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
case NL80211_IFTYPE_WDS:
|
case NL80211_IFTYPE_WDS:
|
||||||
case NL80211_IFTYPE_MESH_POINT:
|
case NL80211_IFTYPE_MESH_POINT:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
|
@ -909,6 +911,7 @@ ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
case NL80211_IFTYPE_AP:
|
case NL80211_IFTYPE_AP:
|
||||||
case NL80211_IFTYPE_MESH_POINT:
|
case NL80211_IFTYPE_MESH_POINT:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
ieee80211_queue_work(&sdata->local->hw,
|
ieee80211_queue_work(&sdata->local->hw,
|
||||||
&sdata->csa_finalize_work);
|
&sdata->csa_finalize_work);
|
||||||
break;
|
break;
|
||||||
|
@ -1634,7 +1637,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case IEEE80211_CHANCTX_WILL_BE_REPLACED:
|
case IEEE80211_CHANCTX_WILL_BE_REPLACED:
|
||||||
/* TODO: Perhaps the bandwith change could be treated as a
|
/* TODO: Perhaps the bandwidth change could be treated as a
|
||||||
* reservation itself? */
|
* reservation itself? */
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
#define __MAC80211_DEBUG_H
|
#define __MAC80211_DEBUG_H
|
||||||
#include <net/cfg80211.h>
|
#include <net/cfg80211.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_MAC80211_OCB_DEBUG
|
||||||
|
#define MAC80211_OCB_DEBUG 1
|
||||||
|
#else
|
||||||
|
#define MAC80211_OCB_DEBUG 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_MAC80211_IBSS_DEBUG
|
#ifdef CONFIG_MAC80211_IBSS_DEBUG
|
||||||
#define MAC80211_IBSS_DEBUG 1
|
#define MAC80211_IBSS_DEBUG 1
|
||||||
#else
|
#else
|
||||||
|
@ -131,6 +137,10 @@ do { \
|
||||||
_sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \
|
_sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \
|
||||||
sdata, fmt, ##__VA_ARGS__)
|
sdata, fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define ocb_dbg(sdata, fmt, ...) \
|
||||||
|
_sdata_dbg(MAC80211_OCB_DEBUG, \
|
||||||
|
sdata, fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
#define ibss_dbg(sdata, fmt, ...) \
|
#define ibss_dbg(sdata, fmt, ...) \
|
||||||
_sdata_dbg(MAC80211_IBSS_DEBUG, \
|
_sdata_dbg(MAC80211_IBSS_DEBUG, \
|
||||||
sdata, fmt, ##__VA_ARGS__)
|
sdata, fmt, ##__VA_ARGS__)
|
||||||
|
|
|
@ -300,10 +300,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
|
||||||
|
|
||||||
lockdep_assert_held(&sdata->local->key_mtx);
|
lockdep_assert_held(&sdata->local->key_mtx);
|
||||||
|
|
||||||
if (sdata->debugfs.default_unicast_key) {
|
debugfs_remove(sdata->debugfs.default_unicast_key);
|
||||||
debugfs_remove(sdata->debugfs.default_unicast_key);
|
sdata->debugfs.default_unicast_key = NULL;
|
||||||
sdata->debugfs.default_unicast_key = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sdata->default_unicast_key) {
|
if (sdata->default_unicast_key) {
|
||||||
key = key_mtx_dereference(sdata->local,
|
key = key_mtx_dereference(sdata->local,
|
||||||
|
@ -314,10 +312,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
|
||||||
sdata->vif.debugfs_dir, buf);
|
sdata->vif.debugfs_dir, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sdata->debugfs.default_multicast_key) {
|
debugfs_remove(sdata->debugfs.default_multicast_key);
|
||||||
debugfs_remove(sdata->debugfs.default_multicast_key);
|
sdata->debugfs.default_multicast_key = NULL;
|
||||||
sdata->debugfs.default_multicast_key = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sdata->default_multicast_key) {
|
if (sdata->default_multicast_key) {
|
||||||
key = key_mtx_dereference(sdata->local,
|
key = key_mtx_dereference(sdata->local,
|
||||||
|
|
|
@ -214,7 +214,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
|
||||||
BSS_CHANGED_BEACON_ENABLED) &&
|
BSS_CHANGED_BEACON_ENABLED) &&
|
||||||
sdata->vif.type != NL80211_IFTYPE_AP &&
|
sdata->vif.type != NL80211_IFTYPE_AP &&
|
||||||
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
|
sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
|
||||||
|
sdata->vif.type != NL80211_IFTYPE_OCB))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
|
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
|
||||||
|
@ -631,6 +632,12 @@ static inline int drv_conf_tx(struct ieee80211_local *local,
|
||||||
if (!check_sdata_in_driver(sdata))
|
if (!check_sdata_in_driver(sdata))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
|
if (WARN_ONCE(params->cw_min == 0 ||
|
||||||
|
params->cw_min > params->cw_max,
|
||||||
|
"%s: invalid CW_min/CW_max: %d/%d\n",
|
||||||
|
sdata->name, params->cw_min, params->cw_max))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
trace_drv_conf_tx(local, sdata, ac, params);
|
trace_drv_conf_tx(local, sdata, ac, params);
|
||||||
if (local->ops->conf_tx)
|
if (local->ops->conf_tx)
|
||||||
ret = local->ops->conf_tx(&local->hw, &sdata->vif,
|
ret = local->ops->conf_tx(&local->hw, &sdata->vif,
|
||||||
|
@ -764,12 +771,13 @@ static inline void drv_flush(struct ieee80211_local *local,
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void drv_channel_switch(struct ieee80211_local *local,
|
static inline void drv_channel_switch(struct ieee80211_local *local,
|
||||||
struct ieee80211_channel_switch *ch_switch)
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee80211_channel_switch *ch_switch)
|
||||||
{
|
{
|
||||||
might_sleep();
|
might_sleep();
|
||||||
|
|
||||||
trace_drv_channel_switch(local, ch_switch);
|
trace_drv_channel_switch(local, sdata, ch_switch);
|
||||||
local->ops->channel_switch(&local->hw, ch_switch);
|
local->ops->channel_switch(&local->hw, &sdata->vif, ch_switch);
|
||||||
trace_drv_return_void(local);
|
trace_drv_return_void(local);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1144,13 +1152,15 @@ static inline void drv_stop_ap(struct ieee80211_local *local,
|
||||||
trace_drv_return_void(local);
|
trace_drv_return_void(local);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void drv_restart_complete(struct ieee80211_local *local)
|
static inline void
|
||||||
|
drv_reconfig_complete(struct ieee80211_local *local,
|
||||||
|
enum ieee80211_reconfig_type reconfig_type)
|
||||||
{
|
{
|
||||||
might_sleep();
|
might_sleep();
|
||||||
|
|
||||||
trace_drv_restart_complete(local);
|
trace_drv_reconfig_complete(local, reconfig_type);
|
||||||
if (local->ops->restart_complete)
|
if (local->ops->reconfig_complete)
|
||||||
local->ops->restart_complete(&local->hw);
|
local->ops->reconfig_complete(&local->hw, reconfig_type);
|
||||||
trace_drv_return_void(local);
|
trace_drv_return_void(local);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1196,6 +1206,40 @@ drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
drv_pre_channel_switch(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee80211_channel_switch *ch_switch)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!check_sdata_in_driver(sdata))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
trace_drv_pre_channel_switch(local, sdata, ch_switch);
|
||||||
|
if (local->ops->pre_channel_switch)
|
||||||
|
ret = local->ops->pre_channel_switch(&local->hw, &sdata->vif,
|
||||||
|
ch_switch);
|
||||||
|
trace_drv_return_int(local, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
drv_post_channel_switch(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!check_sdata_in_driver(sdata))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
trace_drv_post_channel_switch(local, sdata);
|
||||||
|
if (local->ops->post_channel_switch)
|
||||||
|
ret = local->ops->post_channel_switch(&local->hw, &sdata->vif);
|
||||||
|
trace_drv_return_int(local, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int drv_join_ibss(struct ieee80211_local *local,
|
static inline int drv_join_ibss(struct ieee80211_local *local,
|
||||||
struct ieee80211_sub_if_data *sdata)
|
struct ieee80211_sub_if_data *sdata)
|
||||||
{
|
{
|
||||||
|
@ -1238,4 +1282,18 @@ static inline u32 drv_get_expected_throughput(struct ieee80211_local *local,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int drv_get_txpower(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_sub_if_data *sdata, int *dbm)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!local->ops->get_txpower)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
ret = local->ops->get_txpower(&local->hw, &sdata->vif, dbm);
|
||||||
|
trace_drv_get_txpower(local, sdata, *dbm, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __MAC80211_DRIVER_OPS */
|
#endif /* __MAC80211_DRIVER_OPS */
|
||||||
|
|
|
@ -131,7 +131,7 @@ enum ieee80211_bss_corrupt_data_flags {
|
||||||
*
|
*
|
||||||
* These are bss flags that are attached to a bss in the
|
* These are bss flags that are attached to a bss in the
|
||||||
* @valid_data field of &struct ieee80211_bss. They show which parts
|
* @valid_data field of &struct ieee80211_bss. They show which parts
|
||||||
* of the data structure were recieved as a result of an un-corrupted
|
* of the data structure were received as a result of an un-corrupted
|
||||||
* beacon/probe response.
|
* beacon/probe response.
|
||||||
*/
|
*/
|
||||||
enum ieee80211_bss_valid_data_flags {
|
enum ieee80211_bss_valid_data_flags {
|
||||||
|
@ -399,6 +399,24 @@ struct ieee80211_mgd_assoc_data {
|
||||||
u8 ie[];
|
u8 ie[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ieee80211_sta_tx_tspec {
|
||||||
|
/* timestamp of the first packet in the time slice */
|
||||||
|
unsigned long time_slice_start;
|
||||||
|
|
||||||
|
u32 admitted_time; /* in usecs, unlike over the air */
|
||||||
|
u8 tsid;
|
||||||
|
s8 up; /* signed to be able to invalidate with -1 during teardown */
|
||||||
|
|
||||||
|
/* consumed TX time in microseconds in the time slice */
|
||||||
|
u32 consumed_tx_time;
|
||||||
|
enum {
|
||||||
|
TX_TSPEC_ACTION_NONE = 0,
|
||||||
|
TX_TSPEC_ACTION_DOWNGRADE,
|
||||||
|
TX_TSPEC_ACTION_STOP_DOWNGRADE,
|
||||||
|
} action;
|
||||||
|
bool downgraded;
|
||||||
|
};
|
||||||
|
|
||||||
struct ieee80211_if_managed {
|
struct ieee80211_if_managed {
|
||||||
struct timer_list timer;
|
struct timer_list timer;
|
||||||
struct timer_list conn_mon_timer;
|
struct timer_list conn_mon_timer;
|
||||||
|
@ -434,6 +452,8 @@ struct ieee80211_if_managed {
|
||||||
|
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
|
||||||
|
bool csa_waiting_bcn;
|
||||||
|
|
||||||
bool beacon_crc_valid;
|
bool beacon_crc_valid;
|
||||||
u32 beacon_crc;
|
u32 beacon_crc;
|
||||||
|
|
||||||
|
@ -507,6 +527,16 @@ struct ieee80211_if_managed {
|
||||||
|
|
||||||
u8 tdls_peer[ETH_ALEN] __aligned(2);
|
u8 tdls_peer[ETH_ALEN] __aligned(2);
|
||||||
struct delayed_work tdls_peer_del_work;
|
struct delayed_work tdls_peer_del_work;
|
||||||
|
|
||||||
|
/* WMM-AC TSPEC support */
|
||||||
|
struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS];
|
||||||
|
/* Use a separate work struct so that we can do something here
|
||||||
|
* while the sdata->work is flushing the queues, for example.
|
||||||
|
* otherwise, in scenarios where we hardly get any traffic out
|
||||||
|
* on the BE queue, but there's a lot of VO traffic, we might
|
||||||
|
* get stuck in a downgraded situation and flush takes forever.
|
||||||
|
*/
|
||||||
|
struct delayed_work tx_tspec_wk;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ieee80211_if_ibss {
|
struct ieee80211_if_ibss {
|
||||||
|
@ -546,6 +576,25 @@ struct ieee80211_if_ibss {
|
||||||
} state;
|
} state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ieee80211_if_ocb - OCB mode state
|
||||||
|
*
|
||||||
|
* @housekeeping_timer: timer for periodic invocation of a housekeeping task
|
||||||
|
* @wrkq_flags: OCB deferred task action
|
||||||
|
* @incomplete_lock: delayed STA insertion lock
|
||||||
|
* @incomplete_stations: list of STAs waiting for delayed insertion
|
||||||
|
* @joined: indication if the interface is connected to an OCB network
|
||||||
|
*/
|
||||||
|
struct ieee80211_if_ocb {
|
||||||
|
struct timer_list housekeeping_timer;
|
||||||
|
unsigned long wrkq_flags;
|
||||||
|
|
||||||
|
spinlock_t incomplete_lock;
|
||||||
|
struct list_head incomplete_stations;
|
||||||
|
|
||||||
|
bool joined;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface
|
* struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface
|
||||||
*
|
*
|
||||||
|
@ -839,6 +888,7 @@ struct ieee80211_sub_if_data {
|
||||||
struct ieee80211_if_managed mgd;
|
struct ieee80211_if_managed mgd;
|
||||||
struct ieee80211_if_ibss ibss;
|
struct ieee80211_if_ibss ibss;
|
||||||
struct ieee80211_if_mesh mesh;
|
struct ieee80211_if_mesh mesh;
|
||||||
|
struct ieee80211_if_ocb ocb;
|
||||||
u32 mntr_flags;
|
u32 mntr_flags;
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
|
@ -1307,6 +1357,9 @@ struct ieee80211_local {
|
||||||
/* virtual monitor interface */
|
/* virtual monitor interface */
|
||||||
struct ieee80211_sub_if_data __rcu *monitor_sdata;
|
struct ieee80211_sub_if_data __rcu *monitor_sdata;
|
||||||
struct cfg80211_chan_def monitor_chandef;
|
struct cfg80211_chan_def monitor_chandef;
|
||||||
|
|
||||||
|
/* extended capabilities provided by mac80211 */
|
||||||
|
u8 ext_capa[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct ieee80211_sub_if_data *
|
static inline struct ieee80211_sub_if_data *
|
||||||
|
@ -1454,6 +1507,7 @@ void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
|
||||||
__le16 fc, bool acked);
|
__le16 fc, bool acked);
|
||||||
void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata);
|
void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata);
|
||||||
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
|
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
|
||||||
|
void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata);
|
||||||
|
|
||||||
/* IBSS code */
|
/* IBSS code */
|
||||||
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
|
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
|
||||||
|
@ -1471,6 +1525,15 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||||
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
|
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
|
||||||
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
|
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
|
||||||
|
|
||||||
|
/* OCB code */
|
||||||
|
void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata);
|
||||||
|
void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
|
||||||
|
const u8 *bssid, const u8 *addr, u32 supp_rates);
|
||||||
|
void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata);
|
||||||
|
int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ocb_setup *setup);
|
||||||
|
int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata);
|
||||||
|
|
||||||
/* mesh code */
|
/* mesh code */
|
||||||
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
|
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
|
||||||
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||||
|
@ -1758,6 +1821,13 @@ static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern const int ieee802_1d_to_ac[8];
|
||||||
|
|
||||||
|
static inline int ieee80211_ac_from_tid(int tid)
|
||||||
|
{
|
||||||
|
return ieee802_1d_to_ac[tid & 7];
|
||||||
|
}
|
||||||
|
|
||||||
void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
|
void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
|
||||||
void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
|
void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
|
||||||
void ieee80211_dynamic_ps_timer(unsigned long data);
|
void ieee80211_dynamic_ps_timer(unsigned long data);
|
||||||
|
@ -1767,7 +1837,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
|
||||||
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
|
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_hdr *hdr);
|
struct ieee80211_hdr *hdr);
|
||||||
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_hdr *hdr, bool ack);
|
struct ieee80211_hdr *hdr, bool ack, u16 tx_time);
|
||||||
|
|
||||||
void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
|
void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
|
||||||
unsigned long queues,
|
unsigned long queues,
|
||||||
|
@ -1833,8 +1903,10 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
|
||||||
void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
|
void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
|
||||||
void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);
|
void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);
|
||||||
|
|
||||||
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
|
size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
|
||||||
const u8 *ids, int n_ids, size_t offset);
|
const u8 *ids, int n_ids,
|
||||||
|
const u8 *after_ric, int n_after_ric,
|
||||||
|
size_t offset);
|
||||||
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
|
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
|
||||||
u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
|
u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
|
||||||
u16 cap);
|
u16 cap);
|
||||||
|
|
|
@ -258,6 +258,15 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
|
||||||
/* we hold the RTNL here so can safely walk the list */
|
/* we hold the RTNL here so can safely walk the list */
|
||||||
list_for_each_entry(nsdata, &local->interfaces, list) {
|
list_for_each_entry(nsdata, &local->interfaces, list) {
|
||||||
if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
|
if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
|
||||||
|
/*
|
||||||
|
* Only OCB and monitor mode may coexist
|
||||||
|
*/
|
||||||
|
if ((sdata->vif.type == NL80211_IFTYPE_OCB &&
|
||||||
|
nsdata->vif.type != NL80211_IFTYPE_MONITOR) ||
|
||||||
|
(sdata->vif.type != NL80211_IFTYPE_MONITOR &&
|
||||||
|
nsdata->vif.type == NL80211_IFTYPE_OCB))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allow only a single IBSS interface to be up at any
|
* Allow only a single IBSS interface to be up at any
|
||||||
* time. This is restricted because beacon distribution
|
* time. This is restricted because beacon distribution
|
||||||
|
@ -521,6 +530,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
|
||||||
case NL80211_IFTYPE_MONITOR:
|
case NL80211_IFTYPE_MONITOR:
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
case NL80211_IFTYPE_P2P_DEVICE:
|
case NL80211_IFTYPE_P2P_DEVICE:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
/* no special treatment */
|
/* no special treatment */
|
||||||
break;
|
break;
|
||||||
case NL80211_IFTYPE_UNSPECIFIED:
|
case NL80211_IFTYPE_UNSPECIFIED:
|
||||||
|
@ -631,6 +641,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
case NL80211_IFTYPE_AP:
|
case NL80211_IFTYPE_AP:
|
||||||
case NL80211_IFTYPE_MESH_POINT:
|
case NL80211_IFTYPE_MESH_POINT:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
netif_carrier_off(dev);
|
netif_carrier_off(dev);
|
||||||
break;
|
break;
|
||||||
case NL80211_IFTYPE_WDS:
|
case NL80211_IFTYPE_WDS:
|
||||||
|
@ -842,6 +853,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||||||
sdata_lock(sdata);
|
sdata_lock(sdata);
|
||||||
mutex_lock(&local->mtx);
|
mutex_lock(&local->mtx);
|
||||||
sdata->vif.csa_active = false;
|
sdata->vif.csa_active = false;
|
||||||
|
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||||
|
sdata->u.mgd.csa_waiting_bcn = false;
|
||||||
if (sdata->csa_block_tx) {
|
if (sdata->csa_block_tx) {
|
||||||
ieee80211_wake_vif_queues(local, sdata,
|
ieee80211_wake_vif_queues(local, sdata,
|
||||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||||
|
@ -1279,6 +1292,9 @@ static void ieee80211_iface_work(struct work_struct *work)
|
||||||
break;
|
break;
|
||||||
ieee80211_mesh_work(sdata);
|
ieee80211_mesh_work(sdata);
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
ieee80211_ocb_work(sdata);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1298,6 +1314,9 @@ static void ieee80211_recalc_smps_work(struct work_struct *work)
|
||||||
static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
|
static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
|
||||||
enum nl80211_iftype type)
|
enum nl80211_iftype type)
|
||||||
{
|
{
|
||||||
|
static const u8 bssid_wildcard[ETH_ALEN] = {0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff};
|
||||||
|
|
||||||
/* clear type-dependent union */
|
/* clear type-dependent union */
|
||||||
memset(&sdata->u, 0, sizeof(sdata->u));
|
memset(&sdata->u, 0, sizeof(sdata->u));
|
||||||
|
|
||||||
|
@ -1349,6 +1368,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
|
||||||
sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
|
sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
|
||||||
ieee80211_sta_setup_sdata(sdata);
|
ieee80211_sta_setup_sdata(sdata);
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
sdata->vif.bss_conf.bssid = bssid_wildcard;
|
||||||
|
ieee80211_ocb_setup_sdata(sdata);
|
||||||
|
break;
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
|
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
|
||||||
ieee80211_ibss_setup_sdata(sdata);
|
ieee80211_ibss_setup_sdata(sdata);
|
||||||
|
@ -1396,6 +1419,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
|
||||||
case NL80211_IFTYPE_AP:
|
case NL80211_IFTYPE_AP:
|
||||||
case NL80211_IFTYPE_STATION:
|
case NL80211_IFTYPE_STATION:
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
/*
|
/*
|
||||||
* Could maybe also all others here?
|
* Could maybe also all others here?
|
||||||
* Just not sure how that interacts
|
* Just not sure how that interacts
|
||||||
|
@ -1411,6 +1435,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
|
||||||
case NL80211_IFTYPE_AP:
|
case NL80211_IFTYPE_AP:
|
||||||
case NL80211_IFTYPE_STATION:
|
case NL80211_IFTYPE_STATION:
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
/*
|
/*
|
||||||
* Could probably support everything
|
* Could probably support everything
|
||||||
* but WDS here (WDS do_open can fail
|
* but WDS here (WDS do_open can fail
|
||||||
|
@ -1669,7 +1694,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
|
||||||
}
|
}
|
||||||
|
|
||||||
ieee80211_assign_perm_addr(local, ndev->perm_addr, type);
|
ieee80211_assign_perm_addr(local, ndev->perm_addr, type);
|
||||||
memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
|
if (params && is_valid_ether_addr(params->macaddr))
|
||||||
|
memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
|
||||||
|
else
|
||||||
|
memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
|
||||||
SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
|
SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
|
||||||
|
|
||||||
/* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */
|
/* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */
|
||||||
|
|
|
@ -94,8 +94,17 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
|
||||||
|
|
||||||
might_sleep();
|
might_sleep();
|
||||||
|
|
||||||
if (key->flags & KEY_FLAG_TAINTED)
|
if (key->flags & KEY_FLAG_TAINTED) {
|
||||||
|
/* If we get here, it's during resume and the key is
|
||||||
|
* tainted so shouldn't be used/programmed any more.
|
||||||
|
* However, its flags may still indicate that it was
|
||||||
|
* programmed into the device (since we're in resume)
|
||||||
|
* so clear that flag now to avoid trying to remove
|
||||||
|
* it again later.
|
||||||
|
*/
|
||||||
|
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (!key->local->ops->set_key)
|
if (!key->local->ops->set_key)
|
||||||
goto out_unsupported;
|
goto out_unsupported;
|
||||||
|
|
|
@ -478,13 +478,9 @@ static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u8 extended_capabilities[] = {
|
struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
||||||
0, 0, 0, 0, 0, 0, 0,
|
const struct ieee80211_ops *ops,
|
||||||
WLAN_EXT_CAPA8_OPMODE_NOTIF,
|
const char *requested_name)
|
||||||
};
|
|
||||||
|
|
||||||
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
|
||||||
const struct ieee80211_ops *ops)
|
|
||||||
{
|
{
|
||||||
struct ieee80211_local *local;
|
struct ieee80211_local *local;
|
||||||
int priv_size, i;
|
int priv_size, i;
|
||||||
|
@ -524,7 +520,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||||
*/
|
*/
|
||||||
priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
|
priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
|
||||||
|
|
||||||
wiphy = wiphy_new(&mac80211_config_ops, priv_size);
|
wiphy = wiphy_new_nm(&mac80211_config_ops, priv_size, requested_name);
|
||||||
|
|
||||||
if (!wiphy)
|
if (!wiphy)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -539,10 +535,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||||
WIPHY_FLAG_REPORTS_OBSS |
|
WIPHY_FLAG_REPORTS_OBSS |
|
||||||
WIPHY_FLAG_OFFCHAN_TX;
|
WIPHY_FLAG_OFFCHAN_TX;
|
||||||
|
|
||||||
wiphy->extended_capabilities = extended_capabilities;
|
|
||||||
wiphy->extended_capabilities_mask = extended_capabilities;
|
|
||||||
wiphy->extended_capabilities_len = ARRAY_SIZE(extended_capabilities);
|
|
||||||
|
|
||||||
if (ops->remain_on_channel)
|
if (ops->remain_on_channel)
|
||||||
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
|
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
|
||||||
|
|
||||||
|
@ -550,6 +542,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||||
NL80211_FEATURE_SAE |
|
NL80211_FEATURE_SAE |
|
||||||
NL80211_FEATURE_HT_IBSS |
|
NL80211_FEATURE_HT_IBSS |
|
||||||
NL80211_FEATURE_VIF_TXPOWER |
|
NL80211_FEATURE_VIF_TXPOWER |
|
||||||
|
NL80211_FEATURE_MAC_ON_CREATE |
|
||||||
NL80211_FEATURE_USERSPACE_MPM;
|
NL80211_FEATURE_USERSPACE_MPM;
|
||||||
|
|
||||||
if (!ops->hw_scan)
|
if (!ops->hw_scan)
|
||||||
|
@ -591,6 +584,13 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||||
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
|
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
|
||||||
wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask;
|
wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask;
|
||||||
|
|
||||||
|
local->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF;
|
||||||
|
|
||||||
|
wiphy->extended_capabilities = local->ext_capa;
|
||||||
|
wiphy->extended_capabilities_mask = local->ext_capa;
|
||||||
|
wiphy->extended_capabilities_len =
|
||||||
|
ARRAY_SIZE(local->ext_capa);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&local->interfaces);
|
INIT_LIST_HEAD(&local->interfaces);
|
||||||
|
|
||||||
__hw_addr_init(&local->mc_list);
|
__hw_addr_init(&local->mc_list);
|
||||||
|
@ -651,7 +651,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||||
|
|
||||||
return &local->hw;
|
return &local->hw;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ieee80211_alloc_hw);
|
EXPORT_SYMBOL(ieee80211_alloc_hw_nm);
|
||||||
|
|
||||||
static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
|
static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
|
||||||
{
|
{
|
||||||
|
@ -787,13 +787,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||||
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
|
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* DFS currently not supported with channel context drivers */
|
/* DFS is not supported with multi-channel combinations yet */
|
||||||
for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
|
for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
|
||||||
const struct ieee80211_iface_combination *comb;
|
const struct ieee80211_iface_combination *comb;
|
||||||
|
|
||||||
comb = &local->hw.wiphy->iface_combinations[i];
|
comb = &local->hw.wiphy->iface_combinations[i];
|
||||||
|
|
||||||
if (comb->radar_detect_widths)
|
if (comb->radar_detect_widths &&
|
||||||
|
comb->num_different_channels > 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -958,6 +959,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||||
if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)
|
if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)
|
||||||
local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
|
local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
|
||||||
|
|
||||||
|
/* mac80211 supports eCSA, if the driver supports STA CSA at all */
|
||||||
|
if (local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)
|
||||||
|
local->ext_capa[0] |= WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
|
||||||
|
|
||||||
local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
|
local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
|
||||||
|
|
||||||
result = wiphy_register(local->hw.wiphy);
|
result = wiphy_register(local->hw.wiphy);
|
||||||
|
@ -1019,7 +1024,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add one default STA interface if supported */
|
/* add one default STA interface if supported */
|
||||||
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
|
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
|
||||||
|
!(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) {
|
||||||
result = ieee80211_if_add(local, "wlan%d", NULL,
|
result = ieee80211_if_add(local, "wlan%d", NULL,
|
||||||
NL80211_IFTYPE_STATION, NULL);
|
NL80211_IFTYPE_STATION, NULL);
|
||||||
if (result)
|
if (result)
|
||||||
|
|
|
@ -270,6 +270,8 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
|
||||||
const u8 *dst, const u8 *mpp);
|
const u8 *dst, const u8 *mpp);
|
||||||
struct mesh_path *
|
struct mesh_path *
|
||||||
mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx);
|
mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx);
|
||||||
|
struct mesh_path *
|
||||||
|
mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx);
|
||||||
void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);
|
void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);
|
||||||
void mesh_path_expire(struct ieee80211_sub_if_data *sdata);
|
void mesh_path_expire(struct ieee80211_sub_if_data *sdata);
|
||||||
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
|
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
|
||||||
|
@ -317,6 +319,7 @@ void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
|
||||||
|
|
||||||
bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
|
bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
|
||||||
extern int mesh_paths_generation;
|
extern int mesh_paths_generation;
|
||||||
|
extern int mpp_paths_generation;
|
||||||
|
|
||||||
#ifdef CONFIG_MAC80211_MESH
|
#ifdef CONFIG_MAC80211_MESH
|
||||||
static inline
|
static inline
|
||||||
|
|
|
@ -44,6 +44,7 @@ static struct mesh_table __rcu *mesh_paths;
|
||||||
static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */
|
static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */
|
||||||
|
|
||||||
int mesh_paths_generation;
|
int mesh_paths_generation;
|
||||||
|
int mpp_paths_generation;
|
||||||
|
|
||||||
/* This lock will have the grow table function as writer and add / delete nodes
|
/* This lock will have the grow table function as writer and add / delete nodes
|
||||||
* as readers. RCU provides sufficient protection only when reading the table
|
* as readers. RCU provides sufficient protection only when reading the table
|
||||||
|
@ -409,6 +410,33 @@ mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mpp_path_lookup_by_idx - look up a path in the proxy path table by its index
|
||||||
|
* @idx: index
|
||||||
|
* @sdata: local subif, or NULL for all entries
|
||||||
|
*
|
||||||
|
* Returns: pointer to the proxy path structure, or NULL if not found.
|
||||||
|
*
|
||||||
|
* Locking: must be called within a read rcu section.
|
||||||
|
*/
|
||||||
|
struct mesh_path *
|
||||||
|
mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx)
|
||||||
|
{
|
||||||
|
struct mesh_table *tbl = rcu_dereference(mpp_paths);
|
||||||
|
struct mpath_node *node;
|
||||||
|
int i;
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
|
for_each_mesh_entry(tbl, node, i) {
|
||||||
|
if (sdata && node->mpath->sdata != sdata)
|
||||||
|
continue;
|
||||||
|
if (j++ == idx)
|
||||||
|
return node->mpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mesh_path_add_gate - add the given mpath to a mesh gate to our path table
|
* mesh_path_add_gate - add the given mpath to a mesh gate to our path table
|
||||||
* @mpath: gate path to add to table
|
* @mpath: gate path to add to table
|
||||||
|
@ -691,6 +719,9 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
spin_unlock(&tbl->hashwlock[hash_idx]);
|
spin_unlock(&tbl->hashwlock[hash_idx]);
|
||||||
read_unlock_bh(&pathtbl_resize_lock);
|
read_unlock_bh(&pathtbl_resize_lock);
|
||||||
|
|
||||||
|
mpp_paths_generation++;
|
||||||
|
|
||||||
if (grow) {
|
if (grow) {
|
||||||
set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags);
|
set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags);
|
||||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||||
|
|
|
@ -775,11 +775,30 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
||||||
WLAN_EID_QOS_CAPA,
|
WLAN_EID_QOS_CAPA,
|
||||||
WLAN_EID_RRM_ENABLED_CAPABILITIES,
|
WLAN_EID_RRM_ENABLED_CAPABILITIES,
|
||||||
WLAN_EID_MOBILITY_DOMAIN,
|
WLAN_EID_MOBILITY_DOMAIN,
|
||||||
|
WLAN_EID_FAST_BSS_TRANSITION, /* reassoc only */
|
||||||
|
WLAN_EID_RIC_DATA, /* reassoc only */
|
||||||
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
||||||
};
|
};
|
||||||
noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
|
static const u8 after_ric[] = {
|
||||||
before_ht, ARRAY_SIZE(before_ht),
|
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
||||||
offset);
|
WLAN_EID_HT_CAPABILITY,
|
||||||
|
WLAN_EID_BSS_COEX_2040,
|
||||||
|
WLAN_EID_EXT_CAPABILITY,
|
||||||
|
WLAN_EID_QOS_TRAFFIC_CAPA,
|
||||||
|
WLAN_EID_TIM_BCAST_REQ,
|
||||||
|
WLAN_EID_INTERWORKING,
|
||||||
|
/* 60GHz doesn't happen right now */
|
||||||
|
WLAN_EID_VHT_CAPABILITY,
|
||||||
|
WLAN_EID_OPMODE_NOTIF,
|
||||||
|
};
|
||||||
|
|
||||||
|
noffset = ieee80211_ie_split_ric(assoc_data->ie,
|
||||||
|
assoc_data->ie_len,
|
||||||
|
before_ht,
|
||||||
|
ARRAY_SIZE(before_ht),
|
||||||
|
after_ric,
|
||||||
|
ARRAY_SIZE(after_ric),
|
||||||
|
offset);
|
||||||
pos = skb_put(skb, noffset - offset);
|
pos = skb_put(skb, noffset - offset);
|
||||||
memcpy(pos, assoc_data->ie + offset, noffset - offset);
|
memcpy(pos, assoc_data->ie + offset, noffset - offset);
|
||||||
offset = noffset;
|
offset = noffset;
|
||||||
|
@ -813,6 +832,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
||||||
WLAN_EID_TIM_BCAST_REQ,
|
WLAN_EID_TIM_BCAST_REQ,
|
||||||
WLAN_EID_INTERWORKING,
|
WLAN_EID_INTERWORKING,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* RIC already taken above, so no need to handle here anymore */
|
||||||
noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
|
noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
|
||||||
before_vht, ARRAY_SIZE(before_vht),
|
before_vht, ARRAY_SIZE(before_vht),
|
||||||
offset);
|
offset);
|
||||||
|
@ -1001,14 +1022,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
||||||
/* XXX: shouldn't really modify cfg80211-owned data! */
|
/* XXX: shouldn't really modify cfg80211-owned data! */
|
||||||
ifmgd->associated->channel = sdata->csa_chandef.chan;
|
ifmgd->associated->channel = sdata->csa_chandef.chan;
|
||||||
|
|
||||||
sdata->vif.csa_active = false;
|
ifmgd->csa_waiting_bcn = true;
|
||||||
|
|
||||||
/* XXX: wait for a beacon first? */
|
|
||||||
if (sdata->csa_block_tx) {
|
|
||||||
ieee80211_wake_vif_queues(local, sdata,
|
|
||||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
||||||
sdata->csa_block_tx = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ieee80211_sta_reset_beacon_monitor(sdata);
|
ieee80211_sta_reset_beacon_monitor(sdata);
|
||||||
ieee80211_sta_reset_conn_monitor(sdata);
|
ieee80211_sta_reset_conn_monitor(sdata);
|
||||||
|
@ -1019,6 +1033,35 @@ out:
|
||||||
sdata_unlock(sdata);
|
sdata_unlock(sdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
sdata_assert_lock(sdata);
|
||||||
|
|
||||||
|
WARN_ON(!sdata->vif.csa_active);
|
||||||
|
|
||||||
|
if (sdata->csa_block_tx) {
|
||||||
|
ieee80211_wake_vif_queues(local, sdata,
|
||||||
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||||
|
sdata->csa_block_tx = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdata->vif.csa_active = false;
|
||||||
|
ifmgd->csa_waiting_bcn = false;
|
||||||
|
|
||||||
|
ret = drv_post_channel_switch(sdata);
|
||||||
|
if (ret) {
|
||||||
|
sdata_info(sdata,
|
||||||
|
"driver post channel switch failed, disconnecting\n");
|
||||||
|
ieee80211_queue_work(&local->hw,
|
||||||
|
&ifmgd->csa_connection_drop_work);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
|
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
|
||||||
{
|
{
|
||||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||||
|
@ -1046,7 +1089,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||||
u64 timestamp, struct ieee802_11_elems *elems,
|
u64 timestamp, u32 device_timestamp,
|
||||||
|
struct ieee802_11_elems *elems,
|
||||||
bool beacon)
|
bool beacon)
|
||||||
{
|
{
|
||||||
struct ieee80211_local *local = sdata->local;
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
@ -1056,6 +1100,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_chanctx *chanctx;
|
struct ieee80211_chanctx *chanctx;
|
||||||
enum ieee80211_band current_band;
|
enum ieee80211_band current_band;
|
||||||
struct ieee80211_csa_ie csa_ie;
|
struct ieee80211_csa_ie csa_ie;
|
||||||
|
struct ieee80211_channel_switch ch_switch;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
sdata_assert_lock(sdata);
|
sdata_assert_lock(sdata);
|
||||||
|
@ -1110,21 +1155,31 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
|
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||||
|
|
||||||
if (local->use_chanctx) {
|
if (local->use_chanctx &&
|
||||||
u32 num_chanctx = 0;
|
!(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
|
||||||
list_for_each_entry(chanctx, &local->chanctx_list, list)
|
sdata_info(sdata,
|
||||||
num_chanctx++;
|
"driver doesn't support chan-switch with channel contexts\n");
|
||||||
|
ieee80211_queue_work(&local->hw,
|
||||||
|
&ifmgd->csa_connection_drop_work);
|
||||||
|
mutex_unlock(&local->chanctx_mtx);
|
||||||
|
mutex_unlock(&local->mtx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (num_chanctx > 1 ||
|
ch_switch.timestamp = timestamp;
|
||||||
!(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
|
ch_switch.device_timestamp = device_timestamp;
|
||||||
sdata_info(sdata,
|
ch_switch.block_tx = csa_ie.mode;
|
||||||
"not handling chan-switch with channel contexts\n");
|
ch_switch.chandef = csa_ie.chandef;
|
||||||
ieee80211_queue_work(&local->hw,
|
ch_switch.count = csa_ie.count;
|
||||||
&ifmgd->csa_connection_drop_work);
|
|
||||||
mutex_unlock(&local->chanctx_mtx);
|
if (drv_pre_channel_switch(sdata, &ch_switch)) {
|
||||||
mutex_unlock(&local->mtx);
|
sdata_info(sdata,
|
||||||
return;
|
"preparing for channel switch failed, disconnecting\n");
|
||||||
}
|
ieee80211_queue_work(&local->hw,
|
||||||
|
&ifmgd->csa_connection_drop_work);
|
||||||
|
mutex_unlock(&local->chanctx_mtx);
|
||||||
|
mutex_unlock(&local->mtx);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
|
res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
|
||||||
|
@ -1152,14 +1207,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
if (local->ops->channel_switch) {
|
if (local->ops->channel_switch) {
|
||||||
/* use driver's channel switch callback */
|
/* use driver's channel switch callback */
|
||||||
struct ieee80211_channel_switch ch_switch = {
|
drv_channel_switch(local, sdata, &ch_switch);
|
||||||
.timestamp = timestamp,
|
|
||||||
.block_tx = csa_ie.mode,
|
|
||||||
.chandef = csa_ie.chandef,
|
|
||||||
.count = csa_ie.count,
|
|
||||||
};
|
|
||||||
|
|
||||||
drv_channel_switch(local, &ch_switch);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1579,6 +1627,95 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work)
|
||||||
mutex_unlock(&sdata->local->mtx);
|
mutex_unlock(&sdata->local->mtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||||
|
bool ret;
|
||||||
|
int ac;
|
||||||
|
|
||||||
|
if (local->hw.queues < IEEE80211_NUM_ACS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||||
|
struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
|
||||||
|
int non_acm_ac;
|
||||||
|
unsigned long now = jiffies;
|
||||||
|
|
||||||
|
if (tx_tspec->action == TX_TSPEC_ACTION_NONE &&
|
||||||
|
tx_tspec->admitted_time &&
|
||||||
|
time_after(now, tx_tspec->time_slice_start + HZ)) {
|
||||||
|
tx_tspec->consumed_tx_time = 0;
|
||||||
|
tx_tspec->time_slice_start = now;
|
||||||
|
|
||||||
|
if (tx_tspec->downgraded)
|
||||||
|
tx_tspec->action =
|
||||||
|
TX_TSPEC_ACTION_STOP_DOWNGRADE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (tx_tspec->action) {
|
||||||
|
case TX_TSPEC_ACTION_STOP_DOWNGRADE:
|
||||||
|
/* take the original parameters */
|
||||||
|
if (drv_conf_tx(local, sdata, ac, &sdata->tx_conf[ac]))
|
||||||
|
sdata_err(sdata,
|
||||||
|
"failed to set TX queue parameters for queue %d\n",
|
||||||
|
ac);
|
||||||
|
tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
||||||
|
tx_tspec->downgraded = false;
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
case TX_TSPEC_ACTION_DOWNGRADE:
|
||||||
|
if (time_after(now, tx_tspec->time_slice_start + HZ)) {
|
||||||
|
tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* downgrade next lower non-ACM AC */
|
||||||
|
for (non_acm_ac = ac + 1;
|
||||||
|
non_acm_ac < IEEE80211_NUM_ACS;
|
||||||
|
non_acm_ac++)
|
||||||
|
if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac)))
|
||||||
|
break;
|
||||||
|
/* The loop will result in using BK even if it requires
|
||||||
|
* admission control, such configuration makes no sense
|
||||||
|
* and we have to transmit somehow - the AC selection
|
||||||
|
* does the same thing.
|
||||||
|
*/
|
||||||
|
if (drv_conf_tx(local, sdata, ac,
|
||||||
|
&sdata->tx_conf[non_acm_ac]))
|
||||||
|
sdata_err(sdata,
|
||||||
|
"failed to set TX queue parameters for queue %d\n",
|
||||||
|
ac);
|
||||||
|
tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
||||||
|
ret = true;
|
||||||
|
schedule_delayed_work(&ifmgd->tx_tspec_wk,
|
||||||
|
tx_tspec->time_slice_start + HZ - now + 1);
|
||||||
|
break;
|
||||||
|
case TX_TSPEC_ACTION_NONE:
|
||||||
|
/* nothing now */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
if (__ieee80211_sta_handle_tspec_ac_params(sdata))
|
||||||
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata;
|
||||||
|
|
||||||
|
sdata = container_of(work, struct ieee80211_sub_if_data,
|
||||||
|
u.mgd.tx_tspec_wk.work);
|
||||||
|
ieee80211_sta_handle_tspec_ac_params(sdata);
|
||||||
|
}
|
||||||
|
|
||||||
/* MLME */
|
/* MLME */
|
||||||
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
||||||
struct ieee80211_sub_if_data *sdata,
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
@ -1663,12 +1800,14 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
||||||
params.uapsd = uapsd;
|
params.uapsd = uapsd;
|
||||||
|
|
||||||
mlme_dbg(sdata,
|
mlme_dbg(sdata,
|
||||||
"WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
|
"WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
|
||||||
queue, aci, acm,
|
queue, aci, acm,
|
||||||
params.aifs, params.cw_min, params.cw_max,
|
params.aifs, params.cw_min, params.cw_max,
|
||||||
params.txop, params.uapsd);
|
params.txop, params.uapsd,
|
||||||
|
ifmgd->tx_tspec[queue].downgraded);
|
||||||
sdata->tx_conf[queue] = params;
|
sdata->tx_conf[queue] = params;
|
||||||
if (drv_conf_tx(local, sdata, queue, ¶ms))
|
if (!ifmgd->tx_tspec[queue].downgraded &&
|
||||||
|
drv_conf_tx(local, sdata, queue, ¶ms))
|
||||||
sdata_err(sdata,
|
sdata_err(sdata,
|
||||||
"failed to set TX queue parameters for queue %d\n",
|
"failed to set TX queue parameters for queue %d\n",
|
||||||
queue);
|
queue);
|
||||||
|
@ -1923,6 +2062,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||||
ieee80211_vif_release_channel(sdata);
|
ieee80211_vif_release_channel(sdata);
|
||||||
|
|
||||||
sdata->vif.csa_active = false;
|
sdata->vif.csa_active = false;
|
||||||
|
ifmgd->csa_waiting_bcn = false;
|
||||||
if (sdata->csa_block_tx) {
|
if (sdata->csa_block_tx) {
|
||||||
ieee80211_wake_vif_queues(local, sdata,
|
ieee80211_wake_vif_queues(local, sdata,
|
||||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||||
|
@ -1930,6 +2070,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||||
}
|
}
|
||||||
mutex_unlock(&local->mtx);
|
mutex_unlock(&local->mtx);
|
||||||
|
|
||||||
|
/* existing TX TSPEC sessions no longer exist */
|
||||||
|
memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec));
|
||||||
|
cancel_delayed_work_sync(&ifmgd->tx_tspec_wk);
|
||||||
|
|
||||||
sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
|
sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1982,9 +2126,46 @@ out:
|
||||||
mutex_unlock(&local->mtx);
|
mutex_unlock(&local->mtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_hdr *hdr, bool ack)
|
struct ieee80211_hdr *hdr,
|
||||||
|
u16 tx_time)
|
||||||
{
|
{
|
||||||
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||||
|
u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
|
||||||
|
int ac = ieee80211_ac_from_tid(tid);
|
||||||
|
struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
|
||||||
|
unsigned long now = jiffies;
|
||||||
|
|
||||||
|
if (likely(!tx_tspec->admitted_time))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (time_after(now, tx_tspec->time_slice_start + HZ)) {
|
||||||
|
tx_tspec->consumed_tx_time = 0;
|
||||||
|
tx_tspec->time_slice_start = now;
|
||||||
|
|
||||||
|
if (tx_tspec->downgraded) {
|
||||||
|
tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
|
||||||
|
schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx_tspec->downgraded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tx_tspec->consumed_tx_time += tx_time;
|
||||||
|
|
||||||
|
if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) {
|
||||||
|
tx_tspec->downgraded = true;
|
||||||
|
tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE;
|
||||||
|
schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee80211_hdr *hdr, bool ack, u16 tx_time)
|
||||||
|
{
|
||||||
|
ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
|
||||||
|
|
||||||
if (!ieee80211_is_data(hdr->frame_control))
|
if (!ieee80211_is_data(hdr->frame_control))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2047,8 +2228,6 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
||||||
|
|
||||||
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
|
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
|
||||||
run_again(sdata, ifmgd->probe_timeout);
|
run_again(sdata, ifmgd->probe_timeout);
|
||||||
if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
|
|
||||||
ieee80211_flush_queues(sdata->local, sdata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
|
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
|
||||||
|
@ -2171,6 +2350,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
||||||
true, frame_buf);
|
true, frame_buf);
|
||||||
mutex_lock(&local->mtx);
|
mutex_lock(&local->mtx);
|
||||||
sdata->vif.csa_active = false;
|
sdata->vif.csa_active = false;
|
||||||
|
ifmgd->csa_waiting_bcn = false;
|
||||||
if (sdata->csa_block_tx) {
|
if (sdata->csa_block_tx) {
|
||||||
ieee80211_wake_vif_queues(local, sdata,
|
ieee80211_wake_vif_queues(local, sdata,
|
||||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||||
|
@ -3195,6 +3375,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ifmgd->csa_waiting_bcn)
|
||||||
|
ieee80211_chswitch_post_beacon(sdata);
|
||||||
|
|
||||||
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
|
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
|
||||||
return;
|
return;
|
||||||
ifmgd->beacon_crc = ncrc;
|
ifmgd->beacon_crc = ncrc;
|
||||||
|
@ -3203,6 +3386,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||||
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
||||||
|
|
||||||
ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
|
ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
|
||||||
|
rx_status->device_timestamp,
|
||||||
&elems, true);
|
&elems, true);
|
||||||
|
|
||||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
|
||||||
|
@ -3334,8 +3518,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ieee80211_sta_process_chanswitch(sdata,
|
ieee80211_sta_process_chanswitch(sdata,
|
||||||
rx_status->mactime,
|
rx_status->mactime,
|
||||||
&elems, false);
|
rx_status->device_timestamp,
|
||||||
|
&elems, false);
|
||||||
} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
|
} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
|
||||||
ies_len = skb->len -
|
ies_len = skb->len -
|
||||||
offsetof(struct ieee80211_mgmt,
|
offsetof(struct ieee80211_mgmt,
|
||||||
|
@ -3356,8 +3541,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||||
&mgmt->u.action.u.ext_chan_switch.data;
|
&mgmt->u.action.u.ext_chan_switch.data;
|
||||||
|
|
||||||
ieee80211_sta_process_chanswitch(sdata,
|
ieee80211_sta_process_chanswitch(sdata,
|
||||||
rx_status->mactime,
|
rx_status->mactime,
|
||||||
&elems, false);
|
rx_status->device_timestamp,
|
||||||
|
&elems, false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3664,11 +3850,12 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
|
||||||
struct ieee80211_sub_if_data *sdata =
|
struct ieee80211_sub_if_data *sdata =
|
||||||
(struct ieee80211_sub_if_data *) data;
|
(struct ieee80211_sub_if_data *) data;
|
||||||
struct ieee80211_local *local = sdata->local;
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||||
|
|
||||||
if (local->quiescing)
|
if (local->quiescing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (sdata->vif.csa_active)
|
if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sdata->u.mgd.connection_loss = false;
|
sdata->u.mgd.connection_loss = false;
|
||||||
|
@ -3686,7 +3873,7 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
|
||||||
if (local->quiescing)
|
if (local->quiescing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (sdata->vif.csa_active)
|
if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
|
ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
|
||||||
|
@ -3798,6 +3985,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||||
(unsigned long) sdata);
|
(unsigned long) sdata);
|
||||||
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
|
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
|
||||||
(unsigned long) sdata);
|
(unsigned long) sdata);
|
||||||
|
INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
|
||||||
|
ieee80211_sta_handle_tspec_ac_params_wk);
|
||||||
|
|
||||||
ifmgd->flags = 0;
|
ifmgd->flags = 0;
|
||||||
ifmgd->powersave = sdata->wdev.ps;
|
ifmgd->powersave = sdata->wdev.ps;
|
||||||
|
|
250
net/mac80211/ocb.c
Normal file
250
net/mac80211/ocb.c
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
* OCB mode implementation
|
||||||
|
*
|
||||||
|
* Copyright: (c) 2014 Czech Technical University in Prague
|
||||||
|
* (c) 2014 Volkswagen Group Research
|
||||||
|
* Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
|
||||||
|
* Funded by: Volkswagen Group Research
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
#include <linux/if_arp.h>
|
||||||
|
#include <linux/etherdevice.h>
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
#include <net/mac80211.h>
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
#include "ieee80211_i.h"
|
||||||
|
#include "driver-ops.h"
|
||||||
|
#include "rate.h"
|
||||||
|
|
||||||
|
#define IEEE80211_OCB_HOUSEKEEPING_INTERVAL (60 * HZ)
|
||||||
|
#define IEEE80211_OCB_PEER_INACTIVITY_LIMIT (240 * HZ)
|
||||||
|
#define IEEE80211_OCB_MAX_STA_ENTRIES 128
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ocb_deferred_task_flags - mac80211 OCB deferred tasks
|
||||||
|
* @OCB_WORK_HOUSEKEEPING: run the periodic OCB housekeeping tasks
|
||||||
|
*
|
||||||
|
* These flags are used in @wrkq_flags field of &struct ieee80211_if_ocb
|
||||||
|
*/
|
||||||
|
enum ocb_deferred_task_flags {
|
||||||
|
OCB_WORK_HOUSEKEEPING,
|
||||||
|
};
|
||||||
|
|
||||||
|
void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
|
||||||
|
const u8 *bssid, const u8 *addr,
|
||||||
|
u32 supp_rates)
|
||||||
|
{
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||||
|
struct ieee80211_supported_band *sband;
|
||||||
|
enum nl80211_bss_scan_width scan_width;
|
||||||
|
struct sta_info *sta;
|
||||||
|
int band;
|
||||||
|
|
||||||
|
/* XXX: Consider removing the least recently used entry and
|
||||||
|
* allow new one to be added.
|
||||||
|
*/
|
||||||
|
if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) {
|
||||||
|
net_info_ratelimited("%s: No room for a new OCB STA entry %pM\n",
|
||||||
|
sdata->name, addr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ocb_dbg(sdata, "Adding new OCB station %pM\n", addr);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||||
|
if (WARN_ON_ONCE(!chanctx_conf)) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
band = chanctx_conf->def.chan->band;
|
||||||
|
scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
|
||||||
|
if (!sta)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sta->last_rx = jiffies;
|
||||||
|
|
||||||
|
/* Add only mandatory rates for now */
|
||||||
|
sband = local->hw.wiphy->bands[band];
|
||||||
|
sta->sta.supp_rates[band] =
|
||||||
|
ieee80211_mandatory_rates(sband, scan_width);
|
||||||
|
|
||||||
|
spin_lock(&ifocb->incomplete_lock);
|
||||||
|
list_add(&sta->list, &ifocb->incomplete_stations);
|
||||||
|
spin_unlock(&ifocb->incomplete_lock);
|
||||||
|
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta)
|
||||||
|
__acquires(RCU)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
|
||||||
|
memcpy(addr, sta->sta.addr, ETH_ALEN);
|
||||||
|
|
||||||
|
ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n",
|
||||||
|
addr, sdata->name);
|
||||||
|
|
||||||
|
sta_info_move_state(sta, IEEE80211_STA_AUTH);
|
||||||
|
sta_info_move_state(sta, IEEE80211_STA_ASSOC);
|
||||||
|
sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
|
||||||
|
|
||||||
|
rate_control_rate_init(sta);
|
||||||
|
|
||||||
|
/* If it fails, maybe we raced another insertion? */
|
||||||
|
if (sta_info_insert_rcu(sta))
|
||||||
|
return sta_info_get(sdata, addr);
|
||||||
|
return sta;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
|
||||||
|
ocb_dbg(sdata, "Running ocb housekeeping\n");
|
||||||
|
|
||||||
|
ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT);
|
||||||
|
|
||||||
|
mod_timer(&ifocb->housekeeping_timer,
|
||||||
|
round_jiffies(jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
if (ifocb->joined != true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sdata_lock(sdata);
|
||||||
|
|
||||||
|
spin_lock_bh(&ifocb->incomplete_lock);
|
||||||
|
while (!list_empty(&ifocb->incomplete_stations)) {
|
||||||
|
sta = list_first_entry(&ifocb->incomplete_stations,
|
||||||
|
struct sta_info, list);
|
||||||
|
list_del(&sta->list);
|
||||||
|
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||||
|
|
||||||
|
ieee80211_ocb_finish_sta(sta);
|
||||||
|
rcu_read_unlock();
|
||||||
|
spin_lock_bh(&ifocb->incomplete_lock);
|
||||||
|
}
|
||||||
|
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||||
|
|
||||||
|
if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags))
|
||||||
|
ieee80211_ocb_housekeeping(sdata);
|
||||||
|
|
||||||
|
sdata_unlock(sdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ieee80211_ocb_housekeeping_timer(unsigned long data)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = (void *)data;
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
|
||||||
|
set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
|
||||||
|
|
||||||
|
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
|
||||||
|
setup_timer(&ifocb->housekeeping_timer,
|
||||||
|
ieee80211_ocb_housekeeping_timer,
|
||||||
|
(unsigned long)sdata);
|
||||||
|
INIT_LIST_HEAD(&ifocb->incomplete_stations);
|
||||||
|
spin_lock_init(&ifocb->incomplete_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ocb_setup *setup)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
u32 changed = BSS_CHANGED_OCB;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (ifocb->joined == true)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
|
||||||
|
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
||||||
|
sdata->needed_rx_chains = sdata->local->rx_chains;
|
||||||
|
|
||||||
|
mutex_lock(&sdata->local->mtx);
|
||||||
|
err = ieee80211_vif_use_channel(sdata, &setup->chandef,
|
||||||
|
IEEE80211_CHANCTX_SHARED);
|
||||||
|
mutex_unlock(&sdata->local->mtx);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
ieee80211_bss_info_change_notify(sdata, changed);
|
||||||
|
|
||||||
|
ifocb->joined = true;
|
||||||
|
|
||||||
|
set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
|
||||||
|
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||||
|
|
||||||
|
netif_carrier_on(sdata->dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
ifocb->joined = false;
|
||||||
|
sta_info_flush(sdata);
|
||||||
|
|
||||||
|
spin_lock_bh(&ifocb->incomplete_lock);
|
||||||
|
while (!list_empty(&ifocb->incomplete_stations)) {
|
||||||
|
sta = list_first_entry(&ifocb->incomplete_stations,
|
||||||
|
struct sta_info, list);
|
||||||
|
list_del(&sta->list);
|
||||||
|
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||||
|
|
||||||
|
sta_info_free(local, sta);
|
||||||
|
spin_lock_bh(&ifocb->incomplete_lock);
|
||||||
|
}
|
||||||
|
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||||
|
|
||||||
|
netif_carrier_off(sdata->dev);
|
||||||
|
clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
|
||||||
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB);
|
||||||
|
|
||||||
|
mutex_lock(&sdata->local->mtx);
|
||||||
|
ieee80211_vif_release_channel(sdata);
|
||||||
|
mutex_unlock(&sdata->local->mtx);
|
||||||
|
|
||||||
|
skb_queue_purge(&sdata->skb_queue);
|
||||||
|
|
||||||
|
del_timer_sync(&sdata->u.ocb.housekeeping_timer);
|
||||||
|
/* If the timer fired while we waited for it, it will have
|
||||||
|
* requeued the work. Now the work will be running again
|
||||||
|
* but will not rearm the timer again because it checks
|
||||||
|
* whether we are connected to the network or not -- at this
|
||||||
|
* point we shouldn't be anymore.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -448,7 +448,7 @@ static void rate_fixup_ratelist(struct ieee80211_vif *vif,
|
||||||
*/
|
*/
|
||||||
if (!(rates[0].flags & IEEE80211_TX_RC_MCS)) {
|
if (!(rates[0].flags & IEEE80211_TX_RC_MCS)) {
|
||||||
u32 basic_rates = vif->bss_conf.basic_rates;
|
u32 basic_rates = vif->bss_conf.basic_rates;
|
||||||
s8 baserate = basic_rates ? ffs(basic_rates - 1) : 0;
|
s8 baserate = basic_rates ? ffs(basic_rates) - 1 : 0;
|
||||||
|
|
||||||
rate = &sband->bitrates[rates[0].idx];
|
rate = &sband->bitrates[rates[0].idx];
|
||||||
|
|
||||||
|
|
|
@ -191,7 +191,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
|
||||||
* (1) if any success probabilitiy >= 95%, out of those rates
|
* (1) if any success probabilitiy >= 95%, out of those rates
|
||||||
* choose the maximum throughput rate as max_prob_rate
|
* choose the maximum throughput rate as max_prob_rate
|
||||||
* (2) if all success probabilities < 95%, the rate with
|
* (2) if all success probabilities < 95%, the rate with
|
||||||
* highest success probability is choosen as max_prob_rate */
|
* highest success probability is chosen as max_prob_rate */
|
||||||
if (mrs->probability >= MINSTREL_FRAC(95, 100)) {
|
if (mrs->probability >= MINSTREL_FRAC(95, 100)) {
|
||||||
if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp)
|
if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp)
|
||||||
tmp_prob_rate = i;
|
tmp_prob_rate = i;
|
||||||
|
|
|
@ -62,14 +62,14 @@ minstrel_stats_open(struct inode *inode, struct file *file)
|
||||||
unsigned int i, tp, prob, eprob;
|
unsigned int i, tp, prob, eprob;
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
ms = kmalloc(sizeof(*ms) + 4096, GFP_KERNEL);
|
ms = kmalloc(2048, GFP_KERNEL);
|
||||||
if (!ms)
|
if (!ms)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
file->private_data = ms;
|
file->private_data = ms;
|
||||||
p = ms->buf;
|
p = ms->buf;
|
||||||
p += sprintf(p, "rate throughput ewma prob this prob "
|
p += sprintf(p, "rate tpt eprob *prob"
|
||||||
"this succ/attempt success attempts\n");
|
" *ok(*cum) ok( cum)\n");
|
||||||
for (i = 0; i < mi->n_rates; i++) {
|
for (i = 0; i < mi->n_rates; i++) {
|
||||||
struct minstrel_rate *mr = &mi->r[i];
|
struct minstrel_rate *mr = &mi->r[i];
|
||||||
struct minstrel_rate_stats *mrs = &mi->r[i].stats;
|
struct minstrel_rate_stats *mrs = &mi->r[i].stats;
|
||||||
|
@ -86,8 +86,8 @@ minstrel_stats_open(struct inode *inode, struct file *file)
|
||||||
prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
|
prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
|
||||||
eprob = MINSTREL_TRUNC(mrs->probability * 1000);
|
eprob = MINSTREL_TRUNC(mrs->probability * 1000);
|
||||||
|
|
||||||
p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
|
p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u"
|
||||||
" %3u(%3u) %8llu %8llu\n",
|
" %4u(%4u) %9llu(%9llu)\n",
|
||||||
tp / 10, tp % 10,
|
tp / 10, tp % 10,
|
||||||
eprob / 10, eprob % 10,
|
eprob / 10, eprob % 10,
|
||||||
prob / 10, prob % 10,
|
prob / 10, prob % 10,
|
||||||
|
@ -102,6 +102,8 @@ minstrel_stats_open(struct inode *inode, struct file *file)
|
||||||
mi->sample_packets);
|
mi->sample_packets);
|
||||||
ms->len = p - ms->buf;
|
ms->len = p - ms->buf;
|
||||||
|
|
||||||
|
WARN_ON(ms->len + sizeof(*ms) > 2048);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/ieee80211.h>
|
#include <linux/ieee80211.h>
|
||||||
#include <net/mac80211.h>
|
#include <net/mac80211.h>
|
||||||
#include "rate.h"
|
#include "rate.h"
|
||||||
|
@ -34,12 +35,17 @@
|
||||||
/* Transmit duration for the raw data part of an average sized packet */
|
/* Transmit duration for the raw data part of an average sized packet */
|
||||||
#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps)))
|
#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps)))
|
||||||
|
|
||||||
|
#define BW_20 0
|
||||||
|
#define BW_40 1
|
||||||
|
#define BW_80 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define group sort order: HT40 -> SGI -> #streams
|
* Define group sort order: HT40 -> SGI -> #streams
|
||||||
*/
|
*/
|
||||||
#define GROUP_IDX(_streams, _sgi, _ht40) \
|
#define GROUP_IDX(_streams, _sgi, _ht40) \
|
||||||
|
MINSTREL_HT_GROUP_0 + \
|
||||||
MINSTREL_MAX_STREAMS * 2 * _ht40 + \
|
MINSTREL_MAX_STREAMS * 2 * _ht40 + \
|
||||||
MINSTREL_MAX_STREAMS * _sgi + \
|
MINSTREL_MAX_STREAMS * _sgi + \
|
||||||
_streams - 1
|
_streams - 1
|
||||||
|
|
||||||
/* MCS rate information for an MCS group */
|
/* MCS rate information for an MCS group */
|
||||||
|
@ -47,6 +53,7 @@
|
||||||
[GROUP_IDX(_streams, _sgi, _ht40)] = { \
|
[GROUP_IDX(_streams, _sgi, _ht40)] = { \
|
||||||
.streams = _streams, \
|
.streams = _streams, \
|
||||||
.flags = \
|
.flags = \
|
||||||
|
IEEE80211_TX_RC_MCS | \
|
||||||
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
|
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
|
||||||
(_ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \
|
(_ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \
|
||||||
.duration = { \
|
.duration = { \
|
||||||
|
@ -61,6 +68,47 @@
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define VHT_GROUP_IDX(_streams, _sgi, _bw) \
|
||||||
|
(MINSTREL_VHT_GROUP_0 + \
|
||||||
|
MINSTREL_MAX_STREAMS * 2 * (_bw) + \
|
||||||
|
MINSTREL_MAX_STREAMS * (_sgi) + \
|
||||||
|
(_streams) - 1)
|
||||||
|
|
||||||
|
#define BW2VBPS(_bw, r3, r2, r1) \
|
||||||
|
(_bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)
|
||||||
|
|
||||||
|
#define VHT_GROUP(_streams, _sgi, _bw) \
|
||||||
|
[VHT_GROUP_IDX(_streams, _sgi, _bw)] = { \
|
||||||
|
.streams = _streams, \
|
||||||
|
.flags = \
|
||||||
|
IEEE80211_TX_RC_VHT_MCS | \
|
||||||
|
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
|
||||||
|
(_bw == BW_80 ? IEEE80211_TX_RC_80_MHZ_WIDTH : \
|
||||||
|
_bw == BW_40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \
|
||||||
|
.duration = { \
|
||||||
|
MCS_DURATION(_streams, _sgi, \
|
||||||
|
BW2VBPS(_bw, 117, 54, 26)), \
|
||||||
|
MCS_DURATION(_streams, _sgi, \
|
||||||
|
BW2VBPS(_bw, 234, 108, 52)), \
|
||||||
|
MCS_DURATION(_streams, _sgi, \
|
||||||
|
BW2VBPS(_bw, 351, 162, 78)), \
|
||||||
|
MCS_DURATION(_streams, _sgi, \
|
||||||
|
BW2VBPS(_bw, 468, 216, 104)), \
|
||||||
|
MCS_DURATION(_streams, _sgi, \
|
||||||
|
BW2VBPS(_bw, 702, 324, 156)), \
|
||||||
|
MCS_DURATION(_streams, _sgi, \
|
||||||
|
BW2VBPS(_bw, 936, 432, 208)), \
|
||||||
|
MCS_DURATION(_streams, _sgi, \
|
||||||
|
BW2VBPS(_bw, 1053, 486, 234)), \
|
||||||
|
MCS_DURATION(_streams, _sgi, \
|
||||||
|
BW2VBPS(_bw, 1170, 540, 260)), \
|
||||||
|
MCS_DURATION(_streams, _sgi, \
|
||||||
|
BW2VBPS(_bw, 1404, 648, 312)), \
|
||||||
|
MCS_DURATION(_streams, _sgi, \
|
||||||
|
BW2VBPS(_bw, 1560, 720, 346)) \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
#define CCK_DURATION(_bitrate, _short, _len) \
|
#define CCK_DURATION(_bitrate, _short, _len) \
|
||||||
(1000 * (10 /* SIFS */ + \
|
(1000 * (10 /* SIFS */ + \
|
||||||
(_short ? 72 + 24 : 144 + 48) + \
|
(_short ? 72 + 24 : 144 + 48) + \
|
||||||
|
@ -76,70 +124,161 @@
|
||||||
CCK_ACK_DURATION(55, _short), \
|
CCK_ACK_DURATION(55, _short), \
|
||||||
CCK_ACK_DURATION(110, _short)
|
CCK_ACK_DURATION(110, _short)
|
||||||
|
|
||||||
#define CCK_GROUP \
|
#define CCK_GROUP \
|
||||||
[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = { \
|
[MINSTREL_CCK_GROUP] = { \
|
||||||
.streams = 0, \
|
.streams = 0, \
|
||||||
.duration = { \
|
.flags = 0, \
|
||||||
CCK_DURATION_LIST(false), \
|
.duration = { \
|
||||||
CCK_DURATION_LIST(true) \
|
CCK_DURATION_LIST(false), \
|
||||||
} \
|
CCK_DURATION_LIST(true) \
|
||||||
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||||
|
static bool minstrel_vht_only = true;
|
||||||
|
module_param(minstrel_vht_only, bool, 0644);
|
||||||
|
MODULE_PARM_DESC(minstrel_vht_only,
|
||||||
|
"Use only VHT rates when VHT is supported by sta.");
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To enable sufficiently targeted rate sampling, MCS rates are divided into
|
* To enable sufficiently targeted rate sampling, MCS rates are divided into
|
||||||
* groups, based on the number of streams and flags (HT40, SGI) that they
|
* groups, based on the number of streams and flags (HT40, SGI) that they
|
||||||
* use.
|
* use.
|
||||||
*
|
*
|
||||||
* Sortorder has to be fixed for GROUP_IDX macro to be applicable:
|
* Sortorder has to be fixed for GROUP_IDX macro to be applicable:
|
||||||
* HT40 -> SGI -> #streams
|
* BW -> SGI -> #streams
|
||||||
*/
|
*/
|
||||||
const struct mcs_group minstrel_mcs_groups[] = {
|
const struct mcs_group minstrel_mcs_groups[] = {
|
||||||
MCS_GROUP(1, 0, 0),
|
MCS_GROUP(1, 0, BW_20),
|
||||||
MCS_GROUP(2, 0, 0),
|
MCS_GROUP(2, 0, BW_20),
|
||||||
#if MINSTREL_MAX_STREAMS >= 3
|
#if MINSTREL_MAX_STREAMS >= 3
|
||||||
MCS_GROUP(3, 0, 0),
|
MCS_GROUP(3, 0, BW_20),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MCS_GROUP(1, 1, 0),
|
MCS_GROUP(1, 1, BW_20),
|
||||||
MCS_GROUP(2, 1, 0),
|
MCS_GROUP(2, 1, BW_20),
|
||||||
#if MINSTREL_MAX_STREAMS >= 3
|
#if MINSTREL_MAX_STREAMS >= 3
|
||||||
MCS_GROUP(3, 1, 0),
|
MCS_GROUP(3, 1, BW_20),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MCS_GROUP(1, 0, 1),
|
MCS_GROUP(1, 0, BW_40),
|
||||||
MCS_GROUP(2, 0, 1),
|
MCS_GROUP(2, 0, BW_40),
|
||||||
#if MINSTREL_MAX_STREAMS >= 3
|
#if MINSTREL_MAX_STREAMS >= 3
|
||||||
MCS_GROUP(3, 0, 1),
|
MCS_GROUP(3, 0, BW_40),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MCS_GROUP(1, 1, 1),
|
MCS_GROUP(1, 1, BW_40),
|
||||||
MCS_GROUP(2, 1, 1),
|
MCS_GROUP(2, 1, BW_40),
|
||||||
#if MINSTREL_MAX_STREAMS >= 3
|
#if MINSTREL_MAX_STREAMS >= 3
|
||||||
MCS_GROUP(3, 1, 1),
|
MCS_GROUP(3, 1, BW_40),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* must be last */
|
CCK_GROUP,
|
||||||
CCK_GROUP
|
|
||||||
|
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||||
|
VHT_GROUP(1, 0, BW_20),
|
||||||
|
VHT_GROUP(2, 0, BW_20),
|
||||||
|
#if MINSTREL_MAX_STREAMS >= 3
|
||||||
|
VHT_GROUP(3, 0, BW_20),
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VHT_GROUP(1, 1, BW_20),
|
||||||
|
VHT_GROUP(2, 1, BW_20),
|
||||||
|
#if MINSTREL_MAX_STREAMS >= 3
|
||||||
|
VHT_GROUP(3, 1, BW_20),
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VHT_GROUP(1, 0, BW_40),
|
||||||
|
VHT_GROUP(2, 0, BW_40),
|
||||||
|
#if MINSTREL_MAX_STREAMS >= 3
|
||||||
|
VHT_GROUP(3, 0, BW_40),
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VHT_GROUP(1, 1, BW_40),
|
||||||
|
VHT_GROUP(2, 1, BW_40),
|
||||||
|
#if MINSTREL_MAX_STREAMS >= 3
|
||||||
|
VHT_GROUP(3, 1, BW_40),
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VHT_GROUP(1, 0, BW_80),
|
||||||
|
VHT_GROUP(2, 0, BW_80),
|
||||||
|
#if MINSTREL_MAX_STREAMS >= 3
|
||||||
|
VHT_GROUP(3, 0, BW_80),
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VHT_GROUP(1, 1, BW_80),
|
||||||
|
VHT_GROUP(2, 1, BW_80),
|
||||||
|
#if MINSTREL_MAX_STREAMS >= 3
|
||||||
|
VHT_GROUP(3, 1, BW_80),
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1)
|
|
||||||
|
|
||||||
static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
|
static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
|
minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some VHT MCSes are invalid (when Ndbps / Nes is not an integer)
|
||||||
|
* e.g for MCS9@20MHzx1Nss: Ndbps=8x52*(5/6) Nes=1
|
||||||
|
*
|
||||||
|
* Returns the valid mcs map for struct minstrel_mcs_group_data.supported
|
||||||
|
*/
|
||||||
|
static u16
|
||||||
|
minstrel_get_valid_vht_rates(int bw, int nss, __le16 mcs_map)
|
||||||
|
{
|
||||||
|
u16 mask = 0;
|
||||||
|
|
||||||
|
if (bw == BW_20) {
|
||||||
|
if (nss != 3 && nss != 6)
|
||||||
|
mask = BIT(9);
|
||||||
|
} else if (bw == BW_80) {
|
||||||
|
if (nss == 3 || nss == 7)
|
||||||
|
mask = BIT(6);
|
||||||
|
else if (nss == 6)
|
||||||
|
mask = BIT(9);
|
||||||
|
} else {
|
||||||
|
WARN_ON(bw != BW_40);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ((le16_to_cpu(mcs_map) >> (2 * (nss - 1))) & 3) {
|
||||||
|
case IEEE80211_VHT_MCS_SUPPORT_0_7:
|
||||||
|
mask |= 0x300;
|
||||||
|
break;
|
||||||
|
case IEEE80211_VHT_MCS_SUPPORT_0_8:
|
||||||
|
mask |= 0x200;
|
||||||
|
break;
|
||||||
|
case IEEE80211_VHT_MCS_SUPPORT_0_9:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mask = 0x3ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0x3ff & ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look up an MCS group index based on mac80211 rate information
|
* Look up an MCS group index based on mac80211 rate information
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
|
minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
|
||||||
{
|
{
|
||||||
return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1,
|
return GROUP_IDX((rate->idx / 8) + 1,
|
||||||
!!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
|
!!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
|
||||||
!!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
|
!!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
minstrel_vht_get_group_idx(struct ieee80211_tx_rate *rate)
|
||||||
|
{
|
||||||
|
return VHT_GROUP_IDX(ieee80211_rate_get_vht_nss(rate),
|
||||||
|
!!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
|
||||||
|
!!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +
|
||||||
|
2*!!(rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH));
|
||||||
|
}
|
||||||
|
|
||||||
static struct minstrel_rate_stats *
|
static struct minstrel_rate_stats *
|
||||||
minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||||
struct ieee80211_tx_rate *rate)
|
struct ieee80211_tx_rate *rate)
|
||||||
|
@ -149,6 +288,9 @@ minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||||
if (rate->flags & IEEE80211_TX_RC_MCS) {
|
if (rate->flags & IEEE80211_TX_RC_MCS) {
|
||||||
group = minstrel_ht_get_group_idx(rate);
|
group = minstrel_ht_get_group_idx(rate);
|
||||||
idx = rate->idx % 8;
|
idx = rate->idx % 8;
|
||||||
|
} else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
|
||||||
|
group = minstrel_vht_get_group_idx(rate);
|
||||||
|
idx = ieee80211_rate_get_vht_mcs(rate);
|
||||||
} else {
|
} else {
|
||||||
group = MINSTREL_CCK_GROUP;
|
group = MINSTREL_CCK_GROUP;
|
||||||
|
|
||||||
|
@ -240,8 +382,8 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
|
||||||
* MCS groups, CCK rates do not provide aggregation and are therefore at last.
|
* MCS groups, CCK rates do not provide aggregation and are therefore at last.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index,
|
minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index,
|
||||||
u8 *tp_list)
|
u16 *tp_list)
|
||||||
{
|
{
|
||||||
int cur_group, cur_idx, cur_thr, cur_prob;
|
int cur_group, cur_idx, cur_thr, cur_prob;
|
||||||
int tmp_group, tmp_idx, tmp_thr, tmp_prob;
|
int tmp_group, tmp_idx, tmp_thr, tmp_prob;
|
||||||
|
@ -278,7 +420,7 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index,
|
||||||
* Find and set the topmost probability rate per sta and per group
|
* Find and set the topmost probability rate per sta and per group
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index)
|
minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
|
||||||
{
|
{
|
||||||
struct minstrel_mcs_group_data *mg;
|
struct minstrel_mcs_group_data *mg;
|
||||||
struct minstrel_rate_stats *mr;
|
struct minstrel_rate_stats *mr;
|
||||||
|
@ -321,8 +463,8 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index)
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
|
minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
|
||||||
u8 tmp_mcs_tp_rate[MAX_THR_RATES],
|
u16 tmp_mcs_tp_rate[MAX_THR_RATES],
|
||||||
u8 tmp_cck_tp_rate[MAX_THR_RATES])
|
u16 tmp_cck_tp_rate[MAX_THR_RATES])
|
||||||
{
|
{
|
||||||
unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp;
|
unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp;
|
||||||
int i;
|
int i;
|
||||||
|
@ -386,8 +528,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||||
struct minstrel_mcs_group_data *mg;
|
struct minstrel_mcs_group_data *mg;
|
||||||
struct minstrel_rate_stats *mr;
|
struct minstrel_rate_stats *mr;
|
||||||
int group, i, j;
|
int group, i, j;
|
||||||
u8 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
|
u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
|
||||||
u8 tmp_cck_tp_rate[MAX_THR_RATES], index;
|
u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
|
||||||
|
|
||||||
if (mi->ampdu_packets > 0) {
|
if (mi->ampdu_packets > 0) {
|
||||||
mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
|
mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
|
||||||
|
@ -485,7 +627,8 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat
|
||||||
if (!rate->count)
|
if (!rate->count)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (rate->flags & IEEE80211_TX_RC_MCS)
|
if (rate->flags & IEEE80211_TX_RC_MCS ||
|
||||||
|
rate->flags & IEEE80211_TX_RC_VHT_MCS)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return rate->idx == mp->cck_rates[0] ||
|
return rate->idx == mp->cck_rates[0] ||
|
||||||
|
@ -517,7 +660,7 @@ minstrel_next_sample_idx(struct minstrel_ht_sta *mi)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u8 *idx, bool primary)
|
minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
|
||||||
{
|
{
|
||||||
int group, orig_group;
|
int group, orig_group;
|
||||||
|
|
||||||
|
@ -714,7 +857,7 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||||
const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
|
const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
|
||||||
struct minstrel_rate_stats *mr;
|
struct minstrel_rate_stats *mr;
|
||||||
u8 idx;
|
u8 idx;
|
||||||
u16 flags;
|
u16 flags = group->flags;
|
||||||
|
|
||||||
mr = minstrel_get_ratestats(mi, index);
|
mr = minstrel_get_ratestats(mi, index);
|
||||||
if (!mr->retry_updated)
|
if (!mr->retry_updated)
|
||||||
|
@ -730,13 +873,13 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||||
ratetbl->rate[offset].count_rts = mr->retry_count_rtscts;
|
ratetbl->rate[offset].count_rts = mr->retry_count_rtscts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
|
if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP)
|
||||||
idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];
|
idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];
|
||||||
flags = 0;
|
else if (flags & IEEE80211_TX_RC_VHT_MCS)
|
||||||
} else {
|
idx = ((group->streams - 1) << 4) |
|
||||||
|
((index % MCS_GROUP_RATES) & 0xF);
|
||||||
|
else
|
||||||
idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
|
idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
|
||||||
flags = IEEE80211_TX_RC_MCS | group->flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset > 0) {
|
if (offset > 0) {
|
||||||
ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts;
|
ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts;
|
||||||
|
@ -916,13 +1059,15 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
|
||||||
if (sample_idx / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
|
if (sample_idx / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
|
||||||
int idx = sample_idx % ARRAY_SIZE(mp->cck_rates);
|
int idx = sample_idx % ARRAY_SIZE(mp->cck_rates);
|
||||||
rate->idx = mp->cck_rates[idx];
|
rate->idx = mp->cck_rates[idx];
|
||||||
rate->flags = 0;
|
} else if (sample_group->flags & IEEE80211_TX_RC_VHT_MCS) {
|
||||||
return;
|
ieee80211_rate_set_vht(rate, sample_idx % MCS_GROUP_RATES,
|
||||||
|
sample_group->streams);
|
||||||
|
} else {
|
||||||
|
rate->idx = sample_idx % MCS_GROUP_RATES +
|
||||||
|
(sample_group->streams - 1) * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
rate->idx = sample_idx % MCS_GROUP_RATES +
|
rate->flags = sample_group->flags;
|
||||||
(sample_group->streams - 1) * 8;
|
|
||||||
rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -962,6 +1107,8 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
||||||
struct minstrel_ht_sta *mi = &msp->ht;
|
struct minstrel_ht_sta *mi = &msp->ht;
|
||||||
struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
|
struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
|
||||||
u16 sta_cap = sta->ht_cap.cap;
|
u16 sta_cap = sta->ht_cap.cap;
|
||||||
|
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
|
||||||
|
int use_vht;
|
||||||
int n_supported = 0;
|
int n_supported = 0;
|
||||||
int ack_dur;
|
int ack_dur;
|
||||||
int stbc;
|
int stbc;
|
||||||
|
@ -971,8 +1118,14 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
||||||
if (!sta->ht_cap.ht_supported)
|
if (!sta->ht_cap.ht_supported)
|
||||||
goto use_legacy;
|
goto use_legacy;
|
||||||
|
|
||||||
BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) !=
|
BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_GROUPS_NB);
|
||||||
MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1);
|
|
||||||
|
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||||
|
if (vht_cap->vht_supported)
|
||||||
|
use_vht = vht_cap->vht_mcs.tx_mcs_map != cpu_to_le16(~0);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
use_vht = 0;
|
||||||
|
|
||||||
msp->is_ht = true;
|
msp->is_ht = true;
|
||||||
memset(mi, 0, sizeof(*mi));
|
memset(mi, 0, sizeof(*mi));
|
||||||
|
@ -997,22 +1150,28 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
||||||
}
|
}
|
||||||
mi->sample_tries = 4;
|
mi->sample_tries = 4;
|
||||||
|
|
||||||
stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >>
|
/* TODO tx_flags for vht - ATM the RC API is not fine-grained enough */
|
||||||
IEEE80211_HT_CAP_RX_STBC_SHIFT;
|
if (!use_vht) {
|
||||||
mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
|
stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >>
|
||||||
|
IEEE80211_HT_CAP_RX_STBC_SHIFT;
|
||||||
|
mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
|
||||||
|
|
||||||
if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
|
if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
|
||||||
mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
|
mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
|
for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
|
||||||
|
u32 gflags = minstrel_mcs_groups[i].flags;
|
||||||
|
int bw, nss;
|
||||||
|
|
||||||
mi->groups[i].supported = 0;
|
mi->groups[i].supported = 0;
|
||||||
if (i == MINSTREL_CCK_GROUP) {
|
if (i == MINSTREL_CCK_GROUP) {
|
||||||
minstrel_ht_update_cck(mp, mi, sband, sta);
|
minstrel_ht_update_cck(mp, mi, sband, sta);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) {
|
if (gflags & IEEE80211_TX_RC_SHORT_GI) {
|
||||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
|
if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
|
||||||
if (!(sta_cap & IEEE80211_HT_CAP_SGI_40))
|
if (!(sta_cap & IEEE80211_HT_CAP_SGI_40))
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1021,17 +1180,51 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
|
if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
|
||||||
sta->bandwidth < IEEE80211_STA_RX_BW_40)
|
sta->bandwidth < IEEE80211_STA_RX_BW_40)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
nss = minstrel_mcs_groups[i].streams;
|
||||||
|
|
||||||
/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
|
/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
|
||||||
if (sta->smps_mode == IEEE80211_SMPS_STATIC &&
|
if (sta->smps_mode == IEEE80211_SMPS_STATIC && nss > 1)
|
||||||
minstrel_mcs_groups[i].streams > 1)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
mi->groups[i].supported =
|
/* HT rate */
|
||||||
mcs->rx_mask[minstrel_mcs_groups[i].streams - 1];
|
if (gflags & IEEE80211_TX_RC_MCS) {
|
||||||
|
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||||
|
if (use_vht && minstrel_vht_only)
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
|
mi->groups[i].supported = mcs->rx_mask[nss - 1];
|
||||||
|
if (mi->groups[i].supported)
|
||||||
|
n_supported++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VHT rate */
|
||||||
|
if (!vht_cap->vht_supported ||
|
||||||
|
WARN_ON(!(gflags & IEEE80211_TX_RC_VHT_MCS)) ||
|
||||||
|
WARN_ON(gflags & IEEE80211_TX_RC_160_MHZ_WIDTH))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) {
|
||||||
|
if (sta->bandwidth < IEEE80211_STA_RX_BW_80 ||
|
||||||
|
((gflags & IEEE80211_TX_RC_SHORT_GI) &&
|
||||||
|
!(vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||||
|
bw = BW_40;
|
||||||
|
else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
|
||||||
|
bw = BW_80;
|
||||||
|
else
|
||||||
|
bw = BW_20;
|
||||||
|
|
||||||
|
mi->groups[i].supported = minstrel_get_valid_vht_rates(bw, nss,
|
||||||
|
vht_cap->vht_mcs.tx_mcs_map);
|
||||||
|
|
||||||
if (mi->groups[i].supported)
|
if (mi->groups[i].supported)
|
||||||
n_supported++;
|
n_supported++;
|
||||||
|
|
|
@ -13,10 +13,32 @@
|
||||||
* The number of streams can be changed to 2 to reduce code
|
* The number of streams can be changed to 2 to reduce code
|
||||||
* size and memory footprint.
|
* size and memory footprint.
|
||||||
*/
|
*/
|
||||||
#define MINSTREL_MAX_STREAMS 3
|
#define MINSTREL_MAX_STREAMS 3
|
||||||
#define MINSTREL_STREAM_GROUPS 4
|
#define MINSTREL_HT_STREAM_GROUPS 4 /* BW(=2) * SGI(=2) */
|
||||||
|
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||||
|
#define MINSTREL_VHT_STREAM_GROUPS 6 /* BW(=3) * SGI(=2) */
|
||||||
|
#else
|
||||||
|
#define MINSTREL_VHT_STREAM_GROUPS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MCS_GROUP_RATES 8
|
#define MINSTREL_HT_GROUPS_NB (MINSTREL_MAX_STREAMS * \
|
||||||
|
MINSTREL_HT_STREAM_GROUPS)
|
||||||
|
#define MINSTREL_VHT_GROUPS_NB (MINSTREL_MAX_STREAMS * \
|
||||||
|
MINSTREL_VHT_STREAM_GROUPS)
|
||||||
|
#define MINSTREL_CCK_GROUPS_NB 1
|
||||||
|
#define MINSTREL_GROUPS_NB (MINSTREL_HT_GROUPS_NB + \
|
||||||
|
MINSTREL_VHT_GROUPS_NB + \
|
||||||
|
MINSTREL_CCK_GROUPS_NB)
|
||||||
|
|
||||||
|
#define MINSTREL_HT_GROUP_0 0
|
||||||
|
#define MINSTREL_CCK_GROUP (MINSTREL_HT_GROUP_0 + MINSTREL_HT_GROUPS_NB)
|
||||||
|
#define MINSTREL_VHT_GROUP_0 (MINSTREL_CCK_GROUP + 1)
|
||||||
|
|
||||||
|
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||||
|
#define MCS_GROUP_RATES 10
|
||||||
|
#else
|
||||||
|
#define MCS_GROUP_RATES 8
|
||||||
|
#endif
|
||||||
|
|
||||||
struct mcs_group {
|
struct mcs_group {
|
||||||
u32 flags;
|
u32 flags;
|
||||||
|
@ -31,11 +53,11 @@ struct minstrel_mcs_group_data {
|
||||||
u8 column;
|
u8 column;
|
||||||
|
|
||||||
/* bitfield of supported MCS rates of this group */
|
/* bitfield of supported MCS rates of this group */
|
||||||
u8 supported;
|
u16 supported;
|
||||||
|
|
||||||
/* sorted rate set within a MCS group*/
|
/* sorted rate set within a MCS group*/
|
||||||
u8 max_group_tp_rate[MAX_THR_RATES];
|
u16 max_group_tp_rate[MAX_THR_RATES];
|
||||||
u8 max_group_prob_rate;
|
u16 max_group_prob_rate;
|
||||||
|
|
||||||
/* MCS rate statistics */
|
/* MCS rate statistics */
|
||||||
struct minstrel_rate_stats rates[MCS_GROUP_RATES];
|
struct minstrel_rate_stats rates[MCS_GROUP_RATES];
|
||||||
|
@ -52,8 +74,8 @@ struct minstrel_ht_sta {
|
||||||
unsigned int avg_ampdu_len;
|
unsigned int avg_ampdu_len;
|
||||||
|
|
||||||
/* overall sorted rate set */
|
/* overall sorted rate set */
|
||||||
u8 max_tp_rate[MAX_THR_RATES];
|
u16 max_tp_rate[MAX_THR_RATES];
|
||||||
u8 max_prob_rate;
|
u16 max_prob_rate;
|
||||||
|
|
||||||
/* time of last status update */
|
/* time of last status update */
|
||||||
unsigned long stats_update;
|
unsigned long stats_update;
|
||||||
|
@ -80,7 +102,7 @@ struct minstrel_ht_sta {
|
||||||
u8 cck_supported_short;
|
u8 cck_supported_short;
|
||||||
|
|
||||||
/* MCS rate group info and statistics */
|
/* MCS rate group info and statistics */
|
||||||
struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1];
|
struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct minstrel_ht_sta_priv {
|
struct minstrel_ht_sta_priv {
|
||||||
|
|
|
@ -18,19 +18,23 @@
|
||||||
static char *
|
static char *
|
||||||
minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
|
minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
|
||||||
{
|
{
|
||||||
unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
|
|
||||||
const struct mcs_group *mg;
|
const struct mcs_group *mg;
|
||||||
unsigned int j, tp, prob, eprob;
|
unsigned int j, tp, prob, eprob;
|
||||||
char htmode = '2';
|
char htmode = '2';
|
||||||
char gimode = 'L';
|
char gimode = 'L';
|
||||||
|
u32 gflags;
|
||||||
|
|
||||||
if (!mi->groups[i].supported)
|
if (!mi->groups[i].supported)
|
||||||
return p;
|
return p;
|
||||||
|
|
||||||
mg = &minstrel_mcs_groups[i];
|
mg = &minstrel_mcs_groups[i];
|
||||||
if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
gflags = mg->flags;
|
||||||
|
|
||||||
|
if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||||
htmode = '4';
|
htmode = '4';
|
||||||
if (mg->flags & IEEE80211_TX_RC_SHORT_GI)
|
else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
|
||||||
|
htmode = '8';
|
||||||
|
if (gflags & IEEE80211_TX_RC_SHORT_GI)
|
||||||
gimode = 'S';
|
gimode = 'S';
|
||||||
|
|
||||||
for (j = 0; j < MCS_GROUP_RATES; j++) {
|
for (j = 0; j < MCS_GROUP_RATES; j++) {
|
||||||
|
@ -41,10 +45,12 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
|
||||||
if (!(mi->groups[i].supported & BIT(j)))
|
if (!(mi->groups[i].supported & BIT(j)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (i == max_mcs)
|
if (gflags & IEEE80211_TX_RC_MCS)
|
||||||
p += sprintf(p, "CCK/%cP ", j < 4 ? 'L' : 'S');
|
p += sprintf(p, " HT%c0/%cGI ", htmode, gimode);
|
||||||
|
else if (gflags & IEEE80211_TX_RC_VHT_MCS)
|
||||||
|
p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode);
|
||||||
else
|
else
|
||||||
p += sprintf(p, "HT%c0/%cGI ", htmode, gimode);
|
p += sprintf(p, " CCK/%cP ", j < 4 ? 'L' : 'S');
|
||||||
|
|
||||||
*(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
|
*(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
|
||||||
*(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
|
*(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
|
||||||
|
@ -52,19 +58,22 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
|
||||||
*(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' ';
|
*(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' ';
|
||||||
*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
|
*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
|
||||||
|
|
||||||
if (i == max_mcs) {
|
if (gflags & IEEE80211_TX_RC_MCS) {
|
||||||
int r = bitrates[j % 4];
|
p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j);
|
||||||
p += sprintf(p, " %2u.%1uM", r / 10, r % 10);
|
} else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
|
||||||
|
p += sprintf(p, " MCS%-1u/%1u", j, mg->streams);
|
||||||
} else {
|
} else {
|
||||||
p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j);
|
int r = bitrates[j % 4];
|
||||||
|
|
||||||
|
p += sprintf(p, " %2u.%1uM ", r / 10, r % 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
tp = mr->cur_tp / 10;
|
tp = mr->cur_tp / 10;
|
||||||
prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
|
prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
|
||||||
eprob = MINSTREL_TRUNC(mr->probability * 1000);
|
eprob = MINSTREL_TRUNC(mr->probability * 1000);
|
||||||
|
|
||||||
p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
|
p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u "
|
||||||
"%3u %3u(%3u) %8llu %8llu\n",
|
"%3u %4u(%4u) %9llu(%9llu)\n",
|
||||||
tp / 10, tp % 10,
|
tp / 10, tp % 10,
|
||||||
eprob / 10, eprob % 10,
|
eprob / 10, eprob % 10,
|
||||||
prob / 10, prob % 10,
|
prob / 10, prob % 10,
|
||||||
|
@ -85,7 +94,6 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
|
||||||
struct minstrel_ht_sta *mi = &msp->ht;
|
struct minstrel_ht_sta *mi = &msp->ht;
|
||||||
struct minstrel_debugfs_info *ms;
|
struct minstrel_debugfs_info *ms;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
|
|
||||||
char *p;
|
char *p;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -96,17 +104,19 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ms = kmalloc(sizeof(*ms) + 8192, GFP_KERNEL);
|
ms = kmalloc(32768, GFP_KERNEL);
|
||||||
if (!ms)
|
if (!ms)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
file->private_data = ms;
|
file->private_data = ms;
|
||||||
p = ms->buf;
|
p = ms->buf;
|
||||||
p += sprintf(p, "type rate throughput ewma prob "
|
p += sprintf(p, " type rate tpt eprob *prob "
|
||||||
"this prob retry this succ/attempt success attempts\n");
|
"ret *ok(*cum) ok( cum)\n");
|
||||||
|
|
||||||
p = minstrel_ht_stats_dump(mi, max_mcs, p);
|
p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
|
||||||
for (i = 0; i < max_mcs; i++)
|
for (i = 0; i < MINSTREL_CCK_GROUP; i++)
|
||||||
|
p = minstrel_ht_stats_dump(mi, i, p);
|
||||||
|
for (i++; i < ARRAY_SIZE(mi->groups); i++)
|
||||||
p = minstrel_ht_stats_dump(mi, i, p);
|
p = minstrel_ht_stats_dump(mi, i, p);
|
||||||
|
|
||||||
p += sprintf(p, "\nTotal packet count:: ideal %d "
|
p += sprintf(p, "\nTotal packet count:: ideal %d "
|
||||||
|
@ -118,6 +128,8 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
|
||||||
MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
|
MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
|
||||||
ms->len = p - ms->buf;
|
ms->len = p - ms->buf;
|
||||||
|
|
||||||
|
WARN_ON(ms->len + sizeof(*ms) > 32768);
|
||||||
|
|
||||||
return nonseekable_open(inode, file);
|
return nonseekable_open(inode, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1032,6 +1032,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
|
||||||
ieee80211_is_pspoll(hdr->frame_control)) &&
|
ieee80211_is_pspoll(hdr->frame_control)) &&
|
||||||
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||||
rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
|
rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
|
||||||
|
rx->sdata->vif.type != NL80211_IFTYPE_OCB &&
|
||||||
(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) {
|
(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) {
|
||||||
/*
|
/*
|
||||||
* accept port control frames from the AP even when it's not
|
* accept port control frames from the AP even when it's not
|
||||||
|
@ -1272,6 +1273,12 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
|
||||||
sta->last_rx_rate_vht_nss = status->vht_nss;
|
sta->last_rx_rate_vht_nss = status->vht_nss;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
|
||||||
|
u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
|
||||||
|
NL80211_IFTYPE_OCB);
|
||||||
|
/* OCB uses wild-card BSSID */
|
||||||
|
if (is_broadcast_ether_addr(bssid))
|
||||||
|
sta->last_rx = jiffies;
|
||||||
} else if (!is_multicast_ether_addr(hdr->addr1)) {
|
} else if (!is_multicast_ether_addr(hdr->addr1)) {
|
||||||
/*
|
/*
|
||||||
* Mesh beacons will update last_rx when if they are found to
|
* Mesh beacons will update last_rx when if they are found to
|
||||||
|
@ -2820,6 +2827,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
|
||||||
|
|
||||||
if (!ieee80211_vif_is_mesh(&sdata->vif) &&
|
if (!ieee80211_vif_is_mesh(&sdata->vif) &&
|
||||||
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||||
|
sdata->vif.type != NL80211_IFTYPE_OCB &&
|
||||||
sdata->vif.type != NL80211_IFTYPE_STATION)
|
sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||||
return RX_DROP_MONITOR;
|
return RX_DROP_MONITOR;
|
||||||
|
|
||||||
|
@ -3130,6 +3138,33 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
|
||||||
BIT(rate_idx));
|
BIT(rate_idx));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
if (!bssid)
|
||||||
|
return false;
|
||||||
|
if (ieee80211_is_beacon(hdr->frame_control)) {
|
||||||
|
return false;
|
||||||
|
} else if (!is_broadcast_ether_addr(bssid)) {
|
||||||
|
ocb_dbg(sdata, "BSSID mismatch in OCB mode!\n");
|
||||||
|
return false;
|
||||||
|
} else if (!multicast &&
|
||||||
|
!ether_addr_equal(sdata->dev->dev_addr,
|
||||||
|
hdr->addr1)) {
|
||||||
|
/* if we are in promisc mode we also accept
|
||||||
|
* packets not destined for us
|
||||||
|
*/
|
||||||
|
if (!(sdata->dev->flags & IFF_PROMISC))
|
||||||
|
return false;
|
||||||
|
rx->flags &= ~IEEE80211_RX_RA_MATCH;
|
||||||
|
} else if (!rx->sta) {
|
||||||
|
int rate_idx;
|
||||||
|
if (status->flag & RX_FLAG_HT)
|
||||||
|
rate_idx = 0; /* TODO: HT rates */
|
||||||
|
else
|
||||||
|
rate_idx = status->rate_idx;
|
||||||
|
ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2,
|
||||||
|
BIT(rate_idx));
|
||||||
|
}
|
||||||
|
break;
|
||||||
case NL80211_IFTYPE_MESH_POINT:
|
case NL80211_IFTYPE_MESH_POINT:
|
||||||
if (!multicast &&
|
if (!multicast &&
|
||||||
!ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
|
!ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
|
||||||
|
|
|
@ -501,7 +501,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
|
||||||
/* make the station visible */
|
/* make the station visible */
|
||||||
sta_info_hash_add(local, sta);
|
sta_info_hash_add(local, sta);
|
||||||
|
|
||||||
list_add_rcu(&sta->list, &local->sta_list);
|
list_add_tail_rcu(&sta->list, &local->sta_list);
|
||||||
|
|
||||||
/* notify driver */
|
/* notify driver */
|
||||||
err = sta_info_insert_drv_state(local, sdata, sta);
|
err = sta_info_insert_drv_state(local, sdata, sta);
|
||||||
|
@ -1531,7 +1531,7 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta)
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
/* XXX: what is a good value? */
|
/* XXX: what is a good value? */
|
||||||
n_frames = 8;
|
n_frames = 128;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -336,6 +336,7 @@ struct ieee80211_tx_latency_stat {
|
||||||
* @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
|
* @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
|
||||||
* AP only.
|
* AP only.
|
||||||
* @cipher_scheme: optional cipher scheme for this station
|
* @cipher_scheme: optional cipher scheme for this station
|
||||||
|
* @last_tdls_pkt_time: holds the time in jiffies of last TDLS pkt ACKed
|
||||||
*/
|
*/
|
||||||
struct sta_info {
|
struct sta_info {
|
||||||
/* General information, mostly static */
|
/* General information, mostly static */
|
||||||
|
|
|
@ -704,7 +704,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||||
|
|
||||||
if ((sta->sdata->vif.type == NL80211_IFTYPE_STATION) &&
|
if ((sta->sdata->vif.type == NL80211_IFTYPE_STATION) &&
|
||||||
(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS))
|
(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS))
|
||||||
ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data, acked);
|
ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
|
||||||
|
acked, info->status.tx_time);
|
||||||
|
|
||||||
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
|
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
|
||||||
if (info->flags & IEEE80211_TX_STAT_ACK) {
|
if (info->flags & IEEE80211_TX_STAT_ACK) {
|
||||||
|
|
|
@ -562,8 +562,10 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||||
/* infer the initiator if we can, to support old userspace */
|
/* infer the initiator if we can, to support old userspace */
|
||||||
switch (action_code) {
|
switch (action_code) {
|
||||||
case WLAN_TDLS_SETUP_REQUEST:
|
case WLAN_TDLS_SETUP_REQUEST:
|
||||||
if (sta)
|
if (sta) {
|
||||||
set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
|
set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
|
||||||
|
sta->sta.tdls_initiator = false;
|
||||||
|
}
|
||||||
/* fall-through */
|
/* fall-through */
|
||||||
case WLAN_TDLS_SETUP_CONFIRM:
|
case WLAN_TDLS_SETUP_CONFIRM:
|
||||||
case WLAN_TDLS_DISCOVERY_REQUEST:
|
case WLAN_TDLS_DISCOVERY_REQUEST:
|
||||||
|
@ -575,8 +577,10 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||||
* Make the last packet sent take effect for the initiator
|
* Make the last packet sent take effect for the initiator
|
||||||
* value.
|
* value.
|
||||||
*/
|
*/
|
||||||
if (sta)
|
if (sta) {
|
||||||
clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
|
clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
|
||||||
|
sta->sta.tdls_initiator = true;
|
||||||
|
}
|
||||||
/* fall-through */
|
/* fall-through */
|
||||||
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
||||||
initiator = false;
|
initiator = false;
|
||||||
|
|
|
@ -987,29 +987,34 @@ TRACE_EVENT(drv_flush,
|
||||||
|
|
||||||
TRACE_EVENT(drv_channel_switch,
|
TRACE_EVENT(drv_channel_switch,
|
||||||
TP_PROTO(struct ieee80211_local *local,
|
TP_PROTO(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_channel_switch *ch_switch),
|
struct ieee80211_channel_switch *ch_switch),
|
||||||
|
|
||||||
TP_ARGS(local, ch_switch),
|
TP_ARGS(local, sdata, ch_switch),
|
||||||
|
|
||||||
TP_STRUCT__entry(
|
TP_STRUCT__entry(
|
||||||
LOCAL_ENTRY
|
LOCAL_ENTRY
|
||||||
|
VIF_ENTRY
|
||||||
CHANDEF_ENTRY
|
CHANDEF_ENTRY
|
||||||
__field(u64, timestamp)
|
__field(u64, timestamp)
|
||||||
|
__field(u32, device_timestamp)
|
||||||
__field(bool, block_tx)
|
__field(bool, block_tx)
|
||||||
__field(u8, count)
|
__field(u8, count)
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
LOCAL_ASSIGN;
|
LOCAL_ASSIGN;
|
||||||
|
VIF_ASSIGN;
|
||||||
CHANDEF_ASSIGN(&ch_switch->chandef)
|
CHANDEF_ASSIGN(&ch_switch->chandef)
|
||||||
__entry->timestamp = ch_switch->timestamp;
|
__entry->timestamp = ch_switch->timestamp;
|
||||||
|
__entry->device_timestamp = ch_switch->device_timestamp;
|
||||||
__entry->block_tx = ch_switch->block_tx;
|
__entry->block_tx = ch_switch->block_tx;
|
||||||
__entry->count = ch_switch->count;
|
__entry->count = ch_switch->count;
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk(
|
TP_printk(
|
||||||
LOCAL_PR_FMT " new " CHANDEF_PR_FMT " count:%d",
|
LOCAL_PR_FMT VIF_PR_FMT " new " CHANDEF_PR_FMT " count:%d",
|
||||||
LOCAL_PR_ARG, CHANDEF_PR_ARG, __entry->count
|
LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1557,9 +1562,26 @@ DEFINE_EVENT(local_sdata_evt, drv_stop_ap,
|
||||||
TP_ARGS(local, sdata)
|
TP_ARGS(local, sdata)
|
||||||
);
|
);
|
||||||
|
|
||||||
DEFINE_EVENT(local_only_evt, drv_restart_complete,
|
TRACE_EVENT(drv_reconfig_complete,
|
||||||
TP_PROTO(struct ieee80211_local *local),
|
TP_PROTO(struct ieee80211_local *local,
|
||||||
TP_ARGS(local)
|
enum ieee80211_reconfig_type reconfig_type),
|
||||||
|
TP_ARGS(local, reconfig_type),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
LOCAL_ENTRY
|
||||||
|
__field(u8, reconfig_type)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
LOCAL_ASSIGN;
|
||||||
|
__entry->reconfig_type = reconfig_type;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(
|
||||||
|
LOCAL_PR_FMT " reconfig_type:%d",
|
||||||
|
LOCAL_PR_ARG, __entry->reconfig_type
|
||||||
|
)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
|
@ -2106,6 +2128,72 @@ TRACE_EVENT(drv_channel_switch_beacon,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(drv_pre_channel_switch,
|
||||||
|
TP_PROTO(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee80211_channel_switch *ch_switch),
|
||||||
|
|
||||||
|
TP_ARGS(local, sdata, ch_switch),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
LOCAL_ENTRY
|
||||||
|
VIF_ENTRY
|
||||||
|
CHANDEF_ENTRY
|
||||||
|
__field(u64, timestamp)
|
||||||
|
__field(bool, block_tx)
|
||||||
|
__field(u8, count)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
LOCAL_ASSIGN;
|
||||||
|
VIF_ASSIGN;
|
||||||
|
CHANDEF_ASSIGN(&ch_switch->chandef)
|
||||||
|
__entry->timestamp = ch_switch->timestamp;
|
||||||
|
__entry->block_tx = ch_switch->block_tx;
|
||||||
|
__entry->count = ch_switch->count;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(
|
||||||
|
LOCAL_PR_FMT VIF_PR_FMT " prepare channel switch to "
|
||||||
|
CHANDEF_PR_FMT " count:%d block_tx:%d timestamp:%llu",
|
||||||
|
LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count,
|
||||||
|
__entry->block_tx, __entry->timestamp
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(local_sdata_evt, drv_post_channel_switch,
|
||||||
|
TP_PROTO(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_sub_if_data *sdata),
|
||||||
|
TP_ARGS(local, sdata)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(drv_get_txpower,
|
||||||
|
TP_PROTO(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
int dbm, int ret),
|
||||||
|
|
||||||
|
TP_ARGS(local, sdata, dbm, ret),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
LOCAL_ENTRY
|
||||||
|
VIF_ENTRY
|
||||||
|
__field(int, dbm)
|
||||||
|
__field(int, ret)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
LOCAL_ASSIGN;
|
||||||
|
VIF_ASSIGN;
|
||||||
|
__entry->dbm = dbm;
|
||||||
|
__entry->ret = ret;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(
|
||||||
|
LOCAL_PR_FMT VIF_PR_FMT " dbm:%d ret:%d",
|
||||||
|
LOCAL_PR_ARG, VIF_PR_ARG, __entry->dbm, __entry->ret
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
|
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
|
||||||
#undef TRACE_SYSTEM
|
#undef TRACE_SYSTEM
|
||||||
|
|
|
@ -296,6 +296,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
|
||||||
*/
|
*/
|
||||||
return TX_DROP;
|
return TX_DROP;
|
||||||
|
|
||||||
|
if (tx->sdata->vif.type == NL80211_IFTYPE_OCB)
|
||||||
|
return TX_CONTINUE;
|
||||||
|
|
||||||
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
|
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
|
||||||
return TX_CONTINUE;
|
return TX_CONTINUE;
|
||||||
|
|
||||||
|
@ -2013,6 +2016,17 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||||
goto fail_rcu;
|
goto fail_rcu;
|
||||||
band = chanctx_conf->def.chan->band;
|
band = chanctx_conf->def.chan->band;
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
/* DA SA BSSID */
|
||||||
|
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
||||||
|
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
|
||||||
|
eth_broadcast_addr(hdr.addr3);
|
||||||
|
hdrlen = 24;
|
||||||
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||||
|
if (!chanctx_conf)
|
||||||
|
goto fail_rcu;
|
||||||
|
band = chanctx_conf->def.chan->band;
|
||||||
|
break;
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
/* DA SA BSSID */
|
/* DA SA BSSID */
|
||||||
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
||||||
|
@ -2057,6 +2071,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||||
* EAPOL frames from the local station.
|
* EAPOL frames from the local station.
|
||||||
*/
|
*/
|
||||||
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
|
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
|
||||||
|
(sdata->vif.type != NL80211_IFTYPE_OCB) &&
|
||||||
!multicast && !authorized &&
|
!multicast && !authorized &&
|
||||||
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
|
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
|
||||||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
|
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
|
||||||
|
|
|
@ -693,6 +693,34 @@ void ieee80211_iterate_active_interfaces_rtnl(
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
|
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
|
||||||
|
|
||||||
|
static void __iterate_stations(struct ieee80211_local *local,
|
||||||
|
void (*iterator)(void *data,
|
||||||
|
struct ieee80211_sta *sta),
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||||
|
if (!sta->uploaded)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
iterator(data, &sta->sta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
|
||||||
|
void (*iterator)(void *data,
|
||||||
|
struct ieee80211_sta *sta),
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = hw_to_local(hw);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
__iterate_stations(local, iterator, data);
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic);
|
||||||
|
|
||||||
struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
|
struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
|
||||||
{
|
{
|
||||||
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
||||||
|
@ -1073,6 +1101,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||||
int ac;
|
int ac;
|
||||||
bool use_11b, enable_qos;
|
bool use_11b, enable_qos;
|
||||||
|
bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */
|
||||||
int aCWmin, aCWmax;
|
int aCWmin, aCWmax;
|
||||||
|
|
||||||
if (!local->ops->conf_tx)
|
if (!local->ops->conf_tx)
|
||||||
|
@ -1097,6 +1126,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||||
*/
|
*/
|
||||||
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
|
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
|
||||||
|
|
||||||
|
is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);
|
||||||
|
|
||||||
/* Set defaults according to 802.11-2007 Table 7-37 */
|
/* Set defaults according to 802.11-2007 Table 7-37 */
|
||||||
aCWmax = 1023;
|
aCWmax = 1023;
|
||||||
if (use_11b)
|
if (use_11b)
|
||||||
|
@ -1118,7 +1149,10 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||||
qparam.cw_max = aCWmax;
|
qparam.cw_max = aCWmax;
|
||||||
qparam.cw_min = aCWmin;
|
qparam.cw_min = aCWmin;
|
||||||
qparam.txop = 0;
|
qparam.txop = 0;
|
||||||
qparam.aifs = 7;
|
if (is_ocb)
|
||||||
|
qparam.aifs = 9;
|
||||||
|
else
|
||||||
|
qparam.aifs = 7;
|
||||||
break;
|
break;
|
||||||
/* never happens but let's not leave undefined */
|
/* never happens but let's not leave undefined */
|
||||||
default:
|
default:
|
||||||
|
@ -1126,21 +1160,32 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||||
qparam.cw_max = aCWmax;
|
qparam.cw_max = aCWmax;
|
||||||
qparam.cw_min = aCWmin;
|
qparam.cw_min = aCWmin;
|
||||||
qparam.txop = 0;
|
qparam.txop = 0;
|
||||||
qparam.aifs = 3;
|
if (is_ocb)
|
||||||
|
qparam.aifs = 6;
|
||||||
|
else
|
||||||
|
qparam.aifs = 3;
|
||||||
break;
|
break;
|
||||||
case IEEE80211_AC_VI:
|
case IEEE80211_AC_VI:
|
||||||
qparam.cw_max = aCWmin;
|
qparam.cw_max = aCWmin;
|
||||||
qparam.cw_min = (aCWmin + 1) / 2 - 1;
|
qparam.cw_min = (aCWmin + 1) / 2 - 1;
|
||||||
if (use_11b)
|
if (is_ocb)
|
||||||
|
qparam.txop = 0;
|
||||||
|
else if (use_11b)
|
||||||
qparam.txop = 6016/32;
|
qparam.txop = 6016/32;
|
||||||
else
|
else
|
||||||
qparam.txop = 3008/32;
|
qparam.txop = 3008/32;
|
||||||
qparam.aifs = 2;
|
|
||||||
|
if (is_ocb)
|
||||||
|
qparam.aifs = 3;
|
||||||
|
else
|
||||||
|
qparam.aifs = 2;
|
||||||
break;
|
break;
|
||||||
case IEEE80211_AC_VO:
|
case IEEE80211_AC_VO:
|
||||||
qparam.cw_max = (aCWmin + 1) / 2 - 1;
|
qparam.cw_max = (aCWmin + 1) / 2 - 1;
|
||||||
qparam.cw_min = (aCWmin + 1) / 4 - 1;
|
qparam.cw_min = (aCWmin + 1) / 4 - 1;
|
||||||
if (use_11b)
|
if (is_ocb)
|
||||||
|
qparam.txop = 0;
|
||||||
|
else if (use_11b)
|
||||||
qparam.txop = 3264/32;
|
qparam.txop = 3264/32;
|
||||||
else
|
else
|
||||||
qparam.txop = 1504/32;
|
qparam.txop = 1504/32;
|
||||||
|
@ -1813,6 +1858,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||||
ieee80211_bss_info_change_notify(sdata, changed);
|
ieee80211_bss_info_change_notify(sdata, changed);
|
||||||
sdata_unlock(sdata);
|
sdata_unlock(sdata);
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
changed |= BSS_CHANGED_OCB;
|
||||||
|
ieee80211_bss_info_change_notify(sdata, changed);
|
||||||
|
break;
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
changed |= BSS_CHANGED_IBSS;
|
changed |= BSS_CHANGED_IBSS;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
@ -1949,7 +1998,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||||
* We may want to change that later, however.
|
* We may want to change that later, however.
|
||||||
*/
|
*/
|
||||||
if (!local->suspended || reconfig_due_to_wowlan)
|
if (!local->suspended || reconfig_due_to_wowlan)
|
||||||
drv_restart_complete(local);
|
drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
|
||||||
|
|
||||||
if (!local->suspended)
|
if (!local->suspended)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1960,6 +2009,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||||
mb();
|
mb();
|
||||||
local->resuming = false;
|
local->resuming = false;
|
||||||
|
|
||||||
|
if (!reconfig_due_to_wowlan)
|
||||||
|
drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
|
||||||
|
|
||||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||||
if (!ieee80211_sdata_running(sdata))
|
if (!ieee80211_sdata_running(sdata))
|
||||||
continue;
|
continue;
|
||||||
|
@ -2052,42 +2104,36 @@ static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
|
||||||
* ieee80211_ie_split - split an IE buffer according to ordering
|
const u8 *ids, int n_ids,
|
||||||
*
|
const u8 *after_ric, int n_after_ric,
|
||||||
* @ies: the IE buffer
|
size_t offset)
|
||||||
* @ielen: the length of the IE buffer
|
|
||||||
* @ids: an array with element IDs that are allowed before
|
|
||||||
* the split
|
|
||||||
* @n_ids: the size of the element ID array
|
|
||||||
* @offset: offset where to start splitting in the buffer
|
|
||||||
*
|
|
||||||
* This function splits an IE buffer by updating the @offset
|
|
||||||
* variable to point to the location where the buffer should be
|
|
||||||
* split.
|
|
||||||
*
|
|
||||||
* It assumes that the given IE buffer is well-formed, this
|
|
||||||
* has to be guaranteed by the caller!
|
|
||||||
*
|
|
||||||
* It also assumes that the IEs in the buffer are ordered
|
|
||||||
* correctly, if not the result of using this function will not
|
|
||||||
* be ordered correctly either, i.e. it does no reordering.
|
|
||||||
*
|
|
||||||
* The function returns the offset where the next part of the
|
|
||||||
* buffer starts, which may be @ielen if the entire (remainder)
|
|
||||||
* of the buffer should be used.
|
|
||||||
*/
|
|
||||||
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
|
|
||||||
const u8 *ids, int n_ids, size_t offset)
|
|
||||||
{
|
{
|
||||||
size_t pos = offset;
|
size_t pos = offset;
|
||||||
|
|
||||||
while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos]))
|
while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
|
||||||
pos += 2 + ies[pos + 1];
|
if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
|
||||||
|
pos += 2 + ies[pos + 1];
|
||||||
|
|
||||||
|
while (pos < ielen &&
|
||||||
|
!ieee80211_id_in_list(after_ric, n_after_ric,
|
||||||
|
ies[pos]))
|
||||||
|
pos += 2 + ies[pos + 1];
|
||||||
|
} else {
|
||||||
|
pos += 2 + ies[pos + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
|
||||||
|
const u8 *ids, int n_ids, size_t offset)
|
||||||
|
{
|
||||||
|
return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(ieee80211_ie_split);
|
||||||
|
|
||||||
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
|
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
|
||||||
{
|
{
|
||||||
size_t pos = offset;
|
size_t pos = offset;
|
||||||
|
@ -2526,11 +2572,23 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work)
|
||||||
struct ieee80211_local *local =
|
struct ieee80211_local *local =
|
||||||
container_of(work, struct ieee80211_local, radar_detected_work);
|
container_of(work, struct ieee80211_local, radar_detected_work);
|
||||||
struct cfg80211_chan_def chandef = local->hw.conf.chandef;
|
struct cfg80211_chan_def chandef = local->hw.conf.chandef;
|
||||||
|
struct ieee80211_chanctx *ctx;
|
||||||
|
int num_chanctx = 0;
|
||||||
|
|
||||||
|
mutex_lock(&local->chanctx_mtx);
|
||||||
|
list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||||
|
if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
num_chanctx++;
|
||||||
|
chandef = ctx->conf.def;
|
||||||
|
}
|
||||||
|
mutex_unlock(&local->chanctx_mtx);
|
||||||
|
|
||||||
ieee80211_dfs_cac_cancel(local);
|
ieee80211_dfs_cac_cancel(local);
|
||||||
|
|
||||||
if (local->use_chanctx)
|
if (num_chanctx > 1)
|
||||||
/* currently not handled */
|
/* XXX: multi-channel is not supported yet */
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
else
|
else
|
||||||
cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
|
cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
|
||||||
|
|
|
@ -111,8 +111,6 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local,
|
||||||
(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
|
(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
|
||||||
return newhdr + hdrlen;
|
return newhdr + hdrlen;
|
||||||
|
|
||||||
skb_set_network_header(skb, skb_network_offset(skb) +
|
|
||||||
IEEE80211_WEP_IV_LEN);
|
|
||||||
ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen);
|
ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen);
|
||||||
return newhdr + hdrlen;
|
return newhdr + hdrlen;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,10 +54,18 @@ static int wme_downgrade_ac(struct sk_buff *skb)
|
||||||
}
|
}
|
||||||
|
|
||||||
static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
|
static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
|
||||||
struct sk_buff *skb)
|
struct sta_info *sta, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||||
|
|
||||||
/* in case we are a client verify acm is not set for this ac */
|
/* in case we are a client verify acm is not set for this ac */
|
||||||
while (unlikely(sdata->wmm_acm & BIT(skb->priority))) {
|
while (sdata->wmm_acm & BIT(skb->priority)) {
|
||||||
|
int ac = ieee802_1d_to_ac[skb->priority];
|
||||||
|
|
||||||
|
if (ifmgd->tx_tspec[ac].admitted_time &&
|
||||||
|
skb->priority == ifmgd->tx_tspec[ac].up)
|
||||||
|
return ac;
|
||||||
|
|
||||||
if (wme_downgrade_ac(skb)) {
|
if (wme_downgrade_ac(skb)) {
|
||||||
/*
|
/*
|
||||||
* This should not really happen. The AP has marked all
|
* This should not really happen. The AP has marked all
|
||||||
|
@ -96,7 +104,7 @@ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
|
||||||
p = ieee80211_get_qos_ctl(hdr);
|
p = ieee80211_get_qos_ctl(hdr);
|
||||||
skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK;
|
skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK;
|
||||||
|
|
||||||
return ieee80211_downgrade_queue(sdata, skb);
|
return ieee80211_downgrade_queue(sdata, NULL, skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Indicate which queue to use. */
|
/* Indicate which queue to use. */
|
||||||
|
@ -108,6 +116,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||||
const u8 *ra = NULL;
|
const u8 *ra = NULL;
|
||||||
bool qos = false;
|
bool qos = false;
|
||||||
struct mac80211_qos_map *qos_map;
|
struct mac80211_qos_map *qos_map;
|
||||||
|
u16 ret;
|
||||||
|
|
||||||
if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {
|
if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {
|
||||||
skb->priority = 0; /* required for correct WPA/11i MIC */
|
skb->priority = 0; /* required for correct WPA/11i MIC */
|
||||||
|
@ -139,6 +148,10 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
ra = skb->data;
|
ra = skb->data;
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
/* all stations are required to support WME */
|
||||||
|
qos = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -148,27 +161,29 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||||
if (sta)
|
if (sta)
|
||||||
qos = sta->sta.wme;
|
qos = sta->sta.wme;
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
if (!qos) {
|
if (!qos) {
|
||||||
skb->priority = 0; /* required for correct WPA/11i MIC */
|
skb->priority = 0; /* required for correct WPA/11i MIC */
|
||||||
return IEEE80211_AC_BE;
|
ret = IEEE80211_AC_BE;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skb->protocol == sdata->control_port_protocol) {
|
if (skb->protocol == sdata->control_port_protocol) {
|
||||||
skb->priority = 7;
|
skb->priority = 7;
|
||||||
return ieee80211_downgrade_queue(sdata, skb);
|
goto downgrade;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* use the data classifier to determine what 802.1d tag the
|
/* use the data classifier to determine what 802.1d tag the
|
||||||
* data frame has */
|
* data frame has */
|
||||||
rcu_read_lock();
|
|
||||||
qos_map = rcu_dereference(sdata->qos_map);
|
qos_map = rcu_dereference(sdata->qos_map);
|
||||||
skb->priority = cfg80211_classify8021d(skb, qos_map ?
|
skb->priority = cfg80211_classify8021d(skb, qos_map ?
|
||||||
&qos_map->qos_map : NULL);
|
&qos_map->qos_map : NULL);
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
return ieee80211_downgrade_queue(sdata, skb);
|
downgrade:
|
||||||
|
ret = ieee80211_downgrade_queue(sdata, sta, skb);
|
||||||
|
out:
|
||||||
|
rcu_read_unlock();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include "ieee80211_i.h"
|
#include "ieee80211_i.h"
|
||||||
|
|
||||||
extern const int ieee802_1d_to_ac[8];
|
|
||||||
|
|
||||||
u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
|
u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
|
||||||
struct sk_buff *skb,
|
struct sk_buff *skb,
|
||||||
struct ieee80211_hdr *hdr);
|
struct ieee80211_hdr *hdr);
|
||||||
|
|
|
@ -209,8 +209,6 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
|
||||||
|
|
||||||
pos = skb_push(skb, IEEE80211_TKIP_IV_LEN);
|
pos = skb_push(skb, IEEE80211_TKIP_IV_LEN);
|
||||||
memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen);
|
memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen);
|
||||||
skb_set_network_header(skb, skb_network_offset(skb) +
|
|
||||||
IEEE80211_TKIP_IV_LEN);
|
|
||||||
pos += hdrlen;
|
pos += hdrlen;
|
||||||
|
|
||||||
/* the HW only needs room for the IV, but not the actual IV */
|
/* the HW only needs room for the IV, but not the actual IV */
|
||||||
|
@ -434,8 +432,6 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
|
||||||
|
|
||||||
pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN);
|
pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN);
|
||||||
memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen);
|
memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen);
|
||||||
skb_set_network_header(skb, skb_network_offset(skb) +
|
|
||||||
IEEE80211_CCMP_HDR_LEN);
|
|
||||||
|
|
||||||
/* the HW only needs room for the IV, but not the actual IV */
|
/* the HW only needs room for the IV, but not the actual IV */
|
||||||
if (info->control.hw_key &&
|
if (info->control.hw_key &&
|
||||||
|
@ -575,7 +571,6 @@ ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
|
||||||
|
|
||||||
pos = skb_push(skb, cs->hdr_len);
|
pos = skb_push(skb, cs->hdr_len);
|
||||||
memmove(pos, pos + cs->hdr_len, hdrlen);
|
memmove(pos, pos + cs->hdr_len, hdrlen);
|
||||||
skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len);
|
|
||||||
|
|
||||||
return TX_CONTINUE;
|
return TX_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ obj-$(CONFIG_WEXT_SPY) += wext-spy.o
|
||||||
obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
|
obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
|
||||||
|
|
||||||
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
|
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
|
||||||
cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o
|
cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
|
||||||
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
|
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
|
||||||
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
|
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
|
||||||
cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
|
cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
|
||||||
|
|
|
@ -115,7 +115,7 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
|
||||||
EXPORT_SYMBOL(cfg80211_chandef_valid);
|
EXPORT_SYMBOL(cfg80211_chandef_valid);
|
||||||
|
|
||||||
static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
|
static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
|
||||||
int *pri40, int *pri80)
|
u32 *pri40, u32 *pri80)
|
||||||
{
|
{
|
||||||
int tmp;
|
int tmp;
|
||||||
|
|
||||||
|
@ -366,6 +366,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case NL80211_IFTYPE_STATION:
|
case NL80211_IFTYPE_STATION:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
case NL80211_IFTYPE_P2P_CLIENT:
|
case NL80211_IFTYPE_P2P_CLIENT:
|
||||||
case NL80211_IFTYPE_MONITOR:
|
case NL80211_IFTYPE_MONITOR:
|
||||||
case NL80211_IFTYPE_AP_VLAN:
|
case NL80211_IFTYPE_AP_VLAN:
|
||||||
|
@ -892,6 +893,13 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
|
||||||
*radar_detect |= BIT(wdev->chandef.width);
|
*radar_detect |= BIT(wdev->chandef.width);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
if (wdev->chandef.chan) {
|
||||||
|
*chan = wdev->chandef.chan;
|
||||||
|
*chanmode = CHAN_MODE_SHARED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case NL80211_IFTYPE_MONITOR:
|
case NL80211_IFTYPE_MONITOR:
|
||||||
case NL80211_IFTYPE_AP_VLAN:
|
case NL80211_IFTYPE_AP_VLAN:
|
||||||
case NL80211_IFTYPE_WDS:
|
case NL80211_IFTYPE_WDS:
|
||||||
|
|
|
@ -86,11 +86,11 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
|
||||||
return &rdev->wiphy;
|
return &rdev->wiphy;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
|
static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev,
|
||||||
char *newname)
|
const char *newname)
|
||||||
{
|
{
|
||||||
struct cfg80211_registered_device *rdev2;
|
struct cfg80211_registered_device *rdev2;
|
||||||
int wiphy_idx, taken = -1, result, digits;
|
int wiphy_idx, taken = -1, digits;
|
||||||
|
|
||||||
ASSERT_RTNL();
|
ASSERT_RTNL();
|
||||||
|
|
||||||
|
@ -109,16 +109,29 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Ignore nop renames */
|
|
||||||
if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Ensure another device does not already have this name. */
|
/* Ensure another device does not already have this name. */
|
||||||
list_for_each_entry(rdev2, &cfg80211_rdev_list, list)
|
list_for_each_entry(rdev2, &cfg80211_rdev_list, list)
|
||||||
if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0)
|
if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
|
||||||
|
char *newname)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
ASSERT_RTNL();
|
||||||
|
|
||||||
|
/* Ignore nop renames */
|
||||||
|
if (strcmp(newname, wiphy_name(&rdev->wiphy)) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
result = cfg80211_dev_check_name(rdev, newname);
|
||||||
|
if (result < 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
result = device_rename(&rdev->wiphy.dev, newname);
|
result = device_rename(&rdev->wiphy.dev, newname);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
@ -309,7 +322,8 @@ static void cfg80211_destroy_iface_wk(struct work_struct *work)
|
||||||
|
|
||||||
/* exported functions */
|
/* exported functions */
|
||||||
|
|
||||||
struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
|
struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
|
||||||
|
const char *requested_name)
|
||||||
{
|
{
|
||||||
static atomic_t wiphy_counter = ATOMIC_INIT(0);
|
static atomic_t wiphy_counter = ATOMIC_INIT(0);
|
||||||
|
|
||||||
|
@ -346,7 +360,31 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
|
||||||
rdev->wiphy_idx--;
|
rdev->wiphy_idx--;
|
||||||
|
|
||||||
/* give it a proper name */
|
/* give it a proper name */
|
||||||
dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
|
if (requested_name && requested_name[0]) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rtnl_lock();
|
||||||
|
rv = cfg80211_dev_check_name(rdev, requested_name);
|
||||||
|
|
||||||
|
if (rv < 0) {
|
||||||
|
rtnl_unlock();
|
||||||
|
goto use_default_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name);
|
||||||
|
rtnl_unlock();
|
||||||
|
if (rv)
|
||||||
|
goto use_default_name;
|
||||||
|
} else {
|
||||||
|
use_default_name:
|
||||||
|
/* NOTE: This is *probably* safe w/out holding rtnl because of
|
||||||
|
* the restrictions on phy names. Probably this call could
|
||||||
|
* fail if some other part of the kernel (re)named a device
|
||||||
|
* phyX. But, might should add some locking and check return
|
||||||
|
* value, and use a different name if this one exists?
|
||||||
|
*/
|
||||||
|
dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
|
||||||
|
}
|
||||||
|
|
||||||
INIT_LIST_HEAD(&rdev->wdev_list);
|
INIT_LIST_HEAD(&rdev->wdev_list);
|
||||||
INIT_LIST_HEAD(&rdev->beacon_registrations);
|
INIT_LIST_HEAD(&rdev->beacon_registrations);
|
||||||
|
@ -406,7 +444,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
|
||||||
|
|
||||||
return &rdev->wiphy;
|
return &rdev->wiphy;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(wiphy_new);
|
EXPORT_SYMBOL(wiphy_new_nm);
|
||||||
|
|
||||||
static int wiphy_verify_combinations(struct wiphy *wiphy)
|
static int wiphy_verify_combinations(struct wiphy *wiphy)
|
||||||
{
|
{
|
||||||
|
@ -831,7 +869,22 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
|
||||||
case NL80211_IFTYPE_P2P_GO:
|
case NL80211_IFTYPE_P2P_GO:
|
||||||
__cfg80211_stop_ap(rdev, dev, true);
|
__cfg80211_stop_ap(rdev, dev, true);
|
||||||
break;
|
break;
|
||||||
default:
|
case NL80211_IFTYPE_OCB:
|
||||||
|
__cfg80211_leave_ocb(rdev, dev);
|
||||||
|
break;
|
||||||
|
case NL80211_IFTYPE_WDS:
|
||||||
|
/* must be handled by mac80211/driver, has no APIs */
|
||||||
|
break;
|
||||||
|
case NL80211_IFTYPE_P2P_DEVICE:
|
||||||
|
/* cannot happen, has no netdev */
|
||||||
|
break;
|
||||||
|
case NL80211_IFTYPE_AP_VLAN:
|
||||||
|
case NL80211_IFTYPE_MONITOR:
|
||||||
|
/* nothing to do */
|
||||||
|
break;
|
||||||
|
case NL80211_IFTYPE_UNSPECIFIED:
|
||||||
|
case NUM_NL80211_IFTYPES:
|
||||||
|
/* invalid */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -290,6 +290,18 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
|
||||||
struct wireless_dev *wdev,
|
struct wireless_dev *wdev,
|
||||||
struct cfg80211_chan_def *chandef);
|
struct cfg80211_chan_def *chandef);
|
||||||
|
|
||||||
|
/* OCB */
|
||||||
|
int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev,
|
||||||
|
struct ocb_setup *setup);
|
||||||
|
int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev,
|
||||||
|
struct ocb_setup *setup);
|
||||||
|
int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev);
|
||||||
|
int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev);
|
||||||
|
|
||||||
/* AP */
|
/* AP */
|
||||||
int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
|
int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
|
||||||
struct net_device *dev, bool notify);
|
struct net_device *dev, bool notify);
|
||||||
|
|
|
@ -884,7 +884,12 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
|
||||||
if (!wdev->current_bss)
|
if (!wdev->current_bss)
|
||||||
return -ENOLINK;
|
return -ENOLINK;
|
||||||
break;
|
break;
|
||||||
default:
|
case NL80211_IFTYPE_UNSPECIFIED:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
case NL80211_IFTYPE_MONITOR:
|
||||||
|
case NL80211_IFTYPE_P2P_DEVICE:
|
||||||
|
case NL80211_IFTYPE_WDS:
|
||||||
|
case NUM_NL80211_IFTYPES:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1514,8 +1519,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||||
if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
|
if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
|
||||||
CMD(channel_switch, CHANNEL_SWITCH);
|
CMD(channel_switch, CHANNEL_SWITCH);
|
||||||
CMD(set_qos_map, SET_QOS_MAP);
|
CMD(set_qos_map, SET_QOS_MAP);
|
||||||
if (rdev->wiphy.flags &
|
if (rdev->wiphy.features &
|
||||||
WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)
|
NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
|
||||||
CMD(add_tx_ts, ADD_TX_TS);
|
CMD(add_tx_ts, ADD_TX_TS);
|
||||||
}
|
}
|
||||||
/* add into the if now */
|
/* add into the if now */
|
||||||
|
@ -2605,7 +2610,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
||||||
!(rdev->wiphy.interface_modes & (1 << type)))
|
!(rdev->wiphy.interface_modes & (1 << type)))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) {
|
if ((type == NL80211_IFTYPE_P2P_DEVICE ||
|
||||||
|
rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
|
||||||
|
info->attrs[NL80211_ATTR_MAC]) {
|
||||||
nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
|
nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
|
||||||
ETH_ALEN);
|
ETH_ALEN);
|
||||||
if (!is_valid_ether_addr(params.macaddr))
|
if (!is_valid_ether_addr(params.macaddr))
|
||||||
|
@ -4398,10 +4405,12 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
|
||||||
{
|
{
|
||||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||||
struct net_device *dev = info->user_ptr[1];
|
struct net_device *dev = info->user_ptr[1];
|
||||||
u8 *mac_addr = NULL;
|
struct station_del_parameters params;
|
||||||
|
|
||||||
|
memset(¶ms, 0, sizeof(params));
|
||||||
|
|
||||||
if (info->attrs[NL80211_ATTR_MAC])
|
if (info->attrs[NL80211_ATTR_MAC])
|
||||||
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
params.mac = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
||||||
|
|
||||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
||||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
|
||||||
|
@ -4412,7 +4421,28 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
|
||||||
if (!rdev->ops->del_station)
|
if (!rdev->ops->del_station)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
return rdev_del_station(rdev, dev, mac_addr);
|
if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) {
|
||||||
|
params.subtype =
|
||||||
|
nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
|
||||||
|
if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 &&
|
||||||
|
params.subtype != IEEE80211_STYPE_DEAUTH >> 4)
|
||||||
|
return -EINVAL;
|
||||||
|
} else {
|
||||||
|
/* Default to Deauthentication frame */
|
||||||
|
params.subtype = IEEE80211_STYPE_DEAUTH >> 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->attrs[NL80211_ATTR_REASON_CODE]) {
|
||||||
|
params.reason_code =
|
||||||
|
nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
|
||||||
|
if (params.reason_code == 0)
|
||||||
|
return -EINVAL; /* 0 is reserved */
|
||||||
|
} else {
|
||||||
|
/* Default to reason code 2 */
|
||||||
|
params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rdev_del_station(rdev, dev, ¶ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
|
static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
|
||||||
|
@ -4624,6 +4654,96 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
|
||||||
return rdev_del_mpath(rdev, dev, dst);
|
return rdev_del_mpath(rdev, dev, dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info)
|
||||||
|
{
|
||||||
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||||
|
int err;
|
||||||
|
struct net_device *dev = info->user_ptr[1];
|
||||||
|
struct mpath_info pinfo;
|
||||||
|
struct sk_buff *msg;
|
||||||
|
u8 *dst = NULL;
|
||||||
|
u8 mpp[ETH_ALEN];
|
||||||
|
|
||||||
|
memset(&pinfo, 0, sizeof(pinfo));
|
||||||
|
|
||||||
|
if (!info->attrs[NL80211_ATTR_MAC])
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
||||||
|
|
||||||
|
if (!rdev->ops->get_mpp)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
err = rdev_get_mpp(rdev, dev, dst, mpp, &pinfo);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0,
|
||||||
|
dev, dst, mpp, &pinfo) < 0) {
|
||||||
|
nlmsg_free(msg);
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return genlmsg_reply(msg, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nl80211_dump_mpp(struct sk_buff *skb,
|
||||||
|
struct netlink_callback *cb)
|
||||||
|
{
|
||||||
|
struct mpath_info pinfo;
|
||||||
|
struct cfg80211_registered_device *rdev;
|
||||||
|
struct wireless_dev *wdev;
|
||||||
|
u8 dst[ETH_ALEN];
|
||||||
|
u8 mpp[ETH_ALEN];
|
||||||
|
int path_idx = cb->args[2];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (!rdev->ops->dump_mpp) {
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
err = rdev_dump_mpp(rdev, wdev->netdev, path_idx, dst,
|
||||||
|
mpp, &pinfo);
|
||||||
|
if (err == -ENOENT)
|
||||||
|
break;
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid,
|
||||||
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||||
|
wdev->netdev, dst, mpp,
|
||||||
|
&pinfo) < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
path_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
cb->args[2] = path_idx;
|
||||||
|
err = skb->len;
|
||||||
|
out_err:
|
||||||
|
nl80211_finish_wdev_dump(rdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
|
static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
|
||||||
{
|
{
|
||||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||||
|
@ -5923,10 +6043,10 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||||
* function is called under RTNL lock, so this should not be a problem.
|
* function is called under RTNL lock, so this should not be a problem.
|
||||||
*/
|
*/
|
||||||
static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
|
static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
|
||||||
u8 radar_detect_width = 0;
|
|
||||||
int err;
|
int err;
|
||||||
bool need_new_beacon = false;
|
bool need_new_beacon = false;
|
||||||
int len, i;
|
int len, i;
|
||||||
|
u32 cs_count;
|
||||||
|
|
||||||
if (!rdev->ops->channel_switch ||
|
if (!rdev->ops->channel_switch ||
|
||||||
!(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
|
!(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
|
||||||
|
@ -5963,7 +6083,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||||
if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
|
if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
|
/* Even though the attribute is u32, the specification says
|
||||||
|
* u8, so let's make sure we don't overflow.
|
||||||
|
*/
|
||||||
|
cs_count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
|
||||||
|
if (cs_count > 255)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
params.count = cs_count;
|
||||||
|
|
||||||
if (!need_new_beacon)
|
if (!need_new_beacon)
|
||||||
goto skip_beacons;
|
goto skip_beacons;
|
||||||
|
@ -6051,10 +6178,8 @@ skip_beacons:
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (err > 0) {
|
if (err > 0)
|
||||||
radar_detect_width = BIT(params.chandef.width);
|
|
||||||
params.radar_required = true;
|
params.radar_required = true;
|
||||||
}
|
|
||||||
|
|
||||||
if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
|
if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
|
||||||
params.block_tx = true;
|
params.block_tx = true;
|
||||||
|
@ -8151,6 +8276,28 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info)
|
||||||
|
{
|
||||||
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||||
|
struct net_device *dev = info->user_ptr[1];
|
||||||
|
struct ocb_setup setup = {};
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = nl80211_parse_chandef(rdev, info, &setup.chandef);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return cfg80211_join_ocb(rdev, dev, &setup);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info)
|
||||||
|
{
|
||||||
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||||
|
struct net_device *dev = info->user_ptr[1];
|
||||||
|
|
||||||
|
return cfg80211_leave_ocb(rdev, dev);
|
||||||
|
}
|
||||||
|
|
||||||
static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
|
static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
|
||||||
{
|
{
|
||||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||||
|
@ -9436,7 +9583,7 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
|
||||||
u16 admitted_time = 0;
|
u16 admitted_time = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
|
if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] ||
|
if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] ||
|
||||||
|
@ -9452,12 +9599,10 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* WMM uses TIDs 0-7 even for TSPEC */
|
/* WMM uses TIDs 0-7 even for TSPEC */
|
||||||
if (tsid < IEEE80211_FIRST_TSPEC_TSID) {
|
if (tsid >= IEEE80211_FIRST_TSPEC_TSID) {
|
||||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
|
|
||||||
return -EINVAL;
|
|
||||||
} else {
|
|
||||||
/* TODO: handle 802.11 TSPEC/admission control
|
/* TODO: handle 802.11 TSPEC/admission control
|
||||||
* need more attributes for that (e.g. BA session requirement)
|
* need more attributes for that (e.g. BA session requirement);
|
||||||
|
* change the WMM adminssion test above to allow both then
|
||||||
*/
|
*/
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -9773,6 +9918,15 @@ static const struct genl_ops nl80211_ops[] = {
|
||||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||||
NL80211_FLAG_NEED_RTNL,
|
NL80211_FLAG_NEED_RTNL,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.cmd = NL80211_CMD_GET_MPP,
|
||||||
|
.doit = nl80211_get_mpp,
|
||||||
|
.dumpit = nl80211_dump_mpp,
|
||||||
|
.policy = nl80211_policy,
|
||||||
|
.flags = GENL_ADMIN_PERM,
|
||||||
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||||
|
NL80211_FLAG_NEED_RTNL,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.cmd = NL80211_CMD_SET_MPATH,
|
.cmd = NL80211_CMD_SET_MPATH,
|
||||||
.doit = nl80211_set_mpath,
|
.doit = nl80211_set_mpath,
|
||||||
|
@ -10087,6 +10241,22 @@ static const struct genl_ops nl80211_ops[] = {
|
||||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||||
NL80211_FLAG_NEED_RTNL,
|
NL80211_FLAG_NEED_RTNL,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.cmd = NL80211_CMD_JOIN_OCB,
|
||||||
|
.doit = nl80211_join_ocb,
|
||||||
|
.policy = nl80211_policy,
|
||||||
|
.flags = GENL_ADMIN_PERM,
|
||||||
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||||
|
NL80211_FLAG_NEED_RTNL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.cmd = NL80211_CMD_LEAVE_OCB,
|
||||||
|
.doit = nl80211_leave_ocb,
|
||||||
|
.policy = nl80211_policy,
|
||||||
|
.flags = GENL_ADMIN_PERM,
|
||||||
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||||
|
NL80211_FLAG_NEED_RTNL,
|
||||||
|
},
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
{
|
{
|
||||||
.cmd = NL80211_CMD_GET_WOWLAN,
|
.cmd = NL80211_CMD_GET_WOWLAN,
|
||||||
|
|
88
net/wireless/ocb.c
Normal file
88
net/wireless/ocb.c
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* OCB mode implementation
|
||||||
|
*
|
||||||
|
* Copyright: (c) 2014 Czech Technical University in Prague
|
||||||
|
* (c) 2014 Volkswagen Group Research
|
||||||
|
* Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
|
||||||
|
* Funded by: Volkswagen Group Research
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/ieee80211.h>
|
||||||
|
#include <net/cfg80211.h>
|
||||||
|
#include "nl80211.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include "rdev-ops.h"
|
||||||
|
|
||||||
|
int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev,
|
||||||
|
struct ocb_setup *setup)
|
||||||
|
{
|
||||||
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
ASSERT_WDEV_LOCK(wdev);
|
||||||
|
|
||||||
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (WARN_ON(!setup->chandef.chan))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
err = rdev_join_ocb(rdev, dev, setup);
|
||||||
|
if (!err)
|
||||||
|
wdev->chandef = setup->chandef;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev,
|
||||||
|
struct ocb_setup *setup)
|
||||||
|
{
|
||||||
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
wdev_lock(wdev);
|
||||||
|
err = __cfg80211_join_ocb(rdev, dev, setup);
|
||||||
|
wdev_unlock(wdev);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
ASSERT_WDEV_LOCK(wdev);
|
||||||
|
|
||||||
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (!rdev->ops->leave_ocb)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
err = rdev_leave_ocb(rdev, dev);
|
||||||
|
if (!err)
|
||||||
|
memset(&wdev->chandef, 0, sizeof(wdev->chandef));
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
wdev_lock(wdev);
|
||||||
|
err = __cfg80211_leave_ocb(rdev, dev);
|
||||||
|
wdev_unlock(wdev);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
|
@ -178,11 +178,12 @@ static inline int rdev_add_station(struct cfg80211_registered_device *rdev,
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int rdev_del_station(struct cfg80211_registered_device *rdev,
|
static inline int rdev_del_station(struct cfg80211_registered_device *rdev,
|
||||||
struct net_device *dev, u8 *mac)
|
struct net_device *dev,
|
||||||
|
struct station_del_parameters *params)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
trace_rdev_del_station(&rdev->wiphy, dev, mac);
|
trace_rdev_del_station(&rdev->wiphy, dev, params);
|
||||||
ret = rdev->ops->del_station(&rdev->wiphy, dev, mac);
|
ret = rdev->ops->del_station(&rdev->wiphy, dev, params);
|
||||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -263,6 +264,18 @@ static inline int rdev_get_mpath(struct cfg80211_registered_device *rdev,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int rdev_get_mpp(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev, u8 *dst, u8 *mpp,
|
||||||
|
struct mpath_info *pinfo)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
trace_rdev_get_mpp(&rdev->wiphy, dev, dst, mpp);
|
||||||
|
ret = rdev->ops->get_mpp(&rdev->wiphy, dev, dst, mpp, pinfo);
|
||||||
|
trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev,
|
static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev,
|
||||||
struct net_device *dev, int idx, u8 *dst,
|
struct net_device *dev, int idx, u8 *dst,
|
||||||
u8 *next_hop, struct mpath_info *pinfo)
|
u8 *next_hop, struct mpath_info *pinfo)
|
||||||
|
@ -271,7 +284,20 @@ static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev,
|
||||||
int ret;
|
int ret;
|
||||||
trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop);
|
trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop);
|
||||||
ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop,
|
ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop,
|
||||||
pinfo);
|
pinfo);
|
||||||
|
trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int rdev_dump_mpp(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev, int idx, u8 *dst,
|
||||||
|
u8 *mpp, struct mpath_info *pinfo)
|
||||||
|
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
trace_rdev_dump_mpp(&rdev->wiphy, dev, idx, dst, mpp);
|
||||||
|
ret = rdev->ops->dump_mpp(&rdev->wiphy, dev, idx, dst, mpp, pinfo);
|
||||||
trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
|
trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -322,6 +348,27 @@ static inline int rdev_leave_mesh(struct cfg80211_registered_device *rdev,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int rdev_join_ocb(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev,
|
||||||
|
struct ocb_setup *setup)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
trace_rdev_join_ocb(&rdev->wiphy, dev, setup);
|
||||||
|
ret = rdev->ops->join_ocb(&rdev->wiphy, dev, setup);
|
||||||
|
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int rdev_leave_ocb(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
trace_rdev_leave_ocb(&rdev->wiphy, dev);
|
||||||
|
ret = rdev->ops->leave_ocb(&rdev->wiphy, dev);
|
||||||
|
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int rdev_change_bss(struct cfg80211_registered_device *rdev,
|
static inline int rdev_change_bss(struct cfg80211_registered_device *rdev,
|
||||||
struct net_device *dev,
|
struct net_device *dev,
|
||||||
struct bss_parameters *params)
|
struct bss_parameters *params)
|
||||||
|
|
|
@ -80,9 +80,18 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
|
||||||
if (!request)
|
if (!request)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (wdev->conn->params.channel)
|
if (wdev->conn->params.channel) {
|
||||||
|
enum ieee80211_band band = wdev->conn->params.channel->band;
|
||||||
|
struct ieee80211_supported_band *sband =
|
||||||
|
wdev->wiphy->bands[band];
|
||||||
|
|
||||||
|
if (!sband) {
|
||||||
|
kfree(request);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
request->channels[0] = wdev->conn->params.channel;
|
request->channels[0] = wdev->conn->params.channel;
|
||||||
else {
|
request->rates[band] = (1 << sband->n_bitrates) - 1;
|
||||||
|
} else {
|
||||||
int i = 0, j;
|
int i = 0, j;
|
||||||
enum ieee80211_band band;
|
enum ieee80211_band band;
|
||||||
struct ieee80211_supported_band *bands;
|
struct ieee80211_supported_band *bands;
|
||||||
|
|
|
@ -600,6 +600,11 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ibss,
|
||||||
TP_ARGS(wiphy, netdev)
|
TP_ARGS(wiphy, netdev)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ocb,
|
||||||
|
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
|
||||||
|
TP_ARGS(wiphy, netdev)
|
||||||
|
);
|
||||||
|
|
||||||
DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
|
DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
|
||||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
|
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
|
||||||
TP_ARGS(wiphy, netdev)
|
TP_ARGS(wiphy, netdev)
|
||||||
|
@ -680,9 +685,34 @@ DECLARE_EVENT_CLASS(wiphy_netdev_mac_evt,
|
||||||
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac))
|
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac))
|
||||||
);
|
);
|
||||||
|
|
||||||
DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_station,
|
DECLARE_EVENT_CLASS(station_del,
|
||||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
|
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||||
TP_ARGS(wiphy, netdev, mac)
|
struct station_del_parameters *params),
|
||||||
|
TP_ARGS(wiphy, netdev, params),
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
WIPHY_ENTRY
|
||||||
|
NETDEV_ENTRY
|
||||||
|
MAC_ENTRY(sta_mac)
|
||||||
|
__field(u8, subtype)
|
||||||
|
__field(u16, reason_code)
|
||||||
|
),
|
||||||
|
TP_fast_assign(
|
||||||
|
WIPHY_ASSIGN;
|
||||||
|
NETDEV_ASSIGN;
|
||||||
|
MAC_ASSIGN(sta_mac, params->mac);
|
||||||
|
__entry->subtype = params->subtype;
|
||||||
|
__entry->reason_code = params->reason_code;
|
||||||
|
),
|
||||||
|
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
|
||||||
|
", subtype: %u, reason_code: %u",
|
||||||
|
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
|
||||||
|
__entry->subtype, __entry->reason_code)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(station_del, rdev_del_station,
|
||||||
|
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||||
|
struct station_del_parameters *params),
|
||||||
|
TP_ARGS(wiphy, netdev, params)
|
||||||
);
|
);
|
||||||
|
|
||||||
DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_get_station,
|
DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_get_station,
|
||||||
|
@ -801,6 +831,51 @@ TRACE_EVENT(rdev_dump_mpath,
|
||||||
MAC_PR_ARG(next_hop))
|
MAC_PR_ARG(next_hop))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(rdev_get_mpp,
|
||||||
|
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||||
|
u8 *dst, u8 *mpp),
|
||||||
|
TP_ARGS(wiphy, netdev, dst, mpp),
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
WIPHY_ENTRY
|
||||||
|
NETDEV_ENTRY
|
||||||
|
MAC_ENTRY(dst)
|
||||||
|
MAC_ENTRY(mpp)
|
||||||
|
),
|
||||||
|
TP_fast_assign(
|
||||||
|
WIPHY_ASSIGN;
|
||||||
|
NETDEV_ASSIGN;
|
||||||
|
MAC_ASSIGN(dst, dst);
|
||||||
|
MAC_ASSIGN(mpp, mpp);
|
||||||
|
),
|
||||||
|
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT
|
||||||
|
", mpp: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG,
|
||||||
|
MAC_PR_ARG(dst), MAC_PR_ARG(mpp))
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(rdev_dump_mpp,
|
||||||
|
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
|
||||||
|
u8 *dst, u8 *mpp),
|
||||||
|
TP_ARGS(wiphy, netdev, idx, mpp, dst),
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
WIPHY_ENTRY
|
||||||
|
NETDEV_ENTRY
|
||||||
|
MAC_ENTRY(dst)
|
||||||
|
MAC_ENTRY(mpp)
|
||||||
|
__field(int, idx)
|
||||||
|
),
|
||||||
|
TP_fast_assign(
|
||||||
|
WIPHY_ASSIGN;
|
||||||
|
NETDEV_ASSIGN;
|
||||||
|
MAC_ASSIGN(dst, dst);
|
||||||
|
MAC_ASSIGN(mpp, mpp);
|
||||||
|
__entry->idx = idx;
|
||||||
|
),
|
||||||
|
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
|
||||||
|
MAC_PR_FMT ", mpp: " MAC_PR_FMT,
|
||||||
|
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst),
|
||||||
|
MAC_PR_ARG(mpp))
|
||||||
|
);
|
||||||
|
|
||||||
TRACE_EVENT(rdev_return_int_mpath_info,
|
TRACE_EVENT(rdev_return_int_mpath_info,
|
||||||
TP_PROTO(struct wiphy *wiphy, int ret, struct mpath_info *pinfo),
|
TP_PROTO(struct wiphy *wiphy, int ret, struct mpath_info *pinfo),
|
||||||
TP_ARGS(wiphy, ret, pinfo),
|
TP_ARGS(wiphy, ret, pinfo),
|
||||||
|
@ -1246,6 +1321,22 @@ TRACE_EVENT(rdev_join_ibss,
|
||||||
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid)
|
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(rdev_join_ocb,
|
||||||
|
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||||
|
const struct ocb_setup *setup),
|
||||||
|
TP_ARGS(wiphy, netdev, setup),
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
WIPHY_ENTRY
|
||||||
|
NETDEV_ENTRY
|
||||||
|
),
|
||||||
|
TP_fast_assign(
|
||||||
|
WIPHY_ASSIGN;
|
||||||
|
NETDEV_ASSIGN;
|
||||||
|
),
|
||||||
|
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT,
|
||||||
|
WIPHY_PR_ARG, NETDEV_PR_ARG)
|
||||||
|
);
|
||||||
|
|
||||||
TRACE_EVENT(rdev_set_wiphy_params,
|
TRACE_EVENT(rdev_set_wiphy_params,
|
||||||
TP_PROTO(struct wiphy *wiphy, u32 changed),
|
TP_PROTO(struct wiphy *wiphy, u32 changed),
|
||||||
TP_ARGS(wiphy, changed),
|
TP_ARGS(wiphy, changed),
|
||||||
|
|
|
@ -442,7 +442,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
|
||||||
break;
|
break;
|
||||||
case cpu_to_le16(0):
|
case cpu_to_le16(0):
|
||||||
if (iftype != NL80211_IFTYPE_ADHOC &&
|
if (iftype != NL80211_IFTYPE_ADHOC &&
|
||||||
iftype != NL80211_IFTYPE_STATION)
|
iftype != NL80211_IFTYPE_STATION &&
|
||||||
|
iftype != NL80211_IFTYPE_OCB)
|
||||||
return -1;
|
return -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -519,6 +520,7 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
|
||||||
memcpy(hdr.addr3, skb->data, ETH_ALEN);
|
memcpy(hdr.addr3, skb->data, ETH_ALEN);
|
||||||
hdrlen = 24;
|
hdrlen = 24;
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
/* DA SA BSSID */
|
/* DA SA BSSID */
|
||||||
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
||||||
|
@ -937,6 +939,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
|
||||||
if (dev->ieee80211_ptr->use_4addr)
|
if (dev->ieee80211_ptr->use_4addr)
|
||||||
break;
|
break;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
case NL80211_IFTYPE_P2P_CLIENT:
|
case NL80211_IFTYPE_P2P_CLIENT:
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
dev->priv_flags |= IFF_DONT_BRIDGE;
|
dev->priv_flags |= IFF_DONT_BRIDGE;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue