diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 4c0d0abac..7c2e87e93 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -26,6 +26,7 @@ #include "wps_hostapd.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "hw_features.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, @@ -273,6 +274,31 @@ 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 channel; + + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "driver had channel switch: " + "freq=%d, ht=%d, offset=%d", freq, ht, offset); + + hapd->iface->freq = freq; + + channel = hostapd_hw_get_channel(hapd, freq); + if (!channel) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, "driver switched to " + "bad channel!"); + return; + } + + hapd->iconf->channel = channel; + hapd->iconf->ieee80211n = ht; + hapd->iconf->secondary_channel = offset; +} + + 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) @@ -590,6 +616,13 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, hostapd_rx_action(hapd, &data->rx_action); break; #endif /* NEED_AP_MLME */ + case EVENT_CH_SWITCH: + if (!data) + break; + hostapd_event_ch_switch(hapd, data->ch_switch.freq, + data->ch_switch.ht_enabled, + data->ch_switch.ch_offset); + break; default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 4e45d59e8..f7ed31106 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -283,5 +283,7 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr); 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); #endif /* HOSTAPD_H */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 102ea46d5..9ed52eabd 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2978,7 +2978,14 @@ enum wpa_event_type { /** * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status */ - EVENT_EAPOL_TX_STATUS + EVENT_EAPOL_TX_STATUS, + + /** + * EVENT_CH_SWITCH - AP or GO decided to switch channels + * + * Described in wpa_event_data.ch_switch + * */ + EVENT_CH_SWITCH }; @@ -3573,6 +3580,18 @@ union wpa_event_data { int data_len; int ack; } eapol_tx_status; + + /** + * struct ch_switch + * @freq: Frequency of new channel in MHz + * @ht_enabled: Whether this is an HT channel + * @ch_offset: Secondary channel offset + */ + struct ch_switch { + int freq; + int ht_enabled; + int ch_offset; + } ch_switch; }; /** diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index 345e851d1..81856aa94 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -77,6 +77,7 @@ const char * event_to_string(enum wpa_event_type event) E2S(SCHED_SCAN_STOPPED); E2S(DRIVER_CLIENT_POLL_OK); E2S(EAPOL_TX_STATUS); + E2S(CH_SWITCH); } return "UNKNOWN"; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index b403792d4..376f36382 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -1196,6 +1196,40 @@ 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) +{ + union wpa_event_data data; + int ht_enabled = 1; + int chan_offset = 0; + + wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); + + if (!freq || !type) + 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; + } + + data.ch_switch.freq = nla_get_u32(freq); + data.ch_switch.ht_enabled = ht_enabled; + data.ch_switch.ch_offset = chan_offset; + + wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data); +} + + static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *addr) { @@ -2116,6 +2150,10 @@ static void do_process_drv_event(struct wpa_driver_nl80211_data *drv, tb[NL80211_ATTR_REQ_IE], 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]); + break; case NL80211_CMD_DISCONNECT: mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], tb[NL80211_ATTR_MAC], diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 7e6441e8a..f9e004570 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -957,6 +957,17 @@ int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s) } +void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, + int offset) +{ + if (!wpa_s->ap_iface) + return; + + wpa_s->assoc_freq = freq; + hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset); +} + + int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, const u8 *addr) { diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h index 91ab423ac..bc953d9bc 100644 --- a/wpa_supplicant/ap.h +++ b/wpa_supplicant/ap.h @@ -49,5 +49,7 @@ int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s); int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, const u8 *addr); void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s); +void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, + int offset); #endif /* AP_H */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index e7dfa4e3a..19c8280eb 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2407,6 +2407,21 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr, data->rx_from_unknown.wds); break; + case EVENT_CH_SWITCH: + if (!data) + break; + if (!wpa_s->ap_iface) { + wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch " + "event in non-AP mode"); + break; + } + +#ifdef CONFIG_AP + wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, + data->ch_switch.ht_enabled, + data->ch_switch.ch_offset); +#endif /* CONFIG_AP */ + break; case EVENT_RX_MGMT: { u16 fc, stype; const struct ieee80211_mgmt *mgmt;