1 /*
2 * Implementation of HCD URB scheduler
3 */
4
5 #include <string.h> /* memset */
6
7 #include <usbd/hcd_common.h>
8 #include <usbd/hcd_ddekit.h>
9 #include <usbd/hcd_interface.h>
10 #include <usbd/hcd_schedule.h>
11 #include <usbd/usbd_common.h>
12 #include <usbd/usbd_schedule.h>
13
14
15 /*===========================================================================*
16 * Required for scheduling *
17 *===========================================================================*/
18 /* TODO: Like in DDEKit but power of 2 */
19 #define HCD_MAX_URBS 16
20
21 /* TODO: Structure to hold URBs in DDEKit is limited so this is no better
22 * (but because of that, there is no need for another malloc) */
23 static hcd_urb * stored_urb[HCD_MAX_URBS];
24
25 /* Number of URBs stored during operation */
26 static int num_stored_urbs;
27
28 /* Scheduler thread */
29 static hcd_thread * urb_thread;
30
31 /* This allows waiting for URB */
32 static hcd_lock * urb_lock;
33
34 /* This allows waiting for completion */
35 static hcd_lock * handled_lock;
36
37 /* Makes URB schedule enabled */
38 static int hcd_schedule_urb(hcd_urb *);
39
40 /* Makes URB schedule disabled */
41 static void hcd_unschedule_urb(hcd_urb *);
42
43 /* Scheduler task */
44 static void hcd_urb_scheduler_task(void *);
45
46 /* Completion callback */
47 static void hcd_urb_handled(hcd_urb *);
48
49 /* Stores URB to be handled */
50 static int hcd_store_urb(hcd_urb *);
51
52 /* Removes stored URB */
53 static void hcd_remove_urb(hcd_urb *);
54
55 /* Gets URB to be handled next (based on priority) */
56 static hcd_urb * hcd_get_urb(void);
57
58
59 /*===========================================================================*
60 * usbd_init_scheduler *
61 *===========================================================================*/
62 int
usbd_init_scheduler(void)63 usbd_init_scheduler(void)
64 {
65 DEBUG_DUMP;
66
67 /* Reset everything */
68 num_stored_urbs = 0;
69 memset(stored_urb, 0, sizeof(stored_urb));
70
71 urb_thread = ddekit_thread_create(hcd_urb_scheduler_task, NULL,
72 "scheduler");
73 if (NULL == urb_thread)
74 goto ERR1;
75
76 urb_lock = ddekit_sem_init(0);
77 if (NULL == urb_lock)
78 goto ERR2;
79
80 handled_lock = ddekit_sem_init(0);
81 if (NULL == handled_lock)
82 goto ERR3;
83
84 return EXIT_SUCCESS;
85
86 ERR3:
87 ddekit_sem_deinit(urb_lock);
88 ERR2:
89 ddekit_thread_terminate(urb_thread);
90 ERR1:
91 return EXIT_FAILURE;
92 }
93
94
95 /*===========================================================================*
96 * usbd_deinit_scheduler *
97 *===========================================================================*/
98 void
usbd_deinit_scheduler(void)99 usbd_deinit_scheduler(void)
100 {
101 DEBUG_DUMP;
102
103 ddekit_sem_deinit(handled_lock);
104
105 ddekit_sem_deinit(urb_lock);
106
107 ddekit_thread_terminate(urb_thread);
108 }
109
110
111 /*===========================================================================*
112 * hcd_schedule_external_urb *
113 *===========================================================================*/
114 int
hcd_schedule_external_urb(hcd_urb * urb)115 hcd_schedule_external_urb(hcd_urb * urb)
116 {
117 DEBUG_DUMP;
118
119 return hcd_schedule_urb(urb);
120 }
121
122
123 /*===========================================================================*
124 * hcd_schedule_internal_urb *
125 *===========================================================================*/
126 int
hcd_schedule_internal_urb(hcd_urb * urb)127 hcd_schedule_internal_urb(hcd_urb * urb)
128 {
129 DEBUG_DUMP;
130
131 return hcd_schedule_urb(urb);
132 }
133
134
135 /*===========================================================================*
136 * hcd_schedule_urb *
137 *===========================================================================*/
138 static int
hcd_schedule_urb(hcd_urb * urb)139 hcd_schedule_urb(hcd_urb * urb)
140 {
141 DEBUG_DUMP;
142
143 /* Tell URB what to call on completion */
144 urb->handled = hcd_urb_handled;
145
146 /* Store and check if scheduler should be unlocked */
147 if (EXIT_SUCCESS == hcd_store_urb(urb)) {
148 ddekit_sem_up(urb_lock);
149 return EXIT_SUCCESS;
150 }
151
152 return EXIT_FAILURE;
153 }
154
155
156 /*===========================================================================*
157 * hcd_unschedule_urb *
158 *===========================================================================*/
159 static void
hcd_unschedule_urb(hcd_urb * urb)160 hcd_unschedule_urb(hcd_urb * urb)
161 {
162 DEBUG_DUMP;
163
164 hcd_remove_urb(urb);
165 }
166
167
168 /*===========================================================================*
169 * hcd_urb_scheduler_task *
170 *===========================================================================*/
171 static void
hcd_urb_scheduler_task(void * UNUSED (arg))172 hcd_urb_scheduler_task(void * UNUSED(arg))
173 {
174 hcd_device_state * current_device;
175 hcd_urb * current_urb;
176
177 DEBUG_DUMP;
178
179 for (;;) {
180 /* Wait for scheduler to unlock on any URB submit */
181 ddekit_sem_down(urb_lock);
182
183 /* Get URB */
184 current_urb = hcd_get_urb();
185
186 /* Get URB's target device */
187 current_device = current_urb->target_device;
188
189 /* Check for mismatch */
190 USB_ASSERT(NULL != current_urb, "URB missing after URB unlock");
191
192 /* Check if URB's device is still allocated */
193 if (EXIT_SUCCESS == hcd_check_device(current_device)) {
194 /* Tell device that this is its URB */
195 current_device->urb = current_urb;
196
197 /* Start handling URB event */
198 hcd_handle_event(current_device, HCD_EVENT_URB,
199 HCD_UNUSED_VAL);
200
201 /* Wait for completion */
202 ddekit_sem_down(handled_lock);
203
204 /* TODO: Not enough DDEKit thread priorities
205 * for a better solution */
206 /* Yield, to allow unlocking thread, to continue
207 * before next URB is used */
208 ddekit_yield();
209
210 /* Makes thread debugging easier */
211 USB_DBG("URB handled, scheduler unlocked");
212 } else {
213 USB_MSG("Device 0x%08X for URB 0x%08X, is unavailable",
214 (int)current_device,
215 (int)current_urb);
216 }
217 }
218 }
219
220
221 /*===========================================================================*
222 * hcd_urb_handled *
223 *===========================================================================*/
224 static void
hcd_urb_handled(hcd_urb * urb)225 hcd_urb_handled(hcd_urb * urb)
226 {
227 DEBUG_DUMP;
228
229 /* This URB will be scheduled no more */
230 hcd_unschedule_urb(urb);
231
232 /* Handling completed */
233 ddekit_sem_up(handled_lock);
234 }
235
236
237 /*===========================================================================*
238 * hcd_store_urb *
239 *===========================================================================*/
240 static int
hcd_store_urb(hcd_urb * urb)241 hcd_store_urb(hcd_urb * urb)
242 {
243 int i;
244
245 DEBUG_DUMP;
246
247 for (i = 0; i < HCD_MAX_URBS; i++) {
248 if (NULL == stored_urb[i]) {
249 stored_urb[i] = urb;
250 num_stored_urbs++;
251 return EXIT_SUCCESS;
252 }
253 }
254
255 USB_MSG("No more free URBs");
256
257 return EXIT_FAILURE;
258 }
259
260 /*===========================================================================*
261 * hcd_remove_urb *
262 *===========================================================================*/
263 static void
hcd_remove_urb(hcd_urb * urb)264 hcd_remove_urb(hcd_urb * urb)
265 {
266 int i;
267
268 DEBUG_DUMP;
269
270 for (i = 0; i < HCD_MAX_URBS; i++) {
271 if (urb == stored_urb[i]) {
272 stored_urb[i] = NULL;
273 num_stored_urbs--;
274 return;
275 }
276 }
277
278 USB_ASSERT(0, "URB to be removed, was never stored");
279 }
280
281 /*===========================================================================*
282 * hcd_get_urb *
283 *===========================================================================*/
284 static hcd_urb *
hcd_get_urb(void)285 hcd_get_urb(void)
286 {
287 static int i = 0;
288 int checked;
289
290 DEBUG_DUMP;
291
292 /* TODO: Some priority checking may be here */
293 for (checked = 0; checked < HCD_MAX_URBS; checked++) {
294 /* To avoid starting from 0 every
295 * time (potential starvation) */
296 i = (i + 1) % HCD_MAX_URBS;
297
298 /* When found */
299 if (NULL != stored_urb[i])
300 return stored_urb[i];
301 }
302
303 /* Nothing submitted yet */
304 return NULL;
305 }
306