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 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 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 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 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 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 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 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 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 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 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 * 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