From ffde9ce3dede7aecc60fd19347070cda8a37f5f1 Mon Sep 17 00:00:00 2001 From: tinnci Date: Thu, 29 Jan 2026 21:03:31 +0800 Subject: [PATCH] wifi: allow concurrent P2P and station operation Check interface combination capabilities before activation using the new nm_platform_wifi_can_concurrent() API. If hardware supports concurrency: 1. P2P Device: Log the capability. 2. Wi-Fi Device: Prevent the Station interface from being marked as unavailable when the supplicant state fluctuates during P2P operations. This ensures the infrastructure connection is not torn down when a P2P connection is established on capable hardware. --- src/core/devices/wifi/nm-device-wifi-p2p.c | 40 ++++++++++++++++++++-- src/core/devices/wifi/nm-device-wifi-p2p.h | 4 ++- src/core/devices/wifi/nm-device-wifi.c | 26 +++++++++++--- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.c b/src/core/devices/wifi/nm-device-wifi-p2p.c index 957a2df64f..e26271cb07 100644 --- a/src/core/devices/wifi/nm-device-wifi-p2p.c +++ b/src/core/devices/wifi/nm-device-wifi-p2p.c @@ -53,6 +53,9 @@ typedef struct { guint peer_dump_id; guint peer_missing_id; + /* Parent Wi-Fi device ifindex for checking interface combination capabilities */ + int parent_ifindex; + bool is_waiting_for_supplicant : 1; bool enabled : 1; } NMDeviceWifiP2PPrivate; @@ -365,6 +368,32 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) return NM_ACT_STAGE_RETURN_FAILURE; } + /* Check if the hardware supports concurrent Station + P2P operation. + * We use NL80211_IFTYPE_STATION (2) and NL80211_IFTYPE_P2P_CLIENT (8). + * If concurrency is not supported, log a warning but proceed anyway + * (the kernel/supplicant will handle the actual switching). + */ + if (priv->parent_ifindex > 0) { + guint8 num_channels = 0; + gboolean can_concurrent; + + can_concurrent = nm_platform_wifi_can_concurrent(NM_PLATFORM_GET, + priv->parent_ifindex, + NM_WIFI_IFACE_TYPE_STATION, + NM_WIFI_IFACE_TYPE_P2P_CLIENT, + &num_channels); + + if (can_concurrent) { + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "P2P: Hardware supports concurrent Station + P2P operation (channels: %u)", + num_channels); + } else { + _LOGW(LOGD_DEVICE | LOGD_WIFI, + "P2P: Hardware may not support concurrent Station + P2P operation. " + "Infrastructure connection may be interrupted."); + } + } + connection = nm_device_get_applied_connection(NM_DEVICE(self)); g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE); @@ -1158,7 +1187,9 @@ nm_device_wifi_p2p_get_mgmt_iface(NMDeviceWifiP2P *self) } void -nm_device_wifi_p2p_set_mgmt_iface(NMDeviceWifiP2P *self, NMSupplicantInterface *iface) +nm_device_wifi_p2p_set_mgmt_iface(NMDeviceWifiP2P *self, + NMSupplicantInterface *iface, + int parent_ifindex) { NMDeviceWifiP2PPrivate *priv; @@ -1172,14 +1203,17 @@ nm_device_wifi_p2p_set_mgmt_iface(NMDeviceWifiP2P *self, NMSupplicantInterface * supplicant_interfaces_release(self, FALSE); - if (!iface) + if (!iface) { + priv->parent_ifindex = 0; goto done; + } _LOGD(LOGD_DEVICE | LOGD_WIFI, "P2P: WPA supplicant management interface changed to %s.", nm_ref_string_get_str(nm_supplicant_interface_get_object_path(iface))); - priv->mgmt_iface = g_object_ref(iface); + priv->mgmt_iface = g_object_ref(iface); + priv->parent_ifindex = parent_ifindex; g_signal_connect(priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_STATE, diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.h b/src/core/devices/wifi/nm-device-wifi-p2p.h index 08780464ed..96f18997bd 100644 --- a/src/core/devices/wifi/nm-device-wifi-p2p.h +++ b/src/core/devices/wifi/nm-device-wifi-p2p.h @@ -31,7 +31,9 @@ GType nm_device_wifi_p2p_get_type(void); NMDeviceWifiP2P *nm_device_wifi_p2p_new(const char *iface); NMSupplicantInterface *nm_device_wifi_p2p_get_mgmt_iface(NMDeviceWifiP2P *self); -void nm_device_wifi_p2p_set_mgmt_iface(NMDeviceWifiP2P *self, NMSupplicantInterface *iface); +void nm_device_wifi_p2p_set_mgmt_iface(NMDeviceWifiP2P *self, + NMSupplicantInterface *iface, + int parent_ifindex); void nm_device_wifi_p2p_remove(NMDeviceWifiP2P *self); diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c index 1fd096426e..651360645c 100644 --- a/src/core/devices/wifi/nm-device-wifi.c +++ b/src/core/devices/wifi/nm-device-wifi.c @@ -702,7 +702,7 @@ supplicant_interface_release(NMDeviceWifi *self) if (priv->p2p_device) { /* Signal to P2P device to also release its reference */ - nm_device_wifi_p2p_set_mgmt_iface(priv->p2p_device, NULL); + nm_device_wifi_p2p_set_mgmt_iface(priv->p2p_device, NULL, 0); } _scan_notify_is_scanning(self); @@ -1344,8 +1344,22 @@ is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags) supplicant_state = nm_supplicant_interface_get_state(priv->sup_iface); if (supplicant_state <= NM_SUPPLICANT_INTERFACE_STATE_STARTING - || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) + || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) { + guint8 num_channels = 0; + + if (nm_platform_wifi_can_concurrent(nm_device_get_platform(device), + nm_device_get_ifindex(device), + NM_WIFI_IFACE_TYPE_STATION, + NM_WIFI_IFACE_TYPE_P2P_CLIENT, + &num_channels)) { + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Device is available (state %d) due to hardware concurrency support", + supplicant_state); + return TRUE; + } + return FALSE; + } return TRUE; } @@ -2749,7 +2763,9 @@ recheck_p2p_availability(NMDeviceWifi *self) priv->p2p_device = nm_device_wifi_p2p_new(iface_name); - nm_device_wifi_p2p_set_mgmt_iface(priv->p2p_device, priv->sup_iface); + nm_device_wifi_p2p_set_mgmt_iface(priv->p2p_device, + priv->sup_iface, + nm_device_get_ifindex(NM_DEVICE(self))); g_signal_emit(self, signals[P2P_DEVICE_CREATED], 0, priv->p2p_device); g_object_add_weak_pointer(G_OBJECT(priv->p2p_device), (gpointer *) &priv->p2p_device); @@ -2758,7 +2774,9 @@ recheck_p2p_availability(NMDeviceWifi *self) } if (p2p_available && priv->p2p_device) { - nm_device_wifi_p2p_set_mgmt_iface(priv->p2p_device, priv->sup_iface); + nm_device_wifi_p2p_set_mgmt_iface(priv->p2p_device, + priv->sup_iface, + nm_device_get_ifindex(NM_DEVICE(self))); return; }