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