GCC Code Coverage Report


Directory: ./
File: libfprint/fpi-ssm.c
Date: 2024-05-04 14:54:39
Exec Total Coverage
Lines: 183 203 90.1%
Functions: 25 28 89.3%
Branches: 103 150 68.7%

Line Branch Exec Source
1 /*
2 * Functions to assist with asynchronous driver <---> library communications
3 * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
4 * Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
5 * Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #define FP_COMPONENT "SSM"
23
24 #include "drivers_api.h"
25 #include "fpi-ssm.h"
26
27
28 /**
29 * SECTION:fpi-ssm
30 * @title: Sequential state machine
31 * @short_description: State machine helpers
32 *
33 * Asynchronous driver design encourages some kind of state machine behind it.
34 * #FpiSsm provides a simple mechanism to implement a state machine, which
35 * is often entirely linear. You can however also jump to a specific state
36 * or do an early return from the SSM by completing it.
37 *
38 * e.g. `S1` ↦ `S2` ↦ `S3` ↦ `S4` ↦ `C1` ↦ `C2` ↦ `final`
39 *
40 * Where `S1` is the start state. The `C1` and later states are cleanup states
41 * that may be defined. The difference is that these states will never be
42 * skipped when marking the SSM as completed.
43 *
44 * Use fpi_ssm_new() to create a new state machine with a defined number of
45 * states. Note that the state numbers start at zero, making them match the
46 * first value in a C enumeration.
47 *
48 * To start a ssm, you pass in a completion callback function to fpi_ssm_start()
49 * which gets called when the ssm completes (both on failure and on success).
50 * Starting a ssm also takes ownership of it and it will be automatically
51 * free'ed after the callback function has been called.
52 *
53 * To iterate to the next state, call fpi_ssm_next_state(). It is legal to
54 * attempt to iterate beyond the final state - this is equivalent to marking
55 * the ssm as successfully completed.
56 *
57 * To mark successful completion of a SSM, either iterate beyond the final
58 * state or call fpi_ssm_mark_completed() from any state.
59 *
60 * To mark failed completion of a SSM, call fpi_ssm_mark_failed() from any
61 * state. You must pass a non-zero error code.
62 *
63 * Your state handling function looks at the return value of
64 * fpi_ssm_get_cur_state() in order to determine the current state and hence
65 * which operations to perform (a switch statement is appropriate).
66 *
67 * Typically, the state handling function fires off an asynchronous
68 * communication with the device (such as a USB transfer), and the
69 * callback function iterates the machine to the next state
70 * upon success (or fails).
71 */
72
73 struct _FpiSsm
74 {
75 FpDevice *dev;
76 char *name;
77 FpiSsm *parentsm;
78 gpointer ssm_data;
79 GDestroyNotify ssm_data_destroy;
80 int nr_states;
81 int start_cleanup;
82 int cur_state;
83 gboolean completed;
84 gboolean silence;
85 GSource *timeout;
86 GError *error;
87 FpiSsmCompletedCallback callback;
88 FpiSsmHandlerCallback handler;
89 };
90
91 /**
92 * fpi_ssm_new:
93 * @dev: a #fp_dev fingerprint device
94 * @handler: the callback function
95 * @nr_states: the number of states
96 *
97 * Allocate a new ssm, with @nr_states states. The @handler callback
98 * will be called after each state transition.
99 * This is a macro that calls fpi_ssm_new_full() using @nr_states as the
100 * cleanup states and using the stringified version of @nr_states. It should
101 * be used with an enum value.
102 *
103 * Returns: a new #FpiSsm state machine
104 */
105
106 /**
107 * fpi_ssm_new_full:
108 * @dev: a #fp_dev fingerprint device
109 * @handler: the callback function
110 * @nr_states: the number of states
111 * @start_cleanup: the first cleanup state
112 * @machine_name: the name of the state machine (for debug purposes)
113 *
114 * Allocate a new ssm, with @nr_states states. The @handler callback
115 * will be called after each state transition.
116 *
117 * Returns: a new #FpiSsm state machine
118 */
119 FpiSsm *
120 3211 fpi_ssm_new_full (FpDevice *dev,
121 FpiSsmHandlerCallback handler,
122 int nr_states,
123 int start_cleanup,
124 const char *machine_name)
125 {
126 3211 FpiSsm *machine;
127
128
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3211 times.
3211 BUG_ON (dev == NULL);
129
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3210 times.
3211 BUG_ON (nr_states < 1);
130
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3210 times.
3211 BUG_ON (start_cleanup < 1);
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3211 times.
3211 BUG_ON (start_cleanup > nr_states);
132
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3210 times.
3211 BUG_ON (handler == NULL);
133
134 3211 machine = g_new0 (FpiSsm, 1);
135 3211 machine->handler = handler;
136 3211 machine->nr_states = nr_states;
137 3211 machine->start_cleanup = start_cleanup;
138 3211 machine->dev = dev;
139
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3211 times.
3211 machine->name = g_strdup (machine_name);
140 3211 machine->completed = TRUE;
141 3211 return machine;
142 }
143
144 /**
145 * fpi_ssm_set_data:
146 * @machine: an #FpiSsm state machine
147 * @ssm_data: (nullable): a pointer to machine data
148 * @ssm_data_destroy: (nullable): #GDestroyNotify for @ssm_data
149 *
150 * Sets @machine's data (freeing the existing data, if any).
151 */
152 void
153 3061 fpi_ssm_set_data (FpiSsm *machine,
154 gpointer ssm_data,
155 GDestroyNotify ssm_data_destroy)
156 {
157
1/2
✓ Branch 0 taken 3061 times.
✗ Branch 1 not taken.
3061 g_return_if_fail (machine);
158
159
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3058 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3061 if (machine->ssm_data_destroy && machine->ssm_data)
160 3 machine->ssm_data_destroy (machine->ssm_data);
161
162 3061 machine->ssm_data = ssm_data;
163 3061 machine->ssm_data_destroy = ssm_data_destroy;
164 }
165
166 /**
167 * fpi_ssm_get_data:
168 * @machine: an #FpiSsm state machine
169 *
170 * Retrieve the pointer to SSM data set with fpi_ssm_set_ssm_data()
171 *
172 * Returns: a pointer
173 */
174 void *
175 11719 fpi_ssm_get_data (FpiSsm *machine)
176 {
177
1/2
✓ Branch 0 taken 11719 times.
✗ Branch 1 not taken.
11719 g_return_val_if_fail (machine, NULL);
178
179 11719 return machine->ssm_data;
180 }
181
182 /**
183 * fpi_ssm_get_device:
184 * @machine: an #FpiSsm state machine
185 *
186 * Retrieve the device that the SSM is for.
187 *
188 * Returns: #FpDevice
189 */
190 FpDevice *
191 2418 fpi_ssm_get_device (FpiSsm *machine)
192 {
193
1/2
✓ Branch 0 taken 2418 times.
✗ Branch 1 not taken.
2418 g_return_val_if_fail (machine, NULL);
194
195 2418 return machine->dev;
196 }
197
198 static void
199 40285 fpi_ssm_clear_delayed_action (FpiSsm *machine)
200 {
201
1/2
✓ Branch 0 taken 40285 times.
✗ Branch 1 not taken.
40285 g_return_if_fail (machine);
202
203
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 40277 times.
40285 g_clear_pointer (&machine->timeout, g_source_destroy);
204 }
205
206 static void
207 38 fpi_ssm_set_delayed_action_timeout (FpiSsm *machine,
208 int delay,
209 FpTimeoutFunc callback,
210 gpointer user_data,
211 GDestroyNotify destroy_func)
212 {
213
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 g_return_if_fail (machine);
214
215
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 35 times.
38 BUG_ON (machine->completed);
216
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 BUG_ON (machine->timeout != NULL);
217
218 38 fpi_ssm_clear_delayed_action (machine);
219
220 38 machine->timeout = fpi_device_add_timeout (machine->dev, delay, callback,
221 user_data, destroy_func);
222 }
223
224 /**
225 * fpi_ssm_free:
226 * @machine: an #FpiSsm state machine
227 *
228 * Frees a state machine. This does not call any error or success
229 * callbacks, so you need to do this yourself.
230 */
231 void
232 3211 fpi_ssm_free (FpiSsm *machine)
233 {
234
1/2
✓ Branch 0 taken 3211 times.
✗ Branch 1 not taken.
3211 if (!machine)
235 return;
236
237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3211 times.
3211 BUG_ON (machine->timeout != NULL);
238
239
2/2
✓ Branch 0 taken 602 times.
✓ Branch 1 taken 2609 times.
3211 if (machine->ssm_data_destroy)
240
1/2
✓ Branch 0 taken 602 times.
✗ Branch 1 not taken.
602 g_clear_pointer (&machine->ssm_data, machine->ssm_data_destroy);
241
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3203 times.
3211 g_clear_pointer (&machine->error, g_error_free);
242
1/2
✓ Branch 0 taken 3211 times.
✗ Branch 1 not taken.
3211 g_clear_pointer (&machine->name, g_free);
243 3211 fpi_ssm_clear_delayed_action (machine);
244 3211 g_free (machine);
245 }
246
247 /* Invoke the state handler */
248 static void
249 29518 __ssm_call_handler (FpiSsm *machine, gboolean force_msg)
250 {
251
4/4
✓ Branch 0 taken 26312 times.
✓ Branch 1 taken 3206 times.
✓ Branch 2 taken 10320 times.
✓ Branch 3 taken 15992 times.
29518 if (force_msg || !machine->silence)
252 13526 fp_dbg ("[%s] %s entering state %d", fp_device_get_driver (machine->dev),
253 machine->name, machine->cur_state);
254 29518 machine->handler (machine, machine->dev);
255 29518 }
256
257 /**
258 * fpi_ssm_start:
259 * @ssm: (transfer full): an #FpiSsm state machine
260 * @callback: the #FpiSsmCompletedCallback callback to call on completion
261 *
262 * Starts a state machine. You can also use this function to restart
263 * a completed or failed state machine. The @callback will be called
264 * on completion.
265 *
266 * Note that @ssm will be stolen when this function is called.
267 * So that all associated data will be free'ed automatically, after the
268 * @callback is ran.
269 */
270 void
271 3199 fpi_ssm_start (FpiSsm *ssm, FpiSsmCompletedCallback callback)
272 {
273
1/2
✓ Branch 0 taken 3199 times.
✗ Branch 1 not taken.
3199 g_return_if_fail (ssm != NULL);
274
275
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3198 times.
3199 BUG_ON (!ssm->completed);
276 3199 ssm->callback = callback;
277 3199 ssm->cur_state = 0;
278 3199 ssm->completed = FALSE;
279 3199 ssm->error = NULL;
280 3199 __ssm_call_handler (ssm, TRUE);
281 }
282
283 static void
284 2516 __subsm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
285 {
286 2516 FpiSsm *parent = ssm->parentsm;
287
288
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2516 times.
2516 BUG_ON (!parent);
289
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2515 times.
2516 if (error)
290 1 fpi_ssm_mark_failed (parent, error);
291 else
292 2515 fpi_ssm_next_state (parent);
293 2516 }
294
295 /**
296 * fpi_ssm_start_subsm:
297 * @parent: an #FpiSsm state machine
298 * @child: an #FpiSsm state machine
299 *
300 * Starts a state machine as a child of another. if the child completes
301 * successfully, the parent will be advanced to the next state. if the
302 * child fails, the parent will be marked as failed with the same error code.
303 *
304 * The child will be automatically freed upon completion or failure.
305 */
306 void
307 2516 fpi_ssm_start_subsm (FpiSsm *parent, FpiSsm *child)
308 {
309
1/2
✓ Branch 0 taken 2516 times.
✗ Branch 1 not taken.
2516 g_return_if_fail (parent != NULL);
310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2516 times.
2516 g_return_if_fail (child != NULL);
311
312
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2515 times.
2516 BUG_ON (parent->timeout);
313 2516 child->parentsm = parent;
314
315 2516 fpi_ssm_clear_delayed_action (parent);
316 2516 fpi_ssm_clear_delayed_action (child);
317
318 2516 fpi_ssm_start (child, __subsm_complete);
319 }
320
321 /**
322 * fpi_ssm_mark_completed:
323 * @machine: an #FpiSsm state machine
324 *
325 * Mark a ssm as completed successfully. The callback set when creating
326 * the state machine with fpi_ssm_new() will be called synchronously.
327 *
328 * Note that any later cleanup state will still be executed.
329 */
330 void
331 3190 fpi_ssm_mark_completed (FpiSsm *machine)
332 {
333 3190 int next_state;
334
335
1/2
✓ Branch 0 taken 3190 times.
✗ Branch 1 not taken.
3190 g_return_if_fail (machine != NULL);
336
337
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3187 times.
3190 BUG_ON (machine->completed);
338
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3188 times.
3190 BUG_ON (machine->timeout != NULL);
339
340 3190 fpi_ssm_clear_delayed_action (machine);
341
342 /* complete in a cleanup state just moves forward one step */
343
2/2
✓ Branch 0 taken 2501 times.
✓ Branch 1 taken 689 times.
3190 if (machine->cur_state < machine->start_cleanup)
344 next_state = machine->start_cleanup;
345 else
346 2501 next_state = machine->cur_state + 1;
347
348
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3183 times.
3190 if (next_state < machine->nr_states)
349 {
350 7 machine->cur_state = next_state;
351 7 __ssm_call_handler (machine, TRUE);
352 7 return;
353 }
354
355 3183 machine->completed = TRUE;
356
357
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3175 times.
3183 if (machine->error)
358 8 fp_dbg ("[%s] %s completed with error: %s", fp_device_get_driver (machine->dev),
359 machine->name, machine->error->message);
360 else
361 3175 fp_dbg ("[%s] %s completed successfully", fp_device_get_driver (machine->dev),
362 machine->name);
363
2/2
✓ Branch 0 taken 3179 times.
✓ Branch 1 taken 4 times.
3183 if (machine->callback)
364 {
365
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3172 times.
3179 GError *error = machine->error ? g_error_copy (machine->error) : NULL;
366
367 3179 machine->callback (machine, machine->dev, error);
368 }
369 3183 fpi_ssm_free (machine);
370 }
371
372 static void
373 2 on_device_timeout_complete (FpDevice *dev,
374 gpointer user_data)
375 {
376 2 FpiSsm *machine = user_data;
377
378 2 machine->timeout = NULL;
379 2 fpi_ssm_mark_completed (machine);
380 2 }
381
382 /**
383 * fpi_ssm_mark_completed_delayed:
384 * @machine: an #FpiSsm state machine
385 * @delay: the milliseconds to wait before switching to the next state
386 *
387 * Mark a ssm as completed successfully with a delay of @delay ms.
388 * The callback set when creating the state machine with fpi_ssm_new () will be
389 * called when the timeout is over.
390 */
391 void
392 5 fpi_ssm_mark_completed_delayed (FpiSsm *machine,
393 int delay)
394 {
395 5 g_autofree char *source_name = NULL;
396
397
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 g_return_if_fail (machine != NULL);
398
399 5 fpi_ssm_set_delayed_action_timeout (machine, delay,
400 on_device_timeout_complete,
401 machine, NULL);
402
403 5 source_name = g_strdup_printf ("[%s] ssm %s complete %d",
404 fp_device_get_device_id (machine->dev),
405 5 machine->name, machine->cur_state + 1);
406 5 g_source_set_name (machine->timeout, source_name);
407 }
408
409 /**
410 * fpi_ssm_mark_failed:
411 * @machine: an #FpiSsm state machine
412 * @error: (transfer full): a #GError
413 *
414 * Mark a state machine as failed with @error as the error code, completing it.
415 */
416 void
417 10 fpi_ssm_mark_failed (FpiSsm *machine, GError *error)
418 {
419
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 g_return_if_fail (machine != NULL);
420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 g_assert (error);
421
422 /* During cleanup it is OK to call fpi_ssm_mark_failed a second time */
423
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
10 if (machine->error && machine->cur_state < machine->start_cleanup)
424 {
425 fp_warn ("[%s] SSM %s already has an error set, ignoring new error %s",
426 fp_device_get_driver (machine->dev), machine->name, error->message);
427 g_error_free (error);
428 return;
429 }
430
431
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
18 fp_dbg ("[%s] SSM %s failed in state %d%s with error: %s",
432 fp_device_get_driver (machine->dev), machine->name,
433 machine->cur_state,
434 machine->cur_state >= machine->start_cleanup ? " (cleanup)" : "",
435 error->message);
436
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
10 if (!machine->error)
437 8 machine->error = g_steal_pointer (&error);
438 else
439 2 g_error_free (error);
440 10 fpi_ssm_mark_completed (machine);
441 }
442
443 /**
444 * fpi_ssm_next_state:
445 * @machine: an #FpiSsm state machine
446 *
447 * Iterate to next state of a state machine. If the current state is the
448 * last state, then the state machine will be marked as completed, as
449 * if calling fpi_ssm_mark_completed().
450 */
451 void
452 17502 fpi_ssm_next_state (FpiSsm *machine)
453 {
454
1/2
✓ Branch 0 taken 17502 times.
✗ Branch 1 not taken.
17502 g_return_if_fail (machine != NULL);
455
456
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 17500 times.
17502 BUG_ON (machine->completed);
457
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17501 times.
17502 BUG_ON (machine->timeout != NULL);
458
459 17502 fpi_ssm_clear_delayed_action (machine);
460
461 17502 machine->cur_state++;
462
2/2
✓ Branch 0 taken 2497 times.
✓ Branch 1 taken 15005 times.
17502 if (machine->cur_state == machine->nr_states)
463 2497 fpi_ssm_mark_completed (machine);
464 else
465 15005 __ssm_call_handler (machine, FALSE);
466 }
467
468 void
469 5 fpi_ssm_cancel_delayed_state_change (FpiSsm *machine)
470 {
471
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 g_return_if_fail (machine);
472
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 BUG_ON (machine->completed);
473
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 BUG_ON (machine->timeout == NULL);
474
475 5 fp_dbg ("[%s] %s cancelled delayed state change",
476 fp_device_get_driver (machine->dev), machine->name);
477
478 5 fpi_ssm_clear_delayed_action (machine);
479 }
480
481 static void
482 22 on_device_timeout_next_state (FpDevice *dev,
483 gpointer user_data)
484 {
485 22 FpiSsm *machine = user_data;
486
487 22 machine->timeout = NULL;
488 22 fpi_ssm_next_state (machine);
489 22 }
490
491 /**
492 * fpi_ssm_next_state_delayed:
493 * @machine: an #FpiSsm state machine
494 * @delay: the milliseconds to wait before switching to the next state
495 *
496 * Iterate to next state of a state machine with a delay of @delay ms. If the
497 * current state is the last state, then the state machine will be marked as
498 * completed, as if calling fpi_ssm_mark_completed().
499 */
500 void
501 25 fpi_ssm_next_state_delayed (FpiSsm *machine,
502 int delay)
503 {
504 25 g_autofree char *source_name = NULL;
505
506
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 g_return_if_fail (machine != NULL);
507
508 25 fpi_ssm_set_delayed_action_timeout (machine, delay,
509 on_device_timeout_next_state,
510 machine, NULL);
511
512 25 source_name = g_strdup_printf ("[%s] ssm %s jump to next state %d",
513 fp_device_get_device_id (machine->dev),
514 25 machine->name, machine->cur_state + 1);
515 25 g_source_set_name (machine->timeout, source_name);
516 }
517
518 /**
519 * fpi_ssm_jump_to_state:
520 * @machine: an #FpiSsm state machine
521 * @state: the state to jump to
522 *
523 * Jump to the @state state, bypassing intermediary states.
524 * If @state is the last state, the machine won't be completed unless
525 * fpi_ssm_mark_completed() isn't explicitly called.
526 */
527 void
528 11307 fpi_ssm_jump_to_state (FpiSsm *machine, int state)
529 {
530
1/2
✓ Branch 0 taken 11307 times.
✗ Branch 1 not taken.
11307 g_return_if_fail (machine != NULL);
531
532
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11305 times.
11307 BUG_ON (machine->completed);
533
4/4
✓ Branch 0 taken 11305 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 11303 times.
11307 BUG_ON (state < 0 || state > machine->nr_states);
534
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11306 times.
11307 BUG_ON (machine->timeout != NULL);
535
536 11307 fpi_ssm_clear_delayed_action (machine);
537
538 11307 machine->cur_state = state;
539
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11307 times.
11307 if (machine->cur_state == machine->nr_states)
540 fpi_ssm_mark_completed (machine);
541 else
542 11307 __ssm_call_handler (machine, FALSE);
543 }
544
545 typedef struct
546 {
547 FpiSsm *machine;
548 int next_state;
549 } FpiSsmJumpToStateDelayedData;
550
551 static void
552 6 on_device_timeout_jump_to_state (FpDevice *dev,
553 gpointer user_data)
554 {
555 6 FpiSsmJumpToStateDelayedData *data = user_data;
556
557 6 data->machine->timeout = NULL;
558 6 fpi_ssm_jump_to_state (data->machine, data->next_state);
559 6 }
560
561 /**
562 * fpi_ssm_jump_to_state_delayed:
563 * @machine: an #FpiSsm state machine
564 * @state: the state to jump to
565 * @delay: the milliseconds to wait before switching to @state state
566 *
567 * Jump to the @state state with a delay of @delay milliseconds, bypassing
568 * intermediary states.
569 */
570 void
571 8 fpi_ssm_jump_to_state_delayed (FpiSsm *machine,
572 int state,
573 int delay)
574 {
575 8 FpiSsmJumpToStateDelayedData *data;
576 8 g_autofree char *source_name = NULL;
577
578
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 g_return_if_fail (machine != NULL);
579
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 6 times.
8 BUG_ON (state < 0 || state > machine->nr_states);
580
581 8 data = g_new0 (FpiSsmJumpToStateDelayedData, 1);
582 8 data->machine = machine;
583 8 data->next_state = state;
584
585 8 fpi_ssm_set_delayed_action_timeout (machine, delay,
586 on_device_timeout_jump_to_state,
587 data, g_free);
588
589 8 source_name = g_strdup_printf ("[%s] ssm %s jump to state %d",
590 fp_device_get_device_id (machine->dev),
591 machine->name, state);
592 8 g_source_set_name (machine->timeout, source_name);
593 }
594
595 /**
596 * fpi_ssm_get_cur_state:
597 * @machine: an #FpiSsm state machine
598 *
599 * Returns the value of the current state. Note that states are
600 * 0-indexed, so a value of 0 means “the first state”.
601 *
602 * Returns: the current state.
603 */
604 int
605 45014 fpi_ssm_get_cur_state (FpiSsm *machine)
606 {
607
1/2
✓ Branch 0 taken 45014 times.
✗ Branch 1 not taken.
45014 g_return_val_if_fail (machine != NULL, 0);
608
609 45014 return machine->cur_state;
610 }
611
612 /**
613 * fpi_ssm_get_error:
614 * @machine: an #FpiSsm state machine
615 *
616 * Returns the error code set by fpi_ssm_mark_failed().
617 *
618 * Returns: (transfer none): a error code
619 */
620 GError *
621 7 fpi_ssm_get_error (FpiSsm *machine)
622 {
623
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 g_return_val_if_fail (machine != NULL, NULL);
624
625 7 return machine->error;
626 }
627
628 /**
629 * fpi_ssm_dup_error:
630 * @machine: an #FpiSsm state machine
631 *
632 * Returns the error code set by fpi_ssm_mark_failed().
633 *
634 * Returns: (transfer full): a error code
635 */
636 GError *
637 fpi_ssm_dup_error (FpiSsm *machine)
638 {
639 g_return_val_if_fail (machine != NULL, NULL);
640
641 if (machine->error)
642 return g_error_copy (machine->error);
643
644 return NULL;
645 }
646
647 /**
648 * fpi_ssm_silence_debug:
649 * @machine: an #FpiSsm state machine
650 *
651 * Turn off state change debug messages from this SSM. This does not disable
652 * all messages, as e.g. the initial state, SSM completion and cleanup states
653 * are still printed out.
654 *
655 * Use if the SSM loops and would flood the debug log otherwise.
656 */
657 void
658 78 fpi_ssm_silence_debug (FpiSsm *machine)
659 {
660 78 machine->silence = TRUE;
661 78 }
662
663 /**
664 * fpi_ssm_usb_transfer_cb:
665 * @transfer: a #FpiUsbTransfer
666 * @device: a #FpDevice
667 * @unused_data: User data (unused)
668 * @error: The #GError or %NULL
669 *
670 * Can be used in as a #FpiUsbTransfer callback handler to automatically
671 * advance or fail a statemachine on transfer completion.
672 *
673 * Make sure to set the #FpiSsm on the transfer.
674 */
675 void
676 784 fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device,
677 gpointer unused_data, GError *error)
678 {
679
1/2
✓ Branch 0 taken 784 times.
✗ Branch 1 not taken.
784 g_return_if_fail (transfer->ssm);
680
681
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 784 times.
784 if (error)
682 fpi_ssm_mark_failed (transfer->ssm, error);
683 else
684 784 fpi_ssm_next_state (transfer->ssm);
685 }
686
687 /**
688 * fpi_ssm_usb_transfer_with_weak_pointer_cb:
689 * @transfer: a #FpiUsbTransfer
690 * @device: a #FpDevice
691 * @weak_ptr: A #gpointer pointer to nullify. You can pass a pointer to any
692 * #gpointer to nullify when the callback is completed. I.e a
693 * pointer to the current #FpiUsbTransfer.
694 * @error: The #GError or %NULL
695 *
696 * Can be used in as a #FpiUsbTransfer callback handler to automatically
697 * advance or fail a statemachine on transfer completion.
698 * Passing a #gpointer* as @weak_ptr permits to nullify it once we're done
699 * with the transfer.
700 *
701 * Make sure to set the #FpiSsm on the transfer.
702 */
703 void
704 fpi_ssm_usb_transfer_with_weak_pointer_cb (FpiUsbTransfer *transfer,
705 FpDevice *device, gpointer weak_ptr,
706 GError *error)
707 {
708 g_return_if_fail (transfer->ssm);
709
710 if (weak_ptr)
711 g_nullify_pointer ((gpointer *) weak_ptr);
712
713 fpi_ssm_usb_transfer_cb (transfer, device, weak_ptr, error);
714 }
715
716 /**
717 * fpi_ssm_spi_transfer_cb:
718 * @transfer: a #FpiSpiTransfer
719 * @device: a #FpDevice
720 * @unused_data: User data (unused)
721 * @error: The #GError or %NULL
722 *
723 * Can be used in as a #FpiSpiTransfer callback handler to automatically
724 * advance or fail a statemachine on transfer completion.
725 *
726 * Make sure to set the #FpiSsm on the transfer.
727 */
728 void
729 8112 fpi_ssm_spi_transfer_cb (FpiSpiTransfer *transfer, FpDevice *device,
730 gpointer unused_data, GError *error)
731 {
732
1/2
✓ Branch 0 taken 8112 times.
✗ Branch 1 not taken.
8112 g_return_if_fail (transfer->ssm);
733
734
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8112 times.
8112 if (error)
735 fpi_ssm_mark_failed (transfer->ssm, error);
736 else
737 8112 fpi_ssm_next_state (transfer->ssm);
738 }
739
740 /**
741 * fpi_ssm_spi_transfer_with_weak_pointer_cb:
742 * @transfer: a #FpiSpiTransfer
743 * @device: a #FpDevice
744 * @weak_ptr: A #gpointer pointer to nullify. You can pass a pointer to any
745 * #gpointer to nullify when the callback is completed. I.e a
746 * pointer to the current #FpiSpiTransfer.
747 * @error: The #GError or %NULL
748 *
749 * Can be used in as a #FpiSpiTransfer callback handler to automatically
750 * advance or fail a statemachine on transfer completion.
751 * Passing a #gpointer* as @weak_ptr permits to nullify it once we're done
752 * with the transfer.
753 *
754 * Make sure to set the #FpiSsm on the transfer.
755 */
756 void
757 fpi_ssm_spi_transfer_with_weak_pointer_cb (FpiSpiTransfer *transfer,
758 FpDevice *device, gpointer weak_ptr,
759 GError *error)
760 {
761 g_return_if_fail (transfer->ssm);
762
763 if (weak_ptr)
764 g_nullify_pointer ((gpointer *) weak_ptr);
765
766 fpi_ssm_spi_transfer_cb (transfer, device, weak_ptr, error);
767 }
768