diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2020-10-22 16:58:40 +0200 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2020-10-22 16:58:40 +0200 |
commit | 880fe31ef284ce7fee77694df2eb0448e17aeb8a (patch) | |
tree | 2e20d6c9baa6cf64ebf7619aa715bad405d6ce9b | |
parent | cc030b9112d2fd474a64e4356d4bc42b017eff13 (diff) | |
parent | ada71a4af6af7f2e830f266e757aabfe2e5dcabb (diff) |
wifi: merge branch 'balrog-kun/NetworkManager-iwd-query-speed'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/652
-rw-r--r-- | src/devices/wifi/nm-device-iwd.c | 82 | ||||
-rw-r--r-- | src/platform/wifi/nm-wifi-utils-nl80211.c | 286 |
2 files changed, 172 insertions, 196 deletions
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index 15c1191701..c479b3d453 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -54,6 +54,7 @@ typedef struct { NMDeviceWifiCapabilities capabilities; NMActRequestGetSecretsCallId *wifi_secrets_id; guint periodic_scan_id; + guint periodic_update_id; bool enabled : 1; bool can_scan : 1; bool can_connect : 1; @@ -65,6 +66,8 @@ typedef struct { bool networks_changed : 1; gint64 last_scan; uint32_t ap_id; + guint32 rate; + uint8_t current_ap_bssid[ETH_ALEN]; } NMDeviceIwdPrivate; struct _NMDeviceIwd { @@ -163,6 +166,7 @@ set_current_ap(NMDeviceIwd *self, NMWifiAP *new_ap, gboolean recheck_available_c g_object_unref(old_ap); } + memset(priv->current_ap_bssid, 0, ETH_ALEN); _notify(self, PROP_ACTIVE_ACCESS_POINT); _notify(self, PROP_MODE); schedule_periodic_scan(self, TRUE); @@ -177,8 +181,6 @@ remove_all_aps(NMDeviceIwd *self) if (c_list_is_empty(&priv->aps_lst_head)) return; - set_current_ap(self, NULL, FALSE); - c_list_for_each_entry_safe (ap, ap_safe, &priv->aps_lst_head, aps_lst) ap_add_remove(self, FALSE, ap, FALSE); @@ -404,6 +406,63 @@ update_aps(NMDeviceIwd *self) } static void +periodic_update(NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE(self); + int ifindex; + guint32 new_rate; + int percent; + guint8 bssid[ETH_ALEN]; + gboolean ap_changed = FALSE; + NMPlatform * platform; + + ifindex = nm_device_get_ifindex(NM_DEVICE(self)); + if (ifindex <= 0) + g_return_if_reached(); + + platform = nm_device_get_platform(NM_DEVICE(self)); + + /* TODO: obtain this through the net.connman.iwd.SignalLevelAgent API. + * For now we're waking up for the rate updates anyway. + */ + percent = nm_platform_wifi_get_quality(platform, ifindex); + if (percent >= 0 && percent <= 100) { + if (nm_wifi_ap_set_strength(priv->current_ap, (gint8) percent)) { +#if NM_MORE_LOGGING + ap_changed = TRUE; +#endif + } + } + + new_rate = nm_platform_wifi_get_rate(platform, ifindex); + if (new_rate != priv->rate) { + priv->rate = new_rate; + _notify(self, PROP_BITRATE); + } + + if (nm_platform_wifi_get_bssid(platform, ifindex, bssid) + && nm_ethernet_address_is_valid(bssid, ETH_ALEN) + && memcmp(bssid, priv->current_ap_bssid, ETH_ALEN)) { + gs_free char *bssid_str = NULL; + memcpy(priv->current_ap_bssid, bssid, ETH_ALEN); + bssid_str = nm_utils_hwaddr_ntoa(bssid, ETH_ALEN); + ap_changed |= nm_wifi_ap_set_address(priv->current_ap, bssid_str); + ap_changed |= nm_wifi_ap_set_freq(priv->current_ap, + nm_platform_wifi_get_frequency(platform, ifindex)); + } + + if (ap_changed) + _ap_dump(self, LOGL_DEBUG, priv->current_ap, "updated"); +} + +static gboolean +periodic_update_cb(gpointer user_data) +{ + periodic_update(user_data); + return TRUE; +} + +static void send_disconnect(NMDeviceIwd *self) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE(self); @@ -436,6 +495,7 @@ cleanup_association_attempt(NMDeviceIwd *self, gboolean disconnect) wifi_secrets_cancel(self); set_current_ap(self, NULL, TRUE); + nm_clear_g_source(&priv->periodic_update_id); if (disconnect && priv->dbus_station_proxy) send_disconnect(self); @@ -1450,6 +1510,11 @@ network_connect_cb(GObject *source, GAsyncResult *res, gpointer user_data) ssid_utf8); nm_device_activate_schedule_stage3_ip_config_start(device); + if (!priv->periodic_update_id) { + priv->periodic_update_id = g_timeout_add_seconds(6, periodic_update_cb, self); + periodic_update(self); + } + return; failed: @@ -1499,11 +1564,12 @@ act_start_cb(GObject *source, GAsyncResult *res, gpointer user_data) NMSettingWireless * s_wireless; GBytes * ssid; gs_free char * ssid_utf8 = NULL; + const char * mode; variant = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); if (!variant) { _LOGE(LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) Network.Connect failed: %s", + "Activation: (wifi) {AccessPoint,AdHoc}.Start() failed: %s", error->message); if (nm_utils_error_is_cancelled(error)) @@ -1530,8 +1596,14 @@ act_start_cb(GObject *source, GAsyncResult *res, gpointer user_data) _LOGI(LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) Stage 2 of 5 (Device Configure) successful. Started '%s'.", ssid_utf8); - nm_device_activate_schedule_stage3_ip_config_start(device); + + mode = nm_setting_wireless_get_mode(s_wireless); + if (!priv->periodic_update_id && nm_streq0(mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { + priv->periodic_update_id = g_timeout_add_seconds(6, periodic_update_cb, self); + periodic_update(self); + } + return; error: @@ -2153,7 +2225,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) break; case PROP_BITRATE: - g_value_set_uint(value, 65000); + g_value_set_uint(value, priv->rate); break; case PROP_CAPABILITIES: g_value_set_uint(value, priv->capabilities); diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.c b/src/platform/wifi/nm-wifi-utils-nl80211.c index dddfa8fafb..13f8f5bf1a 100644 --- a/src/platform/wifi/nm-wifi-utils-nl80211.c +++ b/src/platform/wifi/nm-wifi-utils-nl80211.c @@ -41,6 +41,16 @@ } \ G_STMT_END +struct nl80211_station_info { + gboolean valid; + guint8 bssid[ETH_ALEN]; + guint32 txrate; + gboolean txrate_valid; + guint8 signal; + gboolean signal_valid; + gint64 timestamp; +}; + typedef struct { NMWifiUtils parent; struct nl_sock *nl_sock; @@ -49,6 +59,8 @@ typedef struct { int num_freqs; int phy; bool can_wowlan : 1; + + struct nl80211_station_info sta_info; } NMWifiUtilsNl80211; typedef struct { @@ -163,6 +175,7 @@ dispose(GObject *object) struct nl80211_iface_info { NM80211Mode mode; + uint32_t freq; }; static int @@ -193,6 +206,9 @@ nl80211_iface_info_handler(struct nl_msg *msg, void *arg) break; } + if (tb[NL80211_ATTR_WIPHY_FREQ] != NULL) + info->freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + return NL_SKIP; } @@ -359,142 +375,19 @@ nla_put_failure: g_return_val_if_reached(FALSE); } -/* @divisor: pass what value @xbm should be divided by to get dBm */ -static guint32 -nl80211_xbm_to_percent(gint32 xbm, guint32 divisor) -{ -#define NOISE_FLOOR_DBM -90 -#define SIGNAL_MAX_DBM -20 - - xbm /= divisor; - xbm = CLAMP(xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM); - - return 100 - - 70 - * (((float) SIGNAL_MAX_DBM - (float) xbm) - / ((float) SIGNAL_MAX_DBM - (float) NOISE_FLOOR_DBM)); -} - -struct nl80211_bss_info { - guint32 freq; - guint8 bssid[ETH_ALEN]; - guint8 ssid[32]; - guint32 ssid_len; - guint32 beacon_signal; - gboolean valid; -}; - -#define WLAN_EID_SSID 0 - -static void -find_ssid(guint8 *ies, guint32 ies_len, guint8 **ssid, guint32 *ssid_len) -{ - *ssid = NULL; - *ssid_len = 0; - - while (ies_len > 2 && ies[0] != WLAN_EID_SSID) { - ies_len -= ies[1] + 2; - ies += ies[1] + 2; - } - if (ies_len < 2) - return; - if (ies_len < 2 + ies[1]) - return; - - *ssid_len = ies[1]; - *ssid = ies + 2; -} - -static int -nl80211_bss_dump_handler(struct nl_msg *msg, void *arg) -{ - static const struct nla_policy bss_policy[] = { - [NL80211_BSS_TSF] = {.type = NLA_U64}, - [NL80211_BSS_FREQUENCY] = {.type = NLA_U32}, - [NL80211_BSS_BSSID] = {.minlen = ETH_ALEN}, - [NL80211_BSS_BEACON_INTERVAL] = {.type = NLA_U16}, - [NL80211_BSS_CAPABILITY] = {.type = NLA_U16}, - [NL80211_BSS_INFORMATION_ELEMENTS] = {}, - [NL80211_BSS_SIGNAL_MBM] = {.type = NLA_U32}, - [NL80211_BSS_SIGNAL_UNSPEC] = {.type = NLA_U8}, - [NL80211_BSS_STATUS] = {.type = NLA_U32}, - }; - struct nl80211_bss_info *info = arg; - struct genlmsghdr * gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr * tb[NL80211_ATTR_MAX + 1]; - struct nlattr * bss[G_N_ELEMENTS(bss_policy)]; - guint32 status; - - if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) - return NL_SKIP; - - if (tb[NL80211_ATTR_BSS] == NULL) - return NL_SKIP; - - if (nla_parse_nested_arr(bss, tb[NL80211_ATTR_BSS], bss_policy)) - return NL_SKIP; - - if (bss[NL80211_BSS_STATUS] == NULL) - return NL_SKIP; - - status = nla_get_u32(bss[NL80211_BSS_STATUS]); - - if (status != NL80211_BSS_STATUS_ASSOCIATED && status != NL80211_BSS_STATUS_IBSS_JOINED) - return NL_SKIP; - - if (bss[NL80211_BSS_BSSID] == NULL) - return NL_SKIP; - memcpy(info->bssid, nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); - - if (bss[NL80211_BSS_FREQUENCY]) - info->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - - if (bss[NL80211_BSS_SIGNAL_UNSPEC]) - info->beacon_signal = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); - - if (bss[NL80211_BSS_SIGNAL_MBM]) - info->beacon_signal = nl80211_xbm_to_percent(nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]), 100); - - if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { - guint8 *ssid; - guint32 ssid_len; - - find_ssid(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]), - nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]), - &ssid, - &ssid_len); - if (ssid && ssid_len && ssid_len <= sizeof(info->ssid)) { - memcpy(info->ssid, ssid, ssid_len); - info->ssid_len = ssid_len; - } - } - - info->valid = TRUE; - - return NL_SKIP; -} - -static void -nl80211_get_bss_info(NMWifiUtilsNl80211 *self, struct nl80211_bss_info *bss_info) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - - memset(bss_info, 0, sizeof(*bss_info)); - - msg = nl80211_alloc_msg(self, NL80211_CMD_GET_SCAN, NLM_F_DUMP); - - nl80211_send_and_recv(self, msg, nl80211_bss_dump_handler, bss_info); -} - static guint32 wifi_nl80211_get_freq(NMWifiUtils *data) { - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - struct nl80211_bss_info bss_info; + NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; + struct nl80211_iface_info iface_info = {}; + nm_auto_nlmsg struct nl_msg *msg = NULL; - nl80211_get_bss_info(self, &bss_info); + msg = nl80211_alloc_msg(self, NL80211_CMD_GET_INTERFACE, 0); - return bss_info.freq; + if (nl80211_send_and_recv(self, msg, nl80211_iface_info_handler, &iface_info) < 0) + return 0; + + return iface_info.freq; } static guint32 @@ -513,41 +406,38 @@ wifi_nl80211_find_freq(NMWifiUtils *data, const guint32 *freqs) return 0; } -static gboolean -wifi_nl80211_get_bssid(NMWifiUtils *data, guint8 *out_bssid) +/* @divisor: pass what value @xbm should be divided by to get dBm */ +static guint32 +nl80211_xbm_to_percent(gint32 xbm, guint32 divisor) { - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - struct nl80211_bss_info bss_info; - - nl80211_get_bss_info(self, &bss_info); +#define NOISE_FLOOR_DBM -90 +#define SIGNAL_MAX_DBM -20 - if (bss_info.valid) - memcpy(out_bssid, bss_info.bssid, ETH_ALEN); + xbm /= divisor; + xbm = CLAMP(xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM); - return bss_info.valid; + return 100 + - 70 + * (((float) SIGNAL_MAX_DBM - (float) xbm) + / ((float) SIGNAL_MAX_DBM - (float) NOISE_FLOOR_DBM)); } -struct nl80211_station_info { - guint32 txrate; - gboolean txrate_valid; - guint8 signal; - gboolean signal_valid; -}; - static int -nl80211_station_handler(struct nl_msg *msg, void *arg) +nl80211_station_dump_handler(struct nl_msg *msg, void *arg) { static const struct nla_policy stats_policy[] = { - [NL80211_STA_INFO_INACTIVE_TIME] = {.type = NLA_U32}, - [NL80211_STA_INFO_RX_BYTES] = {.type = NLA_U32}, - [NL80211_STA_INFO_TX_BYTES] = {.type = NLA_U32}, - [NL80211_STA_INFO_RX_PACKETS] = {.type = NLA_U32}, - [NL80211_STA_INFO_TX_PACKETS] = {.type = NLA_U32}, - [NL80211_STA_INFO_SIGNAL] = {.type = NLA_U8}, - [NL80211_STA_INFO_TX_BITRATE] = {.type = NLA_NESTED}, - [NL80211_STA_INFO_LLID] = {.type = NLA_U16}, - [NL80211_STA_INFO_PLID] = {.type = NLA_U16}, - [NL80211_STA_INFO_PLINK_STATE] = {.type = NLA_U8}, + [NL80211_STA_INFO_INACTIVE_TIME] = {.type = NLA_U32}, + [NL80211_STA_INFO_RX_BYTES] = {.type = NLA_U32}, + [NL80211_STA_INFO_TX_BYTES] = {.type = NLA_U32}, + [NL80211_STA_INFO_RX_PACKETS] = {.type = NLA_U32}, + [NL80211_STA_INFO_TX_PACKETS] = {.type = NLA_U32}, + [NL80211_STA_INFO_SIGNAL] = {.type = NLA_U8}, + [NL80211_STA_INFO_TX_BITRATE] = {.type = NLA_NESTED}, + [NL80211_STA_INFO_LLID] = {.type = NLA_U16}, + [NL80211_STA_INFO_PLID] = {.type = NLA_U16}, + [NL80211_STA_INFO_PLINK_STATE] = {.type = NLA_U8}, + [NL80211_STA_INFO_STA_FLAGS] = {.minlen = sizeof(struct nl80211_sta_flag_update)}, + [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = {.type = NLA_U8}, }; static const struct nla_policy rate_policy[] = { [NL80211_RATE_INFO_BITRATE] = {.type = NLA_U16}, @@ -564,80 +454,94 @@ nl80211_station_handler(struct nl_msg *msg, void *arg) if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) return NL_SKIP; + if (tb[NL80211_ATTR_MAC] == NULL) + return NL_SKIP; + if (tb[NL80211_ATTR_STA_INFO] == NULL) return NL_SKIP; if (nla_parse_nested_arr(sinfo, tb[NL80211_ATTR_STA_INFO], stats_policy)) return NL_SKIP; - if (sinfo[NL80211_STA_INFO_TX_BITRATE] == NULL) - return NL_SKIP; + if (sinfo[NL80211_STA_INFO_STA_FLAGS] != NULL) { + const struct nl80211_sta_flag_update *flags = nla_data(sinfo[NL80211_STA_INFO_STA_FLAGS]); - if (nla_parse_nested_arr(rinfo, sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy)) - return NL_SKIP; + if (flags->mask & ~flags->set & (1 << NL80211_STA_FLAG_ASSOCIATED)) + return NL_SKIP; + } - if (rinfo[NL80211_RATE_INFO_BITRATE] == NULL) - return NL_SKIP; + memcpy(info->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + info->valid = TRUE; - /* convert from nl80211's units of 100kbps to NM's kbps */ - info->txrate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]) * 100; - info->txrate_valid = TRUE; + if (sinfo[NL80211_STA_INFO_TX_BITRATE] != NULL + && !nla_parse_nested_arr(rinfo, sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy) + && rinfo[NL80211_RATE_INFO_BITRATE] != NULL) { + /* convert from nl80211's units of 100kbps to NM's kbps */ + info->txrate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]) * 100; + info->txrate_valid = TRUE; + } if (sinfo[NL80211_STA_INFO_SIGNAL] != NULL) { info->signal = nl80211_xbm_to_percent((gint8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]), 1); info->signal_valid = TRUE; + } else if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG] != NULL) { + /* Fall back to beacon signal strength */ + info->signal = + nl80211_xbm_to_percent((gint8) nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]), 1); + info->signal_valid = TRUE; } return NL_SKIP; } static void -nl80211_get_ap_info(NMWifiUtilsNl80211 *self, struct nl80211_station_info *sta_info) +nl80211_get_sta_info(NMWifiUtilsNl80211 *self) { nm_auto_nlmsg struct nl_msg *msg = NULL; - struct nl80211_bss_info bss_info; - - memset(sta_info, 0, sizeof(*sta_info)); + gint64 now = nm_utils_get_monotonic_timestamp_msec(); - nl80211_get_bss_info(self, &bss_info); - if (!bss_info.valid) + if (self->sta_info.valid && now - self->sta_info.timestamp < 500) return; - msg = nl80211_alloc_msg(self, NL80211_CMD_GET_STATION, 0); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bss_info.bssid); + memset(&self->sta_info, 0, sizeof(self->sta_info)); - nl80211_send_and_recv(self, msg, nl80211_station_handler, sta_info); - if (!sta_info->signal_valid) { - /* Fall back to bss_info signal quality (both are in percent) */ - sta_info->signal = bss_info.beacon_signal; - } + msg = nl80211_alloc_msg(self, NL80211_CMD_GET_STATION, NLM_F_DUMP); - return; + nl80211_send_and_recv(self, msg, nl80211_station_dump_handler, &self->sta_info); + self->sta_info.timestamp = now; +} -nla_put_failure: - g_return_if_reached(); +static gboolean +wifi_nl80211_get_bssid(NMWifiUtils *data, guint8 *out_bssid) +{ + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; + + nl80211_get_sta_info(self); + + if (self->sta_info.valid) + memcpy(out_bssid, self->sta_info.bssid, ETH_ALEN); + + return self->sta_info.valid; } static guint32 wifi_nl80211_get_rate(NMWifiUtils *data) { - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - struct nl80211_station_info sta_info; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; - nl80211_get_ap_info(self, &sta_info); + nl80211_get_sta_info(self); - return sta_info.txrate; + return self->sta_info.txrate; } static int wifi_nl80211_get_qual(NMWifiUtils *data) { - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - struct nl80211_station_info sta_info; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; - nl80211_get_ap_info(self, &sta_info); - return sta_info.signal; + nl80211_get_sta_info(self); + return self->sta_info.signal; } static gboolean |