102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 3*718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4*718cf2ccSPedro F. Giffuni * 502ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 602ac6454SAndrew Thompson * 702ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 802ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 902ac6454SAndrew Thompson * are met: 1002ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1202ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1302ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1402ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1502ac6454SAndrew Thompson * 1602ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1702ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1802ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1902ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2002ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2102ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2202ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2302ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2402ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2502ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2602ac6454SAndrew Thompson * SUCH DAMAGE. 2702ac6454SAndrew Thompson */ 2802ac6454SAndrew Thompson 29d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 30d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 31d2b99310SHans Petter Selasky #else 32ed6d949aSAndrew Thompson #include <sys/stdint.h> 33ed6d949aSAndrew Thompson #include <sys/stddef.h> 34ed6d949aSAndrew Thompson #include <sys/param.h> 35ed6d949aSAndrew Thompson #include <sys/queue.h> 36ed6d949aSAndrew Thompson #include <sys/types.h> 37ed6d949aSAndrew Thompson #include <sys/systm.h> 38ed6d949aSAndrew Thompson #include <sys/kernel.h> 39ed6d949aSAndrew Thompson #include <sys/bus.h> 40ed6d949aSAndrew Thompson #include <sys/module.h> 41ed6d949aSAndrew Thompson #include <sys/lock.h> 42ed6d949aSAndrew Thompson #include <sys/mutex.h> 43ed6d949aSAndrew Thompson #include <sys/condvar.h> 44ed6d949aSAndrew Thompson #include <sys/sysctl.h> 45ed6d949aSAndrew Thompson #include <sys/sx.h> 46ed6d949aSAndrew Thompson #include <sys/unistd.h> 47ed6d949aSAndrew Thompson #include <sys/callout.h> 48ed6d949aSAndrew Thompson #include <sys/malloc.h> 49ed6d949aSAndrew Thompson #include <sys/priv.h> 50ed6d949aSAndrew Thompson 51ed6d949aSAndrew Thompson #include <dev/usb/usb.h> 52ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 53ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5402ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 55ff182db6SHans Petter Selasky 56ff182db6SHans Petter Selasky #define USB_DEBUG_VAR usb_proc_debug 5702ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 5802ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 5902ac6454SAndrew Thompson 6002ac6454SAndrew Thompson #include <sys/proc.h> 6102ac6454SAndrew Thompson #include <sys/kthread.h> 6202ac6454SAndrew Thompson #include <sys/sched.h> 63d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 6402ac6454SAndrew Thompson 6502ac6454SAndrew Thompson #if (__FreeBSD_version < 700000) 6602ac6454SAndrew Thompson #define thread_lock(td) mtx_lock_spin(&sched_lock) 6702ac6454SAndrew Thompson #define thread_unlock(td) mtx_unlock_spin(&sched_lock) 6802ac6454SAndrew Thompson #endif 6902ac6454SAndrew Thompson 7002ac6454SAndrew Thompson #if (__FreeBSD_version >= 800000) 71532b1952SAndrew Thompson static struct proc *usbproc; 7251e8c0d6SAndrew Thompson static int usb_pcount; 7302ac6454SAndrew Thompson #define USB_THREAD_CREATE(f, s, p, ...) \ 74532b1952SAndrew Thompson kproc_kthread_add((f), (s), &usbproc, (p), RFHIGHPID, \ 75532b1952SAndrew Thompson 0, "usb", __VA_ARGS__) 76e6c503f7SHans Petter Selasky #if (__FreeBSD_version >= 900000) 7787a133a7SHans Petter Selasky #define USB_THREAD_SUSPEND_CHECK() kthread_suspend_check() 78e6c503f7SHans Petter Selasky #else 79e6c503f7SHans Petter Selasky #define USB_THREAD_SUSPEND_CHECK() kthread_suspend_check(curthread) 80e6c503f7SHans Petter Selasky #endif 81532b1952SAndrew Thompson #define USB_THREAD_SUSPEND(p) kthread_suspend(p,0) 82532b1952SAndrew Thompson #define USB_THREAD_EXIT(err) kthread_exit() 8302ac6454SAndrew Thompson #else 8402ac6454SAndrew Thompson #define USB_THREAD_CREATE(f, s, p, ...) \ 8502ac6454SAndrew Thompson kthread_create((f), (s), (p), RFHIGHPID, 0, __VA_ARGS__) 86e6c503f7SHans Petter Selasky #define USB_THREAD_SUSPEND_CHECK() kthread_suspend_check(curproc) 8702ac6454SAndrew Thompson #define USB_THREAD_SUSPEND(p) kthread_suspend(p,0) 8802ac6454SAndrew Thompson #define USB_THREAD_EXIT(err) kthread_exit(err) 8902ac6454SAndrew Thompson #endif 9002ac6454SAndrew Thompson 91ed6d949aSAndrew Thompson #ifdef USB_DEBUG 92a593f6b8SAndrew Thompson static int usb_proc_debug; 9302ac6454SAndrew Thompson 946472ac3dSEd Schouten static SYSCTL_NODE(_hw_usb, OID_AUTO, proc, CTLFLAG_RW, 0, "USB process"); 95af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb_proc, OID_AUTO, debug, CTLFLAG_RWTUN, &usb_proc_debug, 0, 9602ac6454SAndrew Thompson "Debug level"); 9702ac6454SAndrew Thompson #endif 9802ac6454SAndrew Thompson 9902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 100760bc48eSAndrew Thompson * usb_process 10102ac6454SAndrew Thompson * 10202ac6454SAndrew Thompson * This function is the USB process dispatcher. 10302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 10402ac6454SAndrew Thompson static void 105760bc48eSAndrew Thompson usb_process(void *arg) 10602ac6454SAndrew Thompson { 107760bc48eSAndrew Thompson struct usb_process *up = arg; 108760bc48eSAndrew Thompson struct usb_proc_msg *pm; 10902ac6454SAndrew Thompson struct thread *td; 11002ac6454SAndrew Thompson 11187a133a7SHans Petter Selasky /* in case of attach error, check for suspended */ 11287a133a7SHans Petter Selasky USB_THREAD_SUSPEND_CHECK(); 11387a133a7SHans Petter Selasky 11402ac6454SAndrew Thompson /* adjust priority */ 11502ac6454SAndrew Thompson td = curthread; 11602ac6454SAndrew Thompson thread_lock(td); 11702ac6454SAndrew Thompson sched_prio(td, up->up_prio); 11802ac6454SAndrew Thompson thread_unlock(td); 11902ac6454SAndrew Thompson 1200eb8d462SHans Petter Selasky USB_MTX_LOCK(up->up_mtx); 12102ac6454SAndrew Thompson 12202ac6454SAndrew Thompson up->up_curtd = td; 12302ac6454SAndrew Thompson 12402ac6454SAndrew Thompson while (1) { 12502ac6454SAndrew Thompson 12602ac6454SAndrew Thompson if (up->up_gone) 12702ac6454SAndrew Thompson break; 12802ac6454SAndrew Thompson 12902ac6454SAndrew Thompson /* 13002ac6454SAndrew Thompson * NOTE to reimplementors: dequeueing a command from the 13102ac6454SAndrew Thompson * "used" queue and executing it must be atomic, with regard 13202ac6454SAndrew Thompson * to the "up_mtx" mutex. That means any attempt to queue a 13302ac6454SAndrew Thompson * command by another thread must be blocked until either: 13402ac6454SAndrew Thompson * 13502ac6454SAndrew Thompson * 1) the command sleeps 13602ac6454SAndrew Thompson * 13702ac6454SAndrew Thompson * 2) the command returns 13802ac6454SAndrew Thompson * 13902ac6454SAndrew Thompson * Here is a practical example that shows how this helps 14002ac6454SAndrew Thompson * solving a problem: 14102ac6454SAndrew Thompson * 14202ac6454SAndrew Thompson * Assume that you want to set the baud rate on a USB serial 14302ac6454SAndrew Thompson * device. During the programming of the device you don't 14402ac6454SAndrew Thompson * want to receive nor transmit any data, because it will be 14502ac6454SAndrew Thompson * garbage most likely anyway. The programming of our USB 14602ac6454SAndrew Thompson * device takes 20 milliseconds and it needs to call 14702ac6454SAndrew Thompson * functions that sleep. 14802ac6454SAndrew Thompson * 14902ac6454SAndrew Thompson * Non-working solution: Before we queue the programming 15002ac6454SAndrew Thompson * command, we stop transmission and reception of data. Then 15102ac6454SAndrew Thompson * we queue a programming command. At the end of the 15202ac6454SAndrew Thompson * programming command we enable transmission and reception 15302ac6454SAndrew Thompson * of data. 15402ac6454SAndrew Thompson * 15502ac6454SAndrew Thompson * Problem: If a second programming command is queued while the 15602ac6454SAndrew Thompson * first one is sleeping, we end up enabling transmission 15702ac6454SAndrew Thompson * and reception of data too early. 15802ac6454SAndrew Thompson * 15902ac6454SAndrew Thompson * Working solution: Before we queue the programming command, 16002ac6454SAndrew Thompson * we stop transmission and reception of data. Then we queue 16102ac6454SAndrew Thompson * a programming command. Then we queue a second command 16202ac6454SAndrew Thompson * that only enables transmission and reception of data. 16302ac6454SAndrew Thompson * 16402ac6454SAndrew Thompson * Why it works: If a second programming command is queued 16502ac6454SAndrew Thompson * while the first one is sleeping, then the queueing of a 16602ac6454SAndrew Thompson * second command to enable the data transfers, will cause 16702ac6454SAndrew Thompson * the previous one, which is still on the queue, to be 16802ac6454SAndrew Thompson * removed from the queue, and re-inserted after the last 16902ac6454SAndrew Thompson * baud rate programming command, which then gives the 17002ac6454SAndrew Thompson * desired result. 17102ac6454SAndrew Thompson */ 17202ac6454SAndrew Thompson pm = TAILQ_FIRST(&up->up_qhead); 17302ac6454SAndrew Thompson 17402ac6454SAndrew Thompson if (pm) { 17502ac6454SAndrew Thompson DPRINTF("Message pm=%p, cb=%p (enter)\n", 17602ac6454SAndrew Thompson pm, pm->pm_callback); 17702ac6454SAndrew Thompson 17802ac6454SAndrew Thompson (pm->pm_callback) (pm); 17902ac6454SAndrew Thompson 18002ac6454SAndrew Thompson if (pm == TAILQ_FIRST(&up->up_qhead)) { 18102ac6454SAndrew Thompson /* nothing changed */ 18202ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm, pm_qentry); 18302ac6454SAndrew Thompson pm->pm_qentry.tqe_prev = NULL; 18402ac6454SAndrew Thompson } 18502ac6454SAndrew Thompson DPRINTF("Message pm=%p (leave)\n", pm); 18602ac6454SAndrew Thompson 18702ac6454SAndrew Thompson continue; 18802ac6454SAndrew Thompson } 189c0f71e31SHans Petter Selasky /* end of messages - check if anyone is waiting for sync */ 19002ac6454SAndrew Thompson if (up->up_dsleep) { 19102ac6454SAndrew Thompson up->up_dsleep = 0; 1928437751dSAndrew Thompson cv_broadcast(&up->up_drain); 19302ac6454SAndrew Thompson } 19402ac6454SAndrew Thompson up->up_msleep = 1; 1958437751dSAndrew Thompson cv_wait(&up->up_cv, up->up_mtx); 19602ac6454SAndrew Thompson } 19702ac6454SAndrew Thompson 19802ac6454SAndrew Thompson up->up_ptr = NULL; 1998437751dSAndrew Thompson cv_signal(&up->up_cv); 2000eb8d462SHans Petter Selasky USB_MTX_UNLOCK(up->up_mtx); 20151e8c0d6SAndrew Thompson #if (__FreeBSD_version >= 800000) 20251e8c0d6SAndrew Thompson /* Clear the proc pointer if this is the last thread. */ 20351e8c0d6SAndrew Thompson if (--usb_pcount == 0) 20451e8c0d6SAndrew Thompson usbproc = NULL; 20551e8c0d6SAndrew Thompson #endif 20602ac6454SAndrew Thompson 20702ac6454SAndrew Thompson USB_THREAD_EXIT(0); 20802ac6454SAndrew Thompson } 20902ac6454SAndrew Thompson 21002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 211a593f6b8SAndrew Thompson * usb_proc_create 21202ac6454SAndrew Thompson * 21302ac6454SAndrew Thompson * This function will create a process using the given "prio" that can 21402ac6454SAndrew Thompson * execute callbacks. The mutex pointed to by "p_mtx" will be applied 21502ac6454SAndrew Thompson * before calling the callbacks and released after that the callback 21602ac6454SAndrew Thompson * has returned. The structure pointed to by "up" is assumed to be 21702ac6454SAndrew Thompson * zeroed before this function is called. 21802ac6454SAndrew Thompson * 21902ac6454SAndrew Thompson * Return values: 22002ac6454SAndrew Thompson * 0: success 22102ac6454SAndrew Thompson * Else: failure 22202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 22302ac6454SAndrew Thompson int 224a593f6b8SAndrew Thompson usb_proc_create(struct usb_process *up, struct mtx *p_mtx, 22502ac6454SAndrew Thompson const char *pmesg, uint8_t prio) 22602ac6454SAndrew Thompson { 22702ac6454SAndrew Thompson up->up_mtx = p_mtx; 22802ac6454SAndrew Thompson up->up_prio = prio; 22902ac6454SAndrew Thompson 23002ac6454SAndrew Thompson TAILQ_INIT(&up->up_qhead); 23102ac6454SAndrew Thompson 232532b1952SAndrew Thompson cv_init(&up->up_cv, "-"); 233532b1952SAndrew Thompson cv_init(&up->up_drain, "usbdrain"); 23402ac6454SAndrew Thompson 235760bc48eSAndrew Thompson if (USB_THREAD_CREATE(&usb_process, up, 2361bdfff22SAndriy Gapon &up->up_ptr, "%s", pmesg)) { 23702ac6454SAndrew Thompson DPRINTFN(0, "Unable to create USB process."); 23802ac6454SAndrew Thompson up->up_ptr = NULL; 23902ac6454SAndrew Thompson goto error; 24002ac6454SAndrew Thompson } 24151e8c0d6SAndrew Thompson #if (__FreeBSD_version >= 800000) 24251e8c0d6SAndrew Thompson usb_pcount++; 24351e8c0d6SAndrew Thompson #endif 24402ac6454SAndrew Thompson return (0); 24502ac6454SAndrew Thompson 24602ac6454SAndrew Thompson error: 247a593f6b8SAndrew Thompson usb_proc_free(up); 24802ac6454SAndrew Thompson return (ENOMEM); 24902ac6454SAndrew Thompson } 25002ac6454SAndrew Thompson 25102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 252a593f6b8SAndrew Thompson * usb_proc_free 25302ac6454SAndrew Thompson * 25402ac6454SAndrew Thompson * NOTE: If the structure pointed to by "up" is all zero, this 25502ac6454SAndrew Thompson * function does nothing. 25602ac6454SAndrew Thompson * 25702ac6454SAndrew Thompson * NOTE: Messages that are pending on the process queue will not be 25802ac6454SAndrew Thompson * removed nor called. 25902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 26002ac6454SAndrew Thompson void 261a593f6b8SAndrew Thompson usb_proc_free(struct usb_process *up) 26202ac6454SAndrew Thompson { 26302ac6454SAndrew Thompson /* check if not initialised */ 26402ac6454SAndrew Thompson if (up->up_mtx == NULL) 26502ac6454SAndrew Thompson return; 26602ac6454SAndrew Thompson 267a593f6b8SAndrew Thompson usb_proc_drain(up); 26802ac6454SAndrew Thompson 2698437751dSAndrew Thompson cv_destroy(&up->up_cv); 2708437751dSAndrew Thompson cv_destroy(&up->up_drain); 27102ac6454SAndrew Thompson 27202ac6454SAndrew Thompson /* make sure that we do not enter here again */ 27302ac6454SAndrew Thompson up->up_mtx = NULL; 27402ac6454SAndrew Thompson } 27502ac6454SAndrew Thompson 27602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 277a593f6b8SAndrew Thompson * usb_proc_msignal 27802ac6454SAndrew Thompson * 27902ac6454SAndrew Thompson * This function will queue one of the passed USB process messages on 28002ac6454SAndrew Thompson * the USB process queue. The first message that is not already queued 28102ac6454SAndrew Thompson * will get queued. If both messages are already queued the one queued 28202ac6454SAndrew Thompson * last will be removed from the queue and queued in the end. The USB 28302ac6454SAndrew Thompson * process mutex must be locked when calling this function. This 28402ac6454SAndrew Thompson * function exploits the fact that a process can only do one callback 28502ac6454SAndrew Thompson * at a time. The message that was queued is returned. 28602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 28702ac6454SAndrew Thompson void * 288a593f6b8SAndrew Thompson usb_proc_msignal(struct usb_process *up, void *_pm0, void *_pm1) 28902ac6454SAndrew Thompson { 290760bc48eSAndrew Thompson struct usb_proc_msg *pm0 = _pm0; 291760bc48eSAndrew Thompson struct usb_proc_msg *pm1 = _pm1; 292760bc48eSAndrew Thompson struct usb_proc_msg *pm2; 293f9cb546cSAndrew Thompson usb_size_t d; 29402ac6454SAndrew Thompson uint8_t t; 29502ac6454SAndrew Thompson 2960eb8d462SHans Petter Selasky /* check if gone or in polling mode, return dummy value */ 2970eb8d462SHans Petter Selasky if (up->up_gone != 0 || 2980eb8d462SHans Petter Selasky USB_IN_POLLING_MODE_FUNC() != 0) 29902ac6454SAndrew Thompson return (_pm0); 30002ac6454SAndrew Thompson 3010eb8d462SHans Petter Selasky USB_MTX_ASSERT(up->up_mtx, MA_OWNED); 30202ac6454SAndrew Thompson 30302ac6454SAndrew Thompson t = 0; 30402ac6454SAndrew Thompson 30502ac6454SAndrew Thompson if (pm0->pm_qentry.tqe_prev) { 30602ac6454SAndrew Thompson t |= 1; 30702ac6454SAndrew Thompson } 30802ac6454SAndrew Thompson if (pm1->pm_qentry.tqe_prev) { 30902ac6454SAndrew Thompson t |= 2; 31002ac6454SAndrew Thompson } 31102ac6454SAndrew Thompson if (t == 0) { 31202ac6454SAndrew Thompson /* 31302ac6454SAndrew Thompson * No entries are queued. Queue "pm0" and use the existing 31402ac6454SAndrew Thompson * message number. 31502ac6454SAndrew Thompson */ 31602ac6454SAndrew Thompson pm2 = pm0; 31702ac6454SAndrew Thompson } else if (t == 1) { 31802ac6454SAndrew Thompson /* Check if we need to increment the message number. */ 31902ac6454SAndrew Thompson if (pm0->pm_num == up->up_msg_num) { 32002ac6454SAndrew Thompson up->up_msg_num++; 32102ac6454SAndrew Thompson } 32202ac6454SAndrew Thompson pm2 = pm1; 32302ac6454SAndrew Thompson } else if (t == 2) { 32402ac6454SAndrew Thompson /* Check if we need to increment the message number. */ 32502ac6454SAndrew Thompson if (pm1->pm_num == up->up_msg_num) { 32602ac6454SAndrew Thompson up->up_msg_num++; 32702ac6454SAndrew Thompson } 32802ac6454SAndrew Thompson pm2 = pm0; 32902ac6454SAndrew Thompson } else if (t == 3) { 33002ac6454SAndrew Thompson /* 33102ac6454SAndrew Thompson * Both entries are queued. Re-queue the entry closest to 33202ac6454SAndrew Thompson * the end. 33302ac6454SAndrew Thompson */ 33402ac6454SAndrew Thompson d = (pm1->pm_num - pm0->pm_num); 33502ac6454SAndrew Thompson 33602ac6454SAndrew Thompson /* Check sign after subtraction */ 33702ac6454SAndrew Thompson if (d & 0x80000000) { 33802ac6454SAndrew Thompson pm2 = pm0; 33902ac6454SAndrew Thompson } else { 34002ac6454SAndrew Thompson pm2 = pm1; 34102ac6454SAndrew Thompson } 34202ac6454SAndrew Thompson 34302ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm2, pm_qentry); 34402ac6454SAndrew Thompson } else { 34502ac6454SAndrew Thompson pm2 = NULL; /* panic - should not happen */ 34602ac6454SAndrew Thompson } 34702ac6454SAndrew Thompson 34802ac6454SAndrew Thompson DPRINTF(" t=%u, num=%u\n", t, up->up_msg_num); 34902ac6454SAndrew Thompson 35002ac6454SAndrew Thompson /* Put message last on queue */ 35102ac6454SAndrew Thompson 35202ac6454SAndrew Thompson pm2->pm_num = up->up_msg_num; 35302ac6454SAndrew Thompson TAILQ_INSERT_TAIL(&up->up_qhead, pm2, pm_qentry); 35402ac6454SAndrew Thompson 35502ac6454SAndrew Thompson /* Check if we need to wakeup the USB process. */ 35602ac6454SAndrew Thompson 35702ac6454SAndrew Thompson if (up->up_msleep) { 35802ac6454SAndrew Thompson up->up_msleep = 0; /* save "cv_signal()" calls */ 3598437751dSAndrew Thompson cv_signal(&up->up_cv); 36002ac6454SAndrew Thompson } 36102ac6454SAndrew Thompson return (pm2); 36202ac6454SAndrew Thompson } 36302ac6454SAndrew Thompson 36402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 365a593f6b8SAndrew Thompson * usb_proc_is_gone 36602ac6454SAndrew Thompson * 36702ac6454SAndrew Thompson * Return values: 36802ac6454SAndrew Thompson * 0: USB process is running 36902ac6454SAndrew Thompson * Else: USB process is tearing down 37002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 37102ac6454SAndrew Thompson uint8_t 372a593f6b8SAndrew Thompson usb_proc_is_gone(struct usb_process *up) 37302ac6454SAndrew Thompson { 37402ac6454SAndrew Thompson if (up->up_gone) 37502ac6454SAndrew Thompson return (1); 37602ac6454SAndrew Thompson 3770c38ca8cSHans Petter Selasky /* 3780c38ca8cSHans Petter Selasky * Allow calls when up_mtx is NULL, before the USB process 3790c38ca8cSHans Petter Selasky * structure is initialised. 3800c38ca8cSHans Petter Selasky */ 3810c38ca8cSHans Petter Selasky if (up->up_mtx != NULL) 3820eb8d462SHans Petter Selasky USB_MTX_ASSERT(up->up_mtx, MA_OWNED); 38302ac6454SAndrew Thompson return (0); 38402ac6454SAndrew Thompson } 38502ac6454SAndrew Thompson 38602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 387a593f6b8SAndrew Thompson * usb_proc_mwait 38802ac6454SAndrew Thompson * 38902ac6454SAndrew Thompson * This function will return when the USB process message pointed to 39002ac6454SAndrew Thompson * by "pm" is no longer on a queue. This function must be called 39102ac6454SAndrew Thompson * having "up->up_mtx" locked. 39202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 39302ac6454SAndrew Thompson void 394a593f6b8SAndrew Thompson usb_proc_mwait(struct usb_process *up, void *_pm0, void *_pm1) 39502ac6454SAndrew Thompson { 396760bc48eSAndrew Thompson struct usb_proc_msg *pm0 = _pm0; 397760bc48eSAndrew Thompson struct usb_proc_msg *pm1 = _pm1; 39802ac6454SAndrew Thompson 39902ac6454SAndrew Thompson /* check if gone */ 40002ac6454SAndrew Thompson if (up->up_gone) 40102ac6454SAndrew Thompson return; 40202ac6454SAndrew Thompson 4030eb8d462SHans Petter Selasky USB_MTX_ASSERT(up->up_mtx, MA_OWNED); 40402ac6454SAndrew Thompson 40502ac6454SAndrew Thompson if (up->up_curtd == curthread) { 40602ac6454SAndrew Thompson /* Just remove the messages from the queue. */ 40702ac6454SAndrew Thompson if (pm0->pm_qentry.tqe_prev) { 40802ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm0, pm_qentry); 40902ac6454SAndrew Thompson pm0->pm_qentry.tqe_prev = NULL; 41002ac6454SAndrew Thompson } 41102ac6454SAndrew Thompson if (pm1->pm_qentry.tqe_prev) { 41202ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm1, pm_qentry); 41302ac6454SAndrew Thompson pm1->pm_qentry.tqe_prev = NULL; 41402ac6454SAndrew Thompson } 41502ac6454SAndrew Thompson } else 41602ac6454SAndrew Thompson while (pm0->pm_qentry.tqe_prev || 41702ac6454SAndrew Thompson pm1->pm_qentry.tqe_prev) { 41802ac6454SAndrew Thompson /* check if config thread is gone */ 41902ac6454SAndrew Thompson if (up->up_gone) 42002ac6454SAndrew Thompson break; 42102ac6454SAndrew Thompson up->up_dsleep = 1; 4228437751dSAndrew Thompson cv_wait(&up->up_drain, up->up_mtx); 42302ac6454SAndrew Thompson } 42402ac6454SAndrew Thompson } 42502ac6454SAndrew Thompson 42602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 427a593f6b8SAndrew Thompson * usb_proc_drain 42802ac6454SAndrew Thompson * 42902ac6454SAndrew Thompson * This function will tear down an USB process, waiting for the 43002ac6454SAndrew Thompson * currently executing command to return. 43102ac6454SAndrew Thompson * 43202ac6454SAndrew Thompson * NOTE: If the structure pointed to by "up" is all zero, 43302ac6454SAndrew Thompson * this function does nothing. 43402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 43502ac6454SAndrew Thompson void 436a593f6b8SAndrew Thompson usb_proc_drain(struct usb_process *up) 43702ac6454SAndrew Thompson { 43802ac6454SAndrew Thompson /* check if not initialised */ 43902ac6454SAndrew Thompson if (up->up_mtx == NULL) 44002ac6454SAndrew Thompson return; 44102ac6454SAndrew Thompson /* handle special case with Giant */ 44202ac6454SAndrew Thompson if (up->up_mtx != &Giant) 4430eb8d462SHans Petter Selasky USB_MTX_ASSERT(up->up_mtx, MA_NOTOWNED); 44402ac6454SAndrew Thompson 4450eb8d462SHans Petter Selasky USB_MTX_LOCK(up->up_mtx); 44602ac6454SAndrew Thompson 44702ac6454SAndrew Thompson /* Set the gone flag */ 44802ac6454SAndrew Thompson 44902ac6454SAndrew Thompson up->up_gone = 1; 45002ac6454SAndrew Thompson 45102ac6454SAndrew Thompson while (up->up_ptr) { 45202ac6454SAndrew Thompson 45302ac6454SAndrew Thompson /* Check if we need to wakeup the USB process */ 45402ac6454SAndrew Thompson 45502ac6454SAndrew Thompson if (up->up_msleep || up->up_csleep) { 45602ac6454SAndrew Thompson up->up_msleep = 0; 45702ac6454SAndrew Thompson up->up_csleep = 0; 4588437751dSAndrew Thompson cv_signal(&up->up_cv); 45902ac6454SAndrew Thompson } 460433ded8bSHans Petter Selasky #ifndef EARLY_AP_STARTUP 46102ac6454SAndrew Thompson /* Check if we are still cold booted */ 46202ac6454SAndrew Thompson if (cold) { 46302ac6454SAndrew Thompson USB_THREAD_SUSPEND(up->up_ptr); 46402ac6454SAndrew Thompson printf("WARNING: A USB process has " 465767cb2e2SAndrew Thompson "been left suspended\n"); 46602ac6454SAndrew Thompson break; 46702ac6454SAndrew Thompson } 468433ded8bSHans Petter Selasky #endif 4698437751dSAndrew Thompson cv_wait(&up->up_cv, up->up_mtx); 47002ac6454SAndrew Thompson } 47102ac6454SAndrew Thompson /* Check if someone is waiting - should not happen */ 47202ac6454SAndrew Thompson 47302ac6454SAndrew Thompson if (up->up_dsleep) { 47402ac6454SAndrew Thompson up->up_dsleep = 0; 4758437751dSAndrew Thompson cv_broadcast(&up->up_drain); 47602ac6454SAndrew Thompson DPRINTF("WARNING: Someone is waiting " 47702ac6454SAndrew Thompson "for USB process drain!\n"); 47802ac6454SAndrew Thompson } 4790eb8d462SHans Petter Selasky USB_MTX_UNLOCK(up->up_mtx); 48002ac6454SAndrew Thompson } 481cb18f7d1SAlfred Perlstein 482cb18f7d1SAlfred Perlstein /*------------------------------------------------------------------------* 483cb18f7d1SAlfred Perlstein * usb_proc_rewakeup 484cb18f7d1SAlfred Perlstein * 4856bccea7cSRebecca Cran * This function is called to re-wakeup the given USB 486cb18f7d1SAlfred Perlstein * process. This usually happens after that the USB system has been in 487cb18f7d1SAlfred Perlstein * polling mode, like during a panic. This function must be called 488cb18f7d1SAlfred Perlstein * having "up->up_mtx" locked. 489cb18f7d1SAlfred Perlstein *------------------------------------------------------------------------*/ 490cb18f7d1SAlfred Perlstein void 491cb18f7d1SAlfred Perlstein usb_proc_rewakeup(struct usb_process *up) 492cb18f7d1SAlfred Perlstein { 493cb18f7d1SAlfred Perlstein /* check if not initialised */ 494cb18f7d1SAlfred Perlstein if (up->up_mtx == NULL) 495cb18f7d1SAlfred Perlstein return; 496cb18f7d1SAlfred Perlstein /* check if gone */ 497cb18f7d1SAlfred Perlstein if (up->up_gone) 498cb18f7d1SAlfred Perlstein return; 499cb18f7d1SAlfred Perlstein 5000eb8d462SHans Petter Selasky USB_MTX_ASSERT(up->up_mtx, MA_OWNED); 501cb18f7d1SAlfred Perlstein 502cb18f7d1SAlfred Perlstein if (up->up_msleep == 0) { 503cb18f7d1SAlfred Perlstein /* re-wakeup */ 504cb18f7d1SAlfred Perlstein cv_signal(&up->up_cv); 505cb18f7d1SAlfred Perlstein } 506cb18f7d1SAlfred Perlstein } 507d008478eSHans Petter Selasky 508d008478eSHans Petter Selasky /*------------------------------------------------------------------------* 509d008478eSHans Petter Selasky * usb_proc_is_called_from 510d008478eSHans Petter Selasky * 511d008478eSHans Petter Selasky * This function will return non-zero if called from inside the USB 512d008478eSHans Petter Selasky * process passed as first argument. Else this function returns zero. 513d008478eSHans Petter Selasky *------------------------------------------------------------------------*/ 514d008478eSHans Petter Selasky int 515d008478eSHans Petter Selasky usb_proc_is_called_from(struct usb_process *up) 516d008478eSHans Petter Selasky { 517d008478eSHans Petter Selasky return (up->up_curtd == curthread); 518d008478eSHans Petter Selasky } 519