QCA vendor command support to set band to driver
[mech_eap.git] / src / drivers / driver_nl80211.c
index 2c4c0b3..a6441e3 100644 (file)
@@ -87,7 +87,6 @@ static void nl80211_handle_destroy(struct nl_handle *handle)
 #undef nl_socket_set_nonblocking
 #define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h)
 
-#define genl_ctrl_resolve android_genl_ctrl_resolve
 #endif /* ANDROID */
 
 
@@ -164,6 +163,7 @@ static void nl80211_destroy_eloop_handle(struct nl_handle **handle)
 
 
 static void nl80211_global_deinit(void *priv);
+static void nl80211_check_global(struct nl80211_global *global);
 
 static void wpa_driver_nl80211_deinit(struct i802_bss *bss);
 static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
@@ -864,6 +864,7 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
                return 1;
 
        if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
+               nl80211_check_global(drv->global);
                wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
                           "interface");
                wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL);
@@ -1185,6 +1186,7 @@ static int get_link_signal(struct nl_msg *msg, void *arg)
        static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
                [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
                [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
+               [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8 },
        };
        struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
        static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
@@ -1213,6 +1215,13 @@ static int get_link_signal(struct nl_msg *msg, void *arg)
        else
                sig_change->avg_signal = 0;
 
+       if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
+               sig_change->avg_beacon_signal =
+                       (s8)
+                       nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
+       else
+               sig_change->avg_beacon_signal = 0;
+
        if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
                if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
                                     sinfo[NL80211_STA_INFO_TX_BITRATE],
@@ -1481,6 +1490,33 @@ err:
 }
 
 
+static void nl80211_check_global(struct nl80211_global *global)
+{
+       struct nl_handle *handle;
+       const char *groups[] = { "scan", "mlme", "regulatory", "vendor", NULL };
+       int ret;
+       unsigned int i;
+
+       /*
+        * Try to re-add memberships to handle case of cfg80211 getting reloaded
+        * and all registration having been cleared.
+        */
+       handle = (void *) (((intptr_t) global->nl_event) ^
+                          ELOOP_SOCKET_INVALID);
+
+       for (i = 0; groups[i]; i++) {
+               ret = nl_get_multicast_id(global, "nl80211", groups[i]);
+               if (ret >= 0)
+                       ret = nl_socket_add_membership(handle, ret);
+               if (ret < 0) {
+                       wpa_printf(MSG_INFO,
+                                  "nl80211: Could not re-add multicast membership for %s events: %d (%s)",
+                                  groups[i], ret, strerror(-ret));
+               }
+       }
+}
+
+
 static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
 {
        wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
@@ -1674,6 +1710,7 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
        }
 
        if (drv->global) {
+               nl80211_check_global(drv->global);
                dl_list_add(&drv->global->interfaces, &drv->list);
                drv->in_interface_list = 1;
        }
@@ -1841,6 +1878,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
                        ret = -1;
        }
 #endif /* CONFIG_TDLS */
+#ifdef CONFIG_FST
+       /* FST Action frames */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
+               ret = -1;
+#endif /* CONFIG_FST */
 
        /* FT Action frames */
        if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
@@ -2463,7 +2505,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
        int ifindex;
-       struct nl_msg *msg;
+       struct nl_msg *msg = NULL;
        int ret;
        int tdls = 0;
 
@@ -2496,11 +2538,15 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
                if (!msg)
                        return -ENOBUFS;
        } else {
+               u32 suite;
+
+               suite = wpa_alg_to_cipher_suite(alg, key_len);
+               if (!suite)
+                       goto fail;
                msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY);
                if (!msg ||
                    nla_put(msg, NL80211_ATTR_KEY_DATA, key_len, key) ||
-                   nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER,
-                               wpa_alg_to_cipher_suite(alg, key_len)))
+                   nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER, suite))
                        goto fail;
                wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len);
        }
@@ -2602,9 +2648,15 @@ static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
                      const u8 *key, size_t key_len)
 {
        struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY);
+       u32 suite;
+
        if (!key_attr)
                return -1;
 
+       suite = wpa_alg_to_cipher_suite(alg, key_len);
+       if (!suite)
+               return -1;
+
        if (defkey && alg == WPA_ALG_IGTK) {
                if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT))
                        return -1;
@@ -2614,8 +2666,7 @@ static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
        }
 
        if (nla_put_u8(msg, NL80211_KEY_IDX, key_idx) ||
-           nla_put_u32(msg, NL80211_KEY_CIPHER,
-                       wpa_alg_to_cipher_suite(alg, key_len)) ||
+           nla_put_u32(msg, NL80211_KEY_CIPHER, suite) ||
            (seq && seq_len &&
             nla_put(msg, NL80211_KEY_SEQ, seq_len, seq)) ||
            nla_put(msg, NL80211_KEY_DATA, key_len, key))
@@ -4203,8 +4254,9 @@ static int wpa_driver_nl80211_hapd_send_eapol(
 
 
 static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
-                                           int total_flags,
-                                           int flags_or, int flags_and)
+                                           unsigned int total_flags,
+                                           unsigned int flags_or,
+                                           unsigned int flags_and)
 {
        struct i802_bss *bss = priv;
        struct nl_msg *msg;
@@ -4271,7 +4323,8 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
                return -1;
        }
 
-       if (nl80211_set_channel(drv->first_bss, &params->freq, 0)) {
+       if (params->freq.freq &&
+           nl80211_set_channel(drv->first_bss, &params->freq, 0)) {
                if (old_mode != nlmode)
                        wpa_driver_nl80211_set_mode(drv->first_bss, old_mode);
                nl80211_remove_monitor_interface(drv);
@@ -5627,8 +5680,8 @@ static void *i802_init(struct hostapd_data *hapd,
        struct wpa_driver_nl80211_data *drv;
        struct i802_bss *bss;
        size_t i;
-       char brname[IFNAMSIZ];
-       int ifindex, br_ifindex;
+       char master_ifname[IFNAMSIZ];
+       int ifindex, br_ifindex = 0;
        int br_added = 0;
 
        bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
@@ -5639,15 +5692,21 @@ static void *i802_init(struct hostapd_data *hapd,
 
        drv = bss->drv;
 
-       if (linux_br_get(brname, params->ifname) == 0) {
+       if (linux_br_get(master_ifname, params->ifname) == 0) {
                wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
-                          params->ifname, brname);
-               br_ifindex = if_nametoindex(brname);
-               os_strlcpy(bss->brname, brname, IFNAMSIZ);
+                          params->ifname, master_ifname);
+               br_ifindex = if_nametoindex(master_ifname);
+               os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+       } else if ((params->num_bridge == 0 || !params->bridge[0]) &&
+                  linux_master_get(master_ifname, params->ifname) == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s",
+                       params->ifname, master_ifname);
+               /* start listening for EAPOL on the master interface */
+               add_ifidx(drv, if_nametoindex(master_ifname));
        } else {
-               brname[0] = '\0';
-               br_ifindex = 0;
+               master_ifname[0] = '\0';
        }
+
        bss->br_ifindex = br_ifindex;
 
        for (i = 0; i < params->num_bridge; i++) {
@@ -5667,7 +5726,7 @@ static void *i802_init(struct hostapd_data *hapd,
                if (i802_check_bridge(drv, bss, params->bridge[0],
                                      params->ifname) < 0)
                        goto failed;
-               if (os_strcmp(params->bridge[0], brname) != 0)
+               if (os_strcmp(params->bridge[0], master_ifname) != 0)
                        br_added = 1;
        }
 
@@ -5750,8 +5809,6 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type(
 }
 
 
-#if defined(CONFIG_P2P) || defined(CONFIG_MESH)
-
 static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
 {
        struct wpa_driver_nl80211_data *drv;
@@ -5787,8 +5844,6 @@ static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr)
        return 0;
 }
 
-#endif /* CONFIG_P2P || CONFIG_MESH */
-
 
 struct wdev_info {
        u64 wdev_id;
@@ -5864,21 +5919,21 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
        }
 
        if (!addr) {
-               if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+               if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
                        os_memcpy(if_addr, bss->addr, ETH_ALEN);
                else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
-                                           bss->ifname, if_addr) < 0) {
+                                           ifname, if_addr) < 0) {
                        if (added)
                                nl80211_remove_iface(drv, ifidx);
                        return -1;
                }
        }
 
-#if defined(CONFIG_P2P) || defined(CONFIG_MESH)
        if (!addr &&
            (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
-            type == WPA_IF_P2P_GO || type == WPA_IF_MESH)) {
-               /* Enforce unique P2P Interface Address */
+            type == WPA_IF_P2P_GO || type == WPA_IF_MESH ||
+            type == WPA_IF_STATION)) {
+               /* Enforce unique address */
                u8 new_addr[ETH_ALEN];
 
                if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
@@ -5889,8 +5944,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
                }
                if (nl80211_addr_in_use(drv->global, new_addr)) {
                        wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
-                                  "for %s interface", type == WPA_IF_MESH ?
-                                  "mesh" : "P2P group");
+                                  "for interface %s type %d", ifname, type);
                        if (nl80211_vif_addr(drv, new_addr) < 0) {
                                if (added)
                                        nl80211_remove_iface(drv, ifidx);
@@ -5905,7 +5959,6 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
                }
                os_memcpy(if_addr, new_addr, ETH_ALEN);
        }
-#endif /* CONFIG_P2P || CONFIG_MESH */
 
        if (type == WPA_IF_AP_BSS) {
                struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
@@ -6483,47 +6536,6 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
 }
 
 
-static int wpa_driver_nl80211_shared_freq(void *priv)
-{
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct wpa_driver_nl80211_data *driver;
-       int freq = 0;
-
-       /*
-        * If the same PHY is in connected state with some other interface,
-        * then retrieve the assoc freq.
-        */
-       wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s",
-                  drv->phyname);
-
-       dl_list_for_each(driver, &drv->global->interfaces,
-                        struct wpa_driver_nl80211_data, list) {
-               if (drv == driver ||
-                   os_strcmp(drv->phyname, driver->phyname) != 0 ||
-                   !driver->associated)
-                       continue;
-
-               wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s "
-                          MACSTR,
-                          driver->phyname, driver->first_bss->ifname,
-                          MAC2STR(driver->first_bss->addr));
-               if (is_ap_interface(driver->nlmode))
-                       freq = driver->first_bss->freq;
-               else
-                       freq = nl80211_get_assoc_freq(driver);
-               wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d",
-                          drv->phyname, freq);
-       }
-
-       if (!freq)
-               wpa_printf(MSG_DEBUG, "nl80211: No shared interface for "
-                          "PHY (%s) in associated state", drv->phyname);
-
-       return freq;
-}
-
-
 static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
                              int encrypt)
 {
@@ -7244,11 +7256,12 @@ static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type,
 
 
 static int driver_nl80211_send_mlme(void *priv, const u8 *data,
-                                   size_t data_len, int noack)
+                                   size_t data_len, int noack,
+                                   unsigned int freq)
 {
        struct i802_bss *bss = priv;
        return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
-                                           0, 0, 0, 0);
+                                           freq, 0, 0, 0);
 }
 
 
@@ -7806,7 +7819,7 @@ static int nl80211_set_wowlan(void *priv,
 
        wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan");
 
-       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WOWLAN)) ||
+       if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_WOWLAN)) ||
            !(wowlan_triggers = nla_nest_start(msg,
                                               NL80211_ATTR_WOWLAN_TRIGGERS)) ||
            (triggers->any &&
@@ -8274,7 +8287,7 @@ static const char * drv_br_port_attr_str(enum drv_br_port_attr attr)
 {
        switch (attr) {
        case DRV_BR_PORT_ATTR_PROXYARP:
-               return "proxyarp";
+               return "proxyarp_wifi";
        case DRV_BR_PORT_ATTR_HAIRPIN_MODE:
                return "hairpin_mode";
        }
@@ -8309,9 +8322,9 @@ static const char * drv_br_net_param_str(enum drv_br_net_param param)
        switch (param) {
        case DRV_BR_NET_PARAM_GARP_ACCEPT:
                return "arp_accept";
+       default:
+               return NULL;
        }
-
-       return NULL;
 }
 
 
@@ -8323,6 +8336,13 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
        const char *param_txt;
        int ip_version = 4;
 
+       if (param == DRV_BR_MULTICAST_SNOOPING) {
+               os_snprintf(path, sizeof(path),
+                           "/sys/devices/virtual/net/%s/bridge/multicast_snooping",
+                           bss->brname);
+               goto set_val;
+       }
+
        param_txt = drv_br_net_param_str(param);
        if (param_txt == NULL)
                return -EINVAL;
@@ -8338,6 +8358,7 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
        os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
                    ip_version, bss->brname, param_txt);
 
+set_val:
        if (linux_write_system_file(path, val))
                return -1;
 
@@ -8356,6 +8377,8 @@ static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
                return QCA_ACS_MODE_IEEE80211A;
        case HOSTAPD_MODE_IEEE80211AD:
                return QCA_ACS_MODE_IEEE80211AD;
+       case HOSTAPD_MODE_IEEE80211ANY:
+               return QCA_ACS_MODE_IEEE80211ANY;
        default:
                return -1;
        }
@@ -8384,12 +8407,24 @@ static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
            (params->ht_enabled &&
             nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) ||
            (params->ht40_enabled &&
-            nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED))) {
+            nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED)) ||
+           (params->vht_enabled &&
+            nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) ||
+           nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
+                       params->ch_width) ||
+           (params->ch_list_len &&
+            nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len,
+                    params->ch_list))) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
        nla_nest_end(msg, data);
 
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d CH_LIST_LEN: %u",
+                  params->hw_mode, params->ht_enabled, params->ht40_enabled,
+                  params->vht_enabled, params->ch_width, params->ch_list_len);
+
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        if (ret) {
                wpa_printf(MSG_DEBUG,
@@ -8400,6 +8435,53 @@ static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
 }
 
 
+static int nl80211_set_band(void *priv, enum set_band band)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nlattr *data;
+       int ret;
+       enum qca_set_band qca_band;
+
+       if (!drv->setband_vendor_cmd_avail)
+               return -1;
+
+       switch (band) {
+       case WPA_SETBAND_AUTO:
+               qca_band = QCA_SETBAND_AUTO;
+               break;
+       case WPA_SETBAND_5G:
+               qca_band = QCA_SETBAND_5G;
+               break;
+       case WPA_SETBAND_2G:
+               qca_band = QCA_SETBAND_2G;
+               break;
+       default:
+               return -1;
+       }
+
+       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_SETBAND) ||
+           !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+           nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE, qca_band)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
+       nla_nest_end(msg, data);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Driver setband function failed: %s",
+                          strerror(errno));
+       }
+       return ret;
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -8459,7 +8541,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .signal_monitor = nl80211_signal_monitor,
        .signal_poll = nl80211_signal_poll,
        .send_frame = nl80211_send_frame,
-       .shared_freq = wpa_driver_nl80211_shared_freq,
        .set_param = nl80211_set_param,
        .get_radio_name = nl80211_get_radio_name,
        .add_pmkid = nl80211_add_pmkid,
@@ -8487,7 +8568,9 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie,
 #endif /* ANDROID_P2P */
 #ifdef ANDROID
+#ifndef ANDROID_LIB_STUB
        .driver_cmd = wpa_driver_nl80211_driver_cmd,
+#endif /* !ANDROID_LIB_STUB */
 #endif /* ANDROID */
        .vendor_cmd = nl80211_vendor_cmd,
        .set_qos_map = nl80211_set_qos_map,
@@ -8506,4 +8589,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .add_tx_ts = nl80211_add_ts,
        .del_tx_ts = nl80211_del_ts,
        .do_acs = wpa_driver_do_acs,
+       .set_band = nl80211_set_band,
 };