+static int nl80211_get_scanlist_wpactl(const char *ifname, char *buf, int *len)
+{
+ int sock, qmax, rssi, tries, count = -1, ready = 0;
+ char *pos, *line, *bssid, *freq, *signal, *flags, *ssid, reply[4096];
+ struct sockaddr_un local = { 0 };
+ struct iwinfo_scanlist_entry *e = (struct iwinfo_scanlist_entry *)buf;
+
+ sock = nl80211_wpactl_connect(ifname, &local);
+
+ if (sock < 0)
+ return sock;
+
+ send(sock, "ATTACH", 6, 0);
+ send(sock, "SCAN", 4, 0);
+
+ /*
+ * wait for scan results:
+ * nl80211_wpactl_recv() will use a timeout of 256ms and we need to scan
+ * 72 channels at most. We'll also receive two "OK" messages acknowledging
+ * the "ATTACH" and "SCAN" commands and the driver might need a bit extra
+ * time to process the results, so try 72 + 2 + 1 times.
+ */
+ for (tries = 0; tries < 75; tries++)
+ {
+ if (nl80211_wpactl_recv(sock, reply, sizeof(reply)) <= 0)
+ continue;
+
+ /* got an event notification */
+ if (reply[0] == '<')
+ {
+ /* scan results are ready */
+ if (strstr(reply, "CTRL-EVENT-SCAN-RESULTS"))
+ {
+ /* send "SCAN_RESULTS" command */
+ ready = (send(sock, "SCAN_RESULTS", 12, 0) == 12);
+ break;
+ }
+
+ /* is another unrelated event, retry */
+ tries--;
+ }
+ }
+
+ /* receive and parse scan results if the wait above didn't time out */
+ if (ready && nl80211_wpactl_recv(sock, reply, sizeof(reply)) > 0)
+ {
+ nl80211_get_quality_max(ifname, &qmax);
+
+ for (line = strtok_r(reply, "\n", &pos);
+ line != NULL;
+ line = strtok_r(NULL, "\n", &pos))
+ {
+ /* skip header line */
+ if (count < 0)
+ {
+ count++;
+ continue;
+ }
+
+ bssid = strtok(line, "\t");
+ freq = strtok(NULL, "\t");
+ signal = strtok(NULL, "\t");
+ flags = strtok(NULL, "\t");
+ ssid = strtok(NULL, "\n");
+
+ if (!bssid || !freq || !signal || !flags || !ssid)
+ continue;
+
+ /* BSSID */
+ e->mac[0] = strtol(&bssid[0], NULL, 16);
+ e->mac[1] = strtol(&bssid[3], NULL, 16);
+ e->mac[2] = strtol(&bssid[6], NULL, 16);
+ e->mac[3] = strtol(&bssid[9], NULL, 16);
+ e->mac[4] = strtol(&bssid[12], NULL, 16);
+ e->mac[5] = strtol(&bssid[15], NULL, 16);
+
+ /* SSID */
+ wpasupp_ssid_decode(ssid, e->ssid, sizeof(e->ssid));
+
+ /* Mode */
+ if (strstr(flags, "[MESH]"))
+ e->mode = IWINFO_OPMODE_MESHPOINT;
+ else if (strstr(flags, "[IBSS]"))
+ e->mode = IWINFO_OPMODE_ADHOC;
+ else
+ e->mode = IWINFO_OPMODE_MASTER;
+
+ /* Channel */
+ e->channel = nl80211_freq2channel(atoi(freq));
+
+ /* Signal */
+ rssi = atoi(signal);
+ e->signal = rssi;
+
+ /* Quality */
+ if (rssi < 0)
+ {
+ /* The cfg80211 wext compat layer assumes a signal range
+ * of -110 dBm to -40 dBm, the quality value is derived
+ * by adding 110 to the signal level */
+ if (rssi < -110)
+ rssi = -110;
+ else if (rssi > -40)
+ rssi = -40;
+
+ e->quality = (rssi + 110);
+ }
+ else
+ {
+ e->quality = rssi;
+ }
+
+ /* Max. Quality */
+ e->quality_max = qmax;
+
+ /* Crypto */
+ nl80211_get_scancrypto(flags, &e->crypto);
+
+ count++;
+ e++;
+ }
+
+ *len = count * sizeof(struct iwinfo_scanlist_entry);
+ }
+
+ close(sock);
+ unlink(local.sun_path);
+
+ return (count >= 0) ? 0 : -1;
+}
+