+
+/* Compare function for sorting scan results. Return >0 if @b is considered
+ * better. */
+static int wpa_scan_result_compar(const void *a, const void *b)
+{
+ struct wpa_scan_res **_wa = (void *) a;
+ struct wpa_scan_res **_wb = (void *) b;
+ struct wpa_scan_res *wa = *_wa;
+ struct wpa_scan_res *wb = *_wb;
+ int wpa_a, wpa_b, maxrate_a, maxrate_b;
+
+ /* WPA/WPA2 support preferred */
+ wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
+ wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
+ wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
+ wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
+
+ if (wpa_b && !wpa_a)
+ return 1;
+ if (!wpa_b && wpa_a)
+ return -1;
+
+ /* privacy support preferred */
+ if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
+ (wb->caps & IEEE80211_CAP_PRIVACY))
+ return 1;
+ if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
+ (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
+ return -1;
+
+ /* best/max rate preferred if signal level close enough XXX */
+ if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) ||
+ (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
+ maxrate_a = wpa_scan_get_max_rate(wa);
+ maxrate_b = wpa_scan_get_max_rate(wb);
+ if (maxrate_a != maxrate_b)
+ return maxrate_b - maxrate_a;
+ }
+
+ /* use freq for channel preference */
+
+ /* all things being equal, use signal level; if signal levels are
+ * identical, use quality values since some drivers may only report
+ * that value and leave the signal level zero */
+ if (wb->level == wa->level)
+ return wb->qual - wa->qual;
+ return wb->level - wa->level;
+}
+
+
+#ifdef CONFIG_WPS
+/* Compare function for sorting scan results when searching a WPS AP for
+ * provisioning. Return >0 if @b is considered better. */
+static int wpa_scan_result_wps_compar(const void *a, const void *b)
+{
+ struct wpa_scan_res **_wa = (void *) a;
+ struct wpa_scan_res **_wb = (void *) b;
+ struct wpa_scan_res *wa = *_wa;
+ struct wpa_scan_res *wb = *_wb;
+ int uses_wps_a, uses_wps_b;
+ struct wpabuf *wps_a, *wps_b;
+ int res;
+
+ /* Optimization - check WPS IE existence before allocated memory and
+ * doing full reassembly. */
+ uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL;
+ uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL;
+ if (uses_wps_a && !uses_wps_b)
+ return -1;
+ if (!uses_wps_a && uses_wps_b)
+ return 1;
+
+ if (uses_wps_a && uses_wps_b) {
+ wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE);
+ wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE);
+ res = wps_ap_priority_compar(wps_a, wps_b);
+ wpabuf_free(wps_a);
+ wpabuf_free(wps_b);
+ if (res)
+ return res;
+ }
+
+ /*
+ * Do not use current AP security policy as a sorting criteria during
+ * WPS provisioning step since the AP may get reconfigured at the
+ * completion of provisioning.
+ */
+
+ /* all things being equal, use signal level; if signal levels are
+ * identical, use quality values since some drivers may only report
+ * that value and leave the signal level zero */
+ if (wb->level == wa->level)
+ return wb->qual - wa->qual;
+ return wb->level - wa->level;
+}
+#endif /* CONFIG_WPS */
+
+
+/**
+ * wpa_supplicant_get_scan_results - Get scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @info: Information about what was scanned or %NULL if not available
+ * @new_scan: Whether a new scan was performed
+ * Returns: Scan results, %NULL on failure
+ *
+ * This function request the current scan results from the driver and updates
+ * the local BSS list wpa_s->bss. The caller is responsible for freeing the
+ * results with wpa_scan_results_free().
+ */
+struct wpa_scan_results *
+wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
+ struct scan_info *info, int new_scan)
+{
+ struct wpa_scan_results *scan_res;
+ size_t i;
+ int (*compar)(const void *, const void *) = wpa_scan_result_compar;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+ scan_res = ieee80211_sta_get_scan_results(wpa_s);
+ else
+ scan_res = wpa_drv_get_scan_results2(wpa_s);
+ if (scan_res == NULL) {
+ wpa_printf(MSG_DEBUG, "Failed to get scan results");
+ return NULL;
+ }
+
+#ifdef CONFIG_WPS
+ if (wpas_wps_in_progress(wpa_s)) {
+ wpa_printf(MSG_DEBUG, "WPS: Order scan results with WPS "
+ "provisioning rules");
+ compar = wpa_scan_result_wps_compar;
+ }
+#endif /* CONFIG_WPS */
+
+ qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
+ compar);
+
+ wpa_bss_update_start(wpa_s);
+ for (i = 0; i < scan_res->num; i++)
+ wpa_bss_update_scan_res(wpa_s, scan_res->res[i]);
+ wpa_bss_update_end(wpa_s, info, new_scan);
+
+ return scan_res;
+}
+
+
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_scan_results *scan_res;
+ scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+ if (scan_res == NULL)
+ return -1;
+ wpa_scan_results_free(scan_res);
+
+ return 0;
+}
+
+
+void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+ size_t i;
+
+ if (res == NULL)
+ return;
+
+ for (i = 0; i < res->num; i++)
+ os_free(res->res[i]);
+ os_free(res->res);
+ os_free(res);
+}