P2P: Allow P2P listen being offloaded to the driver/firmware
[mech_eap.git] / src / drivers / driver_nl80211_event.c
index 5c8b294..762e3ac 100644 (file)
@@ -180,34 +180,23 @@ static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
 }
 
 
-static int nl80211_parse_wmm_params(struct nlattr *wmm_attr,
-                                   struct wmm_params *wmm_params)
+static void nl80211_parse_wmm_params(struct nlattr *wmm_attr,
+                                    struct wmm_params *wmm_params)
 {
        struct nlattr *wmm_info[NL80211_STA_WME_MAX + 1];
        static struct nla_policy wme_policy[NL80211_STA_WME_MAX + 1] = {
                [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
        };
 
-       if (!wmm_attr) {
-               wpa_printf(MSG_DEBUG, "nl80211: WMM data missing");
-               return -1;
-       }
-
-       if (nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr,
-                            wme_policy)) {
-               wpa_printf(MSG_DEBUG,
-                          "nl80211: Failed to parse nested attributes");
-               return -1;
-       }
-
-       if (!wmm_info[NL80211_STA_WME_UAPSD_QUEUES])
-               return -1;
+       if (!wmm_attr ||
+           nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr,
+                            wme_policy) ||
+           !wmm_info[NL80211_STA_WME_UAPSD_QUEUES])
+               return;
 
        wmm_params->uapsd_queues =
                nla_get_u8(wmm_info[NL80211_STA_WME_UAPSD_QUEUES]);
        wmm_params->info_bitmap |= WMM_PARAMS_UAPSD_QUEUES_INFO;
-
-       return 0;
 }
 
 
@@ -276,12 +265,15 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
                               enum nl80211_commands cmd, struct nlattr *status,
                               struct nlattr *addr, struct nlattr *req_ie,
                               struct nlattr *resp_ie,
+                              struct nlattr *timed_out,
                               struct nlattr *authorized,
                               struct nlattr *key_replay_ctr,
                               struct nlattr *ptk_kck,
-                              struct nlattr *ptk_kek)
+                              struct nlattr *ptk_kek,
+                              struct nlattr *subnet_status)
 {
        union wpa_event_data event;
+       const u8 *ssid;
        u16 status_code;
 
        if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
@@ -294,6 +286,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
                return;
        }
 
+       drv->connect_reassoc = 0;
+
        status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS;
 
        if (cmd == NL80211_CMD_CONNECT) {
@@ -329,6 +323,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
                        event.assoc_reject.resp_ies_len = nla_len(resp_ie);
                }
                event.assoc_reject.status_code = status_code;
+               event.assoc_reject.timed_out = timed_out != NULL;
                wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
                return;
        }
@@ -342,6 +337,16 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
        if (req_ie) {
                event.assoc_info.req_ies = nla_data(req_ie);
                event.assoc_info.req_ies_len = nla_len(req_ie);
+
+               if (cmd == NL80211_CMD_ROAM) {
+                       ssid = get_ie(event.assoc_info.req_ies,
+                                     event.assoc_info.req_ies_len,
+                                     WLAN_EID_SSID);
+                       if (ssid && ssid[1] > 0 && ssid[1] <= 32) {
+                               drv->ssid_len = ssid[1];
+                               os_memcpy(drv->ssid, ssid + 2, ssid[1]);
+                       }
+               }
        }
        if (resp_ie) {
                event.assoc_info.resp_ies = nla_data(resp_ie);
@@ -367,6 +372,17 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
                event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
        }
 
+       if (subnet_status) {
+               /*
+                * At least for now, this is only available from
+                * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS and that
+                * attribute has the same values 0, 1, 2 as are used in the
+                * variable here, so no mapping between different values are
+                * needed.
+                */
+               event.assoc_info.subnet_status = nla_get_u8(subnet_status);
+       }
+
        wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -560,9 +576,10 @@ static void mlme_event_mgmt(struct i802_bss *bss,
                rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
        }
        wpa_printf(MSG_DEBUG,
-                  "nl80211: RX frame sa=" MACSTR
+                  "nl80211: RX frame da=" MACSTR " sa=" MACSTR " bssid=" MACSTR
                   " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u",
-                  MAC2STR(mgmt->sa), rx_freq, ssi_signal, fc,
+                  MAC2STR(mgmt->da), MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid),
+                  rx_freq, ssi_signal, fc,
                   le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc),
                   (unsigned int) len);
        event.rx_mgmt.frame = frame;
@@ -639,10 +656,39 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
                         * Avoid issues with some roaming cases where
                         * disconnection event for the old AP may show up after
                         * we have started connection with the new AP.
+                        * In case of locally generated event clear
+                        * ignore_next_local_deauth as well, to avoid next local
+                        * deauth event be wrongly ignored.
                         */
-                       wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
-                                  MAC2STR(bssid),
-                                  MAC2STR(drv->auth_attempt_bssid));
+                       if (!os_memcmp(mgmt->sa, drv->first_bss->addr,
+                                      ETH_ALEN)) {
+                               wpa_printf(MSG_DEBUG,
+                                          "nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag");
+                               drv->ignore_next_local_deauth = 0;
+                       } else {
+                               wpa_printf(MSG_DEBUG,
+                                          "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
+                                          MAC2STR(bssid),
+                                          MAC2STR(drv->auth_attempt_bssid));
+                       }
+                       return;
+               }
+
+               if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+                   drv->connect_reassoc && drv->associated &&
+                   os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0 &&
+                   os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0) {
+                       /*
+                        * Avoid issues with some roaming cases where
+                        * disconnection event for the old AP may show up after
+                        * we have started connection with the new AP.
+                        */
+                        wpa_printf(MSG_DEBUG,
+                                   "nl80211: Ignore deauth/disassoc event from old AP "
+                                   MACSTR
+                                   " when already connecting with " MACSTR,
+                                   MAC2STR(bssid),
+                                   MAC2STR(drv->auth_attempt_bssid));
                        return;
                }
 
@@ -679,13 +725,15 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
                                mgmt->u.disassoc.variable;
                }
        } else {
+               event.deauth_info.locally_generated =
+                       !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
                if (drv->ignore_deauth_event) {
                        wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
                        drv->ignore_deauth_event = 0;
+                       if (event.deauth_info.locally_generated)
+                               drv->ignore_next_local_deauth = 0;
                        return;
                }
-               event.deauth_info.locally_generated =
-                       !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
                if (drv->ignore_next_local_deauth) {
                        drv->ignore_next_local_deauth = 0;
                        if (event.deauth_info.locally_generated) {
@@ -868,6 +916,7 @@ static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
                                 struct nlattr *tb[])
 {
        unsigned int freq;
+       union wpa_event_data event;
 
        if (tb[NL80211_ATTR_MAC] == NULL) {
                wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
@@ -887,7 +936,10 @@ static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
                drv->first_bss->freq = freq;
        }
 
-       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+       os_memset(&event, 0, sizeof(event));
+       event.assoc_info.freq = freq;
+
+       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
 
@@ -968,7 +1020,7 @@ static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv,
 
 
 static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
-                           struct nlattr *tb[])
+                           struct nlattr *tb[], int external_scan)
 {
        union wpa_event_data event;
        struct nlattr *nl;
@@ -978,7 +1030,7 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
        int freqs[MAX_REPORT_FREQS];
        int num_freqs = 0;
 
-       if (drv->scan_for_auth) {
+       if (!external_scan && drv->scan_for_auth) {
                drv->scan_for_auth = 0;
                wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
                           "cfg80211 BSS entry");
@@ -989,6 +1041,8 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
        os_memset(&event, 0, sizeof(event));
        info = &event.scan_info;
        info->aborted = aborted;
+       info->external_scan = external_scan;
+       info->nl_scan_event = 1;
 
        if (tb[NL80211_ATTR_SCAN_SSIDS]) {
                nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
@@ -1004,7 +1058,7 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
                }
        }
        if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
-               char msg[200], *pos, *end;
+               char msg[300], *pos, *end;
                int res;
 
                pos = msg;
@@ -1104,14 +1158,12 @@ static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv,
        const u8 *addr;
        union wpa_event_data data;
 
-       if (drv->nlmode != NL80211_IFTYPE_MESH_POINT)
-               return;
-
-       if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
+       if (drv->nlmode != NL80211_IFTYPE_MESH_POINT ||
+           !tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
                return;
 
        addr = nla_data(tb[NL80211_ATTR_MAC]);
-       wpa_printf(MSG_DEBUG, "nl80211: New peer candidate" MACSTR,
+       wpa_printf(MSG_DEBUG, "nl80211: New peer candidate " MACSTR,
                   MAC2STR(addr));
 
        os_memset(&data, 0, sizeof(data));
@@ -1156,6 +1208,7 @@ static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
 
 
 static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
+                                     struct i802_bss *bss,
                                      struct nlattr **tb)
 {
        u8 *addr;
@@ -1168,7 +1221,7 @@ static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
                   MAC2STR(addr));
 
        if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
-               drv_event_disassoc(drv->ctx, addr);
+               drv_event_disassoc(bss->ctx, addr);
                return;
        }
 
@@ -1177,7 +1230,7 @@ static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
 
        os_memset(&data, 0, sizeof(data));
        os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN);
-       wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data);
+       wpa_supplicant_event(bss->ctx, EVENT_IBSS_PEER_LOST, &data);
 }
 
 
@@ -1201,14 +1254,11 @@ static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv,
        };
        union wpa_event_data data;
 
-       if (!tb[NL80211_ATTR_MAC])
-               return;
-       if (!tb[NL80211_ATTR_REKEY_DATA])
-               return;
-       if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
-                            tb[NL80211_ATTR_REKEY_DATA], rekey_policy))
-               return;
-       if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
+       if (!tb[NL80211_ATTR_MAC] ||
+           !tb[NL80211_ATTR_REKEY_DATA] ||
+           nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
+                            tb[NL80211_ATTR_REKEY_DATA], rekey_policy) ||
+           !rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
                return;
 
        os_memset(&data, 0, sizeof(data));
@@ -1239,12 +1289,10 @@ static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv,
 
        wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event");
 
-       if (!tb[NL80211_ATTR_PMKSA_CANDIDATE])
-               return;
-       if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
-                            tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy))
-               return;
-       if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
+       if (!tb[NL80211_ATTR_PMKSA_CANDIDATE] ||
+           nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
+                            tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy) ||
+           !cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
            !cand[NL80211_PMKSA_CANDIDATE_BSSID])
                return;
 
@@ -1300,6 +1348,12 @@ static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv,
                           MACSTR, MAC2STR(data.tdls.peer));
                data.tdls.oper = TDLS_REQUEST_TEARDOWN;
                break;
+       case NL80211_TDLS_DISCOVERY_REQ:
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: TDLS discovery request for peer " MACSTR,
+                          MAC2STR(data.tdls.peer));
+               data.tdls.oper = TDLS_REQUEST_DISCOVER;
+               break;
        default:
                wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione "
                           "event");
@@ -1445,6 +1499,8 @@ static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
 static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
                                   const u8 *data, size_t len)
 {
@@ -1492,6 +1548,25 @@ static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static enum hostapd_hw_mode get_qca_hw_mode(u8 hw_mode)
+{
+       switch (hw_mode) {
+       case QCA_ACS_MODE_IEEE80211B:
+               return HOSTAPD_MODE_IEEE80211B;
+       case QCA_ACS_MODE_IEEE80211G:
+               return HOSTAPD_MODE_IEEE80211G;
+       case QCA_ACS_MODE_IEEE80211A:
+               return HOSTAPD_MODE_IEEE80211A;
+       case QCA_ACS_MODE_IEEE80211AD:
+               return HOSTAPD_MODE_IEEE80211AD;
+       case QCA_ACS_MODE_IEEE80211ANY:
+               return HOSTAPD_MODE_IEEE80211ANY;
+       default:
+               return NUM_HOSTAPD_MODES;
+       }
+}
+
+
 static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
                                   const u8 *data, size_t len)
 {
@@ -1502,10 +1577,8 @@ static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
                   "nl80211: ACS channel selection vendor event received");
 
        if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX,
-                     (struct nlattr *) data, len, NULL))
-               return;
-
-       if (!tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL] ||
+                     (struct nlattr *) data, len, NULL) ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL] ||
            !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL])
                return;
 
@@ -1514,6 +1587,39 @@ static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
                nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]);
        event.acs_selected_channels.sec_channel =
                nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]);
+       if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+               event.acs_selected_channels.vht_seg0_center_ch =
+                       nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
+       if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+               event.acs_selected_channels.vht_seg1_center_ch =
+                       nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]);
+       if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH])
+               event.acs_selected_channels.ch_width =
+                       nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]);
+       if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) {
+               u8 hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]);
+
+               event.acs_selected_channels.hw_mode = get_qca_hw_mode(hw_mode);
+               if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES ||
+                   event.acs_selected_channels.hw_mode ==
+                   HOSTAPD_MODE_IEEE80211ANY) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Invalid hw_mode %d in ACS selection event",
+                                  hw_mode);
+                       return;
+               }
+       }
+
+       wpa_printf(MSG_INFO,
+                  "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d",
+                  event.acs_selected_channels.pri_channel,
+                  event.acs_selected_channels.sec_channel,
+                  event.acs_selected_channels.ch_width,
+                  event.acs_selected_channels.vht_seg0_center_ch,
+                  event.acs_selected_channels.vht_seg1_center_ch,
+                  event.acs_selected_channels.hw_mode);
+
+       /* Ignore ACS channel list check for backwards compatibility */
 
        wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event);
 }
@@ -1529,9 +1635,8 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
                   "nl80211: Key management roam+auth vendor event received");
 
        if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX,
-                     (struct nlattr *) data, len, NULL))
-               return;
-       if (!tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] ||
+                     (struct nlattr *) data, len, NULL) ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] ||
            nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN ||
            !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] ||
            !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] ||
@@ -1545,17 +1650,268 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
                           tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
                           tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
                           tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
+                          NULL,
                           tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
                           tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
                           tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
-                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]);
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]);
 }
 
 
+static void qca_nl80211_dfs_offload_radar_event(
+       struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *msg, int length)
+{
+       union wpa_event_data data;
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: DFS offload radar vendor event received");
+
+       if (nla_parse(tb, NL80211_ATTR_MAX,
+                     (struct nlattr *) msg, length, NULL))
+               return;
+
+       if (!tb[NL80211_ATTR_WIPHY_FREQ]) {
+               wpa_printf(MSG_INFO,
+                          "nl80211: Error parsing WIPHY_FREQ in FS offload radar vendor event");
+               return;
+       }
+
+       os_memset(&data, 0, sizeof(data));
+       data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+
+       wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz",
+                  data.dfs_event.freq);
+
+       /* Check HT params */
+       if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+               data.dfs_event.ht_enabled = 1;
+               data.dfs_event.chan_offset = 0;
+
+               switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
+               case NL80211_CHAN_NO_HT:
+                       data.dfs_event.ht_enabled = 0;
+                       break;
+               case NL80211_CHAN_HT20:
+                       break;
+               case NL80211_CHAN_HT40PLUS:
+                       data.dfs_event.chan_offset = 1;
+                       break;
+               case NL80211_CHAN_HT40MINUS:
+                       data.dfs_event.chan_offset = -1;
+                       break;
+               }
+       }
+
+       /* Get VHT params */
+       if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+               data.dfs_event.chan_width =
+                       convert2width(nla_get_u32(
+                                             tb[NL80211_ATTR_CHANNEL_WIDTH]));
+       if (tb[NL80211_ATTR_CENTER_FREQ1])
+               data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+       if (tb[NL80211_ATTR_CENTER_FREQ2])
+               data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+       wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, "
+                   "offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
+                   data.dfs_event.freq, data.dfs_event.ht_enabled,
+                   data.dfs_event.chan_offset, data.dfs_event.chan_width,
+                   data.dfs_event.cf1, data.dfs_event.cf2);
+
+       switch (subcmd) {
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Unknown DFS offload radar event %d received",
+                          subcmd);
+               break;
+       }
+}
+
+
+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;
+       union wpa_event_data event;
+       struct scan_info *info;
+
+       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;
+       }
+
+       /* Cookie match, own scan */
+       os_memset(&event, 0, sizeof(event));
+       info = &event.scan_info;
+       info->external_scan = 0;
+       info->nl_scan_event = 0;
+
+       drv->scan_state = SCAN_STARTED;
+       wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event);
+}
+
+
+static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv,
+                                  int aborted, struct nlattr *tb[],
+                                  int external_scan)
+{
+       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;
+       info->external_scan = external_scan;
+
+       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[300], *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;
+       int external_scan;
+
+       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 */
+               external_scan = 1;
+       } else {
+               external_scan = 0;
+               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;
+               drv->last_scan_cmd = 0;
+       }
+
+       send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb,
+                              external_scan);
+}
+
+
+static void qca_nl80211_p2p_lo_stop_event(struct wpa_driver_nl80211_data *drv,
+                                         u8 *data, size_t len)
+{
+       struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1];
+       union wpa_event_data event;
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: P2P listen offload stop vendor event received");
+
+       if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX,
+                     (struct nlattr *) data, len, NULL) ||
+           !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON])
+               return;
+
+       os_memset(&event, 0, sizeof(event));
+       event.p2p_lo_stop.reason_code =
+               nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON]);
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: P2P Listen offload stop reason: %d",
+                  event.p2p_lo_stop.reason_code);
+       wpa_supplicant_event(drv->ctx, EVENT_P2P_LO_STOP, &event);
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
 static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
                                     u32 subcmd, u8 *data, size_t len)
 {
        switch (subcmd) {
+       case QCA_NL80211_VENDOR_SUBCMD_TEST:
+               wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len);
+               break;
+#ifdef CONFIG_DRIVER_NL80211_QCA
        case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
                qca_nl80211_avoid_freq(drv, data, len);
                break;
@@ -1565,6 +1921,23 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
        case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
                qca_nl80211_acs_select_ch(drv, data, len);
                break;
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+       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;
+       case QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP:
+               qca_nl80211_p2p_lo_stop_event(drv, data, len);
+               break;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
        default:
                wpa_printf(MSG_DEBUG,
                           "nl80211: Ignore unsupported QCA vendor event %u",
@@ -1687,6 +2060,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
        union wpa_event_data data;
+       int external_scan_event = 0;
 
        wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
                   cmd, nl80211_command_to_string(cmd), bss->ifname);
@@ -1739,28 +2113,38 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
        case NL80211_CMD_NEW_SCAN_RESULTS:
                wpa_dbg(drv->ctx, MSG_DEBUG,
                        "nl80211: New scan results available");
-               drv->scan_state = SCAN_COMPLETED;
                drv->scan_complete_events = 1;
-               eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
-                                    drv->ctx);
-               send_scan_event(drv, 0, tb);
+               if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+                       drv->scan_state = SCAN_COMPLETED;
+                       eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+                                            drv, drv->ctx);
+                       drv->last_scan_cmd = 0;
+               } else {
+                       external_scan_event = 1;
+               }
+               send_scan_event(drv, 0, tb, external_scan_event);
                break;
        case NL80211_CMD_SCHED_SCAN_RESULTS:
                wpa_dbg(drv->ctx, MSG_DEBUG,
                        "nl80211: New sched scan results available");
                drv->scan_state = SCHED_SCAN_RESULTS;
-               send_scan_event(drv, 0, tb);
+               send_scan_event(drv, 0, tb, 0);
                break;
        case NL80211_CMD_SCAN_ABORTED:
                wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
-               drv->scan_state = SCAN_ABORTED;
-               /*
-                * Need to indicate that scan results are available in order
-                * not to make wpa_supplicant stop its scanning.
-                */
-               eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
-                                    drv->ctx);
-               send_scan_event(drv, 1, tb);
+               if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+                       drv->scan_state = SCAN_ABORTED;
+                       /*
+                        * Need to indicate that scan results are available in
+                        * order not to make wpa_supplicant stop its scanning.
+                        */
+                       eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+                                            drv, drv->ctx);
+                       drv->last_scan_cmd = 0;
+               } else {
+                       external_scan_event = 1;
+               }
+               send_scan_event(drv, 1, tb, external_scan_event);
                break;
        case NL80211_CMD_AUTHENTICATE:
        case NL80211_CMD_ASSOCIATE:
@@ -1783,7 +2167,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
                                   tb[NL80211_ATTR_MAC],
                                   tb[NL80211_ATTR_REQ_IE],
                                   tb[NL80211_ATTR_RESP_IE],
-                                  NULL, NULL, NULL, NULL);
+                                  tb[NL80211_ATTR_TIMED_OUT],
+                                  NULL, NULL, NULL, NULL, NULL);
                break;
        case NL80211_CMD_CH_SWITCH_NOTIFY:
                mlme_event_ch_switch(drv,
@@ -1828,7 +2213,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
                nl80211_new_station_event(drv, bss, tb);
                break;
        case NL80211_CMD_DEL_STATION:
-               nl80211_del_station_event(drv, tb);
+               nl80211_del_station_event(drv, bss, tb);
                break;
        case NL80211_CMD_SET_REKEY_OFFLOAD:
                nl80211_rekey_offload_event(drv, tb);
@@ -1868,46 +2253,6 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
 }
 
 
-int process_drv_event(struct nl_msg *msg, void *arg)
-{
-       struct wpa_driver_nl80211_data *drv = arg;
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct i802_bss *bss;
-       int ifidx = -1;
-
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
-
-       if (tb[NL80211_ATTR_IFINDEX]) {
-               ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
-
-               for (bss = drv->first_bss; bss; bss = bss->next)
-                       if (ifidx == -1 || ifidx == bss->ifindex) {
-                               do_process_drv_event(bss, gnlh->cmd, tb);
-                               return NL_SKIP;
-                       }
-               wpa_printf(MSG_DEBUG,
-                          "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d)",
-                          gnlh->cmd, ifidx);
-       } else if (tb[NL80211_ATTR_WDEV]) {
-               u64 wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
-               wpa_printf(MSG_DEBUG, "nl80211: Process event on P2P device");
-               for (bss = drv->first_bss; bss; bss = bss->next) {
-                       if (bss->wdev_id_set && wdev_id == bss->wdev_id) {
-                               do_process_drv_event(bss, gnlh->cmd, tb);
-                               return NL_SKIP;
-                       }
-               }
-               wpa_printf(MSG_DEBUG,
-                          "nl80211: Ignored event (cmd=%d) for foreign interface (wdev 0x%llx)",
-                          gnlh->cmd, (long long unsigned int) wdev_id);
-       }
-
-       return NL_SKIP;
-}
-
-
 int process_global_event(struct nl_msg *msg, void *arg)
 {
        struct nl80211_global *global = arg;
@@ -1940,6 +2285,9 @@ int process_global_event(struct nl_msg *msg, void *arg)
                                return NL_SKIP;
                        }
                }
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d wdev 0x%llx)",
+                          gnlh->cmd, ifidx, (long long unsigned int) wdev_id);
        }
 
        return NL_SKIP;