mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-29 16:10:11 +01:00
core: better missing firmware handling when firmware appears (rh #609587)
Monitor the kernel firmware directory (set at configure-time with --with-kernel-firmware-dir=<path>) for changes, and if there are any, try bringing up devices that are missing firmware.
This commit is contained in:
parent
c38d1c28ff
commit
62d03cf33e
5 changed files with 141 additions and 14 deletions
10
configure.ac
10
configure.ac
|
|
@ -423,6 +423,16 @@ fi
|
|||
AC_DEFINE_UNQUOTED(SYSTEM_CA_PATH, "$SYSTEM_CA_PATH", [Define to path to system CA certificates])
|
||||
AC_SUBST(SYSTEM_CA_PATH)
|
||||
|
||||
AC_ARG_WITH(kernel-firmware-dir, AS_HELP_STRING([--with-kernel-firmware-dir=DIR], [where kernel firmware directory is (default is /lib/firmware)]))
|
||||
|
||||
if test -n "$with_kernel_firmware_dir" ; then
|
||||
KERNEL_FIRMWARE_DIR="$with_kernel_firmware_dir"
|
||||
else
|
||||
KERNEL_FIRMWARE_DIR="/lib/firmware"
|
||||
fi
|
||||
AC_DEFINE_UNQUOTED(KERNEL_FIRMWARE_DIR, "$KERNEL_FIRMWARE_DIR", [Define to path of the kernel firmware directory])
|
||||
AC_SUBST(KERNEL_FIRMWARE_DIR)
|
||||
|
||||
NM_COMPILER_WARNINGS
|
||||
|
||||
GTK_DOC_CHECK(1.0)
|
||||
|
|
|
|||
|
|
@ -42,4 +42,8 @@ void nm_device_handle_autoip4_event (NMDevice *self,
|
|||
|
||||
gboolean nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6);
|
||||
|
||||
gboolean nm_device_get_firmware_missing (NMDevice *self);
|
||||
|
||||
void nm_device_set_firmware_missing (NMDevice *self, gboolean missing);
|
||||
|
||||
#endif /* NM_DEVICE_PRIVATE_H */
|
||||
|
|
|
|||
|
|
@ -2609,7 +2609,8 @@ supplicant_mgr_state_cb_handler (gpointer user_data)
|
|||
dev_state = nm_device_get_state (dev);
|
||||
if ( priv->enabled
|
||||
&& !priv->supplicant.iface
|
||||
&& (dev_state >= NM_DEVICE_STATE_UNAVAILABLE)) {
|
||||
&& (dev_state >= NM_DEVICE_STATE_UNAVAILABLE)
|
||||
&& (nm_device_get_firmware_missing (NM_DEVICE (self)) == FALSE)) {
|
||||
/* request a supplicant interface from the supplicant manager */
|
||||
supplicant_interface_acquire (self);
|
||||
|
||||
|
|
@ -3599,7 +3600,7 @@ device_state_changed (NMDevice *device,
|
|||
* acquire a supplicant interface and transition to DISCONNECTED because
|
||||
* the device is now ready to use.
|
||||
*/
|
||||
if (priv->enabled) {
|
||||
if (priv->enabled && (nm_device_get_firmware_missing (device) == FALSE)) {
|
||||
gboolean success;
|
||||
struct iw_range range;
|
||||
|
||||
|
|
@ -3692,8 +3693,12 @@ real_set_enabled (NMDeviceInterface *device, gboolean enabled)
|
|||
nm_log_dbg (LOGD_WIFI, "(%s): enable blocked by failure to bring device up",
|
||||
nm_device_get_iface (NM_DEVICE (device)));
|
||||
|
||||
/* The device sucks, or HAL was lying to us about the killswitch state */
|
||||
priv->enabled = FALSE;
|
||||
if (no_firmware)
|
||||
nm_device_set_firmware_missing (NM_DEVICE (device), TRUE);
|
||||
else {
|
||||
/* The device sucks, or the kernel was lying to us about the killswitch state */
|
||||
priv->enabled = FALSE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3655,17 +3655,27 @@ unavailable_to_disconnected (gpointer user_data)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
set_firmware_missing (NMDevice *self, gboolean new_missing)
|
||||
void
|
||||
nm_device_set_firmware_missing (NMDevice *self, gboolean new_missing)
|
||||
{
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
NMDevicePrivate *priv;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (NM_IS_DEVICE (self));
|
||||
|
||||
priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
if (priv->firmware_missing != new_missing) {
|
||||
priv->firmware_missing = new_missing;
|
||||
g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_FIRMWARE_MISSING);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_device_get_firmware_missing (NMDevice *self)
|
||||
{
|
||||
return NM_DEVICE_GET_PRIVATE (self)->firmware_missing;
|
||||
}
|
||||
|
||||
void
|
||||
nm_device_state_changed (NMDevice *device,
|
||||
NMDeviceState state,
|
||||
|
|
@ -3678,7 +3688,12 @@ nm_device_state_changed (NMDevice *device,
|
|||
|
||||
g_return_if_fail (NM_IS_DEVICE (device));
|
||||
|
||||
if (priv->state == state)
|
||||
/* Do nothing if state isn't changing, but as a special case allow
|
||||
* re-setting UNAVAILABLE if the device is missing firmware so that we
|
||||
* can retry device initialization.
|
||||
*/
|
||||
if ( (priv->state == state)
|
||||
&& !(state == NM_DEVICE_STATE_UNAVAILABLE && priv->firmware_missing))
|
||||
return;
|
||||
|
||||
old_state = priv->state;
|
||||
|
|
@ -3698,16 +3713,15 @@ nm_device_state_changed (NMDevice *device,
|
|||
*/
|
||||
switch (state) {
|
||||
case NM_DEVICE_STATE_UNMANAGED:
|
||||
set_firmware_missing (device, FALSE);
|
||||
nm_device_set_firmware_missing (device, FALSE);
|
||||
if (old_state > NM_DEVICE_STATE_UNMANAGED)
|
||||
nm_device_take_down (device, TRUE, reason);
|
||||
break;
|
||||
case NM_DEVICE_STATE_UNAVAILABLE:
|
||||
if (old_state == NM_DEVICE_STATE_UNMANAGED) {
|
||||
if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware) {
|
||||
nm_log_warn (LOGD_HW, "%s: firmware may be missing.", nm_device_get_iface (device));
|
||||
set_firmware_missing (device, TRUE);
|
||||
}
|
||||
if (old_state == NM_DEVICE_STATE_UNMANAGED || priv->firmware_missing) {
|
||||
if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware)
|
||||
nm_log_warn (LOGD_HW, "(%s): firmware may be missing.", nm_device_get_iface (device));
|
||||
nm_device_set_firmware_missing (device, no_firmware ? TRUE : FALSE);
|
||||
}
|
||||
/* Ensure the device gets deactivated in response to stuff like
|
||||
* carrier changes or rfkill. But don't deactivate devices that are
|
||||
|
|
|
|||
|
|
@ -221,6 +221,11 @@ typedef struct {
|
|||
guint auth_changed_id;
|
||||
GSList *auth_chains;
|
||||
|
||||
/* Firmware dir monitor */
|
||||
GFileMonitor *fw_monitor;
|
||||
guint fw_monitor_id;
|
||||
guint fw_changed_id;
|
||||
|
||||
gboolean disposed;
|
||||
} NMManagerPrivate;
|
||||
|
||||
|
|
@ -3884,6 +3889,65 @@ nm_manager_start (NMManager *self)
|
|||
bluez_manager_resync_devices (self);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_firmware_changed (gpointer user_data)
|
||||
{
|
||||
NMManager *self = NM_MANAGER (user_data);
|
||||
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
|
||||
GSList *iter;
|
||||
|
||||
priv->fw_changed_id = 0;
|
||||
|
||||
if (manager_sleeping (self))
|
||||
return FALSE;
|
||||
|
||||
/* Try to re-enable devices with missing firmware */
|
||||
for (iter = priv->devices; iter; iter = iter->next) {
|
||||
NMDevice *candidate = NM_DEVICE (iter->data);
|
||||
NMDeviceState state = nm_device_get_state (candidate);
|
||||
|
||||
if ( nm_device_get_firmware_missing (candidate)
|
||||
&& (state == NM_DEVICE_STATE_UNAVAILABLE)) {
|
||||
nm_log_info (LOGD_CORE, "(%s): firmware may now be available",
|
||||
nm_device_get_iface (candidate));
|
||||
|
||||
/* Re-set unavailable state to try bringing the device up again */
|
||||
nm_device_state_changed (candidate,
|
||||
NM_DEVICE_STATE_UNAVAILABLE,
|
||||
NM_DEVICE_STATE_REASON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
firmware_dir_changed (GFileMonitor *monitor,
|
||||
GFile *file,
|
||||
GFile *other_file,
|
||||
GFileMonitorEvent event_type,
|
||||
gpointer user_data)
|
||||
{
|
||||
NMManager *self = NM_MANAGER (user_data);
|
||||
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
|
||||
|
||||
switch (event_type) {
|
||||
case G_FILE_MONITOR_EVENT_CREATED:
|
||||
case G_FILE_MONITOR_EVENT_CHANGED:
|
||||
case G_FILE_MONITOR_EVENT_MOVED:
|
||||
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
|
||||
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
|
||||
if (!priv->fw_changed_id) {
|
||||
priv->fw_changed_id = g_timeout_add_seconds (4, handle_firmware_changed, self);
|
||||
nm_log_info (LOGD_CORE, "kernel firmware directory '%s' changed",
|
||||
KERNEL_FIRMWARE_DIR);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NMManager *
|
||||
nm_manager_get (const char *config_file,
|
||||
const char *plugins,
|
||||
|
|
@ -4039,6 +4103,17 @@ dispose (GObject *object)
|
|||
if (priv->bluez_mgr)
|
||||
g_object_unref (priv->bluez_mgr);
|
||||
|
||||
if (priv->fw_monitor) {
|
||||
if (priv->fw_monitor_id)
|
||||
g_signal_handler_disconnect (priv->fw_monitor, priv->fw_monitor_id);
|
||||
|
||||
if (priv->fw_changed_id)
|
||||
g_source_remove (priv->fw_changed_id);
|
||||
|
||||
g_file_monitor_cancel (priv->fw_monitor);
|
||||
g_object_unref (priv->fw_monitor);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
|
|
@ -4118,6 +4193,7 @@ nm_manager_init (NMManager *manager)
|
|||
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
|
||||
DBusGConnection *g_connection;
|
||||
guint id, i;
|
||||
GFile *file;
|
||||
|
||||
/* Initialize rfkill structures and states */
|
||||
memset (priv->radio_states, 0, sizeof (priv->radio_states));
|
||||
|
|
@ -4208,6 +4284,24 @@ nm_manager_init (NMManager *manager)
|
|||
manager);
|
||||
} else
|
||||
nm_log_warn (LOGD_CORE, "failed to create PolicyKit authority.");
|
||||
|
||||
/* Monitor the firmware directory */
|
||||
if (strlen (KERNEL_FIRMWARE_DIR)) {
|
||||
file = g_file_new_for_path (KERNEL_FIRMWARE_DIR "/");
|
||||
priv->fw_monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
|
||||
g_object_unref (file);
|
||||
}
|
||||
|
||||
if (priv->fw_monitor) {
|
||||
priv->fw_monitor_id = g_signal_connect (priv->fw_monitor, "changed",
|
||||
G_CALLBACK (firmware_dir_changed),
|
||||
manager);
|
||||
nm_log_info (LOGD_CORE, "monitoring kernel firmware directory '%s'.",
|
||||
KERNEL_FIRMWARE_DIR);
|
||||
} else {
|
||||
nm_log_warn (LOGD_CORE, "failed to monitor kernel firmware directory '%s'.",
|
||||
KERNEL_FIRMWARE_DIR);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue