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 <[email protected]>
This commit is contained in:
Augustin Cavalier
2026-03-16 13:17:57 +00:00
committed by waddlesplash
parent c0374aed39
commit 9936387223
14 changed files with 1629 additions and 37 deletions
-1
View File
@@ -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
@@ -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 <sys/kthread.h>
#include <sys/pcpu.h>
#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_ */
+3
View File
@@ -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
;
@@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/limits.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/bpf.h>
#include <net/ethernet.h>
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_ */
@@ -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 */
@@ -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_ */
@@ -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.
@@ -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_ */
@@ -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
@@ -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 <sys/pcpu.h>
#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_ */
+29 -9
View File
@@ -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);
}
@@ -0,0 +1,685 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2009 Andrew Thompson ([email protected])
*
* 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 <sys/cdefs.h>
#include <sys/epoch.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/condvar.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/ethernet.h>
#include <net/if_types.h>
#include <net/if_media.h>
#include <net/if_vlan_var.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/net/usb_ethernet.h>
#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, "<USB Ethernet> 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
@@ -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 <stdint.h>
#include <stddef.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <unistd.h>
#include <sys/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usb_process.h>
#define USB_DEBUG_VAR usb_proc_debug
#include <dev/usb/usb_debug.h>
//#include <dev/usb/usb_util.h>
#include <sys/proc.h>
#include <sys/kthread.h>
//#include <sys/sched.h>
#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);
}