aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishal Mahaveer <vishalm@ti.com>2012-09-20 10:35:40 -0500
committerVishal Mahaveer <vishalm@ti.com>2012-09-20 10:35:40 -0500
commit761368a07b3754dced0e351cac57c4c95730d5d3 (patch)
treef0417285014ab1fe77950ce0c1a8106d6cd3d45c
parent846a11d74da8cb7b8c6c470a8c1f35e4429d6156 (diff)
parent9c03c3c348b3660a820d7dfd4fb762d5eb9fca4c (diff)
downloadexternal_wpa_supplicant_8_ti-761368a07b3754dced0e351cac57c4c95730d5d3.zip
external_wpa_supplicant_8_ti-761368a07b3754dced0e351cac57c4c95730d5d3.tar.gz
external_wpa_supplicant_8_ti-761368a07b3754dced0e351cac57c4c95730d5d3.tar.bz2
Merge commit 'ol_R5.SP3.01' into d-jb-release
-rw-r--r--hostapd/config_file.c2
-rw-r--r--src/ap/ap_config.h3
-rw-r--r--src/ap/ap_drv_ops.c31
-rw-r--r--src/ap/ap_drv_ops.h2
-rw-r--r--src/ap/beacon.c25
-rw-r--r--src/ap/drv_callbacks.c33
-rw-r--r--src/ap/hostapd.c31
-rw-r--r--src/ap/hostapd.h5
-rw-r--r--src/ap/hw_features.c18
-rw-r--r--src/ap/hw_features.h1
-rw-r--r--src/ap/ieee802_11.c30
-rw-r--r--src/drivers/driver.h28
-rw-r--r--src/drivers/driver_common.c1
-rw-r--r--src/drivers/driver_nl80211.c418
-rw-r--r--src/drivers/nl80211_copy.h26
-rw-r--r--wpa_supplicant/events.c6
16 files changed, 479 insertions, 181 deletions
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 03f82b2..59745fa 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1441,6 +1441,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
conf->country[2] = ' ';
} else if (os_strcmp(buf, "ieee80211d") == 0) {
conf->ieee80211d = atoi(pos);
+ } else if (os_strcmp(buf, "channel_switch_count") == 0) {
+ conf->channel_switch_count = atoi(pos);
} else if (os_strcmp(buf, "ieee8021x") == 0) {
bss->ieee802_1x = atoi(pos);
} else if (os_strcmp(buf, "eapol_version") == 0) {
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 4e10a0f..d6b67fe 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -432,6 +432,9 @@ struct hostapd_config {
int ieee80211d;
+ /* AP/GO channel switch count */
+ int channel_switch_count;
+
struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
/*
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 859b529..fbeaf9c 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -596,3 +596,34 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
hapd->own_addr, hapd->own_addr, data,
len, 0);
}
+
+
+int hostapd_channel_switch(struct hostapd_data *hapd, int freq, int flags,
+ u8 tx_block, u8 post_switch_block_tx)
+{
+ struct hostapd_channel_switch params;
+
+ if (!hapd->driver || !hapd->driver->hapd_channel_switch)
+ return 0;
+
+ if (flags & HOSTAPD_CHAN_RADAR) {
+ wpa_printf(MSG_ERROR, "Can't switch to radar channel, "
+ "DFS functionality is not supported");
+ return -1;
+ }
+
+ if (hapd->iface->conf->secondary_channel) {
+ wpa_printf(MSG_ERROR, "Channel switch is not supported "
+ "with HT40");
+ return -1;
+ }
+
+ params.freq = freq;
+ params.tx_block = tx_block;
+ params.post_switch_block_tx = post_switch_block_tx;
+ params.ch_switch_count =
+ (hapd->iconf->channel_switch_count > hapd->conf->dtim_period) ?
+ hapd->iconf->channel_switch_count : hapd->conf->dtim_period * 2;
+
+ return hapd->driver->hapd_channel_switch(hapd->drv_priv, &params);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index e3b15c0..6305050 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -97,6 +97,8 @@ int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
int reassoc, u16 status, const u8 *ie, size_t len);
int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
u8 *tspec_ie, size_t tspec_ielen);
+int hostapd_channel_switch(struct hostapd_data *hapd, int freq, int flags,
+ u8 tx_block, u8 post_switch_block_tx);
#include "drivers/driver.h"
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index a1bb067..72c3ead 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -33,6 +33,7 @@
#include "p2p_hostapd.h"
#include "ap_drv_ops.h"
#include "beacon.h"
+#include "hw_features.h"
#ifdef NEED_AP_MLME
@@ -171,6 +172,27 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
return pos;
}
+static u8 *hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
+{
+ int current_ch_flag = 0;
+
+ if (!hapd->next_channel)
+ return eid;
+
+ current_ch_flag = hostapd_hw_get_channel_flag(hapd,
+ hapd->iconf->channel);
+
+ *eid++ = WLAN_EID_CHANNEL_SWITCH;
+ *eid++ = 3; /* IE length */
+ /* STAs should cease transmit if the switch is due to radar */
+ *eid++ = (current_ch_flag & HOSTAPD_CHAN_RADAR) ? 1 : 0;
+ *eid++ = (u8)hapd->next_channel->chan;
+ *eid++ = (hapd->iconf->channel_switch_count > hapd->conf->dtim_period) ?
+ (u8)hapd->iconf->channel_switch_count :
+ hapd->conf->dtim_period * 2;
+ return eid;
+}
+
static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len)
{
@@ -573,6 +595,9 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
tailpos = hostapd_eid_country(hapd, tailpos,
tail + BEACON_TAIL_BUF_SIZE - tailpos);
+ /* Channel Switch Announcement */
+ tailpos = hostapd_eid_csa(hapd, tailpos);
+
/* ERP Information element */
tailpos = hostapd_eid_erp_info(hapd, tailpos);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index f80a08c..7e478a5 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -27,6 +27,7 @@
#include "ap_drv_ops.h"
#include "ap_config.h"
#include "hw_features.h"
+#include "beacon.h"
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -286,8 +287,6 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
HOSTAPD_LEVEL_INFO, "driver had channel switch: "
"freq=%d, ht=%d, offset=%d", freq, ht, offset);
- hapd->iface->freq = freq;
-
channel = hostapd_hw_get_channel(hapd, freq);
if (!channel) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
@@ -299,6 +298,30 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
hapd->iconf->channel = channel;
hapd->iconf->ieee80211n = ht;
hapd->iconf->secondary_channel = offset;
+
+
+ if (hapd->iface->freq == freq)
+ return;
+
+ hapd->iface->freq = freq;
+
+ if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, freq,
+ hapd->iconf->channel,
+ hapd->iconf->ieee80211n,
+ hapd->iconf->secondary_channel)) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "failed to update the freq !");
+ return;
+ }
+
+ hapd->next_channel = NULL;
+
+ /* update the DS IE */
+ ieee802_11_set_beacon(hapd);
+
+ wpa_printf(MSG_DEBUG, "AP/GO moved to channel %d", channel);
+
#endif /* NEED_AP_MLME */
}
@@ -627,6 +650,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->ch_switch.ht_enabled,
data->ch_switch.ch_offset);
break;
+ case EVENT_REQ_CH_SW:
+ if (!data)
+ break;
+ ieee802_11_start_channel_switch(hapd, data->ch_switch.freq,
+ FALSE);
+ break;
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 3406cd1..4e06808 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1106,3 +1106,34 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
ap_handle_timer, hapd, sta);
}
+
+struct
+hostapd_channel_data *hostapd_get_valid_channel(struct hostapd_data *hapd,
+ int req_freq)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+ int i, channel_idx = 0;
+
+ wpa_printf(MSG_DEBUG, "Selecting next channel");
+
+ if (hapd->iface->current_mode == NULL)
+ return NULL;
+
+ mode = hapd->iface->current_mode;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_RADAR))
+ continue;
+ channel_idx++;
+
+ /* request specific channel */
+ if (req_freq && (req_freq == chan->freq))
+ return chan;
+ }
+
+ wpa_printf(MSG_WARNING, "Could't get requested channel");
+ return NULL;
+}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 0465844..feea42d 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -162,6 +162,8 @@ struct hostapd_data {
void (*setup_complete_cb)(void *ctx);
void *setup_complete_cb_ctx;
+ struct hostapd_channel_data *next_channel;
+
#ifdef CONFIG_P2P
struct p2p_data *p2p;
struct p2p_group *p2p_group;
@@ -271,6 +273,9 @@ void hostapd_interface_deinit(struct hostapd_iface *iface);
void hostapd_interface_free(struct hostapd_iface *iface);
void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
int reassoc);
+struct
+hostapd_channel_data *hostapd_get_valid_channel(struct hostapd_data *hapd,
+ int req_freq);
/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 0900e78..930d35b 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -796,3 +796,21 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
return 0;
}
+
+
+int hostapd_hw_get_channel_flag(struct hostapd_data *hapd, int chan)
+{
+ int i;
+
+ if (!hapd->iface->current_mode)
+ return 0;
+
+ for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
+ struct hostapd_channel_data *ch =
+ &hapd->iface->current_mode->channels[i];
+ if (ch->chan == chan)
+ return ch->flag;
+ }
+
+ return 0;
+}
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index abadcd1..b8e287b 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -28,6 +28,7 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
int hostapd_check_ht_capab(struct hostapd_iface *iface);
int hostapd_prepare_rates(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode);
+int hostapd_hw_get_channel_flag(struct hostapd_data *hapd, int chan);
#else /* NEED_AP_MLME */
static inline void
hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index ed1661a..0b23561 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1889,4 +1889,34 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
}
+int ieee802_11_start_channel_switch(struct hostapd_data *hapd,
+ int freq, u8 radar_detected)
+{
+ struct hostapd_channel_data *next_channel;
+
+ next_channel = hostapd_get_valid_channel(hapd, freq);
+ if (!next_channel)
+ return -1;
+
+ hapd->next_channel = next_channel;
+ u8 radar_on_next_channel =
+ (next_channel->flag & HOSTAPD_CHAN_RADAR) ? 1 : 0;
+ wpa_printf(MSG_INFO, "switching to %sch. #%d, freq %d",
+ radar_on_next_channel ? "(DFS) " : "",
+ next_channel->chan, next_channel->freq);
+
+ /* Add CSA */
+ ieee802_11_set_beacon(hapd);
+
+ if (hostapd_channel_switch(hapd, next_channel->freq,
+ next_channel->flag,
+ radar_detected,
+ radar_on_next_channel)) {
+ wpa_printf(MSG_ERROR, "Channel switch failed");
+ return -1;
+ }
+ return 0;
+}
+
+
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 7afbda1..6446593 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -898,6 +898,16 @@ struct hostapd_freq_params {
* enabled, secondary channel above primary */
};
+struct hostapd_channel_switch {
+ int freq;
+ int tx_block; /* immediately block the tx on the
+ * operational channel
+ * (prior channel switch) */
+ int post_switch_block_tx; /* block tx on the target ch (after
+ * channel switch) */
+ int ch_switch_count;
+};
+
enum wpa_driver_if_type {
/**
* WPA_IF_STATION - Station mode interface
@@ -2587,6 +2597,15 @@ struct wpa_driver_ops {
* Returns: 0 on success, -1 on failure
*/
int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len);
+
+ /**
+ * hapd_channel_switch - Perform an AP channel switch
+ * @priv: Private driver interface data
+ * @params: Channels switch parameters
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*hapd_channel_switch)(void *priv,
+ struct hostapd_channel_switch *params);
};
@@ -3041,7 +3060,12 @@ enum wpa_event_type {
*
* Described in wpa_event_data.ch_switch
* */
- EVENT_CH_SWITCH
+ EVENT_CH_SWITCH,
+
+ /**
+ * EVENT_REQ_CH_SW - a request to perform a channel switch for GO/AP
+ */
+ EVENT_REQ_CH_SW
};
@@ -3704,6 +3728,8 @@ static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data,
/* driver_common.c */
void wpa_scan_results_free(struct wpa_scan_results *res);
+int ieee802_11_start_channel_switch(struct hostapd_data *hapd,
+ int freq, u8 radar_detected);
/* Convert wpa_event_type to a string for logging */
const char * event_to_string(enum wpa_event_type event);
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 5e4dc89..a0ada97 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -82,6 +82,7 @@ const char * event_to_string(enum wpa_event_type event)
E2S(ROAMING_ENABLED);
E2S(ROAMING_DISABLED);
E2S(START_ROAMING);
+ E2S(REQ_CH_SW);
}
return "UNKNOWN";
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index f7b56b2..a1a09a5 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -167,6 +167,10 @@ struct nl80211_global {
int ioctl_sock; /* socket for ioctl() use */
struct nl_handle *nl_event;
+#ifdef ANDROID
+ int wowlan_triggers;
+ int wowlan_enabled;
+#endif
};
struct nl80211_wiphy_data {
@@ -203,6 +207,10 @@ struct i802_bss {
struct nl80211_wiphy_data *wiphy_data;
struct dl_list wiphy_list;
+
+#ifdef ANDROID
+ int rx_filter_idx;
+#endif
};
struct wpa_driver_nl80211_data {
@@ -285,10 +293,6 @@ struct wpa_driver_nl80211_data {
int auth_wep_tx_keyidx;
int auth_local_state_change;
int auth_p2p;
-#ifdef ANDROID
- u8 wowlan_triggers;
- u8 wowlan_enabled;
-#endif
};
@@ -312,6 +316,10 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report);
static int android_pno_start(struct i802_bss *bss,
struct wpa_driver_scan_params *params);
static int android_pno_stop(struct i802_bss *bss);
+static int nl80211_register_rx_filter(struct i802_bss *bss, char *name,
+ u8 *pattern, int len, u8 *mask,
+ u8 action);
+static int nl80211_unregister_rx_filter(struct i802_bss *bss, int filter_idx);
#endif /* ANDROID */
#ifdef ANDROID_BRCM_P2P_PATCH
static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
@@ -1209,13 +1217,14 @@ 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 *freq, struct nlattr *type,
+ enum wpa_event_type event)
{
union wpa_event_data data;
int ht_enabled = 1;
int chan_offset = 0;
- wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
+ wpa_printf(MSG_INFO, "nl80211: Channel switch event");
if (!freq || !type)
return;
@@ -1238,7 +1247,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
data.ch_switch.ht_enabled = ht_enabled;
data.ch_switch.ch_offset = chan_offset;
- wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data);
+ wpa_supplicant_event(drv->ctx, event, &data);
}
@@ -2102,6 +2111,22 @@ static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data);
}
+static void nl80211_req_ch_sw_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data data;
+
+ if (!tb[NL80211_ATTR_WIPHY_FREQ])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ data.ch_switch.freq = nla_get_u16(tb[NL80211_ATTR_WIPHY_FREQ]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: Requst to switch to %d freq",
+ data.ch_switch.freq);
+
+ wpa_supplicant_event(drv->ctx, EVENT_REQ_CH_SW, &data);
+}
static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
int wds)
@@ -2188,7 +2213,11 @@ static void do_process_drv_event(struct wpa_driver_nl80211_data *drv,
break;
case NL80211_CMD_CH_SWITCH_NOTIFY:
mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ],
- tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+ tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
+ EVENT_CH_SWITCH);
+ break;
+ case NL80211_CMD_REQ_CH_SW:
+ nl80211_req_ch_sw_event(drv, tb);
break;
case NL80211_CMD_DISCONNECT:
mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
@@ -3007,6 +3036,9 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
bss = &drv->first_bss;
bss->drv = drv;
os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
+#ifdef ANDROID
+ bss->rx_filter_idx = -1;
+#endif
drv->monitor_ifidx = -1;
drv->monitor_sock = -1;
drv->eapol_tx_sock = -1;
@@ -7957,6 +7989,10 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int ifidx;
+#ifdef ANDROID
+ int filter_idx;
+#endif
+
#ifdef HOSTAPD
struct i802_bss *new_bss = NULL;
@@ -8017,6 +8053,26 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
}
#endif /* CONFIG_P2P */
+#if defined(ANDROID) && !defined(HOSTAPD)
+ static u8 eth_addr_mask[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+ filter_idx = nl80211_register_rx_filter(bss, "unicast",
+ if_addr, ETH_ALEN,
+ eth_addr_mask,
+ NL80211_WOWLAN_ACTION_ALLOW);
+ if (filter_idx < 0) {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+
+ if (bss->rx_filter_idx != -1)
+ wpa_printf(MSG_WARNING, "nl80211: Rx filter is already "
+ "configured when it shouldn't be (idx=%d)",
+ bss->rx_filter_idx);
+
+ bss->rx_filter_idx = filter_idx;
+#endif /* ANDROID && !HOSTAPD */
+
#ifdef HOSTAPD
if (bridge &&
i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
@@ -8089,6 +8145,10 @@ static int wpa_driver_nl80211_if_remove(void *priv,
nl80211_remove_iface(drv, ifindex);
+#ifdef ANDROID
+ nl80211_unregister_rx_filter(bss, bss->rx_filter_idx);
+#endif
+
#ifdef HOSTAPD
if (type != WPA_IF_AP_BSS)
return 0;
@@ -8883,13 +8943,13 @@ static u8 *nl80211_rx_filter_get_pattern(struct rx_filter *filter, void *arg)
}
static int
-nl80211_self_filter_get_pattern_handler(u8 *buf, int buflen, void *arg)
+nl80211_sta_unicast_filter_get_pattern_handler(u8 *buf, int buflen, void *arg)
{
int ret;
struct i802_bss *bss = (struct i802_bss *)arg;
ret = linux_get_ifhwaddr(bss->drv->global->ioctl_sock,
- bss->ifname, buf);
+ "wlan0", buf);
if (ret) {
wpa_printf(MSG_ERROR, "Failed to get own HW addr (%d)", ret);
return -1;
@@ -8897,14 +8957,16 @@ nl80211_self_filter_get_pattern_handler(u8 *buf, int buflen, void *arg)
return 0;
}
-static struct rx_filter rx_filters[] = {
+#define NUM_RX_FILTERS 15
+
+static struct rx_filter rx_filters[NUM_RX_FILTERS] = {
/* ID 0 */
- {.name = "self",
+ {.name = "sta_unicast",
.pattern = {},
.pattern_len = 6,
.mask = { BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) },
.mask_len = 1,
- .get_pattern_handler = nl80211_self_filter_get_pattern_handler,
+ .get_pattern_handler = nl80211_sta_unicast_filter_get_pattern_handler,
.action = NL80211_WOWLAN_ACTION_ALLOW,
},
@@ -8999,22 +9061,28 @@ static struct rx_filter rx_filters[] = {
};
-#define NR_RX_FILTERS (int)(sizeof(rx_filters) / sizeof(struct rx_filter))
+#define DIV_ROUND_UP(x, y) (((x) + (y - 1)) / (y))
+
+static inline int nl80211_rx_filter_configured(struct rx_filter *rx_filter)
+{
+ return (rx_filter->name != NULL);
+}
static int nl80211_set_wowlan_triggers(struct i802_bss *bss, int enable)
{
+ struct nl80211_global *global = bss->drv->global;
struct nl_msg *msg, *pats = NULL;
struct nlattr *wowtrig, *pat;
int i, ret = -1;
int filters;
- bss->drv->wowlan_enabled = !!enable;
+ global->wowlan_enabled = !!enable;
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, bss->drv->global->nl80211_id, 0,
+ genlmsg_put(msg, 0, 0, global->nl80211_id, 0,
0, NL80211_CMD_SET_WOWLAN, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->drv->first_bss.ifindex);
@@ -9040,13 +9108,16 @@ static int nl80211_set_wowlan_triggers(struct i802_bss *bss, int enable)
* so unicast traffic won't be dropped in any case.
*/
- filters = bss->drv->wowlan_triggers |= 1;
+ filters = global->wowlan_triggers |= 1;
- for (i = 0; i < NR_RX_FILTERS; i++) {
+ for (i = 0; i < NUM_RX_FILTERS; i++) {
struct rx_filter *rx_filter = &rx_filters[i];
int patnr = 1;
u8 *pattern;
+ if (!nl80211_rx_filter_configured(rx_filter))
+ continue;
+
if (!(filters & (1 << i)))
continue;
@@ -9093,20 +9164,32 @@ nla_put_failure:
static int nl80211_toggle_wowlan_trigger(struct i802_bss *bss, int nr,
int enabled)
{
- if (nr >= NR_RX_FILTERS) {
- wpa_printf(MSG_ERROR, "Unknown filter: %d\n", nr);
+ struct nl80211_global *global = bss->drv->global;
+ int prev_triggers;
+ int ret = 0;
+
+ if (nr >= NUM_RX_FILTERS) {
+ wpa_printf(MSG_ERROR, "nl80211: Invalid RX filter: %d\n", nr);
return -1;
}
+ prev_triggers = global->wowlan_triggers;
+
if (enabled)
- bss->drv->wowlan_triggers |= 1 << nr;
+ global->wowlan_triggers |= 1 << nr;
else
- bss->drv->wowlan_triggers &= ~(1 << nr);
+ global->wowlan_triggers &= ~(1 << nr);
- if (bss->drv->wowlan_enabled)
- nl80211_set_wowlan_triggers(bss, 1);
+ if (global->wowlan_enabled)
+ ret = nl80211_set_wowlan_triggers(bss, 1);
- return 0;
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to set wowlan triggers "
+ "(%d)", ret);
+ global->wowlan_triggers = prev_triggers;
+ }
+
+ return ret;
}
static int nl80211_parse_wowlan_trigger_nr(char *s)
@@ -9121,60 +9204,112 @@ static int nl80211_parse_wowlan_trigger_nr(char *s)
return i;
}
-static int nl80211_toggle_dropbcast(int enable)
+/* Helper for nl80211_register_rx_filter. Don't call directly */
+static int nl80211_add_rx_filter(char *name, u8 *pattern, int len,
+ u8 *mask, u8 action)
{
- char filename[90];
- int rv;
- FILE *f;
+ int i, j, pos;
- snprintf(filename, sizeof(filename) - 1,
- "/sys/bus/platform/devices/wl12xx/drop_bcast");
- f = fopen(filename, "w");
- if (!f) {
- wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
- filename, strerror(errno));
+ if (name == NULL || pattern == NULL || mask == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Add RX filter failed: "
+ "invalid params");
return -1;
}
- rv = fprintf(f, "%d", enable);
- fclose(f);
- if (rv < 1) {
- wpa_printf(MSG_DEBUG, "Could not write to file %s: %s",
- filename, strerror(errno));
+ if (len > MAX_PATTERN_SIZE) {
+ wpa_printf(MSG_ERROR, "nl80211: Add RX filter failed: "
+ "Pattern too big (len=%d)", len);
return -1;
}
- return 0;
+ if (action > MAX_NL80211_WOWLAN_ACTION) {
+ wpa_printf(MSG_ERROR, "nl80211: Add RX filter failed: "
+ "bad action (action=%d)", action);
+ return -1;
+ }
+
+ for (i = 0; i < NUM_RX_FILTERS; i++) {
+ struct rx_filter *filter = &rx_filters[i];
+
+ if (filter->name)
+ continue;
+
+ filter->name = name;
+ filter->pattern_len = len;
+ memcpy(filter->pattern, pattern, len);
+ for (j = 0; j < len; j++)
+ if (mask[j]) {
+ pos = j / 8;
+ filter->mask[pos] |= 1 << (j % 8);
+ }
+
+ filter->mask_len = DIV_ROUND_UP(len, 8);
+ filter->action = action;
+ break;
+ }
+
+ if (i == NUM_RX_FILTERS) {
+ wpa_printf(MSG_ERROR, "nl80211: Out of RX filters");
+ return -1;
+ }
+
+ return i;
}
-static int nl80211_dropbcast_get(char *buf, size_t buf_len)
+/* Helper for nl80211_unregister_rx_filter. Don't call directly */
+static int nl80211_remove_rx_filter(int filter_idx)
{
- char filename[90], value[10], *pos;
- int f, rv;
+ struct rx_filter *filter;
- snprintf(filename, sizeof(filename) - 1,
- "/sys/bus/platform/devices/wl12xx/drop_bcast");
- f = open(filename, O_RDONLY);
- if (f < 0) {
- wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
- filename, strerror(errno));
+ if (filter_idx < 0 || filter_idx >= NUM_RX_FILTERS) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to remove RX filter: "
+ "bad filter (idx=%d)", filter_idx);
return -1;
}
- rv = read(f, value, sizeof(value) - 1);
- close(f);
- if (rv < 0) {
- wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
- filename, strerror(errno));
+ filter = &rx_filters[filter_idx];
+ filter->name = NULL;
+ memset(filter->pattern, 0, MAX_PATTERN_SIZE);
+ memset(filter->mask, 0, MAX_MASK_SIZE);
+ filter->mask_len = 0;
+ filter->pattern_len = 0;
+ filter->action = 0;
+
+ return 0;
+}
+
+static int nl80211_register_rx_filter(struct i802_bss *bss, char *name,
+ u8 *pattern, int len, u8 *mask, u8 action)
+{
+ int filter_idx, ret;
+
+ filter_idx = nl80211_add_rx_filter(name, pattern, len, mask, action);
+
+ if (filter_idx < 0)
return -1;
- }
- value[rv] = '\0';
- pos = os_strchr(value, '\n');
- if (pos)
- *pos = '\0';
+ ret = nl80211_toggle_wowlan_trigger(bss, filter_idx, 1);
+ if (ret < 0)
+ goto fail;
+
+ return filter_idx;
- return snprintf(buf, buf_len, "Drop bcast = %s\n", value);
+fail:
+ nl80211_remove_rx_filter(filter_idx);
+ return -1;
+}
+
+static int nl80211_unregister_rx_filter(struct i802_bss *bss, int filter_idx)
+{
+ int ret;
+
+ ret = nl80211_toggle_wowlan_trigger(bss, filter_idx, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = nl80211_remove_rx_filter(filter_idx);
+
+ return ret;
}
#endif /* ANDROID */
@@ -9352,6 +9487,49 @@ static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
}
+static int nl80211_ap_channel_switch(void *priv,
+ struct hostapd_channel_switch *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_INFO, "nl80211: Channel switch, "
+ "ch_switch_count = %d, tx_block = %d, "
+ "freq = %d, post_switch_block_tx = %d.",
+ params->ch_switch_count, params->tx_block,
+ params->freq, params->post_switch_block_tx);
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_AP_CH_SWITCH);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_CH_SWITCH_COUNT, params->ch_switch_count);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+
+ /* only HT20 is supported at this point */
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20);
+
+ if (params->tx_block)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX);
+
+ if (params->post_switch_block_tx)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_POST_BLOCK_TX);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == 0) {
+ bss->freq = params->freq;
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to channel switch: "
+ "%d (%s)", ret, strerror(-ret));
+nla_put_failure:
+ return -1;
+}
#ifdef CONFIG_TDLS
static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
@@ -9460,65 +9638,6 @@ static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
}
}
-
-#define WPA_PS_ENABLED 0
-#define WPA_PS_DISABLED 1
-
-static int wpa_driver_set_power_save(void *priv, int state)
-{
- return nl80211_set_power_save(priv, state == WPA_PS_ENABLED);
-}
-
-
-static int get_power_mode_handler(struct nl_msg *msg, void *arg)
-{
- struct nlattr *tb[NL80211_ATTR_MAX + 1];
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- int *state = arg;
-
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
-
- if (!tb[NL80211_ATTR_PS_STATE])
- return NL_SKIP;
-
- if (state) {
- int s = (int) nla_get_u32(tb[NL80211_ATTR_PS_STATE]);
- wpa_printf(MSG_DEBUG, "nl80211: Get power mode = %d", s);
- *state = (s == NL80211_PS_ENABLED) ?
- WPA_PS_ENABLED : WPA_PS_DISABLED;
- }
-
- return NL_SKIP;
-}
-
-
-static int wpa_driver_get_power_save(void *priv, int *state)
-{
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- struct nl_msg *msg;
- int ret = -1;
- enum nl80211_ps_state ps_state;
-
- msg = nlmsg_alloc();
- if (!msg)
- return -1;
-
- nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_POWER_SAVE);
-
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-
- ret = send_and_recv_msgs(drv, msg, get_power_mode_handler, state);
- msg = NULL;
- if (ret < 0)
- wpa_printf(MSG_ERROR, "nl80211: Get power mode fail: %d", ret);
-nla_put_failure:
- nlmsg_free(msg);
- return ret;
-}
-
-
static int android_priv_cmd(struct i802_bss *bss, const char *cmd)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -9656,19 +9775,6 @@ static int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
MAC2STR(macaddr));
} else if (os_strcasecmp(cmd, "RELOAD") == 0) {
wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
- } else if (os_strncasecmp(cmd, "POWERMODE ", 10) == 0) {
- int state = atoi(cmd + 10);
- ret = wpa_driver_set_power_save(priv, state);
- if (ret < 0)
- wpa_driver_send_hang_msg(drv);
- else
- drv_errors = 0;
- } else if (os_strncasecmp(cmd, "GETPOWER", 8) == 0) {
- int state = -1;
- ret = wpa_driver_get_power_save(priv, &state);
- if (!ret && (state != -1))
- ret = os_snprintf(buf, buf_len, "POWERMODE = %d\n",
- state);
} else if( os_strncasecmp(cmd, "RXFILTER-ADD ", 13) == 0 ) {
int i = nl80211_parse_wowlan_trigger_nr(cmd + 13);
if(i < 0)
@@ -9683,51 +9789,6 @@ static int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
return nl80211_set_wowlan_triggers(bss, 1);
} else if (os_strcasecmp(cmd, "RXFILTER-STOP") == 0) {
return nl80211_set_wowlan_triggers(bss, 0);
- } else if (os_strncasecmp(cmd, "DROPBCAST", 9) == 0) {
- char *value = cmd + 10;
-
- if (!os_strcasecmp(value, "ENABLE") ||
- !os_strcasecmp(value, "1")) {
- ret = nl80211_toggle_dropbcast(1);
- } else if (!os_strcasecmp(value, "DISABLE") ||
- !os_strcasecmp(value, "0")) {
- ret = nl80211_toggle_dropbcast(0);
- } else if (!os_strcasecmp(value, "GET") ||
- !os_strlen(value)) {
- ret = nl80211_dropbcast_get(buf, buf_len);
- } else {
- wpa_printf(MSG_ERROR,
- "Invalid parameter for DROPBCAST: %s",
- value);
- ret = -1;
- }
- } else if( os_strcasecmp(cmd, "LINKSPEED") == 0 ) {
- struct wpa_signal_info sig;
- int linkspeed;
-
- if (!drv->associated)
- return -1;
-
- ret = nl80211_get_link_signal(drv, &sig);
- if (ret == 0) {
- linkspeed = sig.current_txrate / 1000;
- ret = os_snprintf(buf, buf_len, "LinkSpeed %d\n",
- linkspeed);
- }
- } else if (os_strcasecmp(cmd, "RSSI") == 0 ||
- os_strcasecmp(cmd, "RSSI-APPROX") == 0) {
- struct wpa_signal_info sig;
- int rssi;
-
- if (!drv->associated)
- return -1;
-
- ret = nl80211_get_link_signal(drv, &sig);
- if (ret == 0) {
- rssi = sig.current_signal;
- ret = os_snprintf(buf, buf_len, "%s rssi %d\n",
- drv->ssid, rssi);
- }
} else {
wpa_printf(MSG_ERROR, "Unsupported command: %s", cmd);
ret = -1;
@@ -9894,6 +9955,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.set_rekey_info = nl80211_set_rekey_info,
.poll_client = nl80211_poll_client,
.set_p2p_powersave = nl80211_set_p2p_powersave,
+ .hapd_channel_switch = nl80211_ap_channel_switch,
#ifdef CONFIG_TDLS
.send_tdls_mgmt = nl80211_send_tdls_mgmt,
.tdls_oper = nl80211_tdls_oper,
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 9cc385a..fbe3733 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -583,6 +583,16 @@
* disabled (marked by the presence of @NL80211_ATTR_ROAMING_DISABLED flag)
* userspace should disable background scans and roaming attempts.
*
+ * @NL80211_CMD_AP_CH_SWITCH: Perform a channel switch in the driver (for
+ * AP/GO).
+ * %NL80211_ATTR_WIPHY_FREQ: new channel frequency.
+ * %NL80211_ATTR_CH_SWITCH_BLOCK_TX: block tx on the current channel.
+ * %NL80211_ATTR_CH_SWITCH_POST_BLOCK_TX: block tx on the target channel.
+ * %NL80211_FREQ_ATTR_CH_SWITCH_COUNT: number of TBTT's until the channel
+ * switch event.
+ *
+ * @NL80211_CMD_REQ_CH_SW: Request a channel switch from a GO/AP.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -736,6 +746,9 @@ enum nl80211_commands {
NL80211_CMD_SET_PRIORITY,
NL80211_CMD_CANCEL_PRIORITY,
+ NL80211_CMD_AP_CH_SWITCH,
+ NL80211_CMD_REQ_CH_SW,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1314,6 +1327,14 @@ enum nl80211_commands {
* @NL80211_ATTR_ROAMING_DISABLED: indicates that the driver can't do roaming
* currently.
*
+ * @NL80211_ATTR_CH_SWITCH_COUNT: the number of TBTT's until the channel
+ * switch event
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: block tx on the current channel before the
+ * channel switch operation.
+ * @NL80211_ATTR_CH_SWITCH_POST_BLOCK_TX: block tx on the target channel after
+ * the channel switch operation, should be set if the target channel is
+ * DFS channel.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1581,6 +1602,9 @@ enum nl80211_attrs {
NL80211_ATTR_SCHED_SCAN_NUM_SHORT_INTERVALS,
NL80211_ATTR_ROAMING_DISABLED,
+ NL80211_ATTR_CH_SWITCH_COUNT,
+ NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+ NL80211_ATTR_CH_SWITCH_POST_BLOCK_TX,
/* add attributes here, update the policy in nl80211.c */
@@ -3099,6 +3123,7 @@ enum nl80211_ap_sme_features {
* @NL80211_FEATURE_SCHED_SCAN_INTERVALS: This driver supports using
* short interval for sched scan and then switching to a longer
* interval.
+ * @NL80211_FEATURE_AP_CH_SWITCH: This driver supports AP channel switch.
*/
enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
@@ -3108,6 +3133,7 @@ enum nl80211_feature_flags {
/* leave room for new feature flags */
NL80211_FEATURE_SCHED_SCAN_INTERVALS = 1 << 20,
+ NL80211_FEATURE_AP_CH_SWITCH = 1 << 21,
};
/**
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 56ae94c..c3e9ed6 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -2556,6 +2556,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->ch_switch.ch_offset);
#endif /* CONFIG_AP */
break;
+ case EVENT_REQ_CH_SW:
+ if (!data)
+ break;
+ ieee802_11_start_channel_switch(wpa_s->ap_iface->bss[0],
+ data->ch_switch.freq, FALSE);
+ break;
case EVENT_RX_MGMT: {
u16 fc, stype;
const struct ieee80211_mgmt *mgmt;