xref: /minix3/minix/drivers/usb/usbd/hcd/hcd_schedule.c (revision 2d64210c1dbcd340904718f2d4e9e81adeab3c7d)
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