Commit 5c89bda7 authored by Benjamin Berg's avatar Benjamin Berg
Browse files

synaptics: Implement suspend/resume methods

We only allow suspending while we are in the interrupt transfer stage.
To suspend, we cancel the interrupt transfer and at resume time we
restart it.

This has been tested to work correctly on an X1 Carbon 8th Gen with
suspend mode set to "Windows 10" (i.e. S0ix [s2idle] and not S3 [suspend
to RAM]). With S3 suspend, the USB root hub appears to be turned off or
reset and the device will be unresponsive afterwards (if it returns). To
avoid issues, libfprint disables the "persist" mode in the kernel and
we'll see a new device instead after resume.
parent 8147372b
......@@ -198,12 +198,17 @@ cmd_interrupt_cb (FpiUsbTransfer *transfer,
GError *error)
{
g_debug ("interrupt transfer done");
fpi_device_critical_enter (device);
if (error)
{
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_error_free (error);
fpi_ssm_jump_to_state (transfer->ssm, SYNAPTICS_CMD_GET_RESP);
if (FPI_DEVICE_SYNAPTICS (device)->cmd_suspended)
fpi_ssm_jump_to_state (transfer->ssm, SYNAPTICS_CMD_SUSPENDED);
else
fpi_ssm_jump_to_state (transfer->ssm, SYNAPTICS_CMD_GET_RESP);
return;
}
......@@ -264,6 +269,9 @@ synaptics_cmd_run_state (FpiSsm *ssm,
break;
case SYNAPTICS_CMD_WAIT_INTERRUPT:
/* Interruptions are permitted only during an interrupt transfer */
fpi_device_critical_leave (dev);
transfer = fpi_usb_transfer_new (dev);
transfer->ssm = ssm;
fpi_usb_transfer_fill_interrupt (transfer, USB_EP_INTERRUPT, USB_INTERRUPT_DATA_SIZE);
......@@ -291,6 +299,17 @@ synaptics_cmd_run_state (FpiSsm *ssm,
case SYNAPTICS_CMD_RESTART:
fpi_ssm_jump_to_state (ssm, SYNAPTICS_CMD_SEND_PENDING);
break;
case SYNAPTICS_CMD_SUSPENDED:
/* The resume handler continues to the next state! */
fpi_device_critical_leave (dev);
fpi_device_suspend_complete (dev, NULL);
break;
case SYNAPTICS_CMD_RESUME:
fpi_device_critical_enter (dev);
fpi_ssm_jump_to_state (ssm, SYNAPTICS_CMD_WAIT_INTERRUPT);
break;
}
}
......@@ -306,6 +325,7 @@ cmd_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
if (error || self->cmd_complete_on_removal)
callback (self, NULL, error);
fpi_device_critical_leave (dev);
self->cmd_complete_on_removal = FALSE;
}
......@@ -415,6 +435,7 @@ synaptics_sensor_cmd (FpiDeviceSynaptics *self,
SYNAPTICS_CMD_NUM_STATES);
fpi_ssm_set_data (self->cmd_ssm, callback, NULL);
fpi_device_critical_enter (FP_DEVICE (self));
fpi_ssm_start (self->cmd_ssm, cmd_ssm_done);
}
}
......@@ -1399,6 +1420,59 @@ cancel (FpDevice *dev)
self->interrupt_cancellable = g_cancellable_new ();
}
static void
suspend (FpDevice *dev)
{
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (dev);
FpiDeviceAction action = fpi_device_get_current_action (dev);
g_debug ("got suspend request");
if (action != FPI_DEVICE_ACTION_VERIFY && action != FPI_DEVICE_ACTION_IDENTIFY)
{
fpi_device_suspend_complete (dev, fpi_device_error_new (FP_DEVICE_ERROR_NOT_SUPPORTED));
return;
}
/* We are guaranteed to have a cmd_ssm running at this time. */
g_assert (self->cmd_ssm);
g_assert (fpi_ssm_get_cur_state (self->cmd_ssm) == SYNAPTICS_CMD_WAIT_INTERRUPT);
self->cmd_suspended = TRUE;
/* Cancel the current transfer.
* The CMD SSM will go into the suspend state and signal readyness. */
g_cancellable_cancel (self->interrupt_cancellable);
g_clear_object (&self->interrupt_cancellable);
self->interrupt_cancellable = g_cancellable_new ();
}
static void
resume (FpDevice *dev)
{
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (dev);
FpiDeviceAction action = fpi_device_get_current_action (dev);
g_debug ("got resume request");
if (action != FPI_DEVICE_ACTION_VERIFY && action != FPI_DEVICE_ACTION_IDENTIFY)
{
g_assert_not_reached ();
fpi_device_resume_complete (dev, fpi_device_error_new (FP_DEVICE_ERROR_NOT_SUPPORTED));
return;
}
/* We must have a suspended cmd_ssm at this point */
g_assert (self->cmd_ssm);
g_assert (self->cmd_suspended);
g_assert (fpi_ssm_get_cur_state (self->cmd_ssm) == SYNAPTICS_CMD_SUSPENDED);
self->cmd_suspended = FALSE;
/* Restart interrupt transfer. */
fpi_ssm_jump_to_state (self->cmd_ssm, SYNAPTICS_CMD_RESUME);
fpi_device_resume_complete (dev, NULL);
}
static void
fpi_device_synaptics_init (FpiDeviceSynaptics *self)
{
......@@ -1427,6 +1501,8 @@ fpi_device_synaptics_class_init (FpiDeviceSynapticsClass *klass)
dev_class->delete = delete_print;
dev_class->clear_storage = clear_storage;
dev_class->cancel = cancel;
dev_class->suspend = suspend;
dev_class->resume = resume;
fpi_device_class_auto_initialize_features (dev_class);
}
......@@ -93,6 +93,8 @@ typedef enum {
SYNAPTICS_CMD_WAIT_INTERRUPT,
SYNAPTICS_CMD_SEND_ASYNC,
SYNAPTICS_CMD_RESTART,
SYNAPTICS_CMD_SUSPENDED,
SYNAPTICS_CMD_RESUME,
SYNAPTICS_CMD_NUM_STATES,
} SynapticsCmdState;
......@@ -110,6 +112,7 @@ struct _FpiDeviceSynaptics
FpiSsm *cmd_ssm;
FpiUsbTransfer *cmd_pending_transfer;
gboolean cmd_complete_on_removal;
gboolean cmd_suspended;
guint8 id_idx;
bmkt_sensor_version_t mis_version;
......
......@@ -1126,7 +1126,7 @@ fp_device_resume (FpDevice *device,
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();
g_assert_not_reached ();
complete_suspend_resume_task (device);
break;
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment