提交 0f622485 编写于 作者: J John W. Linville
...@@ -511,8 +511,9 @@ ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah, ...@@ -511,8 +511,9 @@ ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
ath5k_vif_iter(&iter_data, vif->addr, vif); ath5k_vif_iter(&iter_data, vif->addr, vif);
/* Get list of all active MAC addresses */ /* Get list of all active MAC addresses */
ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, ieee80211_iterate_active_interfaces_atomic(
&iter_data); ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath5k_vif_iter, &iter_data);
memcpy(ah->bssidmask, iter_data.mask, ETH_ALEN); memcpy(ah->bssidmask, iter_data.mask, ETH_ALEN);
ah->opmode = iter_data.opmode; ah->opmode = iter_data.opmode;
...@@ -3045,8 +3046,9 @@ ath5k_any_vif_assoc(struct ath5k_hw *ah) ...@@ -3045,8 +3046,9 @@ ath5k_any_vif_assoc(struct ath5k_hw *ah)
iter_data.need_set_hw_addr = false; iter_data.need_set_hw_addr = false;
iter_data.found_active = true; iter_data.found_active = true;
ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, ieee80211_iterate_active_interfaces_atomic(
&iter_data); ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath5k_vif_iter, &iter_data);
return iter_data.any_assoc; return iter_data.any_assoc;
} }
......
...@@ -452,8 +452,9 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, ...@@ -452,8 +452,9 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
iter_data.hw_macaddr = NULL; iter_data.hw_macaddr = NULL;
iter_data.n_stas = 0; iter_data.n_stas = 0;
iter_data.need_set_hw_addr = false; iter_data.need_set_hw_addr = false;
ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, ieee80211_iterate_active_interfaces_atomic(
&iter_data); ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath5k_vif_iter, &iter_data);
/* Set up RX Filter */ /* Set up RX Filter */
if (iter_data.n_stas > 1) { if (iter_data.n_stas > 1) {
......
...@@ -1384,11 +1384,8 @@ static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) ...@@ -1384,11 +1384,8 @@ static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
return 0; return 0;
} }
/*
* The type nl80211_tx_power_setting replaces the following
* data type from 2.6.36 onwards
*/
static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, enum nl80211_tx_power_setting type,
int mbm) int mbm)
{ {
...@@ -1423,7 +1420,9 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, ...@@ -1423,7 +1420,9 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
return 0; return 0;
} }
static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm) static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy,
struct wireless_dev *wdev,
int *dbm)
{ {
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
struct ath6kl_vif *vif; struct ath6kl_vif *vif;
......
...@@ -587,9 +587,9 @@ static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv, ...@@ -587,9 +587,9 @@ static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv,
(priv->num_sta_vif > 1) && (priv->num_sta_vif > 1) &&
(vif->type == NL80211_IFTYPE_STATION)) { (vif->type == NL80211_IFTYPE_STATION)) {
beacon_configured = false; beacon_configured = false;
ieee80211_iterate_active_interfaces_atomic(priv->hw, ieee80211_iterate_active_interfaces_atomic(
ath9k_htc_beacon_iter, priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
&beacon_configured); ath9k_htc_beacon_iter, &beacon_configured);
if (beacon_configured) { if (beacon_configured) {
ath_dbg(common, CONFIG, ath_dbg(common, CONFIG,
......
...@@ -127,8 +127,9 @@ static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv) ...@@ -127,8 +127,9 @@ static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv)
priv->rearm_ani = false; priv->rearm_ani = false;
priv->reconfig_beacon = false; priv->reconfig_beacon = false;
ieee80211_iterate_active_interfaces_atomic(priv->hw, ieee80211_iterate_active_interfaces_atomic(
ath9k_htc_vif_iter, priv); priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath9k_htc_vif_iter, priv);
if (priv->rearm_ani) if (priv->rearm_ani)
ath9k_htc_start_ani(priv); ath9k_htc_start_ani(priv);
...@@ -165,8 +166,9 @@ static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv, ...@@ -165,8 +166,9 @@ static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
ath9k_htc_bssid_iter(&iter_data, vif->addr, vif); ath9k_htc_bssid_iter(&iter_data, vif->addr, vif);
/* Get list of all active MAC addresses */ /* Get list of all active MAC addresses */
ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter, ieee80211_iterate_active_interfaces_atomic(
&iter_data); priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath9k_htc_bssid_iter, &iter_data);
memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
ath_hw_setbssidmask(common); ath_hw_setbssidmask(common);
...@@ -1144,8 +1146,9 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, ...@@ -1144,8 +1146,9 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
*/ */
if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) { if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
priv->rearm_ani = false; priv->rearm_ani = false;
ieee80211_iterate_active_interfaces_atomic(priv->hw, ieee80211_iterate_active_interfaces_atomic(
ath9k_htc_vif_iter, priv); priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath9k_htc_vif_iter, priv);
if (!priv->rearm_ani) if (!priv->rearm_ani)
ath9k_htc_stop_ani(priv); ath9k_htc_stop_ani(priv);
} }
...@@ -1466,8 +1469,9 @@ static void ath9k_htc_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) ...@@ -1466,8 +1469,9 @@ static void ath9k_htc_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
static void ath9k_htc_choose_set_bssid(struct ath9k_htc_priv *priv) static void ath9k_htc_choose_set_bssid(struct ath9k_htc_priv *priv)
{ {
if (priv->num_sta_assoc_vif == 1) { if (priv->num_sta_assoc_vif == 1) {
ieee80211_iterate_active_interfaces_atomic(priv->hw, ieee80211_iterate_active_interfaces_atomic(
ath9k_htc_bss_iter, priv); priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath9k_htc_bss_iter, priv);
ath9k_htc_set_bssid(priv); ath9k_htc_set_bssid(priv);
} }
} }
......
...@@ -924,8 +924,9 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw, ...@@ -924,8 +924,9 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
ath9k_vif_iter(iter_data, vif->addr, vif); ath9k_vif_iter(iter_data, vif->addr, vif);
/* Get list of all active MAC addresses */ /* Get list of all active MAC addresses */
ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter, ieee80211_iterate_active_interfaces_atomic(
iter_data); sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath9k_vif_iter, iter_data);
} }
/* Called with sc->mutex held. */ /* Called with sc->mutex held. */
...@@ -975,8 +976,9 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, ...@@ -975,8 +976,9 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
if (ah->opmode == NL80211_IFTYPE_STATION && if (ah->opmode == NL80211_IFTYPE_STATION &&
old_opmode == NL80211_IFTYPE_AP && old_opmode == NL80211_IFTYPE_AP &&
test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
ieee80211_iterate_active_interfaces_atomic(sc->hw, ieee80211_iterate_active_interfaces_atomic(
ath9k_sta_vif_iter, sc); sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath9k_sta_vif_iter, sc);
} }
} }
...@@ -1505,8 +1507,9 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ...@@ -1505,8 +1507,9 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
clear_bit(SC_OP_BEACONS, &sc->sc_flags); clear_bit(SC_OP_BEACONS, &sc->sc_flags);
} }
ieee80211_iterate_active_interfaces_atomic(sc->hw, ieee80211_iterate_active_interfaces_atomic(
ath9k_bss_assoc_iter, sc); sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath9k_bss_assoc_iter, sc);
if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags) && if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags) &&
ah->opmode == NL80211_IFTYPE_STATION) { ah->opmode == NL80211_IFTYPE_STATION) {
......
...@@ -1529,7 +1529,7 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, ...@@ -1529,7 +1529,7 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
} }
static s32 static s32
brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, s32 mbm) enum nl80211_tx_power_setting type, s32 mbm)
{ {
...@@ -1578,7 +1578,9 @@ brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, ...@@ -1578,7 +1578,9 @@ brcmf_cfg80211_set_tx_power(struct wiphy *wiphy,
return err; return err;
} }
static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm) static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy,
struct wireless_dev *wdev,
s32 *dbm)
{ {
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
......
...@@ -324,6 +324,7 @@ mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, ...@@ -324,6 +324,7 @@ mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
*/ */
static int static int
mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy,
struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, enum nl80211_tx_power_setting type,
int mbm) int mbm)
{ {
......
...@@ -490,9 +490,12 @@ static int rndis_scan(struct wiphy *wiphy, ...@@ -490,9 +490,12 @@ static int rndis_scan(struct wiphy *wiphy,
static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed); static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed);
static int rndis_set_tx_power(struct wiphy *wiphy, static int rndis_set_tx_power(struct wiphy *wiphy,
struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, enum nl80211_tx_power_setting type,
int mbm); int mbm);
static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm); static int rndis_get_tx_power(struct wiphy *wiphy,
struct wireless_dev *wdev,
int *dbm);
static int rndis_connect(struct wiphy *wiphy, struct net_device *dev, static int rndis_connect(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme); struct cfg80211_connect_params *sme);
...@@ -1903,6 +1906,7 @@ static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed) ...@@ -1903,6 +1906,7 @@ static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed)
} }
static int rndis_set_tx_power(struct wiphy *wiphy, static int rndis_set_tx_power(struct wiphy *wiphy,
struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, enum nl80211_tx_power_setting type,
int mbm) int mbm)
{ {
...@@ -1930,7 +1934,9 @@ static int rndis_set_tx_power(struct wiphy *wiphy, ...@@ -1930,7 +1934,9 @@ static int rndis_set_tx_power(struct wiphy *wiphy,
return -ENOTSUPP; return -ENOTSUPP;
} }
static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm) static int rndis_get_tx_power(struct wiphy *wiphy,
struct wireless_dev *wdev,
int *dbm)
{ {
struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct rndis_wlan_private *priv = wiphy_priv(wiphy);
struct usbnet *usbdev = priv->usbdev; struct usbnet *usbdev = priv->usbdev;
......
...@@ -157,6 +157,7 @@ static void rt2x00lib_intf_scheduled(struct work_struct *work) ...@@ -157,6 +157,7 @@ static void rt2x00lib_intf_scheduled(struct work_struct *work)
* requested configurations. * requested configurations.
*/ */
ieee80211_iterate_active_interfaces(rt2x00dev->hw, ieee80211_iterate_active_interfaces(rt2x00dev->hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
rt2x00lib_intf_scheduled_iter, rt2x00lib_intf_scheduled_iter,
rt2x00dev); rt2x00dev);
} }
...@@ -225,9 +226,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) ...@@ -225,9 +226,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
return; return;
/* send buffered bc/mc frames out for every bssid */ /* send buffered bc/mc frames out for every bssid */
ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, ieee80211_iterate_active_interfaces_atomic(
rt2x00lib_bc_buffer_iter, rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
rt2x00dev); rt2x00lib_bc_buffer_iter, rt2x00dev);
/* /*
* Devices with pre tbtt interrupt don't need to update the beacon * Devices with pre tbtt interrupt don't need to update the beacon
* here as they will fetch the next beacon directly prior to * here as they will fetch the next beacon directly prior to
...@@ -237,9 +238,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) ...@@ -237,9 +238,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
return; return;
/* fetch next beacon */ /* fetch next beacon */
ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, ieee80211_iterate_active_interfaces_atomic(
rt2x00lib_beaconupdate_iter, rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
rt2x00dev); rt2x00lib_beaconupdate_iter, rt2x00dev);
} }
EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); EXPORT_SYMBOL_GPL(rt2x00lib_beacondone);
...@@ -249,9 +250,9 @@ void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev) ...@@ -249,9 +250,9 @@ void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev)
return; return;
/* fetch next beacon */ /* fetch next beacon */
ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, ieee80211_iterate_active_interfaces_atomic(
rt2x00lib_beaconupdate_iter, rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
rt2x00dev); rt2x00lib_beaconupdate_iter, rt2x00dev);
} }
EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt); EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt);
......
...@@ -424,9 +424,9 @@ int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, ...@@ -424,9 +424,9 @@ int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return 0; return 0;
ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, ieee80211_iterate_active_interfaces_atomic(
rt2x00mac_set_tim_iter, rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
rt2x00dev); rt2x00mac_set_tim_iter, rt2x00dev);
/* queue work to upodate the beacon template */ /* queue work to upodate the beacon template */
ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work);
......
...@@ -677,7 +677,7 @@ static void wl12xx_get_vif_count(struct ieee80211_hw *hw, ...@@ -677,7 +677,7 @@ static void wl12xx_get_vif_count(struct ieee80211_hw *hw,
memset(data, 0, sizeof(*data)); memset(data, 0, sizeof(*data));
data->cur_vif = cur_vif; data->cur_vif = cur_vif;
ieee80211_iterate_active_interfaces(hw, ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
wl12xx_vif_count_iter, data); wl12xx_vif_count_iter, data);
} }
......
...@@ -905,6 +905,38 @@ struct ieee80211_tdls_data { ...@@ -905,6 +905,38 @@ struct ieee80211_tdls_data {
} u; } u;
} __packed; } __packed;
/*
* Peer-to-Peer IE attribute related definitions.
*/
/**
* enum ieee80211_p2p_attr_id - identifies type of peer-to-peer attribute.
*/
enum ieee80211_p2p_attr_id {
IEEE80211_P2P_ATTR_STATUS = 0,
IEEE80211_P2P_ATTR_MINOR_REASON,
IEEE80211_P2P_ATTR_CAPABILITY,
IEEE80211_P2P_ATTR_DEVICE_ID,
IEEE80211_P2P_ATTR_GO_INTENT,
IEEE80211_P2P_ATTR_GO_CONFIG_TIMEOUT,
IEEE80211_P2P_ATTR_LISTEN_CHANNEL,
IEEE80211_P2P_ATTR_GROUP_BSSID,
IEEE80211_P2P_ATTR_EXT_LISTEN_TIMING,
IEEE80211_P2P_ATTR_INTENDED_IFACE_ADDR,
IEEE80211_P2P_ATTR_MANAGABILITY,
IEEE80211_P2P_ATTR_CHANNEL_LIST,
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
IEEE80211_P2P_ATTR_DEVICE_INFO,
IEEE80211_P2P_ATTR_GROUP_INFO,
IEEE80211_P2P_ATTR_GROUP_ID,
IEEE80211_P2P_ATTR_INTERFACE,
IEEE80211_P2P_ATTR_OPER_CHANNEL,
IEEE80211_P2P_ATTR_INVITE_FLAGS,
/* 19 - 220: Reserved */
IEEE80211_P2P_ATTR_VENDOR_SPECIFIC = 221,
IEEE80211_P2P_ATTR_MAX
};
/** /**
* struct ieee80211_bar - HT Block Ack Request * struct ieee80211_bar - HT Block Ack Request
* *
......
...@@ -1545,13 +1545,19 @@ struct cfg80211_gtk_rekey_data { ...@@ -1545,13 +1545,19 @@ struct cfg80211_gtk_rekey_data {
* to a merge. * to a merge.
* @leave_ibss: Leave the IBSS. * @leave_ibss: Leave the IBSS.
* *
* @set_mcast_rate: Set the specified multicast rate (only if vif is in ADHOC or
* MESH mode)
*
* @set_wiphy_params: Notify that wiphy parameters have changed; * @set_wiphy_params: Notify that wiphy parameters have changed;
* @changed bitfield (see &enum wiphy_params_flags) describes which values * @changed bitfield (see &enum wiphy_params_flags) describes which values
* have changed. The actual parameter values are available in * have changed. The actual parameter values are available in
* struct wiphy. If returning an error, no value should be changed. * struct wiphy. If returning an error, no value should be changed.
* *
* @set_tx_power: set the transmit power according to the parameters, * @set_tx_power: set the transmit power according to the parameters,
* the power passed is in mBm, to get dBm use MBM_TO_DBM(). * the power passed is in mBm, to get dBm use MBM_TO_DBM(). The
* wdev may be %NULL if power was set for the wiphy, and will
* always be %NULL unless the driver supports per-vif TX power
* (as advertised by the nl80211 feature flag.)
* @get_tx_power: store the current TX power into the dbm variable; * @get_tx_power: store the current TX power into the dbm variable;
* return 0 if successful * return 0 if successful
* *
...@@ -1746,11 +1752,15 @@ struct cfg80211_ops { ...@@ -1746,11 +1752,15 @@ struct cfg80211_ops {
struct cfg80211_ibss_params *params); struct cfg80211_ibss_params *params);
int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev); int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev);
int (*set_mcast_rate)(struct wiphy *wiphy, struct net_device *dev,
int rate[IEEE80211_NUM_BANDS]);
int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed); int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed);
int (*set_tx_power)(struct wiphy *wiphy, int (*set_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, int mbm); enum nl80211_tx_power_setting type, int mbm);
int (*get_tx_power)(struct wiphy *wiphy, int *dbm); int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
int *dbm);
int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev, int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev,
const u8 *addr); const u8 *addr);
...@@ -3550,7 +3560,6 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, ...@@ -3550,7 +3560,6 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
* @len: length of the frame * @len: length of the frame
* @freq: frequency the frame was received on * @freq: frequency the frame was received on
* @sig_dbm: signal strength in mBm, or 0 if unknown * @sig_dbm: signal strength in mBm, or 0 if unknown
* @gfp: allocation flags
* *
* Use this function to report to userspace when a beacon was * Use this function to report to userspace when a beacon was
* received. It is not useful to call this when there is no * received. It is not useful to call this when there is no
...@@ -3558,7 +3567,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, ...@@ -3558,7 +3567,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
*/ */
void cfg80211_report_obss_beacon(struct wiphy *wiphy, void cfg80211_report_obss_beacon(struct wiphy *wiphy,
const u8 *frame, size_t len, const u8 *frame, size_t len,
int freq, int sig_dbm, gfp_t gfp); int freq, int sig_dbm);
/** /**
* cfg80211_can_beacon_sec_chan - test if ht40 on extension channel can be used * cfg80211_can_beacon_sec_chan - test if ht40 on extension channel can be used
...@@ -3608,6 +3617,25 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate); ...@@ -3608,6 +3617,25 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate);
*/ */
void cfg80211_unregister_wdev(struct wireless_dev *wdev); void cfg80211_unregister_wdev(struct wireless_dev *wdev);
/**
* cfg80211_get_p2p_attr - find and copy a P2P attribute from IE buffer
* @ies: the input IE buffer
* @len: the input length
* @attr: the attribute ID to find
* @buf: output buffer, can be %NULL if the data isn't needed, e.g.
* if the function is only called to get the needed buffer size
* @bufsize: size of the output buffer
*
* The function finds a given P2P attribute in the (vendor) IEs and
* copies its contents to the given buffer.
*
* The return value is a negative error code (-%EILSEQ or -%ENOENT) if
* the data is malformed or the attribute can't be found (respectively),
* or the length of the found attribute (which can be zero).
*/
unsigned int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
u8 attr, u8 *buf, unsigned int bufsize);
/* Logging, debugging and troubleshooting/diagnostic helpers. */ /* Logging, debugging and troubleshooting/diagnostic helpers. */
/* wiphy_printk helpers, similar to dev_printk */ /* wiphy_printk helpers, similar to dev_printk */
......
...@@ -207,6 +207,9 @@ struct ieee80211_chanctx_conf { ...@@ -207,6 +207,9 @@ struct ieee80211_chanctx_conf {
* @BSS_CHANGED_SSID: SSID changed for this BSS (AP mode) * @BSS_CHANGED_SSID: SSID changed for this BSS (AP mode)
* @BSS_CHANGED_AP_PROBE_RESP: Probe Response changed for this BSS (AP mode) * @BSS_CHANGED_AP_PROBE_RESP: Probe Response changed for this BSS (AP mode)
* @BSS_CHANGED_PS: PS changed for this BSS (STA mode) * @BSS_CHANGED_PS: PS changed for this BSS (STA mode)
* @BSS_CHANGED_TXPOWER: TX power setting changed for this interface
* @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS)
* changed (currently only in P2P client mode, GO mode will be later)
*/ */
enum ieee80211_bss_change { enum ieee80211_bss_change {
BSS_CHANGED_ASSOC = 1<<0, BSS_CHANGED_ASSOC = 1<<0,
...@@ -227,6 +230,8 @@ enum ieee80211_bss_change { ...@@ -227,6 +230,8 @@ enum ieee80211_bss_change {
BSS_CHANGED_SSID = 1<<15, BSS_CHANGED_SSID = 1<<15,
BSS_CHANGED_AP_PROBE_RESP = 1<<16, BSS_CHANGED_AP_PROBE_RESP = 1<<16,
BSS_CHANGED_PS = 1<<17, BSS_CHANGED_PS = 1<<17,
BSS_CHANGED_TXPOWER = 1<<18,
BSS_CHANGED_P2P_PS = 1<<19,
/* when adding here, make sure to change ieee80211_reconfig */ /* when adding here, make sure to change ieee80211_reconfig */
}; };
...@@ -309,6 +314,9 @@ enum ieee80211_rssi_event { ...@@ -309,6 +314,9 @@ enum ieee80211_rssi_event {
* @ssid: The SSID of the current vif. Only valid in AP-mode. * @ssid: The SSID of the current vif. Only valid in AP-mode.
* @ssid_len: Length of SSID given in @ssid. * @ssid_len: Length of SSID given in @ssid.
* @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode. * @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode.
* @txpower: TX power in dBm
* @p2p_ctwindow: P2P CTWindow, only for P2P client interfaces
* @p2p_oppps: P2P opportunistic PS is enabled
*/ */
struct ieee80211_bss_conf { struct ieee80211_bss_conf {
const u8 *bssid; const u8 *bssid;
...@@ -341,6 +349,9 @@ struct ieee80211_bss_conf { ...@@ -341,6 +349,9 @@ struct ieee80211_bss_conf {
u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid[IEEE80211_MAX_SSID_LEN];
size_t ssid_len; size_t ssid_len;
bool hidden_ssid; bool hidden_ssid;
int txpower;
u8 p2p_ctwindow;
bool p2p_oppps;
}; };
/** /**
...@@ -884,7 +895,8 @@ enum ieee80211_smps_mode { ...@@ -884,7 +895,8 @@ enum ieee80211_smps_mode {
* powersave documentation below. This variable is valid only when * powersave documentation below. This variable is valid only when
* the CONF_PS flag is set. * the CONF_PS flag is set.
* *
* @power_level: requested transmit power (in dBm) * @power_level: requested transmit power (in dBm), backward compatibility
* value only that is set to the minimum of all interfaces
* *
* @channel: the channel to tune to * @channel: the channel to tune to
* @channel_type: the channel (HT) type * @channel_type: the channel (HT) type
...@@ -2381,6 +2393,17 @@ enum ieee80211_rate_control_changed { ...@@ -2381,6 +2393,17 @@ enum ieee80211_rate_control_changed {
* to vif. Possible use is for hw queue remapping. * to vif. Possible use is for hw queue remapping.
* @unassign_vif_chanctx: Notifies device driver about channel context being * @unassign_vif_chanctx: Notifies device driver about channel context being
* unbound from vif. * unbound from vif.
* @start_ap: Start operation on the AP interface, this is called after all the
* information in bss_conf is set and beacon can be retrieved. A channel
* context is bound before this is called. Note that if the driver uses
* software scan or ROC, this (and @stop_ap) isn't called when the AP is
* just "paused" for scanning/ROC, which is indicated by the beacon being
* disabled/enabled via @bss_info_changed.
* @stop_ap: Stop operation on the AP interface.
*
* @restart_complete: Called after a call to ieee80211_restart_hw(), when the
* reconfiguration has completed. This can help the driver implement the
* reconfiguration step. This callback may sleep.
*/ */
struct ieee80211_ops { struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw, void (*tx)(struct ieee80211_hw *hw,
...@@ -2406,6 +2429,9 @@ struct ieee80211_ops { ...@@ -2406,6 +2429,9 @@ struct ieee80211_ops {
struct ieee80211_bss_conf *info, struct ieee80211_bss_conf *info,
u32 changed); u32 changed);
int (*start_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
void (*stop_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
u64 (*prepare_multicast)(struct ieee80211_hw *hw, u64 (*prepare_multicast)(struct ieee80211_hw *hw,
struct netdev_hw_addr_list *mc_list); struct netdev_hw_addr_list *mc_list);
void (*configure_filter)(struct ieee80211_hw *hw, void (*configure_filter)(struct ieee80211_hw *hw,
...@@ -2539,6 +2565,8 @@ struct ieee80211_ops { ...@@ -2539,6 +2565,8 @@ struct ieee80211_ops {
void (*unassign_vif_chanctx)(struct ieee80211_hw *hw, void (*unassign_vif_chanctx)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *ctx); struct ieee80211_chanctx_conf *ctx);
void (*restart_complete)(struct ieee80211_hw *hw);
}; };
/** /**
...@@ -3384,6 +3412,21 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw); ...@@ -3384,6 +3412,21 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw);
*/ */
void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw); void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw);
/**
* enum ieee80211_interface_iteration_flags - interface iteration flags
* @IEEE80211_IFACE_ITER_NORMAL: Iterate over all interfaces that have
* been added to the driver; However, note that during hardware
* reconfiguration (after restart_hw) it will iterate over a new
* interface and over all the existing interfaces even if they
* haven't been re-added to the driver yet.
* @IEEE80211_IFACE_ITER_RESUME_ALL: During resume, iterate over all
* interfaces, even if they haven't been re-added to the driver yet.
*/
enum ieee80211_interface_iteration_flags {
IEEE80211_IFACE_ITER_NORMAL = 0,
IEEE80211_IFACE_ITER_RESUME_ALL = BIT(0),
};
/** /**
* ieee80211_iterate_active_interfaces - iterate active interfaces * ieee80211_iterate_active_interfaces - iterate active interfaces
* *
...@@ -3392,13 +3435,15 @@ void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw); ...@@ -3392,13 +3435,15 @@ void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw);
* This function allows the iterator function to sleep, when the iterator * This function allows the iterator function to sleep, when the iterator
* function is atomic @ieee80211_iterate_active_interfaces_atomic can * function is atomic @ieee80211_iterate_active_interfaces_atomic can
* be used. * be used.
* Does not iterate over a new interface during add_interface() * Does not iterate over a new interface during add_interface().
* *
* @hw: the hardware struct of which the interfaces should be iterated over * @hw: the hardware struct of which the interfaces should be iterated over
* @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
* @iterator: the iterator function to call * @iterator: the iterator function to call
* @data: first argument of the iterator function * @data: first argument of the iterator function
*/ */
void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw,
u32 iter_flags,
void (*iterator)(void *data, u8 *mac, void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif), struct ieee80211_vif *vif),
void *data); void *data);
...@@ -3410,13 +3455,15 @@ void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, ...@@ -3410,13 +3455,15 @@ void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw,
* hardware that are currently active and calls the callback for them. * hardware that are currently active and calls the callback for them.
* This function requires the iterator callback function to be atomic, * This function requires the iterator callback function to be atomic,
* if that is not desired, use @ieee80211_iterate_active_interfaces instead. * if that is not desired, use @ieee80211_iterate_active_interfaces instead.
* Does not iterate over a new interface during add_interface() * Does not iterate over a new interface during add_interface().
* *
* @hw: the hardware struct of which the interfaces should be iterated over * @hw: the hardware struct of which the interfaces should be iterated over
* @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
* @iterator: the iterator function to call, cannot sleep * @iterator: the iterator function to call, cannot sleep
* @data: first argument of the iterator function * @data: first argument of the iterator function
*/ */
void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw, void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw,
u32 iter_flags,
void (*iterator)(void *data, void (*iterator)(void *data,
u8 *mac, u8 *mac,
struct ieee80211_vif *vif), struct ieee80211_vif *vif),
......
...@@ -578,6 +578,9 @@ ...@@ -578,6 +578,9 @@
* station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON * station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON
* is used for this. * is used for this.
* *
* @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames
* for IBSS or MESH vif.
*
* @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
*/ */
...@@ -726,6 +729,8 @@ enum nl80211_commands { ...@@ -726,6 +729,8 @@ enum nl80211_commands {
NL80211_CMD_CONN_FAILED, NL80211_CMD_CONN_FAILED,
NL80211_CMD_SET_MCAST_RATE,
/* add new commands above here */ /* add new commands above here */
/* used to define NL80211_CMD_MAX below */ /* used to define NL80211_CMD_MAX below */
...@@ -3051,6 +3056,7 @@ enum nl80211_ap_sme_features { ...@@ -3051,6 +3056,7 @@ enum nl80211_ap_sme_features {
* @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan
* @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported
* @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif
* @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting
*/ */
enum nl80211_feature_flags { enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0, NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
...@@ -3062,6 +3068,7 @@ enum nl80211_feature_flags { ...@@ -3062,6 +3068,7 @@ enum nl80211_feature_flags {
NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6,
NL80211_FEATURE_SCAN_FLUSH = 1 << 7, NL80211_FEATURE_SCAN_FLUSH = 1 << 7,
NL80211_FEATURE_AP_SCAN = 1 << 8, NL80211_FEATURE_AP_SCAN = 1 << 8,
NL80211_FEATURE_VIF_TXPOWER = 1 << 9,
}; };
/** /**
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/crypto.h> #include <linux/crypto.h>
#include <linux/export.h>
#include <linux/err.h> #include <linux/err.h>
#include <crypto/aes.h> #include <crypto/aes.h>
......
...@@ -922,6 +922,15 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, ...@@ -922,6 +922,15 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
return err; return err;
changed |= err; changed |= err;
err = drv_start_ap(sdata->local, sdata);
if (err) {
old = rtnl_dereference(sdata->u.ap.beacon);
if (old)
kfree_rcu(old, rcu_head);
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
return err;
}
ieee80211_bss_info_change_notify(sdata, changed); ieee80211_bss_info_change_notify(sdata, changed);
netif_carrier_on(dev); netif_carrier_on(dev);
...@@ -953,26 +962,38 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, ...@@ -953,26 +962,38 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
{ {
struct ieee80211_sub_if_data *sdata, *vlan; struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct beacon_data *old; struct ieee80211_sub_if_data *vlan;
struct ieee80211_local *local = sdata->local;
sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct beacon_data *old_beacon;
struct probe_resp *old_probe_resp;
old = rtnl_dereference(sdata->u.ap.beacon); old_beacon = rtnl_dereference(sdata->u.ap.beacon);
if (!old) if (!old_beacon)
return -ENOENT; return -ENOENT;
old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
/* turn off carrier for this interface and dependent VLANs */
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
netif_carrier_off(vlan->dev); netif_carrier_off(vlan->dev);
netif_carrier_off(dev); netif_carrier_off(dev);
/* remove beacon and probe response */
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
kfree_rcu(old_beacon, rcu_head);
if (old_probe_resp)
kfree_rcu(old_probe_resp, rcu_head);
kfree_rcu(old, rcu_head); sta_info_flush(local, sdata);
sta_info_flush(sdata->local, sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
drv_stop_ap(sdata->local, sdata);
/* free all potentially still buffered bcast frames */
local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
skb_queue_purge(&sdata->u.ap.ps.bc_buf);
ieee80211_vif_release_channel(sdata); ieee80211_vif_release_channel(sdata);
return 0; return 0;
...@@ -1933,6 +1954,16 @@ static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) ...@@ -1933,6 +1954,16 @@ 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_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
int rate[IEEE80211_NUM_BANDS])
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
memcpy(sdata->vif.bss_conf.mcast_rate, rate, sizeof(rate));
return 0;
}
static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
{ {
struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_local *local = wiphy_priv(wiphy);
...@@ -1971,45 +2002,65 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) ...@@ -1971,45 +2002,65 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
} }
static int ieee80211_set_tx_power(struct wiphy *wiphy, static int ieee80211_set_tx_power(struct wiphy *wiphy,
struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, int mbm) enum nl80211_tx_power_setting type, int mbm)
{ {
struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_channel *chan = local->_oper_channel; struct ieee80211_sub_if_data *sdata;
u32 changes = 0;
/* FIXME */ if (wdev) {
if (local->use_chanctx) sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
return -EOPNOTSUPP;
switch (type) {
case NL80211_TX_POWER_AUTOMATIC:
sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
break;
case NL80211_TX_POWER_LIMITED:
case NL80211_TX_POWER_FIXED:
if (mbm < 0 || (mbm % 100))
return -EOPNOTSUPP;
sdata->user_power_level = MBM_TO_DBM(mbm);
break;
}
ieee80211_recalc_txpower(sdata);
return 0;
}
switch (type) { switch (type) {
case NL80211_TX_POWER_AUTOMATIC: case NL80211_TX_POWER_AUTOMATIC:
local->user_power_level = -1; local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
break; break;
case NL80211_TX_POWER_LIMITED: case NL80211_TX_POWER_LIMITED:
if (mbm < 0 || (mbm % 100))
return -EOPNOTSUPP;
local->user_power_level = MBM_TO_DBM(mbm);
break;
case NL80211_TX_POWER_FIXED: case NL80211_TX_POWER_FIXED:
if (mbm < 0 || (mbm % 100)) if (mbm < 0 || (mbm % 100))
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* TODO: move to cfg80211 when it knows the channel */
if (MBM_TO_DBM(mbm) > chan->max_power)
return -EINVAL;
local->user_power_level = MBM_TO_DBM(mbm); local->user_power_level = MBM_TO_DBM(mbm);
break; break;
} }
ieee80211_hw_config(local, changes); mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list)
sdata->user_power_level = local->user_power_level;
list_for_each_entry(sdata, &local->interfaces, list)
ieee80211_recalc_txpower(sdata);
mutex_unlock(&local->iflist_mtx);
return 0; return 0;
} }
static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm) static int ieee80211_get_tx_power(struct wiphy *wiphy,
struct wireless_dev *wdev,
int *dbm)
{ {
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);
*dbm = local->hw.conf.power_level; if (!local->use_chanctx)
*dbm = local->hw.conf.power_level;
else
*dbm = sdata->vif.bss_conf.txpower;
return 0; return 0;
} }
...@@ -2341,13 +2392,22 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, ...@@ -2341,13 +2392,22 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
list_add_tail(&roc->list, &local->roc_list); list_add_tail(&roc->list, &local->roc_list);
/* /*
* cookie is either the roc (for normal roc) * cookie is either the roc cookie (for normal roc)
* or the SKB (for mgmt TX) * or the SKB (for mgmt TX)
*/ */
if (txskb) if (!txskb) {
/* local->mtx protects this */
local->roc_cookie_counter++;
roc->cookie = local->roc_cookie_counter;
/* wow, you wrapped 64 bits ... more likely a bug */
if (WARN_ON(roc->cookie == 0)) {
roc->cookie = 1;
local->roc_cookie_counter++;
}
*cookie = roc->cookie;
} else {
*cookie = (unsigned long)txskb; *cookie = (unsigned long)txskb;
else }
*cookie = (unsigned long)roc;
return 0; return 0;
} }
...@@ -2382,7 +2442,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local, ...@@ -2382,7 +2442,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
struct ieee80211_roc_work *dep, *tmp2; struct ieee80211_roc_work *dep, *tmp2;
list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) { list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) {
if (!mgmt_tx && (unsigned long)dep != cookie) if (!mgmt_tx && dep->cookie != cookie)
continue; continue;
else if (mgmt_tx && dep->mgmt_tx_cookie != cookie) else if (mgmt_tx && dep->mgmt_tx_cookie != cookie)
continue; continue;
...@@ -2394,7 +2454,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local, ...@@ -2394,7 +2454,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
return 0; return 0;
} }
if (!mgmt_tx && (unsigned long)roc != cookie) if (!mgmt_tx && roc->cookie != cookie)
continue; continue;
else if (mgmt_tx && roc->mgmt_tx_cookie != cookie) else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
continue; continue;
...@@ -3130,6 +3190,7 @@ struct cfg80211_ops mac80211_config_ops = { ...@@ -3130,6 +3190,7 @@ struct cfg80211_ops mac80211_config_ops = {
.disassoc = ieee80211_disassoc, .disassoc = ieee80211_disassoc,
.join_ibss = ieee80211_join_ibss, .join_ibss = ieee80211_join_ibss,
.leave_ibss = ieee80211_leave_ibss, .leave_ibss = ieee80211_leave_ibss,
.set_mcast_rate = ieee80211_set_mcast_rate,
.set_wiphy_params = ieee80211_set_wiphy_params, .set_wiphy_params = ieee80211_set_wiphy_params,
.set_tx_power = ieee80211_set_tx_power, .set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power, .get_tx_power = ieee80211_get_tx_power,
......
...@@ -173,6 +173,8 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, ...@@ -173,6 +173,8 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
ctx->refcount++; ctx->refcount++;
ieee80211_recalc_txpower(sdata);
return 0; return 0;
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/if.h> #include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
...@@ -168,6 +169,29 @@ IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz, ...@@ -168,6 +169,29 @@ IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz,
IEEE80211_IF_FILE(flags, flags, HEX); IEEE80211_IF_FILE(flags, flags, HEX);
IEEE80211_IF_FILE(state, state, LHEX); IEEE80211_IF_FILE(state, state, LHEX);
IEEE80211_IF_FILE(channel_type, vif.bss_conf.channel_type, DEC); IEEE80211_IF_FILE(channel_type, vif.bss_conf.channel_type, DEC);
IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC);
IEEE80211_IF_FILE(ap_power_level, ap_power_level, DEC);
IEEE80211_IF_FILE(user_power_level, user_power_level, DEC);
static ssize_t
ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data *sdata,
char *buf, int buflen)
{
int len;
len = scnprintf(buf, buflen, "AC queues: VO:%d VI:%d BE:%d BK:%d\n",
sdata->vif.hw_queue[IEEE80211_AC_VO],
sdata->vif.hw_queue[IEEE80211_AC_VI],
sdata->vif.hw_queue[IEEE80211_AC_BE],
sdata->vif.hw_queue[IEEE80211_AC_BK]);
if (sdata->vif.type == NL80211_IFTYPE_AP)
len += scnprintf(buf + len, buflen - len, "cab queue: %d\n",
sdata->vif.cab_queue);
return len;
}
__IEEE80211_IF_FILE(hw_queues, NULL);
/* STA attributes */ /* STA attributes */
IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
...@@ -245,27 +269,6 @@ static ssize_t ieee80211_if_fmt_tkip_mic_test( ...@@ -245,27 +269,6 @@ static ssize_t ieee80211_if_fmt_tkip_mic_test(
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static int hwaddr_aton(const char *txt, u8 *addr)
{
int i;
for (i = 0; i < ETH_ALEN; i++) {
int a, b;
a = hex_to_bin(*txt++);
if (a < 0)
return -1;
b = hex_to_bin(*txt++);
if (b < 0)
return -1;
*addr++ = (a << 4) | b;
if (i < 5 && *txt++ != ':')
return -1;
}
return 0;
}
static ssize_t ieee80211_if_parse_tkip_mic_test( static ssize_t ieee80211_if_parse_tkip_mic_test(
struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
{ {
...@@ -275,13 +278,7 @@ static ssize_t ieee80211_if_parse_tkip_mic_test( ...@@ -275,13 +278,7 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
__le16 fc; __le16 fc;
/* if (!mac_pton(buf, addr))
* Assume colon-delimited MAC address with possible white space
* following.
*/
if (buflen < 3 * ETH_ALEN - 1)
return -EINVAL;
if (hwaddr_aton(buf, addr) < 0)
return -EINVAL; return -EINVAL;
if (!ieee80211_sdata_running(sdata)) if (!ieee80211_sdata_running(sdata))
...@@ -307,13 +304,16 @@ static ssize_t ieee80211_if_parse_tkip_mic_test( ...@@ -307,13 +304,16 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
fc |= cpu_to_le16(IEEE80211_FCTL_TODS); fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
/* BSSID SA DA */ /* BSSID SA DA */
if (sdata->vif.bss_conf.bssid == NULL) { mutex_lock(&sdata->u.mgd.mtx);
if (!sdata->u.mgd.associated) {
mutex_unlock(&sdata->u.mgd.mtx);
dev_kfree_skb(skb); dev_kfree_skb(skb);
return -ENOTCONN; return -ENOTCONN;
} }
memcpy(hdr->addr1, sdata->vif.bss_conf.bssid, ETH_ALEN); memcpy(hdr->addr1, sdata->u.mgd.associated->bssid, ETH_ALEN);
memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
memcpy(hdr->addr3, addr, ETH_ALEN); memcpy(hdr->addr3, addr, ETH_ALEN);
mutex_unlock(&sdata->u.mgd.mtx);
break; break;
default: default:
dev_kfree_skb(skb); dev_kfree_skb(skb);
...@@ -443,7 +443,7 @@ static ssize_t ieee80211_if_parse_tsf( ...@@ -443,7 +443,7 @@ static ssize_t ieee80211_if_parse_tsf(
} }
ret = kstrtoull(buf, 10, &tsf); ret = kstrtoull(buf, 10, &tsf);
if (ret < 0) if (ret < 0)
return -EINVAL; return ret;
if (tsf_is_delta) if (tsf_is_delta)
tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf; tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf;
if (local->ops->set_tsf) { if (local->ops->set_tsf) {
...@@ -531,6 +531,7 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata) ...@@ -531,6 +531,7 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(rc_rateidx_mask_5ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
DEBUGFS_ADD(hw_queues);
} }
static void add_sta_files(struct ieee80211_sub_if_data *sdata) static void add_sta_files(struct ieee80211_sub_if_data *sdata)
...@@ -632,6 +633,9 @@ static void add_files(struct ieee80211_sub_if_data *sdata) ...@@ -632,6 +633,9 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(flags); DEBUGFS_ADD(flags);
DEBUGFS_ADD(state); DEBUGFS_ADD(state);
DEBUGFS_ADD(channel_type); DEBUGFS_ADD(channel_type);
DEBUGFS_ADD(txpower);
DEBUGFS_ADD(user_power_level);
DEBUGFS_ADD(ap_power_level);
if (sdata->vif.type != NL80211_IFTYPE_MONITOR) if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
add_common_files(sdata); add_common_files(sdata);
......
...@@ -936,4 +936,39 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, ...@@ -936,4 +936,39 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
trace_drv_return_void(local); trace_drv_return_void(local);
} }
static inline int drv_start_ap(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
int ret = 0;
check_sdata_in_driver(sdata);
trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf);
if (local->ops->start_ap)
ret = local->ops->start_ap(&local->hw, &sdata->vif);
trace_drv_return_int(local, ret);
return ret;
}
static inline void drv_stop_ap(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
check_sdata_in_driver(sdata);
trace_drv_stop_ap(local, sdata);
if (local->ops->stop_ap)
local->ops->stop_ap(&local->hw, &sdata->vif);
trace_drv_return_void(local);
}
static inline void drv_restart_complete(struct ieee80211_local *local)
{
might_sleep();
trace_drv_restart_complete(local);
if (local->ops->restart_complete)
local->ops->restart_complete(&local->hw);
trace_drv_return_void(local);
}
#endif /* __MAC80211_DRIVER_OPS */ #endif /* __MAC80211_DRIVER_OPS */
...@@ -56,6 +56,9 @@ struct ieee80211_local; ...@@ -56,6 +56,9 @@ struct ieee80211_local;
#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024)) #define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x)) #define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
/* power level hasn't been configured (or set to automatic) */
#define IEEE80211_UNSET_POWER_LEVEL INT_MIN
/* /*
* Some APs experience problems when working with U-APSD. Decrease the * Some APs experience problems when working with U-APSD. Decrease the
* probability of that happening by using legacy mode for all ACs but VO. * probability of that happening by using legacy mode for all ACs but VO.
...@@ -353,7 +356,7 @@ struct ieee80211_roc_work { ...@@ -353,7 +356,7 @@ struct ieee80211_roc_work {
u32 duration, req_duration; u32 duration, req_duration;
struct sk_buff *frame; struct sk_buff *frame;
u64 mgmt_tx_cookie; u64 cookie, mgmt_tx_cookie;
}; };
/* flags used in struct ieee80211_if_managed.flags */ /* flags used in struct ieee80211_if_managed.flags */
...@@ -470,6 +473,8 @@ struct ieee80211_if_managed { ...@@ -470,6 +473,8 @@ struct ieee80211_if_managed {
u8 use_4addr; u8 use_4addr;
u8 p2p_noa_index;
/* Signal strength from the last Beacon frame in the current BSS. */ /* Signal strength from the last Beacon frame in the current BSS. */
int last_beacon_signal; int last_beacon_signal;
...@@ -743,6 +748,9 @@ struct ieee80211_sub_if_data { ...@@ -743,6 +748,9 @@ struct ieee80211_sub_if_data {
u8 needed_rx_chains; u8 needed_rx_chains;
enum ieee80211_smps_mode smps_mode; enum ieee80211_smps_mode smps_mode;
int user_power_level; /* in dBm */
int ap_power_level; /* in dBm */
/* /*
* AP this belongs to: self in AP mode and * AP this belongs to: self in AP mode and
* corresponding AP in VLAN mode, NULL for * corresponding AP in VLAN mode, NULL for
...@@ -1117,8 +1125,7 @@ struct ieee80211_local { ...@@ -1117,8 +1125,7 @@ struct ieee80211_local {
int dynamic_ps_user_timeout; int dynamic_ps_user_timeout;
bool disable_dynamic_ps; bool disable_dynamic_ps;
int user_power_level; /* in dBm */ int user_power_level; /* in dBm, for all interfaces */
int ap_power_level; /* in dBm */
enum ieee80211_smps_mode smps_mode; enum ieee80211_smps_mode smps_mode;
...@@ -1137,6 +1144,7 @@ struct ieee80211_local { ...@@ -1137,6 +1144,7 @@ struct ieee80211_local {
struct list_head roc_list; struct list_head roc_list;
struct work_struct hw_roc_start, hw_roc_done; struct work_struct hw_roc_start, hw_roc_done;
unsigned long hw_roc_start_time; unsigned long hw_roc_start_time;
u64 roc_cookie_counter;
struct idr ack_status_frames; struct idr ack_status_frames;
spinlock_t ack_status_lock; spinlock_t ack_status_lock;
...@@ -1365,6 +1373,9 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, ...@@ -1365,6 +1373,9 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up); int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up);
void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata); void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata);
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
{ {
return test_bit(SDATA_STATE_RUNNING, &sdata->state); return test_bit(SDATA_STATE_RUNNING, &sdata->state);
......
...@@ -42,6 +42,41 @@ ...@@ -42,6 +42,41 @@
* by either the RTNL, the iflist_mtx or RCU. * by either the RTNL, the iflist_mtx or RCU.
*/ */
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_chanctx_conf *chanctx_conf;
int power;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf) {
rcu_read_unlock();
return false;
}
power = chanctx_conf->channel->max_power;
rcu_read_unlock();
if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
power = min(power, sdata->user_power_level);
if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
power = min(power, sdata->ap_power_level);
if (power != sdata->vif.bss_conf.txpower) {
sdata->vif.bss_conf.txpower = power;
ieee80211_hw_config(sdata->local, 0);
return true;
}
return false;
}
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
{
if (__ieee80211_recalc_txpower(sdata))
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
}
static u32 ieee80211_idle_off(struct ieee80211_local *local, static u32 ieee80211_idle_off(struct ieee80211_local *local,
const char *reason) const char *reason)
...@@ -744,31 +779,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ...@@ -744,31 +779,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
/* APs need special treatment */ /* APs need special treatment */
if (sdata->vif.type == NL80211_IFTYPE_AP) { if (sdata->vif.type == NL80211_IFTYPE_AP) {
struct ieee80211_sub_if_data *vlan, *tmpsdata; struct ieee80211_sub_if_data *vlan, *tmpsdata;
struct beacon_data *old_beacon =
rtnl_dereference(sdata->u.ap.beacon);
struct probe_resp *old_probe_resp =
rtnl_dereference(sdata->u.ap.probe_resp);
/* sdata_running will return false, so this will disable */
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_BEACON_ENABLED);
/* remove beacon and probe response */
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
synchronize_rcu();
kfree(old_beacon);
kfree(old_probe_resp);
/* down all dependent devices, that is VLANs */ /* down all dependent devices, that is VLANs */
list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans, list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
u.vlan.list) u.vlan.list)
dev_close(vlan->dev); dev_close(vlan->dev);
WARN_ON(!list_empty(&sdata->u.ap.vlans)); WARN_ON(!list_empty(&sdata->u.ap.vlans));
/* free all potentially still buffered bcast frames */
local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
skb_queue_purge(&sdata->u.ap.ps.bc_buf);
} else if (sdata->vif.type == NL80211_IFTYPE_STATION) { } else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
ieee80211_mgd_stop(sdata); ieee80211_mgd_stop(sdata);
} }
...@@ -1529,6 +1545,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ...@@ -1529,6 +1545,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ieee80211_set_default_queues(sdata); ieee80211_set_default_queues(sdata);
sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
sdata->user_power_level = local->user_power_level;
/* setup type-dependent data */ /* setup type-dependent data */
ieee80211_setup_sdata(sdata, type); ieee80211_setup_sdata(sdata, type);
......
...@@ -95,11 +95,13 @@ static void ieee80211_reconfig_filter(struct work_struct *work) ...@@ -95,11 +95,13 @@ static void ieee80211_reconfig_filter(struct work_struct *work)
static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
{ {
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *chan; struct ieee80211_channel *chan;
u32 changed = 0; u32 changed = 0;
int power; int power;
enum nl80211_channel_type channel_type; enum nl80211_channel_type channel_type;
u32 offchannel_flag; u32 offchannel_flag;
bool scanning = false;
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
if (local->scan_channel) { if (local->scan_channel) {
...@@ -146,16 +148,18 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) ...@@ -146,16 +148,18 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
changed |= IEEE80211_CONF_CHANGE_SMPS; changed |= IEEE80211_CONF_CHANGE_SMPS;
} }
if (test_bit(SCAN_SW_SCANNING, &local->scanning) || scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) || test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
test_bit(SCAN_HW_SCANNING, &local->scanning) || test_bit(SCAN_HW_SCANNING, &local->scanning);
!local->ap_power_level) power = chan->max_power;
power = chan->max_power;
else
power = min(chan->max_power, local->ap_power_level);
if (local->user_power_level >= 0) rcu_read_lock();
power = min(power, local->user_power_level); list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (!rcu_access_pointer(sdata->vif.chanctx_conf))
continue;
power = min(power, sdata->vif.bss_conf.txpower);
}
rcu_read_unlock();
if (local->hw.conf.power_level != power) { if (local->hw.conf.power_level != power) {
changed |= IEEE80211_CONF_CHANGE_POWER; changed |= IEEE80211_CONF_CHANGE_POWER;
...@@ -600,7 +604,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, ...@@ -600,7 +604,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
NL80211_FEATURE_SAE | NL80211_FEATURE_SAE |
NL80211_FEATURE_HT_IBSS; NL80211_FEATURE_HT_IBSS |
NL80211_FEATURE_VIF_TXPOWER;
if (!ops->hw_scan) if (!ops->hw_scan)
wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
...@@ -633,7 +638,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, ...@@ -633,7 +638,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS | local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_GI |
IEEE80211_RADIOTAP_MCS_HAVE_BW; IEEE80211_RADIOTAP_MCS_HAVE_BW;
local->user_power_level = -1; local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
INIT_LIST_HEAD(&local->interfaces); INIT_LIST_HEAD(&local->interfaces);
......
...@@ -820,10 +820,10 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, ...@@ -820,10 +820,10 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
cbss->beacon_interval)); cbss->beacon_interval));
} }
static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
const u8 *country_ie, u8 country_ie_len, const u8 *country_ie, u8 country_ie_len,
const u8 *pwr_constr_elem) const u8 *pwr_constr_elem)
{ {
struct ieee80211_country_ie_triplet *triplet; struct ieee80211_country_ie_triplet *triplet;
int chan = ieee80211_frequency_to_channel(channel->center_freq); int chan = ieee80211_frequency_to_channel(channel->center_freq);
...@@ -832,7 +832,7 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, ...@@ -832,7 +832,7 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
/* Invalid IE */ /* Invalid IE */
if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
return; return 0;
triplet = (void *)(country_ie + 3); triplet = (void *)(country_ie + 3);
country_ie_len -= 3; country_ie_len -= 3;
...@@ -873,19 +873,21 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, ...@@ -873,19 +873,21 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
} }
if (!have_chan_pwr) if (!have_chan_pwr)
return; return 0;
new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem); new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem);
if (sdata->local->ap_power_level == new_ap_level) if (sdata->ap_power_level == new_ap_level)
return; return 0;
sdata_info(sdata, sdata_info(sdata,
"Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
new_ap_level, chan_pwr, *pwr_constr_elem, new_ap_level, chan_pwr, *pwr_constr_elem,
sdata->u.mgd.bssid); sdata->u.mgd.bssid);
sdata->local->ap_power_level = new_ap_level; sdata->ap_power_level = new_ap_level;
ieee80211_hw_config(sdata->local, 0); if (__ieee80211_recalc_txpower(sdata))
return BSS_CHANGED_TXPOWER;
return 0;
} }
void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif) void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
...@@ -1363,6 +1365,22 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ...@@ -1363,6 +1365,22 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
if (sdata->vif.p2p) {
u8 noa[2];
int ret;
ret = cfg80211_get_p2p_attr(cbss->information_elements,
cbss->len_information_elements,
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
noa, sizeof(noa));
if (ret >= 2) {
bss_conf->p2p_oppps = noa[1] & 0x80;
bss_conf->p2p_ctwindow = noa[1] & 0x7f;
bss_info_changed |= BSS_CHANGED_P2P_PS;
sdata->u.mgd.p2p_noa_index = noa[0];
}
}
/* just to be sure */ /* just to be sure */
ieee80211_stop_poll(sdata); ieee80211_stop_poll(sdata);
...@@ -1485,11 +1503,14 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ...@@ -1485,11 +1503,14 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_ASSOC; changed |= BSS_CHANGED_ASSOC;
sdata->vif.bss_conf.assoc = false; sdata->vif.bss_conf.assoc = false;
sdata->vif.bss_conf.p2p_ctwindow = 0;
sdata->vif.bss_conf.p2p_oppps = false;
/* on the next assoc, re-program HT parameters */ /* on the next assoc, re-program HT parameters */
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
local->ap_power_level = 0; sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
del_timer_sync(&local->dynamic_ps_timer); del_timer_sync(&local->dynamic_ps_timer);
cancel_work_sync(&local->dynamic_ps_enable_work); cancel_work_sync(&local->dynamic_ps_enable_work);
...@@ -2592,6 +2613,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ...@@ -2592,6 +2613,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
} }
} }
if (sdata->vif.p2p) {
u8 noa[2];
int ret;
ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable,
len - baselen,
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
noa, sizeof(noa));
if (ret >= 2 && sdata->u.mgd.p2p_noa_index != noa[0]) {
bss_conf->p2p_oppps = noa[1] & 0x80;
bss_conf->p2p_ctwindow = noa[1] & 0x7f;
changed |= BSS_CHANGED_P2P_PS;
sdata->u.mgd.p2p_noa_index = noa[0];
/*
* make sure we update all information, the CRC
* mechanism doesn't look at P2P attributes.
*/
ifmgd->beacon_crc_valid = false;
}
}
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;
...@@ -2623,10 +2665,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ...@@ -2623,10 +2665,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (elems.country_elem && elems.pwr_constr_elem && if (elems.country_elem && elems.pwr_constr_elem &&
mgmt->u.probe_resp.capab_info & mgmt->u.probe_resp.capab_info &
cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT))
ieee80211_handle_pwr_constr(sdata, chan, changed |= ieee80211_handle_pwr_constr(sdata, chan,
elems.country_elem, elems.country_elem,
elems.country_elem_len, elems.country_elem_len,
elems.pwr_constr_elem); elems.pwr_constr_elem);
ieee80211_bss_info_change_notify(sdata, changed); ieee80211_bss_info_change_notify(sdata, changed);
} }
...@@ -3660,40 +3702,44 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, ...@@ -3660,40 +3702,44 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
bool tx = !req->local_state_change; bool tx = !req->local_state_change;
bool sent_frame = false;
mutex_lock(&ifmgd->mtx); mutex_lock(&ifmgd->mtx);
if (ifmgd->auth_data) {
ieee80211_destroy_auth_data(sdata, false);
mutex_unlock(&ifmgd->mtx);
return 0;
}
sdata_info(sdata, sdata_info(sdata,
"deauthenticating from %pM by local choice (reason=%d)\n", "deauthenticating from %pM by local choice (reason=%d)\n",
req->bssid, req->reason_code); req->bssid, req->reason_code);
if (ifmgd->associated && if (ifmgd->auth_data) {
ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
req->reason_code, tx, frame_buf);
} else {
drv_mgd_prepare_tx(sdata->local, sdata); drv_mgd_prepare_tx(sdata->local, sdata);
ieee80211_send_deauth_disassoc(sdata, req->bssid, ieee80211_send_deauth_disassoc(sdata, req->bssid,
IEEE80211_STYPE_DEAUTH, IEEE80211_STYPE_DEAUTH,
req->reason_code, tx, req->reason_code, tx,
frame_buf); frame_buf);
ieee80211_destroy_auth_data(sdata, false);
mutex_unlock(&ifmgd->mtx);
sent_frame = tx;
goto out;
} }
if (ifmgd->associated &&
ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
req->reason_code, tx, frame_buf);
sent_frame = tx;
}
mutex_unlock(&ifmgd->mtx); mutex_unlock(&ifmgd->mtx);
__cfg80211_send_deauth(sdata->dev, frame_buf, out:
IEEE80211_DEAUTH_FRAME_LEN);
mutex_lock(&sdata->local->mtx); mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local); ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx); mutex_unlock(&sdata->local->mtx);
if (sent_frame)
__cfg80211_send_deauth(sdata->dev, frame_buf,
IEEE80211_DEAUTH_FRAME_LEN);
return 0; return 0;
} }
......
...@@ -204,7 +204,7 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc) ...@@ -204,7 +204,7 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
roc->frame = NULL; roc->frame = NULL;
} }
} else { } else {
cfg80211_ready_on_channel(&roc->sdata->wdev, (unsigned long)roc, cfg80211_ready_on_channel(&roc->sdata->wdev, roc->cookie,
roc->chan, roc->chan_type, roc->chan, roc->chan_type,
roc->req_duration, GFP_KERNEL); roc->req_duration, GFP_KERNEL);
} }
...@@ -320,9 +320,8 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) ...@@ -320,9 +320,8 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
if (!roc->mgmt_tx_cookie) if (!roc->mgmt_tx_cookie)
cfg80211_remain_on_channel_expired(&roc->sdata->wdev, cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
(unsigned long)roc, roc->cookie, roc->chan,
roc->chan, roc->chan_type, roc->chan_type, GFP_KERNEL);
GFP_KERNEL);
list_for_each_entry_safe(dep, tmp, &roc->dependents, list) list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
ieee80211_roc_notify_destroy(dep); ieee80211_roc_notify_destroy(dep);
......
...@@ -135,6 +135,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ...@@ -135,6 +135,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
ieee80211_bss_info_change_notify(sdata, ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_BEACON_ENABLED); BSS_CHANGED_BEACON_ENABLED);
if (sdata->vif.type == NL80211_IFTYPE_AP &&
rcu_access_pointer(sdata->u.ap.beacon))
drv_stop_ap(local, sdata);
/* the interface is leaving the channel and is removed */ /* the interface is leaving the channel and is removed */
ieee80211_vif_release_channel(sdata); ieee80211_vif_release_channel(sdata);
drv_remove_interface(local, sdata); drv_remove_interface(local, sdata);
......
...@@ -54,8 +54,7 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, ...@@ -54,8 +54,7 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
return skb; return skb;
} }
static inline int should_drop_frame(struct sk_buff *skb, static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len)
int present_fcs_len)
{ {
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
...@@ -130,15 +129,14 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, ...@@ -130,15 +129,14 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
(1 << IEEE80211_RADIOTAP_RX_FLAGS)); (1 << IEEE80211_RADIOTAP_RX_FLAGS));
rthdr->it_len = cpu_to_le16(rtap_len); rthdr->it_len = cpu_to_le16(rtap_len);
pos = (unsigned char *)(rthdr+1); pos = (unsigned char *)(rthdr + 1);
/* the order of the following fields is important */ /* the order of the following fields is important */
/* IEEE80211_RADIOTAP_TSFT */ /* IEEE80211_RADIOTAP_TSFT */
if (status->flag & RX_FLAG_MACTIME_MPDU) { if (status->flag & RX_FLAG_MACTIME_MPDU) {
put_unaligned_le64(status->mactime, pos); put_unaligned_le64(status->mactime, pos);
rthdr->it_present |= rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
pos += 8; pos += 8;
} }
...@@ -374,7 +372,6 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, ...@@ -374,7 +372,6 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
return origskb; return origskb;
} }
static void ieee80211_parse_qos(struct ieee80211_rx_data *rx) static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
{ {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
...@@ -481,8 +478,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb) ...@@ -481,8 +478,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data; struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data;
struct ieee80211_mmie *mmie; struct ieee80211_mmie *mmie;
if (skb->len < 24 + sizeof(*mmie) || if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))
!is_multicast_ether_addr(hdr->da))
return -1; return -1;
if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr)) if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr))
...@@ -497,9 +493,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb) ...@@ -497,9 +493,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
return le16_to_cpu(mmie->key_id); return le16_to_cpu(mmie->key_id);
} }
static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
static ieee80211_rx_result
ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
{ {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
char *dev_addr = rx->sdata->vif.addr; char *dev_addr = rx->sdata->vif.addr;
...@@ -507,7 +501,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) ...@@ -507,7 +501,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control)) { if (ieee80211_is_data(hdr->frame_control)) {
if (is_multicast_ether_addr(hdr->addr1)) { if (is_multicast_ether_addr(hdr->addr1)) {
if (ieee80211_has_tods(hdr->frame_control) || if (ieee80211_has_tods(hdr->frame_control) ||
!ieee80211_has_fromds(hdr->frame_control)) !ieee80211_has_fromds(hdr->frame_control))
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
if (ether_addr_equal(hdr->addr3, dev_addr)) if (ether_addr_equal(hdr->addr3, dev_addr))
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
...@@ -539,7 +533,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) ...@@ -539,7 +533,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
mgmt = (struct ieee80211_mgmt *)hdr; mgmt = (struct ieee80211_mgmt *)hdr;
category = mgmt->u.action.category; category = mgmt->u.action.category;
if (category != WLAN_CATEGORY_MESH_ACTION && if (category != WLAN_CATEGORY_MESH_ACTION &&
category != WLAN_CATEGORY_SELF_PROTECTED) category != WLAN_CATEGORY_SELF_PROTECTED)
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
return RX_CONTINUE; return RX_CONTINUE;
} }
...@@ -551,7 +545,6 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) ...@@ -551,7 +545,6 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
return RX_CONTINUE; return RX_CONTINUE;
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
} }
return RX_CONTINUE; return RX_CONTINUE;
...@@ -575,7 +568,6 @@ static inline u16 seq_sub(u16 sq1, u16 sq2) ...@@ -575,7 +568,6 @@ static inline u16 seq_sub(u16 sq1, u16 sq2)
return (sq1 - sq2) & SEQ_MASK; return (sq1 - sq2) & SEQ_MASK;
} }
static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
struct tid_ampdu_rx *tid_agg_rx, struct tid_ampdu_rx *tid_agg_rx,
int index) int index)
...@@ -1585,18 +1577,15 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) ...@@ -1585,18 +1577,15 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
return RX_CONTINUE; return RX_CONTINUE;
} }
static int static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
{ {
if (unlikely(!rx->sta || if (unlikely(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))
!test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))
return -EACCES; return -EACCES;
return 0; return 0;
} }
static int static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
{ {
struct sk_buff *skb = rx->skb; struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
...@@ -1618,8 +1607,7 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) ...@@ -1618,8 +1607,7 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
return 0; return 0;
} }
static int static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
{ {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
...@@ -2003,7 +1991,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) ...@@ -2003,7 +1991,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
} else { } else {
/* unable to resolve next hop */ /* unable to resolve next hop */
mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3, mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3,
0, reason, fwd_hdr->addr2, sdata); 0, reason, fwd_hdr->addr2, sdata);
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route); IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
kfree_skb(fwd_skb); kfree_skb(fwd_skb);
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
...@@ -2212,7 +2200,7 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) ...@@ -2212,7 +2200,7 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
cfg80211_report_obss_beacon(rx->local->hw.wiphy, cfg80211_report_obss_beacon(rx->local->hw.wiphy,
rx->skb->data, rx->skb->len, rx->skb->data, rx->skb->len,
status->freq, sig, GFP_ATOMIC); status->freq, sig);
rx->flags |= IEEE80211_RX_BEACON_REPORTED; rx->flags |= IEEE80211_RX_BEACON_REPORTED;
} }
...@@ -2412,7 +2400,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ...@@ -2412,7 +2400,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (!ieee80211_vif_is_mesh(&sdata->vif)) if (!ieee80211_vif_is_mesh(&sdata->vif))
break; break;
if (mesh_action_is_path_sel(mgmt) && if (mesh_action_is_path_sel(mgmt) &&
(!mesh_path_sel_is_hwmp(sdata))) !mesh_path_sel_is_hwmp(sdata))
break; break;
goto queue; goto queue;
} }
...@@ -2468,7 +2456,6 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) ...@@ -2468,7 +2456,6 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
return RX_QUEUED; return RX_QUEUED;
} }
return RX_CONTINUE; return RX_CONTINUE;
} }
......
...@@ -325,6 +325,75 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band ...@@ -325,6 +325,75 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
} }
static void ieee80211_report_used_skb(struct ieee80211_local *local,
struct sk_buff *skb, bool dropped)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (void *)skb->data;
bool acked = info->flags & IEEE80211_TX_STAT_ACK;
if (dropped)
acked = false;
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
struct ieee80211_sub_if_data *sdata = NULL;
struct ieee80211_sub_if_data *iter_sdata;
u64 cookie = (unsigned long)skb;
rcu_read_lock();
if (skb->dev) {
list_for_each_entry_rcu(iter_sdata, &local->interfaces,
list) {
if (!iter_sdata->dev)
continue;
if (skb->dev == iter_sdata->dev) {
sdata = iter_sdata;
break;
}
}
} else {
sdata = rcu_dereference(local->p2p_sdata);
}
if (!sdata)
skb->dev = NULL;
else if (ieee80211_is_nullfunc(hdr->frame_control) ||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
cfg80211_probe_status(sdata->dev, hdr->addr1,
cookie, acked, GFP_ATOMIC);
} else {
cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
skb->len, acked, GFP_ATOMIC);
}
rcu_read_unlock();
}
if (unlikely(info->ack_frame_id)) {
struct sk_buff *ack_skb;
unsigned long flags;
spin_lock_irqsave(&local->ack_status_lock, flags);
ack_skb = idr_find(&local->ack_status_frames,
info->ack_frame_id);
if (ack_skb)
idr_remove(&local->ack_status_frames,
info->ack_frame_id);
spin_unlock_irqrestore(&local->ack_status_lock, flags);
if (ack_skb) {
if (!dropped) {
/* consumes ack_skb */
skb_complete_wifi_ack(ack_skb, acked);
} else {
dev_kfree_skb_any(ack_skb);
}
}
}
}
/* /*
* Use a static threshold for now, best value to be determined * Use a static threshold for now, best value to be determined
* by testing ... * by testing ...
...@@ -516,62 +585,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -516,62 +585,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
msecs_to_jiffies(10)); msecs_to_jiffies(10));
} }
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { ieee80211_report_used_skb(local, skb, false);
u64 cookie = (unsigned long)skb;
bool found = false;
acked = info->flags & IEEE80211_TX_STAT_ACK;
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (!sdata->dev)
continue;
if (skb->dev != sdata->dev)
continue;
found = true;
break;
}
if (!skb->dev) {
sdata = rcu_dereference(local->p2p_sdata);
if (sdata)
found = true;
}
if (!found)
skb->dev = NULL;
else if (ieee80211_is_nullfunc(hdr->frame_control) ||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
cfg80211_probe_status(sdata->dev, hdr->addr1,
cookie, acked, GFP_ATOMIC);
} else {
cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
skb->len, acked, GFP_ATOMIC);
}
rcu_read_unlock();
}
if (unlikely(info->ack_frame_id)) {
struct sk_buff *ack_skb;
unsigned long flags;
spin_lock_irqsave(&local->ack_status_lock, flags);
ack_skb = idr_find(&local->ack_status_frames,
info->ack_frame_id);
if (ack_skb)
idr_remove(&local->ack_status_frames,
info->ack_frame_id);
spin_unlock_irqrestore(&local->ack_status_lock, flags);
/* consumes ack_skb */
if (ack_skb)
skb_complete_wifi_ack(ack_skb,
info->flags & IEEE80211_TX_STAT_ACK);
}
/* this was a transmitted frame, but now we want to reuse it */ /* this was a transmitted frame, but now we want to reuse it */
skb_orphan(skb); skb_orphan(skb);
...@@ -647,25 +661,8 @@ EXPORT_SYMBOL(ieee80211_report_low_ack); ...@@ -647,25 +661,8 @@ EXPORT_SYMBOL(ieee80211_report_low_ack);
void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb) void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb)
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
if (unlikely(info->ack_frame_id)) {
struct sk_buff *ack_skb;
unsigned long flags;
spin_lock_irqsave(&local->ack_status_lock, flags);
ack_skb = idr_find(&local->ack_status_frames,
info->ack_frame_id);
if (ack_skb)
idr_remove(&local->ack_status_frames,
info->ack_frame_id);
spin_unlock_irqrestore(&local->ack_status_lock, flags);
/* consumes ack_skb */
if (ack_skb)
dev_kfree_skb_any(ack_skb);
}
ieee80211_report_used_skb(local, skb, true);
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
} }
EXPORT_SYMBOL(ieee80211_free_txskb); EXPORT_SYMBOL(ieee80211_free_txskb);
...@@ -342,6 +342,9 @@ TRACE_EVENT(drv_bss_info_changed, ...@@ -342,6 +342,9 @@ TRACE_EVENT(drv_bss_info_changed,
__field(bool, ps); __field(bool, ps);
__dynamic_array(u8, ssid, info->ssid_len); __dynamic_array(u8, ssid, info->ssid_len);
__field(bool, hidden_ssid); __field(bool, hidden_ssid);
__field(int, txpower)
__field(u8, p2p_ctwindow)
__field(bool, p2p_oppps)
), ),
TP_fast_assign( TP_fast_assign(
...@@ -376,6 +379,9 @@ TRACE_EVENT(drv_bss_info_changed, ...@@ -376,6 +379,9 @@ TRACE_EVENT(drv_bss_info_changed,
__entry->ps = info->ps; __entry->ps = info->ps;
memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len); memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
__entry->hidden_ssid = info->hidden_ssid; __entry->hidden_ssid = info->hidden_ssid;
__entry->txpower = info->txpower;
__entry->p2p_ctwindow = info->p2p_ctwindow;
__entry->p2p_oppps = info->p2p_oppps;
), ),
TP_printk( TP_printk(
...@@ -1043,34 +1049,6 @@ DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel, ...@@ -1043,34 +1049,6 @@ DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel,
TP_ARGS(local) TP_ARGS(local)
); );
TRACE_EVENT(drv_offchannel_tx,
TP_PROTO(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
unsigned int wait),
TP_ARGS(local, skb, chan, channel_type, wait),
TP_STRUCT__entry(
LOCAL_ENTRY
__field(int, center_freq)
__field(int, channel_type)
__field(unsigned int, wait)
),
TP_fast_assign(
LOCAL_ASSIGN;
__entry->center_freq = chan->center_freq;
__entry->channel_type = channel_type;
__entry->wait = wait;
),
TP_printk(
LOCAL_PR_FMT " freq:%dMHz, wait:%dms",
LOCAL_PR_ARG, __entry->center_freq, __entry->wait
)
);
TRACE_EVENT(drv_set_ringparam, TRACE_EVENT(drv_set_ringparam,
TP_PROTO(struct ieee80211_local *local, u32 tx, u32 rx), TP_PROTO(struct ieee80211_local *local, u32 tx, u32 rx),
...@@ -1396,6 +1374,48 @@ DEFINE_EVENT(local_sdata_chanctx, drv_unassign_vif_chanctx, ...@@ -1396,6 +1374,48 @@ DEFINE_EVENT(local_sdata_chanctx, drv_unassign_vif_chanctx,
TP_ARGS(local, sdata, ctx) TP_ARGS(local, sdata, ctx)
); );
TRACE_EVENT(drv_start_ap,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_conf *info),
TP_ARGS(local, sdata, info),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
__field(u8, dtimper)
__field(u16, bcnint)
__dynamic_array(u8, ssid, info->ssid_len);
__field(bool, hidden_ssid);
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
__entry->dtimper = info->dtim_period;
__entry->bcnint = info->beacon_int;
memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
__entry->hidden_ssid = info->hidden_ssid;
),
TP_printk(
LOCAL_PR_FMT VIF_PR_FMT,
LOCAL_PR_ARG, VIF_PR_ARG
)
);
DEFINE_EVENT(local_sdata_evt, drv_stop_ap,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata),
TP_ARGS(local, sdata)
);
DEFINE_EVENT(local_only_evt, drv_restart_complete,
TP_PROTO(struct ieee80211_local *local),
TP_ARGS(local)
);
/* /*
* Tracing for API calls that drivers call. * Tracing for API calls that drivers call.
*/ */
......
...@@ -2089,6 +2089,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, ...@@ -2089,6 +2089,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
head_need = max_t(int, 0, head_need); head_need = max_t(int, 0, head_need);
if (ieee80211_skb_resize(sdata, skb, head_need, true)) { if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
ieee80211_free_txskb(&local->hw, skb); ieee80211_free_txskb(&local->hw, skb);
skb = NULL;
goto fail_rcu; goto fail_rcu;
} }
} }
......
...@@ -512,7 +512,7 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw) ...@@ -512,7 +512,7 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw)
EXPORT_SYMBOL(ieee80211_wake_queues); EXPORT_SYMBOL(ieee80211_wake_queues);
void ieee80211_iterate_active_interfaces( void ieee80211_iterate_active_interfaces(
struct ieee80211_hw *hw, struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac, void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif), struct ieee80211_vif *vif),
void *data) void *data)
...@@ -530,6 +530,9 @@ void ieee80211_iterate_active_interfaces( ...@@ -530,6 +530,9 @@ void ieee80211_iterate_active_interfaces(
default: default:
break; break;
} }
if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
continue;
if (ieee80211_sdata_running(sdata)) if (ieee80211_sdata_running(sdata))
iterator(data, sdata->vif.addr, iterator(data, sdata->vif.addr,
&sdata->vif); &sdata->vif);
...@@ -537,7 +540,9 @@ void ieee80211_iterate_active_interfaces( ...@@ -537,7 +540,9 @@ void ieee80211_iterate_active_interfaces(
sdata = rcu_dereference_protected(local->monitor_sdata, sdata = rcu_dereference_protected(local->monitor_sdata,
lockdep_is_held(&local->iflist_mtx)); lockdep_is_held(&local->iflist_mtx));
if (sdata) if (sdata &&
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif); iterator(data, sdata->vif.addr, &sdata->vif);
mutex_unlock(&local->iflist_mtx); mutex_unlock(&local->iflist_mtx);
...@@ -545,7 +550,7 @@ void ieee80211_iterate_active_interfaces( ...@@ -545,7 +550,7 @@ void ieee80211_iterate_active_interfaces(
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
void ieee80211_iterate_active_interfaces_atomic( void ieee80211_iterate_active_interfaces_atomic(
struct ieee80211_hw *hw, struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac, void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif), struct ieee80211_vif *vif),
void *data) void *data)
...@@ -563,13 +568,18 @@ void ieee80211_iterate_active_interfaces_atomic( ...@@ -563,13 +568,18 @@ void ieee80211_iterate_active_interfaces_atomic(
default: default:
break; break;
} }
if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
continue;
if (ieee80211_sdata_running(sdata)) if (ieee80211_sdata_running(sdata))
iterator(data, sdata->vif.addr, iterator(data, sdata->vif.addr,
&sdata->vif); &sdata->vif);
} }
sdata = rcu_dereference(local->monitor_sdata); sdata = rcu_dereference(local->monitor_sdata);
if (sdata) if (sdata &&
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif); iterator(data, sdata->vif.addr, &sdata->vif);
rcu_read_unlock(); rcu_read_unlock();
...@@ -1412,6 +1422,23 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -1412,6 +1422,23 @@ int ieee80211_reconfig(struct ieee80211_local *local)
WARN_ON(drv_add_chanctx(local, ctx)); WARN_ON(drv_add_chanctx(local, ctx));
mutex_unlock(&local->chanctx_mtx); mutex_unlock(&local->chanctx_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
struct ieee80211_chanctx_conf *ctx_conf;
if (!ieee80211_sdata_running(sdata))
continue;
mutex_lock(&local->chanctx_mtx);
ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
lockdep_is_held(&local->chanctx_mtx));
if (ctx_conf) {
ctx = container_of(ctx_conf, struct ieee80211_chanctx,
conf);
drv_assign_vif_chanctx(local, sdata, ctx);
}
mutex_unlock(&local->chanctx_mtx);
}
/* add STAs back */ /* add STAs back */
mutex_lock(&local->sta_mtx); mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) { list_for_each_entry(sta, &local->sta_list, list) {
...@@ -1452,22 +1479,11 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -1452,22 +1479,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
/* Finally also reconfigure all the BSS information */ /* Finally also reconfigure all the BSS information */
list_for_each_entry(sdata, &local->interfaces, list) { list_for_each_entry(sdata, &local->interfaces, list) {
struct ieee80211_chanctx_conf *ctx_conf;
u32 changed; u32 changed;
if (!ieee80211_sdata_running(sdata)) if (!ieee80211_sdata_running(sdata))
continue; continue;
mutex_lock(&local->chanctx_mtx);
ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
lockdep_is_held(&local->chanctx_mtx));
if (ctx_conf) {
ctx = container_of(ctx_conf, struct ieee80211_chanctx,
conf);
drv_assign_vif_chanctx(local, sdata, ctx);
}
mutex_unlock(&local->chanctx_mtx);
/* common change flags for all interface types */ /* common change flags for all interface types */
changed = BSS_CHANGED_ERP_CTS_PROT | changed = BSS_CHANGED_ERP_CTS_PROT |
BSS_CHANGED_ERP_PREAMBLE | BSS_CHANGED_ERP_PREAMBLE |
...@@ -1478,7 +1494,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -1478,7 +1494,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
BSS_CHANGED_BSSID | BSS_CHANGED_BSSID |
BSS_CHANGED_CQM | BSS_CHANGED_CQM |
BSS_CHANGED_QOS | BSS_CHANGED_QOS |
BSS_CHANGED_IDLE; BSS_CHANGED_IDLE |
BSS_CHANGED_TXPOWER;
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
...@@ -1495,9 +1512,13 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -1495,9 +1512,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
changed |= BSS_CHANGED_SSID; changed |= BSS_CHANGED_SSID;
if (sdata->vif.type == NL80211_IFTYPE_AP) if (sdata->vif.type == NL80211_IFTYPE_AP) {
changed |= BSS_CHANGED_AP_PROBE_RESP; changed |= BSS_CHANGED_AP_PROBE_RESP;
if (rcu_access_pointer(sdata->u.ap.beacon))
drv_start_ap(local, sdata);
}
/* fall through */ /* fall through */
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
changed |= BSS_CHANGED_BEACON | changed |= BSS_CHANGED_BEACON |
...@@ -1594,8 +1615,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -1594,8 +1615,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
* If this is for hw restart things are still running. * If this is for hw restart things are still running.
* We may want to change that later, however. * We may want to change that later, however.
*/ */
if (!local->suspended) if (!local->suspended) {
drv_restart_complete(local);
return 0; return 0;
}
#ifdef CONFIG_PM #ifdef CONFIG_PM
/* first set suspended false, then resuming */ /* first set suspended false, then resuming */
......
...@@ -28,6 +28,7 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, ...@@ -28,6 +28,7 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
if (!err) { if (!err) {
wdev->beacon_interval = 0; wdev->beacon_interval = 0;
wdev->channel = NULL; wdev->channel = NULL;
wdev->ssid_len = 0;
} }
return err; return err;
......
...@@ -326,6 +326,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) ...@@ -326,6 +326,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
mutex_init(&rdev->devlist_mtx); mutex_init(&rdev->devlist_mtx);
mutex_init(&rdev->sched_scan_mtx); mutex_init(&rdev->sched_scan_mtx);
INIT_LIST_HEAD(&rdev->wdev_list); INIT_LIST_HEAD(&rdev->wdev_list);
INIT_LIST_HEAD(&rdev->beacon_registrations);
spin_lock_init(&rdev->beacon_registrations_lock);
spin_lock_init(&rdev->bss_lock); spin_lock_init(&rdev->bss_lock);
INIT_LIST_HEAD(&rdev->bss_list); INIT_LIST_HEAD(&rdev->bss_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
...@@ -698,10 +700,15 @@ EXPORT_SYMBOL(wiphy_unregister); ...@@ -698,10 +700,15 @@ EXPORT_SYMBOL(wiphy_unregister);
void cfg80211_dev_free(struct cfg80211_registered_device *rdev) void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
{ {
struct cfg80211_internal_bss *scan, *tmp; struct cfg80211_internal_bss *scan, *tmp;
struct cfg80211_beacon_registration *reg, *treg;
rfkill_destroy(rdev->rfkill); rfkill_destroy(rdev->rfkill);
mutex_destroy(&rdev->mtx); mutex_destroy(&rdev->mtx);
mutex_destroy(&rdev->devlist_mtx); mutex_destroy(&rdev->devlist_mtx);
mutex_destroy(&rdev->sched_scan_mtx); mutex_destroy(&rdev->sched_scan_mtx);
list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) {
list_del(&reg->list);
kfree(reg);
}
list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
cfg80211_put_bss(&scan->pub); cfg80211_put_bss(&scan->pub);
kfree(rdev); kfree(rdev);
......
...@@ -55,7 +55,8 @@ struct cfg80211_registered_device { ...@@ -55,7 +55,8 @@ struct cfg80211_registered_device {
int opencount; /* also protected by devlist_mtx */ int opencount; /* also protected by devlist_mtx */
wait_queue_head_t dev_wait; wait_queue_head_t dev_wait;
u32 ap_beacons_nlportid; struct list_head beacon_registrations;
spinlock_t beacon_registrations_lock;
/* protected by RTNL only */ /* protected by RTNL only */
int num_running_ifaces; int num_running_ifaces;
...@@ -260,6 +261,10 @@ enum cfg80211_chan_mode { ...@@ -260,6 +261,10 @@ enum cfg80211_chan_mode {
CHAN_MODE_EXCLUSIVE, CHAN_MODE_EXCLUSIVE,
}; };
struct cfg80211_beacon_registration {
struct list_head list;
u32 nlportid;
};
/* free object */ /* free object */
extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
......
...@@ -1110,6 +1110,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag ...@@ -1110,6 +1110,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
goto nla_put_failure; goto nla_put_failure;
} }
CMD(start_p2p_device, START_P2P_DEVICE); CMD(start_p2p_device, START_P2P_DEVICE);
CMD(set_mcast_rate, SET_MCAST_RATE);
#ifdef CONFIG_NL80211_TESTMODE #ifdef CONFIG_NL80211_TESTMODE
CMD(testmode_cmd, TESTMODE); CMD(testmode_cmd, TESTMODE);
...@@ -1516,10 +1517,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) ...@@ -1516,10 +1517,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
result = 0; result = 0;
mutex_lock(&rdev->mtx); mutex_lock(&rdev->mtx);
} else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) } else
wdev = netdev->ieee80211_ptr; wdev = netdev->ieee80211_ptr;
else
wdev = NULL;
/* /*
* end workaround code, by now the rdev is available * end workaround code, by now the rdev is available
...@@ -1579,15 +1578,21 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) ...@@ -1579,15 +1578,21 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
} }
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
result = __nl80211_set_channel(rdev, wdev, info); result = __nl80211_set_channel(rdev,
nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
info);
if (result) if (result)
goto bad_res; goto bad_res;
} }
if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
struct wireless_dev *txp_wdev = wdev;
enum nl80211_tx_power_setting type; enum nl80211_tx_power_setting type;
int idx, mbm = 0; int idx, mbm = 0;
if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
txp_wdev = NULL;
if (!rdev->ops->set_tx_power) { if (!rdev->ops->set_tx_power) {
result = -EOPNOTSUPP; result = -EOPNOTSUPP;
goto bad_res; goto bad_res;
...@@ -1607,7 +1612,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) ...@@ -1607,7 +1612,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
mbm = nla_get_u32(info->attrs[idx]); mbm = nla_get_u32(info->attrs[idx]);
} }
result = rdev_set_tx_power(rdev, type, mbm); result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
if (result) if (result)
goto bad_res; goto bad_res;
} }
...@@ -1782,6 +1787,11 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag ...@@ -1782,6 +1787,11 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
goto nla_put_failure; goto nla_put_failure;
} }
if (wdev->ssid_len) {
if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
goto nla_put_failure;
}
return genlmsg_end(msg, hdr); return genlmsg_end(msg, hdr);
nla_put_failure: nla_put_failure:
...@@ -2644,6 +2654,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) ...@@ -2644,6 +2654,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
wdev->preset_chantype = params.channel_type; wdev->preset_chantype = params.channel_type;
wdev->beacon_interval = params.beacon_interval; wdev->beacon_interval = params.beacon_interval;
wdev->channel = params.channel; wdev->channel = params.channel;
wdev->ssid_len = params.ssid_len;
memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
} }
return err; return err;
} }
...@@ -5444,6 +5456,36 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) ...@@ -5444,6 +5456,36 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
return cfg80211_leave_ibss(rdev, dev, false); return cfg80211_leave_ibss(rdev, dev, false);
} }
static int nl80211_set_mcast_rate(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];
int mcast_rate[IEEE80211_NUM_BANDS];
u32 nla_rate;
int err;
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
if (!rdev->ops->set_mcast_rate)
return -EOPNOTSUPP;
memset(mcast_rate, 0, sizeof(mcast_rate));
if (!info->attrs[NL80211_ATTR_MCAST_RATE])
return -EINVAL;
nla_rate = nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]);
if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
return -EINVAL;
err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
return err;
}
#ifdef CONFIG_NL80211_TESTMODE #ifdef CONFIG_NL80211_TESTMODE
static struct genl_multicast_group nl80211_testmode_mcgrp = { static struct genl_multicast_group nl80211_testmode_mcgrp = {
.name = "testmode", .name = "testmode",
...@@ -6899,16 +6941,35 @@ static int nl80211_probe_client(struct sk_buff *skb, ...@@ -6899,16 +6941,35 @@ static int nl80211_probe_client(struct sk_buff *skb,
static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) static int nl80211_register_beacons(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 cfg80211_beacon_registration *reg, *nreg;
int rv;
if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (rdev->ap_beacons_nlportid) nreg = kzalloc(sizeof(*nreg), GFP_KERNEL);
return -EBUSY; if (!nreg)
return -ENOMEM;
/* First, check if already registered. */
spin_lock_bh(&rdev->beacon_registrations_lock);
list_for_each_entry(reg, &rdev->beacon_registrations, list) {
if (reg->nlportid == info->snd_portid) {
rv = -EALREADY;
goto out_err;
}
}
/* Add it to the list */
nreg->nlportid = info->snd_portid;
list_add(&nreg->list, &rdev->beacon_registrations);
rdev->ap_beacons_nlportid = info->snd_portid; spin_unlock_bh(&rdev->beacon_registrations_lock);
return 0; return 0;
out_err:
spin_unlock_bh(&rdev->beacon_registrations_lock);
kfree(nreg);
return rv;
} }
static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
...@@ -7625,6 +7686,14 @@ static struct genl_ops nl80211_ops[] = { ...@@ -7625,6 +7686,14 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_WDEV_UP | .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
NL80211_FLAG_NEED_RTNL, NL80211_FLAG_NEED_RTNL,
}, },
{
.cmd = NL80211_CMD_SET_MCAST_RATE,
.doit = nl80211_set_mcast_rate,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV |
NL80211_FLAG_NEED_RTNL,
},
}; };
static struct genl_multicast_group nl80211_mlme_mcgrp = { static struct genl_multicast_group nl80211_mlme_mcgrp = {
...@@ -8914,43 +8983,46 @@ EXPORT_SYMBOL(cfg80211_probe_status); ...@@ -8914,43 +8983,46 @@ EXPORT_SYMBOL(cfg80211_probe_status);
void cfg80211_report_obss_beacon(struct wiphy *wiphy, void cfg80211_report_obss_beacon(struct wiphy *wiphy,
const u8 *frame, size_t len, const u8 *frame, size_t len,
int freq, int sig_dbm, gfp_t gfp) int freq, int sig_dbm)
{ {
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct sk_buff *msg; struct sk_buff *msg;
void *hdr; void *hdr;
u32 nlportid = ACCESS_ONCE(rdev->ap_beacons_nlportid); struct cfg80211_beacon_registration *reg;
trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm); trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm);
if (!nlportid) spin_lock_bh(&rdev->beacon_registrations_lock);
return; list_for_each_entry(reg, &rdev->beacon_registrations, list) {
msg = nlmsg_new(len + 100, GFP_ATOMIC);
msg = nlmsg_new(len + 100, gfp); if (!msg) {
if (!msg) spin_unlock_bh(&rdev->beacon_registrations_lock);
return; return;
}
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
if (!hdr) { if (!hdr)
nlmsg_free(msg); goto nla_put_failure;
return;
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
(freq && (freq &&
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
(sig_dbm && (sig_dbm &&
nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
nla_put(msg, NL80211_ATTR_FRAME, len, frame)) nla_put(msg, NL80211_ATTR_FRAME, len, frame))
goto nla_put_failure; goto nla_put_failure;
genlmsg_end(msg, hdr); genlmsg_end(msg, hdr);
genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid);
}
spin_unlock_bh(&rdev->beacon_registrations_lock);
return; return;
nla_put_failure: nla_put_failure:
genlmsg_cancel(msg, hdr); spin_unlock_bh(&rdev->beacon_registrations_lock);
if (hdr)
genlmsg_cancel(msg, hdr);
nlmsg_free(msg); nlmsg_free(msg);
} }
EXPORT_SYMBOL(cfg80211_report_obss_beacon); EXPORT_SYMBOL(cfg80211_report_obss_beacon);
...@@ -8962,6 +9034,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb, ...@@ -8962,6 +9034,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
struct netlink_notify *notify = _notify; struct netlink_notify *notify = _notify;
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev; struct wireless_dev *wdev;
struct cfg80211_beacon_registration *reg, *tmp;
if (state != NETLINK_URELEASE) if (state != NETLINK_URELEASE)
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -8971,8 +9044,17 @@ static int nl80211_netlink_notify(struct notifier_block * nb, ...@@ -8971,8 +9044,17 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)
cfg80211_mlme_unregister_socket(wdev, notify->portid); cfg80211_mlme_unregister_socket(wdev, notify->portid);
if (rdev->ap_beacons_nlportid == notify->portid)
rdev->ap_beacons_nlportid = 0; spin_lock_bh(&rdev->beacon_registrations_lock);
list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
list) {
if (reg->nlportid == notify->portid) {
list_del(&reg->list);
kfree(reg);
break;
}
}
spin_unlock_bh(&rdev->beacon_registrations_lock);
} }
rcu_read_unlock(); rcu_read_unlock();
......
...@@ -476,21 +476,22 @@ rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed) ...@@ -476,21 +476,22 @@ rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed)
} }
static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev, static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, int mbm) enum nl80211_tx_power_setting type, int mbm)
{ {
int ret; int ret;
trace_rdev_set_tx_power(&rdev->wiphy, type, mbm); trace_rdev_set_tx_power(&rdev->wiphy, wdev, type, mbm);
ret = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm); ret = rdev->ops->set_tx_power(&rdev->wiphy, wdev, type, mbm);
trace_rdev_return_int(&rdev->wiphy, ret); trace_rdev_return_int(&rdev->wiphy, ret);
return ret; return ret;
} }
static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev, static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev,
int *dbm) struct wireless_dev *wdev, int *dbm)
{ {
int ret; int ret;
trace_rdev_get_tx_power(&rdev->wiphy); trace_rdev_get_tx_power(&rdev->wiphy, wdev);
ret = rdev->ops->get_tx_power(&rdev->wiphy, dbm); ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, dbm);
trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm); trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm);
return ret; return ret;
} }
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#define WIPHY_PR_ARG MAC_PR_ARG(wiphy_mac) #define WIPHY_PR_ARG MAC_PR_ARG(wiphy_mac)
#define WDEV_ENTRY __field(u32, id) #define WDEV_ENTRY __field(u32, id)
#define WDEV_ASSIGN (__entry->id) = (wdev->identifier) #define WDEV_ASSIGN (__entry->id) = (wdev ? wdev->identifier : 0)
#define WDEV_PR_FMT ", wdev id: %u" #define WDEV_PR_FMT ", wdev id: %u"
#define WDEV_PR_ARG (__entry->id) #define WDEV_PR_ARG (__entry->id)
...@@ -260,11 +260,6 @@ DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna, ...@@ -260,11 +260,6 @@ DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna,
TP_ARGS(wiphy) TP_ARGS(wiphy)
); );
DEFINE_EVENT(wiphy_only_evt, rdev_get_tx_power,
TP_PROTO(struct wiphy *wiphy),
TP_ARGS(wiphy)
);
DEFINE_EVENT(wiphy_only_evt, rdev_rfkill_poll, DEFINE_EVENT(wiphy_only_evt, rdev_rfkill_poll,
TP_PROTO(struct wiphy *wiphy), TP_PROTO(struct wiphy *wiphy),
TP_ARGS(wiphy) TP_ARGS(wiphy)
...@@ -1230,22 +1225,29 @@ TRACE_EVENT(rdev_set_wiphy_params, ...@@ -1230,22 +1225,29 @@ TRACE_EVENT(rdev_set_wiphy_params,
WIPHY_PR_ARG, __entry->changed) WIPHY_PR_ARG, __entry->changed)
); );
DEFINE_EVENT(wiphy_wdev_evt, rdev_get_tx_power,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
TP_ARGS(wiphy, wdev)
);
TRACE_EVENT(rdev_set_tx_power, TRACE_EVENT(rdev_set_tx_power,
TP_PROTO(struct wiphy *wiphy, enum nl80211_tx_power_setting type, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
int mbm), enum nl80211_tx_power_setting type, int mbm),
TP_ARGS(wiphy, type, mbm), TP_ARGS(wiphy, wdev, type, mbm),
TP_STRUCT__entry( TP_STRUCT__entry(
WIPHY_ENTRY WIPHY_ENTRY
WDEV_ENTRY
__field(enum nl80211_tx_power_setting, type) __field(enum nl80211_tx_power_setting, type)
__field(int, mbm) __field(int, mbm)
), ),
TP_fast_assign( TP_fast_assign(
WIPHY_ASSIGN; WIPHY_ASSIGN;
WDEV_ASSIGN;
__entry->type = type; __entry->type = type;
__entry->mbm = mbm; __entry->mbm = mbm;
), ),
TP_printk(WIPHY_PR_FMT ", type: %d, mbm: %d", TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", type: %d, mbm: %d",
WIPHY_PR_ARG, __entry->type, __entry->mbm) WIPHY_PR_ARG, WDEV_PR_ARG,__entry->type, __entry->mbm)
); );
TRACE_EVENT(rdev_return_int_int, TRACE_EVENT(rdev_return_int_int,
......
...@@ -980,6 +980,105 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate) ...@@ -980,6 +980,105 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
} }
EXPORT_SYMBOL(cfg80211_calculate_bitrate); EXPORT_SYMBOL(cfg80211_calculate_bitrate);
unsigned int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
u8 attr, u8 *buf, unsigned int bufsize)
{
u8 *out = buf;
u16 attr_remaining = 0;
bool desired_attr = false;
u16 desired_len = 0;
while (len > 0) {
unsigned int iedatalen;
unsigned int copy;
const u8 *iedata;
if (len < 2)
return -EILSEQ;
iedatalen = ies[1];
if (iedatalen + 2 > len)
return -EILSEQ;
if (ies[0] != WLAN_EID_VENDOR_SPECIFIC)
goto cont;
if (iedatalen < 4)
goto cont;
iedata = ies + 2;
/* check WFA OUI, P2P subtype */
if (iedata[0] != 0x50 || iedata[1] != 0x6f ||
iedata[2] != 0x9a || iedata[3] != 0x09)
goto cont;
iedatalen -= 4;
iedata += 4;
/* check attribute continuation into this IE */
copy = min_t(unsigned int, attr_remaining, iedatalen);
if (copy && desired_attr) {
desired_len += copy;
if (out) {
memcpy(out, iedata, min(bufsize, copy));
out += min(bufsize, copy);
bufsize -= min(bufsize, copy);
}
if (copy == attr_remaining)
return desired_len;
}
attr_remaining -= copy;
if (attr_remaining)
goto cont;
iedatalen -= copy;
iedata += copy;
while (iedatalen > 0) {
u16 attr_len;
/* P2P attribute ID & size must fit */
if (iedatalen < 3)
return -EILSEQ;
desired_attr = iedata[0] == attr;
attr_len = get_unaligned_le16(iedata + 1);
iedatalen -= 3;
iedata += 3;
copy = min_t(unsigned int, attr_len, iedatalen);
if (desired_attr) {
desired_len += copy;
if (out) {
memcpy(out, iedata, min(bufsize, copy));
out += min(bufsize, copy);
bufsize -= min(bufsize, copy);
}
if (copy == attr_len)
return desired_len;
}
iedata += copy;
iedatalen -= copy;
attr_remaining = attr_len - copy;
}
cont:
len -= ies[1] + 2;
ies += ies[1] + 2;
}
if (attr_remaining && desired_attr)
return -EILSEQ;
return -ENOENT;
}
EXPORT_SYMBOL(cfg80211_get_p2p_attr);
int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
u32 beacon_int) u32 beacon_int)
{ {
......
...@@ -895,7 +895,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev, ...@@ -895,7 +895,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,
return 0; return 0;
} }
return rdev_set_tx_power(rdev, type, DBM_TO_MBM(dbm)); return rdev_set_tx_power(rdev, wdev, type, DBM_TO_MBM(dbm));
} }
static int cfg80211_wext_giwtxpower(struct net_device *dev, static int cfg80211_wext_giwtxpower(struct net_device *dev,
...@@ -914,7 +914,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev, ...@@ -914,7 +914,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev,
if (!rdev->ops->get_tx_power) if (!rdev->ops->get_tx_power)
return -EOPNOTSUPP; return -EOPNOTSUPP;
err = rdev_get_tx_power(rdev, &val); err = rdev_get_tx_power(rdev, wdev, &val);
if (err) if (err)
return err; return err;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册