102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 302ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 402ac6454SAndrew Thompson * 502ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 602ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 702ac6454SAndrew Thompson * are met: 802ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1002ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1202ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1302ac6454SAndrew Thompson * 1402ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1502ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1602ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1702ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1802ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1902ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2002ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2102ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2202ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2302ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2402ac6454SAndrew Thompson * SUCH DAMAGE. 2502ac6454SAndrew Thompson */ 2602ac6454SAndrew Thompson 27a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_proc_debug 2802ac6454SAndrew Thompson 2902ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 3002ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 3102ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 3202ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 3302ac6454SAndrew Thompson 3402ac6454SAndrew Thompson #include <sys/proc.h> 3502ac6454SAndrew Thompson #include <sys/kthread.h> 3602ac6454SAndrew Thompson #include <sys/sched.h> 3702ac6454SAndrew Thompson 3802ac6454SAndrew Thompson #if (__FreeBSD_version < 700000) 3902ac6454SAndrew Thompson #define thread_lock(td) mtx_lock_spin(&sched_lock) 4002ac6454SAndrew Thompson #define thread_unlock(td) mtx_unlock_spin(&sched_lock) 4102ac6454SAndrew Thompson #endif 4202ac6454SAndrew Thompson 4302ac6454SAndrew Thompson #if (__FreeBSD_version >= 800000) 4402ac6454SAndrew Thompson #define USB_THREAD_CREATE(f, s, p, ...) \ 4502ac6454SAndrew Thompson kproc_create((f), (s), (p), RFHIGHPID, 0, __VA_ARGS__) 4602ac6454SAndrew Thompson #define USB_THREAD_SUSPEND(p) kproc_suspend(p,0) 4702ac6454SAndrew Thompson #define USB_THREAD_EXIT(err) kproc_exit(err) 4802ac6454SAndrew Thompson #else 4902ac6454SAndrew Thompson #define USB_THREAD_CREATE(f, s, p, ...) \ 5002ac6454SAndrew Thompson kthread_create((f), (s), (p), RFHIGHPID, 0, __VA_ARGS__) 5102ac6454SAndrew Thompson #define USB_THREAD_SUSPEND(p) kthread_suspend(p,0) 5202ac6454SAndrew Thompson #define USB_THREAD_EXIT(err) kthread_exit(err) 5302ac6454SAndrew Thompson #endif 5402ac6454SAndrew Thompson 5502ac6454SAndrew Thompson #if USB_DEBUG 56a593f6b8SAndrew Thompson static int usb_proc_debug; 5702ac6454SAndrew Thompson 589360ae40SAndrew Thompson SYSCTL_NODE(_hw_usb, OID_AUTO, proc, CTLFLAG_RW, 0, "USB process"); 59a593f6b8SAndrew Thompson SYSCTL_INT(_hw_usb_proc, OID_AUTO, debug, CTLFLAG_RW, &usb_proc_debug, 0, 6002ac6454SAndrew Thompson "Debug level"); 6102ac6454SAndrew Thompson #endif 6202ac6454SAndrew Thompson 6302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 64760bc48eSAndrew Thompson * usb_process 6502ac6454SAndrew Thompson * 6602ac6454SAndrew Thompson * This function is the USB process dispatcher. 6702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 6802ac6454SAndrew Thompson static void 69760bc48eSAndrew Thompson usb_process(void *arg) 7002ac6454SAndrew Thompson { 71760bc48eSAndrew Thompson struct usb_process *up = arg; 72760bc48eSAndrew Thompson struct usb_proc_msg *pm; 7302ac6454SAndrew Thompson struct thread *td; 7402ac6454SAndrew Thompson 7502ac6454SAndrew Thompson /* adjust priority */ 7602ac6454SAndrew Thompson td = curthread; 7702ac6454SAndrew Thompson thread_lock(td); 7802ac6454SAndrew Thompson sched_prio(td, up->up_prio); 7902ac6454SAndrew Thompson thread_unlock(td); 8002ac6454SAndrew Thompson 8102ac6454SAndrew Thompson mtx_lock(up->up_mtx); 8202ac6454SAndrew Thompson 8302ac6454SAndrew Thompson up->up_curtd = td; 8402ac6454SAndrew Thompson 8502ac6454SAndrew Thompson while (1) { 8602ac6454SAndrew Thompson 8702ac6454SAndrew Thompson if (up->up_gone) 8802ac6454SAndrew Thompson break; 8902ac6454SAndrew Thompson 9002ac6454SAndrew Thompson /* 9102ac6454SAndrew Thompson * NOTE to reimplementors: dequeueing a command from the 9202ac6454SAndrew Thompson * "used" queue and executing it must be atomic, with regard 9302ac6454SAndrew Thompson * to the "up_mtx" mutex. That means any attempt to queue a 9402ac6454SAndrew Thompson * command by another thread must be blocked until either: 9502ac6454SAndrew Thompson * 9602ac6454SAndrew Thompson * 1) the command sleeps 9702ac6454SAndrew Thompson * 9802ac6454SAndrew Thompson * 2) the command returns 9902ac6454SAndrew Thompson * 10002ac6454SAndrew Thompson * Here is a practical example that shows how this helps 10102ac6454SAndrew Thompson * solving a problem: 10202ac6454SAndrew Thompson * 10302ac6454SAndrew Thompson * Assume that you want to set the baud rate on a USB serial 10402ac6454SAndrew Thompson * device. During the programming of the device you don't 10502ac6454SAndrew Thompson * want to receive nor transmit any data, because it will be 10602ac6454SAndrew Thompson * garbage most likely anyway. The programming of our USB 10702ac6454SAndrew Thompson * device takes 20 milliseconds and it needs to call 10802ac6454SAndrew Thompson * functions that sleep. 10902ac6454SAndrew Thompson * 11002ac6454SAndrew Thompson * Non-working solution: Before we queue the programming 11102ac6454SAndrew Thompson * command, we stop transmission and reception of data. Then 11202ac6454SAndrew Thompson * we queue a programming command. At the end of the 11302ac6454SAndrew Thompson * programming command we enable transmission and reception 11402ac6454SAndrew Thompson * of data. 11502ac6454SAndrew Thompson * 11602ac6454SAndrew Thompson * Problem: If a second programming command is queued while the 11702ac6454SAndrew Thompson * first one is sleeping, we end up enabling transmission 11802ac6454SAndrew Thompson * and reception of data too early. 11902ac6454SAndrew Thompson * 12002ac6454SAndrew Thompson * Working solution: Before we queue the programming command, 12102ac6454SAndrew Thompson * we stop transmission and reception of data. Then we queue 12202ac6454SAndrew Thompson * a programming command. Then we queue a second command 12302ac6454SAndrew Thompson * that only enables transmission and reception of data. 12402ac6454SAndrew Thompson * 12502ac6454SAndrew Thompson * Why it works: If a second programming command is queued 12602ac6454SAndrew Thompson * while the first one is sleeping, then the queueing of a 12702ac6454SAndrew Thompson * second command to enable the data transfers, will cause 12802ac6454SAndrew Thompson * the previous one, which is still on the queue, to be 12902ac6454SAndrew Thompson * removed from the queue, and re-inserted after the last 13002ac6454SAndrew Thompson * baud rate programming command, which then gives the 13102ac6454SAndrew Thompson * desired result. 13202ac6454SAndrew Thompson */ 13302ac6454SAndrew Thompson pm = TAILQ_FIRST(&up->up_qhead); 13402ac6454SAndrew Thompson 13502ac6454SAndrew Thompson if (pm) { 13602ac6454SAndrew Thompson DPRINTF("Message pm=%p, cb=%p (enter)\n", 13702ac6454SAndrew Thompson pm, pm->pm_callback); 13802ac6454SAndrew Thompson 13902ac6454SAndrew Thompson (pm->pm_callback) (pm); 14002ac6454SAndrew Thompson 14102ac6454SAndrew Thompson if (pm == TAILQ_FIRST(&up->up_qhead)) { 14202ac6454SAndrew Thompson /* nothing changed */ 14302ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm, pm_qentry); 14402ac6454SAndrew Thompson pm->pm_qentry.tqe_prev = NULL; 14502ac6454SAndrew Thompson } 14602ac6454SAndrew Thompson DPRINTF("Message pm=%p (leave)\n", pm); 14702ac6454SAndrew Thompson 14802ac6454SAndrew Thompson continue; 14902ac6454SAndrew Thompson } 15002ac6454SAndrew Thompson /* end if messages - check if anyone is waiting for sync */ 15102ac6454SAndrew Thompson if (up->up_dsleep) { 15202ac6454SAndrew Thompson up->up_dsleep = 0; 1538437751dSAndrew Thompson cv_broadcast(&up->up_drain); 15402ac6454SAndrew Thompson } 15502ac6454SAndrew Thompson up->up_msleep = 1; 1568437751dSAndrew Thompson cv_wait(&up->up_cv, up->up_mtx); 15702ac6454SAndrew Thompson } 15802ac6454SAndrew Thompson 15902ac6454SAndrew Thompson up->up_ptr = NULL; 1608437751dSAndrew Thompson cv_signal(&up->up_cv); 16102ac6454SAndrew Thompson mtx_unlock(up->up_mtx); 16202ac6454SAndrew Thompson 16302ac6454SAndrew Thompson USB_THREAD_EXIT(0); 16402ac6454SAndrew Thompson } 16502ac6454SAndrew Thompson 16602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 167a593f6b8SAndrew Thompson * usb_proc_create 16802ac6454SAndrew Thompson * 16902ac6454SAndrew Thompson * This function will create a process using the given "prio" that can 17002ac6454SAndrew Thompson * execute callbacks. The mutex pointed to by "p_mtx" will be applied 17102ac6454SAndrew Thompson * before calling the callbacks and released after that the callback 17202ac6454SAndrew Thompson * has returned. The structure pointed to by "up" is assumed to be 17302ac6454SAndrew Thompson * zeroed before this function is called. 17402ac6454SAndrew Thompson * 17502ac6454SAndrew Thompson * Return values: 17602ac6454SAndrew Thompson * 0: success 17702ac6454SAndrew Thompson * Else: failure 17802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 17902ac6454SAndrew Thompson int 180a593f6b8SAndrew Thompson usb_proc_create(struct usb_process *up, struct mtx *p_mtx, 18102ac6454SAndrew Thompson const char *pmesg, uint8_t prio) 18202ac6454SAndrew Thompson { 18302ac6454SAndrew Thompson up->up_mtx = p_mtx; 18402ac6454SAndrew Thompson up->up_prio = prio; 18502ac6454SAndrew Thompson 18602ac6454SAndrew Thompson TAILQ_INIT(&up->up_qhead); 18702ac6454SAndrew Thompson 1888437751dSAndrew Thompson cv_init(&up->up_cv, "wmsg"); 1898437751dSAndrew Thompson cv_init(&up->up_drain, "dmsg"); 19002ac6454SAndrew Thompson 191760bc48eSAndrew Thompson if (USB_THREAD_CREATE(&usb_process, up, 19202ac6454SAndrew Thompson &up->up_ptr, pmesg)) { 19302ac6454SAndrew Thompson DPRINTFN(0, "Unable to create USB process."); 19402ac6454SAndrew Thompson up->up_ptr = NULL; 19502ac6454SAndrew Thompson goto error; 19602ac6454SAndrew Thompson } 19702ac6454SAndrew Thompson return (0); 19802ac6454SAndrew Thompson 19902ac6454SAndrew Thompson error: 200a593f6b8SAndrew Thompson usb_proc_free(up); 20102ac6454SAndrew Thompson return (ENOMEM); 20202ac6454SAndrew Thompson } 20302ac6454SAndrew Thompson 20402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 205a593f6b8SAndrew Thompson * usb_proc_free 20602ac6454SAndrew Thompson * 20702ac6454SAndrew Thompson * NOTE: If the structure pointed to by "up" is all zero, this 20802ac6454SAndrew Thompson * function does nothing. 20902ac6454SAndrew Thompson * 21002ac6454SAndrew Thompson * NOTE: Messages that are pending on the process queue will not be 21102ac6454SAndrew Thompson * removed nor called. 21202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 21302ac6454SAndrew Thompson void 214a593f6b8SAndrew Thompson usb_proc_free(struct usb_process *up) 21502ac6454SAndrew Thompson { 21602ac6454SAndrew Thompson /* check if not initialised */ 21702ac6454SAndrew Thompson if (up->up_mtx == NULL) 21802ac6454SAndrew Thompson return; 21902ac6454SAndrew Thompson 220a593f6b8SAndrew Thompson usb_proc_drain(up); 22102ac6454SAndrew Thompson 2228437751dSAndrew Thompson cv_destroy(&up->up_cv); 2238437751dSAndrew Thompson cv_destroy(&up->up_drain); 22402ac6454SAndrew Thompson 22502ac6454SAndrew Thompson /* make sure that we do not enter here again */ 22602ac6454SAndrew Thompson up->up_mtx = NULL; 22702ac6454SAndrew Thompson } 22802ac6454SAndrew Thompson 22902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 230a593f6b8SAndrew Thompson * usb_proc_msignal 23102ac6454SAndrew Thompson * 23202ac6454SAndrew Thompson * This function will queue one of the passed USB process messages on 23302ac6454SAndrew Thompson * the USB process queue. The first message that is not already queued 23402ac6454SAndrew Thompson * will get queued. If both messages are already queued the one queued 23502ac6454SAndrew Thompson * last will be removed from the queue and queued in the end. The USB 23602ac6454SAndrew Thompson * process mutex must be locked when calling this function. This 23702ac6454SAndrew Thompson * function exploits the fact that a process can only do one callback 23802ac6454SAndrew Thompson * at a time. The message that was queued is returned. 23902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 24002ac6454SAndrew Thompson void * 241a593f6b8SAndrew Thompson usb_proc_msignal(struct usb_process *up, void *_pm0, void *_pm1) 24202ac6454SAndrew Thompson { 243760bc48eSAndrew Thompson struct usb_proc_msg *pm0 = _pm0; 244760bc48eSAndrew Thompson struct usb_proc_msg *pm1 = _pm1; 245760bc48eSAndrew Thompson struct usb_proc_msg *pm2; 246f9cb546cSAndrew Thompson usb_size_t d; 24702ac6454SAndrew Thompson uint8_t t; 24802ac6454SAndrew Thompson 24902ac6454SAndrew Thompson /* check if gone, return dummy value */ 25002ac6454SAndrew Thompson if (up->up_gone) 25102ac6454SAndrew Thompson return (_pm0); 25202ac6454SAndrew Thompson 25302ac6454SAndrew Thompson mtx_assert(up->up_mtx, MA_OWNED); 25402ac6454SAndrew Thompson 25502ac6454SAndrew Thompson t = 0; 25602ac6454SAndrew Thompson 25702ac6454SAndrew Thompson if (pm0->pm_qentry.tqe_prev) { 25802ac6454SAndrew Thompson t |= 1; 25902ac6454SAndrew Thompson } 26002ac6454SAndrew Thompson if (pm1->pm_qentry.tqe_prev) { 26102ac6454SAndrew Thompson t |= 2; 26202ac6454SAndrew Thompson } 26302ac6454SAndrew Thompson if (t == 0) { 26402ac6454SAndrew Thompson /* 26502ac6454SAndrew Thompson * No entries are queued. Queue "pm0" and use the existing 26602ac6454SAndrew Thompson * message number. 26702ac6454SAndrew Thompson */ 26802ac6454SAndrew Thompson pm2 = pm0; 26902ac6454SAndrew Thompson } else if (t == 1) { 27002ac6454SAndrew Thompson /* Check if we need to increment the message number. */ 27102ac6454SAndrew Thompson if (pm0->pm_num == up->up_msg_num) { 27202ac6454SAndrew Thompson up->up_msg_num++; 27302ac6454SAndrew Thompson } 27402ac6454SAndrew Thompson pm2 = pm1; 27502ac6454SAndrew Thompson } else if (t == 2) { 27602ac6454SAndrew Thompson /* Check if we need to increment the message number. */ 27702ac6454SAndrew Thompson if (pm1->pm_num == up->up_msg_num) { 27802ac6454SAndrew Thompson up->up_msg_num++; 27902ac6454SAndrew Thompson } 28002ac6454SAndrew Thompson pm2 = pm0; 28102ac6454SAndrew Thompson } else if (t == 3) { 28202ac6454SAndrew Thompson /* 28302ac6454SAndrew Thompson * Both entries are queued. Re-queue the entry closest to 28402ac6454SAndrew Thompson * the end. 28502ac6454SAndrew Thompson */ 28602ac6454SAndrew Thompson d = (pm1->pm_num - pm0->pm_num); 28702ac6454SAndrew Thompson 28802ac6454SAndrew Thompson /* Check sign after subtraction */ 28902ac6454SAndrew Thompson if (d & 0x80000000) { 29002ac6454SAndrew Thompson pm2 = pm0; 29102ac6454SAndrew Thompson } else { 29202ac6454SAndrew Thompson pm2 = pm1; 29302ac6454SAndrew Thompson } 29402ac6454SAndrew Thompson 29502ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm2, pm_qentry); 29602ac6454SAndrew Thompson } else { 29702ac6454SAndrew Thompson pm2 = NULL; /* panic - should not happen */ 29802ac6454SAndrew Thompson } 29902ac6454SAndrew Thompson 30002ac6454SAndrew Thompson DPRINTF(" t=%u, num=%u\n", t, up->up_msg_num); 30102ac6454SAndrew Thompson 30202ac6454SAndrew Thompson /* Put message last on queue */ 30302ac6454SAndrew Thompson 30402ac6454SAndrew Thompson pm2->pm_num = up->up_msg_num; 30502ac6454SAndrew Thompson TAILQ_INSERT_TAIL(&up->up_qhead, pm2, pm_qentry); 30602ac6454SAndrew Thompson 30702ac6454SAndrew Thompson /* Check if we need to wakeup the USB process. */ 30802ac6454SAndrew Thompson 30902ac6454SAndrew Thompson if (up->up_msleep) { 31002ac6454SAndrew Thompson up->up_msleep = 0; /* save "cv_signal()" calls */ 3118437751dSAndrew Thompson cv_signal(&up->up_cv); 31202ac6454SAndrew Thompson } 31302ac6454SAndrew Thompson return (pm2); 31402ac6454SAndrew Thompson } 31502ac6454SAndrew Thompson 31602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 317a593f6b8SAndrew Thompson * usb_proc_is_gone 31802ac6454SAndrew Thompson * 31902ac6454SAndrew Thompson * Return values: 32002ac6454SAndrew Thompson * 0: USB process is running 32102ac6454SAndrew Thompson * Else: USB process is tearing down 32202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 32302ac6454SAndrew Thompson uint8_t 324a593f6b8SAndrew Thompson usb_proc_is_gone(struct usb_process *up) 32502ac6454SAndrew Thompson { 32602ac6454SAndrew Thompson if (up->up_gone) 32702ac6454SAndrew Thompson return (1); 32802ac6454SAndrew Thompson 32902ac6454SAndrew Thompson mtx_assert(up->up_mtx, MA_OWNED); 33002ac6454SAndrew Thompson return (0); 33102ac6454SAndrew Thompson } 33202ac6454SAndrew Thompson 33302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 334a593f6b8SAndrew Thompson * usb_proc_mwait 33502ac6454SAndrew Thompson * 33602ac6454SAndrew Thompson * This function will return when the USB process message pointed to 33702ac6454SAndrew Thompson * by "pm" is no longer on a queue. This function must be called 33802ac6454SAndrew Thompson * having "up->up_mtx" locked. 33902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 34002ac6454SAndrew Thompson void 341a593f6b8SAndrew Thompson usb_proc_mwait(struct usb_process *up, void *_pm0, void *_pm1) 34202ac6454SAndrew Thompson { 343760bc48eSAndrew Thompson struct usb_proc_msg *pm0 = _pm0; 344760bc48eSAndrew Thompson struct usb_proc_msg *pm1 = _pm1; 34502ac6454SAndrew Thompson 34602ac6454SAndrew Thompson /* check if gone */ 34702ac6454SAndrew Thompson if (up->up_gone) 34802ac6454SAndrew Thompson return; 34902ac6454SAndrew Thompson 35002ac6454SAndrew Thompson mtx_assert(up->up_mtx, MA_OWNED); 35102ac6454SAndrew Thompson 35202ac6454SAndrew Thompson if (up->up_curtd == curthread) { 35302ac6454SAndrew Thompson /* Just remove the messages from the queue. */ 35402ac6454SAndrew Thompson if (pm0->pm_qentry.tqe_prev) { 35502ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm0, pm_qentry); 35602ac6454SAndrew Thompson pm0->pm_qentry.tqe_prev = NULL; 35702ac6454SAndrew Thompson } 35802ac6454SAndrew Thompson if (pm1->pm_qentry.tqe_prev) { 35902ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm1, pm_qentry); 36002ac6454SAndrew Thompson pm1->pm_qentry.tqe_prev = NULL; 36102ac6454SAndrew Thompson } 36202ac6454SAndrew Thompson } else 36302ac6454SAndrew Thompson while (pm0->pm_qentry.tqe_prev || 36402ac6454SAndrew Thompson pm1->pm_qentry.tqe_prev) { 36502ac6454SAndrew Thompson /* check if config thread is gone */ 36602ac6454SAndrew Thompson if (up->up_gone) 36702ac6454SAndrew Thompson break; 36802ac6454SAndrew Thompson up->up_dsleep = 1; 3698437751dSAndrew Thompson cv_wait(&up->up_drain, up->up_mtx); 37002ac6454SAndrew Thompson } 37102ac6454SAndrew Thompson } 37202ac6454SAndrew Thompson 37302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 374a593f6b8SAndrew Thompson * usb_proc_drain 37502ac6454SAndrew Thompson * 37602ac6454SAndrew Thompson * This function will tear down an USB process, waiting for the 37702ac6454SAndrew Thompson * currently executing command to return. 37802ac6454SAndrew Thompson * 37902ac6454SAndrew Thompson * NOTE: If the structure pointed to by "up" is all zero, 38002ac6454SAndrew Thompson * this function does nothing. 38102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 38202ac6454SAndrew Thompson void 383a593f6b8SAndrew Thompson usb_proc_drain(struct usb_process *up) 38402ac6454SAndrew Thompson { 38502ac6454SAndrew Thompson /* check if not initialised */ 38602ac6454SAndrew Thompson if (up->up_mtx == NULL) 38702ac6454SAndrew Thompson return; 38802ac6454SAndrew Thompson /* handle special case with Giant */ 38902ac6454SAndrew Thompson if (up->up_mtx != &Giant) 39002ac6454SAndrew Thompson mtx_assert(up->up_mtx, MA_NOTOWNED); 39102ac6454SAndrew Thompson 39202ac6454SAndrew Thompson mtx_lock(up->up_mtx); 39302ac6454SAndrew Thompson 39402ac6454SAndrew Thompson /* Set the gone flag */ 39502ac6454SAndrew Thompson 39602ac6454SAndrew Thompson up->up_gone = 1; 39702ac6454SAndrew Thompson 39802ac6454SAndrew Thompson while (up->up_ptr) { 39902ac6454SAndrew Thompson 40002ac6454SAndrew Thompson /* Check if we need to wakeup the USB process */ 40102ac6454SAndrew Thompson 40202ac6454SAndrew Thompson if (up->up_msleep || up->up_csleep) { 40302ac6454SAndrew Thompson up->up_msleep = 0; 40402ac6454SAndrew Thompson up->up_csleep = 0; 4058437751dSAndrew Thompson cv_signal(&up->up_cv); 40602ac6454SAndrew Thompson } 40702ac6454SAndrew Thompson /* Check if we are still cold booted */ 40802ac6454SAndrew Thompson 40902ac6454SAndrew Thompson if (cold) { 41002ac6454SAndrew Thompson USB_THREAD_SUSPEND(up->up_ptr); 41102ac6454SAndrew Thompson printf("WARNING: A USB process has " 41202ac6454SAndrew Thompson "been left suspended!\n"); 41302ac6454SAndrew Thompson break; 41402ac6454SAndrew Thompson } 4158437751dSAndrew Thompson cv_wait(&up->up_cv, up->up_mtx); 41602ac6454SAndrew Thompson } 41702ac6454SAndrew Thompson /* Check if someone is waiting - should not happen */ 41802ac6454SAndrew Thompson 41902ac6454SAndrew Thompson if (up->up_dsleep) { 42002ac6454SAndrew Thompson up->up_dsleep = 0; 4218437751dSAndrew Thompson cv_broadcast(&up->up_drain); 42202ac6454SAndrew Thompson DPRINTF("WARNING: Someone is waiting " 42302ac6454SAndrew Thompson "for USB process drain!\n"); 42402ac6454SAndrew Thompson } 42502ac6454SAndrew Thompson mtx_unlock(up->up_mtx); 42602ac6454SAndrew Thompson } 427