aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPeng Xu <pxu@qca.qualcomm.com>2014-11-18 18:11:09 +0000
committerRavi Shankar Upadrasta <raviupadrasta@codeaurora.org>2014-12-12 12:56:17 +0530
commite71138fdb9fcc0e963950546c82840fbd5b82971 (patch)
tree1f063e29bec6fcb512c09a3601350d2813f933c3 /src
parentd47b40847f52d9b45e5d64729232d3b8a7372bb7 (diff)
downloadandroid_external_wpa_supplicant_8-e71138fdb9fcc0e963950546c82840fbd5b82971.tar.gz
android_external_wpa_supplicant_8-e71138fdb9fcc0e963950546c82840fbd5b82971.tar.bz2
android_external_wpa_supplicant_8-e71138fdb9fcc0e963950546c82840fbd5b82971.zip
hostapd: Allow ACS to be offloaded to the driver
Using QCA vendor command, allow ACS function to be offloaded to the driver. Once channels are selected, hostapd is notified to perform OBSS operation Conflicts: src/ap/ap_drv_ops.c src/common/qca-vendor.h src/drivers/driver.h src/drivers/driver_common.c src/drivers/driver_nl80211.c Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com> Git-commit: 16689c7cfc99c66aecbf16eb2f4a8bc941cb5d0f Git-repo : git://w1.fi/srv/git/hostap.git Change-Id: Ib36cdc5b267901ba3e3cc373d722f9fdc5ff50bb CRs-fixed: 752061
Diffstat (limited to 'src')
-rw-r--r--src/ap/acs.c8
-rw-r--r--src/ap/ap_drv_ops.c15
-rw-r--r--src/ap/ap_drv_ops.h1
-rw-r--r--src/ap/drv_callbacks.c52
-rw-r--r--src/common/qca-vendor.h25
-rw-r--r--src/drivers/driver.h44
-rw-r--r--src/drivers/driver_common.c1
-rw-r--r--src/drivers/driver_nl80211.c99
8 files changed, 244 insertions, 1 deletions
diff --git a/src/ap/acs.c b/src/ap/acs.c
index b94b8a43..97cf26fb 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -816,6 +816,14 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
+ wpa_printf(MSG_INFO, "ACS: Offloading to driver");
+ err = hostapd_drv_do_acs(iface->bss[0]);
+ if (err)
+ return HOSTAPD_CHAN_INVALID;
+ return HOSTAPD_CHAN_ACS;
+ }
+
acs_cleanup(iface);
err = acs_request_scan(iface);
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index cc4ac102..ce0faab7 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -792,3 +792,18 @@ int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
qos_map_set_len);
}
+
+
+int hostapd_drv_do_acs(struct hostapd_data *hapd)
+{
+ struct drv_acs_params params;
+
+ if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
+ return 0;
+ os_memset(&params, 0, sizeof(params));
+ params.hw_mode = hapd->iface->conf->hw_mode;
+ params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
+ params.ht40_enabled = !!(hapd->iface->conf->ht_capab |
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+ return hapd->driver->do_acs(hapd->drv_priv, &params);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 7cc9d7de..bd181a2f 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -111,6 +111,7 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode,
int vht_enabled, int sec_channel_offset,
int vht_oper_chwidth, int center_segment0,
int center_segment1, u32 vht_caps);
+int hostapd_drv_do_acs(struct hostapd_data *hapd);
#include "drivers/driver.h"
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index ae4c968c..c687ba03 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -517,6 +517,51 @@ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
}
+#ifdef CONFIG_ACS
+static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+ u8 pri_channel, u8 sec_channel)
+{
+ int channel;
+ int ret;
+
+ if (hapd->iconf->channel) {
+ wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
+ hapd->iconf->channel);
+ return;
+ }
+
+ hapd->iface->freq = hostapd_hw_get_freq(hapd, pri_channel);
+
+ channel = pri_channel;
+ if (!channel) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "driver switched to bad channel");
+ return;
+ }
+
+ hapd->iconf->channel = channel;
+
+ if (sec_channel == 0)
+ hapd->iconf->secondary_channel = 0;
+ else if (sec_channel < pri_channel)
+ hapd->iconf->secondary_channel = -1;
+ else if (sec_channel > pri_channel)
+ hapd->iconf->secondary_channel = 1;
+ else {
+ wpa_printf(MSG_ERROR, "Invalid secondary channel!");
+ return;
+ }
+
+ ret = hostapd_acs_completed(hapd->iface, 0);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "ACS: Possibly channel configuration is invalid");
+ }
+}
+#endif /* CONFIG_ACS */
+
+
int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
const u8 *bssid, const u8 *ie, size_t ie_len,
int ssi_signal)
@@ -1120,6 +1165,13 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
hapd->iface, data->channel_list_changed.initiator);
break;
#endif /* NEED_AP_MLME */
+#ifdef CONFIG_ACS
+ case EVENT_ACS_CHANNEL_SELECTED:
+ hostapd_acs_channel_selected(
+ hapd, data->acs_selected_channels.pri_channel,
+ data->acs_selected_channels.sec_channel);
+ break;
+#endif /* CONFIG_ACS */
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 4d9efd52..e4205f49 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -43,6 +43,10 @@ enum qca_radiotap_vendor_ids {
* @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass
* NAN Request/Response and NAN Indication messages. These messages are
* interpreted between the framework and the firmware component.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to
+ * invoke the ACS function in device and pass selected channels to
+ * hostapd.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -53,6 +57,7 @@ enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_NAN = 12,
QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
/* 14..33 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54,
};
@@ -71,4 +76,24 @@ enum qca_wlan_vendor_attr {
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
};
+enum qca_wlan_vendor_attr_acs_offload {
+ QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL,
+ QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL,
+ QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE,
+ QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED,
+ QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ACS_MAX =
+ QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_acs_hw_mode {
+ QCA_ACS_MODE_IEEE80211B,
+ QCA_ACS_MODE_IEEE80211G,
+ QCA_ACS_MODE_IEEE80211A,
+ QCA_ACS_MODE_IEEE80211AD,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 7db0e835..c504f2fe 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1005,6 +1005,8 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS_IBSS 0x08000000
/* Driver supports radar detection */
#define WPA_DRIVER_FLAGS_RADAR 0x10000000
+/* Driver support ACS offload */
+#define WPA_DRIVER_FLAGS_ACS_OFFLOAD 0x0000000200000000ULL
/* Driver supports a dedicated interface for P2P Device */
#define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE 0x20000000
/* Driver supports QoS Mapping */
@@ -1352,6 +1354,17 @@ struct macsec_init_params {
};
#endif /* CONFIG_MACSEC */
+struct drv_acs_params {
+ /* Selected mode (HOSTAPD_MODE_*) */
+ enum hostapd_hw_mode hw_mode;
+
+ /* Indicates whether HT is enabled */
+ int ht_enabled;
+
+ /* Indicates whether HT40 is enabled */
+ int ht40_enabled;
+};
+
/**
* struct wpa_driver_ops - Driver interface API definition
@@ -3044,6 +3057,17 @@ struct wpa_driver_ops {
* already established PMKSA.
*/
int (*key_mgmt_set_pmk)(void *priv, const u8 *pmk, size_t pmk_len);
+
+ /**
+ * do_acs - Automatically select channel
+ * @priv: Private driver interface data
+ * @params: Parameters for ACS
+ * Returns 0 on success, -1 on failure
+ *
+ * This command can be used to offload ACS to the driver if the driver
+ * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD).
+ */
+ int (*do_acs)(void *priv, struct drv_acs_params *params);
};
@@ -3532,7 +3556,15 @@ enum wpa_event_type {
* event might also be sent after the device handles a PTK rekeying
* operation.
*/
- EVENT_AUTHORIZATION
+ EVENT_AUTHORIZATION,
+
+ /**
+ * EVENT_ACS_CHANNEL_SELECTED - Received selected channels by ACS
+ *
+ * Indicates a pair of primary and secondary channels chosen by ACS
+ * in device.
+ */
+ EVENT_ACS_CHANNEL_SELECTED
};
@@ -4191,6 +4223,16 @@ union wpa_event_data {
u8 *ptk_kck;
u8 *ptk_kek;
} authorization_info;
+
+ /**
+ * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED
+ * @pri_channel: Selected primary channel
+ * @sec_channel: Selected secondary channel
+ */
+ struct acs_selected_channels {
+ u8 pri_channel;
+ u8 sec_channel;
+ } acs_selected_channels;
};
/**
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 3ed4cc77..413552ca 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -80,6 +80,7 @@ const char * event_to_string(enum wpa_event_type event)
E2S(SCAN_STARTED);
E2S(AVOID_FREQUENCIES);
E2S(AUTHORIZATION);
+ E2S(ACS_CHANNEL_SELECTED);
}
return "UNKNOWN";
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 021fb17c..14391b2f 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -2866,6 +2866,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 nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
u32 subcmd, u8 *data, size_t len)
{
@@ -2873,6 +2900,9 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
qca_nl80211_avoid_freq(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",
@@ -3942,6 +3972,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
if (vinfo->subcmd ==
QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)
drv->dfs_vendor_cmd_avail = 1;
+ if (vinfo->subcmd ==
+ QCA_NL80211_VENDOR_SUBCMD_DO_ACS)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD;
wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
vinfo->vendor_id, vinfo->subcmd);
@@ -12590,6 +12623,71 @@ nla_put_failure:
}
+static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
+{
+ switch (hw_mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ return QCA_ACS_MODE_IEEE80211B;
+ case HOSTAPD_MODE_IEEE80211G:
+ return QCA_ACS_MODE_IEEE80211G;
+ case HOSTAPD_MODE_IEEE80211A:
+ return QCA_ACS_MODE_IEEE80211A;
+ case HOSTAPD_MODE_IEEE80211AD:
+ return QCA_ACS_MODE_IEEE80211AD;
+ default:
+ return -1;
+ }
+}
+
+
+static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *data;
+ int ret = -ENOBUFS;
+ int mode;
+
+ mode = hw_mode_to_qca_acs(params->hw_mode);
+ if (mode < 0)
+ return -1;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(drv, msg, 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_DO_ACS);
+
+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!data)
+ goto nla_put_failure;
+ NLA_PUT_U8(msg, QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, mode);
+ if (params->ht_enabled)
+ NLA_PUT_FLAG(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED);
+ if (params->ht40_enabled)
+ NLA_PUT_FLAG(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED);
+ nla_nest_end(msg, data);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to invoke driver ACS function: %s",
+ strerror(errno));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
@@ -12682,4 +12780,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.set_qos_map = nl80211_set_qos_map,
.set_wowlan = nl80211_set_wowlan,
.key_mgmt_set_pmk = nl80211_key_mgmt_set_pmk,
+ .do_acs = wpa_driver_do_acs,
};