]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
nl80211/hostapd: Extend channel switch notify handling
authorJanusz Dziedzic <janusz.dziedzic@tieto.com>
Mon, 25 Nov 2013 19:16:12 +0000 (20:16 +0100)
committerJouni Malinen <j@w1.fi>
Sun, 8 Dec 2013 04:50:15 +0000 (20:50 -0800)
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>

src/ap/drv_callbacks.c
src/ap/hostapd.h
src/drivers/driver.h
src/drivers/driver_nl80211.c
wpa_supplicant/ap.c
wpa_supplicant/ap.h
wpa_supplicant/events.c

index 1b69ba826fd0fa0d7650ed39fa6581d26d694ef2..b066bffdbe8df79d2c0c02c378af1e104a5c7608 100644 (file)
@@ -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)
index c7ec7dc3c7d3010334a7e407e826f05140ea8c1b..84468de002375d50824c607f14545acf48cb01ba 100644 (file)
@@ -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,
index 3502eb8059ab9ef58830a6deb1ab40fec00b85fc..a3602ed7820a51fb01fd0c4a44495d8fe9a27db5 100644 (file)
@@ -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;
 
        /**
index 64ab29a6356e7aacc6842fe1f1d701af784dfda2..42dddf04ae0f6d3a8af84ddc9965174b03d7bb9b 100644 (file)
@@ -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");
index cbe67a4a855c55f936ac03d04e0d116c1d84672f..394ab30d055b163e29501cc1ef0c12c6d075925d 100644 (file)
@@ -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);
 }
 
 
index 33a3d0f75e7c097fd86d54ce5c7ac01da8e64af2..c3828980d7c1ed07d752c9214f32294c2e2835cc 100644 (file)
@@ -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
index aacf5b9607094802ad52a804b6d917e996daaf79..517d812f04690b8c6ca7ceb94697e794e174fcd5 100644 (file)
@@ -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)