diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index f573717d1..52b5dbc69 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -551,6 +551,9 @@ struct hostapd_iface { #ifdef CONFIG_AIRTIME_POLICY unsigned int airtime_quantum; #endif /* CONFIG_AIRTIME_POLICY */ + + /* Previous WMM element information */ + struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM]; }; /* hostapd.c */ diff --git a/src/ap/wmm.c b/src/ap/wmm.c index 8054c5d2f..dc7349337 100644 --- a/src/ap/wmm.c +++ b/src/ap/wmm.c @@ -20,6 +20,13 @@ #include "ap_drv_ops.h" #include "wmm.h" +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci) { @@ -39,6 +46,62 @@ static inline u8 wmm_ecw(int ecwmin, int ecwmax) } +static void +wmm_set_regulatory_limit(const struct hostapd_wmm_ac_params *wmm_conf, + struct hostapd_wmm_ac_params *wmm, + const struct hostapd_wmm_rule *wmm_reg) +{ + int ac; + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + wmm[ac].cwmin = MAX(wmm_conf[ac].cwmin, wmm_reg[ac].min_cwmin); + wmm[ac].cwmax = MAX(wmm_conf[ac].cwmax, wmm_reg[ac].min_cwmax); + wmm[ac].aifs = MAX(wmm_conf[ac].aifs, wmm_reg[ac].min_aifs); + wmm[ac].txop_limit = + MIN(wmm_conf[ac].txop_limit, wmm_reg[ac].max_txop); + wmm[ac].admission_control_mandatory = + wmm_conf[ac].admission_control_mandatory; + } +} + + +/* + * Calculate WMM regulatory limit if any. + */ +static void wmm_calc_regulatory_limit(struct hostapd_data *hapd, + struct hostapd_wmm_ac_params *acp) +{ + struct hostapd_hw_modes *mode = hapd->iface->current_mode; + int c; + + os_memcpy(acp, hapd->iconf->wmm_ac_params, + sizeof(hapd->iconf->wmm_ac_params)); + + for (c = 0; mode && c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + + if (chan->freq != hapd->iface->freq) + continue; + + if (chan->wmm_rules_valid) + wmm_set_regulatory_limit(hapd->iconf->wmm_ac_params, + acp, chan->wmm_rules); + break; + } + + /* + * Check if we need to update set count. Since both were initialized to + * zero we can compare the whole array in one shot. + */ + if (os_memcmp(acp, hapd->iface->prev_wmm, + sizeof(hapd->iconf->wmm_ac_params)) != 0) { + os_memcpy(hapd->iface->prev_wmm, acp, + sizeof(hapd->iconf->wmm_ac_params)); + hapd->parameter_set_count++; + } +} + + /* * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association * Response frames. @@ -48,10 +111,12 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) u8 *pos = eid; struct wmm_parameter_element *wmm = (struct wmm_parameter_element *) (pos + 2); + struct hostapd_wmm_ac_params wmmp[WMM_AC_NUM] = { 0 }; int e; if (!hapd->conf->wmm_enabled) return eid; + wmm_calc_regulatory_limit(hapd, wmmp); eid[0] = WLAN_EID_VENDOR_SPECIFIC; wmm->oui[0] = 0x00; wmm->oui[1] = 0x50; @@ -70,8 +135,7 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) /* fill in a parameter set record for each AC */ for (e = 0; e < 4; e++) { struct wmm_ac_parameter *ac = &wmm->ac[e]; - struct hostapd_wmm_ac_params *acp = - &hapd->iconf->wmm_ac_params[e]; + struct hostapd_wmm_ac_params *acp = &wmmp[e]; ac->aci_aifsn = wmm_aci_aifsn(acp->aifs, acp->admission_control_mandatory,