aboutsummaryrefslogtreecommitdiffstats
path: root/src/drivers
diff options
context:
space:
mode:
authorPeng Xu <pxu@qca.qualcomm.com>2014-11-18 18:11:09 (GMT)
committerJouni Malinen <j@w1.fi>2014-12-03 20:31:53 (GMT)
commit16689c7cfc99c66aecbf16eb2f4a8bc941cb5d0f (patch)
tree99569ec2374d7de19dcbc60749e221270334635d /src/drivers
parentf34891a3af5148f48472ae225026b462b58225bd (diff)
downloadhostap-16689c7cfc99c66aecbf16eb2f4a8bc941cb5d0f.zip
hostap-16689c7cfc99c66aecbf16eb2f4a8bc941cb5d0f.tar.gz
hostap-16689c7cfc99c66aecbf16eb2f4a8bc941cb5d0f.tar.bz2
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. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/driver.h42
-rw-r--r--src/drivers/driver_common.c1
-rw-r--r--src/drivers/driver_nl80211.c66
-rw-r--r--src/drivers/driver_nl80211_capa.c3
-rw-r--r--src/drivers/driver_nl80211_event.c30
5 files changed, 141 insertions, 1 deletions
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index aaac0b1..2c6c4ec 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1073,6 +1073,8 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS_AP_CSA 0x80000000
/* Driver supports mesh */
#define WPA_DRIVER_FLAGS_MESH 0x0000000100000000ULL
+/* Driver support ACS offload */
+#define WPA_DRIVER_FLAGS_ACS_OFFLOAD 0x0000000200000000ULL
u64 flags;
#define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001
@@ -1425,6 +1427,17 @@ enum drv_br_net_param {
DRV_BR_NET_PARAM_GARP_ACCEPT,
};
+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
@@ -3213,6 +3226,17 @@ struct wpa_driver_ops {
* Returns 0 on success, -1 on failure
*/
int (*leave_mesh)(void *priv);
+
+ /**
+ * 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);
};
@@ -3681,8 +3705,15 @@ enum wpa_event_type {
/**
* EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification
*/
- EVENT_NEW_PEER_CANDIDATE
+ EVENT_NEW_PEER_CANDIDATE,
+ /**
+ * 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,
};
@@ -4370,6 +4401,15 @@ union wpa_event_data {
size_t ie_len;
} mesh_peer;
+ /**
+ * 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 e0a7ebb..f897c11 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -78,6 +78,7 @@ const char * event_to_string(enum wpa_event_type event)
E2S(SCAN_STARTED);
E2S(AVOID_FREQUENCIES);
E2S(NEW_PEER_CANDIDATE);
+ E2S(ACS_CHANNEL_SELECTED);
}
return "UNKNOWN";
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index ce5639a..fccae11 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -9249,6 +9249,71 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
}
+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",
@@ -9353,4 +9418,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.br_set_net_param = wpa_driver_br_set_net_param,
.add_tx_ts = nl80211_add_ts,
.del_tx_ts = nl80211_del_ts,
+ .do_acs = wpa_driver_do_acs,
};
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 9ee49e1..99c0027 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -536,6 +536,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY:
drv->key_mgmt_set_key_vendor_cmd_avail = 1;
break;
+ case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+ drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD;
+ break;
}
wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index bb19a20..9835e28 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -1469,6 +1469,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)
{
@@ -1512,6 +1539,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",