2 * Implementation of HCD URB scheduler
5 #include <string.h> /* memset */
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>
15 /*===========================================================================*
16 * Required for scheduling *
17 *===========================================================================*/
18 /* TODO: Like in DDEKit but power of 2 */
19 #define HCD_MAX_URBS 16
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
];
25 /* Number of URBs stored during operation */
26 static int num_stored_urbs
;
28 /* Scheduler thread */
29 static hcd_thread
* urb_thread
;
31 /* This allows waiting for URB */
32 static hcd_lock
* urb_lock
;
34 /* This allows waiting for completion */
35 static hcd_lock
* handled_lock
;
37 /* Makes URB schedule enabled */
38 static int hcd_schedule_urb(hcd_urb
*);
40 /* Makes URB schedule disabled */
41 static void hcd_unschedule_urb(hcd_urb
*);
44 static void hcd_urb_scheduler_task(void *);
46 /* Completion callback */
47 static void hcd_urb_handled(hcd_urb
*);
49 /* Stores URB to be handled */
50 static int hcd_store_urb(hcd_urb
*);
52 /* Removes stored URB */
53 static void hcd_remove_urb(hcd_urb
*);
55 /* Gets URB to be handled next (based on priority) */
56 static hcd_urb
* hcd_get_urb(void);
59 /*===========================================================================*
60 * usbd_init_scheduler *
61 *===========================================================================*/
63 usbd_init_scheduler(void)
67 /* Reset everything */
69 memset(stored_urb
, 0, sizeof(stored_urb
));
71 urb_thread
= ddekit_thread_create(hcd_urb_scheduler_task
, NULL
,
73 if (NULL
== urb_thread
)
76 urb_lock
= ddekit_sem_init(0);
80 handled_lock
= ddekit_sem_init(0);
81 if (NULL
== handled_lock
)
87 ddekit_sem_deinit(urb_lock
);
89 ddekit_thread_terminate(urb_thread
);
95 /*===========================================================================*
96 * usbd_deinit_scheduler *
97 *===========================================================================*/
99 usbd_deinit_scheduler(void)
103 ddekit_sem_deinit(handled_lock
);
105 ddekit_sem_deinit(urb_lock
);
107 ddekit_thread_terminate(urb_thread
);
111 /*===========================================================================*
112 * hcd_schedule_external_urb *
113 *===========================================================================*/
115 hcd_schedule_external_urb(hcd_urb
* urb
)
119 return hcd_schedule_urb(urb
);
123 /*===========================================================================*
124 * hcd_schedule_internal_urb *
125 *===========================================================================*/
127 hcd_schedule_internal_urb(hcd_urb
* urb
)
131 return hcd_schedule_urb(urb
);
135 /*===========================================================================*
137 *===========================================================================*/
139 hcd_schedule_urb(hcd_urb
* urb
)
143 /* Tell URB what to call on completion */
144 urb
->handled
= hcd_urb_handled
;
146 /* Store and check if scheduler should be unlocked */
147 if (EXIT_SUCCESS
== hcd_store_urb(urb
)) {
148 ddekit_sem_up(urb_lock
);
156 /*===========================================================================*
157 * hcd_unschedule_urb *
158 *===========================================================================*/
160 hcd_unschedule_urb(hcd_urb
* urb
)
168 /*===========================================================================*
169 * hcd_urb_scheduler_task *
170 *===========================================================================*/
172 hcd_urb_scheduler_task(void * UNUSED(arg
))
174 hcd_device_state
* current_device
;
175 hcd_urb
* current_urb
;
180 /* Wait for scheduler to unlock on any URB submit */
181 ddekit_sem_down(urb_lock
);
184 current_urb
= hcd_get_urb();
186 /* Get URB's target device */
187 current_device
= current_urb
->target_device
;
189 /* Check for mismatch */
190 USB_ASSERT(NULL
!= current_urb
, "URB missing after URB unlock");
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
;
197 /* Start handling URB event */
198 hcd_handle_event(current_device
, HCD_EVENT_URB
,
201 /* Wait for completion */
202 ddekit_sem_down(handled_lock
);
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 */
210 /* Makes thread debugging easier */
211 USB_DBG("URB handled, scheduler unlocked");
213 USB_MSG("Device 0x%08X for URB 0x%08X, is unavailable",
221 /*===========================================================================*
223 *===========================================================================*/
225 hcd_urb_handled(hcd_urb
* urb
)
229 /* This URB will be scheduled no more */
230 hcd_unschedule_urb(urb
);
232 /* Handling completed */
233 ddekit_sem_up(handled_lock
);
237 /*===========================================================================*
239 *===========================================================================*/
241 hcd_store_urb(hcd_urb
* urb
)
247 for (i
= 0; i
< HCD_MAX_URBS
; i
++) {
248 if (NULL
== stored_urb
[i
]) {
255 USB_MSG("No more free URBs");
260 /*===========================================================================*
262 *===========================================================================*/
264 hcd_remove_urb(hcd_urb
* urb
)
270 for (i
= 0; i
< HCD_MAX_URBS
; i
++) {
271 if (urb
== stored_urb
[i
]) {
272 stored_urb
[i
] = NULL
;
278 USB_ASSERT(0, "URB to be removed, was never stored");
281 /*===========================================================================*
283 *===========================================================================*/
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
;
299 if (NULL
!= stored_urb
[i
])
300 return stored_urb
[i
];
303 /* Nothing submitted yet */