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 ] ;
|
SubDirCcFlags [ FDefines _KERNEL=1 FBSD_DRIVER=1 _XOPEN_SOURCE ALTQ ] ;
|
||||||
|
|
||||||
KernelStaticLibrary freebsd_iflib.a :
|
KernelStaticLibrary freebsd_iflib.a :
|
||||||
kthread.cpp
|
|
||||||
device_if.c
|
device_if.c
|
||||||
kobj.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
|
eventhandler.c
|
||||||
firmware.c
|
firmware.c
|
||||||
if.c
|
if.c
|
||||||
|
kthread.cpp
|
||||||
libkern.cpp
|
libkern.cpp
|
||||||
malloc.cpp
|
malloc.cpp
|
||||||
mbuf.c
|
mbuf.c
|
||||||
@@ -53,6 +54,8 @@ KernelStaticLibrary freebsd_usb.a :
|
|||||||
fbsd_usb_error.c
|
fbsd_usb_error.c
|
||||||
fbsd_usb_util.c
|
fbsd_usb_util.c
|
||||||
fbsd_usb_lookup.c
|
fbsd_usb_lookup.c
|
||||||
|
usb_process.c
|
||||||
|
usb_ethernet.c
|
||||||
usb.cpp
|
usb.cpp
|
||||||
usb_util.c
|
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_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_timeout_t; /* milliseconds */
|
||||||
typedef uint32_t usb_frlength_t; /* bytes */
|
typedef uint32_t usb_frlength_t; /* bytes */
|
||||||
typedef uint32_t usb_frcount_t; /* units */
|
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
|
#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
|
* The following is a wrapper for the callout structure to ease
|
||||||
* porting the code to other platforms.
|
* 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_init(struct cv*, const char*);
|
||||||
void cv_destroy(struct cv*);
|
void cv_destroy(struct cv*);
|
||||||
|
|
||||||
void cv_wait(struct cv*, struct mtx*);
|
void cv_wait(struct cv*, struct mtx*);
|
||||||
int cv_timedwait(struct cv*, struct mtx*, int);
|
int cv_timedwait(struct cv*, struct mtx*, int);
|
||||||
|
int cv_wait_sig(struct cv*, struct mtx*);
|
||||||
void cv_signal(struct cv*);
|
void cv_signal(struct cv*);
|
||||||
|
void cv_broadcast(struct cv*);
|
||||||
|
|
||||||
|
|
||||||
__END_DECLS
|
__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
|
void
|
||||||
cv_destroy(struct cv* variable)
|
cv_destroy(struct cv* variable)
|
||||||
{
|
{
|
||||||
__cv_ConditionVariable(variable)->NotifyAll();
|
cv_broadcast(variable);
|
||||||
|
|
||||||
// Nothing else to do.
|
// Nothing else to do.
|
||||||
}
|
}
|
||||||
@@ -38,19 +38,23 @@ cv_signal(struct cv* variable)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
void
|
||||||
cv_timedwait(struct cv* variable, struct mtx* mutex, int timeout)
|
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);
|
ConditionVariable* condition = __cv_ConditionVariable(variable);
|
||||||
|
|
||||||
const uint32 flags = timeout ? B_RELATIVE_TIMEOUT : 0;
|
|
||||||
const bigtime_t bigtimeout = TICKS_2_USEC(timeout);
|
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
if (mutex->type == MTX_RECURSE) {
|
if (mutex->type == MTX_RECURSE) {
|
||||||
// Special case: let the ConditionVariable handle switching recursive locks.
|
// Special case: let the ConditionVariable handle switching recursive locks.
|
||||||
status = condition->Wait(&mutex->u.recursive,
|
status = condition->Wait(&mutex->u.recursive,
|
||||||
flags, bigtimeout);
|
flags, timeout);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,15 +63,31 @@ cv_timedwait(struct cv* variable, struct mtx* mutex, int timeout)
|
|||||||
|
|
||||||
mtx_unlock(mutex);
|
mtx_unlock(mutex);
|
||||||
|
|
||||||
status = entry.Wait(flags, bigtimeout);
|
status = entry.Wait(flags, timeout);
|
||||||
|
|
||||||
mtx_lock(mutex);
|
mtx_lock(mutex);
|
||||||
return status;
|
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
|
void
|
||||||
cv_wait(struct cv* variable, struct mtx* mutex)
|
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