device: Implement suspend/resume methods
The assumption here is that in most cases, we will just cancel any ongoing operation. However, if the device choses to implement suspend/resume handling and it returns success, then operations will not be cancelled. Note that suspend/resume requests cannot be cancelled. Closes: #256
This commit is contained in:
parent
968331c383
commit
43336a204f
5 changed files with 560 additions and 25 deletions
|
@ -53,6 +53,7 @@ typedef struct
|
||||||
|
|
||||||
gboolean is_removed;
|
gboolean is_removed;
|
||||||
gboolean is_open;
|
gboolean is_open;
|
||||||
|
gboolean is_suspended;
|
||||||
|
|
||||||
gchar *device_id;
|
gchar *device_id;
|
||||||
gchar *device_name;
|
gchar *device_name;
|
||||||
|
@ -83,6 +84,12 @@ typedef struct
|
||||||
guint critical_section;
|
guint critical_section;
|
||||||
GSource *critical_section_flush_source;
|
GSource *critical_section_flush_source;
|
||||||
gboolean cancel_queued;
|
gboolean cancel_queued;
|
||||||
|
gboolean suspend_queued;
|
||||||
|
gboolean resume_queued;
|
||||||
|
|
||||||
|
/* Suspend/resume tasks */
|
||||||
|
GTask *suspend_resume_task;
|
||||||
|
GError *suspend_error;
|
||||||
|
|
||||||
/* Device temperature model information and state */
|
/* Device temperature model information and state */
|
||||||
GSource *temp_timeout;
|
GSource *temp_timeout;
|
||||||
|
@ -123,5 +130,7 @@ typedef struct
|
||||||
|
|
||||||
void match_data_free (FpMatchData *match_data);
|
void match_data_free (FpMatchData *match_data);
|
||||||
|
|
||||||
|
void fpi_device_configure_wakeup (FpDevice *device,
|
||||||
|
gboolean enabled);
|
||||||
void fpi_device_update_temp (FpDevice *device,
|
void fpi_device_update_temp (FpDevice *device,
|
||||||
gboolean is_active);
|
gboolean is_active);
|
||||||
|
|
|
@ -339,6 +339,24 @@ fp_device_set_property (GObject *object,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
device_idle_probe_cb (FpDevice *self, gpointer user_data)
|
||||||
|
{
|
||||||
|
/* This should not be an idle handler, see comment where it is registered.
|
||||||
|
*
|
||||||
|
* This effectively disables USB "persist" for us, and possibly turns off
|
||||||
|
* USB wakeup if it was enabled for some reason.
|
||||||
|
*/
|
||||||
|
fpi_device_configure_wakeup (self, FALSE);
|
||||||
|
|
||||||
|
if (!FP_DEVICE_GET_CLASS (self)->probe)
|
||||||
|
fpi_device_probe_complete (self, NULL, NULL, NULL);
|
||||||
|
else
|
||||||
|
FP_DEVICE_GET_CLASS (self)->probe (self);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fp_device_async_initable_init_async (GAsyncInitable *initable,
|
fp_device_async_initable_init_async (GAsyncInitable *initable,
|
||||||
int io_priority,
|
int io_priority,
|
||||||
|
@ -358,17 +376,16 @@ fp_device_async_initable_init_async (GAsyncInitable *initable,
|
||||||
if (g_task_return_error_if_cancelled (task))
|
if (g_task_return_error_if_cancelled (task))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!FP_DEVICE_GET_CLASS (self)->probe)
|
|
||||||
{
|
|
||||||
g_task_return_boolean (task, TRUE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->current_action = FPI_DEVICE_ACTION_PROBE;
|
priv->current_action = FPI_DEVICE_ACTION_PROBE;
|
||||||
priv->current_task = g_steal_pointer (&task);
|
priv->current_task = g_steal_pointer (&task);
|
||||||
setup_task_cancellable (self);
|
setup_task_cancellable (self);
|
||||||
|
|
||||||
FP_DEVICE_GET_CLASS (self)->probe (self);
|
/* We push this into an idle handler for compatibility with libgusb
|
||||||
|
* 0.3.7 and before.
|
||||||
|
* See https://github.com/hughsie/libgusb/pull/50
|
||||||
|
*/
|
||||||
|
g_source_set_name (fpi_device_add_timeout (self, 0, device_idle_probe_cb, NULL, NULL),
|
||||||
|
"libusb probe in idle");
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -794,7 +811,7 @@ fp_device_open (FpDevice *device,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->current_task)
|
if (priv->current_task || priv->is_suspended)
|
||||||
{
|
{
|
||||||
g_task_return_error (task,
|
g_task_return_error (task,
|
||||||
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
||||||
|
@ -879,7 +896,7 @@ fp_device_close (FpDevice *device,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->current_task)
|
if (priv->current_task || priv->is_suspended)
|
||||||
{
|
{
|
||||||
g_task_return_error (task,
|
g_task_return_error (task,
|
||||||
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
||||||
|
@ -912,6 +929,230 @@ fp_device_close_finish (FpDevice *device,
|
||||||
return g_task_propagate_boolean (G_TASK (result), error);
|
return g_task_propagate_boolean (G_TASK (result), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
complete_suspend_resume_task (FpDevice *device)
|
||||||
|
{
|
||||||
|
FpDevicePrivate *priv = fp_device_get_instance_private (device);
|
||||||
|
|
||||||
|
g_assert (priv->suspend_resume_task);
|
||||||
|
|
||||||
|
g_task_return_boolean (g_steal_pointer (&priv->suspend_resume_task), TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fp_device_suspend:
|
||||||
|
* @device: a #FpDevice
|
||||||
|
* @cancellable: (nullable): a #GCancellable, or %NULL, currently not used
|
||||||
|
* @callback: the function to call on completion
|
||||||
|
* @user_data: the data to pass to @callback
|
||||||
|
*
|
||||||
|
* Prepare the device for system suspend. Retrieve the result with
|
||||||
|
* fp_device_suspend_finish().
|
||||||
|
*
|
||||||
|
* The suspend method can be called at any time (even if the device is not
|
||||||
|
* opened) and must be paired with a corresponding resume call. It is undefined
|
||||||
|
* when or how any ongoing operation is finished. This call might wait for an
|
||||||
|
* ongoing operation to finish, might cancel the ongoing operation or may
|
||||||
|
* prepare the device so that the host is resumed when the operation can be
|
||||||
|
* finished.
|
||||||
|
*
|
||||||
|
* If an ongoing operation must be cancelled then it will complete with an error
|
||||||
|
* code of #FP_DEVICE_ERROR_BUSY before the suspend async routine finishes.
|
||||||
|
*
|
||||||
|
* Any operation started while the device is suspended will fail with
|
||||||
|
* #FP_DEVICE_ERROR_BUSY, this includes calls to open or close the device.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
fp_device_suspend (FpDevice *device,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr(GTask) task = NULL;
|
||||||
|
FpDevicePrivate *priv = fp_device_get_instance_private (device);
|
||||||
|
|
||||||
|
task = g_task_new (device, cancellable, callback, user_data);
|
||||||
|
|
||||||
|
if (priv->suspend_resume_task || priv->is_suspended)
|
||||||
|
{
|
||||||
|
g_task_return_error (task,
|
||||||
|
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priv->is_removed)
|
||||||
|
{
|
||||||
|
g_task_return_error (task,
|
||||||
|
fpi_device_error_new (FP_DEVICE_ERROR_REMOVED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->suspend_resume_task = g_steal_pointer (&task);
|
||||||
|
|
||||||
|
/* If the device is currently idle, just complete immediately.
|
||||||
|
* For long running tasks, call the driver handler right away, for short
|
||||||
|
* tasks, wait for completion and then return the task.
|
||||||
|
*/
|
||||||
|
switch (priv->current_action)
|
||||||
|
{
|
||||||
|
case FPI_DEVICE_ACTION_NONE:
|
||||||
|
fpi_device_suspend_complete (device, NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FPI_DEVICE_ACTION_ENROLL:
|
||||||
|
case FPI_DEVICE_ACTION_VERIFY:
|
||||||
|
case FPI_DEVICE_ACTION_IDENTIFY:
|
||||||
|
case FPI_DEVICE_ACTION_CAPTURE:
|
||||||
|
if (FP_DEVICE_GET_CLASS (device)->suspend)
|
||||||
|
{
|
||||||
|
if (priv->critical_section)
|
||||||
|
priv->suspend_queued = TRUE;
|
||||||
|
else
|
||||||
|
FP_DEVICE_GET_CLASS (device)->suspend (device);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fpi_device_suspend_complete (device, fpi_device_error_new (FP_DEVICE_ERROR_NOT_SUPPORTED));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case FPI_DEVICE_ACTION_PROBE:
|
||||||
|
case FPI_DEVICE_ACTION_OPEN:
|
||||||
|
case FPI_DEVICE_ACTION_CLOSE:
|
||||||
|
case FPI_DEVICE_ACTION_DELETE:
|
||||||
|
case FPI_DEVICE_ACTION_LIST:
|
||||||
|
case FPI_DEVICE_ACTION_CLEAR_STORAGE:
|
||||||
|
g_signal_connect_object (priv->current_task,
|
||||||
|
"notify::completed",
|
||||||
|
G_CALLBACK (complete_suspend_resume_task),
|
||||||
|
device,
|
||||||
|
G_CONNECT_SWAPPED);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fp_device_suspend_finish:
|
||||||
|
* @device: A #FpDevice
|
||||||
|
* @result: A #GAsyncResult
|
||||||
|
* @error: Return location for errors, or %NULL to ignore
|
||||||
|
*
|
||||||
|
* Finish an asynchronous operation to prepare the device for suspend.
|
||||||
|
* See fp_device_suspend().
|
||||||
|
*
|
||||||
|
* The API user should accept an error of #FP_DEVICE_ERROR_NOT_SUPPORTED.
|
||||||
|
*
|
||||||
|
* Returns: (type void): %FALSE on error, %TRUE otherwise
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
fp_device_suspend_finish (FpDevice *device,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_task_propagate_boolean (G_TASK (result), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fp_device_resume:
|
||||||
|
* @device: a #FpDevice
|
||||||
|
* @cancellable: (nullable): a #GCancellable, or %NULL, currently not used
|
||||||
|
* @callback: the function to call on completion
|
||||||
|
* @user_data: the data to pass to @callback
|
||||||
|
*
|
||||||
|
* Resume device after system suspend. Retrieve the result with
|
||||||
|
* fp_device_suspend_finish().
|
||||||
|
*
|
||||||
|
* Note that it is not defined when any ongoing operation may return (success or
|
||||||
|
* error). You must be ready to handle this before, during or after the
|
||||||
|
* resume operation.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
fp_device_resume (FpDevice *device,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr(GTask) task = NULL;
|
||||||
|
FpDevicePrivate *priv = fp_device_get_instance_private (device);
|
||||||
|
|
||||||
|
task = g_task_new (device, cancellable, callback, user_data);
|
||||||
|
|
||||||
|
if (priv->suspend_resume_task || !priv->is_suspended)
|
||||||
|
{
|
||||||
|
g_task_return_error (task,
|
||||||
|
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priv->is_removed)
|
||||||
|
{
|
||||||
|
g_task_return_error (task,
|
||||||
|
fpi_device_error_new (FP_DEVICE_ERROR_REMOVED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->suspend_resume_task = g_steal_pointer (&task);
|
||||||
|
|
||||||
|
switch (priv->current_action)
|
||||||
|
{
|
||||||
|
case FPI_DEVICE_ACTION_NONE:
|
||||||
|
fpi_device_resume_complete (device, NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FPI_DEVICE_ACTION_ENROLL:
|
||||||
|
case FPI_DEVICE_ACTION_VERIFY:
|
||||||
|
case FPI_DEVICE_ACTION_IDENTIFY:
|
||||||
|
case FPI_DEVICE_ACTION_CAPTURE:
|
||||||
|
if (FP_DEVICE_GET_CLASS (device)->resume)
|
||||||
|
{
|
||||||
|
if (priv->critical_section)
|
||||||
|
priv->resume_queued = TRUE;
|
||||||
|
else
|
||||||
|
FP_DEVICE_GET_CLASS (device)->resume (device);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fpi_device_resume_complete (device, fpi_device_error_new (FP_DEVICE_ERROR_NOT_SUPPORTED));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case FPI_DEVICE_ACTION_PROBE:
|
||||||
|
case FPI_DEVICE_ACTION_OPEN:
|
||||||
|
case FPI_DEVICE_ACTION_CLOSE:
|
||||||
|
case FPI_DEVICE_ACTION_DELETE:
|
||||||
|
case FPI_DEVICE_ACTION_LIST:
|
||||||
|
case FPI_DEVICE_ACTION_CLEAR_STORAGE:
|
||||||
|
/* cannot happen as we make sure these tasks complete before suspend */
|
||||||
|
g_assert_not_reached();
|
||||||
|
complete_suspend_resume_task (device);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fp_device_resume_finish:
|
||||||
|
* @device: A #FpDevice
|
||||||
|
* @result: A #GAsyncResult
|
||||||
|
* @error: Return location for errors, or %NULL to ignore
|
||||||
|
*
|
||||||
|
* Finish an asynchronous operation to resume the device after suspend.
|
||||||
|
* See fp_device_resume().
|
||||||
|
*
|
||||||
|
* The API user should accept an error of #FP_DEVICE_ERROR_NOT_SUPPORTED.
|
||||||
|
*
|
||||||
|
* Returns: (type void): %FALSE on error, %TRUE otherwise
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
fp_device_resume_finish (FpDevice *device,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_task_propagate_boolean (G_TASK (result), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fp_device_enroll:
|
* fp_device_enroll:
|
||||||
|
@ -960,7 +1201,7 @@ fp_device_enroll (FpDevice *device,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->current_task)
|
if (priv->current_task || priv->is_suspended)
|
||||||
{
|
{
|
||||||
g_task_return_error (task,
|
g_task_return_error (task,
|
||||||
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
||||||
|
@ -1070,7 +1311,7 @@ fp_device_verify (FpDevice *device,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->current_task)
|
if (priv->current_task || priv->is_suspended)
|
||||||
{
|
{
|
||||||
g_task_return_error (task,
|
g_task_return_error (task,
|
||||||
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
||||||
|
@ -1197,7 +1438,7 @@ fp_device_identify (FpDevice *device,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->current_task)
|
if (priv->current_task || priv->is_suspended)
|
||||||
{
|
{
|
||||||
g_task_return_error (task,
|
g_task_return_error (task,
|
||||||
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
||||||
|
@ -1322,7 +1563,7 @@ fp_device_capture (FpDevice *device,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->current_task)
|
if (priv->current_task || priv->is_suspended)
|
||||||
{
|
{
|
||||||
g_task_return_error (task,
|
g_task_return_error (task,
|
||||||
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
||||||
|
@ -1413,7 +1654,7 @@ fp_device_delete_print (FpDevice *device,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->current_task)
|
if (priv->current_task || priv->is_suspended)
|
||||||
{
|
{
|
||||||
g_task_return_error (task,
|
g_task_return_error (task,
|
||||||
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
||||||
|
@ -1491,7 +1732,7 @@ fp_device_list_prints (FpDevice *device,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->current_task)
|
if (priv->current_task || priv->is_suspended)
|
||||||
{
|
{
|
||||||
g_task_return_error (task,
|
g_task_return_error (task,
|
||||||
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
|
||||||
|
@ -1887,6 +2128,7 @@ fp_device_list_prints_sync (FpDevice *device,
|
||||||
return fp_device_list_prints_finish (device, task, error);
|
return fp_device_list_prints_finish (device, task, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fp_device_clear_storage_sync:
|
* fp_device_clear_storage_sync:
|
||||||
* @device: a #FpDevice
|
* @device: a #FpDevice
|
||||||
|
@ -1915,6 +2157,58 @@ fp_device_clear_storage_sync (FpDevice *device,
|
||||||
return fp_device_clear_storage_finish (device, task, error);
|
return fp_device_clear_storage_finish (device, task, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fp_device_suspend_sync:
|
||||||
|
* @device: a #FpDevice
|
||||||
|
* @cancellable: (nullable): a #GCancellable, or %NULL, currently not used
|
||||||
|
* @error: Return location for errors, or %NULL to ignore
|
||||||
|
*
|
||||||
|
* Prepare device for suspend.
|
||||||
|
*
|
||||||
|
* Returns: (type void): %FALSE on error, %TRUE otherwise
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
fp_device_suspend_sync (FpDevice *device,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(GAsyncResult) task = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
|
||||||
|
|
||||||
|
fp_device_suspend (device, cancellable, async_result_ready, &task);
|
||||||
|
while (!task)
|
||||||
|
g_main_context_iteration (NULL, TRUE);
|
||||||
|
|
||||||
|
return fp_device_suspend_finish (device, task, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fp_device_resume_sync:
|
||||||
|
* @device: a #FpDevice
|
||||||
|
* @cancellable: (nullable): a #GCancellable, or %NULL, currently not used
|
||||||
|
* @error: Return location for errors, or %NULL to ignore
|
||||||
|
*
|
||||||
|
* Resume device after suspend.
|
||||||
|
*
|
||||||
|
* Returns: (type void): %FALSE on error, %TRUE otherwise
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
fp_device_resume_sync (FpDevice *device,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(GAsyncResult) task = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
|
||||||
|
|
||||||
|
fp_device_resume (device, cancellable, async_result_ready, &task);
|
||||||
|
while (!task)
|
||||||
|
g_main_context_iteration (NULL, TRUE);
|
||||||
|
|
||||||
|
return fp_device_resume_finish (device, task, error);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fp_device_get_features:
|
* fp_device_get_features:
|
||||||
* @device: a #FpDevice
|
* @device: a #FpDevice
|
||||||
|
|
|
@ -239,6 +239,16 @@ void fp_device_close (FpDevice *device,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
|
|
||||||
|
void fp_device_suspend (FpDevice *device,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
void fp_device_resume (FpDevice *device,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
void fp_device_enroll (FpDevice *device,
|
void fp_device_enroll (FpDevice *device,
|
||||||
FpPrint *template_print,
|
FpPrint *template_print,
|
||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
|
@ -294,6 +304,12 @@ gboolean fp_device_open_finish (FpDevice *device,
|
||||||
gboolean fp_device_close_finish (FpDevice *device,
|
gboolean fp_device_close_finish (FpDevice *device,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
gboolean fp_device_suspend_finish (FpDevice *device,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
|
gboolean fp_device_resume_finish (FpDevice *device,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
FpPrint *fp_device_enroll_finish (FpDevice *device,
|
FpPrint *fp_device_enroll_finish (FpDevice *device,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
@ -362,6 +378,13 @@ GPtrArray * fp_device_list_prints_sync (FpDevice *device,
|
||||||
gboolean fp_device_clear_storage_sync (FpDevice *device,
|
gboolean fp_device_clear_storage_sync (FpDevice *device,
|
||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
gboolean fp_device_suspend_sync (FpDevice *device,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error);
|
||||||
|
gboolean fp_device_resume_sync (FpDevice *device,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
/* Deprecated functions */
|
/* Deprecated functions */
|
||||||
G_DEPRECATED_FOR (fp_device_get_features)
|
G_DEPRECATED_FOR (fp_device_get_features)
|
||||||
gboolean fp_device_supports_identify (FpDevice *device);
|
gboolean fp_device_supports_identify (FpDevice *device);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#define FP_COMPONENT "device"
|
#define FP_COMPONENT "device"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "fpi-log.h"
|
#include "fpi-log.h"
|
||||||
|
|
||||||
|
@ -863,6 +864,22 @@ fpi_device_critical_section_flush_idle_cb (FpDevice *device)
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (priv->suspend_queued)
|
||||||
|
{
|
||||||
|
cls->suspend (device);
|
||||||
|
priv->suspend_queued = FALSE;
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priv->resume_queued)
|
||||||
|
{
|
||||||
|
cls->resume (device);
|
||||||
|
priv->resume_queued = FALSE;
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
priv->critical_section_flush_source = NULL;
|
priv->critical_section_flush_source = NULL;
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
|
@ -998,15 +1015,6 @@ fp_device_task_return_in_idle_cb (gpointer user_data)
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return internal cancellation reason if we have one.
|
|
||||||
* Note that an external cancellation always returns G_IO_ERROR_CANCELLED */
|
|
||||||
if (cancellation_reason)
|
|
||||||
{
|
|
||||||
g_task_return_error (task, g_steal_pointer (&cancellation_reason));
|
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data->type)
|
switch (data->type)
|
||||||
{
|
{
|
||||||
case FP_DEVICE_TASK_RETURN_INT:
|
case FP_DEVICE_TASK_RETURN_INT:
|
||||||
|
@ -1028,7 +1036,18 @@ fp_device_task_return_in_idle_cb (gpointer user_data)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FP_DEVICE_TASK_RETURN_ERROR:
|
case FP_DEVICE_TASK_RETURN_ERROR:
|
||||||
g_task_return_error (task, g_steal_pointer (&data->result));
|
/* Return internal cancellation reason instead if we have one.
|
||||||
|
* Note that an external cancellation always returns G_IO_ERROR_CANCELLED
|
||||||
|
*/
|
||||||
|
if (cancellation_reason)
|
||||||
|
{
|
||||||
|
g_task_set_task_data (task, NULL, NULL);
|
||||||
|
g_task_return_error (task, g_steal_pointer (&cancellation_reason));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_task_return_error (task, g_steal_pointer (&data->result));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -1531,6 +1550,183 @@ fpi_device_list_complete (FpDevice *device,
|
||||||
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
|
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fpi_device_configure_wakeup (FpDevice *device, gboolean enabled)
|
||||||
|
{
|
||||||
|
FpDevicePrivate *priv = fp_device_get_instance_private (device);
|
||||||
|
|
||||||
|
switch (priv->type)
|
||||||
|
{
|
||||||
|
case FP_DEVICE_TYPE_USB:
|
||||||
|
{
|
||||||
|
g_autoptr(GString) ports = NULL;
|
||||||
|
GUsbDevice *dev, *parent;
|
||||||
|
const char *wakeup_command = enabled ? "enabled" : "disabled";
|
||||||
|
guint8 bus, port;
|
||||||
|
g_autofree gchar *sysfs_wakeup = NULL;
|
||||||
|
g_autofree gchar *sysfs_persist = NULL;
|
||||||
|
gssize r;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
ports = g_string_new (NULL);
|
||||||
|
bus = g_usb_device_get_bus (priv->usb_device);
|
||||||
|
|
||||||
|
/* Walk up, skipping the root hub. */
|
||||||
|
dev = priv->usb_device;
|
||||||
|
while ((parent = g_usb_device_get_parent (dev)))
|
||||||
|
{
|
||||||
|
port = g_usb_device_get_port_number (dev);
|
||||||
|
g_string_prepend (ports, g_strdup_printf ("%d.", port));
|
||||||
|
dev = parent;
|
||||||
|
}
|
||||||
|
g_string_set_size (ports, ports->len - 1);
|
||||||
|
|
||||||
|
sysfs_wakeup = g_strdup_printf ("/sys/bus/usb/devices/%d-%s/power/wakeup", bus, ports->str);
|
||||||
|
fd = open (sysfs_wakeup, O_WRONLY);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
/* Wakeup not existing appears to be relatively normal. */
|
||||||
|
g_debug ("Failed to open %s", sysfs_wakeup);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r = write (fd, wakeup_command, strlen (wakeup_command));
|
||||||
|
if (r < 0)
|
||||||
|
g_warning ("Could not configure wakeup to %s by writing %s", wakeup_command, sysfs_wakeup);
|
||||||
|
close (fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Persist means that the kernel tries to keep the USB device open
|
||||||
|
* in case it is "replugged" due to suspend.
|
||||||
|
* This is not helpful, as it will receive a reset and will be in a bad
|
||||||
|
* state. Instead, seeing an unplug and a new device makes more sense.
|
||||||
|
*/
|
||||||
|
sysfs_persist = g_strdup_printf ("/sys/bus/usb/devices/%d-%s/power/persist", bus, ports->str);
|
||||||
|
fd = open (sysfs_persist, O_WRONLY);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
g_warning ("Failed to open %s", sysfs_persist);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r = write (fd, "0", 1);
|
||||||
|
if (r < 0)
|
||||||
|
g_message ("Could not disable USB persist by writing to %s", sysfs_persist);
|
||||||
|
close (fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case FP_DEVICE_TYPE_VIRTUAL:
|
||||||
|
case FP_DEVICE_TYPE_UDEV:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR,
|
||||||
|
fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fpi_device_suspend_completed (FpDevice *device)
|
||||||
|
{
|
||||||
|
FpDevicePrivate *priv = fp_device_get_instance_private (device);
|
||||||
|
|
||||||
|
/* We have an ongoing operation, allow the device to wake up the machine. */
|
||||||
|
if (priv->current_action != FPI_DEVICE_ACTION_NONE)
|
||||||
|
fpi_device_configure_wakeup (device, TRUE);
|
||||||
|
|
||||||
|
if (priv->critical_section)
|
||||||
|
g_warning ("Driver was in a critical section at suspend time. It likely deadlocked!");
|
||||||
|
|
||||||
|
if (priv->suspend_error)
|
||||||
|
g_task_return_error (g_steal_pointer (&priv->suspend_resume_task),
|
||||||
|
g_steal_pointer (&priv->suspend_error));
|
||||||
|
else
|
||||||
|
g_task_return_boolean (g_steal_pointer (&priv->suspend_resume_task), TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fpi_device_suspend_complete:
|
||||||
|
* @device: The #FpDevice
|
||||||
|
* @error: The #GError or %NULL on success
|
||||||
|
*
|
||||||
|
* Finish a suspend request. Only return a %NULL error if suspend has been
|
||||||
|
* correctly configured and the current action as returned by
|
||||||
|
* fpi_device_get_current_action() will continue to run after resume.
|
||||||
|
*
|
||||||
|
* In all other cases an error must be returned. Should this happen, the
|
||||||
|
* current action will be cancelled before the error is forwarded to the
|
||||||
|
* application.
|
||||||
|
*
|
||||||
|
* It is recommended to set @error to #FP_ERROR_NOT_IMPLEMENTED.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
fpi_device_suspend_complete (FpDevice *device,
|
||||||
|
GError *error)
|
||||||
|
{
|
||||||
|
FpDevicePrivate *priv = fp_device_get_instance_private (device);
|
||||||
|
|
||||||
|
g_return_if_fail (FP_IS_DEVICE (device));
|
||||||
|
g_return_if_fail (priv->suspend_resume_task);
|
||||||
|
g_return_if_fail (priv->suspend_error == NULL);
|
||||||
|
|
||||||
|
priv->suspend_error = error;
|
||||||
|
priv->is_suspended = TRUE;
|
||||||
|
|
||||||
|
/* If there is no error, we have no running task, return immediately. */
|
||||||
|
if (error == NULL || !priv->current_task || g_task_get_completed (priv->current_task))
|
||||||
|
{
|
||||||
|
fpi_device_suspend_completed (device);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for completion of the current task. */
|
||||||
|
g_signal_connect_object (priv->current_task,
|
||||||
|
"notify::completed",
|
||||||
|
G_CALLBACK (fpi_device_suspend_completed),
|
||||||
|
device,
|
||||||
|
G_CONNECT_SWAPPED);
|
||||||
|
|
||||||
|
/* And cancel any action that might be long-running. */
|
||||||
|
if (!priv->current_cancellation_reason)
|
||||||
|
priv->current_cancellation_reason = fpi_device_error_new_msg (FP_DEVICE_ERROR_BUSY,
|
||||||
|
"Cannot run while suspended.");
|
||||||
|
|
||||||
|
g_cancellable_cancel (priv->current_cancellable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fpi_device_resume_complete:
|
||||||
|
* @device: The #FpDevice
|
||||||
|
* @error: The #GError or %NULL on success
|
||||||
|
*
|
||||||
|
* Finish a resume request.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
fpi_device_resume_complete (FpDevice *device,
|
||||||
|
GError *error)
|
||||||
|
{
|
||||||
|
FpDevicePrivate *priv = fp_device_get_instance_private (device);
|
||||||
|
|
||||||
|
g_return_if_fail (FP_IS_DEVICE (device));
|
||||||
|
g_return_if_fail (priv->suspend_resume_task);
|
||||||
|
|
||||||
|
priv->is_suspended = FALSE;
|
||||||
|
fpi_device_configure_wakeup (device, FALSE);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
g_task_return_error (g_steal_pointer (&priv->suspend_resume_task), error);
|
||||||
|
else
|
||||||
|
g_task_return_boolean (g_steal_pointer (&priv->suspend_resume_task), TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fpi_device_clear_storage_complete:
|
* fpi_device_clear_storage_complete:
|
||||||
* @device: The #FpDevice
|
* @device: The #FpDevice
|
||||||
|
|
|
@ -108,6 +108,10 @@ struct _FpIdEntry
|
||||||
* @clear_storage: Delete all prints from the device
|
* @clear_storage: Delete all prints from the device
|
||||||
* @cancel: Called on cancellation, this is a convenience to not need to handle
|
* @cancel: Called on cancellation, this is a convenience to not need to handle
|
||||||
* the #GCancellable directly by using fpi_device_get_cancellable().
|
* the #GCancellable directly by using fpi_device_get_cancellable().
|
||||||
|
* @suspend: Called when an interactive action is running (ENROLL, VERIFY,
|
||||||
|
* IDENTIFY or CAPTURE) and the system is about to go into suspend.
|
||||||
|
* @resume: Called to resume an ongoing interactive action after the system has
|
||||||
|
* resumed from suspend.
|
||||||
*
|
*
|
||||||
* NOTE: If your driver is image based, then you should subclass #FpImageDevice
|
* NOTE: If your driver is image based, then you should subclass #FpImageDevice
|
||||||
* instead. #FpImageDevice based drivers use a different way of interacting
|
* instead. #FpImageDevice based drivers use a different way of interacting
|
||||||
|
@ -126,6 +130,9 @@ struct _FpIdEntry
|
||||||
* operation (i.e. any operation that requires capturing). It is entirely fine
|
* operation (i.e. any operation that requires capturing). It is entirely fine
|
||||||
* to ignore cancellation requests for short operations (e.g. open/close).
|
* to ignore cancellation requests for short operations (e.g. open/close).
|
||||||
*
|
*
|
||||||
|
* Note that @cancel, @suspend and @resume will not be called while the device
|
||||||
|
* is within a fpi_device_critical_enter()/fpi_device_critical_leave() block.
|
||||||
|
*
|
||||||
* This API is solely intended for drivers. It is purely internal and neither
|
* This API is solely intended for drivers. It is purely internal and neither
|
||||||
* API nor ABI stable.
|
* API nor ABI stable.
|
||||||
*/
|
*/
|
||||||
|
@ -164,6 +171,8 @@ struct _FpDeviceClass
|
||||||
void (*clear_storage) (FpDevice * device);
|
void (*clear_storage) (FpDevice * device);
|
||||||
|
|
||||||
void (*cancel) (FpDevice *device);
|
void (*cancel) (FpDevice *device);
|
||||||
|
void (*suspend) (FpDevice *device);
|
||||||
|
void (*resume) (FpDevice *device);
|
||||||
};
|
};
|
||||||
|
|
||||||
void fpi_device_class_auto_initialize_features (FpDeviceClass *device_class);
|
void fpi_device_class_auto_initialize_features (FpDeviceClass *device_class);
|
||||||
|
@ -292,6 +301,10 @@ void fpi_device_list_complete (FpDevice *device,
|
||||||
GError *error);
|
GError *error);
|
||||||
void fpi_device_clear_storage_complete (FpDevice *device,
|
void fpi_device_clear_storage_complete (FpDevice *device,
|
||||||
GError *error);
|
GError *error);
|
||||||
|
void fpi_device_suspend_complete (FpDevice *device,
|
||||||
|
GError *error);
|
||||||
|
void fpi_device_resume_complete (FpDevice *device,
|
||||||
|
GError *error);
|
||||||
|
|
||||||
void fpi_device_enroll_progress (FpDevice *device,
|
void fpi_device_enroll_progress (FpDevice *device,
|
||||||
gint completed_stages,
|
gint completed_stages,
|
||||||
|
|
Loading…
Reference in a new issue