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:
Dan Williams 2010-07-01 10:32:11 -07:00
parent c38d1c28ff
commit 62d03cf33e
5 changed files with 141 additions and 14 deletions

View file

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

View file

@ -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 */

View file

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

View file

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

View file

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