Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

This commit is contained in:
John W. Linville 2013-08-01 15:26:52 -04:00
commit 7546ff9549
41 changed files with 1408 additions and 392 deletions

View File

@ -2094,7 +2094,7 @@ static void ath9k_wow_add_pattern(struct ath_softc *sc,
{ {
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct ath9k_wow_pattern *wow_pattern = NULL; struct ath9k_wow_pattern *wow_pattern = NULL;
struct cfg80211_wowlan_trig_pkt_pattern *patterns = wowlan->patterns; struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
int mask_len; int mask_len;
s8 i = 0; s8 i = 0;

View File

@ -1275,6 +1275,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
} }
static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta) struct ieee80211_sta *sta, void *priv_sta)
{ {
struct ath_softc *sc = priv; struct ath_softc *sc = priv;
@ -1313,6 +1314,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
} }
static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_sta *sta, void *priv_sta,
u32 changed) u32 changed)
{ {

View File

@ -887,6 +887,7 @@ il3945_remove_debugfs(void *il, void *il_sta)
*/ */
static void static void
il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *il_sta) struct ieee80211_sta *sta, void *il_sta)
{ {
} }

View File

@ -2803,6 +2803,7 @@ il4965_rs_remove_debugfs(void *il, void *il_sta)
*/ */
static void static void
il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *il_sta) struct ieee80211_sta *sta, void *il_sta)
{ {
} }

View File

@ -3319,7 +3319,8 @@ static void rs_remove_debugfs(void *priv, void *priv_sta)
* station is added we ignore it. * station is added we ignore it.
*/ */
static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta) struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta)
{ {
} }
static struct rate_control_ops rs_ops = { static struct rate_control_ops rs_ops = {

View File

@ -3159,8 +3159,9 @@ static void rs_remove_debugfs(void *mvm, void *mvm_sta)
* station is added we ignore it. * station is added we ignore it.
*/ */
static void rs_rate_init_stub(void *mvm_r, static void rs_rate_init_stub(void *mvm_r,
struct ieee80211_supported_band *sband, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *mvm_sta) struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *mvm_sta)
{ {
} }
static struct rate_control_ops rs_mvm_ops = { static struct rate_control_ops rs_mvm_ops = {

View File

@ -867,7 +867,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
if (WARN_ON(skb->len < 10)) { if (WARN_ON(skb->len < 10)) {
/* Should not happen; just a sanity check for addr1 use */ /* Should not happen; just a sanity check for addr1 use */
dev_kfree_skb(skb); ieee80211_free_txskb(hw, skb);
return; return;
} }
@ -884,13 +884,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
} }
if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) { if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) {
dev_kfree_skb(skb); ieee80211_free_txskb(hw, skb);
return; return;
} }
if (data->idle && !data->tmp_chan) { if (data->idle && !data->tmp_chan) {
wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n"); wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
dev_kfree_skb(skb); ieee80211_free_txskb(hw, skb);
return; return;
} }
@ -2309,7 +2309,9 @@ static int __init init_mac80211_hwsim(void)
hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE; hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE;
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
WIPHY_FLAG_AP_UAPSD;
hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
/* ask mac80211 to reserve space for magic */ /* ask mac80211 to reserve space for magic */
hw->vif_data_size = sizeof(struct hwsim_vif_priv); hw->vif_data_size = sizeof(struct hwsim_vif_priv);

View File

@ -2309,8 +2309,7 @@ EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
#ifdef CONFIG_PM #ifdef CONFIG_PM
static bool static bool
mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat, mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq)
s8 *byte_seq)
{ {
int j, k, valid_byte_cnt = 0; int j, k, valid_byte_cnt = 0;
bool dont_care_byte = false; bool dont_care_byte = false;

View File

@ -218,6 +218,7 @@ static void rtl_tx_status(void *ppriv,
static void rtl_rate_init(void *ppriv, static void rtl_rate_init(void *ppriv,
struct ieee80211_supported_band *sband, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta) struct ieee80211_sta *sta, void *priv_sta)
{ {
} }

View File

@ -1315,7 +1315,7 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int static int
wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p) wl1271_validate_wowlan_pattern(struct cfg80211_pkt_pattern *p)
{ {
int num_fields = 0, in_field = 0, fields_size = 0; int num_fields = 0, in_field = 0, fields_size = 0;
int i, pattern_len = 0; int i, pattern_len = 0;
@ -1458,9 +1458,9 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
* Allocates an RX filter returned through f * Allocates an RX filter returned through f
* which needs to be freed using rx_filter_free() * which needs to be freed using rx_filter_free()
*/ */
static int wl1271_convert_wowlan_pattern_to_rx_filter( static int
struct cfg80211_wowlan_trig_pkt_pattern *p, wl1271_convert_wowlan_pattern_to_rx_filter(struct cfg80211_pkt_pattern *p,
struct wl12xx_rx_filter **f) struct wl12xx_rx_filter **f)
{ {
int i, j, ret = 0; int i, j, ret = 0;
struct wl12xx_rx_filter *filter; struct wl12xx_rx_filter *filter;
@ -1562,7 +1562,7 @@ static int wl1271_configure_wowlan(struct wl1271 *wl,
/* Translate WoWLAN patterns into filters */ /* Translate WoWLAN patterns into filters */
for (i = 0; i < wow->n_patterns; i++) { for (i = 0; i < wow->n_patterns; i++) {
struct cfg80211_wowlan_trig_pkt_pattern *p; struct cfg80211_pkt_pattern *p;
struct wl12xx_rx_filter *filter = NULL; struct wl12xx_rx_filter *filter = NULL;
p = &wow->patterns[i]; p = &wow->patterns[i];

View File

@ -460,6 +460,33 @@ ieee80211_chandef_rate_flags(struct cfg80211_chan_def *chandef)
return 0; return 0;
} }
/**
* ieee80211_chandef_max_power - maximum transmission power for the chandef
*
* In some regulations, the transmit power may depend on the configured channel
* bandwidth which may be defined as dBm/MHz. This function returns the actual
* max_power for non-standard (20 MHz) channels.
*
* @chandef: channel definition for the channel
*
* Returns: maximum allowed transmission power in dBm for the chandef
*/
static inline int
ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef)
{
switch (chandef->width) {
case NL80211_CHAN_WIDTH_5:
return min(chandef->chan->max_reg_power - 6,
chandef->chan->max_power);
case NL80211_CHAN_WIDTH_10:
return min(chandef->chan->max_reg_power - 3,
chandef->chan->max_power);
default:
break;
}
return chandef->chan->max_power;
}
/** /**
* enum survey_info_flags - survey information flags * enum survey_info_flags - survey information flags
* *
@ -490,7 +517,7 @@ enum survey_info_flags {
* @channel: the channel this survey record reports, mandatory * @channel: the channel this survey record reports, mandatory
* @filled: bitflag of flags from &enum survey_info_flags * @filled: bitflag of flags from &enum survey_info_flags
* @noise: channel noise in dBm. This and all following fields are * @noise: channel noise in dBm. This and all following fields are
* optional * optional
* @channel_time: amount of time in ms the radio spent on the channel * @channel_time: amount of time in ms the radio spent on the channel
* @channel_time_busy: amount of time the primary channel was sensed busy * @channel_time_busy: amount of time the primary channel was sensed busy
* @channel_time_ext_busy: amount of time the extension channel was sensed busy * @channel_time_ext_busy: amount of time the extension channel was sensed busy
@ -546,9 +573,9 @@ struct cfg80211_crypto_settings {
/** /**
* struct cfg80211_beacon_data - beacon data * struct cfg80211_beacon_data - beacon data
* @head: head portion of beacon (before TIM IE) * @head: head portion of beacon (before TIM IE)
* or %NULL if not changed * or %NULL if not changed
* @tail: tail portion of beacon (after TIM IE) * @tail: tail portion of beacon (after TIM IE)
* or %NULL if not changed * or %NULL if not changed
* @head_len: length of @head * @head_len: length of @head
* @tail_len: length of @tail * @tail_len: length of @tail
* @beacon_ies: extra information element(s) to add into Beacon frames or %NULL * @beacon_ies: extra information element(s) to add into Beacon frames or %NULL
@ -764,7 +791,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
* @STATION_INFO_PLINK_STATE: @plink_state filled * @STATION_INFO_PLINK_STATE: @plink_state filled
* @STATION_INFO_SIGNAL: @signal filled * @STATION_INFO_SIGNAL: @signal filled
* @STATION_INFO_TX_BITRATE: @txrate fields are filled * @STATION_INFO_TX_BITRATE: @txrate fields are filled
* (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
* @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value * @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value
* @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value * @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value
* @STATION_INFO_TX_RETRIES: @tx_retries filled * @STATION_INFO_TX_RETRIES: @tx_retries filled
@ -1285,6 +1312,7 @@ struct cfg80211_ssid {
* @n_ssids: number of SSIDs * @n_ssids: number of SSIDs
* @channels: channels to scan on. * @channels: channels to scan on.
* @n_channels: total number of channels to scan * @n_channels: total number of channels to scan
* @scan_width: channel width for scanning
* @ie: optional information element(s) to add into Probe Request or %NULL * @ie: optional information element(s) to add into Probe Request or %NULL
* @ie_len: length of ie in octets * @ie_len: length of ie in octets
* @flags: bit field of flags controlling operation * @flags: bit field of flags controlling operation
@ -1300,6 +1328,7 @@ struct cfg80211_scan_request {
struct cfg80211_ssid *ssids; struct cfg80211_ssid *ssids;
int n_ssids; int n_ssids;
u32 n_channels; u32 n_channels;
enum nl80211_bss_scan_width scan_width;
const u8 *ie; const u8 *ie;
size_t ie_len; size_t ie_len;
u32 flags; u32 flags;
@ -1333,6 +1362,7 @@ struct cfg80211_match_set {
* @ssids: SSIDs to scan for (passed in the probe_reqs in active scans) * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans)
* @n_ssids: number of SSIDs * @n_ssids: number of SSIDs
* @n_channels: total number of channels to scan * @n_channels: total number of channels to scan
* @scan_width: channel width for scanning
* @interval: interval between each scheduled scan cycle * @interval: interval between each scheduled scan cycle
* @ie: optional information element(s) to add into Probe Request or %NULL * @ie: optional information element(s) to add into Probe Request or %NULL
* @ie_len: length of ie in octets * @ie_len: length of ie in octets
@ -1352,6 +1382,7 @@ struct cfg80211_sched_scan_request {
struct cfg80211_ssid *ssids; struct cfg80211_ssid *ssids;
int n_ssids; int n_ssids;
u32 n_channels; u32 n_channels;
enum nl80211_bss_scan_width scan_width;
u32 interval; u32 interval;
const u8 *ie; const u8 *ie;
size_t ie_len; size_t ie_len;
@ -1403,6 +1434,7 @@ struct cfg80211_bss_ies {
* for use in scan results and similar. * for use in scan results and similar.
* *
* @channel: channel this BSS is on * @channel: channel this BSS is on
* @scan_width: width of the control channel
* @bssid: BSSID of the BSS * @bssid: BSSID of the BSS
* @beacon_interval: the beacon interval as from the frame * @beacon_interval: the beacon interval as from the frame
* @capability: the capability field in host byte order * @capability: the capability field in host byte order
@ -1424,6 +1456,7 @@ struct cfg80211_bss_ies {
*/ */
struct cfg80211_bss { struct cfg80211_bss {
struct ieee80211_channel *channel; struct ieee80211_channel *channel;
enum nl80211_bss_scan_width scan_width;
const struct cfg80211_bss_ies __rcu *ies; const struct cfg80211_bss_ies __rcu *ies;
const struct cfg80211_bss_ies __rcu *beacon_ies; const struct cfg80211_bss_ies __rcu *beacon_ies;
@ -1509,7 +1542,7 @@ enum cfg80211_assoc_req_flags {
* @prev_bssid: previous BSSID, if not %NULL use reassociate frame * @prev_bssid: previous BSSID, if not %NULL use reassociate frame
* @flags: See &enum cfg80211_assoc_req_flags * @flags: See &enum cfg80211_assoc_req_flags
* @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
* will be used in ht_capa. Un-supported values will be ignored. * will be used in ht_capa. Un-supported values will be ignored.
* @ht_capa_mask: The bits of ht_capa which are to be used. * @ht_capa_mask: The bits of ht_capa which are to be used.
* @vht_capa: VHT capability override * @vht_capa: VHT capability override
* @vht_capa_mask: VHT capability mask indicating which fields to use * @vht_capa_mask: VHT capability mask indicating which fields to use
@ -1592,6 +1625,9 @@ struct cfg80211_disassoc_request {
* user space. Otherwise, port is marked authorized by default. * user space. Otherwise, port is marked authorized by default.
* @basic_rates: bitmap of basic rates to use when creating the IBSS * @basic_rates: bitmap of basic rates to use when creating the IBSS
* @mcast_rate: per-band multicast rate index + 1 (0: disabled) * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
* @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
* will be used in ht_capa. Un-supported values will be ignored.
* @ht_capa_mask: The bits of ht_capa which are to be used.
*/ */
struct cfg80211_ibss_params { struct cfg80211_ibss_params {
u8 *ssid; u8 *ssid;
@ -1605,6 +1641,8 @@ struct cfg80211_ibss_params {
bool privacy; bool privacy;
bool control_port; bool control_port;
int mcast_rate[IEEE80211_NUM_BANDS]; int mcast_rate[IEEE80211_NUM_BANDS];
struct ieee80211_ht_cap ht_capa;
struct ieee80211_ht_cap ht_capa_mask;
}; };
/** /**
@ -1630,9 +1668,9 @@ struct cfg80211_ibss_params {
* @key: WEP key for shared key authentication * @key: WEP key for shared key authentication
* @flags: See &enum cfg80211_assoc_req_flags * @flags: See &enum cfg80211_assoc_req_flags
* @bg_scan_period: Background scan period in seconds * @bg_scan_period: Background scan period in seconds
* or -1 to indicate that default value is to be used. * or -1 to indicate that default value is to be used.
* @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
* will be used in ht_capa. Un-supported values will be ignored. * will be used in ht_capa. Un-supported values will be ignored.
* @ht_capa_mask: The bits of ht_capa which are to be used. * @ht_capa_mask: The bits of ht_capa which are to be used.
* @vht_capa: VHT Capability overrides * @vht_capa: VHT Capability overrides
* @vht_capa_mask: The bits of vht_capa which are to be used. * @vht_capa_mask: The bits of vht_capa which are to be used.
@ -1698,7 +1736,7 @@ struct cfg80211_pmksa {
}; };
/** /**
* struct cfg80211_wowlan_trig_pkt_pattern - packet pattern * struct cfg80211_pkt_pattern - packet pattern
* @mask: bitmask where to match pattern and where to ignore bytes, * @mask: bitmask where to match pattern and where to ignore bytes,
* one bit per byte, in same format as nl80211 * one bit per byte, in same format as nl80211
* @pattern: bytes to match where bitmask is 1 * @pattern: bytes to match where bitmask is 1
@ -1708,7 +1746,7 @@ struct cfg80211_pmksa {
* Internal note: @mask and @pattern are allocated in one chunk of * Internal note: @mask and @pattern are allocated in one chunk of
* memory, free @mask only! * memory, free @mask only!
*/ */
struct cfg80211_wowlan_trig_pkt_pattern { struct cfg80211_pkt_pattern {
u8 *mask, *pattern; u8 *mask, *pattern;
int pattern_len; int pattern_len;
int pkt_offset; int pkt_offset;
@ -1770,11 +1808,40 @@ struct cfg80211_wowlan {
bool any, disconnect, magic_pkt, gtk_rekey_failure, bool any, disconnect, magic_pkt, gtk_rekey_failure,
eap_identity_req, four_way_handshake, eap_identity_req, four_way_handshake,
rfkill_release; rfkill_release;
struct cfg80211_wowlan_trig_pkt_pattern *patterns; struct cfg80211_pkt_pattern *patterns;
struct cfg80211_wowlan_tcp *tcp; struct cfg80211_wowlan_tcp *tcp;
int n_patterns; int n_patterns;
}; };
/**
* struct cfg80211_coalesce_rules - Coalesce rule parameters
*
* This structure defines coalesce rule for the device.
* @delay: maximum coalescing delay in msecs.
* @condition: condition for packet coalescence.
* see &enum nl80211_coalesce_condition.
* @patterns: array of packet patterns
* @n_patterns: number of patterns
*/
struct cfg80211_coalesce_rules {
int delay;
enum nl80211_coalesce_condition condition;
struct cfg80211_pkt_pattern *patterns;
int n_patterns;
};
/**
* struct cfg80211_coalesce - Packet coalescing settings
*
* This structure defines coalescing settings.
* @rules: array of coalesce rules
* @n_rules: number of rules
*/
struct cfg80211_coalesce {
struct cfg80211_coalesce_rules *rules;
int n_rules;
};
/** /**
* struct cfg80211_wowlan_wakeup - wakeup report * struct cfg80211_wowlan_wakeup - wakeup report
* @disconnect: woke up by getting disconnected * @disconnect: woke up by getting disconnected
@ -2071,6 +2138,7 @@ struct cfg80211_update_ft_ies_params {
* driver can take the most appropriate actions. * driver can take the most appropriate actions.
* @crit_proto_stop: Indicates critical protocol no longer needs increased link * @crit_proto_stop: Indicates critical protocol no longer needs increased link
* reliability. This operation can not fail. * reliability. This operation can not fail.
* @set_coalesce: Set coalesce parameters.
*/ */
struct cfg80211_ops { struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@ -2306,6 +2374,8 @@ struct cfg80211_ops {
u16 duration); u16 duration);
void (*crit_proto_stop)(struct wiphy *wiphy, void (*crit_proto_stop)(struct wiphy *wiphy,
struct wireless_dev *wdev); struct wireless_dev *wdev);
int (*set_coalesce)(struct wiphy *wiphy,
struct cfg80211_coalesce *coalesce);
}; };
/* /*
@ -2531,6 +2601,25 @@ struct wiphy_wowlan_support {
const struct wiphy_wowlan_tcp_support *tcp; const struct wiphy_wowlan_tcp_support *tcp;
}; };
/**
* struct wiphy_coalesce_support - coalesce support data
* @n_rules: maximum number of coalesce rules
* @max_delay: maximum supported coalescing delay in msecs
* @n_patterns: number of supported patterns in a rule
* (see nl80211.h for the pattern definition)
* @pattern_max_len: maximum length of each pattern
* @pattern_min_len: minimum length of each pattern
* @max_pkt_offset: maximum Rx packet offset
*/
struct wiphy_coalesce_support {
int n_rules;
int max_delay;
int n_patterns;
int pattern_max_len;
int pattern_min_len;
int max_pkt_offset;
};
/** /**
* struct wiphy - wireless hardware description * struct wiphy - wireless hardware description
* @reg_notifier: the driver's regulatory notification callback, * @reg_notifier: the driver's regulatory notification callback,
@ -2641,6 +2730,7 @@ struct wiphy_wowlan_support {
* 802.11-2012 8.4.2.29 for the defined fields. * 802.11-2012 8.4.2.29 for the defined fields.
* @extended_capabilities_mask: mask of the valid values * @extended_capabilities_mask: mask of the valid values
* @extended_capabilities_len: length of the extended capabilities * @extended_capabilities_len: length of the extended capabilities
* @coalesce: packet coalescing support information
*/ */
struct wiphy { struct wiphy {
/* assign these fields before you register the wiphy */ /* assign these fields before you register the wiphy */
@ -2750,6 +2840,8 @@ struct wiphy {
const struct iw_handler_def *wext; const struct iw_handler_def *wext;
#endif #endif
const struct wiphy_coalesce_support *coalesce;
char priv[0] __aligned(NETDEV_ALIGN); char priv[0] __aligned(NETDEV_ALIGN);
}; };
@ -3063,11 +3155,13 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
/** /**
* ieee80211_mandatory_rates - get mandatory rates for a given band * ieee80211_mandatory_rates - get mandatory rates for a given band
* @sband: the band to look for rates in * @sband: the band to look for rates in
* @scan_width: width of the control channel
* *
* This function returns a bitmap of the mandatory rates for the given * This function returns a bitmap of the mandatory rates for the given
* band, bits are set according to the rate position in the bitrates array. * band, bits are set according to the rate position in the bitrates array.
*/ */
u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband); u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
enum nl80211_bss_scan_width scan_width);
/* /*
* Radiotap parsing functions -- for controlled injection support * Radiotap parsing functions -- for controlled injection support
@ -3379,10 +3473,11 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy);
void cfg80211_sched_scan_stopped(struct wiphy *wiphy); void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
/** /**
* cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame * cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame
* *
* @wiphy: the wiphy reporting the BSS * @wiphy: the wiphy reporting the BSS
* @channel: The channel the frame was received on * @channel: The channel the frame was received on
* @scan_width: width of the control channel
* @mgmt: the management frame (probe response or beacon) * @mgmt: the management frame (probe response or beacon)
* @len: length of the management frame * @len: length of the management frame
* @signal: the signal strength, type depends on the wiphy's signal_type * @signal: the signal strength, type depends on the wiphy's signal_type
@ -3395,16 +3490,29 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
* Or %NULL on error. * Or %NULL on error.
*/ */
struct cfg80211_bss * __must_check struct cfg80211_bss * __must_check
cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
struct ieee80211_channel *channel,
enum nl80211_bss_scan_width scan_width,
struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp);
static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss_frame(struct wiphy *wiphy, cfg80211_inform_bss_frame(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp); s32 signal, gfp_t gfp)
{
return cfg80211_inform_bss_width_frame(wiphy, channel,
NL80211_BSS_CHAN_WIDTH_20,
mgmt, len, signal, gfp);
}
/** /**
* cfg80211_inform_bss - inform cfg80211 of a new BSS * cfg80211_inform_bss - inform cfg80211 of a new BSS
* *
* @wiphy: the wiphy reporting the BSS * @wiphy: the wiphy reporting the BSS
* @channel: The channel the frame was received on * @channel: The channel the frame was received on
* @scan_width: width of the control channel
* @bssid: the BSSID of the BSS * @bssid: the BSSID of the BSS
* @tsf: the TSF sent by the peer in the beacon/probe response (or 0) * @tsf: the TSF sent by the peer in the beacon/probe response (or 0)
* @capability: the capability field sent by the peer * @capability: the capability field sent by the peer
@ -3421,11 +3529,26 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
* Or %NULL on error. * Or %NULL on error.
*/ */
struct cfg80211_bss * __must_check struct cfg80211_bss * __must_check
cfg80211_inform_bss_width(struct wiphy *wiphy,
struct ieee80211_channel *channel,
enum nl80211_bss_scan_width scan_width,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp);
static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss(struct wiphy *wiphy, cfg80211_inform_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
const u8 *bssid, u64 tsf, u16 capability, const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen, u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp); s32 signal, gfp_t gfp)
{
return cfg80211_inform_bss_width(wiphy, channel,
NL80211_BSS_CHAN_WIDTH_20,
bssid, tsf, capability,
beacon_interval, ie, ielen, signal,
gfp);
}
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
@ -3471,6 +3594,19 @@ void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
*/ */
void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss); void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
static inline enum nl80211_bss_scan_width
cfg80211_chandef_to_scan_width(const struct cfg80211_chan_def *chandef)
{
switch (chandef->width) {
case NL80211_CHAN_WIDTH_5:
return NL80211_BSS_CHAN_WIDTH_5;
case NL80211_CHAN_WIDTH_10:
return NL80211_BSS_CHAN_WIDTH_10;
default:
return NL80211_BSS_CHAN_WIDTH_20;
}
}
/** /**
* cfg80211_rx_mlme_mgmt - notification of processed MLME management frame * cfg80211_rx_mlme_mgmt - notification of processed MLME management frame
* @dev: network device * @dev: network device

View File

@ -230,6 +230,10 @@ enum ieee80211_radiotap_type {
#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ #define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */
#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ #define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */
#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ #define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */
#define IEEE80211_CHAN_GSM 0x1000 /* GSM (900 MHz) */
#define IEEE80211_CHAN_STURBO 0x2000 /* Static Turbo */
#define IEEE80211_CHAN_HALF 0x4000 /* Half channel (10 MHz wide) */
#define IEEE80211_CHAN_QUARTER 0x8000 /* Quarter channel (5 MHz wide) */
/* For IEEE80211_RADIOTAP_FLAGS */ /* For IEEE80211_RADIOTAP_FLAGS */
#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received #define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received

View File

@ -811,6 +811,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
* is stored in the @ampdu_delimiter_crc field) * is stored in the @ampdu_delimiter_crc field)
* @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3 * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
* @RX_FLAG_10MHZ: 10 MHz (half channel) was used
* @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
*/ */
enum mac80211_rx_flags { enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0), RX_FLAG_MMIC_ERROR = BIT(0),
@ -839,6 +841,8 @@ enum mac80211_rx_flags {
RX_FLAG_80P80MHZ = BIT(24), RX_FLAG_80P80MHZ = BIT(24),
RX_FLAG_160MHZ = BIT(25), RX_FLAG_160MHZ = BIT(25),
RX_FLAG_STBC_MASK = BIT(26) | BIT(27), RX_FLAG_STBC_MASK = BIT(26) | BIT(27),
RX_FLAG_10MHZ = BIT(28),
RX_FLAG_5MHZ = BIT(29),
}; };
#define RX_FLAG_STBC_SHIFT 26 #define RX_FLAG_STBC_SHIFT 26
@ -1004,11 +1008,11 @@ enum ieee80211_smps_mode {
* @radar_enabled: whether radar detection is enabled * @radar_enabled: whether radar detection is enabled
* *
* @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame
* (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11, * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
* but actually means the number of transmissions not the number of retries * but actually means the number of transmissions not the number of retries
* @short_frame_max_tx_count: Maximum number of transmissions for a "short" * @short_frame_max_tx_count: Maximum number of transmissions for a "short"
* frame, called "dot11ShortRetryLimit" in 802.11, but actually means the * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
* number of transmissions not the number of retries * number of transmissions not the number of retries
* *
* @smps_mode: spatial multiplexing powersave mode; note that * @smps_mode: spatial multiplexing powersave mode; note that
* %IEEE80211_SMPS_STATIC is used when the device is not * %IEEE80211_SMPS_STATIC is used when the device is not
@ -1092,7 +1096,7 @@ enum ieee80211_vif_flags {
* be off when it is %NULL there can still be races and packets could be * be off when it is %NULL there can still be races and packets could be
* processed after it switches back to %NULL. * processed after it switches back to %NULL.
* @debugfs_dir: debugfs dentry, can be used by drivers to create own per * @debugfs_dir: debugfs dentry, can be used by drivers to create own per
* interface debug files. Note that it will be NULL for the virtual * interface debug files. Note that it will be NULL for the virtual
* monitor interface (if that is requested.) * monitor interface (if that is requested.)
* @drv_priv: data area for driver use, will always be aligned to * @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *). * sizeof(void *).
@ -1425,10 +1429,10 @@ struct ieee80211_tx_control {
* the stack. * the stack.
* *
* @IEEE80211_HW_CONNECTION_MONITOR: * @IEEE80211_HW_CONNECTION_MONITOR:
* The hardware performs its own connection monitoring, including * The hardware performs its own connection monitoring, including
* periodic keep-alives to the AP and probing the AP on beacon loss. * periodic keep-alives to the AP and probing the AP on beacon loss.
* When this flag is set, signaling beacon-loss will cause an immediate * When this flag is set, signaling beacon-loss will cause an immediate
* change to disassociated state. * change to disassociated state.
* *
* @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC: * @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC:
* This device needs to get data from beacon before association (i.e. * This device needs to get data from beacon before association (i.e.
@ -1526,10 +1530,10 @@ enum ieee80211_hw_flags {
* @channel_change_time: time (in microseconds) it takes to change channels. * @channel_change_time: time (in microseconds) it takes to change channels.
* *
* @max_signal: Maximum value for signal (rssi) in RX information, used * @max_signal: Maximum value for signal (rssi) in RX information, used
* only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB * only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB
* *
* @max_listen_interval: max listen interval in units of beacon interval * @max_listen_interval: max listen interval in units of beacon interval
* that HW supports * that HW supports
* *
* @queues: number of available hardware transmit queues for * @queues: number of available hardware transmit queues for
* data packets. WMM/QoS requires at least four, these * data packets. WMM/QoS requires at least four, these
@ -2443,7 +2447,7 @@ enum ieee80211_roc_type {
* The callback can sleep. * The callback can sleep.
* *
* @set_tsf: Set the TSF timer to the specified value in the firmware/hardware. * @set_tsf: Set the TSF timer to the specified value in the firmware/hardware.
* Currently, this is only used for IBSS mode debugging. Is not a * Currently, this is only used for IBSS mode debugging. Is not a
* required function. * required function.
* The callback can sleep. * The callback can sleep.
* *
@ -4204,8 +4208,10 @@ struct rate_control_ops {
void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp); void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp);
void (*rate_init)(void *priv, struct ieee80211_supported_band *sband, void (*rate_init)(void *priv, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta); struct ieee80211_sta *sta, void *priv_sta);
void (*rate_update)(void *priv, struct ieee80211_supported_band *sband, void (*rate_update)(void *priv, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_sta *sta, void *priv_sta,
u32 changed); u32 changed);
void (*free_sta)(void *priv, struct ieee80211_sta *sta, void (*free_sta)(void *priv, struct ieee80211_sta *sta,

View File

@ -125,6 +125,31 @@
* interfaces that a given device supports. * interfaces that a given device supports.
*/ */
/**
* DOC: packet coalesce support
*
* In most cases, host that receives IPv4 and IPv6 multicast/broadcast
* packets does not do anything with these packets. Therefore the
* reception of these unwanted packets causes unnecessary processing
* and power consumption.
*
* Packet coalesce feature helps to reduce number of received interrupts
* to host by buffering these packets in firmware/hardware for some
* predefined time. Received interrupt will be generated when one of the
* following events occur.
* a) Expiration of hardware timer whose expiration time is set to maximum
* coalescing delay of matching coalesce rule.
* b) Coalescing buffer in hardware reaches it's limit.
* c) Packet doesn't match any of the configured coalesce rules.
*
* User needs to configure following parameters for creating a coalesce
* rule.
* a) Maximum coalescing delay
* b) List of packet patterns which needs to be matched
* c) Condition for coalescence. pattern 'match' or 'no match'
* Multiple such rules can be created.
*/
/** /**
* enum nl80211_commands - supported nl80211 commands * enum nl80211_commands - supported nl80211 commands
* *
@ -648,6 +673,9 @@
* @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
* return back to normal. * return back to normal.
* *
* @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
* @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
*
* @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
*/ */
@ -810,6 +838,9 @@ enum nl80211_commands {
NL80211_CMD_CRIT_PROTOCOL_START, NL80211_CMD_CRIT_PROTOCOL_START,
NL80211_CMD_CRIT_PROTOCOL_STOP, NL80211_CMD_CRIT_PROTOCOL_STOP,
NL80211_CMD_GET_COALESCE,
NL80211_CMD_SET_COALESCE,
/* add new commands above here */ /* add new commands above here */
/* used to define NL80211_CMD_MAX below */ /* used to define NL80211_CMD_MAX below */
@ -1436,6 +1467,8 @@ enum nl80211_commands {
* allowed to be used with the first @NL80211_CMD_SET_STATION command to * allowed to be used with the first @NL80211_CMD_SET_STATION command to
* update a TDLS peer STA entry. * update a TDLS peer STA entry.
* *
* @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
@ -1736,6 +1769,8 @@ enum nl80211_attrs {
NL80211_ATTR_PEER_AID, NL80211_ATTR_PEER_AID,
NL80211_ATTR_COALESCE_RULE,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
@ -2772,6 +2807,21 @@ enum nl80211_chan_width {
NL80211_CHAN_WIDTH_10, NL80211_CHAN_WIDTH_10,
}; };
/**
* enum nl80211_bss_scan_width - control channel width for a BSS
*
* These values are used with the %NL80211_BSS_CHAN_WIDTH attribute.
*
* @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible
* @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide
* @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide
*/
enum nl80211_bss_scan_width {
NL80211_BSS_CHAN_WIDTH_20,
NL80211_BSS_CHAN_WIDTH_10,
NL80211_BSS_CHAN_WIDTH_5,
};
/** /**
* enum nl80211_bss - netlink attributes for a BSS * enum nl80211_bss - netlink attributes for a BSS
* *
@ -2796,6 +2846,8 @@ enum nl80211_chan_width {
* @NL80211_BSS_BEACON_IES: binary attribute containing the raw information * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information
* elements from a Beacon frame (bin); not present if no Beacon frame has * elements from a Beacon frame (bin); not present if no Beacon frame has
* yet been received * yet been received
* @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
* (u32, enum nl80211_bss_scan_width)
* @__NL80211_BSS_AFTER_LAST: internal * @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute * @NL80211_BSS_MAX: highest BSS attribute
*/ */
@ -2812,6 +2864,7 @@ enum nl80211_bss {
NL80211_BSS_STATUS, NL80211_BSS_STATUS,
NL80211_BSS_SEEN_MS_AGO, NL80211_BSS_SEEN_MS_AGO,
NL80211_BSS_BEACON_IES, NL80211_BSS_BEACON_IES,
NL80211_BSS_CHAN_WIDTH,
/* keep last */ /* keep last */
__NL80211_BSS_AFTER_LAST, __NL80211_BSS_AFTER_LAST,
@ -3060,11 +3113,11 @@ enum nl80211_tx_power_setting {
}; };
/** /**
* enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute * enum nl80211_packet_pattern_attr - packet pattern attribute
* @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
* @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has
* a zero bit are ignored * a zero bit are ignored
* @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have
* a bit for each byte in the pattern. The lowest-order bit corresponds * a bit for each byte in the pattern. The lowest-order bit corresponds
* to the first byte of the pattern, but the bytes of the pattern are * to the first byte of the pattern, but the bytes of the pattern are
* in a little-endian-like format, i.e. the 9th byte of the pattern * in a little-endian-like format, i.e. the 9th byte of the pattern
@ -3075,39 +3128,50 @@ enum nl80211_tx_power_setting {
* Note that the pattern matching is done as though frames were not * Note that the pattern matching is done as though frames were not
* 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
* first (including SNAP header unpacking) and then matched. * first (including SNAP header unpacking) and then matched.
* @NL80211_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
* these fixed number of bytes of received packet * these fixed number of bytes of received packet
* @NUM_NL80211_WOWLAN_PKTPAT: number of attributes * @NUM_NL80211_PKTPAT: number of attributes
* @MAX_NL80211_WOWLAN_PKTPAT: max attribute number * @MAX_NL80211_PKTPAT: max attribute number
*/ */
enum nl80211_wowlan_packet_pattern_attr { enum nl80211_packet_pattern_attr {
__NL80211_WOWLAN_PKTPAT_INVALID, __NL80211_PKTPAT_INVALID,
NL80211_WOWLAN_PKTPAT_MASK, NL80211_PKTPAT_MASK,
NL80211_WOWLAN_PKTPAT_PATTERN, NL80211_PKTPAT_PATTERN,
NL80211_WOWLAN_PKTPAT_OFFSET, NL80211_PKTPAT_OFFSET,
NUM_NL80211_WOWLAN_PKTPAT, NUM_NL80211_PKTPAT,
MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1, MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
}; };
/** /**
* struct nl80211_wowlan_pattern_support - pattern support information * struct nl80211_pattern_support - packet pattern support information
* @max_patterns: maximum number of patterns supported * @max_patterns: maximum number of patterns supported
* @min_pattern_len: minimum length of each pattern * @min_pattern_len: minimum length of each pattern
* @max_pattern_len: maximum length of each pattern * @max_pattern_len: maximum length of each pattern
* @max_pkt_offset: maximum Rx packet offset * @max_pkt_offset: maximum Rx packet offset
* *
* This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
* that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in
* capability information given by the kernel to userspace. * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of
* %NL80211_ATTR_COALESCE_RULE in the capability information given
* by the kernel to userspace.
*/ */
struct nl80211_wowlan_pattern_support { struct nl80211_pattern_support {
__u32 max_patterns; __u32 max_patterns;
__u32 min_pattern_len; __u32 min_pattern_len;
__u32 max_pattern_len; __u32 max_pattern_len;
__u32 max_pkt_offset; __u32 max_pkt_offset;
} __attribute__((packed)); } __attribute__((packed));
/* only for backward compatibility */
#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID
#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK
#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN
#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET
#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT
#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT
#define nl80211_wowlan_pattern_support nl80211_pattern_support
/** /**
* enum nl80211_wowlan_triggers - WoWLAN trigger definitions * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
* @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
@ -3127,7 +3191,7 @@ struct nl80211_wowlan_pattern_support {
* pattern matching is done after the packet is converted to the MSDU. * pattern matching is done after the packet is converted to the MSDU.
* *
* In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
* carrying a &struct nl80211_wowlan_pattern_support. * carrying a &struct nl80211_pattern_support.
* *
* When reporting wakeup. it is a u32 attribute containing the 0-based * When reporting wakeup. it is a u32 attribute containing the 0-based
* index of the pattern that caused the wakeup, in the patterns passed * index of the pattern that caused the wakeup, in the patterns passed
@ -3284,7 +3348,7 @@ struct nl80211_wowlan_tcp_data_token_feature {
* @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
* u32 attribute holding the maximum length * u32 attribute holding the maximum length
* @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
* feature advertising. The mask works like @NL80211_WOWLAN_PKTPAT_MASK * feature advertising. The mask works like @NL80211_PKTPAT_MASK
* but on the TCP payload only. * but on the TCP payload only.
* @NUM_NL80211_WOWLAN_TCP: number of TCP attributes * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
* @MAX_NL80211_WOWLAN_TCP: highest attribute number * @MAX_NL80211_WOWLAN_TCP: highest attribute number
@ -3308,6 +3372,55 @@ enum nl80211_wowlan_tcp_attrs {
MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1 MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1
}; };
/**
* struct nl80211_coalesce_rule_support - coalesce rule support information
* @max_rules: maximum number of rules supported
* @pat: packet pattern support information
* @max_delay: maximum supported coalescing delay in msecs
*
* This struct is carried in %NL80211_ATTR_COALESCE_RULE in the
* capability information given by the kernel to userspace.
*/
struct nl80211_coalesce_rule_support {
__u32 max_rules;
struct nl80211_pattern_support pat;
__u32 max_delay;
} __attribute__((packed));
/**
* enum nl80211_attr_coalesce_rule - coalesce rule attribute
* @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute
* @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing
* @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence,
* see &enum nl80211_coalesce_condition.
* @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched
* after these fixed number of bytes of received packet
* @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes
* @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number
*/
enum nl80211_attr_coalesce_rule {
__NL80211_COALESCE_RULE_INVALID,
NL80211_ATTR_COALESCE_RULE_DELAY,
NL80211_ATTR_COALESCE_RULE_CONDITION,
NL80211_ATTR_COALESCE_RULE_PKT_PATTERN,
/* keep last */
NUM_NL80211_ATTR_COALESCE_RULE,
NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1
};
/**
* enum nl80211_coalesce_condition - coalesce rule conditions
* @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
* in a rule are matched.
* @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
* in a rule are not matched.
*/
enum nl80211_coalesce_condition {
NL80211_COALESCE_CONDITION_MATCH,
NL80211_COALESCE_CONDITION_NO_MATCH
};
/** /**
* enum nl80211_iface_limit_attrs - limit attributes * enum nl80211_iface_limit_attrs - limit attributes
* @NL80211_IFACE_LIMIT_UNSPEC: (reserved) * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)

View File

@ -395,9 +395,13 @@ void sta_set_rate_info_tx(struct sta_info *sta,
rinfo->nss = ieee80211_rate_get_vht_nss(rate); rinfo->nss = ieee80211_rate_get_vht_nss(rate);
} else { } else {
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
u16 brate;
sband = sta->local->hw.wiphy->bands[ sband = sta->local->hw.wiphy->bands[
ieee80211_get_sdata_band(sta->sdata)]; ieee80211_get_sdata_band(sta->sdata)];
rinfo->legacy = sband->bitrates[rate->idx].bitrate; brate = sband->bitrates[rate->idx].bitrate;
rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
} }
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
@ -422,11 +426,13 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
rinfo->mcs = sta->last_rx_rate_idx; rinfo->mcs = sta->last_rx_rate_idx;
} else { } else {
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
u16 brate;
sband = sta->local->hw.wiphy->bands[ sband = sta->local->hw.wiphy->bands[
ieee80211_get_sdata_band(sta->sdata)]; ieee80211_get_sdata_band(sta->sdata)];
rinfo->legacy = brate = sband->bitrates[sta->last_rx_rate_idx].bitrate;
sband->bitrates[sta->last_rx_rate_idx].bitrate; rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
} }
if (sta->last_rx_rate_flag & RX_FLAG_40MHZ) if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
@ -1192,8 +1198,6 @@ static int sta_apply_parameters(struct ieee80211_local *local,
struct station_parameters *params) struct station_parameters *params)
{ {
int ret = 0; int ret = 0;
u32 rates;
int i, j;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
enum ieee80211_band band = ieee80211_get_sdata_band(sdata); enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
@ -1286,16 +1290,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
sta->listen_interval = params->listen_interval; sta->listen_interval = params->listen_interval;
if (params->supported_rates) { if (params->supported_rates) {
rates = 0; ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
sband, params->supported_rates,
for (i = 0; i < params->supported_rates_len; i++) { params->supported_rates_len,
int rate = (params->supported_rates[i] & 0x7f) * 5; &sta->sta.supp_rates[band]);
for (j = 0; j < sband->n_bitrates; j++) {
if (sband->bitrates[j].bitrate == rate)
rates |= BIT(j);
}
}
sta->sta.supp_rates[band] = rates;
} }
if (params->ht_capa) if (params->ht_capa)
@ -1958,18 +1956,11 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
} }
if (params->basic_rates) { if (params->basic_rates) {
int i, j; ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
u32 rates = 0; wiphy->bands[band],
struct ieee80211_supported_band *sband = wiphy->bands[band]; params->basic_rates,
params->basic_rates_len,
for (i = 0; i < params->basic_rates_len; i++) { &sdata->vif.bss_conf.basic_rates);
int rate = (params->basic_rates[i] & 0x7f) * 5;
for (j = 0; j < sband->n_bitrates; j++) {
if (sband->bitrates[j].bitrate == rate)
rates |= BIT(j);
}
}
sdata->vif.bss_conf.basic_rates = rates;
changed |= BSS_CHANGED_BASIC_RATES; changed |= BSS_CHANGED_BASIC_RATES;
} }

View File

@ -19,13 +19,14 @@
#include "ieee80211_i.h" #include "ieee80211_i.h"
#include "rate.h" #include "rate.h"
static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata, static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,
struct ieee80211_ht_cap *ht_capa_mask,
struct ieee80211_sta_ht_cap *ht_cap, struct ieee80211_sta_ht_cap *ht_cap,
u16 flag) u16 flag)
{ {
__le16 le_flag = cpu_to_le16(flag); __le16 le_flag = cpu_to_le16(flag);
if (sdata->u.mgd.ht_capa_mask.cap_info & le_flag) { if (ht_capa_mask->cap_info & le_flag) {
if (!(sdata->u.mgd.ht_capa.cap_info & le_flag)) if (!(ht_capa->cap_info & le_flag))
ht_cap->cap &= ~flag; ht_cap->cap &= ~flag;
} }
} }
@ -33,13 +34,30 @@ static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_ht_cap *ht_cap) struct ieee80211_sta_ht_cap *ht_cap)
{ {
u8 *scaps = (u8 *)(&sdata->u.mgd.ht_capa.mcs.rx_mask); struct ieee80211_ht_cap *ht_capa, *ht_capa_mask;
u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask); u8 *scaps, *smask;
int i; int i;
if (!ht_cap->ht_supported) if (!ht_cap->ht_supported)
return; return;
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
ht_capa = &sdata->u.mgd.ht_capa;
ht_capa_mask = &sdata->u.mgd.ht_capa_mask;
break;
case NL80211_IFTYPE_ADHOC:
ht_capa = &sdata->u.ibss.ht_capa;
ht_capa_mask = &sdata->u.ibss.ht_capa_mask;
break;
default:
WARN_ON_ONCE(1);
return;
}
scaps = (u8 *)(&ht_capa->mcs.rx_mask);
smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);
/* NOTE: If you add more over-rides here, update register_hw /* NOTE: If you add more over-rides here, update register_hw
* ht_capa_mod_msk logic in main.c as well. * ht_capa_mod_msk logic in main.c as well.
* And, if this method can ever change ht_cap.ht_supported, fix * And, if this method can ever change ht_cap.ht_supported, fix
@ -55,28 +73,32 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
} }
/* Force removal of HT-40 capabilities? */ /* Force removal of HT-40 capabilities? */
__check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40); __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
__check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40); IEEE80211_HT_CAP_SUP_WIDTH_20_40);
__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
IEEE80211_HT_CAP_SGI_40);
/* Allow user to disable SGI-20 (SGI-40 is handled above) */ /* Allow user to disable SGI-20 (SGI-40 is handled above) */
__check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_20); __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
IEEE80211_HT_CAP_SGI_20);
/* Allow user to disable the max-AMSDU bit. */ /* Allow user to disable the max-AMSDU bit. */
__check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU); __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
IEEE80211_HT_CAP_MAX_AMSDU);
/* Allow user to decrease AMPDU factor */ /* Allow user to decrease AMPDU factor */
if (sdata->u.mgd.ht_capa_mask.ampdu_params_info & if (ht_capa_mask->ampdu_params_info &
IEEE80211_HT_AMPDU_PARM_FACTOR) { IEEE80211_HT_AMPDU_PARM_FACTOR) {
u8 n = sdata->u.mgd.ht_capa.ampdu_params_info u8 n = ht_capa->ampdu_params_info &
& IEEE80211_HT_AMPDU_PARM_FACTOR; IEEE80211_HT_AMPDU_PARM_FACTOR;
if (n < ht_cap->ampdu_factor) if (n < ht_cap->ampdu_factor)
ht_cap->ampdu_factor = n; ht_cap->ampdu_factor = n;
} }
/* Allow the user to increase AMPDU density. */ /* Allow the user to increase AMPDU density. */
if (sdata->u.mgd.ht_capa_mask.ampdu_params_info & if (ht_capa_mask->ampdu_params_info &
IEEE80211_HT_AMPDU_PARM_DENSITY) { IEEE80211_HT_AMPDU_PARM_DENSITY) {
u8 n = (sdata->u.mgd.ht_capa.ampdu_params_info & u8 n = (ht_capa->ampdu_params_info &
IEEE80211_HT_AMPDU_PARM_DENSITY) IEEE80211_HT_AMPDU_PARM_DENSITY)
>> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
if (n > ht_cap->ampdu_density) if (n > ht_cap->ampdu_density)
@ -112,7 +134,8 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
* we advertised a restricted capability set to. Override * we advertised a restricted capability set to. Override
* our own capabilities and then use those below. * our own capabilities and then use those below.
*/ */
if (sdata->vif.type == NL80211_IFTYPE_STATION && if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
ieee80211_apply_htcap_overrides(sdata, &own_cap); ieee80211_apply_htcap_overrides(sdata, &own_cap);

View File

@ -43,16 +43,18 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
{ {
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
int rates, i; int rates_n = 0, i, ri;
struct ieee80211_mgmt *mgmt; struct ieee80211_mgmt *mgmt;
u8 *pos; u8 *pos;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
struct cfg80211_bss *bss; struct cfg80211_bss *bss;
u32 bss_change; u32 bss_change, rate_flags, rates = 0, rates_added = 0;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
struct cfg80211_chan_def chandef; struct cfg80211_chan_def chandef;
enum nl80211_bss_scan_width scan_width;
bool have_higher_than_11mbit = false;
struct beacon_data *presp; struct beacon_data *presp;
int frame_len; int frame_len;
int shift;
sdata_assert_lock(sdata); sdata_assert_lock(sdata);
@ -83,6 +85,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
chandef = ifibss->chandef; chandef = ifibss->chandef;
if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) { if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
if (chandef.width == NL80211_CHAN_WIDTH_5 ||
chandef.width == NL80211_CHAN_WIDTH_10 ||
chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
chandef.width == NL80211_CHAN_WIDTH_20) {
sdata_info(sdata,
"Failed to join IBSS, beacons forbidden\n");
return;
}
chandef.width = NL80211_CHAN_WIDTH_20; chandef.width = NL80211_CHAN_WIDTH_20;
chandef.center_freq1 = chan->center_freq; chandef.center_freq1 = chan->center_freq;
} }
@ -99,6 +109,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
memcpy(ifibss->bssid, bssid, ETH_ALEN); memcpy(ifibss->bssid, bssid, ETH_ALEN);
sband = local->hw.wiphy->bands[chan->band]; sband = local->hw.wiphy->bands[chan->band];
shift = ieee80211_vif_get_shift(&sdata->vif);
/* Build IBSS probe response */ /* Build IBSS probe response */
frame_len = sizeof(struct ieee80211_hdr_3addr) + frame_len = sizeof(struct ieee80211_hdr_3addr) +
@ -134,15 +145,33 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
memcpy(pos, ifibss->ssid, ifibss->ssid_len); memcpy(pos, ifibss->ssid, ifibss->ssid_len);
pos += ifibss->ssid_len; pos += ifibss->ssid_len;
rates = min_t(int, 8, sband->n_bitrates); rate_flags = ieee80211_chandef_rate_flags(&chandef);
for (i = 0; i < sband->n_bitrates; i++) {
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
continue;
if (sband->bitrates[i].bitrate > 110)
have_higher_than_11mbit = true;
rates |= BIT(i);
rates_n++;
}
*pos++ = WLAN_EID_SUPP_RATES; *pos++ = WLAN_EID_SUPP_RATES;
*pos++ = rates; *pos++ = min_t(int, 8, rates_n);
for (i = 0; i < rates; i++) { for (ri = 0; ri < sband->n_bitrates; ri++) {
int rate = sband->bitrates[i].bitrate; int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
5 * (1 << shift));
u8 basic = 0; u8 basic = 0;
if (basic_rates & BIT(i)) if (!(rates & BIT(ri)))
continue;
if (basic_rates & BIT(ri))
basic = 0x80; basic = 0x80;
*pos++ = basic | (u8) (rate / 5); *pos++ = basic | (u8) rate;
if (++rates_added == 8) {
ri++; /* continue at next rate for EXT_SUPP_RATES */
break;
}
} }
if (sband->band == IEEE80211_BAND_2GHZ) { if (sband->band == IEEE80211_BAND_2GHZ) {
@ -157,15 +186,20 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
*pos++ = 0; *pos++ = 0;
*pos++ = 0; *pos++ = 0;
if (sband->n_bitrates > 8) { /* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
if (rates_n > 8) {
*pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = sband->n_bitrates - 8; *pos++ = rates_n - 8;
for (i = 8; i < sband->n_bitrates; i++) { for (; ri < sband->n_bitrates; ri++) {
int rate = sband->bitrates[i].bitrate; int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
5 * (1 << shift));
u8 basic = 0; u8 basic = 0;
if (basic_rates & BIT(i)) if (!(rates & BIT(ri)))
continue;
if (basic_rates & BIT(ri))
basic = 0x80; basic = 0x80;
*pos++ = basic | (u8) (rate / 5); *pos++ = basic | (u8) rate;
} }
} }
@ -179,8 +213,12 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
chandef.width != NL80211_CHAN_WIDTH_5 && chandef.width != NL80211_CHAN_WIDTH_5 &&
chandef.width != NL80211_CHAN_WIDTH_10 && chandef.width != NL80211_CHAN_WIDTH_10 &&
sband->ht_cap.ht_supported) { sband->ht_cap.ht_supported) {
pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, struct ieee80211_sta_ht_cap ht_cap;
sband->ht_cap.cap);
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
ieee80211_apply_htcap_overrides(sdata, &ht_cap);
pos = ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
/* /*
* Note: According to 802.11n-2009 9.13.3.1, HT Protection * Note: According to 802.11n-2009 9.13.3.1, HT Protection
* field and RIFS Mode are reserved in IBSS mode, therefore * field and RIFS Mode are reserved in IBSS mode, therefore
@ -236,18 +274,26 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.use_short_slot = chan->band == IEEE80211_BAND_5GHZ; sdata->vif.bss_conf.use_short_slot = chan->band == IEEE80211_BAND_5GHZ;
bss_change |= BSS_CHANGED_ERP_SLOT; bss_change |= BSS_CHANGED_ERP_SLOT;
/* cf. IEEE 802.11 9.2.12 */
if (chan->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit)
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
sdata->vif.bss_conf.ibss_joined = true; sdata->vif.bss_conf.ibss_joined = true;
sdata->vif.bss_conf.ibss_creator = creator; sdata->vif.bss_conf.ibss_creator = creator;
ieee80211_bss_info_change_notify(sdata, bss_change); ieee80211_bss_info_change_notify(sdata, bss_change);
ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates); ieee80211_set_wmm_default(sdata, true);
ifibss->state = IEEE80211_IBSS_MLME_JOINED; ifibss->state = IEEE80211_IBSS_MLME_JOINED;
mod_timer(&ifibss->timer, mod_timer(&ifibss->timer,
round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan, scan_width = cfg80211_chandef_to_scan_width(&chandef);
mgmt, presp->head_len, 0, GFP_KERNEL); bss = cfg80211_inform_bss_width_frame(local->hw.wiphy, chan,
scan_width, mgmt,
presp->head_len, 0, GFP_KERNEL);
cfg80211_put_bss(local->hw.wiphy, bss); cfg80211_put_bss(local->hw.wiphy, bss);
netif_carrier_on(sdata->dev); netif_carrier_on(sdata->dev);
cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL); cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
@ -264,6 +310,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
u16 beacon_int = cbss->beacon_interval; u16 beacon_int = cbss->beacon_interval;
const struct cfg80211_bss_ies *ies; const struct cfg80211_bss_ies *ies;
u64 tsf; u64 tsf;
u32 rate_flags;
int shift;
sdata_assert_lock(sdata); sdata_assert_lock(sdata);
@ -271,15 +319,24 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
beacon_int = 10; beacon_int = 10;
sband = sdata->local->hw.wiphy->bands[cbss->channel->band]; sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
rate_flags = ieee80211_chandef_rate_flags(&sdata->u.ibss.chandef);
shift = ieee80211_vif_get_shift(&sdata->vif);
basic_rates = 0; basic_rates = 0;
for (i = 0; i < bss->supp_rates_len; i++) { for (i = 0; i < bss->supp_rates_len; i++) {
int rate = (bss->supp_rates[i] & 0x7f) * 5; int rate = bss->supp_rates[i] & 0x7f;
bool is_basic = !!(bss->supp_rates[i] & 0x80); bool is_basic = !!(bss->supp_rates[i] & 0x80);
for (j = 0; j < sband->n_bitrates; j++) { for (j = 0; j < sband->n_bitrates; j++) {
if (sband->bitrates[j].bitrate == rate) { int brate;
if ((rate_flags & sband->bitrates[j].flags)
!= rate_flags)
continue;
brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
5 * (1 << shift));
if (brate == rate) {
if (is_basic) if (is_basic)
basic_rates |= BIT(j); basic_rates |= BIT(j);
break; break;
@ -335,6 +392,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
struct sta_info *sta; struct sta_info *sta;
struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
enum nl80211_bss_scan_width scan_width;
int band; int band;
/* /*
@ -363,6 +421,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
if (WARN_ON_ONCE(!chanctx_conf)) if (WARN_ON_ONCE(!chanctx_conf))
return NULL; return NULL;
band = chanctx_conf->def.chan->band; band = chanctx_conf->def.chan->band;
scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
rcu_read_unlock(); rcu_read_unlock();
sta = sta_info_alloc(sdata, addr, GFP_KERNEL); sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
@ -376,7 +435,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
/* make sure mandatory rates are always added */ /* make sure mandatory rates are always added */
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
sta->sta.supp_rates[band] = supp_rates | sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(sband); ieee80211_mandatory_rates(sband, scan_width);
return ieee80211_ibss_finish_sta(sta); return ieee80211_ibss_finish_sta(sta);
} }
@ -440,6 +499,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
u64 beacon_timestamp, rx_timestamp; u64 beacon_timestamp, rx_timestamp;
u32 supp_rates = 0; u32 supp_rates = 0;
enum ieee80211_band band = rx_status->band; enum ieee80211_band band = rx_status->band;
enum nl80211_bss_scan_width scan_width;
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
bool rates_updated = false; bool rates_updated = false;
@ -461,16 +521,22 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sta = sta_info_get(sdata, mgmt->sa); sta = sta_info_get(sdata, mgmt->sa);
if (elems->supp_rates) { if (elems->supp_rates) {
supp_rates = ieee80211_sta_get_rates(local, elems, supp_rates = ieee80211_sta_get_rates(sdata, elems,
band, NULL); band, NULL);
if (sta) { if (sta) {
u32 prev_rates; u32 prev_rates;
prev_rates = sta->sta.supp_rates[band]; prev_rates = sta->sta.supp_rates[band];
/* make sure mandatory rates are always added */ /* make sure mandatory rates are always added */
sta->sta.supp_rates[band] = supp_rates | scan_width = NL80211_BSS_CHAN_WIDTH_20;
ieee80211_mandatory_rates(sband); if (rx_status->flag & RX_FLAG_5MHZ)
scan_width = NL80211_BSS_CHAN_WIDTH_5;
if (rx_status->flag & RX_FLAG_10MHZ)
scan_width = NL80211_BSS_CHAN_WIDTH_10;
sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(sband,
scan_width);
if (sta->sta.supp_rates[band] != prev_rates) { if (sta->sta.supp_rates[band] != prev_rates) {
ibss_dbg(sdata, ibss_dbg(sdata,
"updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n", "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
@ -585,7 +651,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
"beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n", "beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n",
mgmt->bssid); mgmt->bssid);
ieee80211_sta_join_ibss(sdata, bss); ieee80211_sta_join_ibss(sdata, bss);
supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL); supp_rates = ieee80211_sta_get_rates(sdata, elems, band, NULL);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
supp_rates); supp_rates);
rcu_read_unlock(); rcu_read_unlock();
@ -604,6 +670,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta; struct sta_info *sta;
struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
enum nl80211_bss_scan_width scan_width;
int band; int band;
/* /*
@ -629,6 +696,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
return; return;
} }
band = chanctx_conf->def.chan->band; band = chanctx_conf->def.chan->band;
scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
rcu_read_unlock(); rcu_read_unlock();
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
@ -640,7 +708,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
/* make sure mandatory rates are always added */ /* make sure mandatory rates are always added */
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
sta->sta.supp_rates[band] = supp_rates | sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(sband); ieee80211_mandatory_rates(sband, scan_width);
spin_lock(&ifibss->incomplete_lock); spin_lock(&ifibss->incomplete_lock);
list_add(&sta->list, &ifibss->incomplete_stations); list_add(&sta->list, &ifibss->incomplete_stations);
@ -679,6 +747,7 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
enum nl80211_bss_scan_width scan_width;
sdata_assert_lock(sdata); sdata_assert_lock(sdata);
@ -700,8 +769,9 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
sdata_info(sdata, sdata_info(sdata,
"No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n"); "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len, ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
NULL); NULL, scan_width);
} }
static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@ -751,6 +821,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
struct cfg80211_bss *cbss; struct cfg80211_bss *cbss;
struct ieee80211_channel *chan = NULL; struct ieee80211_channel *chan = NULL;
const u8 *bssid = NULL; const u8 *bssid = NULL;
enum nl80211_bss_scan_width scan_width;
int active_ibss; int active_ibss;
u16 capability; u16 capability;
@ -799,8 +870,10 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
IEEE80211_SCAN_INTERVAL)) { IEEE80211_SCAN_INTERVAL)) {
sdata_info(sdata, "Trigger new scan to find an IBSS to join\n"); sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
ieee80211_request_ibss_scan(sdata, ifibss->ssid, ieee80211_request_ibss_scan(sdata, ifibss->ssid,
ifibss->ssid_len, chan); ifibss->ssid_len, chan,
scan_width);
} else { } else {
int interval = IEEE80211_SCAN_INTERVAL; int interval = IEEE80211_SCAN_INTERVAL;
@ -1020,6 +1093,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
struct cfg80211_ibss_params *params) struct cfg80211_ibss_params *params)
{ {
u32 changed = 0; u32 changed = 0;
u32 rate_flags;
struct ieee80211_supported_band *sband;
int i;
if (params->bssid) { if (params->bssid) {
memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN); memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
@ -1030,6 +1106,14 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->u.ibss.privacy = params->privacy; sdata->u.ibss.privacy = params->privacy;
sdata->u.ibss.control_port = params->control_port; sdata->u.ibss.control_port = params->control_port;
sdata->u.ibss.basic_rates = params->basic_rates; sdata->u.ibss.basic_rates = params->basic_rates;
/* fix basic_rates if channel does not support these rates */
rate_flags = ieee80211_chandef_rate_flags(&params->chandef);
sband = sdata->local->hw.wiphy->bands[params->chandef.chan->band];
for (i = 0; i < sband->n_bitrates; i++) {
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
sdata->u.ibss.basic_rates &= ~BIT(i);
}
memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate, memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate,
sizeof(params->mcast_rate)); sizeof(params->mcast_rate));
@ -1051,6 +1135,11 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len); memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);
sdata->u.ibss.ssid_len = params->ssid_len; sdata->u.ibss.ssid_len = params->ssid_len;
memcpy(&sdata->u.ibss.ht_capa, &params->ht_capa,
sizeof(sdata->u.ibss.ht_capa));
memcpy(&sdata->u.ibss.ht_capa_mask, &params->ht_capa_mask,
sizeof(sdata->u.ibss.ht_capa_mask));
/* /*
* 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
* reserved, but an HT STA shall protect HT transmissions as though * reserved, but an HT STA shall protect HT transmissions as though
@ -1131,6 +1220,11 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
presp = rcu_dereference_protected(ifibss->presp, presp = rcu_dereference_protected(ifibss->presp,
lockdep_is_held(&sdata->wdev.mtx)); lockdep_is_held(&sdata->wdev.mtx));
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
/* on the next join, re-program HT parameters */
memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
sdata->vif.bss_conf.ibss_joined = false; sdata->vif.bss_conf.ibss_joined = false;
sdata->vif.bss_conf.ibss_creator = false; sdata->vif.bss_conf.ibss_creator = false;
sdata->vif.bss_conf.enable_beacon = false; sdata->vif.bss_conf.enable_beacon = false;

View File

@ -509,6 +509,9 @@ struct ieee80211_if_ibss {
/* probe response/beacon for IBSS */ /* probe response/beacon for IBSS */
struct beacon_data __rcu *presp; struct beacon_data __rcu *presp;
struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */
struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
spinlock_t incomplete_lock; spinlock_t incomplete_lock;
struct list_head incomplete_stations; struct list_head incomplete_stations;
@ -809,6 +812,34 @@ ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
return band; return band;
} }
static inline int
ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef)
{
switch (chandef->width) {
case NL80211_CHAN_WIDTH_5:
return 2;
case NL80211_CHAN_WIDTH_10:
return 1;
default:
return 0;
}
}
static inline int
ieee80211_vif_get_shift(struct ieee80211_vif *vif)
{
struct ieee80211_chanctx_conf *chanctx_conf;
int shift = 0;
rcu_read_lock();
chanctx_conf = rcu_dereference(vif->chanctx_conf);
if (chanctx_conf)
shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
rcu_read_unlock();
return shift;
}
enum sdata_queue_type { enum sdata_queue_type {
IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0, IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0,
IEEE80211_SDATA_QUEUE_AGG_START = 1, IEEE80211_SDATA_QUEUE_AGG_START = 1,
@ -1026,7 +1057,7 @@ struct ieee80211_local {
struct cfg80211_ssid scan_ssid; struct cfg80211_ssid scan_ssid;
struct cfg80211_scan_request *int_scan_req; struct cfg80211_scan_request *int_scan_req;
struct cfg80211_scan_request *scan_req, *hw_scan_req; struct cfg80211_scan_request *scan_req, *hw_scan_req;
struct ieee80211_channel *scan_channel; struct cfg80211_chan_def scan_chandef;
enum ieee80211_band hw_scan_band; enum ieee80211_band hw_scan_band;
int scan_channel_idx; int scan_channel_idx;
int scan_ies_len; int scan_ies_len;
@ -1306,7 +1337,8 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
void ieee80211_scan_work(struct work_struct *work); void ieee80211_scan_work(struct work_struct *work);
int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
const u8 *ssid, u8 ssid_len, const u8 *ssid, u8 ssid_len,
struct ieee80211_channel *chan); struct ieee80211_channel *chan,
enum nl80211_bss_scan_width scan_width);
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req); struct cfg80211_scan_request *req);
void ieee80211_scan_cancel(struct ieee80211_local *local); void ieee80211_scan_cancel(struct ieee80211_local *local);
@ -1465,7 +1497,8 @@ extern void *mac80211_wiphy_privid; /* for wiphy privid */
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
enum nl80211_iftype type); enum nl80211_iftype type);
int ieee80211_frame_duration(enum ieee80211_band band, size_t len, int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
int rate, int erp, int short_preamble); int rate, int erp, int short_preamble,
int shift);
void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx, void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
struct ieee80211_hdr *hdr, const u8 *tsc, struct ieee80211_hdr *hdr, const u8 *tsc,
gfp_t gfp); gfp_t gfp);
@ -1569,7 +1602,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
size_t buffer_len, const u8 *ie, size_t ie_len, size_t buffer_len, const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask, enum ieee80211_band band, u32 rate_mask,
u8 channel); struct cfg80211_chan_def *chandef);
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
u8 *dst, u32 ratemask, u8 *dst, u32 ratemask,
struct ieee80211_channel *chan, struct ieee80211_channel *chan,
@ -1582,10 +1615,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
u32 ratemask, bool directed, u32 tx_flags, u32 ratemask, bool directed, u32 tx_flags,
struct ieee80211_channel *channel, bool scan); struct ieee80211_channel *channel, bool scan);
void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
const size_t supp_rates_len,
const u8 *supp_rates);
u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
struct ieee802_11_elems *elems, struct ieee802_11_elems *elems,
enum ieee80211_band band, u32 *basic_rates); enum ieee80211_band band, u32 *basic_rates);
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
@ -1602,6 +1632,9 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
u16 prot_mode); u16 prot_mode);
u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u32 cap); u32 cap);
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates);
int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, bool need_basic, struct sk_buff *skb, bool need_basic,
enum ieee80211_band band); enum ieee80211_band band);

View File

@ -54,7 +54,7 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
return false; return false;
} }
power = chanctx_conf->def.chan->max_power; power = ieee80211_chandef_max_power(&chanctx_conf->def);
rcu_read_unlock(); rcu_read_unlock();
if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL) if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)

View File

@ -102,17 +102,8 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
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_chandef.chan) {
chandef.chan = local->scan_channel; chandef = local->scan_chandef;
/* If scanning on oper channel, use whatever channel-type
* is currently in use.
*/
if (chandef.chan == local->_oper_chandef.chan) {
chandef = local->_oper_chandef;
} else {
chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
chandef.center_freq1 = chandef.chan->center_freq;
}
} else if (local->tmp_channel) { } else if (local->tmp_channel) {
chandef.chan = local->tmp_channel; chandef.chan = local->tmp_channel;
chandef.width = NL80211_CHAN_WIDTH_20_NOHT; chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
@ -151,7 +142,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
changed |= IEEE80211_CONF_CHANGE_SMPS; changed |= IEEE80211_CONF_CHANGE_SMPS;
} }
power = chandef.chan->max_power; power = ieee80211_chandef_max_power(&chandef);
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) { list_for_each_entry_rcu(sdata, &local->interfaces, list) {

View File

@ -62,7 +62,6 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *ie) struct ieee802_11_elems *ie)
{ {
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
u32 basic_rates = 0; u32 basic_rates = 0;
struct cfg80211_chan_def sta_chan_def; struct cfg80211_chan_def sta_chan_def;
@ -85,7 +84,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
(ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth))) (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
return false; return false;
ieee80211_sta_get_rates(local, ie, ieee80211_get_sdata_band(sdata), ieee80211_sta_get_rates(sdata, ie, ieee80211_get_sdata_band(sdata),
&basic_rates); &basic_rates);
if (sdata->vif.bss_conf.basic_rates != basic_rates) if (sdata->vif.bss_conf.basic_rates != basic_rates)
@ -274,7 +273,9 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS); neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);
*pos++ = neighbors << 1; *pos++ = neighbors << 1;
/* Mesh capability */ /* Mesh capability */
*pos = IEEE80211_MESHCONF_CAPAB_FORWARDING; *pos = 0x00;
*pos |= ifmsh->mshcfg.dot11MeshForwarding ?
IEEE80211_MESHCONF_CAPAB_FORWARDING : 0x00;
*pos |= ifmsh->accepting_plinks ? *pos |= ifmsh->accepting_plinks ?
IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
/* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */ /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */

View File

@ -379,7 +379,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
u32 rates, basic_rates = 0, changed = 0; u32 rates, basic_rates = 0, changed = 0;
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates); rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->lock);
sta->last_rx = jiffies; sta->last_rx = jiffies;

View File

@ -478,27 +478,6 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
/* frame sending functions */ /* frame sending functions */
static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
struct ieee80211_supported_band *sband,
u32 *rates)
{
int i, j, count;
*rates = 0;
count = 0;
for (i = 0; i < supp_rates_len; i++) {
int rate = (supp_rates[i] & 0x7F) * 5;
for (j = 0; j < sband->n_bitrates; j++)
if (sband->bitrates[j].bitrate == rate) {
*rates |= BIT(j);
count++;
break;
}
}
return count;
}
static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, u8 ap_ht_param, struct sk_buff *skb, u8 ap_ht_param,
struct ieee80211_supported_band *sband, struct ieee80211_supported_band *sband,
@ -617,12 +596,12 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
struct ieee80211_mgmt *mgmt; struct ieee80211_mgmt *mgmt;
u8 *pos, qos_info; u8 *pos, qos_info;
size_t offset = 0, noffset; size_t offset = 0, noffset;
int i, count, rates_len, supp_rates_len; int i, count, rates_len, supp_rates_len, shift;
u16 capab; u16 capab;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *chan; struct ieee80211_channel *chan;
u32 rates = 0; u32 rate_flags, rates = 0;
sdata_assert_lock(sdata); sdata_assert_lock(sdata);
@ -633,8 +612,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
return; return;
} }
chan = chanctx_conf->def.chan; chan = chanctx_conf->def.chan;
rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
rcu_read_unlock(); rcu_read_unlock();
sband = local->hw.wiphy->bands[chan->band]; sband = local->hw.wiphy->bands[chan->band];
shift = ieee80211_vif_get_shift(&sdata->vif);
if (assoc_data->supp_rates_len) { if (assoc_data->supp_rates_len) {
/* /*
@ -643,17 +624,24 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
* in the association request (e.g. D-Link DAP 1353 in * in the association request (e.g. D-Link DAP 1353 in
* b-only mode)... * b-only mode)...
*/ */
rates_len = ieee80211_compatible_rates(assoc_data->supp_rates, rates_len = ieee80211_parse_bitrates(&chanctx_conf->def, sband,
assoc_data->supp_rates_len, assoc_data->supp_rates,
sband, &rates); assoc_data->supp_rates_len,
&rates);
} else { } else {
/* /*
* In case AP not provide any supported rates information * In case AP not provide any supported rates information
* before association, we send information element(s) with * before association, we send information element(s) with
* all rates that we support. * all rates that we support.
*/ */
rates = ~0; rates_len = 0;
rates_len = sband->n_bitrates; for (i = 0; i < sband->n_bitrates; i++) {
if ((rate_flags & sband->bitrates[i].flags)
!= rate_flags)
continue;
rates |= BIT(i);
rates_len++;
}
} }
skb = alloc_skb(local->hw.extra_tx_headroom + skb = alloc_skb(local->hw.extra_tx_headroom +
@ -730,8 +718,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
count = 0; count = 0;
for (i = 0; i < sband->n_bitrates; i++) { for (i = 0; i < sband->n_bitrates; i++) {
if (BIT(i) & rates) { if (BIT(i) & rates) {
int rate = sband->bitrates[i].bitrate; int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
*pos++ = (u8) (rate / 5); 5 * (1 << shift));
*pos++ = (u8) rate;
if (++count == 8) if (++count == 8)
break; break;
} }
@ -744,8 +733,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
for (i++; i < sband->n_bitrates; i++) { for (i++; i < sband->n_bitrates; i++) {
if (BIT(i) & rates) { if (BIT(i) & rates) {
int rate = sband->bitrates[i].bitrate; int rate;
*pos++ = (u8) (rate / 5); rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
5 * (1 << shift));
*pos++ = (u8) rate;
} }
} }
} }
@ -756,7 +747,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
*pos++ = WLAN_EID_PWR_CAPABILITY; *pos++ = WLAN_EID_PWR_CAPABILITY;
*pos++ = 2; *pos++ = 2;
*pos++ = 0; /* min tx power */ *pos++ = 0; /* min tx power */
*pos++ = chan->max_power; /* max tx power */ /* max tx power */
*pos++ = ieee80211_chandef_max_power(&chanctx_conf->def);
/* 2. supported channels */ /* 2. supported channels */
/* TODO: get this in reg domain format */ /* TODO: get this in reg domain format */
@ -2432,15 +2424,16 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
u8 *supp_rates, unsigned int supp_rates_len, u8 *supp_rates, unsigned int supp_rates_len,
u32 *rates, u32 *basic_rates, u32 *rates, u32 *basic_rates,
bool *have_higher_than_11mbit, bool *have_higher_than_11mbit,
int *min_rate, int *min_rate_index) int *min_rate, int *min_rate_index,
int shift, u32 rate_flags)
{ {
int i, j; int i, j;
for (i = 0; i < supp_rates_len; i++) { for (i = 0; i < supp_rates_len; i++) {
int rate = (supp_rates[i] & 0x7f) * 5; int rate = supp_rates[i] & 0x7f;
bool is_basic = !!(supp_rates[i] & 0x80); bool is_basic = !!(supp_rates[i] & 0x80);
if (rate > 110) if ((rate * 5 * (1 << shift)) > 110)
*have_higher_than_11mbit = true; *have_higher_than_11mbit = true;
/* /*
@ -2456,12 +2449,20 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
continue; continue;
for (j = 0; j < sband->n_bitrates; j++) { for (j = 0; j < sband->n_bitrates; j++) {
if (sband->bitrates[j].bitrate == rate) { struct ieee80211_rate *br;
int brate;
br = &sband->bitrates[j];
if ((rate_flags & br->flags) != rate_flags)
continue;
brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
if (brate == rate) {
*rates |= BIT(j); *rates |= BIT(j);
if (is_basic) if (is_basic)
*basic_rates |= BIT(j); *basic_rates |= BIT(j);
if (rate < *min_rate) { if ((rate * 5) < *min_rate) {
*min_rate = rate; *min_rate = rate * 5;
*min_rate_index = j; *min_rate_index = j;
} }
break; break;
@ -3884,27 +3885,40 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
if (!new_sta) if (!new_sta)
return -ENOMEM; return -ENOMEM;
} }
if (new_sta) { if (new_sta) {
u32 rates = 0, basic_rates = 0; u32 rates = 0, basic_rates = 0;
bool have_higher_than_11mbit; bool have_higher_than_11mbit;
int min_rate = INT_MAX, min_rate_index = -1; int min_rate = INT_MAX, min_rate_index = -1;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
const struct cfg80211_bss_ies *ies; const struct cfg80211_bss_ies *ies;
int shift;
u32 rate_flags;
sband = local->hw.wiphy->bands[cbss->channel->band]; sband = local->hw.wiphy->bands[cbss->channel->band];
err = ieee80211_prep_channel(sdata, cbss); err = ieee80211_prep_channel(sdata, cbss);
if (err) { if (err) {
sta_info_free(local, new_sta); sta_info_free(local, new_sta);
return err; return -EINVAL;
} }
shift = ieee80211_vif_get_shift(&sdata->vif);
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {
rcu_read_unlock();
return -EINVAL;
}
rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
rcu_read_unlock();
ieee80211_get_rates(sband, bss->supp_rates, ieee80211_get_rates(sband, bss->supp_rates,
bss->supp_rates_len, bss->supp_rates_len,
&rates, &basic_rates, &rates, &basic_rates,
&have_higher_than_11mbit, &have_higher_than_11mbit,
&min_rate, &min_rate_index); &min_rate, &min_rate_index,
shift, rate_flags);
/* /*
* This used to be a workaround for basic rates missing * This used to be a workaround for basic rates missing

View File

@ -232,37 +232,28 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
/* could not find a basic rate; use original selection */ /* could not find a basic rate; use original selection */
} }
static inline s8
rate_lowest_non_cck_index(struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta)
{
int i;
for (i = 0; i < sband->n_bitrates; i++) {
struct ieee80211_rate *srate = &sband->bitrates[i];
if ((srate->bitrate == 10) || (srate->bitrate == 20) ||
(srate->bitrate == 55) || (srate->bitrate == 110))
continue;
if (rate_supported(sta, sband->band, i))
return i;
}
/* No matching rate found */
return 0;
}
static void __rate_control_send_low(struct ieee80211_hw *hw, static void __rate_control_send_low(struct ieee80211_hw *hw,
struct ieee80211_supported_band *sband, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
struct ieee80211_tx_info *info) struct ieee80211_tx_info *info)
{ {
if ((sband->band != IEEE80211_BAND_2GHZ) || int i;
!(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) u32 rate_flags =
info->control.rates[0].idx = rate_lowest_index(sband, sta); ieee80211_chandef_rate_flags(&hw->conf.chandef);
else
info->control.rates[0].idx = if ((sband->band == IEEE80211_BAND_2GHZ) &&
rate_lowest_non_cck_index(sband, sta); (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
rate_flags |= IEEE80211_RATE_ERP_G;
info->control.rates[0].idx = 0;
for (i = 0; i < sband->n_bitrates; i++) {
if (!rate_supported(sta, sband->band, i))
continue;
info->control.rates[0].idx = i;
break;
}
WARN_ON_ONCE(i == sband->n_bitrates);
info->control.rates[0].count = info->control.rates[0].count =
(info->flags & IEEE80211_TX_CTL_NO_ACK) ? (info->flags & IEEE80211_TX_CTL_NO_ACK) ?
@ -585,6 +576,7 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]; u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
bool has_mcs_mask; bool has_mcs_mask;
u32 mask; u32 mask;
u32 rate_flags;
int i; int i;
/* /*
@ -594,6 +586,12 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
*/ */
mask = sdata->rc_rateidx_mask[info->band]; mask = sdata->rc_rateidx_mask[info->band];
has_mcs_mask = sdata->rc_has_mcs_mask[info->band]; has_mcs_mask = sdata->rc_has_mcs_mask[info->band];
rate_flags =
ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
for (i = 0; i < sband->n_bitrates; i++)
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
mask &= ~BIT(i);
if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask) if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask)
return; return;

View File

@ -66,11 +66,12 @@ static inline void rate_control_rate_init(struct sta_info *sta)
} }
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band]; sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
rcu_read_unlock();
ieee80211_sta_set_rx_nss(sta); ieee80211_sta_set_rx_nss(sta);
ref->ops->rate_init(ref->priv, sband, ista, priv_sta); ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
priv_sta);
rcu_read_unlock();
set_sta_flag(sta, WLAN_STA_RATE_CONTROL); set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
} }
@ -81,10 +82,21 @@ static inline void rate_control_rate_update(struct ieee80211_local *local,
struct rate_control_ref *ref = local->rate_ctrl; struct rate_control_ref *ref = local->rate_ctrl;
struct ieee80211_sta *ista = &sta->sta; struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv; void *priv_sta = sta->rate_ctrl_priv;
struct ieee80211_chanctx_conf *chanctx_conf;
if (ref && ref->ops->rate_update) if (ref && ref->ops->rate_update) {
ref->ops->rate_update(ref->priv, sband, ista, rcu_read_lock();
priv_sta, changed);
chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {
rcu_read_unlock();
return;
}
ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
ista, priv_sta, changed);
rcu_read_unlock();
}
drv_sta_rc_update(local, sta->sdata, &sta->sta, changed); drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
} }

View File

@ -383,14 +383,18 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
static void static void
calc_rate_durations(enum ieee80211_band band, calc_rate_durations(enum ieee80211_band band,
struct minstrel_rate *d, struct minstrel_rate *d,
struct ieee80211_rate *rate) struct ieee80211_rate *rate,
struct cfg80211_chan_def *chandef)
{ {
int erp = !!(rate->flags & IEEE80211_RATE_ERP_G); int erp = !!(rate->flags & IEEE80211_RATE_ERP_G);
int shift = ieee80211_chandef_get_shift(chandef);
d->perfect_tx_time = ieee80211_frame_duration(band, 1200, d->perfect_tx_time = ieee80211_frame_duration(band, 1200,
rate->bitrate, erp, 1); DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
shift);
d->ack_time = ieee80211_frame_duration(band, 10, d->ack_time = ieee80211_frame_duration(band, 10,
rate->bitrate, erp, 1); DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
shift);
} }
static void static void
@ -418,21 +422,25 @@ init_sample_table(struct minstrel_sta_info *mi)
static void static void
minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta) struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta)
{ {
struct minstrel_sta_info *mi = priv_sta; struct minstrel_sta_info *mi = priv_sta;
struct minstrel_priv *mp = priv; struct minstrel_priv *mp = priv;
struct ieee80211_rate *ctl_rate; struct ieee80211_rate *ctl_rate;
unsigned int i, n = 0; unsigned int i, n = 0;
unsigned int t_slot = 9; /* FIXME: get real slot time */ unsigned int t_slot = 9; /* FIXME: get real slot time */
u32 rate_flags;
mi->sta = sta; mi->sta = sta;
mi->lowest_rix = rate_lowest_index(sband, sta); mi->lowest_rix = rate_lowest_index(sband, sta);
ctl_rate = &sband->bitrates[mi->lowest_rix]; ctl_rate = &sband->bitrates[mi->lowest_rix];
mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10, mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10,
ctl_rate->bitrate, ctl_rate->bitrate,
!!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1); !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1,
ieee80211_chandef_get_shift(chandef));
rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate)); memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate));
mi->max_prob_rate = 0; mi->max_prob_rate = 0;
@ -441,15 +449,22 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0; unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0;
unsigned int tx_time_single; unsigned int tx_time_single;
unsigned int cw = mp->cw_min; unsigned int cw = mp->cw_min;
int shift;
if (!rate_supported(sta, sband->band, i)) if (!rate_supported(sta, sband->band, i))
continue; continue;
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
continue;
n++; n++;
memset(mr, 0, sizeof(*mr)); memset(mr, 0, sizeof(*mr));
mr->rix = i; mr->rix = i;
mr->bitrate = sband->bitrates[i].bitrate / 5; shift = ieee80211_chandef_get_shift(chandef);
calc_rate_durations(sband->band, mr, &sband->bitrates[i]); mr->bitrate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
(1 << shift) * 5);
calc_rate_durations(sband->band, mr, &sband->bitrates[i],
chandef);
/* calculate maximum number of retransmissions before /* calculate maximum number of retransmissions before
* fallback (based on maximum segment size) */ * fallback (based on maximum segment size) */
@ -547,6 +562,7 @@ minstrel_init_cck_rates(struct minstrel_priv *mp)
{ {
static const int bitrates[4] = { 10, 20, 55, 110 }; static const int bitrates[4] = { 10, 20, 55, 110 };
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
int i, j; int i, j;
sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
@ -559,6 +575,9 @@ minstrel_init_cck_rates(struct minstrel_priv *mp)
if (rate->flags & IEEE80211_RATE_ERP_G) if (rate->flags & IEEE80211_RATE_ERP_G)
continue; continue;
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
continue;
for (j = 0; j < ARRAY_SIZE(bitrates); j++) { for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
if (rate->bitrate != bitrates[j]) if (rate->bitrate != bitrates[j])
continue; continue;

View File

@ -844,6 +844,7 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
static void static void
minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta) struct ieee80211_sta *sta, void *priv_sta)
{ {
struct minstrel_priv *mp = priv; struct minstrel_priv *mp = priv;
@ -869,8 +870,9 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
mi->sta = sta; mi->sta = sta;
mi->stats_update = jiffies; mi->stats_update = jiffies;
ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1); ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0);
mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1) + ack_dur; mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0);
mi->overhead += ack_dur;
mi->overhead_rtscts = mi->overhead + 2 * ack_dur; mi->overhead_rtscts = mi->overhead + 2 * ack_dur;
mi->avg_ampdu_len = MINSTREL_FRAC(1, 1); mi->avg_ampdu_len = MINSTREL_FRAC(1, 1);
@ -939,22 +941,25 @@ use_legacy:
memset(&msp->legacy, 0, sizeof(msp->legacy)); memset(&msp->legacy, 0, sizeof(msp->legacy));
msp->legacy.r = msp->ratelist; msp->legacy.r = msp->ratelist;
msp->legacy.sample_table = msp->sample_table; msp->legacy.sample_table = msp->sample_table;
return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy); return mac80211_minstrel.rate_init(priv, sband, chandef, sta,
&msp->legacy);
} }
static void static void
minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband, minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta) struct ieee80211_sta *sta, void *priv_sta)
{ {
minstrel_ht_update_caps(priv, sband, sta, priv_sta); minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
} }
static void static void
minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband, minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_sta *sta, void *priv_sta,
u32 changed) u32 changed)
{ {
minstrel_ht_update_caps(priv, sband, sta, priv_sta); minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
} }
static void * static void *

View File

@ -293,6 +293,7 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
static void static void
rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta) struct ieee80211_sta *sta, void *priv_sta)
{ {
struct rc_pid_sta_info *spinfo = priv_sta; struct rc_pid_sta_info *spinfo = priv_sta;

View File

@ -87,11 +87,13 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
int len; int len;
/* always present fields */ /* always present fields */
len = sizeof(struct ieee80211_radiotap_header) + 9; len = sizeof(struct ieee80211_radiotap_header) + 8;
/* allocate extra bitmap */ /* allocate extra bitmaps */
if (status->vendor_radiotap_len) if (status->vendor_radiotap_len)
len += 4; len += 4;
if (status->chains)
len += 4 * hweight8(status->chains);
if (ieee80211_have_rx_timestamp(status)) { if (ieee80211_have_rx_timestamp(status)) {
len = ALIGN(len, 8); len = ALIGN(len, 8);
@ -100,6 +102,10 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
len += 1; len += 1;
/* antenna field, if we don't have per-chain info */
if (!status->chains)
len += 1;
/* padding for RX_FLAGS if necessary */ /* padding for RX_FLAGS if necessary */
len = ALIGN(len, 2); len = ALIGN(len, 2);
@ -116,6 +122,11 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
len += 12; len += 12;
} }
if (status->chains) {
/* antenna and antenna signal fields */
len += 2 * hweight8(status->chains);
}
if (status->vendor_radiotap_len) { if (status->vendor_radiotap_len) {
if (WARN_ON_ONCE(status->vendor_radiotap_align == 0)) if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
status->vendor_radiotap_align = 1; status->vendor_radiotap_align = 1;
@ -145,8 +156,12 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_header *rthdr; struct ieee80211_radiotap_header *rthdr;
unsigned char *pos; unsigned char *pos;
__le32 *it_present;
u32 it_present_val;
u16 rx_flags = 0; u16 rx_flags = 0;
int mpdulen; u16 channel_flags = 0;
int mpdulen, chain;
unsigned long chains = status->chains;
mpdulen = skb->len; mpdulen = skb->len;
if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS))) if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
@ -154,25 +169,39 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len); rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
memset(rthdr, 0, rtap_len); memset(rthdr, 0, rtap_len);
it_present = &rthdr->it_present;
/* radiotap header, set always present flags */ /* radiotap header, set always present flags */
rthdr->it_present =
cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_ANTENNA) |
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len); rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) |
BIT(IEEE80211_RADIOTAP_CHANNEL) |
BIT(IEEE80211_RADIOTAP_RX_FLAGS);
pos = (unsigned char *)(rthdr + 1); if (!status->chains)
it_present_val |= BIT(IEEE80211_RADIOTAP_ANTENNA);
for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
it_present_val |=
BIT(IEEE80211_RADIOTAP_EXT) |
BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
put_unaligned_le32(it_present_val, it_present);
it_present++;
it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
}
if (status->vendor_radiotap_len) { if (status->vendor_radiotap_len) {
rthdr->it_present |= it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
cpu_to_le32(BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) | BIT(IEEE80211_RADIOTAP_EXT);
cpu_to_le32(BIT(IEEE80211_RADIOTAP_EXT)); put_unaligned_le32(it_present_val, it_present);
put_unaligned_le32(status->vendor_radiotap_bitmap, pos); it_present++;
pos += 4; it_present_val = status->vendor_radiotap_bitmap;
} }
put_unaligned_le32(it_present_val, it_present);
pos = (void *)(it_present + 1);
/* the order of the following fields is important */ /* the order of the following fields is important */
/* IEEE80211_RADIOTAP_TSFT */ /* IEEE80211_RADIOTAP_TSFT */
@ -207,28 +236,35 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*/ */
*pos = 0; *pos = 0;
} else { } else {
int shift = 0;
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
*pos = rate->bitrate / 5; if (status->flag & RX_FLAG_10MHZ)
shift = 1;
else if (status->flag & RX_FLAG_5MHZ)
shift = 2;
*pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift));
} }
pos++; pos++;
/* IEEE80211_RADIOTAP_CHANNEL */ /* IEEE80211_RADIOTAP_CHANNEL */
put_unaligned_le16(status->freq, pos); put_unaligned_le16(status->freq, pos);
pos += 2; pos += 2;
if (status->flag & RX_FLAG_10MHZ)
channel_flags |= IEEE80211_CHAN_HALF;
else if (status->flag & RX_FLAG_5MHZ)
channel_flags |= IEEE80211_CHAN_QUARTER;
if (status->band == IEEE80211_BAND_5GHZ) if (status->band == IEEE80211_BAND_5GHZ)
put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ;
pos);
else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ, channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
pos);
else if (rate && rate->flags & IEEE80211_RATE_ERP_G) else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ, channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
pos);
else if (rate) else if (rate)
put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ, channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
pos);
else else
put_unaligned_le16(IEEE80211_CHAN_2GHZ, pos); channel_flags |= IEEE80211_CHAN_2GHZ;
put_unaligned_le16(channel_flags, pos);
pos += 2; pos += 2;
/* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */ /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
@ -242,9 +278,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */ /* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */
/* IEEE80211_RADIOTAP_ANTENNA */ if (!status->chains) {
*pos = status->antenna; /* IEEE80211_RADIOTAP_ANTENNA */
pos++; *pos = status->antenna;
pos++;
}
/* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */ /* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */
@ -341,6 +379,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos += 2; pos += 2;
} }
for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
*pos++ = status->chain_signal[chain];
*pos++ = chain;
}
if (status->vendor_radiotap_len) { if (status->vendor_radiotap_len) {
/* ensure 2 byte alignment for the vendor field as required */ /* ensure 2 byte alignment for the vendor field as required */
if ((pos - (u8 *)rthdr) & 1) if ((pos - (u8 *)rthdr) & 1)

View File

@ -66,6 +66,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
struct cfg80211_bss *cbss; struct cfg80211_bss *cbss;
struct ieee80211_bss *bss; struct ieee80211_bss *bss;
int clen, srlen; int clen, srlen;
enum nl80211_bss_scan_width scan_width;
s32 signal = 0; s32 signal = 0;
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
@ -73,8 +74,15 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
signal = (rx_status->signal * 100) / local->hw.max_signal; signal = (rx_status->signal * 100) / local->hw.max_signal;
cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel, scan_width = NL80211_BSS_CHAN_WIDTH_20;
mgmt, len, signal, GFP_ATOMIC); if (rx_status->flag & RX_FLAG_5MHZ)
scan_width = NL80211_BSS_CHAN_WIDTH_5;
if (rx_status->flag & RX_FLAG_10MHZ)
scan_width = NL80211_BSS_CHAN_WIDTH_10;
cbss = cfg80211_inform_bss_width_frame(local->hw.wiphy, channel,
scan_width, mgmt, len, signal,
GFP_ATOMIC);
if (!cbss) if (!cbss)
return NULL; return NULL;
@ -204,10 +212,29 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
ieee80211_rx_bss_put(local, bss); ieee80211_rx_bss_put(local, bss);
} }
static void
ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
enum nl80211_bss_scan_width scan_width)
{
memset(chandef, 0, sizeof(*chandef));
switch (scan_width) {
case NL80211_BSS_CHAN_WIDTH_5:
chandef->width = NL80211_CHAN_WIDTH_5;
break;
case NL80211_BSS_CHAN_WIDTH_10:
chandef->width = NL80211_CHAN_WIDTH_10;
break;
default:
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
break;
}
}
/* return false if no more work */ /* return false if no more work */
static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
{ {
struct cfg80211_scan_request *req = local->scan_req; struct cfg80211_scan_request *req = local->scan_req;
struct cfg80211_chan_def chandef;
enum ieee80211_band band; enum ieee80211_band band;
int i, ielen, n_chans; int i, ielen, n_chans;
@ -229,11 +256,12 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
} while (!n_chans); } while (!n_chans);
local->hw_scan_req->n_channels = n_chans; local->hw_scan_req->n_channels = n_chans;
ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
local->hw_scan_ies_bufsize, local->hw_scan_ies_bufsize,
req->ie, req->ie_len, band, req->ie, req->ie_len, band,
req->rates[band], 0); req->rates[band], &chandef);
local->hw_scan_req->ie_len = ielen; local->hw_scan_req->ie_len = ielen;
local->hw_scan_req->no_cck = req->no_cck; local->hw_scan_req->no_cck = req->no_cck;
@ -280,7 +308,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
rcu_assign_pointer(local->scan_sdata, NULL); rcu_assign_pointer(local->scan_sdata, NULL);
local->scanning = 0; local->scanning = 0;
local->scan_channel = NULL; local->scan_chandef.chan = NULL;
/* Set power back to normal operating levels. */ /* Set power back to normal operating levels. */
ieee80211_hw_config(local, 0); ieee80211_hw_config(local, 0);
@ -615,11 +643,34 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
{ {
int skip; int skip;
struct ieee80211_channel *chan; struct ieee80211_channel *chan;
enum nl80211_bss_scan_width oper_scan_width;
skip = 0; skip = 0;
chan = local->scan_req->channels[local->scan_channel_idx]; chan = local->scan_req->channels[local->scan_channel_idx];
local->scan_channel = chan; local->scan_chandef.chan = chan;
local->scan_chandef.center_freq1 = chan->center_freq;
local->scan_chandef.center_freq2 = 0;
switch (local->scan_req->scan_width) {
case NL80211_BSS_CHAN_WIDTH_5:
local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
break;
case NL80211_BSS_CHAN_WIDTH_10:
local->scan_chandef.width = NL80211_CHAN_WIDTH_10;
break;
case NL80211_BSS_CHAN_WIDTH_20:
/* If scanning on oper channel, use whatever channel-type
* is currently in use.
*/
oper_scan_width = cfg80211_chandef_to_scan_width(
&local->_oper_chandef);
if (chan == local->_oper_chandef.chan &&
oper_scan_width == local->scan_req->scan_width)
local->scan_chandef = local->_oper_chandef;
else
local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
break;
}
if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
skip = 1; skip = 1;
@ -659,7 +710,7 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
unsigned long *next_delay) unsigned long *next_delay)
{ {
/* switch back to the operating channel */ /* switch back to the operating channel */
local->scan_channel = NULL; local->scan_chandef.chan = NULL;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
/* disable PS */ /* disable PS */
@ -801,7 +852,8 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
const u8 *ssid, u8 ssid_len, const u8 *ssid, u8 ssid_len,
struct ieee80211_channel *chan) struct ieee80211_channel *chan,
enum nl80211_bss_scan_width scan_width)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
int ret = -EBUSY; int ret = -EBUSY;
@ -851,6 +903,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
local->int_scan_req->ssids = &local->scan_ssid; local->int_scan_req->ssids = &local->scan_ssid;
local->int_scan_req->n_ssids = 1; local->int_scan_req->n_ssids = 1;
local->int_scan_req->scan_width = scan_width;
memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN); memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
local->int_scan_req->ssids[0].ssid_len = ssid_len; local->int_scan_req->ssids[0].ssid_len = ssid_len;
@ -912,6 +965,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_sched_scan_ies sched_scan_ies = {}; struct ieee80211_sched_scan_ies sched_scan_ies = {};
struct cfg80211_chan_def chandef;
int ret, i, iebufsz; int ret, i, iebufsz;
iebufsz = 2 + IEEE80211_MAX_SSID_LEN + iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
@ -939,10 +993,12 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
goto out_free; goto out_free;
} }
ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
sched_scan_ies.len[i] = sched_scan_ies.len[i] =
ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
iebufsz, req->ie, req->ie_len, iebufsz, req->ie, req->ie_len,
i, (u32) -1, 0); i, (u32) -1, &chandef);
} }
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);

View File

@ -252,9 +252,10 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
return len; return len;
} }
static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band static void
*sband, struct sk_buff *skb, ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband,
int retry_count, int rtap_len) struct sk_buff *skb, int retry_count,
int rtap_len, int shift)
{ {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@ -280,8 +281,11 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
/* IEEE80211_RADIOTAP_RATE */ /* IEEE80211_RADIOTAP_RATE */
if (info->status.rates[0].idx >= 0 && if (info->status.rates[0].idx >= 0 &&
!(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) { !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) {
u16 rate;
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
*pos = sband->bitrates[info->status.rates[0].idx].bitrate / 5; rate = sband->bitrates[info->status.rates[0].idx].bitrate;
*pos = DIV_ROUND_UP(rate, 5 * (1 << shift));
/* padding for tx flags */ /* padding for tx flags */
pos += 2; pos += 2;
} }
@ -424,6 +428,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
bool acked; bool acked;
struct ieee80211_bar *bar; struct ieee80211_bar *bar;
int rtap_len; int rtap_len;
int shift = 0;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
if ((info->flags & IEEE80211_TX_CTL_AMPDU) && if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@ -458,6 +463,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr)) if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
continue; continue;
shift = ieee80211_vif_get_shift(&sta->sdata->vif);
if (info->flags & IEEE80211_TX_STATUS_EOSP) if (info->flags & IEEE80211_TX_STATUS_EOSP)
clear_sta_flag(sta, WLAN_STA_SP); clear_sta_flag(sta, WLAN_STA_SP);
@ -624,7 +631,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
dev_kfree_skb(skb); dev_kfree_skb(skb);
return; return;
} }
ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len); ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len,
shift);
/* XXX: is this sufficient for BPF? */ /* XXX: is this sufficient for BPF? */
skb_set_mac_header(skb, 0); skb_set_mac_header(skb, 0);

View File

@ -40,12 +40,22 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
struct sk_buff *skb, int group_addr, struct sk_buff *skb, int group_addr,
int next_frag_len) int next_frag_len)
{ {
int rate, mrate, erp, dur, i; int rate, mrate, erp, dur, i, shift = 0;
struct ieee80211_rate *txrate; struct ieee80211_rate *txrate;
struct ieee80211_local *local = tx->local; struct ieee80211_local *local = tx->local;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_chanctx_conf *chanctx_conf;
u32 rate_flags = 0;
rcu_read_lock();
chanctx_conf = rcu_dereference(tx->sdata->vif.chanctx_conf);
if (chanctx_conf) {
shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
}
rcu_read_unlock();
/* assume HW handles this */ /* assume HW handles this */
if (tx->rate.flags & IEEE80211_TX_RC_MCS) if (tx->rate.flags & IEEE80211_TX_RC_MCS)
@ -122,8 +132,11 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
if (r->bitrate > txrate->bitrate) if (r->bitrate > txrate->bitrate)
break; break;
if ((rate_flags & r->flags) != rate_flags)
continue;
if (tx->sdata->vif.bss_conf.basic_rates & BIT(i)) if (tx->sdata->vif.bss_conf.basic_rates & BIT(i))
rate = r->bitrate; rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
switch (sband->band) { switch (sband->band) {
case IEEE80211_BAND_2GHZ: { case IEEE80211_BAND_2GHZ: {
@ -150,7 +163,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
if (rate == -1) { if (rate == -1) {
/* No matching basic rate found; use highest suitable mandatory /* No matching basic rate found; use highest suitable mandatory
* PHY rate */ * PHY rate */
rate = mrate; rate = DIV_ROUND_UP(mrate, 1 << shift);
} }
/* Don't calculate ACKs for QoS Frames with NoAck Policy set */ /* Don't calculate ACKs for QoS Frames with NoAck Policy set */
@ -162,7 +175,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
* (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
* to closest integer */ * to closest integer */
dur = ieee80211_frame_duration(sband->band, 10, rate, erp, dur = ieee80211_frame_duration(sband->band, 10, rate, erp,
tx->sdata->vif.bss_conf.use_short_preamble); tx->sdata->vif.bss_conf.use_short_preamble,
shift);
if (next_frag_len) { if (next_frag_len) {
/* Frame is fragmented: duration increases with time needed to /* Frame is fragmented: duration increases with time needed to
@ -171,7 +185,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
/* next fragment */ /* next fragment */
dur += ieee80211_frame_duration(sband->band, next_frag_len, dur += ieee80211_frame_duration(sband->band, next_frag_len,
txrate->bitrate, erp, txrate->bitrate, erp,
tx->sdata->vif.bss_conf.use_short_preamble); tx->sdata->vif.bss_conf.use_short_preamble,
shift);
} }
return cpu_to_le16(dur); return cpu_to_le16(dur);
@ -1257,6 +1272,10 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
vif = &sdata->vif;
break;
}
sdata = rcu_dereference(local->monitor_sdata); sdata = rcu_dereference(local->monitor_sdata);
if (sdata) { if (sdata) {
vif = &sdata->vif; vif = &sdata->vif;

View File

@ -107,7 +107,8 @@ void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
} }
int ieee80211_frame_duration(enum ieee80211_band band, size_t len, int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
int rate, int erp, int short_preamble) int rate, int erp, int short_preamble,
int shift)
{ {
int dur; int dur;
@ -118,6 +119,9 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
* *
* rate is in 100 kbps, so divident is multiplied by 10 in the * rate is in 100 kbps, so divident is multiplied by 10 in the
* DIV_ROUND_UP() operations. * DIV_ROUND_UP() operations.
*
* shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and
* is assumed to be 0 otherwise.
*/ */
if (band == IEEE80211_BAND_5GHZ || erp) { if (band == IEEE80211_BAND_5GHZ || erp) {
@ -130,13 +134,23 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
* TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
* *
* T_SYM = 4 usec * T_SYM = 4 usec
* 802.11a - 17.5.2: aSIFSTime = 16 usec * 802.11a - 18.5.2: aSIFSTime = 16 usec
* 802.11g - 19.8.4: aSIFSTime = 10 usec + * 802.11g - 19.8.4: aSIFSTime = 10 usec +
* signal ext = 6 usec * signal ext = 6 usec
*/ */
dur = 16; /* SIFS + signal ext */ dur = 16; /* SIFS + signal ext */
dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */ dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */
dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */ dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */
/* IEEE 802.11-2012 18.3.2.4: all values above are:
* * times 4 for 5 MHz
* * times 2 for 10 MHz
*/
dur *= 1 << shift;
/* rates should already consider the channel bandwidth,
* don't apply divisor again.
*/
dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10, dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
4 * rate); /* T_SYM x N_SYM */ 4 * rate); /* T_SYM x N_SYM */
} else { } else {
@ -168,7 +182,7 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
u16 dur; u16 dur;
int erp; int erp, shift = 0;
bool short_preamble = false; bool short_preamble = false;
erp = 0; erp = 0;
@ -177,10 +191,11 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
short_preamble = sdata->vif.bss_conf.use_short_preamble; short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G; erp = rate->flags & IEEE80211_RATE_ERP_G;
shift = ieee80211_vif_get_shift(vif);
} }
dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp, dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,
short_preamble); short_preamble, shift);
return cpu_to_le16(dur); return cpu_to_le16(dur);
} }
@ -194,7 +209,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
struct ieee80211_rate *rate; struct ieee80211_rate *rate;
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
bool short_preamble; bool short_preamble;
int erp; int erp, shift = 0, bitrate;
u16 dur; u16 dur;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
@ -210,17 +225,20 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
short_preamble = sdata->vif.bss_conf.use_short_preamble; short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G; erp = rate->flags & IEEE80211_RATE_ERP_G;
shift = ieee80211_vif_get_shift(vif);
} }
bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
/* CTS duration */ /* CTS duration */
dur = ieee80211_frame_duration(sband->band, 10, rate->bitrate, dur = ieee80211_frame_duration(sband->band, 10, bitrate,
erp, short_preamble); erp, short_preamble, shift);
/* Data frame duration */ /* Data frame duration */
dur += ieee80211_frame_duration(sband->band, frame_len, rate->bitrate, dur += ieee80211_frame_duration(sband->band, frame_len, bitrate,
erp, short_preamble); erp, short_preamble, shift);
/* ACK duration */ /* ACK duration */
dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate, dur += ieee80211_frame_duration(sband->band, 10, bitrate,
erp, short_preamble); erp, short_preamble, shift);
return cpu_to_le16(dur); return cpu_to_le16(dur);
} }
@ -235,7 +253,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
struct ieee80211_rate *rate; struct ieee80211_rate *rate;
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
bool short_preamble; bool short_preamble;
int erp; int erp, shift = 0, bitrate;
u16 dur; u16 dur;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
@ -250,15 +268,18 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
short_preamble = sdata->vif.bss_conf.use_short_preamble; short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G; erp = rate->flags & IEEE80211_RATE_ERP_G;
shift = ieee80211_vif_get_shift(vif);
} }
bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
/* Data frame duration */ /* Data frame duration */
dur = ieee80211_frame_duration(sband->band, frame_len, rate->bitrate, dur = ieee80211_frame_duration(sband->band, frame_len, bitrate,
erp, short_preamble); erp, short_preamble, shift);
if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) { if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
/* ACK duration */ /* ACK duration */
dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate, dur += ieee80211_frame_duration(sband->band, 10, bitrate,
erp, short_preamble); erp, short_preamble, shift);
} }
return cpu_to_le16(dur); return cpu_to_le16(dur);
@ -1052,32 +1073,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
} }
} }
void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
const size_t supp_rates_len,
const u8 *supp_rates)
{
struct ieee80211_chanctx_conf *chanctx_conf;
int i, have_higher_than_11mbit = 0;
/* cf. IEEE 802.11 9.2.12 */
for (i = 0; i < supp_rates_len; i++)
if ((supp_rates[i] & 0x7f) * 5 > 110)
have_higher_than_11mbit = 1;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (chanctx_conf &&
chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ &&
have_higher_than_11mbit)
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
rcu_read_unlock();
ieee80211_set_wmm_default(sdata, true);
}
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg, u16 status, u16 transaction, u16 auth_alg, u16 status,
const u8 *extra, size_t extra_len, const u8 *da, const u8 *extra, size_t extra_len, const u8 *da,
@ -1162,7 +1157,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
size_t buffer_len, const u8 *ie, size_t ie_len, size_t buffer_len, const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask, enum ieee80211_band band, u32 rate_mask,
u8 channel) struct cfg80211_chan_def *chandef)
{ {
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
u8 *pos = buffer, *end = buffer + buffer_len; u8 *pos = buffer, *end = buffer + buffer_len;
@ -1171,16 +1166,26 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
u8 rates[32]; u8 rates[32];
int num_rates; int num_rates;
int ext_rates_len; int ext_rates_len;
int shift;
u32 rate_flags;
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
if (WARN_ON_ONCE(!sband)) if (WARN_ON_ONCE(!sband))
return 0; return 0;
rate_flags = ieee80211_chandef_rate_flags(chandef);
shift = ieee80211_chandef_get_shift(chandef);
num_rates = 0; num_rates = 0;
for (i = 0; i < sband->n_bitrates; i++) { for (i = 0; i < sband->n_bitrates; i++) {
if ((BIT(i) & rate_mask) == 0) if ((BIT(i) & rate_mask) == 0)
continue; /* skip rate */ continue; /* skip rate */
rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5); if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
continue;
rates[num_rates++] =
(u8) DIV_ROUND_UP(sband->bitrates[i].bitrate,
(1 << shift) * 5);
} }
supp_rates_len = min_t(int, num_rates, 8); supp_rates_len = min_t(int, num_rates, 8);
@ -1220,12 +1225,13 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
pos += ext_rates_len; pos += ext_rates_len;
} }
if (channel && sband->band == IEEE80211_BAND_2GHZ) { if (chandef->chan && sband->band == IEEE80211_BAND_2GHZ) {
if (end - pos < 3) if (end - pos < 3)
goto out_err; goto out_err;
*pos++ = WLAN_EID_DS_PARAMS; *pos++ = WLAN_EID_DS_PARAMS;
*pos++ = 1; *pos++ = 1;
*pos++ = channel; *pos++ = ieee80211_frequency_to_channel(
chandef->chan->center_freq);
} }
/* insert custom IEs that go before HT */ /* insert custom IEs that go before HT */
@ -1290,9 +1296,9 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
bool directed) bool directed)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct cfg80211_chan_def chandef;
struct sk_buff *skb; struct sk_buff *skb;
struct ieee80211_mgmt *mgmt; struct ieee80211_mgmt *mgmt;
u8 chan_no;
int ies_len; int ies_len;
/* /*
@ -1300,10 +1306,11 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
* in order to maximize the chance that we get a response. Some * in order to maximize the chance that we get a response. Some
* badly-behaved APs don't respond when this parameter is included. * badly-behaved APs don't respond when this parameter is included.
*/ */
chandef.width = sdata->vif.bss_conf.chandef.width;
if (directed) if (directed)
chan_no = 0; chandef.chan = NULL;
else else
chan_no = ieee80211_frequency_to_channel(chan->center_freq); chandef.chan = chan;
skb = ieee80211_probereq_get(&local->hw, &sdata->vif, skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
ssid, ssid_len, 100 + ie_len); ssid, ssid_len, 100 + ie_len);
@ -1313,7 +1320,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb), ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
skb_tailroom(skb), skb_tailroom(skb),
ie, ie_len, chan->band, ie, ie_len, chan->band,
ratemask, chan_no); ratemask, &chandef);
skb_put(skb, ies_len); skb_put(skb, ies_len);
if (dst) { if (dst) {
@ -1347,16 +1354,19 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
} }
} }
u32 ieee80211_sta_get_rates(struct ieee80211_local *local, u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems, struct ieee802_11_elems *elems,
enum ieee80211_band band, u32 *basic_rates) enum ieee80211_band band, u32 *basic_rates)
{ {
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
struct ieee80211_rate *bitrates; struct ieee80211_rate *bitrates;
size_t num_rates; size_t num_rates;
u32 supp_rates; u32 supp_rates, rate_flags;
int i, j; int i, j, shift;
sband = local->hw.wiphy->bands[band]; sband = sdata->local->hw.wiphy->bands[band];
rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
shift = ieee80211_vif_get_shift(&sdata->vif);
if (WARN_ON(!sband)) if (WARN_ON(!sband))
return 1; return 1;
@ -1381,7 +1391,15 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
continue; continue;
for (j = 0; j < num_rates; j++) { for (j = 0; j < num_rates; j++) {
if (bitrates[j].bitrate == own_rate) { int brate;
if ((rate_flags & sband->bitrates[j].flags)
!= rate_flags)
continue;
brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
1 << shift);
if (brate == own_rate) {
supp_rates |= BIT(j); supp_rates |= BIT(j);
if (basic_rates && is_basic) if (basic_rates && is_basic)
*basic_rates |= BIT(j); *basic_rates |= BIT(j);
@ -2004,18 +2022,56 @@ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
cfg80211_chandef_create(chandef, control_chan, channel_type); cfg80211_chandef_create(chandef, control_chan, channel_type);
} }
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates)
{
u32 rate_flags = ieee80211_chandef_rate_flags(chandef);
int shift = ieee80211_chandef_get_shift(chandef);
struct ieee80211_rate *br;
int brate, rate, i, j, count = 0;
*rates = 0;
for (i = 0; i < srates_len; i++) {
rate = srates[i] & 0x7f;
for (j = 0; j < sband->n_bitrates; j++) {
br = &sband->bitrates[j];
if ((rate_flags & br->flags) != rate_flags)
continue;
brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
if (brate == rate) {
*rates |= BIT(j);
count++;
break;
}
}
}
return count;
}
int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, bool need_basic, struct sk_buff *skb, bool need_basic,
enum ieee80211_band band) enum ieee80211_band band)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
int rate; int rate, shift;
u8 i, rates, *pos; u8 i, rates, *pos;
u32 basic_rates = sdata->vif.bss_conf.basic_rates; u32 basic_rates = sdata->vif.bss_conf.basic_rates;
u32 rate_flags;
shift = ieee80211_vif_get_shift(&sdata->vif);
rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
rates = sband->n_bitrates; rates = 0;
for (i = 0; i < sband->n_bitrates; i++) {
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
continue;
rates++;
}
if (rates > 8) if (rates > 8)
rates = 8; rates = 8;
@ -2027,10 +2083,15 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
*pos++ = rates; *pos++ = rates;
for (i = 0; i < rates; i++) { for (i = 0; i < rates; i++) {
u8 basic = 0; u8 basic = 0;
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
continue;
if (need_basic && basic_rates & BIT(i)) if (need_basic && basic_rates & BIT(i))
basic = 0x80; basic = 0x80;
rate = sband->bitrates[i].bitrate; rate = sband->bitrates[i].bitrate;
*pos++ = basic | (u8) (rate / 5); rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
5 * (1 << shift));
*pos++ = basic | (u8) rate;
} }
return 0; return 0;
@ -2042,12 +2103,22 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
int rate; int rate, skip, shift;
u8 i, exrates, *pos; u8 i, exrates, *pos;
u32 basic_rates = sdata->vif.bss_conf.basic_rates; u32 basic_rates = sdata->vif.bss_conf.basic_rates;
u32 rate_flags;
rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
shift = ieee80211_vif_get_shift(&sdata->vif);
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
exrates = sband->n_bitrates; exrates = 0;
for (i = 0; i < sband->n_bitrates; i++) {
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
continue;
exrates++;
}
if (exrates > 8) if (exrates > 8)
exrates -= 8; exrates -= 8;
else else
@ -2060,12 +2131,19 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
pos = skb_put(skb, exrates + 2); pos = skb_put(skb, exrates + 2);
*pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = exrates; *pos++ = exrates;
skip = 0;
for (i = 8; i < sband->n_bitrates; i++) { for (i = 8; i < sband->n_bitrates; i++) {
u8 basic = 0; u8 basic = 0;
if ((rate_flags & sband->bitrates[i].flags)
!= rate_flags)
continue;
if (skip++ < 8)
continue;
if (need_basic && basic_rates & BIT(i)) if (need_basic && basic_rates & BIT(i))
basic = 0x80; basic = 0x80;
rate = sband->bitrates[i].bitrate; rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
*pos++ = basic | (u8) (rate / 5); 5 * (1 << shift));
*pos++ = basic | (u8) rate;
} }
} }
return 0; return 0;
@ -2149,9 +2227,17 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
ri.flags |= RATE_INFO_FLAGS_SHORT_GI; ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
} else { } else {
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
int shift = 0;
int bitrate;
if (status->flag & RX_FLAG_10MHZ)
shift = 1;
if (status->flag & RX_FLAG_5MHZ)
shift = 2;
sband = local->hw.wiphy->bands[status->band]; sband = local->hw.wiphy->bands[status->band];
ri.legacy = sband->bitrates[status->rate_idx].bitrate; bitrate = sband->bitrates[status->rate_idx].bitrate;
ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
} }
rate = cfg80211_calculate_bitrate(&ri); rate = cfg80211_calculate_bitrate(&ri);

View File

@ -462,6 +462,14 @@ int wiphy_register(struct wiphy *wiphy)
return -EINVAL; return -EINVAL;
#endif #endif
if (WARN_ON(wiphy->coalesce &&
(!wiphy->coalesce->n_rules ||
!wiphy->coalesce->n_patterns) &&
(!wiphy->coalesce->pattern_min_len ||
wiphy->coalesce->pattern_min_len >
wiphy->coalesce->pattern_max_len)))
return -EINVAL;
if (WARN_ON(wiphy->ap_sme_capa && if (WARN_ON(wiphy->ap_sme_capa &&
!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME)))
return -EINVAL; return -EINVAL;
@ -668,6 +676,7 @@ void wiphy_unregister(struct wiphy *wiphy)
rdev_set_wakeup(rdev, false); rdev_set_wakeup(rdev, false);
#endif #endif
cfg80211_rdev_free_wowlan(rdev); cfg80211_rdev_free_wowlan(rdev);
cfg80211_rdev_free_coalesce(rdev);
} }
EXPORT_SYMBOL(wiphy_unregister); EXPORT_SYMBOL(wiphy_unregister);

View File

@ -79,6 +79,8 @@ struct cfg80211_registered_device {
/* netlink port which started critical protocol (0 means not started) */ /* netlink port which started critical protocol (0 means not started) */
u32 crit_proto_nlportid; u32 crit_proto_nlportid;
struct cfg80211_coalesce *coalesce;
/* must be last because of the way we do wiphy_priv(), /* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */ * and it should at least be aligned to NETDEV_ALIGN */
struct wiphy wiphy __aligned(NETDEV_ALIGN); struct wiphy wiphy __aligned(NETDEV_ALIGN);

View File

@ -167,9 +167,12 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
* basic rates * basic rates
*/ */
if (!setup->basic_rates) { if (!setup->basic_rates) {
enum nl80211_bss_scan_width scan_width;
struct ieee80211_supported_band *sband = struct ieee80211_supported_band *sband =
rdev->wiphy.bands[setup->chandef.chan->band]; rdev->wiphy.bands[setup->chandef.chan->band];
setup->basic_rates = ieee80211_mandatory_rates(sband); scan_width = cfg80211_chandef_to_scan_width(&setup->chandef);
setup->basic_rates = ieee80211_mandatory_rates(sband,
scan_width);
} }
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))

View File

@ -403,6 +403,14 @@ nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
[NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 }, [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
}; };
/* policy for coalesce rule attributes */
static const struct nla_policy
nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
[NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 },
[NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 },
[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED },
};
/* policy for GTK rekey offload attributes */ /* policy for GTK rekey offload attributes */
static const struct nla_policy static const struct nla_policy
nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
@ -974,7 +982,7 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
return -ENOBUFS; return -ENOBUFS;
if (dev->wiphy.wowlan->n_patterns) { if (dev->wiphy.wowlan->n_patterns) {
struct nl80211_wowlan_pattern_support pat = { struct nl80211_pattern_support pat = {
.max_patterns = dev->wiphy.wowlan->n_patterns, .max_patterns = dev->wiphy.wowlan->n_patterns,
.min_pattern_len = dev->wiphy.wowlan->pattern_min_len, .min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
.max_pattern_len = dev->wiphy.wowlan->pattern_max_len, .max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
@ -995,6 +1003,27 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
} }
#endif #endif
static int nl80211_send_coalesce(struct sk_buff *msg,
struct cfg80211_registered_device *dev)
{
struct nl80211_coalesce_rule_support rule;
if (!dev->wiphy.coalesce)
return 0;
rule.max_rules = dev->wiphy.coalesce->n_rules;
rule.max_delay = dev->wiphy.coalesce->max_delay;
rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns;
rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len;
rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len;
rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset;
if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
return -ENOBUFS;
return 0;
}
static int nl80211_send_band_rateinfo(struct sk_buff *msg, static int nl80211_send_band_rateinfo(struct sk_buff *msg,
struct ieee80211_supported_band *sband) struct ieee80211_supported_band *sband)
{ {
@ -1513,6 +1542,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
dev->wiphy.vht_capa_mod_mask)) dev->wiphy.vht_capa_mod_mask))
goto nla_put_failure; goto nla_put_failure;
state->split_start++;
break;
case 10:
if (nl80211_send_coalesce(msg, dev))
goto nla_put_failure;
/* done */ /* done */
state->split_start = 0; state->split_start = 0;
break; break;
@ -5639,6 +5674,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
goto nla_put_failure; goto nla_put_failure;
if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) || if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) || nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) ||
nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO, nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
jiffies_to_msecs(jiffies - intbss->ts))) jiffies_to_msecs(jiffies - intbss->ts)))
goto nla_put_failure; goto nla_put_failure;
@ -6319,6 +6355,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
return -EINVAL; return -EINVAL;
switch (ibss.chandef.width) { switch (ibss.chandef.width) {
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20_NOHT:
break; break;
case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20:
@ -6346,6 +6384,19 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
return err; return err;
} }
if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
memcpy(&ibss.ht_capa_mask,
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
sizeof(ibss.ht_capa_mask));
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
return -EINVAL;
memcpy(&ibss.ht_capa,
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
sizeof(ibss.ht_capa));
}
if (info->attrs[NL80211_ATTR_MCAST_RATE] && if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
!nl80211_parse_mcast_rate(rdev, ibss.mcast_rate, !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
@ -7593,12 +7644,11 @@ static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
if (!nl_pat) if (!nl_pat)
return -ENOBUFS; return -ENOBUFS;
pat_len = wowlan->patterns[i].pattern_len; pat_len = wowlan->patterns[i].pattern_len;
if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK, if (nla_put(msg, NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8),
DIV_ROUND_UP(pat_len, 8),
wowlan->patterns[i].mask) || wowlan->patterns[i].mask) ||
nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN, nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
pat_len, wowlan->patterns[i].pattern) || wowlan->patterns[i].pattern) ||
nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET, nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
wowlan->patterns[i].pkt_offset)) wowlan->patterns[i].pkt_offset))
return -ENOBUFS; return -ENOBUFS;
nla_nest_end(msg, nl_pat); nla_nest_end(msg, nl_pat);
@ -7939,7 +7989,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
struct nlattr *pat; struct nlattr *pat;
int n_patterns = 0; int n_patterns = 0;
int rem, pat_len, mask_len, pkt_offset; int rem, pat_len, mask_len, pkt_offset;
struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
rem) rem)
@ -7958,26 +8008,25 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
rem) { rem) {
nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT, nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
nla_data(pat), nla_len(pat), NULL); nla_len(pat), NULL);
err = -EINVAL; err = -EINVAL;
if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] || if (!pat_tb[NL80211_PKTPAT_MASK] ||
!pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]) !pat_tb[NL80211_PKTPAT_PATTERN])
goto error; goto error;
pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]); pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
mask_len = DIV_ROUND_UP(pat_len, 8); mask_len = DIV_ROUND_UP(pat_len, 8);
if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) != if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
mask_len)
goto error; goto error;
if (pat_len > wowlan->pattern_max_len || if (pat_len > wowlan->pattern_max_len ||
pat_len < wowlan->pattern_min_len) pat_len < wowlan->pattern_min_len)
goto error; goto error;
if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]) if (!pat_tb[NL80211_PKTPAT_OFFSET])
pkt_offset = 0; pkt_offset = 0;
else else
pkt_offset = nla_get_u32( pkt_offset = nla_get_u32(
pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]); pat_tb[NL80211_PKTPAT_OFFSET]);
if (pkt_offset > wowlan->max_pkt_offset) if (pkt_offset > wowlan->max_pkt_offset)
goto error; goto error;
new_triggers.patterns[i].pkt_offset = pkt_offset; new_triggers.patterns[i].pkt_offset = pkt_offset;
@ -7991,11 +8040,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
new_triggers.patterns[i].pattern = new_triggers.patterns[i].pattern =
new_triggers.patterns[i].mask + mask_len; new_triggers.patterns[i].mask + mask_len;
memcpy(new_triggers.patterns[i].mask, memcpy(new_triggers.patterns[i].mask,
nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]), nla_data(pat_tb[NL80211_PKTPAT_MASK]),
mask_len); mask_len);
new_triggers.patterns[i].pattern_len = pat_len; new_triggers.patterns[i].pattern_len = pat_len;
memcpy(new_triggers.patterns[i].pattern, memcpy(new_triggers.patterns[i].pattern,
nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]), nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
pat_len); pat_len);
i++; i++;
} }
@ -8034,6 +8083,264 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
} }
#endif #endif
static int nl80211_send_coalesce_rules(struct sk_buff *msg,
struct cfg80211_registered_device *rdev)
{
struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules;
int i, j, pat_len;
struct cfg80211_coalesce_rules *rule;
if (!rdev->coalesce->n_rules)
return 0;
nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
if (!nl_rules)
return -ENOBUFS;
for (i = 0; i < rdev->coalesce->n_rules; i++) {
nl_rule = nla_nest_start(msg, i + 1);
if (!nl_rule)
return -ENOBUFS;
rule = &rdev->coalesce->rules[i];
if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
rule->delay))
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
rule->condition))
return -ENOBUFS;
nl_pats = nla_nest_start(msg,
NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
if (!nl_pats)
return -ENOBUFS;
for (j = 0; j < rule->n_patterns; j++) {
nl_pat = nla_nest_start(msg, j + 1);
if (!nl_pat)
return -ENOBUFS;
pat_len = rule->patterns[j].pattern_len;
if (nla_put(msg, NL80211_PKTPAT_MASK,
DIV_ROUND_UP(pat_len, 8),
rule->patterns[j].mask) ||
nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
rule->patterns[j].pattern) ||
nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
rule->patterns[j].pkt_offset))
return -ENOBUFS;
nla_nest_end(msg, nl_pat);
}
nla_nest_end(msg, nl_pats);
nla_nest_end(msg, nl_rule);
}
nla_nest_end(msg, nl_rules);
return 0;
}
static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct sk_buff *msg;
void *hdr;
if (!rdev->wiphy.coalesce)
return -EOPNOTSUPP;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
NL80211_CMD_GET_COALESCE);
if (!hdr)
goto nla_put_failure;
if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return genlmsg_reply(msg, info);
nla_put_failure:
nlmsg_free(msg);
return -ENOBUFS;
}
void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev)
{
struct cfg80211_coalesce *coalesce = rdev->coalesce;
int i, j;
struct cfg80211_coalesce_rules *rule;
if (!coalesce)
return;
for (i = 0; i < coalesce->n_rules; i++) {
rule = &coalesce->rules[i];
for (j = 0; j < rule->n_patterns; j++)
kfree(rule->patterns[j].mask);
kfree(rule->patterns);
}
kfree(coalesce->rules);
kfree(coalesce);
rdev->coalesce = NULL;
}
static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
struct nlattr *rule,
struct cfg80211_coalesce_rules *new_rule)
{
int err, i;
const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat;
int rem, pat_len, mask_len, pkt_offset, n_patterns = 0;
struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule),
nla_len(rule), nl80211_coalesce_policy);
if (err)
return err;
if (tb[NL80211_ATTR_COALESCE_RULE_DELAY])
new_rule->delay =
nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]);
if (new_rule->delay > coalesce->max_delay)
return -EINVAL;
if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION])
new_rule->condition =
nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]);
if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH &&
new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH)
return -EINVAL;
if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN])
return -EINVAL;
nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
rem)
n_patterns++;
if (n_patterns > coalesce->n_patterns)
return -EINVAL;
new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]),
GFP_KERNEL);
if (!new_rule->patterns)
return -ENOMEM;
new_rule->n_patterns = n_patterns;
i = 0;
nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
rem) {
nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
nla_len(pat), NULL);
if (!pat_tb[NL80211_PKTPAT_MASK] ||
!pat_tb[NL80211_PKTPAT_PATTERN])
return -EINVAL;
pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
mask_len = DIV_ROUND_UP(pat_len, 8);
if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
return -EINVAL;
if (pat_len > coalesce->pattern_max_len ||
pat_len < coalesce->pattern_min_len)
return -EINVAL;
if (!pat_tb[NL80211_PKTPAT_OFFSET])
pkt_offset = 0;
else
pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]);
if (pkt_offset > coalesce->max_pkt_offset)
return -EINVAL;
new_rule->patterns[i].pkt_offset = pkt_offset;
new_rule->patterns[i].mask =
kmalloc(mask_len + pat_len, GFP_KERNEL);
if (!new_rule->patterns[i].mask)
return -ENOMEM;
new_rule->patterns[i].pattern =
new_rule->patterns[i].mask + mask_len;
memcpy(new_rule->patterns[i].mask,
nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len);
new_rule->patterns[i].pattern_len = pat_len;
memcpy(new_rule->patterns[i].pattern,
nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len);
i++;
}
return 0;
}
static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
struct cfg80211_coalesce new_coalesce = {};
struct cfg80211_coalesce *n_coalesce;
int err, rem_rule, n_rules = 0, i, j;
struct nlattr *rule;
struct cfg80211_coalesce_rules *tmp_rule;
if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce)
return -EOPNOTSUPP;
if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
cfg80211_rdev_free_coalesce(rdev);
rdev->ops->set_coalesce(&rdev->wiphy, NULL);
return 0;
}
nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
rem_rule)
n_rules++;
if (n_rules > coalesce->n_rules)
return -EINVAL;
new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]),
GFP_KERNEL);
if (!new_coalesce.rules)
return -ENOMEM;
new_coalesce.n_rules = n_rules;
i = 0;
nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
rem_rule) {
err = nl80211_parse_coalesce_rule(rdev, rule,
&new_coalesce.rules[i]);
if (err)
goto error;
i++;
}
err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
if (err)
goto error;
n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL);
if (!n_coalesce) {
err = -ENOMEM;
goto error;
}
cfg80211_rdev_free_coalesce(rdev);
rdev->coalesce = n_coalesce;
return 0;
error:
for (i = 0; i < new_coalesce.n_rules; i++) {
tmp_rule = &new_coalesce.rules[i];
for (j = 0; j < tmp_rule->n_patterns; j++)
kfree(tmp_rule->patterns[j].mask);
kfree(tmp_rule->patterns);
}
kfree(new_coalesce.rules);
return err;
}
static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_rekey_data(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];
@ -9041,6 +9348,21 @@ static struct genl_ops nl80211_ops[] = {
.flags = GENL_ADMIN_PERM, .flags = GENL_ADMIN_PERM,
.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_GET_COALESCE,
.doit = nl80211_get_coalesce,
.policy = nl80211_policy,
.internal_flags = NL80211_FLAG_NEED_WIPHY |
NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_SET_COALESCE,
.doit = nl80211_set_coalesce,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WIPHY |
NL80211_FLAG_NEED_RTNL,
} }
}; };

View File

@ -74,4 +74,6 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
enum nl80211_radar_event event, enum nl80211_radar_event event,
struct net_device *netdev, gfp_t gfp); struct net_device *netdev, gfp_t gfp);
void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
#endif /* __NET_WIRELESS_NL80211_H */ #endif /* __NET_WIRELESS_NL80211_H */

View File

@ -651,6 +651,8 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
continue; continue;
if (bss->pub.channel != new->pub.channel) if (bss->pub.channel != new->pub.channel)
continue; continue;
if (bss->pub.scan_width != new->pub.scan_width)
continue;
if (rcu_access_pointer(bss->pub.beacon_ies)) if (rcu_access_pointer(bss->pub.beacon_ies))
continue; continue;
ies = rcu_access_pointer(bss->pub.ies); ies = rcu_access_pointer(bss->pub.ies);
@ -870,11 +872,12 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
/* Returned bss is reference counted and must be cleaned up appropriately. */ /* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss* struct cfg80211_bss*
cfg80211_inform_bss(struct wiphy *wiphy, cfg80211_inform_bss_width(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
const u8 *bssid, u64 tsf, u16 capability, enum nl80211_bss_scan_width scan_width,
u16 beacon_interval, const u8 *ie, size_t ielen, const u8 *bssid, u64 tsf, u16 capability,
s32 signal, gfp_t gfp) u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp)
{ {
struct cfg80211_bss_ies *ies; struct cfg80211_bss_ies *ies;
struct cfg80211_internal_bss tmp = {}, *res; struct cfg80211_internal_bss tmp = {}, *res;
@ -892,6 +895,7 @@ cfg80211_inform_bss(struct wiphy *wiphy,
memcpy(tmp.pub.bssid, bssid, ETH_ALEN); memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
tmp.pub.channel = channel; tmp.pub.channel = channel;
tmp.pub.scan_width = scan_width;
tmp.pub.signal = signal; tmp.pub.signal = signal;
tmp.pub.beacon_interval = beacon_interval; tmp.pub.beacon_interval = beacon_interval;
tmp.pub.capability = capability; tmp.pub.capability = capability;
@ -924,14 +928,15 @@ cfg80211_inform_bss(struct wiphy *wiphy,
/* cfg80211_bss_update gives us a referenced result */ /* cfg80211_bss_update gives us a referenced result */
return &res->pub; return &res->pub;
} }
EXPORT_SYMBOL(cfg80211_inform_bss); EXPORT_SYMBOL(cfg80211_inform_bss_width);
/* Returned bss is reference counted and must be cleaned up appropriately. */ /* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss * struct cfg80211_bss *
cfg80211_inform_bss_frame(struct wiphy *wiphy, cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
struct ieee80211_mgmt *mgmt, size_t len, enum nl80211_bss_scan_width scan_width,
s32 signal, gfp_t gfp) struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp)
{ {
struct cfg80211_internal_bss tmp = {}, *res; struct cfg80211_internal_bss tmp = {}, *res;
struct cfg80211_bss_ies *ies; struct cfg80211_bss_ies *ies;
@ -941,7 +946,8 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
offsetof(struct ieee80211_mgmt, u.beacon.variable)); offsetof(struct ieee80211_mgmt, u.beacon.variable));
trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal); trace_cfg80211_inform_bss_width_frame(wiphy, channel, scan_width, mgmt,
len, signal);
if (WARN_ON(!mgmt)) if (WARN_ON(!mgmt))
return NULL; return NULL;
@ -976,6 +982,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
tmp.pub.channel = channel; tmp.pub.channel = channel;
tmp.pub.scan_width = scan_width;
tmp.pub.signal = signal; tmp.pub.signal = signal;
tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
@ -991,7 +998,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
/* cfg80211_bss_update gives us a referenced result */ /* cfg80211_bss_update gives us a referenced result */
return &res->pub; return &res->pub;
} }
EXPORT_SYMBOL(cfg80211_inform_bss_frame); EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);
void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
{ {

View File

@ -2391,26 +2391,30 @@ TRACE_EVENT(cfg80211_get_bss,
__entry->capa_mask, __entry->capa_val) __entry->capa_mask, __entry->capa_val)
); );
TRACE_EVENT(cfg80211_inform_bss_frame, TRACE_EVENT(cfg80211_inform_bss_width_frame,
TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel, TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
enum nl80211_bss_scan_width scan_width,
struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_mgmt *mgmt, size_t len,
s32 signal), s32 signal),
TP_ARGS(wiphy, channel, mgmt, len, signal), TP_ARGS(wiphy, channel, scan_width, mgmt, len, signal),
TP_STRUCT__entry( TP_STRUCT__entry(
WIPHY_ENTRY WIPHY_ENTRY
CHAN_ENTRY CHAN_ENTRY
__field(enum nl80211_bss_scan_width, scan_width)
__dynamic_array(u8, mgmt, len) __dynamic_array(u8, mgmt, len)
__field(s32, signal) __field(s32, signal)
), ),
TP_fast_assign( TP_fast_assign(
WIPHY_ASSIGN; WIPHY_ASSIGN;
CHAN_ASSIGN(channel); CHAN_ASSIGN(channel);
__entry->scan_width = scan_width;
if (mgmt) if (mgmt)
memcpy(__get_dynamic_array(mgmt), mgmt, len); memcpy(__get_dynamic_array(mgmt), mgmt, len);
__entry->signal = signal; __entry->signal = signal;
), ),
TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "signal: %d", TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d",
WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal) WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width,
__entry->signal)
); );
DECLARE_EVENT_CLASS(cfg80211_bss_evt, DECLARE_EVENT_CLASS(cfg80211_bss_evt,

View File

@ -33,7 +33,8 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
} }
EXPORT_SYMBOL(ieee80211_get_response_rate); EXPORT_SYMBOL(ieee80211_get_response_rate);
u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband) u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
enum nl80211_bss_scan_width scan_width)
{ {
struct ieee80211_rate *bitrates; struct ieee80211_rate *bitrates;
u32 mandatory_rates = 0; u32 mandatory_rates = 0;
@ -43,10 +44,15 @@ u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband)
if (WARN_ON(!sband)) if (WARN_ON(!sband))
return 1; return 1;
if (sband->band == IEEE80211_BAND_2GHZ) if (sband->band == IEEE80211_BAND_2GHZ) {
mandatory_flag = IEEE80211_RATE_MANDATORY_B; if (scan_width == NL80211_BSS_CHAN_WIDTH_5 ||
else scan_width == NL80211_BSS_CHAN_WIDTH_10)
mandatory_flag = IEEE80211_RATE_MANDATORY_G;
else
mandatory_flag = IEEE80211_RATE_MANDATORY_B;
} else {
mandatory_flag = IEEE80211_RATE_MANDATORY_A; mandatory_flag = IEEE80211_RATE_MANDATORY_A;
}
bitrates = sband->bitrates; bitrates = sband->bitrates;
for (i = 0; i < sband->n_bitrates; i++) for (i = 0; i < sband->n_bitrates; i++)