nl80211/hostapd: Extend channel switch notify handling
Adds support for VHT by parsing bandwidth and center_freq{1,2}. Signed-hostap: Michal Kazior <michal.kazior@tieto.com> Signed-hostap: Janusz Dziedzic <janusz.dziedzic@tieto.com>
This commit is contained in:
parent
10e694a618
commit
8d1fdde7f0
7 changed files with 127 additions and 51 deletions
|
@ -381,14 +381,15 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
|
|||
|
||||
|
||||
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
|
||||
int offset)
|
||||
int offset, int width, int cf1, int cf2)
|
||||
{
|
||||
#ifdef NEED_AP_MLME
|
||||
int channel;
|
||||
int channel, chwidth, seg0_idx = 0, seg1_idx = 0;
|
||||
|
||||
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_INFO, "driver had channel switch: "
|
||||
"freq=%d, ht=%d, offset=%d", freq, ht, offset);
|
||||
"freq=%d, ht=%d, offset=%d, width=%d, cf1=%d, cf2=%d",
|
||||
freq, ht, offset, width, cf1, cf2);
|
||||
|
||||
hapd->iface->freq = freq;
|
||||
|
||||
|
@ -400,9 +401,43 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
|
|||
return;
|
||||
}
|
||||
|
||||
switch (width) {
|
||||
case CHAN_WIDTH_80:
|
||||
chwidth = VHT_CHANWIDTH_80MHZ;
|
||||
break;
|
||||
case CHAN_WIDTH_80P80:
|
||||
chwidth = VHT_CHANWIDTH_80P80MHZ;
|
||||
break;
|
||||
case CHAN_WIDTH_160:
|
||||
chwidth = VHT_CHANWIDTH_160MHZ;
|
||||
break;
|
||||
case CHAN_WIDTH_20_NOHT:
|
||||
case CHAN_WIDTH_20:
|
||||
case CHAN_WIDTH_40:
|
||||
default:
|
||||
chwidth = VHT_CHANWIDTH_USE_HT;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (hapd->iface->current_mode->mode) {
|
||||
case HOSTAPD_MODE_IEEE80211A:
|
||||
if (cf1 > 5000)
|
||||
seg0_idx = (cf1 - 5000) / 5;
|
||||
if (cf2 > 5000)
|
||||
seg1_idx = (cf2 - 5000) / 5;
|
||||
break;
|
||||
default:
|
||||
seg0_idx = hostapd_hw_get_channel(hapd, cf1);
|
||||
seg1_idx = hostapd_hw_get_channel(hapd, cf2);
|
||||
break;
|
||||
}
|
||||
|
||||
hapd->iconf->channel = channel;
|
||||
hapd->iconf->ieee80211n = ht;
|
||||
hapd->iconf->secondary_channel = offset;
|
||||
hapd->iconf->vht_oper_chwidth = chwidth;
|
||||
hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
|
||||
hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
|
||||
|
||||
if (hapd->iface->csa_in_progress && freq == hapd->iface->cs_freq) {
|
||||
hostapd_cleanup_cs_params(hapd);
|
||||
|
@ -976,7 +1011,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
|
|||
break;
|
||||
hostapd_event_ch_switch(hapd, data->ch_switch.freq,
|
||||
data->ch_switch.ht_enabled,
|
||||
data->ch_switch.ch_offset);
|
||||
data->ch_switch.ch_offset,
|
||||
data->ch_switch.ch_width,
|
||||
data->ch_switch.cf1,
|
||||
data->ch_switch.cf2);
|
||||
break;
|
||||
case EVENT_CONNECT_FAILED_REASON:
|
||||
if (!data)
|
||||
|
|
|
@ -414,7 +414,7 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
|
|||
const u8 *bssid, const u8 *ie, size_t ie_len,
|
||||
int ssi_signal);
|
||||
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
|
||||
int offset);
|
||||
int offset, int width, int cf1, int cf2);
|
||||
|
||||
const struct hostapd_eap_user *
|
||||
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
|
||||
|
|
|
@ -4028,11 +4028,17 @@ union wpa_event_data {
|
|||
* @freq: Frequency of new channel in MHz
|
||||
* @ht_enabled: Whether this is an HT channel
|
||||
* @ch_offset: Secondary channel offset
|
||||
* @ch_width: Channel width
|
||||
* @cf1: Center frequency 1
|
||||
* @cf2: Center frequency 2
|
||||
*/
|
||||
struct ch_switch {
|
||||
int freq;
|
||||
int ht_enabled;
|
||||
int ch_offset;
|
||||
enum chan_width ch_width;
|
||||
int cf1;
|
||||
int cf2;
|
||||
} ch_switch;
|
||||
|
||||
/**
|
||||
|
|
|
@ -504,6 +504,27 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd)
|
|||
}
|
||||
|
||||
|
||||
/* Converts nl80211_chan_width to a common format */
|
||||
static enum chan_width convert2width(int width)
|
||||
{
|
||||
switch (width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
return CHAN_WIDTH_20_NOHT;
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
return CHAN_WIDTH_20;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
return CHAN_WIDTH_40;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
return CHAN_WIDTH_80;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
return CHAN_WIDTH_80P80;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
return CHAN_WIDTH_160;
|
||||
}
|
||||
return CHAN_WIDTH_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
static int is_ap_interface(enum nl80211_iftype nlmode)
|
||||
{
|
||||
return (nlmode == NL80211_IFTYPE_AP ||
|
||||
|
@ -1484,36 +1505,60 @@ static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
|
|||
|
||||
|
||||
static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
|
||||
struct nlattr *freq, struct nlattr *type)
|
||||
struct nlattr *ifindex, struct nlattr *freq,
|
||||
struct nlattr *type, struct nlattr *bw,
|
||||
struct nlattr *cf1, struct nlattr *cf2)
|
||||
{
|
||||
struct i802_bss *bss;
|
||||
union wpa_event_data data;
|
||||
int ht_enabled = 1;
|
||||
int chan_offset = 0;
|
||||
int ifidx;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
|
||||
|
||||
if (!freq || !type)
|
||||
if (!freq)
|
||||
return;
|
||||
|
||||
switch (nla_get_u32(type)) {
|
||||
case NL80211_CHAN_NO_HT:
|
||||
ht_enabled = 0;
|
||||
break;
|
||||
case NL80211_CHAN_HT20:
|
||||
break;
|
||||
case NL80211_CHAN_HT40PLUS:
|
||||
chan_offset = 1;
|
||||
break;
|
||||
case NL80211_CHAN_HT40MINUS:
|
||||
chan_offset = -1;
|
||||
break;
|
||||
ifidx = nla_get_u32(ifindex);
|
||||
for (bss = drv->first_bss; bss; bss = bss->next)
|
||||
if (bss->ifindex == ifidx)
|
||||
break;
|
||||
|
||||
if (bss == NULL) {
|
||||
wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring",
|
||||
ifidx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type) {
|
||||
switch (nla_get_u32(type)) {
|
||||
case NL80211_CHAN_NO_HT:
|
||||
ht_enabled = 0;
|
||||
break;
|
||||
case NL80211_CHAN_HT20:
|
||||
break;
|
||||
case NL80211_CHAN_HT40PLUS:
|
||||
chan_offset = 1;
|
||||
break;
|
||||
case NL80211_CHAN_HT40MINUS:
|
||||
chan_offset = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
os_memset(&data, 0, sizeof(data));
|
||||
data.ch_switch.freq = nla_get_u32(freq);
|
||||
data.ch_switch.ht_enabled = ht_enabled;
|
||||
data.ch_switch.ch_offset = chan_offset;
|
||||
if (bw)
|
||||
data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
|
||||
if (cf1)
|
||||
data.ch_switch.cf1 = nla_get_u32(cf1);
|
||||
if (cf2)
|
||||
data.ch_switch.cf2 = nla_get_u32(cf2);
|
||||
|
||||
drv->first_bss->freq = data.ch_switch.freq;
|
||||
bss->freq = data.ch_switch.freq;
|
||||
|
||||
wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data);
|
||||
}
|
||||
|
@ -2534,8 +2579,6 @@ static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
|
|||
}
|
||||
|
||||
|
||||
static enum chan_width convert2width(int width);
|
||||
|
||||
static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
|
||||
struct nlattr **tb)
|
||||
{
|
||||
|
@ -2702,8 +2745,13 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
|
|||
tb[NL80211_ATTR_RESP_IE]);
|
||||
break;
|
||||
case NL80211_CMD_CH_SWITCH_NOTIFY:
|
||||
mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ],
|
||||
tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
|
||||
mlme_event_ch_switch(drv,
|
||||
tb[NL80211_ATTR_IFINDEX],
|
||||
tb[NL80211_ATTR_WIPHY_FREQ],
|
||||
tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
|
||||
tb[NL80211_ATTR_CHANNEL_WIDTH],
|
||||
tb[NL80211_ATTR_CENTER_FREQ1],
|
||||
tb[NL80211_ATTR_CENTER_FREQ2]);
|
||||
break;
|
||||
case NL80211_CMD_DISCONNECT:
|
||||
mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
|
||||
|
@ -10020,27 +10068,6 @@ nla_put_failure:
|
|||
}
|
||||
|
||||
|
||||
/* Converts nl80211_chan_width to a common format */
|
||||
static enum chan_width convert2width(int width)
|
||||
{
|
||||
switch (width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
return CHAN_WIDTH_20_NOHT;
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
return CHAN_WIDTH_20;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
return CHAN_WIDTH_40;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
return CHAN_WIDTH_80;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
return CHAN_WIDTH_80P80;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
return CHAN_WIDTH_160;
|
||||
}
|
||||
return CHAN_WIDTH_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
static int get_channel_width(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
struct nlattr *tb[NL80211_ATTR_MAX + 1];
|
||||
|
@ -11288,9 +11315,11 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
|
|||
struct nlattr *beacon_csa;
|
||||
int ret = -ENOBUFS;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d)",
|
||||
wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)",
|
||||
settings->cs_count, settings->block_tx,
|
||||
settings->freq_params.freq);
|
||||
settings->freq_params.freq, settings->freq_params.bandwidth,
|
||||
settings->freq_params.center_freq1,
|
||||
settings->freq_params.center_freq2);
|
||||
|
||||
if (!drv->channel_switch_supported) {
|
||||
wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command");
|
||||
|
|
|
@ -1085,13 +1085,13 @@ int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
|
|||
|
||||
|
||||
void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
|
||||
int offset)
|
||||
int offset, int width, int cf1, int cf2)
|
||||
{
|
||||
if (!wpa_s->ap_iface)
|
||||
return;
|
||||
|
||||
wpa_s->assoc_freq = freq;
|
||||
hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset);
|
||||
hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s,
|
|||
struct csa_settings *settings);
|
||||
int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *txtaddr);
|
||||
void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
|
||||
int offset);
|
||||
int offset, int width, int cf1, int cf2);
|
||||
struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
|
||||
int ndef);
|
||||
#ifdef CONFIG_AP
|
||||
|
|
|
@ -2891,7 +2891,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
|
|||
|
||||
wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
|
||||
data->ch_switch.ht_enabled,
|
||||
data->ch_switch.ch_offset);
|
||||
data->ch_switch.ch_offset,
|
||||
data->ch_switch.ch_width,
|
||||
data->ch_switch.cf1,
|
||||
data->ch_switch.cf2);
|
||||
break;
|
||||
#endif /* CONFIG_AP */
|
||||
#if defined(CONFIG_AP) || defined(CONFIG_IBSS_RSN)
|
||||
|
|
Loading…
Reference in a new issue