1/*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include "implementation/global_implementation.h" 29 30struct usb_process usb_process[USB_PROC_MAX]; 31struct mtx sched_lock; 32 33#undef USB_DEBUG_VAR 34#define USB_DEBUG_VAR usb_proc_debug 35#ifdef LOSCFG_USB_DEBUG 36static int usb_proc_debug; 37void 38usb_process_debug_func(int level) 39{ 40 usb_proc_debug = level; 41 PRINTK("The level of usb process debug is %d\n", level); 42} 43DEBUG_MODULE(process, usb_process_debug_func); 44#endif 45 46SPIN_LOCK_INIT(g_usb_process_queue_spinlock); 47 48/*------------------------------------------------------------------------* 49 * usb_process 50 * 51 * This function is the USB process dispatcher. 52 *------------------------------------------------------------------------*/ 53static void* 54usb_process_thread(UINTPTR para) 55{ 56 struct usb_process *up = (struct usb_process*)para; 57 struct usb_proc_msg *pm; 58 struct thread *td; 59 uint32_t int_save; 60 61 /* in case of attach error, check for suspended */ 62 USB_THREAD_SUSPEND_CHECK(); 63 64 /* adjust priority */ 65 td = (struct thread *)(UINTPTR)curthread; 66 thread_lock(td); 67 sched_prio(td, up->up_prio); 68 thread_unlock(td); 69 70 USB_MTX_LOCK(up->up_mtx); 71 72 up->up_curtd = td; 73 while (1) { 74 if (up->up_gone) 75 break; 76 77 /* 78 * NOTE to reimplementors: dequeueing a command from the 79 * "used" queue and executing it must be atomic, with regard 80 * to the "up_mtx" mutex. That means any attempt to queue a 81 * command by another thread must be blocked until either: 82 * 83 * 1) the command sleeps 84 * 85 * 2) the command returns 86 * 87 * Here is a practical example that shows how this helps 88 * solving a problem: 89 * 90 * Assume that you want to set the baud rate on a USB serial 91 * device. During the programming of the device you don't 92 * want to receive nor transmit any data, because it will be 93 * garbage most likely anyway. The programming of our USB 94 * device takes 20 milliseconds and it needs to call 95 * functions that sleep. 96 * 97 * Non-working solution: Before we queue the programming 98 * command, we stop transmission and reception of data. Then 99 * we queue a programming command. At the end of the 100 * programming command we enable transmission and reception 101 * of data. 102 * 103 * Problem: If a second programming command is queued while the 104 * first one is sleeping, we end up enabling transmission 105 * and reception of data too early. 106 * 107 * Working solution: Before we queue the programming command, 108 * we stop transmission and reception of data. Then we queue 109 * a programming command. Then we queue a second command 110 * that only enables transmission and reception of data. 111 * 112 * Why it works: If a second programming command is queued 113 * while the first one is sleeping, then the queueing of a 114 * second command to enable the data transfers, will cause 115 * the previous one, which is still on the queue, to be 116 * removed from the queue, and re-inserted after the last 117 * baud rate programming command, which then gives the 118 * desired result. 119 */ 120 121 LOS_SpinLockSave(&g_usb_process_queue_spinlock, &int_save); 122 pm = TAILQ_FIRST(&up->up_qhead); 123 LOS_SpinUnlockRestore(&g_usb_process_queue_spinlock, int_save); 124 if (pm) { 125 DPRINTF("Message pm=%p, cb=%p (enter)\n", 126 pm, pm->pm_callback); 127 128 if (pm->pm_callback) 129 (pm->pm_callback) (pm); 130 131 LOS_SpinLockSave(&g_usb_process_queue_spinlock, &int_save); 132 if (pm == TAILQ_FIRST(&up->up_qhead)) { 133 /* nothing changed */ 134 TAILQ_REMOVE(&up->up_qhead, pm, pm_qentry); 135 pm->pm_qentry.tqe_prev = NULL; 136 } 137 LOS_SpinUnlockRestore(&g_usb_process_queue_spinlock, int_save); 138 DPRINTF("Message pm=%p (leave)\n", pm); 139 140 continue; 141 } 142 /* end if messages - check if anyone is waiting for sync */ 143 if (up->up_dsleep) { 144 up->up_dsleep = 0; 145 (void)cv_broadcast(&up->up_drain); 146 } 147 up->up_msleep = 1; 148 (void)cv_wait(&up->up_cv, up->up_mtx); 149 } 150 151 up->up_ptr = NULL; 152 (void)cv_signal(&up->up_cv); 153 USB_MTX_UNLOCK(up->up_mtx); 154 USB_THREAD_EXIT(0); 155 return NULL; 156} 157 158uint32_t 159usb_os_task_creat(pthread_t *taskid, TSK_ENTRY_FUNC func, uint32_t prio, const char *nm, UINTPTR para) 160{ 161 uint32_t ret; 162 TSK_INIT_PARAM_S attr; 163 164 (void)memset_s(&attr, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)); 165 166 attr.pfnTaskEntry = func; 167 attr.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; 168 attr.auwArgs[0] = (UINTPTR)para; 169 attr.usTaskPrio = prio; 170 attr.pcName = (char *)nm; 171 attr.uwResved = LOS_TASK_STATUS_DETACHED; 172 173 ret = LOS_TaskCreate((uint32_t *)taskid, &attr); 174 if (ret != LOS_OK) { 175 PRINTK("create %s task error!\n",nm); 176 } 177 return (ret); 178} 179 180uint32_t 181usb_os_task_delete(pthread_t taskid) 182{ 183 uint32_t ret; 184 185 ret = LOS_TaskDelete(taskid); 186 if (ret != LOS_OK) { 187 PRINTK("delete task error!\n"); 188 } 189 return (ret); 190} 191 192/*------------------------------------------------------------------------* 193 * usb_proc_create 194 * 195 * This function will create a process using the given "prio" that can 196 * execute callbacks. The mutex pointed to by "p_mtx" will be applied 197 * before calling the callbacks and released after that the callback 198 * has returned. The structure pointed to by "up" is assumed to be 199 * zeroed before this function is called. 200 * 201 * Return values: 202 * 0: success 203 * Else: failure 204 *------------------------------------------------------------------------*/ 205int 206usb_proc_create(struct usb_process *up, struct mtx *p_mtx, 207 const char *pmesg, uint8_t prio) 208{ 209 uint32_t ret; 210 pthread_t td = 0; 211 up->up_mtx = p_mtx; 212 up->up_prio = prio; 213 214 TAILQ_INIT(&up->up_qhead); 215 216 cv_init(&up->up_cv, "-"); 217 cv_init(&up->up_drain, "usbdrain"); 218 219 ret = usb_os_task_creat(&td, (TSK_ENTRY_FUNC)usb_process_thread, prio, pmesg, (UINTPTR)up); 220 if (ret != LOS_OK) { 221 DPRINTFN(0, "Unable to create USB process."); 222 up->up_ptr = NULL; 223 goto error; 224 } 225 up->up_ptr = (struct thread *)(UINTPTR)td; 226 return (0); 227 228error: 229 usb_proc_free(up); 230 return (ENOMEM); 231} 232 233/*------------------------------------------------------------------------* 234 * usb_proc_free 235 * 236 * NOTE: If the structure pointed to by "up" is all zero, this 237 * function does nothing. 238 * 239 * NOTE: Messages that are pending on the process queue will not be 240 * removed nor called. 241 *------------------------------------------------------------------------*/ 242void 243usb_proc_free(struct usb_process *up) 244{ 245 /* check if not initialised */ 246 if (up->up_mtx == NULL) 247 return; 248 249 usb_proc_drain(up); 250 251 cv_destroy(&up->up_cv); 252 cv_destroy(&up->up_drain); 253 254 /* make sure that we do not enter here again */ 255 up->up_mtx = NULL; 256} 257 258/*------------------------------------------------------------------------* 259 * usb_proc_msignal 260 * 261 * This function will queue one of the passed USB process messages on 262 * the USB process queue. The first message that is not already queued 263 * will get queued. If both messages are already queued the one queued 264 * last will be removed from the queue and queued in the end. The USB 265 * process mutex must be locked when calling this function. This 266 * function exploits the fact that a process can only do one callback 267 * at a time. The message that was queued is returned. 268 *------------------------------------------------------------------------*/ 269void * 270usb_proc_msignal(struct usb_process *up, void *_pm0, void *_pm1) 271{ 272 struct usb_proc_msg *pm0 = _pm0; 273 struct usb_proc_msg *pm1 = _pm1; 274 struct usb_proc_msg *pm2; 275 usb_size_t d; 276 uint8_t t; 277 uint32_t int_save; 278 279 /* check if gone, return dummy value */ 280 if (up->up_gone) 281 return (_pm0); 282 283 t = 0; 284 285 LOS_SpinLockSave(&g_usb_process_queue_spinlock, &int_save); 286 if (pm0->pm_qentry.tqe_prev) { 287 t |= 1; 288 } 289 if (pm1->pm_qentry.tqe_prev) { 290 t |= 2; 291 } 292 if (t == 0) { 293 /* 294 * No entries are queued. Queue "pm0" and use the existing 295 * message number. 296 */ 297 pm2 = pm0; 298 } else if (t == 1) { 299 /* Check if we need to increment the message number. */ 300 if (pm0->pm_num == up->up_msg_num) { 301 up->up_msg_num++; 302 } 303 pm2 = pm1; 304 } else if (t == 2) { 305 /* Check if we need to increment the message number. */ 306 if (pm1->pm_num == up->up_msg_num) { 307 up->up_msg_num++; 308 } 309 pm2 = pm0; 310 } else if (t == 3) { 311 /* 312 * Both entries are queued. Re-queue the entry closest to 313 * the end. 314 */ 315 d = (pm1->pm_num - pm0->pm_num); 316 317 /* Check sign after subtraction */ 318 if (d & 0x80000000) { 319 pm2 = pm0; 320 } else { 321 pm2 = pm1; 322 } 323 324 TAILQ_REMOVE(&up->up_qhead, pm2, pm_qentry); 325 pm2->pm_qentry.tqe_prev = NULL; 326 } else { 327 pm2 = NULL; /* panic - should not happen */ 328 } 329 330 DPRINTF(" t=%u, num=%u\n", t, up->up_msg_num); 331 332 /* Put message last on queue */ 333 334 pm2->pm_num = up->up_msg_num; 335 TAILQ_INSERT_TAIL(&up->up_qhead, pm2, pm_qentry); 336 LOS_SpinUnlockRestore(&g_usb_process_queue_spinlock, int_save); 337 338 /* Check if we need to wakeup the USB process. */ 339 340 up->up_msleep = 0; /* save "cv_signal()" calls */ 341 (void)cv_signal(&up->up_cv); 342 343 return (pm2); 344} 345 346/*------------------------------------------------------------------------* 347 * usb_proc_is_gone 348 * 349 * Return values: 350 * 0: USB process is running 351 * Else: USB process is tearing down 352 *------------------------------------------------------------------------*/ 353uint8_t 354usb_proc_is_gone(struct usb_process *up) 355{ 356 if (up->up_gone) { 357 return (1); 358 } 359 360 /* 361 * Allow calls when up_mtx is NULL, before the USB process 362 * structure is initialised. 363 */ 364 if (up->up_mtx != NULL) { 365 mtx_assert(up->up_mtx, MA_OWNED); 366 } 367 return (0); 368} 369 370/*------------------------------------------------------------------------* 371 * usb_proc_mwait 372 * 373 * This function will return when the USB process message pointed to 374 * by "pm" is no longer on a queue. This function must be called 375 * having "up->up_mtx" locked. 376 *------------------------------------------------------------------------*/ 377void 378usb_proc_mwait(struct usb_process *up, void *_pm0, void *_pm1) 379{ 380 struct usb_proc_msg *pm0 = _pm0; 381 struct usb_proc_msg *pm1 = _pm1; 382 uint32_t int_save; 383 384 /* check if gone */ 385 if (up->up_gone) 386 return; 387 388 mtx_assert(up->up_mtx, MA_OWNED); 389 390 if (up->up_curtd == (struct thread *)(UINTPTR)curthread) { 391 LOS_SpinLockSave(&g_usb_process_queue_spinlock, &int_save); 392 /* Just remove the messages from the queue. */ 393 if (pm0->pm_qentry.tqe_prev) { 394 TAILQ_REMOVE(&up->up_qhead, pm0, pm_qentry); 395 pm0->pm_qentry.tqe_prev = NULL; 396 } 397 if (pm1->pm_qentry.tqe_prev) { 398 TAILQ_REMOVE(&up->up_qhead, pm1, pm_qentry); 399 pm1->pm_qentry.tqe_prev = NULL; 400 } 401 LOS_SpinUnlockRestore(&g_usb_process_queue_spinlock, int_save); 402 } else 403 while (pm0->pm_qentry.tqe_prev || 404 pm1->pm_qentry.tqe_prev) { 405 /* check if config thread is gone */ 406 if (up->up_gone) 407 break; 408 up->up_dsleep = 1; 409 (void)cv_wait(&up->up_drain, up->up_mtx); 410 } 411} 412 413/*------------------------------------------------------------------------* 414 * usb_proc_drain 415 * 416 * This function will tear down an USB process, waiting for the 417 * currently executing command to return. 418 * 419 * NOTE: If the structure pointed to by "up" is all zero, 420 * this function does nothing. 421 *------------------------------------------------------------------------*/ 422void 423usb_proc_drain(struct usb_process *up) 424{ 425 /* check if not initialised */ 426 if (up->up_mtx == NULL) { 427 return; 428 } 429 430 /* handle special case with Giant */ 431 if (up->up_mtx != &Giant) { 432 mtx_assert(up->up_mtx, MA_NOTOWNED); 433 } 434 435 USB_MTX_LOCK(up->up_mtx); 436 437 /* Set the gone flag */ 438 439 up->up_gone = 1; 440 441 while (up->up_ptr) { 442 443 /* Check if we need to wakeup the USB process */ 444 445 if (up->up_msleep || up->up_csleep) { 446 up->up_msleep = 0; 447 up->up_csleep = 0; 448 (void)cv_signal(&up->up_cv); 449 } 450 (void)cv_wait(&up->up_cv, up->up_mtx); 451 } 452 /* Check if someone is waiting - should not happen */ 453 454 if (up->up_dsleep) { 455 up->up_dsleep = 0; 456 (void)cv_broadcast(&up->up_drain); 457 DPRINTF("WARNING: Someone is waiting " 458 "for USB process drain!\n"); 459 } 460 USB_MTX_UNLOCK(up->up_mtx); 461} 462 463/*------------------------------------------------------------------------* 464 * usb_proc_rewakeup 465 * 466 * This function is called to re-wakeup the given USB 467 * process. This usually happens after that the USB system has been in 468 * polling mode, like during a panic. This function must be called 469 * having "up->up_mtx" locked. 470 *------------------------------------------------------------------------*/ 471void 472usb_proc_rewakeup(struct usb_process *up) 473{ 474 /* check if not initialised */ 475 if (up->up_mtx == NULL) 476 return; 477 /* check if gone */ 478 if (up->up_gone) 479 return; 480 481 mtx_assert(up->up_mtx, MA_OWNED); 482 483 if (up->up_msleep == 0) { 484 /* re-wakeup */ 485 (void)cv_signal(&up->up_cv); 486 } 487} 488 489/*------------------------------------------------------------------------* 490 * usb_proc_is_called_from 491 * 492 * This function will return non-zero if called from inside the USB 493 * process passed as first argument. Else this function returns zero. 494 *------------------------------------------------------------------------*/ 495int 496usb_proc_is_called_from(struct usb_process *up) 497{ 498 return (up->up_curtd == (struct thread *)(UINTPTR)curthread); 499} 500 501#undef USB_DEBUG_VAR 502