Clean up HT40 scan and share nl80211 scanning code
authorJouni Malinen <jouni.malinen@atheros.com>
Thu, 16 Apr 2009 13:22:40 +0000 (16:22 +0300)
committerJouni Malinen <j@w1.fi>
Thu, 16 Apr 2009 13:22:40 +0000 (16:22 +0300)
Instead of adding a new driver_ops for fetching neighbor BSS data (that
nl80211 driver interface had to scan during initialization), share the
same scan operations that wpa_supplicant is using. This gets rid of
duplicated scan code in driver_nl80211.c (and better yet, removes large
part of old WEXT code).

hostapd interface initialization is now completed in a callback, if
needed, i.e., he_features channel/hw_mode selection can use as much time
as needed. This can also help with radar detection in the future.

hostapd/driver_i.h
hostapd/drv_callbacks.c
hostapd/hostapd.c
hostapd/hostapd.h
hostapd/hw_features.c
hostapd/hw_features.h
src/drivers/driver.h
src/drivers/driver_ndis.c
src/drivers/driver_nl80211.c
src/drivers/driver_privsep.c
src/drivers/driver_test.c

index 921d1f1..1b6cd5e 100644 (file)
@@ -36,7 +36,6 @@ hostapd_driver_init(struct hostapd_data *hapd, const u8 *bssid)
        params.ssid_len = hapd->conf->ssid.ssid_len;
        params.test_socket = hapd->conf->test_socket;
        params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
-       params.ht_40mhz_scan = hapd->iconf->secondary_channel != 0;
 
        params.num_bridge = hapd->iface->num_bss;
        params.bridge = os_zalloc(hapd->iface->num_bss * sizeof(char *));
@@ -553,12 +552,27 @@ hostapd_set_wps_probe_resp_ie(struct hostapd_data *hapd, const u8 *ie,
                                                   hapd->drv_priv, ie, len);
 }
 
-static inline const struct hostapd_neighbor_bss *
-hostapd_driver_get_neighbor_bss(struct hostapd_data *hapd, size_t *num)
+static inline int hostapd_driver_set_mode(struct hostapd_data *hapd, int mode)
 {
-       if (hapd->driver == NULL || hapd->driver->get_neighbor_bss == NULL)
-               return NULL;
-       return hapd->driver->get_neighbor_bss(hapd->drv_priv, num);
+       if (hapd->driver == NULL || hapd->driver->set_mode == NULL)
+               return 0;
+       return hapd->driver->set_mode(hapd->drv_priv, mode);
+}
+
+static inline int hostapd_driver_scan(struct hostapd_data *hapd,
+                                     struct wpa_driver_scan_params *params)
+{
+       if (hapd->driver && hapd->driver->scan2)
+               return hapd->driver->scan2(hapd->drv_priv, params);
+       return -1;
+}
+
+static inline struct wpa_scan_results * hostapd_driver_get_scan_results(
+       struct hostapd_data *hapd)
+{
+       if (hapd->driver && hapd->driver->get_scan_results2)
+               return hapd->driver->get_scan_results2(hapd->drv_priv);
+       return NULL;
 }
 
 #endif /* DRIVER_I_H */
index 22058cc..1b884c7 100644 (file)
@@ -294,3 +294,25 @@ struct hostapd_data * hostapd_sta_get_bss(struct hostapd_data *hapd,
 
        return NULL;
 }
+
+
+#ifndef CONFIG_AP
+void wpa_supplicant_event(void *ctx, wpa_event_type event,
+                         union wpa_event_data *data)
+{
+       struct hostapd_data *hapd = ctx;
+
+       switch (event) {
+       case EVENT_MICHAEL_MIC_FAILURE:
+               michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
+               break;
+       case EVENT_SCAN_RESULTS:
+               if (hapd->iface->scan_cb)
+                       hapd->iface->scan_cb(hapd->iface);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "Unknown event %d", event);
+               break;
+       }
+}
+#endif /* CONFIG_AP */
index 379e59c..0d7eb65 100644 (file)
@@ -1314,9 +1314,6 @@ static int setup_interface(struct hostapd_iface *iface)
        size_t i;
        char country[4];
        u8 *b = conf->bssid;
-       int freq;
-       size_t j;
-       u8 *prev_addr;
 
        /*
         * Initialize the driver interface and make sure that all BSSes get
@@ -1337,6 +1334,11 @@ static int setup_interface(struct hostapd_iface *iface)
                iface->bss[i]->drv_priv = hapd->drv_priv;
        }
 
+       if (hostapd_driver_set_mode(hapd, IEEE80211_MODE_AP)) {
+               wpa_printf(MSG_ERROR, "Failed to set driver in AP mode");
+               return -1;
+       }
+
        if (hostapd_validate_bssid_configuration(iface))
                return -1;
 
@@ -1379,8 +1381,33 @@ static int setup_interface(struct hostapd_iface *iface)
                                   "channel. (%d)", ret);
                        return -1;
                }
+               ret = hostapd_check_ht_capab(iface);
+               if (ret < 0)
+                       return -1;
+               if (ret == 1) {
+                       wpa_printf(MSG_DEBUG, "Interface initialization will "
+                                  "be completed in a callback");
+                       return 0;
+               }
+       }
+       return hostapd_setup_interface_complete(iface, 0);
+}
+
+
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+{
+       struct hostapd_data *hapd = iface->bss[0];
+       int freq;
+       size_t j;
+       u8 *prev_addr;
+
+       if (err) {
+               wpa_printf(MSG_ERROR, "Interface initialization failed");
+               eloop_terminate();
+               return -1;
        }
 
+       wpa_printf(MSG_DEBUG, "Completing interface initialization");
        if (hapd->iconf->channel) {
                freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel);
                wpa_printf(MSG_DEBUG, "Mode: %s  Channel: %d  "
@@ -1436,6 +1463,9 @@ static int setup_interface(struct hostapd_iface *iface)
                return -1;
        }
 
+       wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+                  iface->bss[0]->conf->iface);
+
        return 0;
 }
 
@@ -1460,9 +1490,6 @@ int hostapd_setup_interface(struct hostapd_iface *iface)
                           iface->bss[0]->conf->iface);
                eloop_terminate();
                return -1;
-       } else if (!hostapd_drv_none(iface->bss[0])) {
-               wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
-                          iface->bss[0]->conf->iface);
        }
 
        return 0;
index 03123f8..d45f461 100644 (file)
@@ -154,9 +154,8 @@ struct hostapd_iface {
        /* Overlapping BSS information */
        int olbc_ht;
 
-#ifdef CONFIG_IEEE80211N
        u16 ht_op_mode;
-#endif /* CONFIG_IEEE80211N */
+       void (*scan_cb)(struct hostapd_iface *iface);
 };
 
 int hostapd_reload_config(struct hostapd_iface *iface);
@@ -165,6 +164,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
                       struct hostapd_config *conf,
                       struct hostapd_bss_config *bss);
 int hostapd_setup_interface(struct hostapd_iface *iface);
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
 void hostapd_interface_deinit(struct hostapd_iface *iface);
 int handle_reload_iface(struct hostapd_iface *iface, void *ctx);
 int handle_dump_state_iface(struct hostapd_iface *iface, void *ctx);
index 79d785f..c29cd19 100644 (file)
@@ -18,6 +18,8 @@
 
 #include "hostapd.h"
 #include "ieee802_11_defs.h"
+#include "ieee802_11_common.h"
+#include "eloop.h"
 #include "hw_features.h"
 #include "driver_i.h"
 #include "config.h"
@@ -249,11 +251,37 @@ static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
 }
 
 
-static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface)
+static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
+                                       int *pri_chan, int *sec_chan)
+{
+       struct ieee80211_ht_operation *oper;
+       struct ieee802_11_elems elems;
+
+       *pri_chan = *sec_chan = 0;
+
+       ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+       if (elems.ht_operation &&
+           elems.ht_operation_len >= sizeof(*oper)) {
+               oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+               *pri_chan = oper->control_chan;
+               if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
+                       if (oper->ht_param &
+                           HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+                               *sec_chan = *pri_chan + 4;
+                       else if (oper->ht_param &
+                                HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+                               *sec_chan = *pri_chan - 4;
+               }
+       }
+}
+
+
+static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
+                                    struct wpa_scan_results *scan_res)
 {
        int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss;
-       const struct hostapd_neighbor_bss *n;
-       size_t i, num;
+       int bss_pri_chan, bss_sec_chan;
+       size_t i;
        int match;
 
        pri_chan = iface->conf->channel;
@@ -264,17 +292,16 @@ static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface)
        else
                sec_freq = pri_freq - 20;
 
-       n = hostapd_driver_get_neighbor_bss(iface->bss[0], &num);
-
        /*
         * Switch PRI/SEC channels if Beacons were detected on selected SEC
         * channel, but not on selected PRI channel.
         */
        pri_bss = sec_bss = 0;
-       for (i = 0; n && i < num; i++) {
-               if (n[i].freq == pri_freq)
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *bss = scan_res->res[i];
+               if (bss->freq == pri_freq)
                        pri_bss++;
-               else if (n[i].freq == sec_freq)
+               else if (bss->freq == sec_freq)
                        sec_bss++;
        }
        if (sec_bss && !pri_bss) {
@@ -290,21 +317,25 @@ static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface)
         * existing BSSes, use own preference).
         */
        match = 0;
-       for (i = 0; n && i < num; i++) {
-               if (pri_chan == n[i].pri_chan &&
-                   sec_chan == n[i].sec_chan) {
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *bss = scan_res->res[i];
+               ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+               if (pri_chan == bss_pri_chan &&
+                   sec_chan == bss_sec_chan) {
                        match = 1;
                        break;
                }
        }
        if (!match) {
-               for (i = 0; n && i < num; i++) {
-                       if (pri_chan == n[i].sec_chan &&
-                           sec_chan == n[i].pri_chan) {
+               for (i = 0; i < scan_res->num; i++) {
+                       struct wpa_scan_res *bss = scan_res->res[i];
+                       ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
+                       if (pri_chan == bss_sec_chan &&
+                           sec_chan == bss_pri_chan) {
                                wpa_printf(MSG_INFO, "Switch own primary and "
                                           "secondary channel due to BSS "
                                           "overlap with " MACSTR,
-                                          MAC2STR(n[i].bssid));
+                                          MAC2STR(bss->bssid));
                                ieee80211n_switch_pri_sec(iface);
                                break;
                        }
@@ -315,12 +346,12 @@ static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface)
 }
 
 
-static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface)
+static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
+                                     struct wpa_scan_results *scan_res)
 {
        int pri_freq, sec_freq;
        int affected_start, affected_end;
-       const struct hostapd_neighbor_bss *n;
-       size_t i, num;
+       size_t i;
 
        pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
        if (iface->conf->secondary_channel > 0)
@@ -331,12 +362,16 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface)
        affected_end = (pri_freq + sec_freq) / 2 + 25;
        wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
                   affected_start, affected_end);
-       n = hostapd_driver_get_neighbor_bss(iface->bss[0], &num);
-       for (i = 0; n && i < num; i++) {
-               int pri = n[i].freq;
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *bss = scan_res->res[i];
+               int pri = bss->freq;
                int sec = pri;
-               if (n[i].sec_chan) {
-                       if (n[i].sec_chan < n[i].pri_chan)
+               int sec_chan, pri_chan;
+
+               ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
+
+               if (sec_chan) {
+                       if (sec_chan < pri_chan)
                                sec = pri - 20;
                        else
                                sec = pri + 20;
@@ -346,13 +381,17 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface)
                    (sec < affected_start || sec > affected_end))
                        continue; /* not within affected channel range */
 
-               if (n[i].sec_chan) {
+               wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
+                          " freq=%d pri=%d sec=%d",
+                          MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
+
+               if (sec_chan) {
                        if (pri_freq != pri || sec_freq != sec) {
                                wpa_printf(MSG_DEBUG, "40 MHz pri/sec "
                                           "mismatch with BSS " MACSTR
                                           " <%d,%d> (chan=%d%c) vs. <%d,%d>",
-                                          MAC2STR(n[i].bssid),
-                                          pri, sec, n[i].pri_chan,
+                                          MAC2STR(bss->bssid),
+                                          pri, sec, pri_chan,
                                           sec > pri ? '+' : '-',
                                           pri_freq, sec_freq);
                                return 0;
@@ -366,20 +405,27 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface)
 }
 
 
-static void ieee80211n_check_40mhz(struct hostapd_iface *iface)
+static void ieee80211n_check_scan(struct hostapd_iface *iface)
 {
+       struct wpa_scan_results *scan_res;
        int oper40;
 
-       if (!iface->conf->secondary_channel)
-               return; /* HT40 not used */
-
        /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
         * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */
 
+       iface->scan_cb = NULL;
+
+       scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
+       if (scan_res == NULL) {
+               hostapd_setup_interface_complete(iface, 1);
+               return;
+       }
+
        if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
-               oper40 = ieee80211n_check_40mhz_5g(iface);
+               oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
        else
-               oper40 = ieee80211n_check_40mhz_2g4(iface);
+               oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
+       wpa_scan_results_free(scan_res);
 
        if (!oper40) {
                wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
@@ -390,6 +436,30 @@ static void ieee80211n_check_40mhz(struct hostapd_iface *iface)
                iface->conf->secondary_channel = 0;
                iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
        }
+
+       hostapd_setup_interface_complete(iface, 0);
+}
+
+
+static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
+{
+       struct wpa_driver_scan_params params;
+
+       if (!iface->conf->secondary_channel)
+               return 0; /* HT40 not used */
+
+       wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
+                  "40 MHz channel");
+       os_memset(&params, 0, sizeof(params));
+       /* TODO: scan only the needed frequency */
+       if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
+               wpa_printf(MSG_ERROR, "Failed to request a scan of "
+                          "neighboring BSSes");
+               return -1;
+       }
+
+       iface->scan_cb = ieee80211n_check_scan;
+       return 1;
 }
 
 
@@ -492,8 +562,26 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
 
        return 1;
 }
+
+#endif /* CONFIG_IEEE80211N */
+
+
+int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211N
+       int ret;
+       ret = ieee80211n_check_40mhz(iface);
+       if (ret)
+               return ret;
+       if (!ieee80211n_allowed_ht40_channel_pair(iface))
+               return -1;
+       if (!ieee80211n_supported_ht_capab(iface))
+               return -1;
 #endif /* CONFIG_IEEE80211N */
 
+       return 0;
+}
+
 
 /**
  * hostapd_select_hw_mode - Select the hardware mode
@@ -558,14 +646,6 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
                return -1;
        }
 
-#ifdef CONFIG_IEEE80211N
-       ieee80211n_check_40mhz(iface);
-       if (!ieee80211n_allowed_ht40_channel_pair(iface))
-               return -1;
-       if (!ieee80211n_supported_ht_capab(iface))
-               return -1;
-#endif /* CONFIG_IEEE80211N */
-
        if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) {
                wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
                hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
index 8a3f282..4449cbb 100644 (file)
@@ -24,6 +24,7 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface);
 const char * hostapd_hw_mode_txt(int mode);
 int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
 int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
+int hostapd_check_ht_capab(struct hostapd_iface *iface);
 #else /* NEED_MLME */
 static inline void
 hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -50,6 +51,12 @@ static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
 {
        return -1;
 }
+
+static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+       return 0;
+}
+
 #endif /* NEED_MLME */
 
 #endif /* HW_FEATURES_H */
index 05b8560..6962942 100644 (file)
@@ -61,6 +61,7 @@ struct hostapd_hw_modes {
 
 #define IEEE80211_MODE_INFRA   0
 #define IEEE80211_MODE_IBSS    1
+#define IEEE80211_MODE_AP      2
 
 #define IEEE80211_CAP_ESS      0x0001
 #define IEEE80211_CAP_IBSS     0x0002
@@ -494,14 +495,6 @@ enum hostapd_driver_if_type {
        HOSTAPD_IF_VLAN, HOSTAPD_IF_WDS
 };
 
-struct hostapd_neighbor_bss {
-       u8 bssid[ETH_ALEN];
-       int freq; /* MHz */
-       unsigned int ht:1;
-       int pri_chan;
-       int sec_chan; /* 0 for 20 MHz channels */
-};
-
 struct wpa_init_params {
        const u8 *bssid;
        const char *ifname;
@@ -509,7 +502,6 @@ struct wpa_init_params {
        size_t ssid_len;
        const char *test_socket;
        int use_pae_group_addr;
-       int ht_40mhz_scan;
        char **bridge;
        size_t num_bridge;
 };
@@ -1341,9 +1333,6 @@ struct wpa_driver_ops {
                                 const u8 *ie, size_t len);
        int (*set_wps_probe_resp_ie)(const char *ifname, void *priv,
                                     const u8 *ie, size_t len);
-
-       const struct hostapd_neighbor_bss *
-       (*get_neighbor_bss)(void *priv, size_t *num);
 };
 
 /**
@@ -1589,6 +1578,7 @@ union wpa_event_data {
         */
        struct michael_mic_failure {
                int unicast;
+               const u8 *src;
        } michael_mic_failure;
 
        /**
index 83122d2..7223ca1 100644 (file)
@@ -3266,6 +3266,5 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = {
        NULL /* set_radius_acl_expire */,
        NULL /* set_ht_params */,
        NULL /* set_wps_beacon_ie */,
-       NULL /* set_wps_probe_resp_ie */,
-       NULL /* get_neighbor_bss */
+       NULL /* set_wps_probe_resp_ie */
 };
index 75e9c73..127f38a 100644 (file)
@@ -110,6 +110,8 @@ struct wpa_driver_nl80211_data {
        int associated;
        u8 ssid[32];
        size_t ssid_len;
+       int nlmode;
+       int ap_scan_as_station;
 
 #if defined(CONFIG_AP) || defined(HOSTAPD)
        int beacon_int;
@@ -131,20 +133,16 @@ struct wpa_driver_nl80211_data {
        int num_if_indices;
 
        struct i802_bss bss;
-       unsigned int ht_40mhz_scan:1;
 
        int last_freq;
        int last_freq_ht;
-       struct hostapd_neighbor_bss *neighbors;
-       size_t num_neighbors;
 #endif /* HOSTAPD */
 };
 
 
 static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx,
                                            void *timeout_ctx);
-static int wpa_driver_nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
-                                      int mode);
+static int wpa_driver_nl80211_set_mode(void *priv, int mode);
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
 
@@ -740,11 +738,6 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv,
 static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv,
                                           struct nlattr *tb[])
 {
-#ifdef HOSTAPD
-       if (tb[NL80211_ATTR_MAC])
-               hostapd_michael_mic_failure(drv->hapd,
-                                           nla_data(tb[NL80211_ATTR_MAC]));
-#else /* HOSTAPD */
        union wpa_event_data data;
 
        wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure");
@@ -753,6 +746,7 @@ static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv,
                wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address",
                            nla_data(tb[NL80211_ATTR_MAC]),
                            nla_len(tb[NL80211_ATTR_MAC]));
+               data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]);
        }
        if (tb[NL80211_ATTR_KEY_SEQ]) {
                wpa_hexdump(MSG_DEBUG, "nl80211: TSC",
@@ -774,7 +768,6 @@ static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv,
        }
 
        wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
-#endif /* HOSTAPD */
 }
 
 
@@ -797,8 +790,14 @@ static int process_event(struct nl_msg *msg, void *arg)
                }
        }
 
+       if (drv->ap_scan_as_station &&
+           (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
+            gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) {
+               wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_AP);
+               drv->ap_scan_as_station = 0;
+       }
+
        switch (gnlh->cmd) {
-#ifndef HOSTAPD
        case NL80211_CMD_NEW_SCAN_RESULTS:
                wpa_printf(MSG_DEBUG, "nl80211: New scan results available");
                drv->scan_complete_events = 1;
@@ -816,6 +815,7 @@ static int process_event(struct nl_msg *msg, void *arg)
                                     drv->ctx);
                wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
                break;
+#ifndef HOSTAPD
        case NL80211_CMD_AUTHENTICATE:
        case NL80211_CMD_ASSOCIATE:
        case NL80211_CMD_DEAUTHENTICATE:
@@ -1174,7 +1174,7 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
 {
        drv->ifindex = if_nametoindex(drv->ifname);
 
-       if (wpa_driver_nl80211_set_mode(drv, 0) < 0) {
+       if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA) < 0) {
                wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to "
                           "use managed mode");
        }
@@ -1222,7 +1222,7 @@ static void wpa_driver_nl80211_deinit(void *priv)
        eloop_unregister_read_sock(drv->link_event_sock);
 
        hostapd_set_iface_flags(drv, drv->ifname, 0);
-       wpa_driver_nl80211_set_mode(drv, 0);
+       wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA);
 
        close(drv->link_event_sock);
        close(drv->ioctl_sock);
@@ -1239,7 +1239,7 @@ static void wpa_driver_nl80211_deinit(void *priv)
 
 /**
  * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
- * @eloop_ctx: Unused
+ * @eloop_ctx: Driver private data
  * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
  *
  * This function can be used as registered timeout when starting a scan to
@@ -1247,16 +1247,19 @@ static void wpa_driver_nl80211_deinit(void *priv)
  */
 static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 {
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       if (drv->ap_scan_as_station) {
+               wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_AP);
+               drv->ap_scan_as_station = 0;
+       }
        wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
-#ifndef HOSTAPD
        wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
-#endif /* HOSTAPD */
 }
 
 
 /**
  * wpa_driver_nl80211_scan - Request the driver to initiate scan
- * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
  * @params: Scan parameters
  * Returns: 0 on success, -1 on failure
  */
@@ -1306,7 +1309,30 @@ static int wpa_driver_nl80211_scan(void *priv,
        if (ret) {
                wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
                           "(%s)", ret, strerror(-ret));
+#ifdef HOSTAPD
+               if (drv->nlmode == NL80211_IFTYPE_AP) {
+                       /*
+                        * mac80211 does not allow scan requests in AP mode, so
+                        * try to do this in station mode.
+                        */
+                       if (wpa_driver_nl80211_set_mode(drv,
+                                                       IEEE80211_MODE_INFRA))
+                               goto nla_put_failure;
+
+                       if (wpa_driver_nl80211_scan(drv, params)) {
+                               wpa_driver_nl80211_set_mode(drv,
+                                                           IEEE80211_MODE_AP);
+                               goto nla_put_failure;
+                       }
+
+                       /* Restore AP mode when processing scan results */
+                       drv->ap_scan_as_station = 1;
+                       ret = 0;
+               } else
+                       goto nla_put_failure;
+#else /* HOSTAPD */
                goto nla_put_failure;
+#endif /* HOSTAPD */
        }
 
        /* Not all drivers generate "scan completed" wireless event, so try to
@@ -2869,17 +2895,35 @@ nla_put_failure:
 }
 
 
-/**
- * wpa_driver_nl80211_set_mode - Set wireless mode (infra/adhoc)
- * @drv: Pointer to private driver data from wpa_driver_nl80211_init()
- * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS
- * Returns: 0 on success, -1 on failure
- */
-static int wpa_driver_nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
-                                      int mode)
+static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
+                           int ifindex, int mode)
 {
-       int ret = -1;
        struct nl_msg *msg;
+       int ret = -ENOBUFS;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_INTERFACE, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (!ret)
+               return 0;
+nla_put_failure:
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
+                  " %d (%s)", ifindex, mode, ret, strerror(-ret));
+       return ret;
+}
+
+
+static int wpa_driver_nl80211_set_mode(void *priv, int mode)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+       int ret = -1;
        int nlmode;
 
        switch (mode) {
@@ -2896,52 +2940,28 @@ static int wpa_driver_nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
                return -1;
        }
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
-
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_SET_INTERFACE, 0);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, nlmode);
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       if (!ret)
+       if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) {
+               drv->nlmode = nlmode;
                return 0;
-       else
-               goto try_again;
+       }
 
-nla_put_failure:
-       wpa_printf(MSG_ERROR, "nl80211: Failed to set interface mode: %d (%s)",
-                  ret, strerror(-ret));
-       return -1;
+       if (nlmode == drv->nlmode)
+               return 0; /* Already in the requested mode */
 
-try_again:
        /* mac80211 doesn't allow mode changes while the device is up, so
         * take the device down, try to set the mode again, and bring the
         * device back up.
         */
        if (hostapd_set_iface_flags(drv, drv->ifname, 0) == 0) {
                /* Try to set the mode again while the interface is down */
-               msg = nlmsg_alloc();
-               if (!msg)
-                       return -1;
-
-               genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                           0, NL80211_CMD_SET_INTERFACE, 0);
-               NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-               NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, nlmode);
-               ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-               if (ret) {
-                       wpa_printf(MSG_ERROR, "Failed to set interface %s "
-                                  "mode(try_again): %d (%s)",
-                                  drv->ifname, ret, strerror(-ret));
-               }
-
+               ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
                if (hostapd_set_iface_flags(drv, drv->ifname, 1))
                        ret = -1;
        }
 
+       if (!ret)
+               drv->nlmode = nlmode;
+
        return ret;
 }
 
@@ -3755,492 +3775,6 @@ static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
 }
 
 
-static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv, const char *ifname,
-                           int mode)
-{
-       struct nl_msg *msg;
-       int ret = -ENOBUFS;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_SET_INTERFACE, 0);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-                   if_nametoindex(ifname));
-       NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       if (!ret)
-               return 0;
- nla_put_failure:
-       wpa_printf(MSG_ERROR, "Failed to set interface %s to master "
-                  "mode.", ifname);
-       return ret;
-}
-
-
-#ifdef CONFIG_IEEE80211N
-static void i802_add_neighbor(struct wpa_driver_nl80211_data *drv, u8 *bssid,
-                             int freq, u8 *ie, size_t ie_len)
-{
-       struct ieee802_11_elems elems;
-       int ht, pri_chan = 0, sec_chan = 0;
-       struct ieee80211_ht_operation *oper;
-       struct hostapd_neighbor_bss *nnei;
-
-       ieee802_11_parse_elems(ie, ie_len, &elems, 0);
-       ht = elems.ht_capabilities || elems.ht_operation;
-       if (elems.ht_operation && elems.ht_operation_len >= sizeof(*oper)) {
-               oper = (struct ieee80211_ht_operation *) elems.ht_operation;
-               pri_chan = oper->control_chan;
-               if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
-                       if (oper->ht_param &
-                           HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
-                               sec_chan = pri_chan + 4;
-                       else if (oper->ht_param &
-                           HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
-                               sec_chan = pri_chan - 4;
-               }
-       }
-
-       wpa_printf(MSG_DEBUG, "nl80211: Neighboring BSS - bssid=" MACSTR
-                  " freq=%d MHz HT=%d pri_chan=%d sec_chan=%d",
-                  MAC2STR(bssid), freq, ht, pri_chan, sec_chan);
-
-       nnei = os_realloc(drv->neighbors, (drv->num_neighbors + 1) *
-                         sizeof(struct hostapd_neighbor_bss));
-       if (nnei == NULL)
-               return;
-       drv->neighbors = nnei;
-       nnei = &nnei[drv->num_neighbors];
-       os_memcpy(nnei->bssid, bssid, ETH_ALEN);
-       nnei->freq = freq;
-       nnei->ht = !!ht;
-       nnei->pri_chan = pri_chan;
-       nnei->sec_chan = sec_chan;
-       drv->num_neighbors++;
-}
-
-
-static int i802_get_scan_freq(struct iw_event *iwe, int *freq)
-{
-       int divi = 1000000, i;
-
-       if (iwe->u.freq.e == 0) {
-               /*
-                * Some drivers do not report frequency, but a channel.
-                * Try to map this to frequency by assuming they are using
-                * IEEE 802.11b/g.  But don't overwrite a previously parsed
-                * frequency if the driver sends both frequency and channel,
-                * since the driver may be sending an A-band channel that we
-                * don't handle here.
-                */
-
-               if (*freq)
-                       return 0;
-
-               if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) {
-                       *freq = 2407 + 5 * iwe->u.freq.m;
-                       return 0;
-               } else if (iwe->u.freq.m == 14) {
-                       *freq = 2484;
-                       return 0;
-               }
-       }
-
-       if (iwe->u.freq.e > 6) {
-               wpa_printf(MSG_DEBUG, "Invalid freq in scan results: "
-                          "m=%d e=%d", iwe->u.freq.m, iwe->u.freq.e);
-               return -1;
-       }
-
-       for (i = 0; i < iwe->u.freq.e; i++)
-               divi /= 10;
-       *freq = iwe->u.freq.m / divi;
-       return 0;
-}
-
-
-static int i802_parse_scan(struct wpa_driver_nl80211_data *drv, u8 *res_buf,
-                          size_t len)
-{
-       size_t ap_num = 0;
-       int first;
-       struct iw_event iwe_buf, *iwe = &iwe_buf;
-       char *pos, *end, *custom;
-       u8 bssid[ETH_ALEN];
-       int freq = 0;
-       u8 *ie = NULL;
-       size_t ie_len = 0;
-
-       ap_num = 0;
-       first = 1;
-
-       pos = (char *) res_buf;
-       end = (char *) res_buf + len;
-
-       while (pos + IW_EV_LCP_LEN <= end) {
-               /* Event data may be unaligned, so make a local, aligned copy
-                * before processing. */
-               os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
-               if (iwe->len <= IW_EV_LCP_LEN)
-                       break;
-
-               custom = pos + IW_EV_POINT_LEN;
-               if (iwe->cmd == IWEVGENIE) {
-                       /* WE-19 removed the pointer from struct iw_point */
-                       char *dpos = (char *) &iwe_buf.u.data.length;
-                       int dlen = dpos - (char *) &iwe_buf;
-                       os_memcpy(dpos, pos + IW_EV_LCP_LEN,
-                                 sizeof(struct iw_event) - dlen);
-               } else {
-                       os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
-                       custom += IW_EV_POINT_OFF;
-               }
-
-               switch (iwe->cmd) {
-               case SIOCGIWAP:
-                       if (!first)
-                               i802_add_neighbor(drv, bssid, freq, ie,
-                                                 ie_len);
-                       first = 0;
-                       os_memcpy(bssid, iwe->u.ap_addr.sa_data, ETH_ALEN);
-                       freq = 0;
-                       ie = NULL;
-                       ie_len = 0;
-                       break;
-               case SIOCGIWFREQ:
-                       i802_get_scan_freq(iwe, &freq);
-                       break;
-               case IWEVGENIE:
-                       if (custom + iwe->u.data.length > end) {
-                               wpa_printf(MSG_ERROR, "IWEVGENIE overflow");
-                               return -1;
-                       }
-                       ie = (u8 *) custom;
-                       ie_len = iwe->u.data.length;
-                       break;
-               }
-
-               pos += iwe->len;
-       }
-
-       if (!first)
-               i802_add_neighbor(drv, bssid, freq, ie, ie_len);
-
-       return 0;
-}
-
-
-static int i802_get_ht_scan_res(struct wpa_driver_nl80211_data *drv)
-{
-       struct iwreq iwr;
-       u8 *res_buf;
-       size_t res_buf_len;
-       int res;
-
-       res_buf_len = IW_SCAN_MAX_DATA;
-       for (;;) {
-               res_buf = os_malloc(res_buf_len);
-               if (res_buf == NULL)
-                       return -1;
-               os_memset(&iwr, 0, sizeof(iwr));
-               os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-               iwr.u.data.pointer = res_buf;
-               iwr.u.data.length = res_buf_len;
-
-               if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
-                       break;
-
-               if (errno == E2BIG && res_buf_len < 65535) {
-                       os_free(res_buf);
-                       res_buf = NULL;
-                       res_buf_len *= 2;
-                       if (res_buf_len > 65535)
-                               res_buf_len = 65535; /* 16-bit length field */
-                       wpa_printf(MSG_DEBUG, "Scan results did not fit - "
-                                  "trying larger buffer (%lu bytes)",
-                                  (unsigned long) res_buf_len);
-               } else {
-                       perror("ioctl[SIOCGIWSCAN]");
-                       os_free(res_buf);
-                       return -1;
-               }
-       }
-
-       if (iwr.u.data.length > res_buf_len) {
-               os_free(res_buf);
-               return -1;
-       }
-
-       res = i802_parse_scan(drv, res_buf, iwr.u.data.length);
-       os_free(res_buf);
-
-       return res;
-}
-
-
-static int i802_is_event_wireless_scan_complete(char *data, int len)
-{
-       struct iw_event iwe_buf, *iwe = &iwe_buf;
-       char *pos, *end;
-
-       pos = data;
-       end = data + len;
-
-       while (pos + IW_EV_LCP_LEN <= end) {
-               /* Event data may be unaligned, so make a local, aligned copy
-                * before processing. */
-               os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
-               if (iwe->cmd == SIOCGIWSCAN)
-                       return 1;
-
-               pos += iwe->len;
-       }
-
-       return 0;
-}
-
-
-static int i802_is_rtm_scan_complete(int ifindex, struct nlmsghdr *h, int len)
-{
-       struct ifinfomsg *ifi;
-       int attrlen, _nlmsg_len, rta_len;
-       struct rtattr *attr;
-
-       if (len < (int) sizeof(*ifi))
-               return 0;
-
-       ifi = NLMSG_DATA(h);
-
-       if (ifindex != ifi->ifi_index)
-               return 0; /* event for foreign ifindex */
-
-       _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
-
-       attrlen = h->nlmsg_len - _nlmsg_len;
-       if (attrlen < 0)
-               return 0;
-
-       attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len);
-
-       rta_len = RTA_ALIGN(sizeof(struct rtattr));
-       while (RTA_OK(attr, attrlen)) {
-               if (attr->rta_type == IFLA_WIRELESS &&
-                   i802_is_event_wireless_scan_complete(
-                           ((char *) attr) + rta_len,
-                           attr->rta_len - rta_len))
-                       return 1;
-               attr = RTA_NEXT(attr, attrlen);
-       }
-
-       return 0;
-}
-
-
-static int i802_is_scan_complete(int s, int ifindex)
-{
-       char buf[1024];
-       int left;
-       struct nlmsghdr *h;
-
-       left = recv(s, buf, sizeof(buf), MSG_DONTWAIT);
-       if (left < 0) {
-               perror("recv(netlink)");
-               return 0;
-       }
-
-       h = (struct nlmsghdr *) buf;
-       while (left >= (int) sizeof(*h)) {
-               int len, plen;
-
-               len = h->nlmsg_len;
-               plen = len - sizeof(*h);
-               if (len > left || plen < 0) {
-                       wpa_printf(MSG_DEBUG, "Malformed netlink message: "
-                                  "len=%d left=%d plen=%d",
-                                  len, left, plen);
-                       break;
-               }
-
-               switch (h->nlmsg_type) {
-               case RTM_NEWLINK:
-                       if (i802_is_rtm_scan_complete(ifindex, h, plen))
-                               return 1;
-                       break;
-               }
-
-               len = NLMSG_ALIGN(len);
-               left -= len;
-               h = (struct nlmsghdr *) ((char *) h + len);
-       }
-
-       return 0;
-}
-
-
-static int i802_ht_scan(struct wpa_driver_nl80211_data *drv)
-{
-       struct iwreq iwr;
-       int s, res, ifindex;
-       struct sockaddr_nl local;
-       time_t now, end;
-       fd_set rfds;
-       struct timeval tv;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Scanning overlapping BSSes before "
-                  "starting HT 20/40 MHz BSS");
-
-       /* Request a new scan */
-       /* TODO: would be enough to scan the selected band */
-       os_memset(&iwr, 0, sizeof(iwr));
-       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-       if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
-               perror("ioctl[SIOCSIWSCAN]");
-               return -1;
-       }
-
-       ifindex = if_nametoindex(drv->ifname);
-
-       /* Wait for scan completion event or timeout */
-       s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-       if (s < 0) {
-               perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
-               return -1;
-       }
-
-       os_memset(&local, 0, sizeof(local));
-       local.nl_family = AF_NETLINK;
-       local.nl_groups = RTMGRP_LINK;
-       if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) {
-               perror("bind(netlink)");
-               close(s);
-               return -1;
-       }
-
-       time(&end);
-       end += 30; /* Wait at most 30 seconds for scan results */
-       for (;;) {
-               time(&now);
-               tv.tv_sec = end > now ? end - now : 0;
-               tv.tv_usec = 0;
-               FD_ZERO(&rfds);
-               FD_SET(s, &rfds);
-               res = select(s + 1, &rfds, NULL, NULL, &tv);
-               if (res < 0) {
-                       perror("select");
-                       /* Assume results are ready after 10 seconds wait */
-                       os_sleep(10, 0);
-                       break;
-               } else if (res) {
-                       if (i802_is_scan_complete(s, ifindex)) {
-                               wpa_printf(MSG_DEBUG, "nl80211: Scan "
-                                          "completed");
-                               break;
-                       }
-               } else {
-                       wpa_printf(MSG_DEBUG, "nl80211: Scan timeout");
-                       /* Assume results are ready to be read now */
-                       break;
-               }
-       }
-
-       close(s);
-
-       return i802_get_ht_scan_res(drv);
-}
-#endif /* CONFIG_IEEE80211N */
-
-
-static int i802_init_sockets(struct wpa_driver_nl80211_data *drv, const u8 *bssid)
-{
-       struct ifreq ifr;
-
-       drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
-       if (drv->ioctl_sock < 0) {
-               perror("socket[PF_INET,SOCK_DGRAM]");
-               return -1;
-       }
-
-       /* start listening for EAPOL on the default AP interface */
-       add_ifidx(drv, if_nametoindex(drv->ifname));
-
-       if (hostapd_set_iface_flags(drv, drv->ifname, 0))
-               return -1;
-
-       if (bssid) {
-               os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ);
-               memcpy(ifr.ifr_hwaddr.sa_data, bssid, ETH_ALEN);
-               ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
-
-               if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) {
-                       perror("ioctl(SIOCSIFHWADDR)");
-                       return -1;
-               }
-       }
-
-       if (wpa_driver_nl80211_init_nl(drv, drv->hapd))
-               return -1;
-
-#ifdef CONFIG_IEEE80211N
-       if (drv->ht_40mhz_scan) {
-               if (nl80211_set_mode(drv, drv->ifname, NL80211_IFTYPE_STATION)
-                   || hostapd_set_iface_flags(drv, drv->ifname, 1) ||
-                   i802_ht_scan(drv) ||
-                   hostapd_set_iface_flags(drv, drv->ifname, 0)) {
-                       wpa_printf(MSG_ERROR, "Failed to scan channels for "
-                                  "HT 40 MHz operations");
-                       return -1;
-               }
-       }
-#endif /* CONFIG_IEEE80211N */
-
-       /* Initialise a monitor interface */
-       if (nl80211_create_monitor_interface(drv))
-               return -1;
-
-       if (nl80211_set_mode(drv, drv->ifname, NL80211_IFTYPE_AP))
-               goto fail1;
-
-       if (hostapd_set_iface_flags(drv, drv->ifname, 1))
-               goto fail1;
-
-       drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
-       if (drv->eapol_sock < 0) {
-               perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)");
-               goto fail1;
-       }
-
-       if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
-       {
-               printf("Could not register read socket for eapol\n");
-               return -1;
-       }
-
-       memset(&ifr, 0, sizeof(ifr));
-       os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
-       if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr) != 0) {
-               perror("ioctl(SIOCGIFHWADDR)");
-               goto fail1;
-       }
-
-       if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
-               printf("Invalid HW-addr family 0x%04x\n",
-                      ifr.ifr_hwaddr.sa_family);
-               goto fail1;
-       }
-       memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
-
-       return 0;
-
-fail1:
-       nl80211_remove_iface(drv, drv->monitor_ifidx);
-       return -1;
-}
-
-
 static int i802_get_inact_sec(void *priv, const u8 *addr)
 {
        struct hostap_sta_driver_data data;
@@ -4299,20 +3833,12 @@ static int i802_sta_disassoc(void *priv, const u8 *addr, int reason)
 }
 
 
-static const struct hostapd_neighbor_bss *
-i802_get_neighbor_bss(void *priv, size_t *num)
-{
-       struct wpa_driver_nl80211_data *drv = priv;
-       *num = drv->num_neighbors;
-       return drv->neighbors;
-}
-
-
 static void *i802_init(struct hostapd_data *hapd,
                       struct wpa_init_params *params)
 {
        struct wpa_driver_nl80211_data *drv;
        size_t i;
+       struct ifreq ifr;
 
        drv = os_zalloc(sizeof(struct wpa_driver_nl80211_data));
        if (drv == NULL) {
@@ -4321,6 +3847,7 @@ static void *i802_init(struct hostapd_data *hapd,
        }
 
        drv->hapd = hapd;
+       drv->ctx = hapd;
        memcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
        drv->ifindex = if_nametoindex(drv->ifname);
        drv->bss.ifindex = drv->ifindex;
@@ -4331,13 +3858,76 @@ static void *i802_init(struct hostapd_data *hapd,
                if (params->bridge[i])
                        add_ifidx(drv, if_nametoindex(params->bridge[i]));
        }
-       drv->ht_40mhz_scan = params->ht_40mhz_scan;
 
-       if (i802_init_sockets(drv, params->bssid))
+       drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->ioctl_sock < 0) {
+               perror("socket[PF_INET,SOCK_DGRAM]");
+               goto failed;
+       }
+
+       /* start listening for EAPOL on the default AP interface */
+       add_ifidx(drv, drv->ifindex);
+
+       if (hostapd_set_iface_flags(drv, drv->ifname, 0))
+               goto failed;
+
+       if (params->bssid) {
+               os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ);
+               os_memcpy(ifr.ifr_hwaddr.sa_data, params->bssid, ETH_ALEN);
+               ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
+
+               if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) {
+                       perror("ioctl(SIOCSIFHWADDR)");
+                       goto failed;
+               }
+       }
+
+       if (wpa_driver_nl80211_init_nl(drv, drv->hapd))
+               goto failed;
+
+       /* Initialise a monitor interface */
+       if (nl80211_create_monitor_interface(drv))
+               goto failed;
+
+       if (nl80211_set_mode(drv, drv->ifindex, NL80211_IFTYPE_AP)) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s "
+                          "into AP mode", drv->ifname);
+               goto fail1;
+       }
+
+       if (hostapd_set_iface_flags(drv, drv->ifname, 1))
+               goto fail1;
+
+       drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
+       if (drv->eapol_sock < 0) {
+               perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)");
+               goto fail1;
+       }
+
+       if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
+       {
+               printf("Could not register read socket for eapol\n");
                goto failed;
+       }
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+       if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr) != 0) {
+               perror("ioctl(SIOCGIFHWADDR)");
+               goto fail1;
+       }
+
+       if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+               printf("Invalid HW-addr family 0x%04x\n",
+                      ifr.ifr_hwaddr.sa_family);
+               goto fail1;
+       }
+       os_memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
 
        return drv;
 
+fail1:
+       nl80211_remove_iface(drv, drv->monitor_ifidx);
 failed:
        free(drv);
        return NULL;
@@ -4384,8 +3974,6 @@ static void i802_deinit(void *priv)
        if (drv->if_indices != drv->default_if_indices)
                free(drv->if_indices);
 
-       os_free(drv->neighbors);
-
        bss = drv->bss.next;
        while (bss) {
                prev = bss;
@@ -4416,6 +4004,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .get_capa = wpa_driver_nl80211_get_capa,
        .set_operstate = wpa_driver_nl80211_set_operstate,
        .set_country = wpa_driver_nl80211_set_country,
+       .set_mode = wpa_driver_nl80211_set_mode,
 #ifdef CONFIG_AP
        .set_beacon = wpa_driver_nl80211_set_beacon,
 #endif /* CONFIG_AP */
@@ -4455,6 +4044,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .if_update = i802_if_update,
        .if_remove = i802_if_remove,
        .set_sta_vlan = i802_set_sta_vlan,
-       .get_neighbor_bss = i802_get_neighbor_bss,
 #endif /* HOSTAPD */
 };
index 93427c1..7522527 100644 (file)
@@ -862,8 +862,7 @@ struct wpa_driver_ops wpa_driver_privsep_ops = {
        NULL /* set_radius_acl_expire */,
        NULL /* set_ht_params */,
        NULL /* set_wps_beacon_ie */,
-       NULL /* set_wps_probe_resp_ie */,
-       NULL /* get_neighbor_bss */
+       NULL /* set_wps_probe_resp_ie */
 };
 
 
index 9fdf010..640b33b 100644 (file)
@@ -2567,7 +2567,6 @@ const struct wpa_driver_ops wpa_driver_test_ops = {
        NULL /* set_radius_acl_expire */,
        NULL /* set_ht_params */,
        NULL /* set_wps_beacon_ie */,
-       NULL /* set_wps_probe_resp_ie */,
-       NULL /* get_neighbor_bss */
+       NULL /* set_wps_probe_resp_ie */
 #endif /* HOSTAPD */
 };