diff options
author | Vishal Mahaveer <vishalm@ti.com> | 2012-09-20 10:35:40 -0500 |
---|---|---|
committer | Vishal Mahaveer <vishalm@ti.com> | 2012-09-20 10:35:40 -0500 |
commit | 761368a07b3754dced0e351cac57c4c95730d5d3 (patch) | |
tree | f0417285014ab1fe77950ce0c1a8106d6cd3d45c | |
parent | 846a11d74da8cb7b8c6c470a8c1f35e4429d6156 (diff) | |
parent | 9c03c3c348b3660a820d7dfd4fb762d5eb9fca4c (diff) | |
download | external_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.c | 2 | ||||
-rw-r--r-- | src/ap/ap_config.h | 3 | ||||
-rw-r--r-- | src/ap/ap_drv_ops.c | 31 | ||||
-rw-r--r-- | src/ap/ap_drv_ops.h | 2 | ||||
-rw-r--r-- | src/ap/beacon.c | 25 | ||||
-rw-r--r-- | src/ap/drv_callbacks.c | 33 | ||||
-rw-r--r-- | src/ap/hostapd.c | 31 | ||||
-rw-r--r-- | src/ap/hostapd.h | 5 | ||||
-rw-r--r-- | src/ap/hw_features.c | 18 | ||||
-rw-r--r-- | src/ap/hw_features.h | 1 | ||||
-rw-r--r-- | src/ap/ieee802_11.c | 30 | ||||
-rw-r--r-- | src/drivers/driver.h | 28 | ||||
-rw-r--r-- | src/drivers/driver_common.c | 1 | ||||
-rw-r--r-- | src/drivers/driver_nl80211.c | 418 | ||||
-rw-r--r-- | src/drivers/nl80211_copy.h | 26 | ||||
-rw-r--r-- | wpa_supplicant/events.c | 6 |
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, ¶ms); +} 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; |