nl80211: Add QCA vendor specific query of device/driver features
[mech_eap.git] / src / drivers / driver_nl80211_event.c
index 7ba8168..f422174 100644 (file)
@@ -129,6 +129,8 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd)
        C2S(NL80211_CMD_CHANNEL_SWITCH)
        C2S(NL80211_CMD_VENDOR)
        C2S(NL80211_CMD_SET_QOS_MAP)
+       C2S(NL80211_CMD_ADD_TX_TS)
+       C2S(NL80211_CMD_DEL_TX_TS)
        default:
                return "NL80211_CMD_UNKNOWN";
        }
@@ -178,8 +180,39 @@ 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)
+{
+       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;
+
+       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;
+}
+
+
 static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
-                           const u8 *frame, size_t len)
+                           const u8 *frame, size_t len, struct nlattr *wmm)
 {
        const struct ieee80211_mgmt *mgmt;
        union wpa_event_data event;
@@ -233,6 +266,8 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
 
        event.assoc_info.freq = drv->assoc_freq;
 
+       nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params);
+
        wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -247,6 +282,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
                               struct nlattr *ptk_kek)
 {
        union wpa_event_data event;
+       u16 status_code;
 
        if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
                /*
@@ -258,21 +294,41 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
                return;
        }
 
-       if (cmd == NL80211_CMD_CONNECT)
-               wpa_printf(MSG_DEBUG, "nl80211: Connect event");
-       else if (cmd == NL80211_CMD_ROAM)
+       status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS;
+
+       if (cmd == NL80211_CMD_CONNECT) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)",
+                          status_code, drv->ignore_next_local_disconnect);
+       } else if (cmd == NL80211_CMD_ROAM) {
                wpa_printf(MSG_DEBUG, "nl80211: Roam event");
+       }
 
        os_memset(&event, 0, sizeof(event));
-       if (cmd == NL80211_CMD_CONNECT &&
-           nla_get_u16(status) != WLAN_STATUS_SUCCESS) {
+       if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) {
                if (addr)
                        event.assoc_reject.bssid = nla_data(addr);
+               if (drv->ignore_next_local_disconnect) {
+                       drv->ignore_next_local_disconnect = 0;
+                       if (!event.assoc_reject.bssid ||
+                           (os_memcmp(event.assoc_reject.bssid,
+                                      drv->auth_attempt_bssid,
+                                      ETH_ALEN) != 0)) {
+                               /*
+                                * Ignore the event that came without a BSSID or
+                                * for the old connection since this is likely
+                                * not relevant to the new Connect command.
+                                */
+                               wpa_printf(MSG_DEBUG,
+                                          "nl80211: Ignore connection failure event triggered during reassociation");
+                               return;
+                       }
+               }
                if (resp_ie) {
                        event.assoc_reject.resp_ies = nla_data(resp_ie);
                        event.assoc_reject.resp_ies_len = nla_len(resp_ie);
                }
-               event.assoc_reject.status_code = nla_get_u16(status);
+               event.assoc_reject.status_code = status_code;
                wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
                return;
        }
@@ -304,7 +360,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
        }
        if (ptk_kck) {
                event.assoc_info.ptk_kck = nla_data(ptk_kck);
-               event.assoc_info.ptk_kck_len = nla_len(ptk_kek);
+               event.assoc_info.ptk_kck_len = nla_len(ptk_kck);
        }
        if (ptk_kek) {
                event.assoc_info.ptk_kek = nla_data(ptk_kek);
@@ -505,8 +561,9 @@ static void mlme_event_mgmt(struct i802_bss *bss,
        }
        wpa_printf(MSG_DEBUG,
                   "nl80211: RX frame sa=" MACSTR
-                  " freq=%d ssi_signal=%d stype=%u (%s) len=%u",
-                  MAC2STR(mgmt->sa), rx_freq, ssi_signal, stype, fc2str(fc),
+                  " 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,
+                  le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc),
                   (unsigned int) len);
        event.rx_mgmt.frame = frame;
        event.rx_mgmt.frame_len = len;
@@ -691,7 +748,8 @@ static void mlme_event(struct i802_bss *bss,
                       enum nl80211_commands cmd, struct nlattr *frame,
                       struct nlattr *addr, struct nlattr *timed_out,
                       struct nlattr *freq, struct nlattr *ack,
-                      struct nlattr *cookie, struct nlattr *sig)
+                      struct nlattr *cookie, struct nlattr *sig,
+                      struct nlattr *wmm)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
        const u8 *data;
@@ -738,7 +796,7 @@ static void mlme_event(struct i802_bss *bss,
                mlme_event_auth(drv, nla_data(frame), nla_len(frame));
                break;
        case NL80211_CMD_ASSOCIATE:
-               mlme_event_assoc(drv, nla_data(frame), nla_len(frame));
+               mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm);
                break;
        case NL80211_CMD_DEAUTHENTICATE:
                mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
@@ -958,7 +1016,7 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
                        freqs[num_freqs] = nla_get_u32(nl);
                        res = os_snprintf(pos, end - pos, " %d",
                                          freqs[num_freqs]);
-                       if (res > 0 && end - pos > res)
+                       if (!os_snprintf_error(end - pos, res))
                                pos += res;
                        num_freqs++;
                        if (num_freqs == MAX_REPORT_FREQS - 1)
@@ -1433,6 +1491,33 @@ static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
+                                  const u8 *data, size_t len)
+{
+       struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1];
+       union wpa_event_data event;
+
+       wpa_printf(MSG_DEBUG,
+                  "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] ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL])
+               return;
+
+       os_memset(&event, 0, sizeof(event));
+       event.acs_selected_channels.pri_channel =
+               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]);
+
+       wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event);
+}
+
+
 static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
                                      const u8 *data, size_t len)
 {
@@ -1476,6 +1561,9 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
        case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH:
                qca_nl80211_key_mgmt_auth(drv, data, len);
                break;
+       case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+               qca_nl80211_acs_select_ch(drv, data, len);
+               break;
        default:
                wpa_printf(MSG_DEBUG,
                           "nl80211: Ignore unsupported QCA vendor event %u",
@@ -1602,7 +1690,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
        wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
                   cmd, nl80211_command_to_string(cmd), bss->ifname);
 
-       if (cmd == NL80211_CMD_ROAM && drv->roam_auth_vendor_event_avail) {
+       if (cmd == NL80211_CMD_ROAM &&
+           (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
                /*
                 * Device will use roam+auth vendor event to indicate
                 * roaming, so ignore the regular roam event.
@@ -1683,7 +1772,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
                           tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
                           tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
                           tb[NL80211_ATTR_COOKIE],
-                          tb[NL80211_ATTR_RX_SIGNAL_DBM]);
+                          tb[NL80211_ATTR_RX_SIGNAL_DBM],
+                          tb[NL80211_ATTR_STA_WME]);
                break;
        case NL80211_CMD_CONNECT:
        case NL80211_CMD_ROAM:
@@ -1875,7 +1965,8 @@ int process_bss_event(struct nl_msg *msg, void *arg)
                           tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
                           tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
                           tb[NL80211_ATTR_COOKIE],
-                          tb[NL80211_ATTR_RX_SIGNAL_DBM]);
+                          tb[NL80211_ATTR_RX_SIGNAL_DBM],
+                          tb[NL80211_ATTR_STA_WME]);
                break;
        case NL80211_CMD_UNEXPECTED_FRAME:
                nl80211_spurious_frame(bss, tb, 0);