From 9936387223ef47e4fdbb438510efd1108a38cc8c Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Thu, 21 Aug 2025 21:48:08 -0400 Subject: [PATCH] freebsd_network: Add everything needed for USB ethernet drivers. Change-Id: I152d3fcdb50b9473f06c2164b9abb38b7c9d7e40 Reviewed-on: https://review.haiku-os.org/c/haiku/+/9598 Reviewed-by: waddlesplash --- src/libs/compat/freebsd_iflib/Jamfile | 1 - .../compat/freebsd_iflib/compat/sys/kthread.h | 27 - src/libs/compat/freebsd_network/Jamfile | 3 + .../compat/dev/usb/net/usb_ethernet.h | 121 ++++ .../compat/dev/usb/usb_haiku.h | 2 + .../compat/dev/usb/usb_process.h | 95 +++ .../freebsd_network/compat/dev/usb/usbdi.h | 33 + .../compat/dev/usb/usbdi_util.h | 37 + .../freebsd_network/compat/sys/condvar.h | 3 + .../freebsd_network/compat/sys/kthread.h | 23 + src/libs/compat/freebsd_network/condvar.cpp | 38 +- .../freebsd_network/dev/usb/usb_ethernet.c | 685 ++++++++++++++++++ .../freebsd_network/dev/usb/usb_process.c | 598 +++++++++++++++ .../kthread.cpp | 0 14 files changed, 1629 insertions(+), 37 deletions(-) delete mode 100644 src/libs/compat/freebsd_iflib/compat/sys/kthread.h create mode 100644 src/libs/compat/freebsd_network/compat/dev/usb/net/usb_ethernet.h create mode 100644 src/libs/compat/freebsd_network/compat/dev/usb/usb_process.h create mode 100644 src/libs/compat/freebsd_network/compat/dev/usb/usbdi_util.h create mode 100644 src/libs/compat/freebsd_network/dev/usb/usb_ethernet.c create mode 100644 src/libs/compat/freebsd_network/dev/usb/usb_process.c rename src/libs/compat/{freebsd_iflib => freebsd_network}/kthread.cpp (100%) diff --git a/src/libs/compat/freebsd_iflib/Jamfile b/src/libs/compat/freebsd_iflib/Jamfile index 21f2d9d717..1afc8a618c 100644 --- a/src/libs/compat/freebsd_iflib/Jamfile +++ b/src/libs/compat/freebsd_iflib/Jamfile @@ -8,7 +8,6 @@ UsePrivateKernelHeaders ; SubDirCcFlags [ FDefines _KERNEL=1 FBSD_DRIVER=1 _XOPEN_SOURCE ALTQ ] ; KernelStaticLibrary freebsd_iflib.a : - kthread.cpp device_if.c kobj.c diff --git a/src/libs/compat/freebsd_iflib/compat/sys/kthread.h b/src/libs/compat/freebsd_iflib/compat/sys/kthread.h deleted file mode 100644 index 416289a6f9..0000000000 --- a/src/libs/compat/freebsd_iflib/compat/sys/kthread.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2018, Haiku, Inc. All rights reserved. - * Distributed under the terms of the MIT License. - */ -#ifndef _FBSD_COMPAT_IFLIB_SYS_KTHREAD_H_ -#define _FBSD_COMPAT_IFLIB_SYS_KTHREAD_H_ - -/* include the real sys/kthread.h */ -#include_next - -#include - - -#define SRQ_BORING 0 - -void sched_prio(struct thread* td, u_char prio); -void sched_add(struct thread* td, int flags); - -int kthread_add(void (*func)(void *), void *arg, void* p, - struct thread** newtdp, int flags, int pages, const char* fmt, ...); -void kthread_exit(); - -#define thread_lock(td) -#define thread_unlock(td) - - -#endif /* _FBSD_COMPAT_IFLIB_SYS_KTHREAD_H_ */ diff --git a/src/libs/compat/freebsd_network/Jamfile b/src/libs/compat/freebsd_network/Jamfile index d9c8817325..639d916345 100644 --- a/src/libs/compat/freebsd_network/Jamfile +++ b/src/libs/compat/freebsd_network/Jamfile @@ -32,6 +32,7 @@ KernelStaticLibrary libfreebsd_network.a : eventhandler.c firmware.c if.c + kthread.cpp libkern.cpp malloc.cpp mbuf.c @@ -53,6 +54,8 @@ KernelStaticLibrary freebsd_usb.a : fbsd_usb_error.c fbsd_usb_util.c fbsd_usb_lookup.c + usb_process.c + usb_ethernet.c usb.cpp usb_util.c ; diff --git a/src/libs/compat/freebsd_network/compat/dev/usb/net/usb_ethernet.h b/src/libs/compat/freebsd_network/compat/dev/usb/net/usb_ethernet.h new file mode 100644 index 0000000000..2c4c8c5b14 --- /dev/null +++ b/src/libs/compat/freebsd_network/compat/dev/usb/net/usb_ethernet.h @@ -0,0 +1,121 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _USB_ETHERNET_H_ +#define _USB_ETHERNET_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct mii_data; +struct usb_ether; +struct usb_device_request; + +typedef void (uether_fn_t)(struct usb_ether *); + +struct usb_ether_methods { + uether_fn_t *ue_attach_post; + uether_fn_t *ue_start; + uether_fn_t *ue_init; + uether_fn_t *ue_stop; + uether_fn_t *ue_setmulti; + uether_fn_t *ue_setpromisc; + uether_fn_t *ue_tick; + int (*ue_mii_upd)(if_t); + void (*ue_mii_sts)(if_t, + struct ifmediareq *); + int (*ue_ioctl)(if_t, u_long, caddr_t); + int (*ue_attach_post_sub)(struct usb_ether *); +}; + +struct usb_ether_cfg_task { + struct usb_proc_msg hdr; + struct usb_ether *ue; +}; + +struct usb_ether { + /* NOTE: the "ue_ifp" pointer must be first --hps */ + if_t ue_ifp; + struct mtx *ue_mtx; + const struct usb_ether_methods *ue_methods; + struct sysctl_oid *ue_sysctl_oid; + void *ue_sc; + struct usb_device *ue_udev; /* used by uether_do_request() */ + device_t ue_dev; + device_t ue_miibus; + + struct usb_process ue_tq; + struct sysctl_ctx_list ue_sysctl_ctx; + struct mbufq ue_rxq; + struct usb_callout ue_watchdog; + struct usb_ether_cfg_task ue_sync_task[2]; + struct usb_ether_cfg_task ue_media_task[2]; + struct usb_ether_cfg_task ue_multi_task[2]; + struct usb_ether_cfg_task ue_promisc_task[2]; + struct usb_ether_cfg_task ue_tick_task[2]; + + int ue_unit; + + /* ethernet address from eeprom */ + uint8_t ue_eaddr[ETHER_ADDR_LEN]; +}; + +#define uether_do_request(ue,req,data,timo) \ + usbd_do_request_proc((ue)->ue_udev,&(ue)->ue_tq,req,data,0,NULL,timo) + +uint8_t uether_pause(struct usb_ether *, unsigned int); +if_t uether_getifp(struct usb_ether *); +struct mii_data *uether_getmii(struct usb_ether *); +void *uether_getsc(struct usb_ether *); +int uether_ifattach(struct usb_ether *); +void uether_ifattach_wait(struct usb_ether *); +void uether_ifdetach(struct usb_ether *); +int uether_ifmedia_upd(if_t); +void uether_init(void *); +int uether_ioctl(if_t, u_long, caddr_t); +struct mbuf *uether_newbuf(void); +int uether_rxmbuf(struct usb_ether *, struct mbuf *, + unsigned); +int uether_rxbuf(struct usb_ether *, + struct usb_page_cache *, + unsigned, unsigned); +void uether_rxflush(struct usb_ether *); +uint8_t uether_is_gone(struct usb_ether *); +void uether_start(if_t); +#endif /* _USB_ETHERNET_H_ */ diff --git a/src/libs/compat/freebsd_network/compat/dev/usb/usb_haiku.h b/src/libs/compat/freebsd_network/compat/dev/usb/usb_haiku.h index b19c62f3da..c0c9e09f8c 100644 --- a/src/libs/compat/freebsd_network/compat/dev/usb/usb_haiku.h +++ b/src/libs/compat/freebsd_network/compat/dev/usb/usb_haiku.h @@ -69,6 +69,8 @@ #define USB_MAX_AUTO_QUIRK 8 /* maximum number of dynamic quirks */ +#define USB_IN_POLLING_MODE_FUNC() 0 + typedef uint32_t usb_timeout_t; /* milliseconds */ typedef uint32_t usb_frlength_t; /* bytes */ typedef uint32_t usb_frcount_t; /* units */ diff --git a/src/libs/compat/freebsd_network/compat/dev/usb/usb_process.h b/src/libs/compat/freebsd_network/compat/dev/usb/usb_process.h new file mode 100644 index 0000000000..b36322c3f7 --- /dev/null +++ b/src/libs/compat/freebsd_network/compat/dev/usb/usb_process.h @@ -0,0 +1,95 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _USB_PROCESS_H_ +#define _USB_PROCESS_H_ + +/* defines */ +#ifdef __HAIKU__ +#define USB_PRI_HIGHEST 20 /* B_URGENT_DISPLAY_PRIORITY */ +#define USB_PRI_HIGH 15 /* B_DISPLAY_PRIORITY */ +#define USB_PRI_MED 10 /* B_NORMAL_PRIORITY */ +#endif + +#define USB_PROC_WAIT_TIMEOUT 2 +#define USB_PROC_WAIT_DRAIN 1 +#define USB_PROC_WAIT_NORMAL 0 + +/* structure prototypes */ + +struct usb_proc_msg; +typedef void (usb_proc_callback_t)(struct usb_proc_msg *); + +struct usb_proc_msg { + TAILQ_ENTRY(usb_proc_msg) pm_qentry; + usb_proc_callback_t *pm_callback; + usb_size_t pm_num; +}; +struct usb_device; + +#define USB_PROC_MSG_ENQUEUED(msg) ((msg)->pm_qentry.tqe_prev != NULL) + +/* + * The following structure defines the USB process. + */ +struct usb_process { + TAILQ_HEAD(, usb_proc_msg) up_qhead; + struct cv up_cv; + struct cv up_drain; + + struct thread *up_ptr; + struct thread *up_curtd; + struct mtx *up_mtx; + + usb_size_t up_msg_num; + + uint8_t up_prio; + uint8_t up_gone; + uint8_t up_msleep; + uint8_t up_csleep; + uint8_t up_dsleep; +}; + +/* prototypes */ + +uint8_t usb_proc_is_gone(struct usb_process *up); +int usb_proc_create(struct usb_process *up, struct mtx *p_mtx, + const char *pmesg, uint8_t prio); +void usb_proc_drain(struct usb_process *up); +void usb_proc_mwait(struct usb_process *up, void *pm0, void *pm1); +int usb_proc_mwait_sig(struct usb_process *up, void *pm0, void *pm1); +void usb_proc_free(struct usb_process *up); +void *usb_proc_msignal(struct usb_process *up, void *pm0, void *pm1); +void usb_proc_rewakeup(struct usb_process *up); +int usb_proc_is_called_from(struct usb_process *up); + +void usb_proc_explore_mwait(struct usb_device *, void *, void *); +void *usb_proc_explore_msignal(struct usb_device *, void *, void *); +void usb_proc_explore_lock(struct usb_device *); +void usb_proc_explore_unlock(struct usb_device *); + +#endif /* _USB_PROCESS_H_ */ diff --git a/src/libs/compat/freebsd_network/compat/dev/usb/usbdi.h b/src/libs/compat/freebsd_network/compat/dev/usb/usbdi.h index 4dc293e201..c5f816e75a 100644 --- a/src/libs/compat/freebsd_network/compat/dev/usb/usbdi.h +++ b/src/libs/compat/freebsd_network/compat/dev/usb/usbdi.h @@ -310,6 +310,39 @@ struct usb_attach_arg { #define UAA_DEV_EJECTING 2 }; +/* + * General purpose locking wrappers to ease supporting + * USB polled mode: + */ +#ifdef INVARIANTS +#define USB_MTX_ASSERT(_m, _t) do { \ + if (!USB_IN_POLLING_MODE_FUNC()) \ + mtx_assert(_m, _t); \ +} while (0) +#else +#define USB_MTX_ASSERT(_m, _t) do { } while (0) +#endif + +#define USB_MTX_LOCK(_m) do { \ + if (!USB_IN_POLLING_MODE_FUNC()) \ + mtx_lock(_m); \ +} while (0) + +#define USB_MTX_UNLOCK(_m) do { \ + if (!USB_IN_POLLING_MODE_FUNC()) \ + mtx_unlock(_m); \ +} while (0) + +#define USB_MTX_LOCK_SPIN(_m) do { \ + if (!USB_IN_POLLING_MODE_FUNC()) \ + mtx_lock_spin(_m); \ +} while (0) + +#define USB_MTX_UNLOCK_SPIN(_m) do { \ + if (!USB_IN_POLLING_MODE_FUNC()) \ + mtx_unlock_spin(_m); \ +} while (0) + /* * The following is a wrapper for the callout structure to ease * porting the code to other platforms. diff --git a/src/libs/compat/freebsd_network/compat/dev/usb/usbdi_util.h b/src/libs/compat/freebsd_network/compat/dev/usb/usbdi_util.h new file mode 100644 index 0000000000..14cb1c29ca --- /dev/null +++ b/src/libs/compat/freebsd_network/compat/dev/usb/usbdi_util.h @@ -0,0 +1,37 @@ +/* + * Copyright 2025, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + */ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2009 Andrew Thompson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _FBSD_COMPAT_USB_USBDI_UTIL_H_ +#define _FBSD_COMPAT_USB_USBDI_UTIL_H_ + +usb_error_t usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc, + struct usb_device_request *req, void *data, uint16_t flags, + uint16_t *actlen, usb_timeout_t timeout); + +#endif /* _FBSD_COMPAT_USB_USBDI_UTIL_H_ */ diff --git a/src/libs/compat/freebsd_network/compat/sys/condvar.h b/src/libs/compat/freebsd_network/compat/sys/condvar.h index 128fc10a19..de824bb2d2 100644 --- a/src/libs/compat/freebsd_network/compat/sys/condvar.h +++ b/src/libs/compat/freebsd_network/compat/sys/condvar.h @@ -25,9 +25,12 @@ struct cv { void cv_init(struct cv*, const char*); void cv_destroy(struct cv*); + void cv_wait(struct cv*, struct mtx*); int cv_timedwait(struct cv*, struct mtx*, int); +int cv_wait_sig(struct cv*, struct mtx*); void cv_signal(struct cv*); +void cv_broadcast(struct cv*); __END_DECLS diff --git a/src/libs/compat/freebsd_network/compat/sys/kthread.h b/src/libs/compat/freebsd_network/compat/sys/kthread.h index 8b13789179..aea7e28549 100644 --- a/src/libs/compat/freebsd_network/compat/sys/kthread.h +++ b/src/libs/compat/freebsd_network/compat/sys/kthread.h @@ -1 +1,24 @@ +/* + * Copyright 2018, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FBSD_COMPAT_IFLIB_SYS_KTHREAD_H_ +#define _FBSD_COMPAT_IFLIB_SYS_KTHREAD_H_ +#include + + +#define SRQ_BORING 0 + +void sched_prio(struct thread* td, u_char prio); +void sched_add(struct thread* td, int flags); + +int kthread_add(void (*func)(void *), void *arg, void* p, + struct thread** newtdp, int flags, int pages, const char* fmt, ...); +void kthread_exit(); + +#define thread_lock(td) +#define thread_unlock(td) + + +#endif /* _FBSD_COMPAT_IFLIB_SYS_KTHREAD_H_ */ diff --git a/src/libs/compat/freebsd_network/condvar.cpp b/src/libs/compat/freebsd_network/condvar.cpp index 082073d3de..88349e496b 100644 --- a/src/libs/compat/freebsd_network/condvar.cpp +++ b/src/libs/compat/freebsd_network/condvar.cpp @@ -25,7 +25,7 @@ cv_init(struct cv* variable, const char* description) void cv_destroy(struct cv* variable) { - __cv_ConditionVariable(variable)->NotifyAll(); + cv_broadcast(variable); // Nothing else to do. } @@ -38,19 +38,23 @@ cv_signal(struct cv* variable) } -int -cv_timedwait(struct cv* variable, struct mtx* mutex, int timeout) +void +cv_broadcast(struct cv* variable) +{ + __cv_ConditionVariable(variable)->NotifyAll(); +} + + +static int +cv_wait_etc(struct cv* variable, struct mtx* mutex, uint32 flags, bigtime_t timeout) { ConditionVariable* condition = __cv_ConditionVariable(variable); - const uint32 flags = timeout ? B_RELATIVE_TIMEOUT : 0; - const bigtime_t bigtimeout = TICKS_2_USEC(timeout); int status; - if (mutex->type == MTX_RECURSE) { // Special case: let the ConditionVariable handle switching recursive locks. status = condition->Wait(&mutex->u.recursive, - flags, bigtimeout); + flags, timeout); return status; } @@ -59,15 +63,31 @@ cv_timedwait(struct cv* variable, struct mtx* mutex, int timeout) mtx_unlock(mutex); - status = entry.Wait(flags, bigtimeout); + status = entry.Wait(flags, timeout); mtx_lock(mutex); return status; } +int +cv_timedwait(struct cv* variable, struct mtx* mutex, int timeout) +{ + const uint32 flags = timeout ? B_RELATIVE_TIMEOUT : 0; + const bigtime_t bigtimeout = TICKS_2_USEC(timeout); + return cv_wait_etc(variable, mutex, flags, bigtimeout); +} + + void cv_wait(struct cv* variable, struct mtx* mutex) { - cv_timedwait(variable, mutex, 0); + cv_wait_etc(variable, mutex, 0, 0); +} + + +int +cv_wait_sig(struct cv* variable, struct mtx* mutex) +{ + return cv_wait_etc(variable, mutex, B_CAN_INTERRUPT, 0); } diff --git a/src/libs/compat/freebsd_network/dev/usb/usb_ethernet.c b/src/libs/compat/freebsd_network/dev/usb/usb_ethernet.c new file mode 100644 index 0000000000..d2dd83a59d --- /dev/null +++ b/src/libs/compat/freebsd_network/dev/usb/usb_ethernet.c @@ -0,0 +1,685 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2009 Andrew Thompson (thompsa@FreeBSD.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#ifdef __HAIKU__ +#define bus_topo_lock() +#define bus_topo_unlock() +#endif + +#define UE_LOCK(_ue) mtx_lock((_ue)->ue_mtx) +#define UE_UNLOCK(_ue) mtx_unlock((_ue)->ue_mtx) +#define UE_LOCK_ASSERT(_ue, t) mtx_assert((_ue)->ue_mtx, t) + +static struct unrhdr *ueunit; + +static usb_proc_callback_t ue_attach_post_task; +static usb_proc_callback_t ue_promisc_task; +static usb_proc_callback_t ue_setmulti_task; +static usb_proc_callback_t ue_ifmedia_task; +static usb_proc_callback_t ue_tick_task; +static usb_proc_callback_t ue_start_task; +static usb_proc_callback_t ue_stop_task; + +static void ue_init(void *); +static void ue_start(if_t); +static int ue_ifmedia_upd(if_t); +static void ue_watchdog(void *); + +/* + * Return values: + * 0: success + * Else: device has been detached + */ +uint8_t +uether_pause(struct usb_ether *ue, unsigned _ticks) +{ + if (usb_proc_is_gone(&ue->ue_tq)) { + /* nothing to do */ + return (1); + } + usb_pause_mtx(ue->ue_mtx, _ticks); + return (0); +} + +static void +ue_queue_command(struct usb_ether *ue, + usb_proc_callback_t *fn, + struct usb_proc_msg *t0, struct usb_proc_msg *t1) +{ + struct usb_ether_cfg_task *task; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + if (usb_proc_is_gone(&ue->ue_tq)) { + return; /* nothing to do */ + } + /* + * NOTE: The task cannot get executed before we drop the + * "sc_mtx" mutex. It is safe to update fields in the message + * structure after that the message got queued. + */ + task = (struct usb_ether_cfg_task *) + usb_proc_msignal(&ue->ue_tq, t0, t1); + + /* Setup callback and self pointers */ + task->hdr.pm_callback = fn; + task->ue = ue; + + /* + * Start and stop must be synchronous! + */ + if ((fn == ue_start_task) || (fn == ue_stop_task) +#ifdef __HAIKU__ + || (fn == ue_attach_post_task) +#endif + ) + usb_proc_mwait(&ue->ue_tq, t0, t1); +} + +if_t +uether_getifp(struct usb_ether *ue) +{ + return (ue->ue_ifp); +} + +struct mii_data * +uether_getmii(struct usb_ether *ue) +{ + return (device_get_softc(ue->ue_miibus)); +} + +void * +uether_getsc(struct usb_ether *ue) +{ + return (ue->ue_sc); +} + +#ifndef __HAIKU__ +static int +ue_sysctl_parent(SYSCTL_HANDLER_ARGS) +{ + struct usb_ether *ue = arg1; + const char *name; + + name = device_get_nameunit(ue->ue_dev); + return SYSCTL_OUT_STR(req, name); +} +#endif + +int +uether_ifattach(struct usb_ether *ue) +{ + int error; + + /* check some critical parameters */ + if ((ue->ue_dev == NULL) || + (ue->ue_udev == NULL) || + (ue->ue_mtx == NULL) || + (ue->ue_methods == NULL)) + return (EINVAL); + + error = usb_proc_create(&ue->ue_tq, ue->ue_mtx, + device_get_nameunit(ue->ue_dev), USB_PRI_MED); + if (error) { + device_printf(ue->ue_dev, "could not setup taskqueue\n"); + goto error; + } + + /* fork rest of the attach code */ + UE_LOCK(ue); + ue_queue_command(ue, ue_attach_post_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + UE_UNLOCK(ue); + +error: + return (error); +} + +void +uether_ifattach_wait(struct usb_ether *ue) +{ + + UE_LOCK(ue); + usb_proc_mwait(&ue->ue_tq, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + UE_UNLOCK(ue); +} + +static void +ue_attach_post_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + if_t ifp; + int error; + char num[14]; /* sufficient for 32 bits */ + + /* first call driver's post attach routine */ + ue->ue_methods->ue_attach_post(ue); + + UE_UNLOCK(ue); + + ue->ue_unit = alloc_unr(ueunit); + usb_callout_init_mtx(&ue->ue_watchdog, ue->ue_mtx, 0); + sysctl_ctx_init(&ue->ue_sysctl_ctx); + mbufq_init(&ue->ue_rxq, 0 /* unlimited length */); + + error = 0; + ifp = if_alloc(IFT_ETHER); + if_setsoftc(ifp, ue); + if_initname(ifp, "ue", ue->ue_unit); + if (ue->ue_methods->ue_attach_post_sub != NULL) { + ue->ue_ifp = ifp; + error = ue->ue_methods->ue_attach_post_sub(ue); + } else { + if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); + if (ue->ue_methods->ue_ioctl != NULL) + if_setioctlfn(ifp, ue->ue_methods->ue_ioctl); + else + if_setioctlfn(ifp, uether_ioctl); + if_setstartfn(ifp, ue_start); + if_setinitfn(ifp, ue_init); + if_setsendqlen(ifp, ifqmaxlen); + if_setsendqready(ifp); + ue->ue_ifp = ifp; + + if (ue->ue_methods->ue_mii_upd != NULL && + ue->ue_methods->ue_mii_sts != NULL) { + bus_topo_lock(); + error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp, + ue_ifmedia_upd, ue->ue_methods->ue_mii_sts, + BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); + bus_topo_unlock(); + } + } + + if (error) { + device_printf(ue->ue_dev, "attaching PHYs failed\n"); + goto fail; + } + + if_printf(ifp, " on %s\n", device_get_nameunit(ue->ue_dev)); + ether_ifattach(ifp, ue->ue_eaddr); + /* Tell upper layer we support VLAN oversized frames. */ + if (if_getcapabilities(ifp) & IFCAP_VLAN_MTU) + if_setifheaderlen(ifp, sizeof(struct ether_vlan_header)); + + snprintf(num, sizeof(num), "%u", ue->ue_unit); +#ifndef __HAIKU__ + ue->ue_sysctl_oid = SYSCTL_ADD_NODE(&ue->ue_sysctl_ctx, + &SYSCTL_NODE_CHILDREN(_net, ue), + OID_AUTO, num, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, ""); + SYSCTL_ADD_PROC(&ue->ue_sysctl_ctx, + SYSCTL_CHILDREN(ue->ue_sysctl_oid), OID_AUTO, "%parent", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, ue, 0, + ue_sysctl_parent, "A", "parent device"); +#endif + + UE_LOCK(ue); + return; + +fail: + /* drain mbuf queue */ + mbufq_drain(&ue->ue_rxq); + + /* free unit */ + free_unr(ueunit, ue->ue_unit); + if (ue->ue_ifp != NULL) { + if_free(ue->ue_ifp); + ue->ue_ifp = NULL; + } + UE_LOCK(ue); + return; +} + +void +uether_ifdetach(struct usb_ether *ue) +{ + if_t ifp; + + /* wait for any post attach or other command to complete */ + usb_proc_drain(&ue->ue_tq); + + /* read "ifnet" pointer after taskqueue drain */ + ifp = ue->ue_ifp; + + if (ifp != NULL) { + /* we are not running any more */ + UE_LOCK(ue); + if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING); + UE_UNLOCK(ue); + + /* drain any callouts */ + usb_callout_drain(&ue->ue_watchdog); + + /* + * Detach ethernet first to stop miibus calls from + * user-space: + */ + ether_ifdetach(ifp); + + /* detach miibus */ + if (ue->ue_miibus != NULL) { + bus_topo_lock(); + device_delete_child(ue->ue_dev, ue->ue_miibus); + bus_topo_unlock(); + } + + /* free interface instance */ + if_free(ifp); + + /* free sysctl */ + sysctl_ctx_free(&ue->ue_sysctl_ctx); + + /* drain mbuf queue */ + mbufq_drain(&ue->ue_rxq); + + /* free unit */ + free_unr(ueunit, ue->ue_unit); + } + + /* free taskqueue, if any */ + usb_proc_free(&ue->ue_tq); +} + +uint8_t +uether_is_gone(struct usb_ether *ue) +{ + return (usb_proc_is_gone(&ue->ue_tq)); +} + +void +uether_init(void *arg) +{ + + ue_init(arg); +} + +static void +ue_init(void *arg) +{ + struct usb_ether *ue = arg; + + UE_LOCK(ue); + ue_queue_command(ue, ue_start_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + UE_UNLOCK(ue); +} + +static void +ue_start_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + if_t ifp = ue->ue_ifp; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + ue->ue_methods->ue_init(ue); + + if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) + return; + + if (ue->ue_methods->ue_tick != NULL) + usb_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue); +} + +static void +ue_stop_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + usb_callout_stop(&ue->ue_watchdog); + + ue->ue_methods->ue_stop(ue); +} + +void +uether_start(if_t ifp) +{ + + ue_start(ifp); +} + +static void +ue_start(if_t ifp) +{ + struct usb_ether *ue = if_getsoftc(ifp); + + if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) + return; + + UE_LOCK(ue); + ue->ue_methods->ue_start(ue); + UE_UNLOCK(ue); +} + +static void +ue_promisc_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + + ue->ue_methods->ue_setpromisc(ue); +} + +static void +ue_setmulti_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + + ue->ue_methods->ue_setmulti(ue); +} + +int +uether_ifmedia_upd(if_t ifp) +{ + + return (ue_ifmedia_upd(ifp)); +} + +static int +ue_ifmedia_upd(if_t ifp) +{ + struct usb_ether *ue = if_getsoftc(ifp); + + /* Defer to process context */ + UE_LOCK(ue); + ue_queue_command(ue, ue_ifmedia_task, + &ue->ue_media_task[0].hdr, + &ue->ue_media_task[1].hdr); + UE_UNLOCK(ue); + + return (0); +} + +static void +ue_ifmedia_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + if_t ifp = ue->ue_ifp; + + ue->ue_methods->ue_mii_upd(ifp); +} + +static void +ue_watchdog(void *arg) +{ + struct usb_ether *ue = arg; + if_t ifp = ue->ue_ifp; + + if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) + return; + + ue_queue_command(ue, ue_tick_task, + &ue->ue_tick_task[0].hdr, + &ue->ue_tick_task[1].hdr); + + usb_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue); +} + +static void +ue_tick_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + if_t ifp = ue->ue_ifp; + + if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) + return; + + ue->ue_methods->ue_tick(ue); +} + +int +uether_ioctl(if_t ifp, u_long command, caddr_t data) +{ + struct usb_ether *ue = if_getsoftc(ifp); + struct ifreq *ifr = (struct ifreq *)data; + struct mii_data *mii; + int error = 0; + + switch (command) { + case SIOCSIFFLAGS: + UE_LOCK(ue); + if (if_getflags(ifp) & IFF_UP) { + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) + ue_queue_command(ue, ue_promisc_task, + &ue->ue_promisc_task[0].hdr, + &ue->ue_promisc_task[1].hdr); + else + ue_queue_command(ue, ue_start_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + } else { + ue_queue_command(ue, ue_stop_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + } + UE_UNLOCK(ue); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + UE_LOCK(ue); + ue_queue_command(ue, ue_setmulti_task, + &ue->ue_multi_task[0].hdr, + &ue->ue_multi_task[1].hdr); + UE_UNLOCK(ue); + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + if (ue->ue_miibus != NULL) { + mii = device_get_softc(ue->ue_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + } else + error = ether_ioctl(ifp, command, data); + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + return (error); +} + +#ifdef __HAIKU__ +static void +init_ue_unrhdr(void *arg) +{ + // TODO: RadixBitmap can't handle INT_MAX limit. + ueunit = new_unrhdr(0, INT_MAX/16, NULL); +} + +static void +uninit_ue_unrhdr(void *arg) +{ + delete_unrhdr(ueunit); +} + +SYSINIT(init_ue_unrhdr, SI_SUB_INIT_IF, SI_ORDER_SECOND, init_ue_unrhdr, NULL); +SYSUNINIT(init_ue_unrhdr, SI_SUB_INIT_IF, SI_ORDER_SECOND, uninit_ue_unrhdr, NULL); +#else +static int +uether_modevent(module_t mod, int type, void *data) +{ + + switch (type) { + case MOD_LOAD: + ueunit = new_unrhdr(0, INT_MAX, NULL); + break; + case MOD_UNLOAD: + break; + default: + return (EOPNOTSUPP); + } + return (0); +} +static moduledata_t uether_mod = { + "uether", + uether_modevent, + 0 +}; +#endif + +struct mbuf * +uether_newbuf(void) +{ + struct mbuf *m_new; + + m_new = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m_new == NULL) + return (NULL); + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + + m_adj(m_new, ETHER_ALIGN); + return (m_new); +} + +int +uether_rxmbuf(struct usb_ether *ue, struct mbuf *m, + unsigned len) +{ + if_t ifp = ue->ue_ifp; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + /* finalize mbuf */ + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + + /* enqueue for later when the lock can be released */ + (void)mbufq_enqueue(&ue->ue_rxq, m); + return (0); +} + +int +uether_rxbuf(struct usb_ether *ue, struct usb_page_cache *pc, + unsigned offset, unsigned len) +{ + if_t ifp = ue->ue_ifp; + struct mbuf *m; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) + return (1); + + m = uether_newbuf(); + if (m == NULL) { + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); + return (ENOMEM); + } + + usbd_copy_out(pc, offset, mtod(m, uint8_t *), len); + + /* finalize mbuf */ + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + + /* enqueue for later when the lock can be released */ + (void)mbufq_enqueue(&ue->ue_rxq, m); + return (0); +} + +void +uether_rxflush(struct usb_ether *ue) +{ + if_t ifp = ue->ue_ifp; + struct epoch_tracker et; + struct mbuf *m, *n; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + n = mbufq_flush(&ue->ue_rxq); + UE_UNLOCK(ue); + NET_EPOCH_ENTER(et); + while ((m = n) != NULL) { + n = STAILQ_NEXT(m, m_stailqpkt); + m->m_nextpkt = NULL; + if_input(ifp, m); + } + NET_EPOCH_EXIT(et); + UE_LOCK(ue); +} + +#ifndef __HAIKU__ +/* + * USB net drivers are run by DRIVER_MODULE() thus SI_SUB_DRIVERS, + * SI_ORDER_MIDDLE. Run uether after that. + */ +DECLARE_MODULE(uether, uether_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); +MODULE_VERSION(uether, 1); +#endif diff --git a/src/libs/compat/freebsd_network/dev/usb/usb_process.c b/src/libs/compat/freebsd_network/dev/usb/usb_process.c new file mode 100644 index 0000000000..66a9b81291 --- /dev/null +++ b/src/libs/compat/freebsd_network/dev/usb/usb_process.c @@ -0,0 +1,598 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef USB_GLOBAL_INCLUDE_FILE +#include USB_GLOBAL_INCLUDE_FILE +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb_proc_debug +#include +//#include + +#include +#include +//#include +#endif /* USB_GLOBAL_INCLUDE_FILE */ + +static struct proc *usbproc; +static int usb_pcount; +#ifdef __HAIKU__ +#undef curthread +#define curthread ((struct thread*)((uintptr_t)find_thread(NULL))) +static int +USB_THREAD_CREATE(void (*func)(void *), void *arg, struct thread **newtdp, const char* fmt, const char* fmtarg) +{ + int ret = kthread_add(func, arg, NULL, newtdp, 0, 0, fmt, fmtarg); + if (ret == 0) + sched_add(*newtdp, 0); + return ret; +} +#define USB_THREAD_SUSPEND_CHECK() +#define USB_THREAD_SUSPEND(p) panic("kthread_suspend not supported") +#else +#define USB_THREAD_CREATE(f, s, p, ...) \ + kproc_kthread_add((f), (s), &usbproc, (p), RFHIGHPID, \ + 0, "usb", __VA_ARGS__) +#define USB_THREAD_SUSPEND_CHECK() kthread_suspend_check() +#define USB_THREAD_SUSPEND(p) kthread_suspend(p,0) +#endif +#define USB_THREAD_EXIT(err) kthread_exit() + +#ifdef USB_DEBUG +static int usb_proc_debug; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, proc, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + "USB process"); +SYSCTL_INT(_hw_usb_proc, OID_AUTO, debug, CTLFLAG_RWTUN, &usb_proc_debug, 0, + "Debug level"); +#endif + +/*------------------------------------------------------------------------* + * usb_process + * + * This function is the USB process dispatcher. + *------------------------------------------------------------------------*/ +static void +usb_process(void *arg) +{ + struct usb_process *up = arg; + struct usb_proc_msg *pm; + struct thread *td; + + /* in case of attach error, check for suspended */ + USB_THREAD_SUSPEND_CHECK(); + + /* adjust priority */ + td = curthread; +#ifndef __HAIKU__ + thread_lock(td); +#endif + sched_prio(td, up->up_prio); +#ifndef __HAIKU__ + thread_unlock(td); +#endif + + USB_MTX_LOCK(up->up_mtx); + + up->up_curtd = td; + + while (1) { + if (up->up_gone) + break; + + /* + * NOTE to reimplementors: dequeueing a command from the + * "used" queue and executing it must be atomic, with regard + * to the "up_mtx" mutex. That means any attempt to queue a + * command by another thread must be blocked until either: + * + * 1) the command sleeps + * + * 2) the command returns + * + * Here is a practical example that shows how this helps + * solving a problem: + * + * Assume that you want to set the baud rate on a USB serial + * device. During the programming of the device you don't + * want to receive nor transmit any data, because it will be + * garbage most likely anyway. The programming of our USB + * device takes 20 milliseconds and it needs to call + * functions that sleep. + * + * Non-working solution: Before we queue the programming + * command, we stop transmission and reception of data. Then + * we queue a programming command. At the end of the + * programming command we enable transmission and reception + * of data. + * + * Problem: If a second programming command is queued while the + * first one is sleeping, we end up enabling transmission + * and reception of data too early. + * + * Working solution: Before we queue the programming command, + * we stop transmission and reception of data. Then we queue + * a programming command. Then we queue a second command + * that only enables transmission and reception of data. + * + * Why it works: If a second programming command is queued + * while the first one is sleeping, then the queueing of a + * second command to enable the data transfers, will cause + * the previous one, which is still on the queue, to be + * removed from the queue, and re-inserted after the last + * baud rate programming command, which then gives the + * desired result. + */ + pm = TAILQ_FIRST(&up->up_qhead); + + if (pm) { + DPRINTF("Message pm=%p, cb=%p (enter)\n", + pm, pm->pm_callback); + + (pm->pm_callback) (pm); + + if (pm == TAILQ_FIRST(&up->up_qhead)) { + /* nothing changed */ + TAILQ_REMOVE(&up->up_qhead, pm, pm_qentry); + pm->pm_qentry.tqe_prev = NULL; + } + DPRINTF("Message pm=%p (leave)\n", pm); + + continue; + } + /* end of messages - check if anyone is waiting for sync */ + if (up->up_dsleep) { + up->up_dsleep = 0; + cv_broadcast(&up->up_drain); + } + up->up_msleep = 1; + cv_wait(&up->up_cv, up->up_mtx); + } + + up->up_ptr = NULL; + cv_signal(&up->up_cv); + USB_MTX_UNLOCK(up->up_mtx); + /* Clear the proc pointer if this is the last thread. */ + if (--usb_pcount == 0) + usbproc = NULL; + + USB_THREAD_EXIT(0); +} + +/*------------------------------------------------------------------------* + * usb_proc_create + * + * This function will create a process using the given "prio" that can + * execute callbacks. The mutex pointed to by "p_mtx" will be applied + * before calling the callbacks and released after that the callback + * has returned. The structure pointed to by "up" is assumed to be + * zeroed before this function is called. + * + * Return values: + * 0: success + * Else: failure + *------------------------------------------------------------------------*/ +int +usb_proc_create(struct usb_process *up, struct mtx *p_mtx, + const char *pmesg, uint8_t prio) +{ + up->up_mtx = p_mtx; + up->up_prio = prio; + + TAILQ_INIT(&up->up_qhead); + + cv_init(&up->up_cv, "-"); + cv_init(&up->up_drain, "usbdrain"); + + if (USB_THREAD_CREATE(&usb_process, up, + &up->up_ptr, "%s", pmesg)) { + DPRINTFN(0, "Unable to create USB process."); + up->up_ptr = NULL; + goto error; + } + usb_pcount++; + return (0); + +error: + usb_proc_free(up); + return (ENOMEM); +} + +/*------------------------------------------------------------------------* + * usb_proc_free + * + * NOTE: If the structure pointed to by "up" is all zero, this + * function does nothing. + * + * NOTE: Messages that are pending on the process queue will not be + * removed nor called. + *------------------------------------------------------------------------*/ +void +usb_proc_free(struct usb_process *up) +{ + /* check if not initialised */ + if (up->up_mtx == NULL) + return; + + usb_proc_drain(up); + + cv_destroy(&up->up_cv); + cv_destroy(&up->up_drain); + + /* make sure that we do not enter here again */ + up->up_mtx = NULL; +} + +/*------------------------------------------------------------------------* + * usb_proc_msignal + * + * This function will queue one of the passed USB process messages on + * the USB process queue. The first message that is not already queued + * will get queued. If both messages are already queued the one queued + * last will be removed from the queue and queued in the end. The USB + * process mutex must be locked when calling this function. This + * function exploits the fact that a process can only do one callback + * at a time. The message that was queued is returned. + *------------------------------------------------------------------------*/ +void * +usb_proc_msignal(struct usb_process *up, void *_pm0, void *_pm1) +{ + struct usb_proc_msg *pm0 = _pm0; + struct usb_proc_msg *pm1 = _pm1; + struct usb_proc_msg *pm2; + usb_size_t d; + uint8_t t; + + /* check if gone or in polling mode, return dummy value */ + if (up->up_gone != 0 || + USB_IN_POLLING_MODE_FUNC() != 0) + return (_pm0); + + USB_MTX_ASSERT(up->up_mtx, MA_OWNED); + + t = 0; + + if (pm0->pm_qentry.tqe_prev) { + t |= 1; + } + if (pm1->pm_qentry.tqe_prev) { + t |= 2; + } + if (t == 0) { + /* + * No entries are queued. Queue "pm0" and use the existing + * message number. + */ + pm2 = pm0; + } else if (t == 1) { + /* Check if we need to increment the message number. */ + if (pm0->pm_num == up->up_msg_num) { + up->up_msg_num++; + } + pm2 = pm1; + } else if (t == 2) { + /* Check if we need to increment the message number. */ + if (pm1->pm_num == up->up_msg_num) { + up->up_msg_num++; + } + pm2 = pm0; + } else if (t == 3) { + /* + * Both entries are queued. Re-queue the entry closest to + * the end. + */ + d = (pm1->pm_num - pm0->pm_num); + + /* Check sign after subtraction */ + if (d & 0x80000000) { + pm2 = pm0; + } else { + pm2 = pm1; + } + + TAILQ_REMOVE(&up->up_qhead, pm2, pm_qentry); + } else { + pm2 = NULL; /* panic - should not happen */ + } + + DPRINTF(" t=%u, num=%u\n", t, up->up_msg_num); + + /* Put message last on queue */ + + pm2->pm_num = up->up_msg_num; + TAILQ_INSERT_TAIL(&up->up_qhead, pm2, pm_qentry); + + /* Check if we need to wakeup the USB process. */ + + if (up->up_msleep) { + up->up_msleep = 0; /* save "cv_signal()" calls */ + cv_signal(&up->up_cv); + } + return (pm2); +} + +/*------------------------------------------------------------------------* + * usb_proc_is_gone + * + * Return values: + * 0: USB process is running + * Else: USB process is tearing down + *------------------------------------------------------------------------*/ +uint8_t +usb_proc_is_gone(struct usb_process *up) +{ + if (up->up_gone) + return (1); + + /* + * Allow calls when up_mtx is NULL, before the USB process + * structure is initialised. + */ + if (up->up_mtx != NULL) + USB_MTX_ASSERT(up->up_mtx, MA_OWNED); + return (0); +} + +static int +usb_proc_mwait_impl(struct usb_process *up, void *_pm0, void *_pm1, + bool interruptible) +{ + struct usb_proc_msg *pm0 = _pm0; + struct usb_proc_msg *pm1 = _pm1; + int error; + + /* check if gone */ + if (up->up_gone) + return (ENXIO); + + USB_MTX_ASSERT(up->up_mtx, MA_OWNED); + + error = 0; + if (up->up_curtd == curthread) { + /* Just remove the messages from the queue. */ + if (pm0->pm_qentry.tqe_prev) { + TAILQ_REMOVE(&up->up_qhead, pm0, pm_qentry); + pm0->pm_qentry.tqe_prev = NULL; + } + if (pm1->pm_qentry.tqe_prev) { + TAILQ_REMOVE(&up->up_qhead, pm1, pm_qentry); + pm1->pm_qentry.tqe_prev = NULL; + } + } else + while (error == 0 && (pm0->pm_qentry.tqe_prev || + pm1->pm_qentry.tqe_prev)) { + /* check if config thread is gone */ + if (up->up_gone) + return (ENXIO); + up->up_dsleep = 1; + if (interruptible) { + error = cv_wait_sig(&up->up_drain, up->up_mtx); + + /* + * The fact that we were interrupted doesn't + * matter if our goal was accomplished anyways. + */ + if (error != 0 && !USB_PROC_MSG_ENQUEUED(pm0) && + !USB_PROC_MSG_ENQUEUED(pm1)) + error = 0; + } else { + cv_wait(&up->up_drain, up->up_mtx); + } + } + + if (error == ERESTART) + error = EINTR; + return (error); +} + +/*------------------------------------------------------------------------* + * usb_proc_mwait + * + * This function will return when the USB process message pointed to + * by "pm" is no longer on a queue. This function must be called + * having "up->up_mtx" locked. + *------------------------------------------------------------------------*/ +void +usb_proc_mwait(struct usb_process *up, void *_pm0, void *_pm1) +{ + + (void)usb_proc_mwait_impl(up, _pm0, _pm1, false); +} + +/*------------------------------------------------------------------------* + * usb_proc_mwait_sig + * + * This function will return when the USB process message pointed to + * by "pm" is no longer on a queue. This function must be called + * having "up->up_mtx" locked. This version of usb_proc_mwait is + * interruptible. + *------------------------------------------------------------------------*/ +int +usb_proc_mwait_sig(struct usb_process *up, void *_pm0, void *_pm1) +{ + + return (usb_proc_mwait_impl(up, _pm0, _pm1, true)); +} + +/*------------------------------------------------------------------------* + * usb_proc_drain + * + * This function will tear down an USB process, waiting for the + * currently executing command to return. + * + * NOTE: If the structure pointed to by "up" is all zero, + * this function does nothing. + *------------------------------------------------------------------------*/ +void +usb_proc_drain(struct usb_process *up) +{ + /* check if not initialised */ + if (up->up_mtx == NULL) + return; + /* handle special case with Giant */ + if (up->up_mtx != &Giant) + USB_MTX_ASSERT(up->up_mtx, MA_NOTOWNED); + + USB_MTX_LOCK(up->up_mtx); + + /* Set the gone flag */ + + up->up_gone = 1; + + while (up->up_ptr) { + /* Check if we need to wakeup the USB process */ + + if (up->up_msleep || up->up_csleep) { + up->up_msleep = 0; + up->up_csleep = 0; + cv_signal(&up->up_cv); + } +#ifndef EARLY_AP_STARTUP + /* Check if we are still cold booted */ + if (cold) { + USB_THREAD_SUSPEND(up->up_ptr); + printf("WARNING: A USB process has " + "been left suspended\n"); + break; + } +#endif + cv_wait(&up->up_cv, up->up_mtx); + } + /* Check if someone is waiting - should not happen */ + + if (up->up_dsleep) { + up->up_dsleep = 0; + cv_broadcast(&up->up_drain); + DPRINTF("WARNING: Someone is waiting " + "for USB process drain!\n"); + } + USB_MTX_UNLOCK(up->up_mtx); +} + +/*------------------------------------------------------------------------* + * usb_proc_rewakeup + * + * This function is called to re-wakeup the given USB + * process. This usually happens after that the USB system has been in + * polling mode, like during a panic. This function must be called + * having "up->up_mtx" locked. + *------------------------------------------------------------------------*/ +void +usb_proc_rewakeup(struct usb_process *up) +{ + /* check if not initialised */ + if (up->up_mtx == NULL) + return; + /* check if gone */ + if (up->up_gone) + return; + + USB_MTX_ASSERT(up->up_mtx, MA_OWNED); + + if (up->up_msleep == 0) { + /* re-wakeup */ + cv_signal(&up->up_cv); + } +} + +/*------------------------------------------------------------------------* + * usb_proc_is_called_from + * + * This function will return non-zero if called from inside the USB + * process passed as first argument. Else this function returns zero. + *------------------------------------------------------------------------*/ +int +usb_proc_is_called_from(struct usb_process *up) +{ + return (up->up_curtd == curthread); +} + +/*------------------------------------------------------------------------* + * usbd_do_request_proc - factored out code + * + * This function is factored out code. It does basically the same like + * usbd_do_request_flags, except it will check the status of the + * passed process argument before doing the USB request. If the + * process is draining the USB_ERR_IOERROR code will be returned. It + * is assumed that the mutex associated with the process is locked + * when calling this function. + *------------------------------------------------------------------------*/ +usb_error_t +usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc, + struct usb_device_request *req, void *data, uint16_t flags, + uint16_t *actlen, usb_timeout_t timeout) +{ + usb_error_t err; + uint16_t len; + + /* get request data length */ + len = UGETW(req->wLength); + + /* check if the device is being detached */ + if (usb_proc_is_gone(pproc)) { + err = USB_ERR_IOERROR; + goto done; + } + + /* forward the USB request */ + err = usbd_do_request_flags(udev, pproc->up_mtx, + req, data, flags, actlen, timeout); + +done: + /* on failure we zero the data */ + /* on short packet we zero the unused data */ + if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) { + if (err) + memset(data, 0, len); + else if (actlen && *actlen != len) + memset(((uint8_t *)data) + *actlen, 0, len - *actlen); + } + return (err); +} diff --git a/src/libs/compat/freebsd_iflib/kthread.cpp b/src/libs/compat/freebsd_network/kthread.cpp similarity index 100% rename from src/libs/compat/freebsd_iflib/kthread.cpp rename to src/libs/compat/freebsd_network/kthread.cpp