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.
This commit is contained in:
tinnci 2026-01-29 21:03:31 +08:00
parent 716efd5377
commit ffde9ce3de
3 changed files with 62 additions and 8 deletions

View file

@ -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,

View file

@ -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);

View file

@ -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;
}