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 | 3660 | fpi_ssm_new_full (FpDevice *dev, | |
121 | FpiSsmHandlerCallback handler, | ||
122 | int nr_states, | ||
123 | int start_cleanup, | ||
124 | const char *machine_name) | ||
125 | { | ||
126 | 3660 | FpiSsm *machine; | |
127 | |||
128 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3660 times.
|
3660 | BUG_ON (dev == NULL); |
129 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3659 times.
|
3660 | BUG_ON (nr_states < 1); |
130 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3659 times.
|
3660 | BUG_ON (start_cleanup < 1); |
131 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3660 times.
|
3660 | BUG_ON (start_cleanup > nr_states); |
132 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3659 times.
|
3660 | BUG_ON (handler == NULL); |
133 | |||
134 | 3660 | machine = g_new0 (FpiSsm, 1); | |
135 | 3660 | machine->handler = handler; | |
136 | 3660 | machine->nr_states = nr_states; | |
137 | 3660 | machine->start_cleanup = start_cleanup; | |
138 | 3660 | machine->dev = dev; | |
139 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3660 times.
|
3660 | machine->name = g_strdup (machine_name); |
140 | 3660 | machine->completed = TRUE; | |
141 | 3660 | 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 | 3481 | fpi_ssm_set_data (FpiSsm *machine, | |
154 | gpointer ssm_data, | ||
155 | GDestroyNotify ssm_data_destroy) | ||
156 | { | ||
157 |
1/2✓ Branch 0 taken 3481 times.
✗ Branch 1 not taken.
|
3481 | g_return_if_fail (machine); |
158 | |||
159 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3478 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3481 | if (machine->ssm_data_destroy && machine->ssm_data) |
160 | 3 | machine->ssm_data_destroy (machine->ssm_data); | |
161 | |||
162 | 3481 | machine->ssm_data = ssm_data; | |
163 | 3481 | 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 | 13080 | fpi_ssm_get_data (FpiSsm *machine) | |
176 | { | ||
177 |
1/2✓ Branch 0 taken 13080 times.
✗ Branch 1 not taken.
|
13080 | g_return_val_if_fail (machine, NULL); |
178 | |||
179 | 13080 | 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 | 42192 | fpi_ssm_clear_delayed_action (FpiSsm *machine) | |
200 | { | ||
201 |
1/2✓ Branch 0 taken 42192 times.
✗ Branch 1 not taken.
|
42192 | g_return_if_fail (machine); |
202 | |||
203 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 42184 times.
|
42192 | 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 | 3660 | fpi_ssm_free (FpiSsm *machine) | |
233 | { | ||
234 |
1/2✓ Branch 0 taken 3660 times.
✗ Branch 1 not taken.
|
3660 | if (!machine) |
235 | return; | ||
236 | |||
237 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3660 times.
|
3660 | BUG_ON (machine->timeout != NULL); |
238 | |||
239 |
2/2✓ Branch 0 taken 1020 times.
✓ Branch 1 taken 2640 times.
|
3660 | if (machine->ssm_data_destroy) |
240 |
1/2✓ Branch 0 taken 1020 times.
✗ Branch 1 not taken.
|
1020 | g_clear_pointer (&machine->ssm_data, machine->ssm_data_destroy); |
241 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 3650 times.
|
3660 | g_clear_pointer (&machine->error, g_error_free); |
242 |
1/2✓ Branch 0 taken 3660 times.
✗ Branch 1 not taken.
|
3660 | g_clear_pointer (&machine->name, g_free); |
243 | 3660 | fpi_ssm_clear_delayed_action (machine); | |
244 | 3660 | g_free (machine); | |
245 | } | ||
246 | |||
247 | /* Invoke the state handler */ | ||
248 | static void | ||
249 | 30944 | __ssm_call_handler (FpiSsm *machine, gboolean force_msg) | |
250 | { | ||
251 |
4/4✓ Branch 0 taken 27289 times.
✓ Branch 1 taken 3655 times.
✓ Branch 2 taken 11297 times.
✓ Branch 3 taken 15992 times.
|
30944 | if (force_msg || !machine->silence) |
252 | 14952 | fp_dbg ("[%s] %s entering state %d", fp_device_get_driver (machine->dev), | |
253 | machine->name, machine->cur_state); | ||
254 | 30944 | machine->handler (machine, machine->dev); | |
255 | 30944 | } | |
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 | 3648 | fpi_ssm_start (FpiSsm *ssm, FpiSsmCompletedCallback callback) | |
272 | { | ||
273 |
1/2✓ Branch 0 taken 3648 times.
✗ Branch 1 not taken.
|
3648 | g_return_if_fail (ssm != NULL); |
274 | |||
275 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3647 times.
|
3648 | BUG_ON (!ssm->completed); |
276 | 3648 | ssm->callback = callback; | |
277 | 3648 | ssm->cur_state = 0; | |
278 | 3648 | ssm->completed = FALSE; | |
279 | 3648 | ssm->error = NULL; | |
280 | 3648 | __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 | 3639 | fpi_ssm_mark_completed (FpiSsm *machine) | |
332 | { | ||
333 | 3639 | int next_state; | |
334 | |||
335 |
1/2✓ Branch 0 taken 3639 times.
✗ Branch 1 not taken.
|
3639 | g_return_if_fail (machine != NULL); |
336 | |||
337 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3636 times.
|
3639 | BUG_ON (machine->completed); |
338 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3637 times.
|
3639 | BUG_ON (machine->timeout != NULL); |
339 | |||
340 | 3639 | fpi_ssm_clear_delayed_action (machine); | |
341 | |||
342 | /* complete in a cleanup state just moves forward one step */ | ||
343 |
2/2✓ Branch 0 taken 2533 times.
✓ Branch 1 taken 1106 times.
|
3639 | if (machine->cur_state < machine->start_cleanup) |
344 | next_state = machine->start_cleanup; | ||
345 | else | ||
346 | 2533 | next_state = machine->cur_state + 1; | |
347 | |||
348 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3632 times.
|
3639 | 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 | 3632 | machine->completed = TRUE; | |
356 | |||
357 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 3622 times.
|
3632 | if (machine->error) |
358 | 10 | fp_dbg ("[%s] %s completed with error: %s", fp_device_get_driver (machine->dev), | |
359 | machine->name, machine->error->message); | ||
360 | else | ||
361 | 3622 | fp_dbg ("[%s] %s completed successfully", fp_device_get_driver (machine->dev), | |
362 | machine->name); | ||
363 |
2/2✓ Branch 0 taken 3628 times.
✓ Branch 1 taken 4 times.
|
3632 | if (machine->callback) |
364 | { | ||
365 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3619 times.
|
3628 | GError *error = machine->error ? g_error_copy (machine->error) : NULL; |
366 | |||
367 | 3628 | machine->callback (machine, machine->dev, error); | |
368 | } | ||
369 | 3632 | 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 | 12 | fpi_ssm_mark_failed (FpiSsm *machine, GError *error) | |
418 | { | ||
419 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | g_return_if_fail (machine != NULL); |
420 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | 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 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
12 | 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 10 times.
✓ Branch 1 taken 2 times.
|
22 | 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 10 times.
✓ Branch 1 taken 2 times.
|
12 | if (!machine->error) |
437 | 10 | machine->error = g_steal_pointer (&error); | |
438 | else | ||
439 | 2 | g_error_free (error); | |
440 | 12 | 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 | 18397 | fpi_ssm_next_state (FpiSsm *machine) | |
453 | { | ||
454 |
1/2✓ Branch 0 taken 18397 times.
✗ Branch 1 not taken.
|
18397 | g_return_if_fail (machine != NULL); |
455 | |||
456 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 18395 times.
|
18397 | BUG_ON (machine->completed); |
457 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 18396 times.
|
18397 | BUG_ON (machine->timeout != NULL); |
458 | |||
459 | 18397 | fpi_ssm_clear_delayed_action (machine); | |
460 | |||
461 | 18397 | machine->cur_state++; | |
462 |
2/2✓ Branch 0 taken 2523 times.
✓ Branch 1 taken 15874 times.
|
18397 | if (machine->cur_state == machine->nr_states) |
463 | 2523 | fpi_ssm_mark_completed (machine); | |
464 | else | ||
465 | 15874 | __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 | 11421 | fpi_ssm_jump_to_state (FpiSsm *machine, int state) | |
529 | { | ||
530 |
1/2✓ Branch 0 taken 11421 times.
✗ Branch 1 not taken.
|
11421 | g_return_if_fail (machine != NULL); |
531 | |||
532 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11419 times.
|
11421 | BUG_ON (machine->completed); |
533 |
4/4✓ Branch 0 taken 11419 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 11417 times.
|
11421 | BUG_ON (state < 0 || state > machine->nr_states); |
534 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11420 times.
|
11421 | BUG_ON (machine->timeout != NULL); |
535 | |||
536 | 11421 | fpi_ssm_clear_delayed_action (machine); | |
537 | |||
538 | 11421 | machine->cur_state = state; | |
539 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 11415 times.
|
11421 | if (machine->cur_state == machine->nr_states) |
540 | 6 | fpi_ssm_mark_completed (machine); | |
541 | else | ||
542 | 11415 | __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 | 46523 | fpi_ssm_get_cur_state (FpiSsm *machine) | |
606 | { | ||
607 |
1/2✓ Branch 0 taken 46523 times.
✗ Branch 1 not taken.
|
46523 | g_return_val_if_fail (machine != NULL, 0); |
608 | |||
609 | 46523 | 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 | 12 | fpi_ssm_get_error (FpiSsm *machine) | |
622 | { | ||
623 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | g_return_val_if_fail (machine != NULL, NULL); |
624 | |||
625 | 12 | 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 | 1207 | fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device, | |
677 | gpointer unused_data, GError *error) | ||
678 | { | ||
679 |
1/2✓ Branch 0 taken 1207 times.
✗ Branch 1 not taken.
|
1207 | g_return_if_fail (transfer->ssm); |
680 | |||
681 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1207 times.
|
1207 | if (error) |
682 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
683 | else | ||
684 | 1207 | 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 |