diff options
| author | Jouni Malinen <j@w1.fi> | 2013-11-03 16:33:11 +0200 |
|---|---|---|
| committer | Sandeep Puligilla <spuligil@codeaurora.org> | 2013-11-20 14:21:49 +0530 |
| commit | 95a686f32a99fb3db9726c03c3e692dc840db363 (patch) | |
| tree | 3612497d8551bb731b3e45b7b8649c7efae6db8e | |
| parent | 2097f64e7757ab4c8096f5f3552bd4a83ae5fa07 (diff) | |
| download | android_external_wpa_supplicant_8-95a686f32a99fb3db9726c03c3e692dc840db363.tar.gz android_external_wpa_supplicant_8-95a686f32a99fb3db9726c03c3e692dc840db363.tar.bz2 android_external_wpa_supplicant_8-95a686f32a99fb3db9726c03c3e692dc840db363.zip | |
hostapd: Wait for channel list update after country code change
If hostapd is requested to set the country code and the previous country
code differs from the new one, the channel list information from the
driver may change. This change may not be instant, so wait for an
EVENT_CHANNEL_LIST_CHANGED event before continuing interface setup with
fetching of the channel list information. This fixes issues where the
selected channel is not available based on the previous regulatory data
and update through CRDA takes some time.
Change-Id: I0c0148a69307dd6893cc79974931b581c1af5dfd
CRs-Fixed: 570018
Git-commit:f0793bf191eb5c16db2d174b0127425bda79310f
Git-repo: git://w1.fi/srv/git/hostap.git
Signed-hostap: Jouni Malinen <j@w1.fi>
This includes changes from another upstream commit
e76da5052980f301fe61f2fc0e1e7a5789716061 ("hostapd: Add AP DFS support")
to address the drv_callbacks.c dependencies.
To avoid having to pull in all the DFS related changes
the conflicts are resolved manually for:
src/ap/drv_callbacks.c
src/ap/hostapd.c
| -rw-r--r-- | src/ap/ap_drv_ops.h | 7 | ||||
| -rw-r--r-- | src/ap/drv_callbacks.c | 9 | ||||
| -rw-r--r-- | src/ap/hostapd.c | 62 | ||||
| -rw-r--r-- | src/ap/hostapd.h | 3 | ||||
| -rw-r--r-- | src/drivers/driver.h | 8 | ||||
| -rw-r--r-- | src/drivers/driver_nl80211.c | 39 |
6 files changed, 127 insertions, 1 deletions
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 23277b6b..423cd8d3 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -235,4 +235,11 @@ static inline int hostapd_drv_get_survey(struct hostapd_data *hapd, return hapd->driver->get_survey(hapd->drv_priv, freq); } +static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2) +{ + if (hapd->driver == NULL || hapd->driver->get_country == NULL) + return -1; + return hapd->driver->get_country(hapd->drv_priv, alpha2); +} + #endif /* AP_DRV_OPS */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index d6bc98d0..129ca818 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -929,6 +929,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_SURVEY: hostapd_event_get_survey(hapd, &data->survey_results); break; +#ifdef NEED_AP_MLME + case EVENT_CHANNEL_LIST_CHANGED: + /* channel list changed (regulatory?), update channel list */ + /* TODO: check this. hostapd_get_hw_features() initializes + * too much stuff. */ + /* hostapd_get_hw_features(hapd->iface); */ + hostapd_channel_list_updated(hapd->iface); + break; +#endif /* NEED_AP_MLME */ default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index fd1ca2b3..f95f6c2d 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -37,6 +37,8 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); +static int setup_interface2(struct hostapd_iface *iface); +static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx); extern int wpa_debug_level; extern struct wpa_driver_ops *wpa_drivers[]; @@ -325,6 +327,8 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) */ static void hostapd_cleanup_iface(struct hostapd_iface *iface) { + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + hostapd_cleanup_iface_partial(iface); hostapd_config_free(iface->conf); iface->conf = NULL; @@ -882,11 +886,39 @@ static void hostapd_set_acl(struct hostapd_data *hapd) } +static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + + if (!iface->wait_channel_update) { + wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it"); + return; + } + + /* + * It is possible that the existing channel list is acceptable, so try + * to proceed. + */ + wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway"); + setup_interface2(iface); +} + + +void hostapd_channel_list_updated(struct hostapd_iface *iface) +{ + if (!iface->wait_channel_update) + return; + + wpa_printf(MSG_DEBUG, "Channel list updated - continue setup"); + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + setup_interface2(iface); +} + + static int setup_interface(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; size_t i; - char country[4]; /* * Make sure that all BSSes get configured with a pointer to the same @@ -901,14 +933,39 @@ static int setup_interface(struct hostapd_iface *iface) return -1; if (hapd->iconf->country[0] && hapd->iconf->country[1]) { + char country[4], previous_country[4]; + + if (hostapd_get_country(hapd, previous_country) < 0) + previous_country[0] = '\0'; + os_memcpy(country, hapd->iconf->country, 3); country[3] = '\0'; if (hostapd_set_country(hapd, country) < 0) { wpa_printf(MSG_ERROR, "Failed to set country code"); return -1; } + + wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s", + previous_country, country); + + if (os_strncmp(previous_country, country, 2) != 0) { + wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update"); + iface->wait_channel_update = 1; + eloop_register_timeout(1, 0, + channel_list_update_timeout, + iface, NULL); + return 0; + } } + return setup_interface2(iface); +} + + +static int setup_interface2(struct hostapd_iface *iface) +{ + iface->wait_channel_update = 0; + if (hostapd_get_hw_features(iface)) { /* Not all drivers support this yet, so continue without hw * feature data. */ @@ -1105,6 +1162,9 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) if (iface == NULL) return; + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + iface->wait_channel_update = 0; + hostapd_cleanup_iface_pre(iface); for (j = 0; j < iface->num_bss; j++) { struct hostapd_data *hapd = iface->bss[j]; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index dbf1b52f..48a1e944 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -251,6 +251,8 @@ struct hostapd_iface { size_t num_bss; struct hostapd_data **bss; + unsigned int wait_channel_update:1; + int num_ap; /* number of entries in ap_list */ struct ap_info *ap_list; /* AP info list head */ struct ap_info *ap_hash[STA_HASH_SIZE]; @@ -353,6 +355,7 @@ int hostapd_reload_iface(struct hostapd_iface *hapd_iface); int hostapd_disable_iface(struct hostapd_iface *hapd_iface); int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf); int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); +void hostapd_channel_list_updated(struct hostapd_iface *iface); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, diff --git a/src/drivers/driver.h b/src/drivers/driver.h index b8c98290..a3fc32d0 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1565,6 +1565,14 @@ struct wpa_driver_ops { int (*set_country)(void *priv, const char *alpha2); /** + * get_country - Get country + * @priv: Private driver interface data + * @alpha2: Buffer for returning country code (at least 3 octets) + * Returns: 0 on success, -1 on failure + */ + int (*get_country)(void *priv, char *alpha2); + + /** * global_init - Global driver initialization * Returns: Pointer to private data (global), %NULL on failure * diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index a3ff189b..f06af0b9 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -2865,6 +2865,44 @@ nla_put_failure: } +static int nl80211_get_country(struct nl_msg *msg, void *arg) +{ + char *alpha2 = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) { + wpa_printf(MSG_DEBUG, "nl80211: No country information available"); + return NL_SKIP; + } + os_strlcpy(alpha2, nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 3); + return NL_SKIP; +} + + +static int wpa_driver_nl80211_get_country(void *priv, char *alpha2) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + alpha2[0] = '\0'; + ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2); + if (!alpha2[0]) + ret = -1; + + return ret; +} + + static int protocol_feature_handler(struct nl_msg *msg, void *arg) { u32 *feat = arg; @@ -10772,6 +10810,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_operstate = wpa_driver_nl80211_set_operstate, .set_supp_port = wpa_driver_nl80211_set_supp_port, .set_country = wpa_driver_nl80211_set_country, + .get_country = wpa_driver_nl80211_get_country, .set_ap = wpa_driver_nl80211_set_ap, .set_acl = wpa_driver_nl80211_set_acl, .if_add = wpa_driver_nl80211_if_add, |
