2 * Functions to assist with asynchronous driver <---> library communications
3 * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #define FP_COMPONENT "drv"
25 #include "fp_internal.h"
27 /* SSM: sequential state machine
28 * Asynchronous driver design encourages some kind of state machine behind it.
29 * In most cases, the state machine is entirely linear - you only go to the
30 * next state, you never jump or go backwards. The SSM functions help you
31 * implement such a machine.
33 * e.g. S1 --> S2 --> S3 --> S4
34 * S1 is the start state
35 * There is also an implicit error state and an implicit accepting state
36 * (both with implicit edges from every state).
38 * You can also jump to any arbitrary state (while marking completion of the
39 * current state) while the machine is running. In other words there are
40 * implicit edges linking one state to every other state. OK, we're stretching
41 * the "state machine" description at this point.
43 * To create a ssm, you pass a state handler function and the total number of
44 * states (4 in the above example).
46 * To start a ssm, you pass in a completion callback function which gets
47 * called when the ssm completes (both on error and on failure).
49 * To iterate to the next state, call fpi_ssm_next_state(). It is legal to
50 * attempt to iterate beyond the final state - this is equivalent to marking
51 * the ssm as successfully completed.
53 * To mark successful completion of a SSM, either iterate beyond the final
54 * state or call fpi_ssm_mark_completed() from any state.
56 * To mark failed completion of a SSM, call fpi_ssm_mark_aborted() from any
57 * state. You must pass a non-zero error code.
59 * Your state handling function looks at ssm->cur_state in order to determine
60 * the current state and hence which operations to perform (a switch statement
62 * Typically, the state handling function fires off an asynchronous libusb
63 * transfer, and the callback function iterates the machine to the next state
64 * upon success (or aborts the machine on transfer failure).
66 * Your completion callback should examine ssm->error in order to determine
67 * whether the ssm completed or failed. An error code of zero indicates
68 * successful completion.
71 /* Allocate a new ssm */
72 struct fpi_ssm
*fpi_ssm_new(struct fp_dev
*dev
, ssm_handler_fn handler
,
75 struct fpi_ssm
*machine
;
76 BUG_ON(nr_states
< 1);
78 machine
= g_malloc0(sizeof(*machine
));
79 machine
->handler
= handler
;
80 machine
->nr_states
= nr_states
;
82 machine
->completed
= TRUE
;
87 void fpi_ssm_free(struct fpi_ssm
*machine
)
94 /* Invoke the state handler */
95 static void __ssm_call_handler(struct fpi_ssm
*machine
)
97 fp_dbg("%p entering state %d", machine
, machine
->cur_state
);
98 machine
->handler(machine
);
101 /* Start a ssm. You can also restart a completed or aborted ssm. */
102 void fpi_ssm_start(struct fpi_ssm
*ssm
, ssm_completed_fn callback
)
104 BUG_ON(!ssm
->completed
);
105 ssm
->callback
= callback
;
107 ssm
->completed
= FALSE
;
109 __ssm_call_handler(ssm
);
112 static void __subsm_complete(struct fpi_ssm
*ssm
)
114 struct fpi_ssm
*parent
= ssm
->parentsm
;
117 fpi_ssm_mark_aborted(parent
, ssm
->error
);
119 fpi_ssm_next_state(parent
);
123 /* start a SSM as a child of another. if the child completes successfully, the
124 * parent will be advanced to the next state. if the child aborts, the parent
125 * will be aborted with the same error code. the child will be automatically
126 * freed upon completion/abortion. */
127 void fpi_ssm_start_subsm(struct fpi_ssm
*parent
, struct fpi_ssm
*child
)
129 child
->parentsm
= parent
;
130 fpi_ssm_start(child
, __subsm_complete
);
133 /* Mark a ssm as completed successfully. */
134 void fpi_ssm_mark_completed(struct fpi_ssm
*machine
)
136 BUG_ON(machine
->completed
);
137 machine
->completed
= TRUE
;
138 fp_dbg("%p completed with status %d", machine
, machine
->error
);
139 if (machine
->callback
)
140 machine
->callback(machine
);
143 /* Mark a ssm as aborted with error. */
144 void fpi_ssm_mark_aborted(struct fpi_ssm
*machine
, int error
)
146 fp_dbg("error %d from state %d", error
, machine
->cur_state
);
148 machine
->error
= error
;
149 fpi_ssm_mark_completed(machine
);
152 /* Iterate to next state of a ssm */
153 void fpi_ssm_next_state(struct fpi_ssm
*machine
)
155 BUG_ON(machine
->completed
);
156 machine
->cur_state
++;
157 if (machine
->cur_state
== machine
->nr_states
) {
158 fpi_ssm_mark_completed(machine
);
160 __ssm_call_handler(machine
);
164 void fpi_ssm_jump_to_state(struct fpi_ssm
*machine
, int state
)
166 BUG_ON(machine
->completed
);
167 BUG_ON(state
>= machine
->nr_states
);
168 machine
->cur_state
= state
;
169 __ssm_call_handler(machine
);