Track whether scan was started by us or an external program
[mech_eap.git] / src / drivers / driver_nl80211.c
index eaca172..a782c11 100644 (file)
@@ -504,6 +504,27 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd)
 }
 
 
+/* Converts nl80211_chan_width to a common format */
+static enum chan_width convert2width(int width)
+{
+       switch (width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               return CHAN_WIDTH_20_NOHT;
+       case NL80211_CHAN_WIDTH_20:
+               return CHAN_WIDTH_20;
+       case NL80211_CHAN_WIDTH_40:
+               return CHAN_WIDTH_40;
+       case NL80211_CHAN_WIDTH_80:
+               return CHAN_WIDTH_80;
+       case NL80211_CHAN_WIDTH_80P80:
+               return CHAN_WIDTH_80P80;
+       case NL80211_CHAN_WIDTH_160:
+               return CHAN_WIDTH_160;
+       }
+       return CHAN_WIDTH_UNKNOWN;
+}
+
+
 static int is_ap_interface(enum nl80211_iftype nlmode)
 {
        return (nlmode == NL80211_IFTYPE_AP ||
@@ -1484,36 +1505,60 @@ static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
 
 
 static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
-                                struct nlattr *freq, struct nlattr *type)
+                                struct nlattr *ifindex, struct nlattr *freq,
+                                struct nlattr *type, struct nlattr *bw,
+                                struct nlattr *cf1, struct nlattr *cf2)
 {
+       struct i802_bss *bss;
        union wpa_event_data data;
        int ht_enabled = 1;
        int chan_offset = 0;
+       int ifidx;
 
        wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
 
-       if (!freq || !type)
+       if (!freq)
                return;
 
-       switch (nla_get_u32(type)) {
-       case NL80211_CHAN_NO_HT:
-               ht_enabled = 0;
-               break;
-       case NL80211_CHAN_HT20:
-               break;
-       case NL80211_CHAN_HT40PLUS:
-               chan_offset = 1;
-               break;
-       case NL80211_CHAN_HT40MINUS:
-               chan_offset = -1;
-               break;
+       ifidx = nla_get_u32(ifindex);
+       for (bss = drv->first_bss; bss; bss = bss->next)
+               if (bss->ifindex == ifidx)
+                       break;
+
+       if (bss == NULL) {
+               wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring",
+                          ifidx);
+               return;
        }
 
+       if (type) {
+               switch (nla_get_u32(type)) {
+               case NL80211_CHAN_NO_HT:
+                       ht_enabled = 0;
+                       break;
+               case NL80211_CHAN_HT20:
+                       break;
+               case NL80211_CHAN_HT40PLUS:
+                       chan_offset = 1;
+                       break;
+               case NL80211_CHAN_HT40MINUS:
+                       chan_offset = -1;
+                       break;
+               }
+       }
+
+       os_memset(&data, 0, sizeof(data));
        data.ch_switch.freq = nla_get_u32(freq);
        data.ch_switch.ht_enabled = ht_enabled;
        data.ch_switch.ch_offset = chan_offset;
+       if (bw)
+               data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
+       if (cf1)
+               data.ch_switch.cf1 = nla_get_u32(cf1);
+       if (cf2)
+               data.ch_switch.cf2 = nla_get_u32(cf2);
 
-       drv->first_bss->freq = data.ch_switch.freq;
+       bss->freq = data.ch_switch.freq;
 
        wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data);
 }
@@ -1992,21 +2037,36 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
                                &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[NL80211_ATTR_SCAN_FREQUENCIES]) {
+               char msg[200], *pos, *end;
+               int res;
+
+               pos = msg;
+               end = pos + sizeof(msg);
+               *pos = '\0';
+
                nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
                {
                        freqs[num_freqs] = nla_get_u32(nl);
+                       res = os_snprintf(pos, end - pos, " %d",
+                                         freqs[num_freqs]);
+                       if (res > 0 && 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);
 }
@@ -2534,8 +2594,6 @@ static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
 }
 
 
-static enum chan_width convert2width(int width);
-
 static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
                                struct nlattr **tb)
 {
@@ -2644,6 +2702,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
        case NL80211_CMD_TRIGGER_SCAN:
                wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger");
                drv->scan_state = SCAN_STARTED;
+               wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
                break;
        case NL80211_CMD_START_SCHED_SCAN:
                wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started");
@@ -2702,8 +2761,13 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
                                   tb[NL80211_ATTR_RESP_IE]);
                break;
        case NL80211_CMD_CH_SWITCH_NOTIFY:
-               mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ],
-                                    tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+               mlme_event_ch_switch(drv,
+                                    tb[NL80211_ATTR_IFINDEX],
+                                    tb[NL80211_ATTR_WIPHY_FREQ],
+                                    tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
+                                    tb[NL80211_ATTR_CHANNEL_WIDTH],
+                                    tb[NL80211_ATTR_CENTER_FREQ1],
+                                    tb[NL80211_ATTR_CENTER_FREQ2]);
                break;
        case NL80211_CMD_DISCONNECT:
                mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
@@ -3057,6 +3121,7 @@ struct wiphy_info_data {
        unsigned int p2p_client_supported:1;
        unsigned int p2p_concurrent:1;
        unsigned int channel_switch_supported:1;
+       unsigned int set_qos_map_supported:1;
 };
 
 
@@ -3217,6 +3282,65 @@ static void wiphy_info_supp_cmds(struct wiphy_info_data *info,
                case NL80211_CMD_CHANNEL_SWITCH:
                        info->channel_switch_supported = 1;
                        break;
+               case NL80211_CMD_SET_QOS_MAP:
+                       info->set_qos_map_supported = 1;
+                       break;
+               }
+       }
+}
+
+
+static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
+                                    struct nlattr *tb)
+{
+       int i, num;
+       u32 *ciphers;
+
+       if (tb == NULL)
+               return;
+
+       num = nla_len(tb) / sizeof(u32);
+       ciphers = nla_data(tb);
+       for (i = 0; i < num; i++) {
+               u32 c = ciphers[i];
+
+               wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d",
+                          c >> 24, (c >> 16) & 0xff,
+                          (c >> 8) & 0xff, c & 0xff);
+               switch (c) {
+               case WLAN_CIPHER_SUITE_CCMP_256:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256;
+                       break;
+               case WLAN_CIPHER_SUITE_GCMP_256:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256;
+                       break;
+               case WLAN_CIPHER_SUITE_CCMP:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+                       break;
+               case WLAN_CIPHER_SUITE_GCMP:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP;
+                       break;
+               case WLAN_CIPHER_SUITE_TKIP:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+                       break;
+               case WLAN_CIPHER_SUITE_WEP104:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104;
+                       break;
+               case WLAN_CIPHER_SUITE_WEP40:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40;
+                       break;
+               case WLAN_CIPHER_SUITE_AES_CMAC:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP;
+                       break;
+               case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128;
+                       break;
+               case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256;
+                       break;
+               case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256;
+                       break;
                }
        }
 }
@@ -3321,6 +3445,7 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
        wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]);
        wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
        wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
+       wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
 
        if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
                wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
@@ -3448,15 +3573,10 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
                return -1;
 
        drv->has_capability = 1;
-       /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
        drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
                WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
                WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
                WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
-       drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 |
-               WPA_DRIVER_CAPA_ENC_WEP104 |
-               WPA_DRIVER_CAPA_ENC_TKIP |
-               WPA_DRIVER_CAPA_ENC_CCMP;
        drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
                WPA_DRIVER_AUTH_SHARED |
                WPA_DRIVER_AUTH_LEAP;
@@ -3479,6 +3599,8 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
        drv->poll_command_supported = info.poll_command_supported;
        drv->data_tx_status = info.data_tx_status;
        drv->channel_switch_supported = info.channel_switch_supported;
+       if (info.set_qos_map_supported)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
 
        /*
         * If poll command and tx status are supported, mac80211 is new enough
@@ -5126,10 +5248,30 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
                        NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
                                    WLAN_CIPHER_SUITE_GCMP);
                        break;
+               case WPA_ALG_CCMP_256:
+                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_CCMP_256);
+                       break;
+               case WPA_ALG_GCMP_256:
+                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_GCMP_256);
+                       break;
                case WPA_ALG_IGTK:
                        NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
                                    WLAN_CIPHER_SUITE_AES_CMAC);
                        break;
+               case WPA_ALG_BIP_GMAC_128:
+                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_BIP_GMAC_128);
+                       break;
+               case WPA_ALG_BIP_GMAC_256:
+                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_BIP_GMAC_256);
+                       break;
+               case WPA_ALG_BIP_CMAC_256:
+                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_BIP_CMAC_256);
+                       break;
                case WPA_ALG_SMS4:
                        NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
                                    WLAN_CIPHER_SUITE_SMS4);
@@ -5266,10 +5408,30 @@ static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
        case WPA_ALG_GCMP:
                NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_GCMP);
                break;
+       case WPA_ALG_CCMP_256:
+               NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+                           WLAN_CIPHER_SUITE_CCMP_256);
+               break;
+       case WPA_ALG_GCMP_256:
+               NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+                           WLAN_CIPHER_SUITE_GCMP_256);
+               break;
        case WPA_ALG_IGTK:
                NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
                            WLAN_CIPHER_SUITE_AES_CMAC);
                break;
+       case WPA_ALG_BIP_GMAC_128:
+               NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+                           WLAN_CIPHER_SUITE_BIP_GMAC_128);
+               break;
+       case WPA_ALG_BIP_GMAC_256:
+               NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+                           WLAN_CIPHER_SUITE_BIP_GMAC_256);
+               break;
+       case WPA_ALG_BIP_CMAC_256:
+               NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+                           WLAN_CIPHER_SUITE_BIP_CMAC_256);
+               break;
        default:
                wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
                           "algorithm %d", __func__, alg);
@@ -5736,10 +5898,8 @@ static void phy_info_freq(struct hostapd_hw_modes *mode,
 
        if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
                chan->flag |= HOSTAPD_CHAN_DISABLED;
-       if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
-               chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN;
-       if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
-               chan->flag |= HOSTAPD_CHAN_NO_IBSS;
+       if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
+               chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN | HOSTAPD_CHAN_NO_IBSS;
        if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
                chan->flag |= HOSTAPD_CHAN_RADAR;
 
@@ -5768,8 +5928,7 @@ static int phy_info_freqs(struct phy_info_arg *phy_info,
        static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
                [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
                [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
-               [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
-               [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
+               [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
                [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
                [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
                [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
@@ -6696,6 +6855,10 @@ static int wpa_driver_nl80211_set_ap(void *priv,
        wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
                   params->pairwise_ciphers);
        num_suites = 0;
+       if (params->pairwise_ciphers & WPA_CIPHER_CCMP_256)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256;
+       if (params->pairwise_ciphers & WPA_CIPHER_GCMP_256)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256;
        if (params->pairwise_ciphers & WPA_CIPHER_CCMP)
                suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
        if (params->pairwise_ciphers & WPA_CIPHER_GCMP)
@@ -6714,6 +6877,14 @@ static int wpa_driver_nl80211_set_ap(void *priv,
        wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x",
                   params->group_cipher);
        switch (params->group_cipher) {
+       case WPA_CIPHER_CCMP_256:
+               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
+                           WLAN_CIPHER_SUITE_CCMP_256);
+               break;
+       case WPA_CIPHER_GCMP_256:
+               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
+                           WLAN_CIPHER_SUITE_GCMP_256);
+               break;
        case WPA_CIPHER_CCMP:
                NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
                            WLAN_CIPHER_SUITE_CCMP);
@@ -8078,6 +8249,12 @@ skip_auth_type:
                case CIPHER_GCMP:
                        cipher = WLAN_CIPHER_SUITE_GCMP;
                        break;
+               case CIPHER_CCMP_256:
+                       cipher = WLAN_CIPHER_SUITE_CCMP_256;
+                       break;
+               case CIPHER_GCMP_256:
+                       cipher = WLAN_CIPHER_SUITE_GCMP_256;
+                       break;
                case CIPHER_TKIP:
                default:
                        cipher = WLAN_CIPHER_SUITE_TKIP;
@@ -8105,6 +8282,12 @@ skip_auth_type:
                case CIPHER_GCMP:
                        cipher = WLAN_CIPHER_SUITE_GCMP;
                        break;
+               case CIPHER_CCMP_256:
+                       cipher = WLAN_CIPHER_SUITE_CCMP_256;
+                       break;
+               case CIPHER_GCMP_256:
+                       cipher = WLAN_CIPHER_SUITE_GCMP_256;
+                       break;
                case CIPHER_TKIP:
                default:
                        cipher = WLAN_CIPHER_SUITE_TKIP;
@@ -8296,6 +8479,12 @@ static int wpa_driver_nl80211_associate(
                case CIPHER_GCMP:
                        cipher = WLAN_CIPHER_SUITE_GCMP;
                        break;
+               case CIPHER_CCMP_256:
+                       cipher = WLAN_CIPHER_SUITE_CCMP_256;
+                       break;
+               case CIPHER_GCMP_256:
+                       cipher = WLAN_CIPHER_SUITE_GCMP_256;
+                       break;
                case CIPHER_TKIP:
                default:
                        cipher = WLAN_CIPHER_SUITE_TKIP;
@@ -8321,6 +8510,12 @@ static int wpa_driver_nl80211_associate(
                case CIPHER_GCMP:
                        cipher = WLAN_CIPHER_SUITE_GCMP;
                        break;
+               case CIPHER_CCMP_256:
+                       cipher = WLAN_CIPHER_SUITE_CCMP_256;
+                       break;
+               case CIPHER_GCMP_256:
+                       cipher = WLAN_CIPHER_SUITE_GCMP_256;
+                       break;
                case CIPHER_TKIP:
                default:
                        cipher = WLAN_CIPHER_SUITE_TKIP;
@@ -9490,7 +9685,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
 
        wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d",
                   __func__, type, ifname, ifindex, bss->added_if);
-       if (ifindex > 0 && bss->added_if)
+       if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex))
                nl80211_remove_iface(drv, ifindex);
 
        if (type != WPA_IF_AP_BSS)
@@ -10020,27 +10215,6 @@ nla_put_failure:
 }
 
 
-/* Converts nl80211_chan_width to a common format */
-static enum chan_width convert2width(int width)
-{
-       switch (width) {
-       case NL80211_CHAN_WIDTH_20_NOHT:
-               return CHAN_WIDTH_20_NOHT;
-       case NL80211_CHAN_WIDTH_20:
-               return CHAN_WIDTH_20;
-       case NL80211_CHAN_WIDTH_40:
-               return CHAN_WIDTH_40;
-       case NL80211_CHAN_WIDTH_80:
-               return CHAN_WIDTH_80;
-       case NL80211_CHAN_WIDTH_80P80:
-               return CHAN_WIDTH_80P80;
-       case NL80211_CHAN_WIDTH_160:
-               return CHAN_WIDTH_160;
-       }
-       return CHAN_WIDTH_UNKNOWN;
-}
-
-
 static int get_channel_width(struct nl_msg *msg, void *arg)
 {
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -11288,9 +11462,11 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
        struct nlattr *beacon_csa;
        int ret = -ENOBUFS;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d)",
+       wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)",
                   settings->cs_count, settings->block_tx,
-                  settings->freq_params.freq);
+                  settings->freq_params.freq, settings->freq_params.bandwidth,
+                  settings->freq_params.center_freq1,
+                  settings->freq_params.center_freq2);
 
        if (!drv->channel_switch_supported) {
                wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command");
@@ -11368,6 +11544,37 @@ error:
 }
 
 
+static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set,
+                              u8 qos_map_set_len)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
+                   qos_map_set, qos_map_set_len);
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_QOS_MAP);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed");
+
+       return ret;
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -11456,4 +11663,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 #ifdef ANDROID
        .driver_cmd = wpa_driver_nl80211_driver_cmd,
 #endif /* ANDROID */
+       .set_qos_map = nl80211_set_qos_map,
 };