diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c index 3551a9e486..f4124bafe5 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.c +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -82,9 +82,9 @@ get_autoconnect_allowed (NMDevice *device) NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); - /* We shall always have a companion if we're >= DISCONENCTED, and this - * ought not be called until then. */ - g_return_val_if_fail (priv->companion, FALSE); + /* We can't even connect if we don't have a companion yet. */ + if (!priv->companion) + return FALSE; /* We must not attempt to autoconnect when the companion is connected or * connecting, * because we'd tear down its connection. */ @@ -170,17 +170,28 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) return NM_ACT_STAGE_RETURN_SUCCESS; } -static void +static gboolean _mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel) { NMPlatform *platform; int ifindex = nm_device_get_ifindex (NM_DEVICE (self)); + guint32 old_channel; platform = nm_device_get_platform (NM_DEVICE (self)); - if (nm_platform_mesh_get_channel (platform, ifindex) != channel) { - if (nm_platform_mesh_set_channel (platform, ifindex, channel)) - _notify (self, PROP_ACTIVE_CHANNEL); - } + old_channel = nm_platform_mesh_get_channel (platform, ifindex); + + if (channel == 0) + channel = old_channel; + + /* We want to call this even if the channel number is the same, + * because that actually starts the mesh with the configured mesh ID. */ + if (!nm_platform_mesh_set_channel (platform, ifindex, channel)) + return FALSE; + + if (old_channel != channel) + _notify (self, PROP_ACTIVE_CHANNEL); + + return TRUE; } static NMActStageReturn @@ -188,27 +199,35 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) { NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); NMSettingOlpcMesh *s_mesh; - guint32 channel; GBytes *ssid; const char *anycast_addr; + gboolean success; s_mesh = nm_device_get_applied_setting (device, NM_TYPE_SETTING_OLPC_MESH); g_return_val_if_fail (s_mesh, NM_ACT_STAGE_RETURN_FAILURE); - channel = nm_setting_olpc_mesh_get_channel (s_mesh); - if (channel != 0) - _mesh_set_channel (self, channel); - ssid = nm_setting_olpc_mesh_get_ssid (s_mesh); - nm_platform_mesh_set_ssid (nm_device_get_platform (device), - nm_device_get_ifindex (device), - g_bytes_get_data (ssid, NULL), - g_bytes_get_size (ssid)); + nm_device_take_down (NM_DEVICE (self), TRUE); + success = nm_platform_mesh_set_ssid (nm_device_get_platform (device), + nm_device_get_ifindex (device), + g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + nm_device_bring_up (NM_DEVICE (self), TRUE, NULL); + if (!success) { + _LOGW (LOGD_WIFI, "Unable to set the mesh ID"); + return NM_ACT_STAGE_RETURN_FAILURE; + } anycast_addr = nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh); nm_device_set_dhcp_anycast_address (device, anycast_addr); + if (!_mesh_set_channel (self, nm_setting_olpc_mesh_get_channel (s_mesh))) { + _LOGW (LOGD_WIFI, "Unable to set the mesh channel"); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + return NM_ACT_STAGE_RETURN_SUCCESS; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 3a6f18aabe..a1ffd30543 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -2883,19 +2883,11 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr && obj->_link.ext_data == NULL) { switch (obj->link.type) { case NM_LINK_TYPE_WIFI: + case NM_LINK_TYPE_OLPC_MESH: obj->_link.ext_data = (GObject *) nm_wifi_utils_new (ifi->ifi_index, _genl_sock (NM_LINUX_PLATFORM (platform)), TRUE); break; - case NM_LINK_TYPE_OLPC_MESH: -#if HAVE_WEXT - /* The kernel driver now uses nl80211, but we force use of WEXT because - * the cfg80211 interactions are not quite ready to support access to - * mesh control through nl80211 just yet. - */ - obj->_link.ext_data = (GObject *) nm_wifi_utils_wext_new (ifi->ifi_index, FALSE); -#endif - break; case NM_LINK_TYPE_WPAN: obj->_link.ext_data = (GObject *) nm_wpan_utils_new (ifi->ifi_index, _genl_sock (NM_LINUX_PLATFORM (platform)), diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.c b/src/platform/wifi/nm-wifi-utils-nl80211.c index 33c4e1d31d..24ff2fb5df 100644 --- a/src/platform/wifi/nm-wifi-utils-nl80211.c +++ b/src/platform/wifi/nm-wifi-utils-nl80211.c @@ -706,6 +706,7 @@ struct nl80211_device_info { int phy; guint32 *freqs; int num_freqs; + guint32 freq; guint32 caps; gboolean can_scan; gboolean can_scan_ssid; @@ -767,6 +768,11 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) info->phy = nla_get_u32 (tb[NL80211_ATTR_WIPHY]); + if (tb[NL80211_ATTR_WIPHY_FREQ]) + info->freq = nla_get_u32 (tb[NL80211_ATTR_WIPHY_FREQ]); + else + info->freq = 0; + if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) { info->can_scan_ssid = nla_get_u8 (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) > 0; @@ -915,6 +921,64 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) return NL_SKIP; } +static guint32 +wifi_nl80211_get_mesh_channel (NMWifiUtils *data) +{ + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; + nm_auto_nlmsg struct nl_msg *msg = NULL; + struct nl80211_device_info device_info = { .self = self }; + int i; + + msg = nl80211_alloc_msg (self, NL80211_CMD_GET_WIPHY, 0); + + if (nl80211_send_and_recv (self, msg, nl80211_wiphy_info_handler, + &device_info) < 0) { + _LOGW ("NL80211_CMD_GET_WIPHY request failed"); + return 0; + } + + for (i = 0; i < self->num_freqs; i++) { + if (device_info.freq == self->freqs[i]) + return i + 1; + } + return 0; +} + +static gboolean +wifi_nl80211_set_mesh_channel (NMWifiUtils *data, guint32 channel) +{ + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; + nm_auto_nlmsg struct nl_msg *msg = NULL; + int err; + + if (channel > self->num_freqs) + return FALSE; + + msg = nl80211_alloc_msg (self, NL80211_CMD_SET_WIPHY, 0); + NLA_PUT_U32 (msg, NL80211_ATTR_WIPHY_FREQ, self->freqs[channel - 1]); + err = nl80211_send_and_recv (self, msg, NULL, NULL); + return err >= 0; + +nla_put_failure: + g_return_val_if_reached (FALSE); +} + +static gboolean +wifi_nl80211_set_mesh_ssid (NMWifiUtils *data, const guint8 *ssid, gsize len) +{ + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; + nm_auto_nlmsg struct nl_msg *msg = NULL; + int err; + + msg = nl80211_alloc_msg (self, NL80211_CMD_SET_INTERFACE, 0); + NLA_PUT (msg, NL80211_ATTR_MESH_ID, len, ssid); + err = nl80211_send_and_recv (self, msg, NULL, NULL); + return err >= 0; + +nla_put_failure: + g_return_val_if_reached (FALSE); +} + static void nm_wifi_utils_nl80211_init (NMWifiUtilsNl80211 *self) { @@ -939,6 +1003,9 @@ nm_wifi_utils_nl80211_class_init (NMWifiUtilsNl80211Class *klass) wifi_utils_class->get_rate = wifi_nl80211_get_rate; wifi_utils_class->get_qual = wifi_nl80211_get_qual; wifi_utils_class->indicate_addressing_running = wifi_nl80211_indicate_addressing_running; + wifi_utils_class->get_mesh_channel = wifi_nl80211_get_mesh_channel; + wifi_utils_class->set_mesh_channel = wifi_nl80211_set_mesh_channel; + wifi_utils_class->set_mesh_ssid = wifi_nl80211_set_mesh_ssid; } NMWifiUtils *