1 /*
2  * Functions to assist with asynchronous driver <---> library communications
3  * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
4  *
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.
9  *
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.
14  *
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
18  */
19 
20 #define FP_COMPONENT "drv"
21 
22 #include <config.h>
23 #include <errno.h>
24 
25 #include "fp_internal.h"
26 
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.
32  *
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).
37  *
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.
42  *
43  * To create a ssm, you pass a state handler function and the total number of
44  * states (4 in the above example).
45  *
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).
48  *
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.
52  *
53  * To mark successful completion of a SSM, either iterate beyond the final
54  * state or call fpi_ssm_mark_completed() from any state.
55  *
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.
58  *
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
61  * is appropriate).
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).
65  *
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.
69  */
70 
71 /* Allocate a new ssm */
fpi_ssm_new(struct fp_dev * dev,ssm_handler_fn handler,int nr_states)72 struct fpi_ssm *fpi_ssm_new(struct fp_dev *dev, ssm_handler_fn handler,
73 	int nr_states)
74 {
75 	struct fpi_ssm *machine;
76 	BUG_ON(nr_states < 1);
77 
78 	machine = g_malloc0(sizeof(*machine));
79 	machine->handler = handler;
80 	machine->nr_states = nr_states;
81 	machine->dev = dev;
82 	machine->completed = TRUE;
83 	return machine;
84 }
85 
86 /* Free a ssm */
fpi_ssm_free(struct fpi_ssm * machine)87 void fpi_ssm_free(struct fpi_ssm *machine)
88 {
89 	if (!machine)
90 		return;
91 	g_free(machine);
92 }
93 
94 /* Invoke the state handler */
__ssm_call_handler(struct fpi_ssm * machine)95 static void __ssm_call_handler(struct fpi_ssm *machine)
96 {
97 	fp_dbg("%p entering state %d", machine, machine->cur_state);
98 	machine->handler(machine);
99 }
100 
101 /* Start a ssm. You can also restart a completed or aborted ssm. */
fpi_ssm_start(struct fpi_ssm * ssm,ssm_completed_fn callback)102 void fpi_ssm_start(struct fpi_ssm *ssm, ssm_completed_fn callback)
103 {
104 	BUG_ON(!ssm->completed);
105 	ssm->callback = callback;
106 	ssm->cur_state = 0;
107 	ssm->completed = FALSE;
108 	ssm->error = 0;
109 	__ssm_call_handler(ssm);
110 }
111 
__subsm_complete(struct fpi_ssm * ssm)112 static void __subsm_complete(struct fpi_ssm *ssm)
113 {
114 	struct fpi_ssm *parent = ssm->parentsm;
115 	BUG_ON(!parent);
116 	if (ssm->error)
117 		fpi_ssm_mark_aborted(parent, ssm->error);
118 	else
119 		fpi_ssm_next_state(parent);
120 	fpi_ssm_free(ssm);
121 }
122 
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. */
fpi_ssm_start_subsm(struct fpi_ssm * parent,struct fpi_ssm * child)127 void fpi_ssm_start_subsm(struct fpi_ssm *parent, struct fpi_ssm *child)
128 {
129 	child->parentsm = parent;
130 	fpi_ssm_start(child, __subsm_complete);
131 }
132 
133 /* Mark a ssm as completed successfully. */
fpi_ssm_mark_completed(struct fpi_ssm * machine)134 void fpi_ssm_mark_completed(struct fpi_ssm *machine)
135 {
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);
141 }
142 
143 /* Mark a ssm as aborted with error. */
fpi_ssm_mark_aborted(struct fpi_ssm * machine,int error)144 void fpi_ssm_mark_aborted(struct fpi_ssm *machine, int error)
145 {
146 	fp_dbg("error %d from state %d", error, machine->cur_state);
147 	BUG_ON(error == 0);
148 	machine->error = error;
149 	fpi_ssm_mark_completed(machine);
150 }
151 
152 /* Iterate to next state of a ssm */
fpi_ssm_next_state(struct fpi_ssm * machine)153 void fpi_ssm_next_state(struct fpi_ssm *machine)
154 {
155 	BUG_ON(machine->completed);
156 	machine->cur_state++;
157 	if (machine->cur_state == machine->nr_states) {
158 		fpi_ssm_mark_completed(machine);
159 	} else {
160 		__ssm_call_handler(machine);
161 	}
162 }
163 
fpi_ssm_jump_to_state(struct fpi_ssm * machine,int state)164 void fpi_ssm_jump_to_state(struct fpi_ssm *machine, int state)
165 {
166 	BUG_ON(machine->completed);
167 	BUG_ON(state >= machine->nr_states);
168 	machine->cur_state = state;
169 	__ssm_call_handler(machine);
170 }
171 
172