]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
nl80211: Add means to query preferred channels
authorAhmad Kholaif <akholaif@qca.qualcomm.com>
Thu, 23 Jul 2015 23:32:58 +0000 (16:32 -0700)
committerJouni Malinen <j@w1.fi>
Wed, 29 Jul 2015 16:46:31 +0000 (19:46 +0300)
Extend the QCA vendor specific nl80211 interface to query the preferred
frequency list from driver and add a new wpa_cli command to query this
information.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211.h
src/drivers/driver_nl80211_capa.c
wpa_supplicant/ctrl_iface.c
wpa_supplicant/driver_i.h
wpa_supplicant/wpa_cli.c

index d73e6fdcda0102c712c12ecdd2e4d209e6792395..3cdab5a7a87d61464e140c62da4dc5c182c683bc 100644 (file)
@@ -1404,6 +1404,16 @@ enum wpa_driver_if_type {
         * WPA_IF_MESH - Mesh interface
         */
        WPA_IF_MESH,
+
+       /*
+        * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
+        */
+       WPA_IF_TDLS,
+
+       /*
+        * WPA_IF_IBSS - IBSS interface (used for pref freq only)
+        */
+       WPA_IF_IBSS,
 };
 
 struct wpa_init_params {
@@ -3404,6 +3414,20 @@ struct wpa_driver_ops {
         */
        int (*set_band)(void *priv, enum set_band band);
 
+       /**
+        * get_pref_freq_list - Get preferred frequency list for an interface
+        * @priv: Private driver interface data
+        * @if_type: Interface type
+        * @num: Number of channels
+        * @freq_list: Preferred channel frequency list encoded in MHz values
+        * Returns 0 on success, -1 on failure
+        *
+        * This command can be used to query the preferred frequency list from
+        * the driver specific to a particular interface type.
+        */
+       int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
+                                 unsigned int *num, unsigned int *freq_list);
+
        /**
         * set_prob_oper_freq - Indicate probable P2P operating channel
         * @priv: Private driver interface data
index fd598901b020213c2ccfba58b7800951d55b4171..96eb90b5f152a847ddc4cb046ff36c4ce8356936 100644 (file)
@@ -5804,8 +5804,9 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type(
                return NL80211_IFTYPE_P2P_DEVICE;
        case WPA_IF_MESH:
                return NL80211_IFTYPE_MESH_POINT;
+       default:
+               return -1;
        }
-       return -1;
 }
 
 
@@ -8509,6 +8510,147 @@ static int nl80211_set_band(void *priv, enum set_band band)
 }
 
 
+struct nl80211_pcl {
+       unsigned int num;
+       unsigned int *freq_list;
+};
+
+static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nl80211_pcl *param = arg;
+       struct nlattr *nl_vend, *attr;
+       enum qca_iface_type iface_type;
+       struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+       unsigned int num, max_num;
+       u32 *freqs;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+       if (!nl_vend)
+               return NL_SKIP;
+
+       nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+                 nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+       attr = tb_vendor[
+               QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE];
+       if (!attr) {
+               wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found");
+               param->num = 0;
+               return NL_SKIP;
+       }
+
+       iface_type = (enum qca_iface_type) nla_get_u32(attr);
+       wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d",
+                  iface_type);
+
+       attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST];
+       if (!attr) {
+               wpa_printf(MSG_ERROR,
+                          "nl80211: preferred_freq_list couldn't be found");
+               param->num = 0;
+               return NL_SKIP;
+       }
+
+       /*
+        * param->num has the maximum number of entries for which there
+        * is room in the freq_list provided by the caller.
+        */
+       freqs = nla_data(attr);
+       max_num = nla_len(attr) / sizeof(u32);
+       if (max_num > param->num)
+               max_num = param->num;
+       for (num = 0; num < max_num; num++)
+               param->freq_list[num] = freqs[num];
+       param->num = num;
+
+       return NL_SKIP;
+}
+
+
+static int nl80211_get_pref_freq_list(void *priv,
+                                     enum wpa_driver_if_type if_type,
+                                     unsigned int *num,
+                                     unsigned int *freq_list)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
+       unsigned int i;
+       struct nlattr *params;
+       struct nl80211_pcl param;
+       enum qca_iface_type iface_type;
+
+       if (!drv->get_pref_freq_list)
+               return -1;
+
+       switch (if_type) {
+       case WPA_IF_STATION:
+               iface_type = QCA_IFACE_TYPE_STA;
+               break;
+       case WPA_IF_AP_BSS:
+               iface_type = QCA_IFACE_TYPE_AP;
+               break;
+       case WPA_IF_P2P_GO:
+               iface_type = QCA_IFACE_TYPE_P2P_GO;
+               break;
+       case WPA_IF_P2P_CLIENT:
+               iface_type = QCA_IFACE_TYPE_P2P_CLIENT;
+               break;
+       case WPA_IF_IBSS:
+               iface_type = QCA_IFACE_TYPE_IBSS;
+               break;
+       case WPA_IF_TDLS:
+               iface_type = QCA_IFACE_TYPE_TDLS;
+               break;
+       default:
+               return -1;
+       }
+
+       param.num = *num;
+       param.freq_list = freq_list;
+
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST) ||
+           !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+           nla_put_u32(msg,
+                       QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
+                       iface_type)) {
+               wpa_printf(MSG_ERROR,
+                          "%s: err in adding vendor_cmd and vendor_data",
+                          __func__);
+               nlmsg_free(msg);
+               return -1;
+       }
+       nla_nest_end(msg, params);
+
+       os_memset(freq_list, 0, *num * sizeof(freq_list[0]));
+       ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, &param);
+       if (ret) {
+               wpa_printf(MSG_ERROR,
+                          "%s: err in send_and_recv_msgs", __func__);
+               return ret;
+       }
+
+       *num = param.num;
+
+       for (i = 0; i < *num; i++) {
+               wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d",
+                          i, freq_list[i]);
+       }
+
+       return 0;
+}
+
+
 static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
 {
        struct i802_bss *bss = priv;
@@ -8663,5 +8805,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .del_tx_ts = nl80211_del_ts,
        .do_acs = wpa_driver_do_acs,
        .set_band = nl80211_set_band,
+       .get_pref_freq_list = nl80211_get_pref_freq_list,
        .set_prob_oper_freq = nl80211_set_prob_oper_freq,
 };
index 078df4c0ffdea26581adbdeca94be6323a285616..5c21e0faf55cd86a603d359bdc3543e5f374fa96 100644 (file)
@@ -146,6 +146,7 @@ struct wpa_driver_nl80211_data {
        unsigned int set_rekey_offload:1;
        unsigned int p2p_go_ctwindow_supported:1;
        unsigned int setband_vendor_cmd_avail:1;
+       unsigned int get_pref_freq_list:1;
        unsigned int set_prob_oper_freq:1;
 
        u64 remain_on_chan_cookie;
index e56f111382757757d4ea5e66281083b8017a1747..4cf31238aeb70703ea5e30d4376de57aeb71d6f4 100644 (file)
@@ -589,6 +589,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                                case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
                                        drv->get_features_vendor_cmd_avail = 1;
                                        break;
+                               case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST:
+                                       drv->get_pref_freq_list = 1;
+                                       break;
                                case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL:
                                        drv->set_prob_oper_freq = 1;
                                        break;
index 41f9090ef5ec495753dc777b73db07b862cdb878..6dca2527fa96cd46f194b0823e4fa9e3a4a5fb78 100644 (file)
@@ -6654,6 +6654,53 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
 }
 
 
+static int wpas_ctrl_iface_get_pref_freq_list(
+       struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+       unsigned int freq_list[100], num = 100, i;
+       int ret;
+       enum wpa_driver_if_type iface_type;
+       char *pos, *end;
+
+       pos = buf;
+       end = buf + buflen;
+
+       /* buf: "<interface_type>" */
+       if (os_strcmp(cmd, "STATION") == 0)
+               iface_type = WPA_IF_STATION;
+       else if (os_strcmp(cmd, "AP") == 0)
+               iface_type = WPA_IF_AP_BSS;
+       else if (os_strcmp(cmd, "P2P_GO") == 0)
+               iface_type = WPA_IF_P2P_GO;
+       else if (os_strcmp(cmd, "P2P_CLIENT") == 0)
+               iface_type = WPA_IF_P2P_CLIENT;
+       else if (os_strcmp(cmd, "IBSS") == 0)
+               iface_type = WPA_IF_IBSS;
+       else if (os_strcmp(cmd, "TDLS") == 0)
+               iface_type = WPA_IF_TDLS;
+       else
+               return -1;
+
+       wpa_printf(MSG_DEBUG,
+                  "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)",
+                  iface_type, buf);
+
+       ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list);
+       if (ret)
+               return -1;
+
+       for (i = 0; i < num; i++) {
+               ret = os_snprintf(pos, end - pos, "%s%u",
+                                 i > 0 ? "," : "", freq_list[i]);
+               if (os_snprintf_error(end - pos, ret))
+                       return -1;
+               pos += ret;
+       }
+
+       return pos - buf;
+}
+
+
 static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
                                      size_t buflen)
 {
@@ -8750,6 +8797,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
                if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
+               reply_len = wpas_ctrl_iface_get_pref_freq_list(
+                       wpa_s, buf + 19, reply, reply_size);
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
index 6ce4c2727191b4c54e5aee6b176df54d723dc8e5..909515b35b40c350c1c2f76f827a8dd2fdfef299 100644 (file)
@@ -893,6 +893,17 @@ static inline int wpa_drv_setband(struct wpa_supplicant *wpa_s,
        return wpa_s->driver->set_band(wpa_s->drv_priv, band);
 }
 
+static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s,
+                                            enum wpa_driver_if_type if_type,
+                                            unsigned int *num,
+                                            unsigned int *freq_list)
+{
+       if (!wpa_s->driver->get_pref_freq_list)
+               return 0;
+       return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type,
+                                                num, freq_list);
+}
+
 static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s,
                                             unsigned int freq)
 {
index 2d1adc9d9be451f65ef7fe99b252f8aff61f9ef2..48897d4f2290cfe89fcf8392209ba4f34b82ac0a 100644 (file)
@@ -2801,6 +2801,13 @@ static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc,
+                                         char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "GET_PREF_FREQ_LIST", 1, argc, argv);
+}
+
+
 enum wpa_cli_cmd_flags {
        cli_cmd_flag_none               = 0x00,
        cli_cmd_flag_sensitive          = 0x01
@@ -3355,6 +3362,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
          "<scan|sched|pno|all> enable=<0/1> [addr=mac-address "
          "mask=mac-address-mask] = scan MAC randomization"
        },
+       { "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
+         cli_cmd_flag_none,
+         "<interface type> = retrieve preferred freq list for the specified interface type" },
        { NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };