Here are a few groups of changes:
* EDMG channel support (60 GHz, just a single patch) * initial 6/7 GHz band support (Arend) * association timestamp recording (Ben) * rate control improvements for better performance with the mt76 driver (Felix) * various fixes for previous HE support changes (John) -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEH1e1rEeCd0AIMq6MB8qZga/fl8QFAl1dFssACgkQB8qZga/f l8SJvw/9FtGTTqIQ7CK8Jse8syUNmk2YsTirqwtlSAwBGD/ymIm0w9FhgzrRgzr3 PYQWX4yrMowfq/kEGaQD6x3IjdqG7YgwaCcdHp0Ag6QY/cP4xedV2jW6U/VPD58L uHVykMHLQaLuq9lcs5idKbPXnOU+j/Q4/YbY25fx7UDy0DdmRuupM909OGLXyWzs nJYr+SZ8ph9PRXvhUMmNhtu7GVYlnlkZ+fA/6LFxHwby6CEN7SGTkljehPFrjY1m 5I5sltlnpbhRsa0ZJBgdelhFlloiYTJYA4+G5nDvSWLz8QOOQ1iSYFo01Ob7hJlH p9gSRypDVsBqNMcOCHsprsmO9gTpopPpcZK7p1il28HLy3mPW3GSKdSneV6kzOcJ 8SdRxM6I3855b05JpfnUsdfp8ZOFu/l1y9glcyWCPQncOLGLYGTtkZTh12R8VzG+ PJaRstUu+AQNYsxfZFCpcDvRT3tLgj2ggY1/+xUQIh6VCAokCKo4LUGhYzIY8LZe cNLJIKtlM4GvyScr3qsdrVZiG0Qu872OK4WdVusFWBIpzjEvg5q8Ss2TpENG9T1k fODJavN7gVyDJ7uhAr1+lpMRHzMkZUFpmoUmtga8eYbeDyaK1tHSVmYI0H2A3YfU 3hkST11tfVjmRKIVsZA1vQfu2fRhvAKnk1EPsGJIF02qDAFrgyw= =Zcma -----END PGP SIGNATURE----- Merge tag 'mac80211-next-for-davem-2019-08-21' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next Johannes Berg: ==================== Here are a few groups of changes: * EDMG channel support (60 GHz, just a single patch) * initial 6/7 GHz band support (Arend) * association timestamp recording (Ben) * rate control improvements for better performance with the mt76 driver (Felix) * various fixes for previous HE support changes (John) ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
fb86b9208f
|
@ -351,7 +351,7 @@ int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
|
|||
BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
|
||||
BIT_ULL(NL80211_STA_INFO_TX_FAILED);
|
||||
|
||||
sinfo->txrate.flags = RATE_INFO_FLAGS_60G;
|
||||
sinfo->txrate.flags = RATE_INFO_FLAGS_DMG;
|
||||
sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs);
|
||||
sinfo->rxrate.mcs = stats->last_mcs_rx;
|
||||
sinfo->rx_bytes = stats->rx_bytes;
|
||||
|
|
|
@ -330,6 +330,60 @@ struct ieee80211_sband_iftype_data {
|
|||
struct ieee80211_sta_he_cap he_cap;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum ieee80211_edmg_bw_config - allowed channel bandwidth configurations
|
||||
*
|
||||
* @IEEE80211_EDMG_BW_CONFIG_4: 2.16GHz
|
||||
* @IEEE80211_EDMG_BW_CONFIG_5: 2.16GHz and 4.32GHz
|
||||
* @IEEE80211_EDMG_BW_CONFIG_6: 2.16GHz, 4.32GHz and 6.48GHz
|
||||
* @IEEE80211_EDMG_BW_CONFIG_7: 2.16GHz, 4.32GHz, 6.48GHz and 8.64GHz
|
||||
* @IEEE80211_EDMG_BW_CONFIG_8: 2.16GHz and 2.16GHz + 2.16GHz
|
||||
* @IEEE80211_EDMG_BW_CONFIG_9: 2.16GHz, 4.32GHz and 2.16GHz + 2.16GHz
|
||||
* @IEEE80211_EDMG_BW_CONFIG_10: 2.16GHz, 4.32GHz, 6.48GHz and 2.16GHz+2.16GHz
|
||||
* @IEEE80211_EDMG_BW_CONFIG_11: 2.16GHz, 4.32GHz, 6.48GHz, 8.64GHz and
|
||||
* 2.16GHz+2.16GHz
|
||||
* @IEEE80211_EDMG_BW_CONFIG_12: 2.16GHz, 2.16GHz + 2.16GHz and
|
||||
* 4.32GHz + 4.32GHz
|
||||
* @IEEE80211_EDMG_BW_CONFIG_13: 2.16GHz, 4.32GHz, 2.16GHz + 2.16GHz and
|
||||
* 4.32GHz + 4.32GHz
|
||||
* @IEEE80211_EDMG_BW_CONFIG_14: 2.16GHz, 4.32GHz, 6.48GHz, 2.16GHz + 2.16GHz
|
||||
* and 4.32GHz + 4.32GHz
|
||||
* @IEEE80211_EDMG_BW_CONFIG_15: 2.16GHz, 4.32GHz, 6.48GHz, 8.64GHz,
|
||||
* 2.16GHz + 2.16GHz and 4.32GHz + 4.32GHz
|
||||
*/
|
||||
enum ieee80211_edmg_bw_config {
|
||||
IEEE80211_EDMG_BW_CONFIG_4 = 4,
|
||||
IEEE80211_EDMG_BW_CONFIG_5 = 5,
|
||||
IEEE80211_EDMG_BW_CONFIG_6 = 6,
|
||||
IEEE80211_EDMG_BW_CONFIG_7 = 7,
|
||||
IEEE80211_EDMG_BW_CONFIG_8 = 8,
|
||||
IEEE80211_EDMG_BW_CONFIG_9 = 9,
|
||||
IEEE80211_EDMG_BW_CONFIG_10 = 10,
|
||||
IEEE80211_EDMG_BW_CONFIG_11 = 11,
|
||||
IEEE80211_EDMG_BW_CONFIG_12 = 12,
|
||||
IEEE80211_EDMG_BW_CONFIG_13 = 13,
|
||||
IEEE80211_EDMG_BW_CONFIG_14 = 14,
|
||||
IEEE80211_EDMG_BW_CONFIG_15 = 15,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_edmg - EDMG configuration
|
||||
*
|
||||
* This structure describes most essential parameters needed
|
||||
* to describe 802.11ay EDMG configuration
|
||||
*
|
||||
* @channels: bitmap that indicates the 2.16 GHz channel(s)
|
||||
* that are allowed to be used for transmissions.
|
||||
* Bit 0 indicates channel 1, bit 1 indicates channel 2, etc.
|
||||
* Set to 0 indicate EDMG not supported.
|
||||
* @bw_config: Channel BW Configuration subfield encodes
|
||||
* the allowed channel bandwidth configurations
|
||||
*/
|
||||
struct ieee80211_edmg {
|
||||
u8 channels;
|
||||
enum ieee80211_edmg_bw_config bw_config;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_supported_band - frequency band definition
|
||||
*
|
||||
|
@ -346,6 +400,7 @@ struct ieee80211_sband_iftype_data {
|
|||
* @n_bitrates: Number of bitrates in @bitrates
|
||||
* @ht_cap: HT capabilities in this band
|
||||
* @vht_cap: VHT capabilities in this band
|
||||
* @edmg_cap: EDMG capabilities in this band
|
||||
* @n_iftype_data: number of iftype data entries
|
||||
* @iftype_data: interface type data entries. Note that the bits in
|
||||
* @types_mask inside this structure cannot overlap (i.e. only
|
||||
|
@ -360,6 +415,7 @@ struct ieee80211_supported_band {
|
|||
int n_bitrates;
|
||||
struct ieee80211_sta_ht_cap ht_cap;
|
||||
struct ieee80211_sta_vht_cap vht_cap;
|
||||
struct ieee80211_edmg edmg_cap;
|
||||
u16 n_iftype_data;
|
||||
const struct ieee80211_sband_iftype_data *iftype_data;
|
||||
};
|
||||
|
@ -527,12 +583,17 @@ struct key_params {
|
|||
* @center_freq1: center frequency of first segment
|
||||
* @center_freq2: center frequency of second segment
|
||||
* (only with 80+80 MHz)
|
||||
* @edmg: define the EDMG channels configuration.
|
||||
* If edmg is requested (i.e. the .channels member is non-zero),
|
||||
* chan will define the primary channel and all other
|
||||
* parameters are ignored.
|
||||
*/
|
||||
struct cfg80211_chan_def {
|
||||
struct ieee80211_channel *chan;
|
||||
enum nl80211_chan_width width;
|
||||
u32 center_freq1;
|
||||
u32 center_freq2;
|
||||
struct ieee80211_edmg edmg;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -590,6 +651,19 @@ cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1,
|
|||
chandef1->center_freq2 == chandef2->center_freq2);
|
||||
}
|
||||
|
||||
/**
|
||||
* cfg80211_chandef_is_edmg - check if chandef represents an EDMG channel
|
||||
*
|
||||
* @chandef: the channel definition
|
||||
*
|
||||
* Return: %true if EDMG defined, %false otherwise.
|
||||
*/
|
||||
static inline bool
|
||||
cfg80211_chandef_is_edmg(const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
return chandef->edmg.channels || chandef->edmg.bw_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* cfg80211_chandef_compatible - check if two channel definitions are compatible
|
||||
* @chandef1: first channel definition
|
||||
|
@ -1177,15 +1251,17 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
|
|||
* @RATE_INFO_FLAGS_MCS: mcs field filled with HT MCS
|
||||
* @RATE_INFO_FLAGS_VHT_MCS: mcs field filled with VHT MCS
|
||||
* @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval
|
||||
* @RATE_INFO_FLAGS_60G: 60GHz MCS
|
||||
* @RATE_INFO_FLAGS_DMG: 60GHz MCS
|
||||
* @RATE_INFO_FLAGS_HE_MCS: HE MCS information
|
||||
* @RATE_INFO_FLAGS_EDMG: 60GHz MCS in EDMG mode
|
||||
*/
|
||||
enum rate_info_flags {
|
||||
RATE_INFO_FLAGS_MCS = BIT(0),
|
||||
RATE_INFO_FLAGS_VHT_MCS = BIT(1),
|
||||
RATE_INFO_FLAGS_SHORT_GI = BIT(2),
|
||||
RATE_INFO_FLAGS_60G = BIT(3),
|
||||
RATE_INFO_FLAGS_DMG = BIT(3),
|
||||
RATE_INFO_FLAGS_HE_MCS = BIT(4),
|
||||
RATE_INFO_FLAGS_EDMG = BIT(5),
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1225,6 +1301,7 @@ enum rate_info_bw {
|
|||
* @he_dcm: HE DCM value
|
||||
* @he_ru_alloc: HE RU allocation (from &enum nl80211_he_ru_alloc,
|
||||
* only valid if bw is %RATE_INFO_BW_HE_RU)
|
||||
* @n_bonded_ch: In case of EDMG the number of bonded channels (1-4)
|
||||
*/
|
||||
struct rate_info {
|
||||
u8 flags;
|
||||
|
@ -1235,6 +1312,7 @@ struct rate_info {
|
|||
u8 he_gi;
|
||||
u8 he_dcm;
|
||||
u8 he_ru_alloc;
|
||||
u8 n_bonded_ch;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1330,6 +1408,7 @@ struct cfg80211_tid_stats {
|
|||
* indicate the relevant values in this struct for them
|
||||
* @connected_time: time(in secs) since a station is last connected
|
||||
* @inactive_time: time since last station activity (tx/rx) in milliseconds
|
||||
* @assoc_at: bootime (ns) of the last association
|
||||
* @rx_bytes: bytes (size of MPDUs) received from this station
|
||||
* @tx_bytes: bytes (size of MPDUs) transmitted to this station
|
||||
* @llid: mesh local link id
|
||||
|
@ -1390,6 +1469,7 @@ struct station_info {
|
|||
u64 filled;
|
||||
u32 connected_time;
|
||||
u32 inactive_time;
|
||||
u64 assoc_at;
|
||||
u64 rx_bytes;
|
||||
u64 tx_bytes;
|
||||
u16 llid;
|
||||
|
@ -2436,6 +2516,9 @@ struct cfg80211_bss_selection {
|
|||
* @fils_erp_rrk_len: Length of @fils_erp_rrk in octets.
|
||||
* @want_1x: indicates user-space supports and wants to use 802.1X driver
|
||||
* offload of 4-way handshake.
|
||||
* @edmg: define the EDMG channels.
|
||||
* This may specify multiple channels and bonding options for the driver
|
||||
* to choose from, based on BSS configuration.
|
||||
*/
|
||||
struct cfg80211_connect_params {
|
||||
struct ieee80211_channel *channel;
|
||||
|
@ -2469,6 +2552,7 @@ struct cfg80211_connect_params {
|
|||
const u8 *fils_erp_rrk;
|
||||
size_t fils_erp_rrk_len;
|
||||
bool want_1x;
|
||||
struct ieee80211_edmg edmg;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -52,6 +52,11 @@
|
|||
#define NL80211_MULTICAST_GROUP_NAN "nan"
|
||||
#define NL80211_MULTICAST_GROUP_TESTMODE "testmode"
|
||||
|
||||
#define NL80211_EDMG_BW_CONFIG_MIN 4
|
||||
#define NL80211_EDMG_BW_CONFIG_MAX 15
|
||||
#define NL80211_EDMG_CHANNELS_MIN 1
|
||||
#define NL80211_EDMG_CHANNELS_MAX 0x3c /* 0b00111100 */
|
||||
|
||||
/**
|
||||
* DOC: Station handling
|
||||
*
|
||||
|
@ -2361,6 +2366,13 @@ enum nl80211_commands {
|
|||
* @NL80211_ATTR_HE_OBSS_PD: nested attribute for OBSS Packet Detection
|
||||
* functionality.
|
||||
*
|
||||
* @NL80211_ATTR_WIPHY_EDMG_CHANNELS: bitmap that indicates the 2.16 GHz
|
||||
* channel(s) that are allowed to be used for EDMG transmissions.
|
||||
* Defined by IEEE P802.11ay/D4.0 section 9.4.2.251. (u8 attribute)
|
||||
* @NL80211_ATTR_WIPHY_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
|
||||
* the allowed channel bandwidth configurations. (u8 attribute)
|
||||
* Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
|
||||
*
|
||||
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
|
@ -2820,6 +2832,9 @@ enum nl80211_attrs {
|
|||
|
||||
NL80211_ATTR_HE_OBSS_PD,
|
||||
|
||||
NL80211_ATTR_WIPHY_EDMG_CHANNELS,
|
||||
NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
|
||||
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
|
@ -3201,6 +3216,8 @@ enum nl80211_sta_bss_param {
|
|||
* sent to the station (u64, usec)
|
||||
* @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16)
|
||||
* @NL80211_STA_INFO_AIRTIME_LINK_METRIC: airtime link metric for mesh station
|
||||
* @NL80211_STA_INFO_ASSOC_AT_BOOTTIME: Timestamp (CLOCK_BOOTTIME, nanoseconds)
|
||||
* of STA's association
|
||||
* @__NL80211_STA_INFO_AFTER_LAST: internal
|
||||
* @NL80211_STA_INFO_MAX: highest possible station info attribute
|
||||
*/
|
||||
|
@ -3247,6 +3264,7 @@ enum nl80211_sta_info {
|
|||
NL80211_STA_INFO_TX_DURATION,
|
||||
NL80211_STA_INFO_AIRTIME_WEIGHT,
|
||||
NL80211_STA_INFO_AIRTIME_LINK_METRIC,
|
||||
NL80211_STA_INFO_ASSOC_AT_BOOTTIME,
|
||||
|
||||
/* keep last */
|
||||
__NL80211_STA_INFO_AFTER_LAST,
|
||||
|
@ -3428,6 +3446,12 @@ enum nl80211_band_iftype_attr {
|
|||
* @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE
|
||||
* @NL80211_BAND_ATTR_IFTYPE_DATA: nested array attribute, with each entry using
|
||||
* attributes from &enum nl80211_band_iftype_attr
|
||||
* @NL80211_BAND_ATTR_EDMG_CHANNELS: bitmap that indicates the 2.16 GHz
|
||||
* channel(s) that are allowed to be used for EDMG transmissions.
|
||||
* Defined by IEEE P802.11ay/D4.0 section 9.4.2.251.
|
||||
* @NL80211_BAND_ATTR_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
|
||||
* the allowed channel bandwidth configurations.
|
||||
* Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
|
||||
* @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
|
||||
* @__NL80211_BAND_ATTR_AFTER_LAST: internal use
|
||||
*/
|
||||
|
@ -3445,6 +3469,9 @@ enum nl80211_band_attr {
|
|||
NL80211_BAND_ATTR_VHT_CAPA,
|
||||
NL80211_BAND_ATTR_IFTYPE_DATA,
|
||||
|
||||
NL80211_BAND_ATTR_EDMG_CHANNELS,
|
||||
NL80211_BAND_ATTR_EDMG_BW_CONFIG,
|
||||
|
||||
/* keep last */
|
||||
__NL80211_BAND_ATTR_AFTER_LAST,
|
||||
NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
|
||||
|
@ -4543,6 +4570,7 @@ enum nl80211_txrate_gi {
|
|||
* @NL80211_BAND_2GHZ: 2.4 GHz ISM band
|
||||
* @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
|
||||
* @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
|
||||
* @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz)
|
||||
* @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
|
||||
* since newer kernel versions may support more bands
|
||||
*/
|
||||
|
@ -4550,6 +4578,7 @@ enum nl80211_band {
|
|||
NL80211_BAND_2GHZ,
|
||||
NL80211_BAND_5GHZ,
|
||||
NL80211_BAND_60GHZ,
|
||||
NL80211_BAND_6GHZ,
|
||||
|
||||
NUM_NL80211_BANDS,
|
||||
};
|
||||
|
|
|
@ -72,12 +72,13 @@ ieee80211_he_spr_ie_to_bss_conf(struct ieee80211_vif *vif,
|
|||
{
|
||||
struct ieee80211_he_obss_pd *he_obss_pd =
|
||||
&vif->bss_conf.he_obss_pd;
|
||||
const u8 *data = he_spr_ie_elem->optional;
|
||||
const u8 *data;
|
||||
|
||||
memset(he_obss_pd, 0, sizeof(*he_obss_pd));
|
||||
|
||||
if (!he_spr_ie_elem)
|
||||
return;
|
||||
data = he_spr_ie_elem->optional;
|
||||
|
||||
if (he_spr_ie_elem->he_sr_control &
|
||||
IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT)
|
||||
|
|
|
@ -158,10 +158,10 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
|||
memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
|
||||
ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
|
||||
|
||||
memset(chandef, 0, sizeof(struct cfg80211_chan_def));
|
||||
chandef->chan = channel;
|
||||
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
chandef->center_freq1 = channel->center_freq;
|
||||
chandef->center_freq2 = 0;
|
||||
|
||||
if (!ht_oper || !sta_ht_cap.ht_supported) {
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
|
|
|
@ -95,6 +95,7 @@ struct minstrel_sta_info {
|
|||
struct minstrel_priv {
|
||||
struct ieee80211_hw *hw;
|
||||
bool has_mrr;
|
||||
u32 sample_switch;
|
||||
unsigned int cw_min;
|
||||
unsigned int cw_max;
|
||||
unsigned int max_retry;
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#define AVG_AMPDU_SIZE 16
|
||||
#define AVG_PKT_SIZE 1200
|
||||
|
||||
#define SAMPLE_SWITCH_THR 100
|
||||
|
||||
/* Number of bits for an average sized packet */
|
||||
#define MCS_NBITS ((AVG_PKT_SIZE * AVG_AMPDU_SIZE) << 3)
|
||||
|
||||
|
@ -58,6 +60,7 @@
|
|||
[GROUP_IDX(_streams, _sgi, _ht40)] = { \
|
||||
.streams = _streams, \
|
||||
.shift = _s, \
|
||||
.bw = _ht40, \
|
||||
.flags = \
|
||||
IEEE80211_TX_RC_MCS | \
|
||||
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
|
||||
|
@ -94,6 +97,7 @@
|
|||
[VHT_GROUP_IDX(_streams, _sgi, _bw)] = { \
|
||||
.streams = _streams, \
|
||||
.shift = _s, \
|
||||
.bw = _bw, \
|
||||
.flags = \
|
||||
IEEE80211_TX_RC_VHT_MCS | \
|
||||
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
|
||||
|
@ -486,7 +490,7 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
|
|||
tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
|
||||
tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
|
||||
|
||||
if (tmp_cck_tp > tmp_mcs_tp) {
|
||||
if (tmp_cck_tp_rate && tmp_cck_tp > tmp_mcs_tp) {
|
||||
for(i = 0; i < MAX_THR_RATES; i++) {
|
||||
minstrel_ht_sort_best_tp_rates(mi, tmp_cck_tp_rate[i],
|
||||
tmp_mcs_tp_rate);
|
||||
|
@ -526,6 +530,133 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
|
|||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
minstrel_get_duration(int index)
|
||||
{
|
||||
const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
|
||||
unsigned int duration = group->duration[index % MCS_GROUP_RATES];
|
||||
return duration << group->shift;
|
||||
}
|
||||
|
||||
static bool
|
||||
minstrel_ht_probe_group(struct minstrel_ht_sta *mi, const struct mcs_group *tp_group,
|
||||
int tp_idx, const struct mcs_group *group)
|
||||
{
|
||||
if (group->bw < tp_group->bw)
|
||||
return false;
|
||||
|
||||
if (group->streams == tp_group->streams)
|
||||
return true;
|
||||
|
||||
if (tp_idx < 4 && group->streams == tp_group->streams - 1)
|
||||
return true;
|
||||
|
||||
return group->streams == tp_group->streams + 1;
|
||||
}
|
||||
|
||||
static void
|
||||
minstrel_ht_find_probe_rates(struct minstrel_ht_sta *mi, u16 *rates, int *n_rates,
|
||||
bool faster_rate)
|
||||
{
|
||||
const struct mcs_group *group, *tp_group;
|
||||
int i, g, max_dur;
|
||||
int tp_idx;
|
||||
|
||||
tp_group = &minstrel_mcs_groups[mi->max_tp_rate[0] / MCS_GROUP_RATES];
|
||||
tp_idx = mi->max_tp_rate[0] % MCS_GROUP_RATES;
|
||||
|
||||
max_dur = minstrel_get_duration(mi->max_tp_rate[0]);
|
||||
if (faster_rate)
|
||||
max_dur -= max_dur / 16;
|
||||
|
||||
for (g = 0; g < MINSTREL_GROUPS_NB; g++) {
|
||||
u16 supported = mi->supported[g];
|
||||
|
||||
if (!supported)
|
||||
continue;
|
||||
|
||||
group = &minstrel_mcs_groups[g];
|
||||
if (!minstrel_ht_probe_group(mi, tp_group, tp_idx, group))
|
||||
continue;
|
||||
|
||||
for (i = 0; supported; supported >>= 1, i++) {
|
||||
int idx;
|
||||
|
||||
if (!(supported & 1))
|
||||
continue;
|
||||
|
||||
if ((group->duration[i] << group->shift) > max_dur)
|
||||
continue;
|
||||
|
||||
idx = g * MCS_GROUP_RATES + i;
|
||||
if (idx == mi->max_tp_rate[0])
|
||||
continue;
|
||||
|
||||
rates[(*n_rates)++] = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
minstrel_ht_rate_sample_switch(struct minstrel_priv *mp,
|
||||
struct minstrel_ht_sta *mi)
|
||||
{
|
||||
struct minstrel_rate_stats *mrs;
|
||||
u16 rates[MINSTREL_GROUPS_NB];
|
||||
int n_rates = 0;
|
||||
int probe_rate = 0;
|
||||
bool faster_rate;
|
||||
int i;
|
||||
u8 random;
|
||||
|
||||
/*
|
||||
* Use rate switching instead of probing packets for devices with
|
||||
* little control over retry fallback behavior
|
||||
*/
|
||||
if (mp->hw->max_rates > 1)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the current EWMA prob is >75%, look for a rate that's 6.25%
|
||||
* faster than the max tp rate.
|
||||
* If that fails, look again for a rate that is at least as fast
|
||||
*/
|
||||
mrs = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
|
||||
faster_rate = mrs->prob_ewma > MINSTREL_FRAC(75, 100);
|
||||
minstrel_ht_find_probe_rates(mi, rates, &n_rates, faster_rate);
|
||||
if (!n_rates && faster_rate)
|
||||
minstrel_ht_find_probe_rates(mi, rates, &n_rates, false);
|
||||
|
||||
/* If no suitable rate was found, try to pick the next one in the group */
|
||||
if (!n_rates) {
|
||||
int g_idx = mi->max_tp_rate[0] / MCS_GROUP_RATES;
|
||||
u16 supported = mi->supported[g_idx];
|
||||
|
||||
supported >>= mi->max_tp_rate[0] % MCS_GROUP_RATES;
|
||||
for (i = 0; supported; i++) {
|
||||
if (!(supported & 1))
|
||||
continue;
|
||||
|
||||
probe_rate = mi->max_tp_rate[0] + i;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
if (n_rates > 1) {
|
||||
random = prandom_u32();
|
||||
i = random % n_rates;
|
||||
}
|
||||
probe_rate = rates[i];
|
||||
|
||||
out:
|
||||
mi->sample_rate = probe_rate;
|
||||
mi->sample_mode = MINSTREL_SAMPLE_ACTIVE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update rate statistics and select new primary rates
|
||||
*
|
||||
|
@ -536,7 +667,8 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
|
|||
* higher throughput rates, even if the probablity is a bit lower
|
||||
*/
|
||||
static void
|
||||
minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||
minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
bool sample)
|
||||
{
|
||||
struct minstrel_mcs_group_data *mg;
|
||||
struct minstrel_rate_stats *mrs;
|
||||
|
@ -544,6 +676,18 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|||
u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
|
||||
u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
|
||||
|
||||
mi->sample_mode = MINSTREL_SAMPLE_IDLE;
|
||||
|
||||
if (sample) {
|
||||
mi->total_packets_cur = mi->total_packets -
|
||||
mi->total_packets_last;
|
||||
mi->total_packets_last = mi->total_packets;
|
||||
}
|
||||
if (!mp->sample_switch)
|
||||
sample = false;
|
||||
if (mi->total_packets_cur < SAMPLE_SWITCH_THR && mp->sample_switch != 1)
|
||||
sample = false;
|
||||
|
||||
if (mi->ampdu_packets > 0) {
|
||||
if (!ieee80211_hw_check(mp->hw, TX_STATUS_NO_AMPDU_LEN))
|
||||
mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
|
||||
|
@ -558,11 +702,19 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|||
mi->sample_slow = 0;
|
||||
mi->sample_count = 0;
|
||||
|
||||
/* Initialize global rate indexes */
|
||||
for(j = 0; j < MAX_THR_RATES; j++){
|
||||
tmp_mcs_tp_rate[j] = 0;
|
||||
tmp_cck_tp_rate[j] = 0;
|
||||
}
|
||||
memset(tmp_mcs_tp_rate, 0, sizeof(tmp_mcs_tp_rate));
|
||||
memset(tmp_cck_tp_rate, 0, sizeof(tmp_cck_tp_rate));
|
||||
if (mi->supported[MINSTREL_CCK_GROUP])
|
||||
for (j = 0; j < ARRAY_SIZE(tmp_cck_tp_rate); j++)
|
||||
tmp_cck_tp_rate[j] = MINSTREL_CCK_GROUP * MCS_GROUP_RATES;
|
||||
|
||||
if (mi->supported[MINSTREL_VHT_GROUP_0])
|
||||
index = MINSTREL_VHT_GROUP_0 * MCS_GROUP_RATES;
|
||||
else
|
||||
index = MINSTREL_HT_GROUP_0 * MCS_GROUP_RATES;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(tmp_mcs_tp_rate); j++)
|
||||
tmp_mcs_tp_rate[j] = index;
|
||||
|
||||
/* Find best rate sets within all MCS groups*/
|
||||
for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
|
||||
|
@ -575,7 +727,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|||
|
||||
/* (re)Initialize group rate indexes */
|
||||
for(j = 0; j < MAX_THR_RATES; j++)
|
||||
tmp_group_tp_rate[j] = group;
|
||||
tmp_group_tp_rate[j] = MCS_GROUP_RATES * group;
|
||||
|
||||
for (i = 0; i < MCS_GROUP_RATES; i++) {
|
||||
if (!(mi->supported[group] & BIT(i)))
|
||||
|
@ -622,12 +774,16 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|||
/* try to sample all available rates during each interval */
|
||||
mi->sample_count *= 8;
|
||||
|
||||
if (sample)
|
||||
minstrel_ht_rate_sample_switch(mp, mi);
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/* use fixed index if set */
|
||||
if (mp->fixed_rate_idx != -1) {
|
||||
for (i = 0; i < 4; i++)
|
||||
mi->max_tp_rate[i] = mp->fixed_rate_idx;
|
||||
mi->max_prob_rate = mp->fixed_rate_idx;
|
||||
mi->sample_mode = MINSTREL_SAMPLE_IDLE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -731,15 +887,17 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
|
|||
struct minstrel_ht_sta_priv *msp = priv_sta;
|
||||
struct minstrel_ht_sta *mi = &msp->ht;
|
||||
struct ieee80211_tx_rate *ar = info->status.rates;
|
||||
struct minstrel_rate_stats *rate, *rate2;
|
||||
struct minstrel_rate_stats *rate, *rate2, *rate_sample = NULL;
|
||||
struct minstrel_priv *mp = priv;
|
||||
bool last, update = false;
|
||||
bool sample_status = false;
|
||||
int i;
|
||||
|
||||
if (!msp->is_ht)
|
||||
return mac80211_minstrel.tx_status_ext(priv, sband,
|
||||
&msp->legacy, st);
|
||||
|
||||
|
||||
/* This packet was aggregated but doesn't carry status info */
|
||||
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
|
||||
!(info->flags & IEEE80211_TX_STAT_AMPDU))
|
||||
|
@ -765,12 +923,17 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
|
|||
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
|
||||
mi->sample_packets += info->status.ampdu_len;
|
||||
|
||||
if (mi->sample_mode != MINSTREL_SAMPLE_IDLE)
|
||||
rate_sample = minstrel_get_ratestats(mi, mi->sample_rate);
|
||||
|
||||
last = !minstrel_ht_txstat_valid(mp, &ar[0]);
|
||||
for (i = 0; !last; i++) {
|
||||
last = (i == IEEE80211_TX_MAX_RATES - 1) ||
|
||||
!minstrel_ht_txstat_valid(mp, &ar[i + 1]);
|
||||
|
||||
rate = minstrel_ht_get_stats(mp, mi, &ar[i]);
|
||||
if (rate == rate_sample)
|
||||
sample_status = true;
|
||||
|
||||
if (last)
|
||||
rate->success += info->status.ampdu_ack_len;
|
||||
|
@ -778,44 +941,60 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
|
|||
rate->attempts += ar[i].count * info->status.ampdu_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* check for sudden death of spatial multiplexing,
|
||||
* downgrade to a lower number of streams if necessary.
|
||||
*/
|
||||
rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
|
||||
if (rate->attempts > 30 &&
|
||||
MINSTREL_FRAC(rate->success, rate->attempts) <
|
||||
MINSTREL_FRAC(20, 100)) {
|
||||
minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
|
||||
switch (mi->sample_mode) {
|
||||
case MINSTREL_SAMPLE_IDLE:
|
||||
break;
|
||||
|
||||
case MINSTREL_SAMPLE_ACTIVE:
|
||||
if (!sample_status)
|
||||
break;
|
||||
|
||||
mi->sample_mode = MINSTREL_SAMPLE_PENDING;
|
||||
update = true;
|
||||
break;
|
||||
|
||||
case MINSTREL_SAMPLE_PENDING:
|
||||
if (sample_status)
|
||||
break;
|
||||
|
||||
update = true;
|
||||
minstrel_ht_update_stats(mp, mi, false);
|
||||
break;
|
||||
}
|
||||
|
||||
rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
|
||||
if (rate2->attempts > 30 &&
|
||||
MINSTREL_FRAC(rate2->success, rate2->attempts) <
|
||||
MINSTREL_FRAC(20, 100)) {
|
||||
minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
|
||||
update = true;
|
||||
|
||||
if (mp->hw->max_rates > 1) {
|
||||
/*
|
||||
* check for sudden death of spatial multiplexing,
|
||||
* downgrade to a lower number of streams if necessary.
|
||||
*/
|
||||
rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
|
||||
if (rate->attempts > 30 &&
|
||||
MINSTREL_FRAC(rate->success, rate->attempts) <
|
||||
MINSTREL_FRAC(20, 100)) {
|
||||
minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
|
||||
update = true;
|
||||
}
|
||||
|
||||
rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
|
||||
if (rate2->attempts > 30 &&
|
||||
MINSTREL_FRAC(rate2->success, rate2->attempts) <
|
||||
MINSTREL_FRAC(20, 100)) {
|
||||
minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
|
||||
update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (time_after(jiffies, mi->last_stats_update +
|
||||
(mp->update_interval / 2 * HZ) / 1000)) {
|
||||
update = true;
|
||||
minstrel_ht_update_stats(mp, mi);
|
||||
minstrel_ht_update_stats(mp, mi, true);
|
||||
}
|
||||
|
||||
if (update)
|
||||
minstrel_ht_update_rates(mp, mi);
|
||||
}
|
||||
|
||||
static inline int
|
||||
minstrel_get_duration(int index)
|
||||
{
|
||||
const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
|
||||
unsigned int duration = group->duration[index % MCS_GROUP_RATES];
|
||||
return duration << group->shift;
|
||||
}
|
||||
|
||||
static void
|
||||
minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
int index)
|
||||
|
@ -980,14 +1159,18 @@ static void
|
|||
minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||
{
|
||||
struct ieee80211_sta_rates *rates;
|
||||
u16 first_rate = mi->max_tp_rate[0];
|
||||
int i = 0;
|
||||
|
||||
if (mi->sample_mode == MINSTREL_SAMPLE_ACTIVE)
|
||||
first_rate = mi->sample_rate;
|
||||
|
||||
rates = kzalloc(sizeof(*rates), GFP_ATOMIC);
|
||||
if (!rates)
|
||||
return;
|
||||
|
||||
/* Start with max_tp_rate[0] */
|
||||
minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate[0]);
|
||||
minstrel_ht_set_rate(mp, mi, rates, i++, first_rate);
|
||||
|
||||
if (mp->hw->max_rates >= 3) {
|
||||
/* At least 3 tx rates supported, use max_tp_rate[1] next */
|
||||
|
@ -1012,6 +1195,11 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|||
int tp_rate1, tp_rate2;
|
||||
int sample_idx = 0;
|
||||
|
||||
if (mp->hw->max_rates == 1 && mp->sample_switch &&
|
||||
(mi->total_packets_cur >= SAMPLE_SWITCH_THR ||
|
||||
mp->sample_switch == 1))
|
||||
return -1;
|
||||
|
||||
if (mi->sample_wait > 0) {
|
||||
mi->sample_wait--;
|
||||
return -1;
|
||||
|
@ -1059,6 +1247,21 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|||
minstrel_get_duration(mi->max_prob_rate) * 3 < sample_dur)
|
||||
return -1;
|
||||
|
||||
|
||||
/*
|
||||
* For devices with no configurable multi-rate retry, skip sampling
|
||||
* below the per-group max throughput rate, and only use one sampling
|
||||
* attempt per rate
|
||||
*/
|
||||
if (mp->hw->max_rates == 1 &&
|
||||
(minstrel_get_duration(mg->max_group_tp_rate[0]) < sample_dur ||
|
||||
mrs->attempts))
|
||||
return -1;
|
||||
|
||||
/* Skip already sampled slow rates */
|
||||
if (sample_dur >= minstrel_get_duration(tp_rate1) && mrs->attempts)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Make sure that lower rates get sampled only occasionally,
|
||||
* if the link is working perfectly.
|
||||
|
@ -1318,7 +1521,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
|||
mi->supported[MINSTREL_CCK_GROUP] |= mi->cck_supported_short << 4;
|
||||
|
||||
/* create an initial rate table with the lowest supported rates */
|
||||
minstrel_ht_update_stats(mp, mi);
|
||||
minstrel_ht_update_stats(mp, mi, true);
|
||||
minstrel_ht_update_rates(mp, mi);
|
||||
|
||||
return;
|
||||
|
@ -1436,6 +1639,8 @@ minstrel_ht_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
|
|||
if (!mp)
|
||||
return NULL;
|
||||
|
||||
mp->sample_switch = -1;
|
||||
|
||||
/* contention window settings
|
||||
* Just an approximation. Using the per-queue values would complicate
|
||||
* the calculations and is probably unnecessary */
|
||||
|
@ -1467,6 +1672,8 @@ minstrel_ht_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
|
|||
mp->fixed_rate_idx = (u32) -1;
|
||||
debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
|
||||
&mp->fixed_rate_idx);
|
||||
debugfs_create_u32("sample_switch", S_IRUGO | S_IWUSR, debugfsdir,
|
||||
&mp->sample_switch);
|
||||
#endif
|
||||
|
||||
minstrel_ht_init_cck_rates(mp);
|
||||
|
|
|
@ -33,6 +33,7 @@ struct mcs_group {
|
|||
u16 flags;
|
||||
u8 streams;
|
||||
u8 shift;
|
||||
u8 bw;
|
||||
u16 duration[MCS_GROUP_RATES];
|
||||
};
|
||||
|
||||
|
@ -50,6 +51,12 @@ struct minstrel_mcs_group_data {
|
|||
struct minstrel_rate_stats rates[MCS_GROUP_RATES];
|
||||
};
|
||||
|
||||
enum minstrel_sample_mode {
|
||||
MINSTREL_SAMPLE_IDLE,
|
||||
MINSTREL_SAMPLE_ACTIVE,
|
||||
MINSTREL_SAMPLE_PENDING,
|
||||
};
|
||||
|
||||
struct minstrel_ht_sta {
|
||||
struct ieee80211_sta *sta;
|
||||
|
||||
|
@ -71,6 +78,8 @@ struct minstrel_ht_sta {
|
|||
unsigned int overhead;
|
||||
unsigned int overhead_rtscts;
|
||||
|
||||
unsigned int total_packets_last;
|
||||
unsigned int total_packets_cur;
|
||||
unsigned int total_packets;
|
||||
unsigned int sample_packets;
|
||||
|
||||
|
@ -82,6 +91,9 @@ struct minstrel_ht_sta {
|
|||
u8 sample_count;
|
||||
u8 sample_slow;
|
||||
|
||||
enum minstrel_sample_mode sample_mode;
|
||||
u16 sample_rate;
|
||||
|
||||
/* current MCS group to be sampled */
|
||||
u8 sample_group;
|
||||
|
||||
|
|
|
@ -1961,6 +1961,7 @@ int sta_info_move_state(struct sta_info *sta,
|
|||
case IEEE80211_STA_ASSOC:
|
||||
if (sta->sta_state == IEEE80211_STA_AUTH) {
|
||||
set_bit(WLAN_STA_ASSOC, &sta->_flags);
|
||||
sta->assoc_at = ktime_get_boottime_ns();
|
||||
ieee80211_recalc_min_chandef(sta->sdata);
|
||||
if (!sta->sta.support_p2p_ps)
|
||||
ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
|
||||
|
@ -2190,6 +2191,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
|
|||
BIT_ULL(NL80211_STA_INFO_STA_FLAGS) |
|
||||
BIT_ULL(NL80211_STA_INFO_BSS_PARAM) |
|
||||
BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME) |
|
||||
BIT_ULL(NL80211_STA_INFO_ASSOC_AT_BOOTTIME) |
|
||||
BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
|
@ -2198,6 +2200,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
|
|||
}
|
||||
|
||||
sinfo->connected_time = ktime_get_seconds() - sta->last_connected;
|
||||
sinfo->assoc_at = sta->assoc_at;
|
||||
sinfo->inactive_time =
|
||||
jiffies_to_msecs(jiffies - ieee80211_sta_last_active(sta));
|
||||
|
||||
|
|
|
@ -466,6 +466,7 @@ struct ieee80211_sta_rx_stats {
|
|||
* the station when it leaves powersave or polls for frames
|
||||
* @driver_buffered_tids: bitmap of TIDs the driver has data buffered on
|
||||
* @txq_buffered_tids: bitmap of TIDs that mac80211 has txq data buffered on
|
||||
* @assoc_at: clock boottime (in ns) of last association
|
||||
* @last_connected: time (in seconds) when a station got connected
|
||||
* @last_seq_ctrl: last received seq/frag number from this STA (per TID
|
||||
* plus one for non-QoS frames)
|
||||
|
@ -562,6 +563,7 @@ struct sta_info {
|
|||
unsigned long driver_buffered_tids;
|
||||
unsigned long txq_buffered_tids;
|
||||
|
||||
u64 assoc_at;
|
||||
long last_connected;
|
||||
|
||||
/* Updated from RX path only, no locking requirements */
|
||||
|
|
|
@ -260,9 +260,16 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info,
|
|||
int len = sizeof(struct ieee80211_radiotap_header);
|
||||
|
||||
/* IEEE80211_RADIOTAP_RATE rate */
|
||||
if (info->status.rates[0].idx >= 0 &&
|
||||
!(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
|
||||
IEEE80211_TX_RC_VHT_MCS)))
|
||||
if (status && status->rate && !(status->rate->flags &
|
||||
(RATE_INFO_FLAGS_MCS |
|
||||
RATE_INFO_FLAGS_DMG |
|
||||
RATE_INFO_FLAGS_EDMG |
|
||||
RATE_INFO_FLAGS_VHT_MCS |
|
||||
RATE_INFO_FLAGS_HE_MCS)))
|
||||
len += 2;
|
||||
else if (info->status.rates[0].idx >= 0 &&
|
||||
!(info->status.rates[0].flags &
|
||||
(IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)))
|
||||
len += 2;
|
||||
|
||||
/* IEEE80211_RADIOTAP_TX_FLAGS */
|
||||
|
@ -321,13 +328,14 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
|
|||
|
||||
/* IEEE80211_RADIOTAP_RATE */
|
||||
|
||||
if (status && status->rate && !(status->rate->flags &
|
||||
(RATE_INFO_FLAGS_MCS |
|
||||
RATE_INFO_FLAGS_60G |
|
||||
RATE_INFO_FLAGS_VHT_MCS |
|
||||
RATE_INFO_FLAGS_HE_MCS)))
|
||||
legacy_rate = status->rate->legacy;
|
||||
else if (info->status.rates[0].idx >= 0 &&
|
||||
if (status && status->rate) {
|
||||
if (!(status->rate->flags & (RATE_INFO_FLAGS_MCS |
|
||||
RATE_INFO_FLAGS_DMG |
|
||||
RATE_INFO_FLAGS_EDMG |
|
||||
RATE_INFO_FLAGS_VHT_MCS |
|
||||
RATE_INFO_FLAGS_HE_MCS)))
|
||||
legacy_rate = status->rate->legacy;
|
||||
} else if (info->status.rates[0].idx >= 0 &&
|
||||
!(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
|
||||
IEEE80211_TX_RC_VHT_MCS)))
|
||||
legacy_rate =
|
||||
|
@ -397,7 +405,7 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
|
|||
*pos = 11;
|
||||
break;
|
||||
case RATE_INFO_BW_80:
|
||||
*pos = 2;
|
||||
*pos = 4;
|
||||
break;
|
||||
case RATE_INFO_BW_40:
|
||||
*pos = 1;
|
||||
|
@ -406,6 +414,7 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
|
|||
*pos = 0;
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
|
||||
/* u8 mcs_nss[4] */
|
||||
*pos = (status->rate->mcs << 4) | status->rate->nss;
|
||||
|
|
|
@ -162,6 +162,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
|
|||
break;
|
||||
}
|
||||
case NL80211_BAND_5GHZ:
|
||||
case NL80211_BAND_6GHZ:
|
||||
if (r->flags & IEEE80211_RATE_MANDATORY_A)
|
||||
mrate = r->bitrate;
|
||||
break;
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
#include "core.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
static bool cfg80211_valid_60g_freq(u32 freq)
|
||||
{
|
||||
return freq >= 58320 && freq <= 70200;
|
||||
}
|
||||
|
||||
void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
|
||||
struct ieee80211_channel *chan,
|
||||
enum nl80211_channel_type chan_type)
|
||||
|
@ -23,6 +28,8 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
|
|||
|
||||
chandef->chan = chan;
|
||||
chandef->center_freq2 = 0;
|
||||
chandef->edmg.bw_config = 0;
|
||||
chandef->edmg.channels = 0;
|
||||
|
||||
switch (chan_type) {
|
||||
case NL80211_CHAN_NO_HT:
|
||||
|
@ -47,6 +54,91 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
|
|||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_create);
|
||||
|
||||
static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
int max_contiguous = 0;
|
||||
int num_of_enabled = 0;
|
||||
int contiguous = 0;
|
||||
int i;
|
||||
|
||||
if (!chandef->edmg.channels || !chandef->edmg.bw_config)
|
||||
return false;
|
||||
|
||||
if (!cfg80211_valid_60g_freq(chandef->chan->center_freq))
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (chandef->edmg.channels & BIT(i)) {
|
||||
contiguous++;
|
||||
num_of_enabled++;
|
||||
} else {
|
||||
contiguous = 0;
|
||||
}
|
||||
|
||||
max_contiguous = max(contiguous, max_contiguous);
|
||||
}
|
||||
/* basic verification of edmg configuration according to
|
||||
* IEEE P802.11ay/D4.0 section 9.4.2.251
|
||||
*/
|
||||
/* check bw_config against contiguous edmg channels */
|
||||
switch (chandef->edmg.bw_config) {
|
||||
case IEEE80211_EDMG_BW_CONFIG_4:
|
||||
case IEEE80211_EDMG_BW_CONFIG_8:
|
||||
case IEEE80211_EDMG_BW_CONFIG_12:
|
||||
if (max_contiguous < 1)
|
||||
return false;
|
||||
break;
|
||||
case IEEE80211_EDMG_BW_CONFIG_5:
|
||||
case IEEE80211_EDMG_BW_CONFIG_9:
|
||||
case IEEE80211_EDMG_BW_CONFIG_13:
|
||||
if (max_contiguous < 2)
|
||||
return false;
|
||||
break;
|
||||
case IEEE80211_EDMG_BW_CONFIG_6:
|
||||
case IEEE80211_EDMG_BW_CONFIG_10:
|
||||
case IEEE80211_EDMG_BW_CONFIG_14:
|
||||
if (max_contiguous < 3)
|
||||
return false;
|
||||
break;
|
||||
case IEEE80211_EDMG_BW_CONFIG_7:
|
||||
case IEEE80211_EDMG_BW_CONFIG_11:
|
||||
case IEEE80211_EDMG_BW_CONFIG_15:
|
||||
if (max_contiguous < 4)
|
||||
return false;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
/* check bw_config against aggregated (non contiguous) edmg channels */
|
||||
switch (chandef->edmg.bw_config) {
|
||||
case IEEE80211_EDMG_BW_CONFIG_4:
|
||||
case IEEE80211_EDMG_BW_CONFIG_5:
|
||||
case IEEE80211_EDMG_BW_CONFIG_6:
|
||||
case IEEE80211_EDMG_BW_CONFIG_7:
|
||||
break;
|
||||
case IEEE80211_EDMG_BW_CONFIG_8:
|
||||
case IEEE80211_EDMG_BW_CONFIG_9:
|
||||
case IEEE80211_EDMG_BW_CONFIG_10:
|
||||
case IEEE80211_EDMG_BW_CONFIG_11:
|
||||
if (num_of_enabled < 2)
|
||||
return false;
|
||||
break;
|
||||
case IEEE80211_EDMG_BW_CONFIG_12:
|
||||
case IEEE80211_EDMG_BW_CONFIG_13:
|
||||
case IEEE80211_EDMG_BW_CONFIG_14:
|
||||
case IEEE80211_EDMG_BW_CONFIG_15:
|
||||
if (num_of_enabled < 4 || max_contiguous < 2)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
u32 control_freq;
|
||||
|
@ -112,6 +204,10 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (cfg80211_chandef_is_edmg(chandef) &&
|
||||
!cfg80211_edmg_chandef_valid(chandef))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_valid);
|
||||
|
@ -721,12 +817,66 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* check if the operating channels are valid and supported */
|
||||
static bool cfg80211_edmg_usable(struct wiphy *wiphy, u8 edmg_channels,
|
||||
enum ieee80211_edmg_bw_config edmg_bw_config,
|
||||
int primary_channel,
|
||||
struct ieee80211_edmg *edmg_cap)
|
||||
{
|
||||
struct ieee80211_channel *chan;
|
||||
int i, freq;
|
||||
int channels_counter = 0;
|
||||
|
||||
if (!edmg_channels && !edmg_bw_config)
|
||||
return true;
|
||||
|
||||
if ((!edmg_channels && edmg_bw_config) ||
|
||||
(edmg_channels && !edmg_bw_config))
|
||||
return false;
|
||||
|
||||
if (!(edmg_channels & BIT(primary_channel - 1)))
|
||||
return false;
|
||||
|
||||
/* 60GHz channels 1..6 */
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (!(edmg_channels & BIT(i)))
|
||||
continue;
|
||||
|
||||
if (!(edmg_cap->channels & BIT(i)))
|
||||
return false;
|
||||
|
||||
channels_counter++;
|
||||
|
||||
freq = ieee80211_channel_to_frequency(i + 1,
|
||||
NL80211_BAND_60GHZ);
|
||||
chan = ieee80211_get_channel(wiphy, freq);
|
||||
if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* IEEE802.11 allows max 4 channels */
|
||||
if (channels_counter > 4)
|
||||
return false;
|
||||
|
||||
/* check bw_config is a subset of what driver supports
|
||||
* (see IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13)
|
||||
*/
|
||||
if ((edmg_bw_config % 4) > (edmg_cap->bw_config % 4))
|
||||
return false;
|
||||
|
||||
if (edmg_bw_config > edmg_cap->bw_config)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 prohibited_flags)
|
||||
{
|
||||
struct ieee80211_sta_ht_cap *ht_cap;
|
||||
struct ieee80211_sta_vht_cap *vht_cap;
|
||||
struct ieee80211_edmg *edmg_cap;
|
||||
u32 width, control_freq, cap;
|
||||
|
||||
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
||||
|
@ -734,6 +884,15 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
|||
|
||||
ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
|
||||
vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
|
||||
edmg_cap = &wiphy->bands[chandef->chan->band]->edmg_cap;
|
||||
|
||||
if (edmg_cap->channels &&
|
||||
!cfg80211_edmg_usable(wiphy,
|
||||
chandef->edmg.channels,
|
||||
chandef->edmg.bw_config,
|
||||
chandef->chan->hw_value,
|
||||
edmg_cap))
|
||||
return false;
|
||||
|
||||
control_freq = chandef->chan->center_freq;
|
||||
|
||||
|
@ -894,7 +1053,8 @@ static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy,
|
|||
if (chan == other_chan)
|
||||
return true;
|
||||
|
||||
if (chan->band != NL80211_BAND_5GHZ)
|
||||
if (chan->band != NL80211_BAND_5GHZ &&
|
||||
chan->band != NL80211_BAND_6GHZ)
|
||||
continue;
|
||||
|
||||
r1 = cfg80211_get_unii(chan->center_freq);
|
||||
|
|
|
@ -104,13 +104,19 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
|
|||
* use the mandatory rate set for 11b or
|
||||
* 11a for maximum compatibility.
|
||||
*/
|
||||
struct ieee80211_supported_band *sband =
|
||||
rdev->wiphy.bands[params->chandef.chan->band];
|
||||
struct ieee80211_supported_band *sband;
|
||||
enum nl80211_band band;
|
||||
u32 flag;
|
||||
int j;
|
||||
u32 flag = params->chandef.chan->band == NL80211_BAND_5GHZ ?
|
||||
IEEE80211_RATE_MANDATORY_A :
|
||||
IEEE80211_RATE_MANDATORY_B;
|
||||
|
||||
band = params->chandef.chan->band;
|
||||
if (band == NL80211_BAND_5GHZ ||
|
||||
band == NL80211_BAND_6GHZ)
|
||||
flag = IEEE80211_RATE_MANDATORY_A;
|
||||
else
|
||||
flag = IEEE80211_RATE_MANDATORY_B;
|
||||
|
||||
sband = rdev->wiphy.bands[band];
|
||||
for (j = 0; j < sband->n_bitrates; j++) {
|
||||
if (sband->bitrates[j].flags & flag)
|
||||
params->basic_rates |= BIT(j);
|
||||
|
|
|
@ -298,6 +298,13 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
|||
|
||||
[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_WIPHY_EDMG_CHANNELS] = NLA_POLICY_RANGE(NLA_U8,
|
||||
NL80211_EDMG_CHANNELS_MIN,
|
||||
NL80211_EDMG_CHANNELS_MAX),
|
||||
[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG] = NLA_POLICY_RANGE(NLA_U8,
|
||||
NL80211_EDMG_BW_CONFIG_MIN,
|
||||
NL80211_EDMG_BW_CONFIG_MAX),
|
||||
|
||||
[NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
|
||||
|
@ -677,6 +684,7 @@ static const struct nla_policy
|
|||
nl80211_match_band_rssi_policy[NUM_NL80211_BANDS] = {
|
||||
[NL80211_BAND_2GHZ] = { .type = NLA_S32 },
|
||||
[NL80211_BAND_5GHZ] = { .type = NLA_S32 },
|
||||
[NL80211_BAND_6GHZ] = { .type = NLA_S32 },
|
||||
[NL80211_BAND_60GHZ] = { .type = NLA_S32 },
|
||||
};
|
||||
|
||||
|
@ -1573,6 +1581,15 @@ static int nl80211_send_band_rateinfo(struct sk_buff *msg,
|
|||
nla_nest_end(msg, nl_iftype_data);
|
||||
}
|
||||
|
||||
/* add EDMG info */
|
||||
if (sband->edmg_cap.channels &&
|
||||
(nla_put_u8(msg, NL80211_BAND_ATTR_EDMG_CHANNELS,
|
||||
sband->edmg_cap.channels) ||
|
||||
nla_put_u8(msg, NL80211_BAND_ATTR_EDMG_BW_CONFIG,
|
||||
sband->edmg_cap.bw_config)))
|
||||
|
||||
return -ENOBUFS;
|
||||
|
||||
/* add bitrates */
|
||||
nl_rates = nla_nest_start_noflag(msg, NL80211_BAND_ATTR_RATES);
|
||||
if (!nl_rates)
|
||||
|
@ -2676,6 +2693,18 @@ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
|
|||
nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ2]);
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]) {
|
||||
chandef->edmg.channels =
|
||||
nla_get_u8(info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]);
|
||||
|
||||
if (info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG])
|
||||
chandef->edmg.bw_config =
|
||||
nla_get_u8(info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]);
|
||||
} else {
|
||||
chandef->edmg.bw_config = 0;
|
||||
chandef->edmg.channels = 0;
|
||||
}
|
||||
|
||||
if (!cfg80211_chandef_valid(chandef)) {
|
||||
NL_SET_ERR_MSG(extack, "invalid channel definition");
|
||||
return -EINVAL;
|
||||
|
@ -5031,6 +5060,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
|
|||
|
||||
PUT_SINFO(CONNECTED_TIME, connected_time, u32);
|
||||
PUT_SINFO(INACTIVE_TIME, inactive_time, u32);
|
||||
PUT_SINFO_U64(ASSOC_AT_BOOTTIME, assoc_at);
|
||||
|
||||
if (sinfo->filled & (BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
|
||||
BIT_ULL(NL80211_STA_INFO_RX_BYTES64)) &&
|
||||
|
@ -9892,6 +9922,15 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]) {
|
||||
connect.edmg.channels =
|
||||
nla_get_u8(info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]);
|
||||
|
||||
if (info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG])
|
||||
connect.edmg.bw_config =
|
||||
nla_get_u8(info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]);
|
||||
}
|
||||
|
||||
if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
|
||||
connkeys = nl80211_parse_connkeys(rdev, info, NULL);
|
||||
if (IS_ERR(connkeys))
|
||||
|
|
|
@ -3806,8 +3806,9 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy)
|
|||
}
|
||||
|
||||
/*
|
||||
* See http://www.fcc.gov/document/5-ghz-unlicensed-spectrum-unii, for
|
||||
* UNII band definitions
|
||||
* See FCC notices for UNII band definitions
|
||||
* 5GHz: https://www.fcc.gov/document/5-ghz-unlicensed-spectrum-unii
|
||||
* 6GHz: https://www.fcc.gov/document/fcc-proposes-more-spectrum-unlicensed-use-0
|
||||
*/
|
||||
int cfg80211_get_unii(int freq)
|
||||
{
|
||||
|
@ -3831,6 +3832,22 @@ int cfg80211_get_unii(int freq)
|
|||
if (freq > 5725 && freq <= 5825)
|
||||
return 4;
|
||||
|
||||
/* UNII-5 */
|
||||
if (freq > 5925 && freq <= 6425)
|
||||
return 5;
|
||||
|
||||
/* UNII-6 */
|
||||
if (freq > 6425 && freq <= 6525)
|
||||
return 6;
|
||||
|
||||
/* UNII-7 */
|
||||
if (freq > 6525 && freq <= 6875)
|
||||
return 7;
|
||||
|
||||
/* UNII-8 */
|
||||
if (freq > 6875 && freq <= 7125)
|
||||
return 8;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -2446,10 +2446,11 @@ TRACE_EVENT(rdev_set_mcast_rate,
|
|||
sizeof(int) * NUM_NL80211_BANDS);
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", "
|
||||
"mcast_rates [2.4GHz=0x%x, 5.2GHz=0x%x, 60GHz=0x%x]",
|
||||
"mcast_rates [2.4GHz=0x%x, 5.2GHz=0x%x, 6GHz=0x%x, 60GHz=0x%x]",
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG,
|
||||
__entry->mcast_rate[NL80211_BAND_2GHZ],
|
||||
__entry->mcast_rate[NL80211_BAND_5GHZ],
|
||||
__entry->mcast_rate[NL80211_BAND_6GHZ],
|
||||
__entry->mcast_rate[NL80211_BAND_60GHZ])
|
||||
);
|
||||
|
||||
|
|
|
@ -91,6 +91,11 @@ int ieee80211_channel_to_frequency(int chan, enum nl80211_band band)
|
|||
else
|
||||
return 5000 + chan * 5;
|
||||
break;
|
||||
case NL80211_BAND_6GHZ:
|
||||
/* see 802.11ax D4.1 27.3.22.2 */
|
||||
if (chan <= 253)
|
||||
return 5940 + chan * 5;
|
||||
break;
|
||||
case NL80211_BAND_60GHZ:
|
||||
if (chan < 7)
|
||||
return 56160 + chan * 2160;
|
||||
|
@ -111,8 +116,11 @@ int ieee80211_frequency_to_channel(int freq)
|
|||
return (freq - 2407) / 5;
|
||||
else if (freq >= 4910 && freq <= 4980)
|
||||
return (freq - 4000) / 5;
|
||||
else if (freq <= 45000) /* DMG band lower limit */
|
||||
else if (freq < 5940)
|
||||
return (freq - 5000) / 5;
|
||||
else if (freq <= 45000) /* DMG band lower limit */
|
||||
/* see 802.11ax D4.1 27.3.22.2 */
|
||||
return (freq - 5940) / 5;
|
||||
else if (freq >= 58320 && freq <= 70200)
|
||||
return (freq - 56160) / 2160;
|
||||
else
|
||||
|
@ -148,6 +156,7 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband)
|
|||
|
||||
switch (sband->band) {
|
||||
case NL80211_BAND_5GHZ:
|
||||
case NL80211_BAND_6GHZ:
|
||||
want = 3;
|
||||
for (i = 0; i < sband->n_bitrates; i++) {
|
||||
if (sband->bitrates[i].bitrate == 60 ||
|
||||
|
@ -1034,7 +1043,7 @@ static u32 cfg80211_calculate_bitrate_ht(struct rate_info *rate)
|
|||
return (bitrate + 50000) / 100000;
|
||||
}
|
||||
|
||||
static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate)
|
||||
static u32 cfg80211_calculate_bitrate_dmg(struct rate_info *rate)
|
||||
{
|
||||
static const u32 __mcs2bitrate[] = {
|
||||
/* control PHY */
|
||||
|
@ -1081,6 +1090,40 @@ static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate)
|
|||
return __mcs2bitrate[rate->mcs];
|
||||
}
|
||||
|
||||
static u32 cfg80211_calculate_bitrate_edmg(struct rate_info *rate)
|
||||
{
|
||||
static const u32 __mcs2bitrate[] = {
|
||||
/* control PHY */
|
||||
[0] = 275,
|
||||
/* SC PHY */
|
||||
[1] = 3850,
|
||||
[2] = 7700,
|
||||
[3] = 9625,
|
||||
[4] = 11550,
|
||||
[5] = 12512, /* 1251.25 mbps */
|
||||
[6] = 13475,
|
||||
[7] = 15400,
|
||||
[8] = 19250,
|
||||
[9] = 23100,
|
||||
[10] = 25025,
|
||||
[11] = 26950,
|
||||
[12] = 30800,
|
||||
[13] = 38500,
|
||||
[14] = 46200,
|
||||
[15] = 50050,
|
||||
[16] = 53900,
|
||||
[17] = 57750,
|
||||
[18] = 69300,
|
||||
[19] = 75075,
|
||||
[20] = 80850,
|
||||
};
|
||||
|
||||
if (WARN_ON_ONCE(rate->mcs >= ARRAY_SIZE(__mcs2bitrate)))
|
||||
return 0;
|
||||
|
||||
return __mcs2bitrate[rate->mcs] * rate->n_bonded_ch;
|
||||
}
|
||||
|
||||
static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
|
||||
{
|
||||
static const u32 base[4][10] = {
|
||||
|
@ -1253,8 +1296,10 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
|
|||
{
|
||||
if (rate->flags & RATE_INFO_FLAGS_MCS)
|
||||
return cfg80211_calculate_bitrate_ht(rate);
|
||||
if (rate->flags & RATE_INFO_FLAGS_60G)
|
||||
return cfg80211_calculate_bitrate_60g(rate);
|
||||
if (rate->flags & RATE_INFO_FLAGS_DMG)
|
||||
return cfg80211_calculate_bitrate_dmg(rate);
|
||||
if (rate->flags & RATE_INFO_FLAGS_EDMG)
|
||||
return cfg80211_calculate_bitrate_edmg(rate);
|
||||
if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
|
||||
return cfg80211_calculate_bitrate_vht(rate);
|
||||
if (rate->flags & RATE_INFO_FLAGS_HE_MCS)
|
||||
|
@ -1466,6 +1511,9 @@ bool ieee80211_operating_class_to_band(u8 operating_class,
|
|||
case 128 ... 130:
|
||||
*band = NL80211_BAND_5GHZ;
|
||||
return true;
|
||||
case 131 ... 135:
|
||||
*band = NL80211_BAND_6GHZ;
|
||||
return true;
|
||||
case 81:
|
||||
case 82:
|
||||
case 83:
|
||||
|
|
Loading…
Reference in New Issue