mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-06 07:58:00 +02:00
iwd: Basic WFD support for NMDeviceIwdP2P
Enable WFD clients to work with the IWD backend.
This commit is contained in:
parent
51ef157096
commit
524675db75
3 changed files with 224 additions and 1 deletions
|
|
@ -41,6 +41,8 @@ typedef struct {
|
|||
bool enabled : 1;
|
||||
|
||||
bool stage2_ready : 1;
|
||||
|
||||
bool wfd_registered : 1;
|
||||
} NMDeviceIwdP2PPrivate;
|
||||
|
||||
struct _NMDeviceIwdP2P {
|
||||
|
|
@ -168,6 +170,70 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_connection_available(NMDevice *device,
|
||||
NMConnection *connection,
|
||||
NMDeviceCheckConAvailableFlags flags,
|
||||
const char *specific_object,
|
||||
GError **error)
|
||||
{
|
||||
NMDeviceIwdP2P *self = NM_DEVICE_IWD_P2P(device);
|
||||
NMDeviceIwdP2PPrivate *priv = NM_DEVICE_IWD_P2P_GET_PRIVATE(self);
|
||||
NMSettingWifiP2P *s_wifi_p2p;
|
||||
GBytes *wfd_ies;
|
||||
NMWifiP2PPeer *peer;
|
||||
|
||||
if (specific_object) {
|
||||
peer = nm_wifi_p2p_peer_lookup_for_device(NM_DEVICE(self), specific_object);
|
||||
if (!peer) {
|
||||
g_set_error(error,
|
||||
NM_UTILS_ERROR,
|
||||
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
|
||||
"The P2P peer %s is unknown",
|
||||
specific_object);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!nm_wifi_p2p_peer_check_compatible(peer, connection)) {
|
||||
nm_utils_error_set_literal(error,
|
||||
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
|
||||
"Requested P2P peer is not compatible with profile");
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
peer = nm_wifi_p2p_peers_find_first_compatible(&priv->peers_lst_head, connection);
|
||||
if (!peer) {
|
||||
nm_utils_error_set_literal(error,
|
||||
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
|
||||
"No compatible P2P peer found");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
s_wifi_p2p =
|
||||
NM_SETTING_WIFI_P2P(nm_connection_get_setting(connection, NM_TYPE_SETTING_WIFI_P2P));
|
||||
wfd_ies = nm_setting_wifi_p2p_get_wfd_ies(s_wifi_p2p);
|
||||
if (wfd_ies) {
|
||||
NMIwdWfdInfo wfd_info = {};
|
||||
|
||||
if (!nm_wifi_utils_parse_wfd_ies(wfd_ies, &wfd_info)) {
|
||||
nm_utils_error_set_literal(error,
|
||||
NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
|
||||
"Can't parse connection WFD IEs");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!nm_iwd_manager_check_wfd_info_compatible(nm_iwd_manager_get(), &wfd_info)) {
|
||||
nm_utils_error_set_literal(error,
|
||||
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
|
||||
"An incompatible WFD connection is active");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
complete_connection(NMDevice *device,
|
||||
NMConnection *connection,
|
||||
|
|
@ -428,6 +494,11 @@ cleanup_connect_attempt(NMDeviceIwdP2P *self)
|
|||
if (priv->find_peer_timeout_source)
|
||||
iwd_release_discovery(self);
|
||||
|
||||
if (priv->wfd_registered) {
|
||||
nm_iwd_manager_unregister_wfd(nm_iwd_manager_get());
|
||||
priv->wfd_registered = FALSE;
|
||||
}
|
||||
|
||||
if (!priv->dbus_peer_proxy)
|
||||
return;
|
||||
|
||||
|
|
@ -473,6 +544,7 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
|
|||
NMConnection *connection;
|
||||
NMSettingWifiP2P *s_wifi_p2p;
|
||||
NMWifiP2PPeer *peer;
|
||||
GBytes *wfd_ies;
|
||||
|
||||
if (!priv->enabled) {
|
||||
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
|
||||
|
|
@ -486,6 +558,44 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
|
|||
NM_SETTING_WIFI_P2P(nm_connection_get_setting(connection, NM_TYPE_SETTING_WIFI_P2P));
|
||||
g_return_val_if_fail(s_wifi_p2p, NM_ACT_STAGE_RETURN_FAILURE);
|
||||
|
||||
/* Set the WFD IEs before connecting and before peer discovery if that is needed,
|
||||
* usually the WFD IEs need to actually be sent in the Probe frames before we can
|
||||
* receive the peers' WFD IEs and decide whether the peer is compatible with the
|
||||
* requested WFD parameters. In the current setup we only get the WFD IEs from
|
||||
* the connection settings so during a normal find the client will not be getting
|
||||
* any WFD information about the peers and has to decide to connect based on the
|
||||
* name and device type (category + subcategory) -- assuming that the peers even
|
||||
* bother to reply to probes without WFD IEs. We'll then need to redo the find
|
||||
* here in PREPARE because IWD wants to see that the parameters in the peer's
|
||||
* WFD IEs match those in our WFD IEs. The normal use case for IWD is that the
|
||||
* WFD client registers its WFD parameters as soon as it starts and they remain
|
||||
* registered during the find and then during the connect. */
|
||||
wfd_ies = nm_setting_wifi_p2p_get_wfd_ies(s_wifi_p2p);
|
||||
if (wfd_ies) {
|
||||
NMIwdWfdInfo wfd_info = {};
|
||||
|
||||
if (!nm_wifi_utils_parse_wfd_ies(wfd_ies, &wfd_info)) {
|
||||
_LOGE(LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi-p2p) Can't parse connection WFD IEs");
|
||||
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
|
||||
return NM_ACT_STAGE_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
if (!nm_iwd_manager_check_wfd_info_compatible(nm_iwd_manager_get(), &wfd_info)) {
|
||||
_LOGE(LOGD_DEVICE | LOGD_WIFI,
|
||||
"Activation: (wifi-p2p) An incompatible WFD connection is active");
|
||||
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
|
||||
return NM_ACT_STAGE_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
if (!nm_iwd_manager_register_wfd(nm_iwd_manager_get(), &wfd_info)) {
|
||||
_LOGE(LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi-p2p) Can't register WFD service");
|
||||
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
|
||||
return NM_ACT_STAGE_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
priv->wfd_registered = TRUE;
|
||||
}
|
||||
|
||||
peer = nm_wifi_p2p_peers_find_first_compatible(&priv->peers_lst_head, connection);
|
||||
if (!peer) {
|
||||
iwd_request_discovery(self, 10);
|
||||
|
|
@ -1126,9 +1236,9 @@ nm_device_iwd_p2p_class_init(NMDeviceIwdP2PClass *klass)
|
|||
device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_WIFI_P2P);
|
||||
device_class->get_type_description = get_type_description;
|
||||
|
||||
/* Do we need compatibility checking or is the default good enough? */
|
||||
device_class->is_available = is_available;
|
||||
device_class->check_connection_compatible = check_connection_compatible;
|
||||
device_class->check_connection_available = check_connection_available;
|
||||
device_class->complete_connection = complete_connection;
|
||||
device_class->get_enabled = get_enabled;
|
||||
device_class->set_enabled = set_enabled;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ typedef struct {
|
|||
char *warned_state_dir;
|
||||
bool netconfig_enabled;
|
||||
GHashTable *p2p_devices;
|
||||
NMIwdWfdInfo wfd_info;
|
||||
guint wfd_use_count;
|
||||
} NMIwdManagerPrivate;
|
||||
|
||||
struct _NMIwdManager {
|
||||
|
|
@ -1759,6 +1761,113 @@ nm_iwd_manager_get_netconfig_enabled(NMIwdManager *self)
|
|||
return priv->netconfig_enabled;
|
||||
}
|
||||
|
||||
/* IWD's net.connman.iwd.p2p.ServiceManager.RegisterDisplayService() is global so
|
||||
* two local Wi-Fi P2P devices can't be connected to (or even scanning for) WFD
|
||||
* peers using different WFD IE contents, e.g. one as a sink and one as a source.
|
||||
* If one device is connected to a peer without a WFD service, another can try
|
||||
* to establish a WFD connection to a peer since this won't disturb the first
|
||||
* connection. Similarly if one device is connected to a peer with WFD, another
|
||||
* can make a connection to a non-WFD peer (if that exists...) because a non-WFD
|
||||
* peer will simply ignore the WFD IEs, but it cannot connect to or search for a
|
||||
* peer that's WFD capable without passing our own WFD IEs, i.e. if the new
|
||||
* NMSettingsConnection has no WFD IEs and we're already in a WFD connection on
|
||||
* another device, we can't activate that new connection. We expose methods
|
||||
* for the NMDeviceIwdP2P's to register/unregister the service and one to check
|
||||
* if there's already an incompatible connection active.
|
||||
*/
|
||||
gboolean
|
||||
nm_iwd_manager_check_wfd_info_compatible(NMIwdManager *self, const NMIwdWfdInfo *wfd_info)
|
||||
{
|
||||
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
|
||||
|
||||
if (priv->wfd_use_count == 0)
|
||||
return TRUE;
|
||||
|
||||
return nm_wifi_utils_wfd_info_eq(&priv->wfd_info, wfd_info);
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_iwd_manager_register_wfd(NMIwdManager *self, const NMIwdWfdInfo *wfd_info)
|
||||
{
|
||||
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
|
||||
gs_unref_object GDBusInterface *service_manager = NULL;
|
||||
GVariantBuilder builder;
|
||||
|
||||
nm_assert(nm_iwd_manager_check_wfd_info_compatible(self, wfd_info));
|
||||
|
||||
if (!priv->object_manager)
|
||||
return FALSE;
|
||||
|
||||
service_manager = g_dbus_object_manager_get_interface(priv->object_manager,
|
||||
"/net/connman/iwd",
|
||||
NM_IWD_P2P_SERVICE_MANAGER_INTERFACE);
|
||||
if (!service_manager) {
|
||||
_LOGE("IWD P2P service manager not found");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
|
||||
g_variant_builder_add(&builder, "{sv}", "Source", g_variant_new_boolean(wfd_info->source));
|
||||
g_variant_builder_add(&builder, "{sv}", "Sink", g_variant_new_boolean(wfd_info->sink));
|
||||
|
||||
if (wfd_info->source)
|
||||
g_variant_builder_add(&builder, "{sv}", "Port", g_variant_new_uint16(wfd_info->port));
|
||||
|
||||
if (wfd_info->sink && wfd_info->has_audio)
|
||||
g_variant_builder_add(&builder, "{sv}", "HasAudio", g_variant_new_boolean(TRUE));
|
||||
|
||||
if (wfd_info->has_uibc)
|
||||
g_variant_builder_add(&builder, "{sv}", "HasUIBC", g_variant_new_boolean(TRUE));
|
||||
|
||||
if (wfd_info->has_cp)
|
||||
g_variant_builder_add(&builder,
|
||||
"{sv}",
|
||||
"HasContentProtection",
|
||||
g_variant_new_boolean(TRUE));
|
||||
|
||||
g_dbus_proxy_call(G_DBUS_PROXY(service_manager),
|
||||
"RegisterDisplayService",
|
||||
g_variant_new("(a{sv})", &builder),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
memcpy(&priv->wfd_info, wfd_info, sizeof(priv->wfd_info));
|
||||
priv->wfd_use_count++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nm_iwd_manager_unregister_wfd(NMIwdManager *self)
|
||||
{
|
||||
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
|
||||
gs_unref_object GDBusInterface *service_manager = NULL;
|
||||
|
||||
nm_assert(priv->wfd_use_count > 0);
|
||||
|
||||
priv->wfd_use_count--;
|
||||
|
||||
if (!priv->object_manager)
|
||||
return;
|
||||
|
||||
service_manager = g_dbus_object_manager_get_interface(priv->object_manager,
|
||||
"/net/connman/iwd",
|
||||
NM_IWD_P2P_SERVICE_MANAGER_INTERFACE);
|
||||
if (!service_manager)
|
||||
return;
|
||||
|
||||
g_dbus_proxy_call(G_DBUS_PROXY(service_manager),
|
||||
"UnregisterDisplayService",
|
||||
g_variant_new("()"),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
NM_DEFINE_SINGLETON_GETTER(NMIwdManager, nm_iwd_manager_get, NM_TYPE_IWD_MANAGER);
|
||||
|
|
|
|||
|
|
@ -59,4 +59,8 @@ nm_iwd_manager_get_dbus_interface(NMIwdManager *self, const char *path, const ch
|
|||
|
||||
gboolean nm_iwd_manager_get_netconfig_enabled(NMIwdManager *self);
|
||||
|
||||
gboolean nm_iwd_manager_check_wfd_info_compatible(NMIwdManager *self, const NMIwdWfdInfo *wfd_info);
|
||||
gboolean nm_iwd_manager_register_wfd(NMIwdManager *self, const NMIwdWfdInfo *wfd_info);
|
||||
void nm_iwd_manager_unregister_wfd(NMIwdManager *self);
|
||||
|
||||
#endif /* __NETWORKMANAGER_IWD_MANAGER_H__ */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue