2008-02-04 10:23:11 +00:00
|
|
|
/*
|
|
|
|
* Functions to assist with asynchronous driver <---> library communications
|
|
|
|
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
2019-07-03 21:29:05 +00:00
|
|
|
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
2019-11-21 19:25:36 +00:00
|
|
|
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
|
2008-02-04 10:23:11 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
#define FP_COMPONENT "SSM"
|
2008-02-18 17:50:48 +00:00
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
#include "drivers_api.h"
|
2018-09-05 10:39:16 +00:00
|
|
|
#include "fpi-ssm.h"
|
2018-05-23 15:48:55 +00:00
|
|
|
|
2008-02-04 10:23:11 +00:00
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
|
|
|
* SECTION:fpi-ssm
|
|
|
|
* @title: Sequential state machine
|
2018-11-12 13:20:28 +00:00
|
|
|
* @short_description: State machine helpers
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
2008-02-04 10:23:11 +00:00
|
|
|
* Asynchronous driver design encourages some kind of state machine behind it.
|
|
|
|
* In most cases, the state machine is entirely linear - you only go to the
|
2019-07-03 21:29:05 +00:00
|
|
|
* next state, you never jump or go backwards. The #FpiSsm functions help you
|
2008-02-04 10:23:11 +00:00
|
|
|
* implement such a machine.
|
|
|
|
*
|
2018-09-05 11:11:51 +00:00
|
|
|
* e.g. `S1` ↦ `S2` ↦ `S3` ↦ `S4`
|
|
|
|
*
|
|
|
|
* `S1` is the start state
|
2008-02-04 10:23:11 +00:00
|
|
|
* There is also an implicit error state and an implicit accepting state
|
|
|
|
* (both with implicit edges from every state).
|
|
|
|
*
|
2008-02-15 18:09:14 +00:00
|
|
|
* You can also jump to any arbitrary state (while marking completion of the
|
|
|
|
* current state) while the machine is running. In other words there are
|
2018-09-05 11:11:51 +00:00
|
|
|
* implicit edges linking one state to every other state.
|
2008-02-15 18:09:14 +00:00
|
|
|
*
|
2018-09-05 11:11:51 +00:00
|
|
|
* To create an #fpi_ssm, you pass a state handler function and the total number of
|
2019-11-20 17:05:46 +00:00
|
|
|
* states (4 in the above example) to fpi_ssm_new (). Note that the state numbers
|
2018-09-05 11:11:51 +00:00
|
|
|
* start at zero, making them match the first value in a C enumeration.
|
2008-02-04 10:23:11 +00:00
|
|
|
*
|
2018-09-05 11:11:51 +00:00
|
|
|
* To start a ssm, you pass in a completion callback function to fpi_ssm_start()
|
|
|
|
* which gets called when the ssm completes (both on error and on failure).
|
2019-11-26 15:18:14 +00:00
|
|
|
* Starting a ssm also takes ownership of it.
|
2008-02-04 10:23:11 +00:00
|
|
|
*
|
|
|
|
* To iterate to the next state, call fpi_ssm_next_state(). It is legal to
|
|
|
|
* attempt to iterate beyond the final state - this is equivalent to marking
|
|
|
|
* the ssm as successfully completed.
|
|
|
|
*
|
|
|
|
* To mark successful completion of a SSM, either iterate beyond the final
|
|
|
|
* state or call fpi_ssm_mark_completed() from any state.
|
2019-11-26 15:18:14 +00:00
|
|
|
* This will also invalidate the machine, freeing it.
|
2008-02-04 10:23:11 +00:00
|
|
|
*
|
2018-09-05 14:29:26 +00:00
|
|
|
* To mark failed completion of a SSM, call fpi_ssm_mark_failed() from any
|
2008-02-04 10:23:11 +00:00
|
|
|
* state. You must pass a non-zero error code.
|
|
|
|
*
|
2018-09-05 11:11:51 +00:00
|
|
|
* Your state handling function looks at the return value of
|
|
|
|
* fpi_ssm_get_cur_state() in order to determine the current state and hence
|
|
|
|
* which operations to perform (a switch statement is appropriate).
|
|
|
|
*
|
|
|
|
* Typically, the state handling function fires off an asynchronous
|
|
|
|
* communication with the device (such as a libsub transfer), and the
|
|
|
|
* callback function iterates the machine to the next state
|
2018-09-05 14:29:26 +00:00
|
|
|
* upon success (or fails).
|
2008-02-04 10:23:11 +00:00
|
|
|
*
|
2018-09-05 11:11:51 +00:00
|
|
|
* Your completion callback should examine the return value of
|
2020-08-18 15:36:16 +00:00
|
|
|
* fpi_ssm_get_error() in order to determine whether the #FpiSsm completed or
|
2018-09-05 11:11:51 +00:00
|
|
|
* failed. An error code of zero indicates successful completion.
|
2008-02-04 10:23:11 +00:00
|
|
|
*/
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
struct _FpiSsm
|
|
|
|
{
|
|
|
|
FpDevice *dev;
|
2019-12-04 19:03:23 +00:00
|
|
|
const char *name;
|
2019-07-03 21:29:05 +00:00
|
|
|
FpiSsm *parentsm;
|
2019-11-20 17:05:46 +00:00
|
|
|
gpointer ssm_data;
|
|
|
|
GDestroyNotify ssm_data_destroy;
|
2019-07-03 21:29:05 +00:00
|
|
|
int nr_states;
|
|
|
|
int cur_state;
|
|
|
|
gboolean completed;
|
2019-11-22 16:19:27 +00:00
|
|
|
GSource *timeout;
|
2019-11-28 18:24:55 +00:00
|
|
|
GCancellable *cancellable;
|
|
|
|
gulong cancellable_id;
|
2019-07-03 21:29:05 +00:00
|
|
|
GError *error;
|
|
|
|
FpiSsmCompletedCallback callback;
|
|
|
|
FpiSsmHandlerCallback handler;
|
2018-09-05 10:39:16 +00:00
|
|
|
};
|
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
|
|
|
* fpi_ssm_new:
|
|
|
|
* @dev: a #fp_dev fingerprint device
|
|
|
|
* @handler: the callback function
|
|
|
|
* @nr_states: the number of states
|
|
|
|
*
|
|
|
|
* Allocate a new ssm, with @nr_states states. The @handler callback
|
|
|
|
* will be called after each state transition.
|
2019-12-04 19:03:23 +00:00
|
|
|
* This is a macro that calls fpi_ssm_new_full() using the stringified
|
|
|
|
* version of @nr_states, so will work better with named parameters.
|
|
|
|
*
|
|
|
|
* Returns: a new #FpiSsm state machine
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* fpi_ssm_new_full:
|
|
|
|
* @dev: a #fp_dev fingerprint device
|
|
|
|
* @handler: the callback function
|
|
|
|
* @nr_states: the number of states
|
2019-12-11 19:07:59 +00:00
|
|
|
* @machine_name: the name of the state machine (for debug purposes)
|
2019-12-04 19:03:23 +00:00
|
|
|
*
|
|
|
|
* Allocate a new ssm, with @nr_states states. The @handler callback
|
|
|
|
* will be called after each state transition.
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
2019-07-03 21:29:05 +00:00
|
|
|
* Returns: a new #FpiSsm state machine
|
2018-09-05 11:11:51 +00:00
|
|
|
*/
|
2019-07-03 21:29:05 +00:00
|
|
|
FpiSsm *
|
2019-12-04 19:03:23 +00:00
|
|
|
fpi_ssm_new_full (FpDevice *dev,
|
|
|
|
FpiSsmHandlerCallback handler,
|
|
|
|
int nr_states,
|
2019-12-11 19:07:59 +00:00
|
|
|
const char *machine_name)
|
2008-02-04 10:23:11 +00:00
|
|
|
{
|
2019-07-03 21:29:05 +00:00
|
|
|
FpiSsm *machine;
|
|
|
|
|
2021-01-26 14:10:11 +00:00
|
|
|
BUG_ON (dev == NULL);
|
2019-07-03 21:29:05 +00:00
|
|
|
BUG_ON (nr_states < 1);
|
2019-11-28 19:15:21 +00:00
|
|
|
BUG_ON (handler == NULL);
|
2008-02-04 10:23:11 +00:00
|
|
|
|
2019-11-20 12:41:35 +00:00
|
|
|
machine = g_new0 (FpiSsm, 1);
|
2019-07-03 21:29:05 +00:00
|
|
|
machine->handler = handler;
|
|
|
|
machine->nr_states = nr_states;
|
|
|
|
machine->dev = dev;
|
2019-12-11 19:07:59 +00:00
|
|
|
machine->name = g_strdup (machine_name);
|
2019-07-03 21:29:05 +00:00
|
|
|
machine->completed = TRUE;
|
|
|
|
return machine;
|
2008-02-04 10:23:11 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
2019-11-20 17:05:46 +00:00
|
|
|
* fpi_ssm_set_data:
|
2019-07-03 21:29:05 +00:00
|
|
|
* @machine: an #FpiSsm state machine
|
2019-11-20 17:05:46 +00:00
|
|
|
* @ssm_data: (nullable): a pointer to machine data
|
|
|
|
* @ssm_data_destroy: (nullable): #GDestroyNotify for @ssm_data
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
2019-11-20 17:05:46 +00:00
|
|
|
* Sets @machine's data (freeing the existing data, if any).
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fpi_ssm_set_data (FpiSsm *machine,
|
|
|
|
gpointer ssm_data,
|
|
|
|
GDestroyNotify ssm_data_destroy)
|
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_if_fail (machine);
|
|
|
|
|
2019-11-20 17:05:46 +00:00
|
|
|
if (machine->ssm_data_destroy && machine->ssm_data)
|
|
|
|
machine->ssm_data_destroy (machine->ssm_data);
|
|
|
|
|
|
|
|
machine->ssm_data = ssm_data;
|
|
|
|
machine->ssm_data_destroy = ssm_data_destroy;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* fpi_ssm_get_data:
|
|
|
|
* @machine: an #FpiSsm state machine
|
|
|
|
*
|
|
|
|
* Retrieve the pointer to SSM data set with fpi_ssm_set_ssm_data()
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
|
|
|
* Returns: a pointer
|
|
|
|
*/
|
2018-05-28 10:40:22 +00:00
|
|
|
void *
|
2019-11-20 17:05:46 +00:00
|
|
|
fpi_ssm_get_data (FpiSsm *machine)
|
2018-05-28 10:40:22 +00:00
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_val_if_fail (machine, NULL);
|
|
|
|
|
2019-11-20 17:05:46 +00:00
|
|
|
return machine->ssm_data;
|
2018-05-28 10:40:22 +00:00
|
|
|
}
|
|
|
|
|
2019-11-28 18:24:55 +00:00
|
|
|
static void
|
|
|
|
fpi_ssm_clear_delayed_action (FpiSsm *machine)
|
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_if_fail (machine);
|
|
|
|
|
2019-11-28 18:24:55 +00:00
|
|
|
if (machine->cancellable_id)
|
|
|
|
{
|
|
|
|
g_cancellable_disconnect (machine->cancellable, machine->cancellable_id);
|
|
|
|
machine->cancellable_id = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_clear_object (&machine->cancellable);
|
|
|
|
g_clear_pointer (&machine->timeout, g_source_destroy);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct _CancelledActionIdleData
|
|
|
|
{
|
|
|
|
gulong cancellable_id;
|
|
|
|
GCancellable *cancellable;
|
|
|
|
} CancelledActionIdleData;
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
on_delayed_action_cancelled_idle (gpointer user_data)
|
|
|
|
{
|
|
|
|
CancelledActionIdleData *data = user_data;
|
|
|
|
|
|
|
|
g_cancellable_disconnect (data->cancellable, data->cancellable_id);
|
|
|
|
g_object_unref (data->cancellable);
|
|
|
|
g_free (data);
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_delayed_action_cancelled (GCancellable *cancellable,
|
|
|
|
FpiSsm *machine)
|
|
|
|
{
|
|
|
|
CancelledActionIdleData *data;
|
|
|
|
|
2019-12-12 14:49:35 +00:00
|
|
|
fp_dbg ("[%s] %s cancelled delayed state change",
|
|
|
|
fp_device_get_driver (machine->dev), machine->name);
|
|
|
|
|
2019-11-28 18:24:55 +00:00
|
|
|
g_clear_pointer (&machine->timeout, g_source_destroy);
|
|
|
|
|
|
|
|
data = g_new0 (CancelledActionIdleData, 1);
|
|
|
|
data->cancellable = g_steal_pointer (&machine->cancellable);
|
|
|
|
data->cancellable_id = machine->cancellable_id;
|
|
|
|
machine->cancellable_id = 0;
|
|
|
|
|
|
|
|
g_idle_add_full (G_PRIORITY_HIGH_IDLE, on_delayed_action_cancelled_idle,
|
|
|
|
data, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fpi_ssm_set_delayed_action_timeout (FpiSsm *machine,
|
|
|
|
int delay,
|
|
|
|
FpTimeoutFunc callback,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
gpointer user_data,
|
|
|
|
GDestroyNotify destroy_func)
|
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_if_fail (machine);
|
|
|
|
|
2019-11-28 18:24:55 +00:00
|
|
|
BUG_ON (machine->completed);
|
|
|
|
BUG_ON (machine->timeout != NULL);
|
|
|
|
|
|
|
|
fpi_ssm_clear_delayed_action (machine);
|
|
|
|
|
|
|
|
if (cancellable != NULL)
|
|
|
|
{
|
|
|
|
g_set_object (&machine->cancellable, cancellable);
|
|
|
|
|
|
|
|
machine->cancellable_id =
|
|
|
|
g_cancellable_connect (machine->cancellable,
|
|
|
|
G_CALLBACK (on_delayed_action_cancelled),
|
|
|
|
machine, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
machine->timeout = fpi_device_add_timeout (machine->dev, delay, callback,
|
|
|
|
user_data, destroy_func);
|
|
|
|
}
|
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
|
|
|
* fpi_ssm_free:
|
2019-07-03 21:29:05 +00:00
|
|
|
* @machine: an #FpiSsm state machine
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
|
|
|
* Frees a state machine. This does not call any error or success
|
|
|
|
* callbacks, so you need to do this yourself.
|
|
|
|
*/
|
2019-07-03 21:29:05 +00:00
|
|
|
void
|
|
|
|
fpi_ssm_free (FpiSsm *machine)
|
2008-02-04 10:23:11 +00:00
|
|
|
{
|
2019-07-03 21:29:05 +00:00
|
|
|
if (!machine)
|
|
|
|
return;
|
|
|
|
|
2019-11-22 17:39:02 +00:00
|
|
|
BUG_ON (machine->timeout != NULL);
|
|
|
|
|
2019-11-20 17:05:46 +00:00
|
|
|
if (machine->ssm_data_destroy)
|
|
|
|
g_clear_pointer (&machine->ssm_data, machine->ssm_data_destroy);
|
2019-07-03 21:29:05 +00:00
|
|
|
g_clear_pointer (&machine->error, g_error_free);
|
2019-12-04 19:03:23 +00:00
|
|
|
g_clear_pointer (&machine->name, g_free);
|
2019-11-28 18:24:55 +00:00
|
|
|
fpi_ssm_clear_delayed_action (machine);
|
2019-07-03 21:29:05 +00:00
|
|
|
g_free (machine);
|
2008-02-04 10:23:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Invoke the state handler */
|
2019-07-03 21:29:05 +00:00
|
|
|
static void
|
|
|
|
__ssm_call_handler (FpiSsm *machine)
|
2008-02-04 10:23:11 +00:00
|
|
|
{
|
2019-12-04 19:14:16 +00:00
|
|
|
fp_dbg ("[%s] %s entering state %d", fp_device_get_driver (machine->dev),
|
|
|
|
machine->name, machine->cur_state);
|
2019-11-20 17:05:46 +00:00
|
|
|
machine->handler (machine, machine->dev);
|
2008-02-04 10:23:11 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
|
|
|
* fpi_ssm_start:
|
2019-11-26 15:18:14 +00:00
|
|
|
* @ssm: (transfer full): an #FpiSsm state machine
|
2019-07-03 21:29:05 +00:00
|
|
|
* @callback: the #FpiSsmCompletedCallback callback to call on completion
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
|
|
|
* Starts a state machine. You can also use this function to restart
|
2018-09-05 14:29:26 +00:00
|
|
|
* a completed or failed state machine. The @callback will be called
|
2018-09-05 11:11:51 +00:00
|
|
|
* on completion.
|
2019-11-26 15:18:14 +00:00
|
|
|
*
|
|
|
|
* Note that @ssm will be stolen when this function is called.
|
|
|
|
* So that all associated data will be free'ed automatically, after the
|
|
|
|
* @callback is ran.
|
2018-09-05 11:11:51 +00:00
|
|
|
*/
|
2019-07-03 21:29:05 +00:00
|
|
|
void
|
|
|
|
fpi_ssm_start (FpiSsm *ssm, FpiSsmCompletedCallback callback)
|
2008-02-04 10:23:11 +00:00
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_if_fail (ssm != NULL);
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
BUG_ON (!ssm->completed);
|
|
|
|
ssm->callback = callback;
|
|
|
|
ssm->cur_state = 0;
|
|
|
|
ssm->completed = FALSE;
|
|
|
|
ssm->error = NULL;
|
|
|
|
__ssm_call_handler (ssm);
|
2008-02-04 10:23:11 +00:00
|
|
|
}
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
static void
|
2019-11-20 17:05:46 +00:00
|
|
|
__subsm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
|
2008-02-18 17:54:15 +00:00
|
|
|
{
|
2019-07-03 21:29:05 +00:00
|
|
|
FpiSsm *parent = ssm->parentsm;
|
|
|
|
|
|
|
|
BUG_ON (!parent);
|
|
|
|
if (error)
|
|
|
|
fpi_ssm_mark_failed (parent, error);
|
|
|
|
else
|
|
|
|
fpi_ssm_next_state (parent);
|
2008-02-18 17:54:15 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
|
|
|
* fpi_ssm_start_subsm:
|
2019-07-03 21:29:05 +00:00
|
|
|
* @parent: an #FpiSsm state machine
|
|
|
|
* @child: an #FpiSsm state machine
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
|
|
|
* Starts a state machine as a child of another. if the child completes
|
|
|
|
* successfully, the parent will be advanced to the next state. if the
|
2018-09-05 14:29:26 +00:00
|
|
|
* child fails, the parent will be marked as failed with the same error code.
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
2018-09-05 14:29:26 +00:00
|
|
|
* The child will be automatically freed upon completion or failure.
|
2018-09-05 11:11:51 +00:00
|
|
|
*/
|
2019-07-03 21:29:05 +00:00
|
|
|
void
|
|
|
|
fpi_ssm_start_subsm (FpiSsm *parent, FpiSsm *child)
|
2008-02-18 17:54:15 +00:00
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_if_fail (parent != NULL);
|
|
|
|
g_return_if_fail (child != NULL);
|
|
|
|
|
2019-11-22 16:19:27 +00:00
|
|
|
BUG_ON (parent->timeout);
|
2019-07-03 21:29:05 +00:00
|
|
|
child->parentsm = parent;
|
2019-12-12 17:41:26 +00:00
|
|
|
|
|
|
|
fpi_ssm_clear_delayed_action (parent);
|
|
|
|
fpi_ssm_clear_delayed_action (child);
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
fpi_ssm_start (child, __subsm_complete);
|
2008-02-18 17:54:15 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
|
|
|
* fpi_ssm_mark_completed:
|
2019-07-03 21:29:05 +00:00
|
|
|
* @machine: an #FpiSsm state machine
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
|
|
|
* Mark a ssm as completed successfully. The callback set when creating
|
2019-11-20 17:05:46 +00:00
|
|
|
* the state machine with fpi_ssm_new () will be called synchronously.
|
2018-09-05 11:11:51 +00:00
|
|
|
*/
|
2019-07-03 21:29:05 +00:00
|
|
|
void
|
|
|
|
fpi_ssm_mark_completed (FpiSsm *machine)
|
2008-02-04 10:23:11 +00:00
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_if_fail (machine != NULL);
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
BUG_ON (machine->completed);
|
2019-11-22 16:19:27 +00:00
|
|
|
BUG_ON (machine->timeout != NULL);
|
|
|
|
|
2019-11-28 18:24:55 +00:00
|
|
|
fpi_ssm_clear_delayed_action (machine);
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
machine->completed = TRUE;
|
2019-11-22 16:19:27 +00:00
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
if (machine->error)
|
2019-12-04 19:14:16 +00:00
|
|
|
fp_dbg ("[%s] %s completed with error: %s", fp_device_get_driver (machine->dev),
|
|
|
|
machine->name, machine->error->message);
|
2019-07-03 21:29:05 +00:00
|
|
|
else
|
2019-12-04 19:14:16 +00:00
|
|
|
fp_dbg ("[%s] %s completed successfully", fp_device_get_driver (machine->dev),
|
|
|
|
machine->name);
|
2019-07-03 21:29:05 +00:00
|
|
|
if (machine->callback)
|
|
|
|
{
|
|
|
|
GError *error = machine->error ? g_error_copy (machine->error) : NULL;
|
|
|
|
|
2019-11-20 17:05:46 +00:00
|
|
|
machine->callback (machine, machine->dev, error);
|
2019-07-03 21:29:05 +00:00
|
|
|
}
|
2019-11-26 15:18:14 +00:00
|
|
|
fpi_ssm_free (machine);
|
2008-02-04 10:23:11 +00:00
|
|
|
}
|
|
|
|
|
2019-12-03 16:22:20 +00:00
|
|
|
static void
|
|
|
|
on_device_timeout_complete (FpDevice *dev,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
FpiSsm *machine = user_data;
|
|
|
|
|
|
|
|
machine->timeout = NULL;
|
|
|
|
fpi_ssm_mark_completed (machine);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* fpi_ssm_mark_completed_delayed:
|
|
|
|
* @machine: an #FpiSsm state machine
|
|
|
|
* @delay: the milliseconds to wait before switching to the next state
|
|
|
|
* @cancellable: (nullable): a #GCancellable to cancel the delayed operation
|
|
|
|
*
|
|
|
|
* Mark a ssm as completed successfully with a delay of @delay ms.
|
|
|
|
* The callback set when creating the state machine with fpi_ssm_new () will be
|
|
|
|
* called when the timeout is over.
|
|
|
|
* The request can be cancelled passing a #GCancellable as @cancellable.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fpi_ssm_mark_completed_delayed (FpiSsm *machine,
|
|
|
|
int delay,
|
|
|
|
GCancellable *cancellable)
|
|
|
|
{
|
|
|
|
g_autofree char *source_name = NULL;
|
|
|
|
|
|
|
|
g_return_if_fail (machine != NULL);
|
|
|
|
|
|
|
|
fpi_ssm_set_delayed_action_timeout (machine, delay,
|
|
|
|
on_device_timeout_complete, cancellable,
|
|
|
|
machine, NULL);
|
|
|
|
|
2019-12-04 19:03:23 +00:00
|
|
|
source_name = g_strdup_printf ("[%s] ssm %s complete %d",
|
2019-12-03 16:22:20 +00:00
|
|
|
fp_device_get_device_id (machine->dev),
|
2019-12-04 19:03:23 +00:00
|
|
|
machine->name, machine->cur_state + 1);
|
2019-12-03 16:22:20 +00:00
|
|
|
g_source_set_name (machine->timeout, source_name);
|
|
|
|
}
|
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
2018-09-05 14:29:26 +00:00
|
|
|
* fpi_ssm_mark_failed:
|
2019-07-03 21:29:05 +00:00
|
|
|
* @machine: an #FpiSsm state machine
|
2019-11-22 17:25:59 +00:00
|
|
|
* @error: (transfer full): a #GError
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
2019-11-26 15:18:14 +00:00
|
|
|
* Mark a state machine as failed with @error as the error code, completing it.
|
2018-09-05 11:11:51 +00:00
|
|
|
*/
|
2019-07-03 21:29:05 +00:00
|
|
|
void
|
|
|
|
fpi_ssm_mark_failed (FpiSsm *machine, GError *error)
|
2008-02-04 10:23:11 +00:00
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_if_fail (machine != NULL);
|
2019-07-03 21:29:05 +00:00
|
|
|
g_assert (error);
|
|
|
|
if (machine->error)
|
|
|
|
{
|
2019-12-04 19:14:16 +00:00
|
|
|
fp_warn ("[%s] SSM %s already has an error set, ignoring new error %s",
|
|
|
|
fp_device_get_driver (machine->dev), machine->name, error->message);
|
2019-07-03 21:29:05 +00:00
|
|
|
g_error_free (error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-04 19:14:16 +00:00
|
|
|
fp_dbg ("[%s] SSM %s failed in state %d with error: %s",
|
|
|
|
fp_device_get_driver (machine->dev), machine->name,
|
|
|
|
machine->cur_state, error->message);
|
2019-11-22 17:25:59 +00:00
|
|
|
machine->error = g_steal_pointer (&error);
|
2019-07-03 21:29:05 +00:00
|
|
|
fpi_ssm_mark_completed (machine);
|
2008-02-04 10:23:11 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
|
|
|
* fpi_ssm_next_state:
|
2019-07-03 21:29:05 +00:00
|
|
|
* @machine: an #FpiSsm state machine
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
|
|
|
* Iterate to next state of a state machine. If the current state is the
|
|
|
|
* last state, then the state machine will be marked as completed, as
|
|
|
|
* if calling fpi_ssm_mark_completed().
|
|
|
|
*/
|
2018-09-19 13:46:29 +00:00
|
|
|
void
|
2019-07-03 21:29:05 +00:00
|
|
|
fpi_ssm_next_state (FpiSsm *machine)
|
2018-09-19 13:46:29 +00:00
|
|
|
{
|
2019-07-03 21:29:05 +00:00
|
|
|
g_return_if_fail (machine != NULL);
|
2018-09-19 13:46:29 +00:00
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
BUG_ON (machine->completed);
|
2019-11-22 16:19:27 +00:00
|
|
|
BUG_ON (machine->timeout != NULL);
|
|
|
|
|
2019-11-28 18:24:55 +00:00
|
|
|
fpi_ssm_clear_delayed_action (machine);
|
2019-11-22 16:19:27 +00:00
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
machine->cur_state++;
|
|
|
|
if (machine->cur_state == machine->nr_states)
|
|
|
|
fpi_ssm_mark_completed (machine);
|
|
|
|
else
|
|
|
|
__ssm_call_handler (machine);
|
2018-09-19 13:46:29 +00:00
|
|
|
}
|
|
|
|
|
2019-11-22 16:19:27 +00:00
|
|
|
void
|
|
|
|
fpi_ssm_cancel_delayed_state_change (FpiSsm *machine)
|
|
|
|
{
|
|
|
|
g_return_if_fail (machine);
|
|
|
|
BUG_ON (machine->completed);
|
|
|
|
BUG_ON (machine->timeout == NULL);
|
|
|
|
|
2019-12-12 14:49:35 +00:00
|
|
|
fp_dbg ("[%s] %s cancelled delayed state change",
|
|
|
|
fp_device_get_driver (machine->dev), machine->name);
|
|
|
|
|
2019-11-28 18:24:55 +00:00
|
|
|
fpi_ssm_clear_delayed_action (machine);
|
2019-11-22 16:19:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_device_timeout_next_state (FpDevice *dev,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
FpiSsm *machine = user_data;
|
|
|
|
|
|
|
|
machine->timeout = NULL;
|
|
|
|
fpi_ssm_next_state (machine);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* fpi_ssm_next_state_delayed:
|
|
|
|
* @machine: an #FpiSsm state machine
|
|
|
|
* @delay: the milliseconds to wait before switching to the next state
|
2019-11-28 18:24:55 +00:00
|
|
|
* @cancellable: (nullable): a #GCancellable to cancel the delayed operation
|
2019-11-22 16:19:27 +00:00
|
|
|
*
|
|
|
|
* Iterate to next state of a state machine with a delay of @delay ms. If the
|
|
|
|
* current state is the last state, then the state machine will be marked as
|
|
|
|
* completed, as if calling fpi_ssm_mark_completed().
|
2019-11-28 18:24:55 +00:00
|
|
|
* Passing a valid #GCancellable will cause the action to be cancelled when
|
|
|
|
* @cancellable is.
|
2019-11-22 16:19:27 +00:00
|
|
|
*/
|
|
|
|
void
|
2019-11-28 18:24:55 +00:00
|
|
|
fpi_ssm_next_state_delayed (FpiSsm *machine,
|
|
|
|
int delay,
|
|
|
|
GCancellable *cancellable)
|
2019-11-22 16:19:27 +00:00
|
|
|
{
|
|
|
|
g_autofree char *source_name = NULL;
|
|
|
|
|
|
|
|
g_return_if_fail (machine != NULL);
|
|
|
|
|
2019-11-28 18:24:55 +00:00
|
|
|
fpi_ssm_set_delayed_action_timeout (machine, delay,
|
|
|
|
on_device_timeout_next_state, cancellable,
|
|
|
|
machine, NULL);
|
2019-11-22 16:19:27 +00:00
|
|
|
|
2019-12-04 19:03:23 +00:00
|
|
|
source_name = g_strdup_printf ("[%s] ssm %s jump to next state %d",
|
2019-11-22 16:19:27 +00:00
|
|
|
fp_device_get_device_id (machine->dev),
|
2019-12-04 19:03:23 +00:00
|
|
|
machine->name, machine->cur_state + 1);
|
2019-11-22 16:19:27 +00:00
|
|
|
g_source_set_name (machine->timeout, source_name);
|
|
|
|
}
|
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
|
|
|
* fpi_ssm_jump_to_state:
|
2019-07-03 21:29:05 +00:00
|
|
|
* @machine: an #FpiSsm state machine
|
2018-09-05 11:11:51 +00:00
|
|
|
* @state: the state to jump to
|
|
|
|
*
|
2018-09-26 12:10:48 +00:00
|
|
|
* Jump to the @state state, bypassing intermediary states.
|
2019-11-26 15:18:14 +00:00
|
|
|
* If @state is the last state, the machine won't be completed unless
|
|
|
|
* fpi_ssm_mark_completed() isn't explicitly called.
|
2018-09-05 11:11:51 +00:00
|
|
|
*/
|
2019-07-03 21:29:05 +00:00
|
|
|
void
|
|
|
|
fpi_ssm_jump_to_state (FpiSsm *machine, int state)
|
2008-02-15 18:09:14 +00:00
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_if_fail (machine != NULL);
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
BUG_ON (machine->completed);
|
2019-11-22 17:03:08 +00:00
|
|
|
BUG_ON (state < 0 || state >= machine->nr_states);
|
2019-11-22 16:19:27 +00:00
|
|
|
BUG_ON (machine->timeout != NULL);
|
|
|
|
|
2019-11-28 18:24:55 +00:00
|
|
|
fpi_ssm_clear_delayed_action (machine);
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
machine->cur_state = state;
|
|
|
|
__ssm_call_handler (machine);
|
2008-02-15 18:09:14 +00:00
|
|
|
}
|
|
|
|
|
2019-11-22 16:19:27 +00:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
FpiSsm *machine;
|
|
|
|
int next_state;
|
|
|
|
} FpiSsmJumpToStateDelayedData;
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_device_timeout_jump_to_state (FpDevice *dev,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
FpiSsmJumpToStateDelayedData *data = user_data;
|
|
|
|
|
|
|
|
data->machine->timeout = NULL;
|
|
|
|
fpi_ssm_jump_to_state (data->machine, data->next_state);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* fpi_ssm_jump_to_state_delayed:
|
|
|
|
* @machine: an #FpiSsm state machine
|
|
|
|
* @state: the state to jump to
|
|
|
|
* @delay: the milliseconds to wait before switching to @state state
|
2019-11-28 18:24:55 +00:00
|
|
|
* @cancellable: (nullable): a #GCancellable to cancel the delayed operation
|
2019-11-22 16:19:27 +00:00
|
|
|
*
|
|
|
|
* Jump to the @state state with a delay of @delay milliseconds, bypassing
|
|
|
|
* intermediary states.
|
2019-11-28 18:24:55 +00:00
|
|
|
* Passing a valid #GCancellable will cause the action to be cancelled when
|
|
|
|
* @cancellable is.
|
2019-11-22 16:19:27 +00:00
|
|
|
*/
|
|
|
|
void
|
2019-11-28 18:24:55 +00:00
|
|
|
fpi_ssm_jump_to_state_delayed (FpiSsm *machine,
|
|
|
|
int state,
|
|
|
|
int delay,
|
|
|
|
GCancellable *cancellable)
|
2019-11-22 16:19:27 +00:00
|
|
|
{
|
|
|
|
FpiSsmJumpToStateDelayedData *data;
|
|
|
|
g_autofree char *source_name = NULL;
|
|
|
|
|
|
|
|
g_return_if_fail (machine != NULL);
|
2019-12-12 13:54:26 +00:00
|
|
|
BUG_ON (state < 0 || state >= machine->nr_states);
|
2019-11-22 16:19:27 +00:00
|
|
|
|
|
|
|
data = g_new0 (FpiSsmJumpToStateDelayedData, 1);
|
|
|
|
data->machine = machine;
|
|
|
|
data->next_state = state;
|
|
|
|
|
2019-11-28 18:24:55 +00:00
|
|
|
fpi_ssm_set_delayed_action_timeout (machine, delay,
|
|
|
|
on_device_timeout_jump_to_state,
|
|
|
|
cancellable, data, g_free);
|
2019-11-22 16:19:27 +00:00
|
|
|
|
2019-12-04 19:03:23 +00:00
|
|
|
source_name = g_strdup_printf ("[%s] ssm %s jump to state %d",
|
2019-11-22 16:19:27 +00:00
|
|
|
fp_device_get_device_id (machine->dev),
|
2019-12-04 19:03:23 +00:00
|
|
|
machine->name, state);
|
2019-11-22 16:19:27 +00:00
|
|
|
g_source_set_name (machine->timeout, source_name);
|
|
|
|
}
|
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
|
|
|
* fpi_ssm_get_cur_state:
|
2019-07-03 21:29:05 +00:00
|
|
|
* @machine: an #FpiSsm state machine
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
|
|
|
* Returns the value of the current state. Note that states are
|
|
|
|
* 0-indexed, so a value of 0 means “the first state”.
|
|
|
|
*
|
|
|
|
* Returns: the current state.
|
|
|
|
*/
|
2019-07-03 21:29:05 +00:00
|
|
|
int
|
|
|
|
fpi_ssm_get_cur_state (FpiSsm *machine)
|
2018-05-28 10:40:22 +00:00
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_val_if_fail (machine != NULL, 0);
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
return machine->cur_state;
|
2018-05-28 10:40:22 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 11:11:51 +00:00
|
|
|
/**
|
|
|
|
* fpi_ssm_get_error:
|
2019-07-03 21:29:05 +00:00
|
|
|
* @machine: an #FpiSsm state machine
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
2018-09-05 14:29:26 +00:00
|
|
|
* Returns the error code set by fpi_ssm_mark_failed().
|
2018-09-05 11:11:51 +00:00
|
|
|
*
|
2019-07-03 21:29:05 +00:00
|
|
|
* Returns: (transfer none): a error code
|
2018-09-05 11:11:51 +00:00
|
|
|
*/
|
2019-07-03 21:29:05 +00:00
|
|
|
GError *
|
|
|
|
fpi_ssm_get_error (FpiSsm *machine)
|
2018-05-28 10:40:22 +00:00
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_val_if_fail (machine != NULL, NULL);
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
return machine->error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* fpi_ssm_dup_error:
|
|
|
|
* @machine: an #FpiSsm state machine
|
|
|
|
*
|
|
|
|
* Returns the error code set by fpi_ssm_mark_failed().
|
|
|
|
*
|
|
|
|
* Returns: (transfer full): a error code
|
|
|
|
*/
|
|
|
|
GError *
|
|
|
|
fpi_ssm_dup_error (FpiSsm *machine)
|
|
|
|
{
|
2021-01-26 14:10:11 +00:00
|
|
|
g_return_val_if_fail (machine != NULL, NULL);
|
|
|
|
|
2019-07-03 21:29:05 +00:00
|
|
|
if (machine->error)
|
|
|
|
return g_error_copy (machine->error);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* fpi_ssm_usb_transfer_cb:
|
|
|
|
* @transfer: a #FpiUsbTransfer
|
|
|
|
* @device: a #FpDevice
|
2019-11-27 19:19:54 +00:00
|
|
|
* @unused_data: User data (unused)
|
2019-07-03 21:29:05 +00:00
|
|
|
* @error: The #GError or %NULL
|
|
|
|
*
|
|
|
|
* Can be used in as a #FpiUsbTransfer callback handler to automatically
|
|
|
|
* advance or fail a statemachine on transfer completion.
|
|
|
|
*
|
|
|
|
* Make sure to set the #FpiSsm on the transfer.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device,
|
2019-11-27 19:19:54 +00:00
|
|
|
gpointer unused_data, GError *error)
|
2019-07-03 21:29:05 +00:00
|
|
|
{
|
|
|
|
g_return_if_fail (transfer->ssm);
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
fpi_ssm_mark_failed (transfer->ssm, error);
|
|
|
|
else
|
|
|
|
fpi_ssm_next_state (transfer->ssm);
|
2018-05-28 10:40:22 +00:00
|
|
|
}
|
2019-11-27 18:36:24 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* fpi_ssm_usb_transfer_with_weak_pointer_cb:
|
|
|
|
* @transfer: a #FpiUsbTransfer
|
|
|
|
* @device: a #FpDevice
|
|
|
|
* @weak_ptr: A #gpointer pointer to nullify. You can pass a pointer to any
|
|
|
|
* #gpointer to nullify when the callback is completed. I.e a
|
|
|
|
* pointer to the current #FpiUsbTransfer.
|
|
|
|
* @error: The #GError or %NULL
|
|
|
|
*
|
|
|
|
* Can be used in as a #FpiUsbTransfer callback handler to automatically
|
|
|
|
* advance or fail a statemachine on transfer completion.
|
|
|
|
* Passing a #gpointer* as @weak_ptr permits to nullify it once we're done
|
|
|
|
* with the transfer.
|
|
|
|
*
|
|
|
|
* Make sure to set the #FpiSsm on the transfer.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fpi_ssm_usb_transfer_with_weak_pointer_cb (FpiUsbTransfer *transfer,
|
|
|
|
FpDevice *device, gpointer weak_ptr,
|
|
|
|
GError *error)
|
|
|
|
{
|
|
|
|
g_return_if_fail (transfer->ssm);
|
|
|
|
|
|
|
|
if (weak_ptr)
|
|
|
|
g_nullify_pointer ((gpointer *) weak_ptr);
|
|
|
|
|
|
|
|
fpi_ssm_usb_transfer_cb (transfer, device, weak_ptr, error);
|
|
|
|
}
|