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:
committed by
waddlesplash
parent
c0374aed39
commit
9936387223
@@ -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_ */
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user