diff options
| -rw-r--r-- | src/drivers/driver_nl80211.h | 4 | ||||
| -rw-r--r-- | src/drivers/driver_nl80211_capa.c | 3 | ||||
| -rw-r--r-- | src/drivers/driver_nl80211_event.c | 123 | ||||
| -rw-r--r-- | src/drivers/driver_nl80211_scan.c | 186 |
4 files changed, 316 insertions, 0 deletions
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index 33bcc0e2..d1cbaeff 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -148,7 +148,9 @@ struct wpa_driver_nl80211_data { unsigned int setband_vendor_cmd_avail:1; unsigned int get_pref_freq_list:1; unsigned int set_prob_oper_freq:1; + unsigned int scan_vendor_cmd_avail:1; + u64 vendor_scan_cookie; u64 remain_on_chan_cookie; u64 send_action_cookie; #define MAX_SEND_ACTION_COOKIES 20 @@ -277,5 +279,7 @@ struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv); void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv); int wpa_driver_nl80211_abort_scan(void *priv); const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie); +int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, + struct wpa_driver_scan_params *params); #endif /* DRIVER_NL80211_H */ diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 4cf31238..4adc95b3 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -602,6 +602,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_SETBAND: drv->setband_vendor_cmd_avail = 1; break; + case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN: + drv->scan_vendor_cmd_avail = 1; + break; } } diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 7b0f721e..647280c8 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -1686,6 +1686,123 @@ static void qca_nl80211_dfs_offload_radar_event( } +static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv, + u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; + u64 cookie = 0; + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]) + return; + + cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); + if (cookie != drv->vendor_scan_cookie) { + /* External scan trigger event, ignore */ + return; + } + + drv->scan_state = SCAN_STARTED; + wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); +} + + +static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv, + int aborted, struct nlattr *tb[]) +{ + union wpa_event_data event; + struct nlattr *nl; + int rem; + struct scan_info *info; + int freqs[MAX_REPORT_FREQS]; + int num_freqs = 0; + + os_memset(&event, 0, sizeof(event)); + info = &event.scan_info; + info->aborted = aborted; + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(nl, + tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS], rem) { + struct wpa_driver_scan_ssid *s = + &info->ssids[info->num_ssids]; + s->ssid = nla_data(nl); + s->ssid_len = nla_len(nl); + wpa_printf(MSG_DEBUG, + "nl80211: Scan probed for SSID '%s'", + wpa_ssid_txt(s->ssid, s->ssid_len)); + info->num_ssids++; + if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) + break; + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) { + char msg[200], *pos, *end; + int res; + + pos = msg; + end = pos + sizeof(msg); + *pos = '\0'; + + nla_for_each_nested(nl, + tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES], + rem) { + freqs[num_freqs] = nla_get_u32(nl); + res = os_snprintf(pos, end - pos, " %d", + freqs[num_freqs]); + if (!os_snprintf_error(end - pos, res)) + pos += res; + num_freqs++; + if (num_freqs == MAX_REPORT_FREQS - 1) + break; + } + + info->freqs = freqs; + info->num_freqs = num_freqs; + wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", + msg); + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); +} + + +static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv, + u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; + u64 cookie = 0; + enum scan_status status; + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS] || + !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]) + return; + + status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS]); + if (status >= VENDOR_SCAN_STATUS_MAX) + return; /* invalid status */ + + cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); + if (cookie != drv->vendor_scan_cookie) { + /* Event from an external scan, get scan results */ + } else { + if (status == VENDOR_SCAN_STATUS_NEW_RESULTS) + drv->scan_state = SCAN_COMPLETED; + else + drv->scan_state = SCAN_ABORTED; + + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + drv->vendor_scan_cookie = 0; + } + + send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb); +} + + static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *data, size_t len) { @@ -1709,6 +1826,12 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len); break; + case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN: + qca_nl80211_scan_trigger_event(drv, data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE: + qca_nl80211_scan_done_event(drv, data, len); + break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported QCA vendor event %u", diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 1c79f763..99e12e74 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -14,6 +14,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/qca-vendor.h" #include "driver_nl80211.h" @@ -798,3 +799,188 @@ int wpa_driver_nl80211_abort_scan(void *priv) return ret; } + +static int scan_cookie_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + u64 *cookie = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + struct nlattr *nl_vendor = tb[NL80211_ATTR_VENDOR_DATA]; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; + + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, + nla_data(nl_vendor), nla_len(nl_vendor), NULL); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]) + *cookie = nla_get_u64( + tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); + } + + return NL_SKIP; +} + + +/** + * wpa_driver_nl80211_vendor_scan - Request the driver to initiate a vendor scan + * @bss: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg = NULL; + struct nlattr *attr; + size_t i; + u32 scan_flags = 0; + int ret = -1; + u64 cookie = 0; + + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request"); + drv->scan_for_auth = 0; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) ) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (attr == NULL) + goto fail; + + if (params->num_ssids) { + struct nlattr *ssids; + + ssids = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS); + if (ssids == NULL) + goto fail; + for (i = 0; i < params->num_ssids; i++) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + if (nla_put(msg, i + 1, params->ssids[i].ssid_len, + params->ssids[i].ssid)) + goto fail; + } + nla_nest_end(msg, ssids); + } + + if (params->extra_ies) { + wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", + params->extra_ies, params->extra_ies_len); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_IE, + params->extra_ies_len, params->extra_ies)) + goto fail; + } + + if (params->freqs) { + struct nlattr *freqs; + + freqs = nla_nest_start(msg, + QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES); + if (freqs == NULL) + goto fail; + for (i = 0; params->freqs[i]; i++) { + wpa_printf(MSG_MSGDUMP, + "nl80211: Scan frequency %u MHz", + params->freqs[i]); + if (nla_put_u32(msg, i + 1, params->freqs[i])) + goto fail; + } + nla_nest_end(msg, freqs); + } + + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + + if (params->low_priority && drv->have_low_prio_scan) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY"); + scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY; + } + + if (params->mac_addr_rand) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR"); + scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR; + + if (params->mac_addr) { + wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR, + MAC2STR(params->mac_addr)); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC, + ETH_ALEN, params->mac_addr)) + goto fail; + } + + if (params->mac_addr_mask) { + wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: " + MACSTR, MAC2STR(params->mac_addr_mask)); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK, + ETH_ALEN, params->mac_addr_mask)) + goto fail; + } + } + + if (scan_flags && + nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags)) + goto fail; + + if (params->p2p_probe) { + struct nlattr *rates; + + wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); + + rates = nla_nest_start(msg, + QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES); + if (rates == NULL) + goto fail; + + /* + * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates + * by masking out everything else apart from the OFDM rates 6, + * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz + * rates are left enabled. + */ + if (nla_put(msg, NL80211_BAND_2GHZ, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c")) + goto fail; + nla_nest_end(msg, rates); + + if (nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE)) + goto fail; + } + + nla_nest_end(msg, attr); + + ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Vendor scan trigger failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + + drv->vendor_scan_cookie = cookie; + drv->scan_state = SCAN_REQUESTED; + + wpa_printf(MSG_DEBUG, + "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx", + ret, (long long unsigned int) cookie); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + +fail: + nlmsg_free(msg); + return ret; +} |
