102ac6454SAndrew Thompson /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 402ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 502ac6454SAndrew Thompson * 602ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 702ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 802ac6454SAndrew Thompson * are met: 902ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1002ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1102ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1202ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1302ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1402ac6454SAndrew Thompson * 1502ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1602ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1702ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1802ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1902ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2002ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2102ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2202ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2302ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2402ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2502ac6454SAndrew Thompson * SUCH DAMAGE. 2602ac6454SAndrew Thompson */ 2702ac6454SAndrew Thompson 28d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 29d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 30d2b99310SHans Petter Selasky #else 31ed6d949aSAndrew Thompson #include <sys/stdint.h> 32ed6d949aSAndrew Thompson #include <sys/stddef.h> 33ed6d949aSAndrew Thompson #include <sys/param.h> 34ed6d949aSAndrew Thompson #include <sys/queue.h> 35ed6d949aSAndrew Thompson #include <sys/types.h> 36ed6d949aSAndrew Thompson #include <sys/systm.h> 37ed6d949aSAndrew Thompson #include <sys/kernel.h> 38ed6d949aSAndrew Thompson #include <sys/bus.h> 39ed6d949aSAndrew Thompson #include <sys/module.h> 40ed6d949aSAndrew Thompson #include <sys/lock.h> 41ed6d949aSAndrew Thompson #include <sys/mutex.h> 42ed6d949aSAndrew Thompson #include <sys/condvar.h> 43ed6d949aSAndrew Thompson #include <sys/sysctl.h> 44ed6d949aSAndrew Thompson #include <sys/sx.h> 45ed6d949aSAndrew Thompson #include <sys/unistd.h> 46ed6d949aSAndrew Thompson #include <sys/callout.h> 47ed6d949aSAndrew Thompson #include <sys/malloc.h> 48ed6d949aSAndrew Thompson #include <sys/priv.h> 49ed6d949aSAndrew Thompson 50ed6d949aSAndrew Thompson #include <dev/usb/usb.h> 51ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 52ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5302ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 54ff182db6SHans Petter Selasky 55ff182db6SHans Petter Selasky #define USB_DEBUG_VAR usb_proc_debug 5602ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 5702ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 5802ac6454SAndrew Thompson 5902ac6454SAndrew Thompson #include <sys/proc.h> 6002ac6454SAndrew Thompson #include <sys/kthread.h> 6102ac6454SAndrew Thompson #include <sys/sched.h> 62d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 6302ac6454SAndrew Thompson 64532b1952SAndrew Thompson static struct proc *usbproc; 6551e8c0d6SAndrew Thompson static int usb_pcount; 6602ac6454SAndrew Thompson #define USB_THREAD_CREATE(f, s, p, ...) \ 67532b1952SAndrew Thompson kproc_kthread_add((f), (s), &usbproc, (p), RFHIGHPID, \ 68532b1952SAndrew Thompson 0, "usb", __VA_ARGS__) 6987a133a7SHans Petter Selasky #define USB_THREAD_SUSPEND_CHECK() kthread_suspend_check() 70532b1952SAndrew Thompson #define USB_THREAD_SUSPEND(p) kthread_suspend(p,0) 71532b1952SAndrew Thompson #define USB_THREAD_EXIT(err) kthread_exit() 7202ac6454SAndrew Thompson 73ed6d949aSAndrew Thompson #ifdef USB_DEBUG 74a593f6b8SAndrew Thompson static int usb_proc_debug; 7502ac6454SAndrew Thompson 76f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, proc, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 77f8d2b1f3SPawel Biernacki "USB process"); 78af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb_proc, OID_AUTO, debug, CTLFLAG_RWTUN, &usb_proc_debug, 0, 7902ac6454SAndrew Thompson "Debug level"); 8002ac6454SAndrew Thompson #endif 8102ac6454SAndrew Thompson 8202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 83760bc48eSAndrew Thompson * usb_process 8402ac6454SAndrew Thompson * 8502ac6454SAndrew Thompson * This function is the USB process dispatcher. 8602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 8702ac6454SAndrew Thompson static void 88760bc48eSAndrew Thompson usb_process(void *arg) 8902ac6454SAndrew Thompson { 90760bc48eSAndrew Thompson struct usb_process *up = arg; 91760bc48eSAndrew Thompson struct usb_proc_msg *pm; 9202ac6454SAndrew Thompson struct thread *td; 9302ac6454SAndrew Thompson 9487a133a7SHans Petter Selasky /* in case of attach error, check for suspended */ 9587a133a7SHans Petter Selasky USB_THREAD_SUSPEND_CHECK(); 9687a133a7SHans Petter Selasky 9702ac6454SAndrew Thompson /* adjust priority */ 9802ac6454SAndrew Thompson td = curthread; 9902ac6454SAndrew Thompson thread_lock(td); 10002ac6454SAndrew Thompson sched_prio(td, up->up_prio); 10102ac6454SAndrew Thompson thread_unlock(td); 10202ac6454SAndrew Thompson 1030eb8d462SHans Petter Selasky USB_MTX_LOCK(up->up_mtx); 10402ac6454SAndrew Thompson 10502ac6454SAndrew Thompson up->up_curtd = td; 10602ac6454SAndrew Thompson 10702ac6454SAndrew Thompson while (1) { 10802ac6454SAndrew Thompson if (up->up_gone) 10902ac6454SAndrew Thompson break; 11002ac6454SAndrew Thompson 11102ac6454SAndrew Thompson /* 11202ac6454SAndrew Thompson * NOTE to reimplementors: dequeueing a command from the 11302ac6454SAndrew Thompson * "used" queue and executing it must be atomic, with regard 11402ac6454SAndrew Thompson * to the "up_mtx" mutex. That means any attempt to queue a 11502ac6454SAndrew Thompson * command by another thread must be blocked until either: 11602ac6454SAndrew Thompson * 11702ac6454SAndrew Thompson * 1) the command sleeps 11802ac6454SAndrew Thompson * 11902ac6454SAndrew Thompson * 2) the command returns 12002ac6454SAndrew Thompson * 12102ac6454SAndrew Thompson * Here is a practical example that shows how this helps 12202ac6454SAndrew Thompson * solving a problem: 12302ac6454SAndrew Thompson * 12402ac6454SAndrew Thompson * Assume that you want to set the baud rate on a USB serial 12502ac6454SAndrew Thompson * device. During the programming of the device you don't 12602ac6454SAndrew Thompson * want to receive nor transmit any data, because it will be 12702ac6454SAndrew Thompson * garbage most likely anyway. The programming of our USB 12802ac6454SAndrew Thompson * device takes 20 milliseconds and it needs to call 12902ac6454SAndrew Thompson * functions that sleep. 13002ac6454SAndrew Thompson * 13102ac6454SAndrew Thompson * Non-working solution: Before we queue the programming 13202ac6454SAndrew Thompson * command, we stop transmission and reception of data. Then 13302ac6454SAndrew Thompson * we queue a programming command. At the end of the 13402ac6454SAndrew Thompson * programming command we enable transmission and reception 13502ac6454SAndrew Thompson * of data. 13602ac6454SAndrew Thompson * 13702ac6454SAndrew Thompson * Problem: If a second programming command is queued while the 13802ac6454SAndrew Thompson * first one is sleeping, we end up enabling transmission 13902ac6454SAndrew Thompson * and reception of data too early. 14002ac6454SAndrew Thompson * 14102ac6454SAndrew Thompson * Working solution: Before we queue the programming command, 14202ac6454SAndrew Thompson * we stop transmission and reception of data. Then we queue 14302ac6454SAndrew Thompson * a programming command. Then we queue a second command 14402ac6454SAndrew Thompson * that only enables transmission and reception of data. 14502ac6454SAndrew Thompson * 14602ac6454SAndrew Thompson * Why it works: If a second programming command is queued 14702ac6454SAndrew Thompson * while the first one is sleeping, then the queueing of a 14802ac6454SAndrew Thompson * second command to enable the data transfers, will cause 14902ac6454SAndrew Thompson * the previous one, which is still on the queue, to be 15002ac6454SAndrew Thompson * removed from the queue, and re-inserted after the last 15102ac6454SAndrew Thompson * baud rate programming command, which then gives the 15202ac6454SAndrew Thompson * desired result. 15302ac6454SAndrew Thompson */ 15402ac6454SAndrew Thompson pm = TAILQ_FIRST(&up->up_qhead); 15502ac6454SAndrew Thompson 15602ac6454SAndrew Thompson if (pm) { 15702ac6454SAndrew Thompson DPRINTF("Message pm=%p, cb=%p (enter)\n", 15802ac6454SAndrew Thompson pm, pm->pm_callback); 15902ac6454SAndrew Thompson 16002ac6454SAndrew Thompson (pm->pm_callback) (pm); 16102ac6454SAndrew Thompson 16202ac6454SAndrew Thompson if (pm == TAILQ_FIRST(&up->up_qhead)) { 16302ac6454SAndrew Thompson /* nothing changed */ 16402ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm, pm_qentry); 16502ac6454SAndrew Thompson pm->pm_qentry.tqe_prev = NULL; 16602ac6454SAndrew Thompson } 16702ac6454SAndrew Thompson DPRINTF("Message pm=%p (leave)\n", pm); 16802ac6454SAndrew Thompson 16902ac6454SAndrew Thompson continue; 17002ac6454SAndrew Thompson } 171c0f71e31SHans Petter Selasky /* end of messages - check if anyone is waiting for sync */ 17202ac6454SAndrew Thompson if (up->up_dsleep) { 17302ac6454SAndrew Thompson up->up_dsleep = 0; 1748437751dSAndrew Thompson cv_broadcast(&up->up_drain); 17502ac6454SAndrew Thompson } 17602ac6454SAndrew Thompson up->up_msleep = 1; 1778437751dSAndrew Thompson cv_wait(&up->up_cv, up->up_mtx); 17802ac6454SAndrew Thompson } 17902ac6454SAndrew Thompson 18002ac6454SAndrew Thompson up->up_ptr = NULL; 1818437751dSAndrew Thompson cv_signal(&up->up_cv); 1820eb8d462SHans Petter Selasky USB_MTX_UNLOCK(up->up_mtx); 18351e8c0d6SAndrew Thompson /* Clear the proc pointer if this is the last thread. */ 18451e8c0d6SAndrew Thompson if (--usb_pcount == 0) 18551e8c0d6SAndrew Thompson usbproc = NULL; 18602ac6454SAndrew Thompson 18702ac6454SAndrew Thompson USB_THREAD_EXIT(0); 18802ac6454SAndrew Thompson } 18902ac6454SAndrew Thompson 19002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 191a593f6b8SAndrew Thompson * usb_proc_create 19202ac6454SAndrew Thompson * 19302ac6454SAndrew Thompson * This function will create a process using the given "prio" that can 19402ac6454SAndrew Thompson * execute callbacks. The mutex pointed to by "p_mtx" will be applied 19502ac6454SAndrew Thompson * before calling the callbacks and released after that the callback 19602ac6454SAndrew Thompson * has returned. The structure pointed to by "up" is assumed to be 19702ac6454SAndrew Thompson * zeroed before this function is called. 19802ac6454SAndrew Thompson * 19902ac6454SAndrew Thompson * Return values: 20002ac6454SAndrew Thompson * 0: success 20102ac6454SAndrew Thompson * Else: failure 20202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 20302ac6454SAndrew Thompson int 204a593f6b8SAndrew Thompson usb_proc_create(struct usb_process *up, struct mtx *p_mtx, 20502ac6454SAndrew Thompson const char *pmesg, uint8_t prio) 20602ac6454SAndrew Thompson { 20702ac6454SAndrew Thompson up->up_mtx = p_mtx; 20802ac6454SAndrew Thompson up->up_prio = prio; 20902ac6454SAndrew Thompson 21002ac6454SAndrew Thompson TAILQ_INIT(&up->up_qhead); 21102ac6454SAndrew Thompson 212532b1952SAndrew Thompson cv_init(&up->up_cv, "-"); 213532b1952SAndrew Thompson cv_init(&up->up_drain, "usbdrain"); 21402ac6454SAndrew Thompson 215760bc48eSAndrew Thompson if (USB_THREAD_CREATE(&usb_process, up, 2161bdfff22SAndriy Gapon &up->up_ptr, "%s", pmesg)) { 21702ac6454SAndrew Thompson DPRINTFN(0, "Unable to create USB process."); 21802ac6454SAndrew Thompson up->up_ptr = NULL; 21902ac6454SAndrew Thompson goto error; 22002ac6454SAndrew Thompson } 22151e8c0d6SAndrew Thompson usb_pcount++; 22202ac6454SAndrew Thompson return (0); 22302ac6454SAndrew Thompson 22402ac6454SAndrew Thompson error: 225a593f6b8SAndrew Thompson usb_proc_free(up); 22602ac6454SAndrew Thompson return (ENOMEM); 22702ac6454SAndrew Thompson } 22802ac6454SAndrew Thompson 22902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 230a593f6b8SAndrew Thompson * usb_proc_free 23102ac6454SAndrew Thompson * 23202ac6454SAndrew Thompson * NOTE: If the structure pointed to by "up" is all zero, this 23302ac6454SAndrew Thompson * function does nothing. 23402ac6454SAndrew Thompson * 23502ac6454SAndrew Thompson * NOTE: Messages that are pending on the process queue will not be 23602ac6454SAndrew Thompson * removed nor called. 23702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 23802ac6454SAndrew Thompson void 239a593f6b8SAndrew Thompson usb_proc_free(struct usb_process *up) 24002ac6454SAndrew Thompson { 24102ac6454SAndrew Thompson /* check if not initialised */ 24202ac6454SAndrew Thompson if (up->up_mtx == NULL) 24302ac6454SAndrew Thompson return; 24402ac6454SAndrew Thompson 245a593f6b8SAndrew Thompson usb_proc_drain(up); 24602ac6454SAndrew Thompson 2478437751dSAndrew Thompson cv_destroy(&up->up_cv); 2488437751dSAndrew Thompson cv_destroy(&up->up_drain); 24902ac6454SAndrew Thompson 25002ac6454SAndrew Thompson /* make sure that we do not enter here again */ 25102ac6454SAndrew Thompson up->up_mtx = NULL; 25202ac6454SAndrew Thompson } 25302ac6454SAndrew Thompson 25402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 255a593f6b8SAndrew Thompson * usb_proc_msignal 25602ac6454SAndrew Thompson * 25702ac6454SAndrew Thompson * This function will queue one of the passed USB process messages on 25802ac6454SAndrew Thompson * the USB process queue. The first message that is not already queued 25902ac6454SAndrew Thompson * will get queued. If both messages are already queued the one queued 26002ac6454SAndrew Thompson * last will be removed from the queue and queued in the end. The USB 26102ac6454SAndrew Thompson * process mutex must be locked when calling this function. This 26202ac6454SAndrew Thompson * function exploits the fact that a process can only do one callback 26302ac6454SAndrew Thompson * at a time. The message that was queued is returned. 26402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 26502ac6454SAndrew Thompson void * 266a593f6b8SAndrew Thompson usb_proc_msignal(struct usb_process *up, void *_pm0, void *_pm1) 26702ac6454SAndrew Thompson { 268760bc48eSAndrew Thompson struct usb_proc_msg *pm0 = _pm0; 269760bc48eSAndrew Thompson struct usb_proc_msg *pm1 = _pm1; 270760bc48eSAndrew Thompson struct usb_proc_msg *pm2; 271f9cb546cSAndrew Thompson usb_size_t d; 27202ac6454SAndrew Thompson uint8_t t; 27302ac6454SAndrew Thompson 2740eb8d462SHans Petter Selasky /* check if gone or in polling mode, return dummy value */ 2750eb8d462SHans Petter Selasky if (up->up_gone != 0 || 2760eb8d462SHans Petter Selasky USB_IN_POLLING_MODE_FUNC() != 0) 27702ac6454SAndrew Thompson return (_pm0); 27802ac6454SAndrew Thompson 2790eb8d462SHans Petter Selasky USB_MTX_ASSERT(up->up_mtx, MA_OWNED); 28002ac6454SAndrew Thompson 28102ac6454SAndrew Thompson t = 0; 28202ac6454SAndrew Thompson 28302ac6454SAndrew Thompson if (pm0->pm_qentry.tqe_prev) { 28402ac6454SAndrew Thompson t |= 1; 28502ac6454SAndrew Thompson } 28602ac6454SAndrew Thompson if (pm1->pm_qentry.tqe_prev) { 28702ac6454SAndrew Thompson t |= 2; 28802ac6454SAndrew Thompson } 28902ac6454SAndrew Thompson if (t == 0) { 29002ac6454SAndrew Thompson /* 29102ac6454SAndrew Thompson * No entries are queued. Queue "pm0" and use the existing 29202ac6454SAndrew Thompson * message number. 29302ac6454SAndrew Thompson */ 29402ac6454SAndrew Thompson pm2 = pm0; 29502ac6454SAndrew Thompson } else if (t == 1) { 29602ac6454SAndrew Thompson /* Check if we need to increment the message number. */ 29702ac6454SAndrew Thompson if (pm0->pm_num == up->up_msg_num) { 29802ac6454SAndrew Thompson up->up_msg_num++; 29902ac6454SAndrew Thompson } 30002ac6454SAndrew Thompson pm2 = pm1; 30102ac6454SAndrew Thompson } else if (t == 2) { 30202ac6454SAndrew Thompson /* Check if we need to increment the message number. */ 30302ac6454SAndrew Thompson if (pm1->pm_num == up->up_msg_num) { 30402ac6454SAndrew Thompson up->up_msg_num++; 30502ac6454SAndrew Thompson } 30602ac6454SAndrew Thompson pm2 = pm0; 30702ac6454SAndrew Thompson } else if (t == 3) { 30802ac6454SAndrew Thompson /* 30902ac6454SAndrew Thompson * Both entries are queued. Re-queue the entry closest to 31002ac6454SAndrew Thompson * the end. 31102ac6454SAndrew Thompson */ 31202ac6454SAndrew Thompson d = (pm1->pm_num - pm0->pm_num); 31302ac6454SAndrew Thompson 31402ac6454SAndrew Thompson /* Check sign after subtraction */ 31502ac6454SAndrew Thompson if (d & 0x80000000) { 31602ac6454SAndrew Thompson pm2 = pm0; 31702ac6454SAndrew Thompson } else { 31802ac6454SAndrew Thompson pm2 = pm1; 31902ac6454SAndrew Thompson } 32002ac6454SAndrew Thompson 32102ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm2, pm_qentry); 32202ac6454SAndrew Thompson } else { 32302ac6454SAndrew Thompson pm2 = NULL; /* panic - should not happen */ 32402ac6454SAndrew Thompson } 32502ac6454SAndrew Thompson 32602ac6454SAndrew Thompson DPRINTF(" t=%u, num=%u\n", t, up->up_msg_num); 32702ac6454SAndrew Thompson 32802ac6454SAndrew Thompson /* Put message last on queue */ 32902ac6454SAndrew Thompson 33002ac6454SAndrew Thompson pm2->pm_num = up->up_msg_num; 33102ac6454SAndrew Thompson TAILQ_INSERT_TAIL(&up->up_qhead, pm2, pm_qentry); 33202ac6454SAndrew Thompson 33302ac6454SAndrew Thompson /* Check if we need to wakeup the USB process. */ 33402ac6454SAndrew Thompson 33502ac6454SAndrew Thompson if (up->up_msleep) { 33602ac6454SAndrew Thompson up->up_msleep = 0; /* save "cv_signal()" calls */ 3378437751dSAndrew Thompson cv_signal(&up->up_cv); 33802ac6454SAndrew Thompson } 33902ac6454SAndrew Thompson return (pm2); 34002ac6454SAndrew Thompson } 34102ac6454SAndrew Thompson 34202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 343a593f6b8SAndrew Thompson * usb_proc_is_gone 34402ac6454SAndrew Thompson * 34502ac6454SAndrew Thompson * Return values: 34602ac6454SAndrew Thompson * 0: USB process is running 34702ac6454SAndrew Thompson * Else: USB process is tearing down 34802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 34902ac6454SAndrew Thompson uint8_t 350a593f6b8SAndrew Thompson usb_proc_is_gone(struct usb_process *up) 35102ac6454SAndrew Thompson { 35202ac6454SAndrew Thompson if (up->up_gone) 35302ac6454SAndrew Thompson return (1); 35402ac6454SAndrew Thompson 3550c38ca8cSHans Petter Selasky /* 3560c38ca8cSHans Petter Selasky * Allow calls when up_mtx is NULL, before the USB process 3570c38ca8cSHans Petter Selasky * structure is initialised. 3580c38ca8cSHans Petter Selasky */ 3590c38ca8cSHans Petter Selasky if (up->up_mtx != NULL) 3600eb8d462SHans Petter Selasky USB_MTX_ASSERT(up->up_mtx, MA_OWNED); 36102ac6454SAndrew Thompson return (0); 36202ac6454SAndrew Thompson } 36302ac6454SAndrew Thompson 364*729eb176SKyle Evans static int 365*729eb176SKyle Evans usb_proc_mwait_impl(struct usb_process *up, void *_pm0, void *_pm1, 366*729eb176SKyle Evans bool interruptible) 36702ac6454SAndrew Thompson { 368760bc48eSAndrew Thompson struct usb_proc_msg *pm0 = _pm0; 369760bc48eSAndrew Thompson struct usb_proc_msg *pm1 = _pm1; 370*729eb176SKyle Evans int error; 37102ac6454SAndrew Thompson 37202ac6454SAndrew Thompson /* check if gone */ 37302ac6454SAndrew Thompson if (up->up_gone) 374*729eb176SKyle Evans return (ENXIO); 37502ac6454SAndrew Thompson 3760eb8d462SHans Petter Selasky USB_MTX_ASSERT(up->up_mtx, MA_OWNED); 37702ac6454SAndrew Thompson 378*729eb176SKyle Evans error = 0; 37902ac6454SAndrew Thompson if (up->up_curtd == curthread) { 38002ac6454SAndrew Thompson /* Just remove the messages from the queue. */ 38102ac6454SAndrew Thompson if (pm0->pm_qentry.tqe_prev) { 38202ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm0, pm_qentry); 38302ac6454SAndrew Thompson pm0->pm_qentry.tqe_prev = NULL; 38402ac6454SAndrew Thompson } 38502ac6454SAndrew Thompson if (pm1->pm_qentry.tqe_prev) { 38602ac6454SAndrew Thompson TAILQ_REMOVE(&up->up_qhead, pm1, pm_qentry); 38702ac6454SAndrew Thompson pm1->pm_qentry.tqe_prev = NULL; 38802ac6454SAndrew Thompson } 38902ac6454SAndrew Thompson } else 390*729eb176SKyle Evans while (error == 0 && (pm0->pm_qentry.tqe_prev || 391*729eb176SKyle Evans pm1->pm_qentry.tqe_prev)) { 39202ac6454SAndrew Thompson /* check if config thread is gone */ 39302ac6454SAndrew Thompson if (up->up_gone) 394*729eb176SKyle Evans return (ENXIO); 39502ac6454SAndrew Thompson up->up_dsleep = 1; 396*729eb176SKyle Evans if (interruptible) { 397*729eb176SKyle Evans error = cv_wait_sig(&up->up_drain, up->up_mtx); 398*729eb176SKyle Evans 399*729eb176SKyle Evans /* 400*729eb176SKyle Evans * The fact that we were interrupted doesn't 401*729eb176SKyle Evans * matter if our goal was accomplished anyways. 402*729eb176SKyle Evans */ 403*729eb176SKyle Evans if (error != 0 && !USB_PROC_MSG_ENQUEUED(pm0) && 404*729eb176SKyle Evans !USB_PROC_MSG_ENQUEUED(pm1)) 405*729eb176SKyle Evans error = 0; 406*729eb176SKyle Evans } else { 4078437751dSAndrew Thompson cv_wait(&up->up_drain, up->up_mtx); 40802ac6454SAndrew Thompson } 40902ac6454SAndrew Thompson } 41002ac6454SAndrew Thompson 411*729eb176SKyle Evans if (error == ERESTART) 412*729eb176SKyle Evans error = EINTR; 413*729eb176SKyle Evans return (error); 414*729eb176SKyle Evans } 415*729eb176SKyle Evans 416*729eb176SKyle Evans /*------------------------------------------------------------------------* 417*729eb176SKyle Evans * usb_proc_mwait 418*729eb176SKyle Evans * 419*729eb176SKyle Evans * This function will return when the USB process message pointed to 420*729eb176SKyle Evans * by "pm" is no longer on a queue. This function must be called 421*729eb176SKyle Evans * having "up->up_mtx" locked. 422*729eb176SKyle Evans *------------------------------------------------------------------------*/ 423*729eb176SKyle Evans void 424*729eb176SKyle Evans usb_proc_mwait(struct usb_process *up, void *_pm0, void *_pm1) 425*729eb176SKyle Evans { 426*729eb176SKyle Evans 427*729eb176SKyle Evans (void)usb_proc_mwait_impl(up, _pm0, _pm1, false); 428*729eb176SKyle Evans } 429*729eb176SKyle Evans 430*729eb176SKyle Evans /*------------------------------------------------------------------------* 431*729eb176SKyle Evans * usb_proc_mwait_sig 432*729eb176SKyle Evans * 433*729eb176SKyle Evans * This function will return when the USB process message pointed to 434*729eb176SKyle Evans * by "pm" is no longer on a queue. This function must be called 435*729eb176SKyle Evans * having "up->up_mtx" locked. This version of usb_proc_mwait is 436*729eb176SKyle Evans * interruptible. 437*729eb176SKyle Evans *------------------------------------------------------------------------*/ 438*729eb176SKyle Evans int 439*729eb176SKyle Evans usb_proc_mwait_sig(struct usb_process *up, void *_pm0, void *_pm1) 440*729eb176SKyle Evans { 441*729eb176SKyle Evans 442*729eb176SKyle Evans return (usb_proc_mwait_impl(up, _pm0, _pm1, true)); 443*729eb176SKyle Evans } 444*729eb176SKyle Evans 44502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 446a593f6b8SAndrew Thompson * usb_proc_drain 44702ac6454SAndrew Thompson * 44802ac6454SAndrew Thompson * This function will tear down an USB process, waiting for the 44902ac6454SAndrew Thompson * currently executing command to return. 45002ac6454SAndrew Thompson * 45102ac6454SAndrew Thompson * NOTE: If the structure pointed to by "up" is all zero, 45202ac6454SAndrew Thompson * this function does nothing. 45302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 45402ac6454SAndrew Thompson void 455a593f6b8SAndrew Thompson usb_proc_drain(struct usb_process *up) 45602ac6454SAndrew Thompson { 45702ac6454SAndrew Thompson /* check if not initialised */ 45802ac6454SAndrew Thompson if (up->up_mtx == NULL) 45902ac6454SAndrew Thompson return; 46002ac6454SAndrew Thompson /* handle special case with Giant */ 46102ac6454SAndrew Thompson if (up->up_mtx != &Giant) 4620eb8d462SHans Petter Selasky USB_MTX_ASSERT(up->up_mtx, MA_NOTOWNED); 46302ac6454SAndrew Thompson 4640eb8d462SHans Petter Selasky USB_MTX_LOCK(up->up_mtx); 46502ac6454SAndrew Thompson 46602ac6454SAndrew Thompson /* Set the gone flag */ 46702ac6454SAndrew Thompson 46802ac6454SAndrew Thompson up->up_gone = 1; 46902ac6454SAndrew Thompson 47002ac6454SAndrew Thompson while (up->up_ptr) { 47102ac6454SAndrew Thompson /* Check if we need to wakeup the USB process */ 47202ac6454SAndrew Thompson 47302ac6454SAndrew Thompson if (up->up_msleep || up->up_csleep) { 47402ac6454SAndrew Thompson up->up_msleep = 0; 47502ac6454SAndrew Thompson up->up_csleep = 0; 4768437751dSAndrew Thompson cv_signal(&up->up_cv); 47702ac6454SAndrew Thompson } 478433ded8bSHans Petter Selasky #ifndef EARLY_AP_STARTUP 47902ac6454SAndrew Thompson /* Check if we are still cold booted */ 48002ac6454SAndrew Thompson if (cold) { 48102ac6454SAndrew Thompson USB_THREAD_SUSPEND(up->up_ptr); 48202ac6454SAndrew Thompson printf("WARNING: A USB process has " 483767cb2e2SAndrew Thompson "been left suspended\n"); 48402ac6454SAndrew Thompson break; 48502ac6454SAndrew Thompson } 486433ded8bSHans Petter Selasky #endif 4878437751dSAndrew Thompson cv_wait(&up->up_cv, up->up_mtx); 48802ac6454SAndrew Thompson } 48902ac6454SAndrew Thompson /* Check if someone is waiting - should not happen */ 49002ac6454SAndrew Thompson 49102ac6454SAndrew Thompson if (up->up_dsleep) { 49202ac6454SAndrew Thompson up->up_dsleep = 0; 4938437751dSAndrew Thompson cv_broadcast(&up->up_drain); 49402ac6454SAndrew Thompson DPRINTF("WARNING: Someone is waiting " 49502ac6454SAndrew Thompson "for USB process drain!\n"); 49602ac6454SAndrew Thompson } 4970eb8d462SHans Petter Selasky USB_MTX_UNLOCK(up->up_mtx); 49802ac6454SAndrew Thompson } 499cb18f7d1SAlfred Perlstein 500cb18f7d1SAlfred Perlstein /*------------------------------------------------------------------------* 501cb18f7d1SAlfred Perlstein * usb_proc_rewakeup 502cb18f7d1SAlfred Perlstein * 5036bccea7cSRebecca Cran * This function is called to re-wakeup the given USB 504cb18f7d1SAlfred Perlstein * process. This usually happens after that the USB system has been in 505cb18f7d1SAlfred Perlstein * polling mode, like during a panic. This function must be called 506cb18f7d1SAlfred Perlstein * having "up->up_mtx" locked. 507cb18f7d1SAlfred Perlstein *------------------------------------------------------------------------*/ 508cb18f7d1SAlfred Perlstein void 509cb18f7d1SAlfred Perlstein usb_proc_rewakeup(struct usb_process *up) 510cb18f7d1SAlfred Perlstein { 511cb18f7d1SAlfred Perlstein /* check if not initialised */ 512cb18f7d1SAlfred Perlstein if (up->up_mtx == NULL) 513cb18f7d1SAlfred Perlstein return; 514cb18f7d1SAlfred Perlstein /* check if gone */ 515cb18f7d1SAlfred Perlstein if (up->up_gone) 516cb18f7d1SAlfred Perlstein return; 517cb18f7d1SAlfred Perlstein 5180eb8d462SHans Petter Selasky USB_MTX_ASSERT(up->up_mtx, MA_OWNED); 519cb18f7d1SAlfred Perlstein 520cb18f7d1SAlfred Perlstein if (up->up_msleep == 0) { 521cb18f7d1SAlfred Perlstein /* re-wakeup */ 522cb18f7d1SAlfred Perlstein cv_signal(&up->up_cv); 523cb18f7d1SAlfred Perlstein } 524cb18f7d1SAlfred Perlstein } 525d008478eSHans Petter Selasky 526d008478eSHans Petter Selasky /*------------------------------------------------------------------------* 527d008478eSHans Petter Selasky * usb_proc_is_called_from 528d008478eSHans Petter Selasky * 529d008478eSHans Petter Selasky * This function will return non-zero if called from inside the USB 530d008478eSHans Petter Selasky * process passed as first argument. Else this function returns zero. 531d008478eSHans Petter Selasky *------------------------------------------------------------------------*/ 532d008478eSHans Petter Selasky int 533d008478eSHans Petter Selasky usb_proc_is_called_from(struct usb_process *up) 534d008478eSHans Petter Selasky { 535d008478eSHans Petter Selasky return (up->up_curtd == curthread); 536d008478eSHans Petter Selasky } 537