freebsd_network: Introduce USB-handling code.

It is not yet wired in to the rest of the compatibilit layer;
that will happen in the next commits.

The bulk of the implementation is in usb.cpp; most of the
rest of the new files are copied from FreeBSD to varying degrees.
This commit is contained in:
Augustin Cavalier
2022-02-24 21:12:28 -05:00
parent a8523a21b5
commit 2e9f01f60d
12 changed files with 1611 additions and 0 deletions
@@ -0,0 +1,77 @@
/*
* Copyright 2018-2019, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Augustin Cavalier <waddlesplash>
*/
#ifndef _FBSD_COMPAT_USB_STANDARD_H_
#define _FBSD_COMPAT_USB_STANDARD_H_
#include <sys/malloc.h>
MALLOC_DECLARE(M_USB);
MALLOC_DECLARE(M_USBDEV);
#include <dev/usb/usb_endian.h>
#include <dev/usb/usb_haiku.h>
#define USB_STACK_VERSION 2000 /* 2.0 */
struct usb_device_request {
uByte bmRequestType;
uByte bRequest;
uWord wValue;
uWord wIndex;
uWord wLength;
} __packed;
typedef struct usb_device_request usb_device_request_t;
/* abbreviated Haiku request types */
#define HAIKU_USB_REQTYPE_DEVICE_IN 0x80
#define HAIKU_USB_REQTYPE_DEVICE_OUT 0x00
#define HAIKU_USB_REQTYPE_VENDOR 0x40
/* FreeBSD request types */
#define UT_READ_VENDOR_DEVICE (HAIKU_USB_REQTYPE_DEVICE_IN | HAIKU_USB_REQTYPE_VENDOR)
#define UT_WRITE_VENDOR_DEVICE (HAIKU_USB_REQTYPE_DEVICE_OUT | HAIKU_USB_REQTYPE_VENDOR)
/* make sure this matches Haiku's usb_speed! */
enum usb_dev_speed {
USB_SPEED_LOW,
USB_SPEED_FULL,
USB_SPEED_HIGH,
USB_SPEED_SUPER,
};
enum usb_hc_mode {
USB_MODE_HOST, /* initiates transfers */
USB_MODE_DEVICE, /* bus transfer target */
USB_MODE_DUAL /* can be host or device */
};
#define USB_MODE_MAX (USB_MODE_DUAL+1)
#define usb_endpoint_descriptor freebsd_usb_endpoint_descriptor
struct usb_endpoint_descriptor {
uByte bLength;
uByte bDescriptorType;
uByte bEndpointAddress;
#define UE_GET_DIR(a) ((a) & 0x80)
#define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7))
#define UE_DIR_IN 0x80 /* IN-token endpoint, fixed */
#define UE_DIR_OUT 0x00 /* OUT-token endpoint, fixed */
#define UE_ADDR 0x0f
#define UE_ADDR_ANY 0xff
#define UE_GET_ADDR(a) ((a) & UE_ADDR)
uByte bmAttributes;
#define UE_XFERTYPE 0x03
#define UE_CONTROL 0x00
#define UE_BULK 0x02
#define UE_INTERRUPT 0x03
#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE)
uWord wMaxPacketSize;
uByte bInterval;
} __packed;
typedef struct usb_endpoint_descriptor usb_endpoint_descriptor_t;
#endif // _FBSD_COMPAT_USB_STANDARD_H_
@@ -0,0 +1,86 @@
/* $FreeBSD$ */
/*-
* 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.
*/
/* This file contains various factored out debug macros. */
#ifndef _USB_DEBUG_H_
#define _USB_DEBUG_H_
/* Declare global USB debug variable. */
extern int usb_debug;
/* Check if USB debugging is enabled. */
#ifdef USB_DEBUG_VAR
#ifdef USB_DEBUG
#define DPRINTFN(n,fmt,...) do { \
if ((USB_DEBUG_VAR) >= (n)) { \
printf("%s: " fmt, \
__FUNCTION__ ,##__VA_ARGS__); \
} \
} while (0)
#define DPRINTF(...) DPRINTFN(1, __VA_ARGS__)
#else
#define DPRINTF(...) do { } while (0)
#define DPRINTFN(...) do { } while (0)
#endif
#endif
struct usb_interface;
struct usb_device;
struct usb_endpoint;
struct usb_xfer;
void usb_dump_iface(struct usb_interface *iface);
void usb_dump_device(struct usb_device *udev);
void usb_dump_queue(struct usb_endpoint *ep);
void usb_dump_endpoint(struct usb_endpoint *ep);
void usb_dump_xfer(struct usb_xfer *xfer);
#ifdef USB_DEBUG
extern unsigned int usb_port_reset_delay;
extern unsigned int usb_port_root_reset_delay;
extern unsigned int usb_port_reset_recovery;
extern unsigned int usb_port_powerup_delay;
extern unsigned int usb_port_resume_delay;
extern unsigned int usb_set_address_settle;
extern unsigned int usb_resume_delay;
extern unsigned int usb_resume_wait;
extern unsigned int usb_resume_recovery;
extern unsigned int usb_extra_power_up_time;
#else
#define usb_port_reset_delay USB_PORT_RESET_DELAY
#define usb_port_root_reset_delay USB_PORT_ROOT_RESET_DELAY
#define usb_port_reset_recovery USB_PORT_RESET_RECOVERY
#define usb_port_powerup_delay USB_PORT_POWERUP_DELAY
#define usb_port_resume_delay USB_PORT_RESUME_DELAY
#define usb_set_address_settle USB_SET_ADDRESS_SETTLE
#define usb_resume_delay USB_RESUME_DELAY
#define usb_resume_wait USB_RESUME_WAIT
#define usb_resume_recovery USB_RESUME_RECOVERY
#define usb_extra_power_up_time USB_EXTRA_POWER_UP_TIME
#endif
#endif /* _USB_DEBUG_H_ */
@@ -0,0 +1,17 @@
/*
* Copyright 2022, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#ifndef _FBSD_COMPAT_USB_DEVICE_H_
#define _FBSD_COMPAT_USB_DEVICE_H_
struct usb_device {
struct usb_endpoint endpoints[USB_MAX_EP_UNITS];
uint8_t endpoints_max; /* number of endpoints present */
uint32 haiku_usb_device;
};
void usb_cleanup_device(struct usb_device* udev);
#endif // _FBSD_COMPAT_USB_DEVICE_H_
@@ -0,0 +1,28 @@
/*
* Copyright 2020, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Augustin Cavalier <waddlesplash>
*/
#ifndef _USB_ENDIAN_H_
#define _USB_ENDIAN_H_
#include <stdint.h>
typedef uint8_t uByte;
typedef uint16_t uWord;
typedef uint32_t uDWord;
typedef uint64_t uQWord;
#define UGETB(w) (uint8_t)(w)
#define UGETW(w) (uint16_t)(w)
#define UGETDW(w) (uint32_t)(w)
#define UGETQW(w) (uint64_t)(w)
#define USETB(w,v) { (w) = (uint8_t)(v); }
#define USETW(w,v) { (w) = (uint16_t)(v); }
#define USETDW(w,v) { (w) = (uint32_t)(v); }
#define USETQW(w,v) { (w) = (uint64_t)(v); }
#endif /* _USB_ENDIAN_H_ */
@@ -0,0 +1,80 @@
/*
* Copyright 2018, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Augustin Cavalier <waddlesplash>
*/
#ifndef _FBSD_COMPAT_USB_HAIKU_H_
#define _FBSD_COMPAT_USB_HAIKU_H_
#include <sys/cdefs.h>
/* Default USB configuration */
#define USB_HAVE_UGEN 1
#define USB_HAVE_DEVCTL 1
#define USB_HAVE_BUSDMA 1
#define USB_HAVE_COMPAT_LINUX 0
#define USB_HAVE_USER_IO 1
#define USB_HAVE_MBUF 1
#define USB_HAVE_TT_SUPPORT 1
#define USB_HAVE_POWERD 1
#define USB_HAVE_MSCTEST 1
#define USB_HAVE_MSCTEST_DETACH 1
#define USB_HAVE_PF 1
#define USB_HAVE_ROOT_MOUNT_HOLD 1
#define USB_HAVE_ID_SECTION 0
#define USB_HAVE_PER_BUS_PROCESS 1
#define USB_HAVE_FIXED_ENDPOINT 0
#define USB_HAVE_FIXED_IFACE 0
#define USB_HAVE_FIXED_CONFIG 0
#define USB_HAVE_FIXED_PORT 0
#define USB_HAVE_DISABLE_ENUM 1
/* define zero ticks callout value */
#define USB_CALLOUT_ZERO_TICKS 1
#define USB_TD_GET_PROC(td) (td)->td_proc
#define USB_PROC_GET_GID(td) (td)->p_pgid
#if (!defined(USB_HOST_ALIGN)) || (USB_HOST_ALIGN <= 0)
/* Use default value. */
#undef USB_HOST_ALIGN
#if defined(__arm__) || defined(__mips__) || defined(__powerpc__)
#define USB_HOST_ALIGN 32 /* Arm and MIPS need at least this much, if not more */
#else
#define USB_HOST_ALIGN 8 /* bytes, must be power of two */
#endif
#endif
/* Sanity check for USB_HOST_ALIGN: Verify power of two. */
#if ((-USB_HOST_ALIGN) & USB_HOST_ALIGN) != USB_HOST_ALIGN
#error "USB_HOST_ALIGN is not power of two."
#endif
#define USB_FS_ISOC_UFRAME_MAX 4 /* exclusive unit */
#define USB_BUS_MAX 256 /* units */
#define USB_MAX_DEVICES 128 /* units */
#define USB_CONFIG_MAX 65535 /* bytes */
#define USB_IFACE_MAX 32 /* units */
#define USB_FIFO_MAX 128 /* units */
#define USB_MAX_EP_STREAMS 8 /* units */
#define USB_MAX_EP_UNITS 32 /* units */
#define USB_MAX_PORTS 255 /* units */
#define USB_MAX_FS_ISOC_FRAMES_PER_XFER (120) /* units */
#define USB_MAX_HS_ISOC_FRAMES_PER_XFER (8*120) /* units */
#define USB_HUB_MAX_DEPTH 5
#define USB_EP0_BUFSIZE 1024 /* bytes */
#define USB_CS_RESET_LIMIT 20 /* failures = 20 * 50 ms = 1sec */
#define USB_MAX_AUTO_QUIRK 8 /* maximum number of dynamic quirks */
typedef uint32_t usb_timeout_t; /* milliseconds */
typedef uint32_t usb_frlength_t; /* bytes */
typedef uint32_t usb_frcount_t; /* units */
typedef uint32_t usb_size_t; /* bytes */
typedef uint32_t usb_ticks_t; /* system defined */
typedef uint16_t usb_power_mask_t; /* see "USB_HW_POWER_XXX" */
typedef uint16_t usb_stream_t; /* stream ID */
#endif // _FBSD_COMPAT_USB_HAIKU_H_
@@ -0,0 +1,375 @@
/*
* Copyright 2022, 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_H_
#define _FBSD_COMPAT_USB_USBDI_H_
#define usb_device freebsd_usb_device
#define usb_interface freebsd_usb_interface
struct usb_device;
struct usb_interface;
struct usb_xfer;
struct usb_attach_arg;
struct usb_endpoint;
struct usb_page_cache;
struct usb_page_search;
struct usb_process;
struct usb_mbuf;
struct usb_fs_privdata;
struct mbuf;
typedef enum { /* keep in sync with usb_errstr_table */
USB_ERR_NORMAL_COMPLETION = 0,
USB_ERR_PENDING_REQUESTS, /* 1 */
USB_ERR_NOT_STARTED, /* 2 */
USB_ERR_INVAL, /* 3 */
USB_ERR_NOMEM, /* 4 */
USB_ERR_CANCELLED, /* 5 */
USB_ERR_BAD_ADDRESS, /* 6 */
USB_ERR_BAD_BUFSIZE, /* 7 */
USB_ERR_BAD_FLAG, /* 8 */
USB_ERR_NO_CALLBACK, /* 9 */
USB_ERR_IN_USE, /* 10 */
USB_ERR_NO_ADDR, /* 11 */
USB_ERR_NO_PIPE, /* 12 */
USB_ERR_ZERO_NFRAMES, /* 13 */
USB_ERR_ZERO_MAXP, /* 14 */
USB_ERR_SET_ADDR_FAILED, /* 15 */
USB_ERR_NO_POWER, /* 16 */
USB_ERR_TOO_DEEP, /* 17 */
USB_ERR_IOERROR, /* 18 */
USB_ERR_NOT_CONFIGURED, /* 19 */
USB_ERR_TIMEOUT, /* 20 */
USB_ERR_SHORT_XFER, /* 21 */
USB_ERR_STALLED, /* 22 */
USB_ERR_INTERRUPTED, /* 23 */
USB_ERR_DMA_LOAD_FAILED, /* 24 */
USB_ERR_BAD_CONTEXT, /* 25 */
USB_ERR_NO_ROOT_HUB, /* 26 */
USB_ERR_NO_INTR_THREAD, /* 27 */
USB_ERR_NOT_LOCKED, /* 28 */
USB_ERR_MAX
} usb_error_t;
/*
* Flags for transfers
*/
#define USB_FORCE_SHORT_XFER 0x0001 /* force a short transmit last */
#define USB_SHORT_XFER_OK 0x0004 /* allow short reads */
#define USB_DELAY_STATUS_STAGE 0x0010 /* insert delay before STATUS stage */
#define USB_USER_DATA_PTR 0x0020 /* internal flag */
#define USB_MULTI_SHORT_OK 0x0040 /* allow multiple short frames */
#define USB_MANUAL_STATUS 0x0080 /* manual ctrl status */
#define USB_NO_TIMEOUT 0
#define USB_DEFAULT_TIMEOUT 5000 /* 5000 ms = 5 seconds */
#if defined(_KERNEL)
/* typedefs */
typedef void (usb_callback_t)(struct usb_xfer *, usb_error_t);
/*
* The following macros are used used to convert milliseconds into
* HZ. We use 1024 instead of 1000 milliseconds per second to save a
* full division.
*/
#define USB_MS_HZ 1024
#define USB_MS_TO_TICKS(ms) \
(((uint32_t)((((uint32_t)(ms)) * ((uint32_t)(hz))) + USB_MS_HZ - 1)) / USB_MS_HZ)
/*
* The following structure defines a USB endpoint.
*/
struct usb_endpoint {
struct usb_endpoint_descriptor* edesc;
uint8_t iface_index; /* not used by "default endpoint" */
};
/*
* The following structure defines a set of USB transfer flags.
*/
struct usb_xfer_flags {
uint8_t force_short_xfer:1;
/* force a short transmit transfer last */
uint8_t short_xfer_ok:1; /* allow short receive transfers */
uint8_t short_frames_ok:1; /* allow short frames */
uint8_t pipe_bof:1; /* block pipe on failure */
uint8_t proxy_buffer:1;
/* makes buffer size a factor of "max_frame_size" */
uint8_t ext_buffer:1; /* uses external DMA buffer */
uint8_t manual_status:1;
/* non automatic status stage on control transfers */
uint8_t no_pipe_ok:1;
/* set if "USB_ERR_NO_PIPE" error can be ignored */
uint8_t stall_pipe:1;
/* set if the endpoint belonging to this USB transfer
* should be stalled before starting this transfer! */
};
struct usb_config {
usb_callback_t *callback; /* USB transfer callback */
usb_frlength_t bufsize; /* total pipe buffer size in bytes */
usb_frcount_t frames; /* maximum number of USB frames */
usb_timeout_t interval; /* interval in milliseconds */
#define USB_DEFAULT_INTERVAL 0
usb_timeout_t timeout; /* transfer timeout in milliseconds */
struct usb_xfer_flags flags; /* transfer flags */
usb_stream_t stream_id; /* USB3.0 specific */
enum usb_hc_mode usb_mode; /* host or device mode */
uint8_t type; /* pipe type */
uint8_t endpoint; /* pipe number */
uint8_t direction; /* pipe direction */
uint8_t ep_index; /* pipe index match to use */
uint8_t if_index; /* "ifaces" index to use */
};
#if USB_HAVE_ID_SECTION
#define STRUCT_USB_HOST_ID \
struct usb_device_id __section("usb_host_id")
#define STRUCT_USB_DEVICE_ID \
struct usb_device_id __section("usb_device_id")
#define STRUCT_USB_DUAL_ID \
struct usb_device_id __section("usb_dual_id")
#else
#define STRUCT_USB_HOST_ID \
struct usb_device_id
#define STRUCT_USB_DEVICE_ID \
struct usb_device_id
#define STRUCT_USB_DUAL_ID \
struct usb_device_id
#endif /* USB_HAVE_ID_SECTION */
struct usb_device_id {
/* Select which fields to match against */
#if BYTE_ORDER == LITTLE_ENDIAN
uint16_t
match_flag_vendor:1,
match_flag_product:1,
match_flag_dev_lo:1,
match_flag_dev_hi:1,
match_flag_dev_class:1,
match_flag_dev_subclass:1,
match_flag_dev_protocol:1,
match_flag_int_class:1,
match_flag_int_subclass:1,
match_flag_int_protocol:1,
match_flag_unused:6;
#else
uint16_t
match_flag_unused:6,
match_flag_int_protocol:1,
match_flag_int_subclass:1,
match_flag_int_class:1,
match_flag_dev_protocol:1,
match_flag_dev_subclass:1,
match_flag_dev_class:1,
match_flag_dev_hi:1,
match_flag_dev_lo:1,
match_flag_product:1,
match_flag_vendor:1;
#endif
/* Used for product specific matches; the BCD range is inclusive */
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice_lo;
uint16_t bcdDevice_hi;
/* Used for device class matches */
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
/* Used for interface class matches */
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
/* Hook for driver specific information */
unsigned long driver_info;
} __aligned(32);
#define USB_PNP_HOST_INFO(table)
#define USB_PNP_DEVICE_INFO(table)
#define USB_PNP_DUAL_INFO(table)
/* check that the size of the structure above is correct */
extern char usb_device_id_assert[(sizeof(struct usb_device_id) == 32) ? 1 : -1];
#define USB_VENDOR(vend) \
.match_flag_vendor = 1, .idVendor = (vend)
#define USB_PRODUCT(prod) \
.match_flag_product = 1, .idProduct = (prod)
#define USB_VP(vend,prod) \
USB_VENDOR(vend), USB_PRODUCT(prod)
#define USB_VPI(vend,prod,info) \
USB_VENDOR(vend), USB_PRODUCT(prod), USB_DRIVER_INFO(info)
#define USB_DEV_BCD_GTEQ(lo) /* greater than or equal */ \
.match_flag_dev_lo = 1, .bcdDevice_lo = (lo)
#define USB_DEV_BCD_LTEQ(hi) /* less than or equal */ \
.match_flag_dev_hi = 1, .bcdDevice_hi = (hi)
#define USB_DEV_CLASS(dc) \
.match_flag_dev_class = 1, .bDeviceClass = (dc)
#define USB_DEV_SUBCLASS(dsc) \
.match_flag_dev_subclass = 1, .bDeviceSubClass = (dsc)
#define USB_DEV_PROTOCOL(dp) \
.match_flag_dev_protocol = 1, .bDeviceProtocol = (dp)
#define USB_IFACE_CLASS(ic) \
.match_flag_int_class = 1, .bInterfaceClass = (ic)
#define USB_IFACE_SUBCLASS(isc) \
.match_flag_int_subclass = 1, .bInterfaceSubClass = (isc)
#define USB_IFACE_PROTOCOL(ip) \
.match_flag_int_protocol = 1, .bInterfaceProtocol = (ip)
#define USB_IF_CSI(class,subclass,info) \
USB_IFACE_CLASS(class), USB_IFACE_SUBCLASS(subclass), USB_DRIVER_INFO(info)
#define USB_DRIVER_INFO(n) \
.driver_info = (n)
#define USB_GET_DRIVER_INFO(did) \
(did)->driver_info
/*
* The following structure keeps information that is used to match
* against an array of "usb_device_id" elements.
*/
struct usbd_lookup_info {
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t bIfaceIndex;
uint8_t bIfaceNum;
uint8_t bConfigIndex;
uint8_t bConfigNum;
};
/* Structure used by probe and attach */
struct usb_attach_arg {
struct usbd_lookup_info info;
device_t temp_dev; /* for internal use */
unsigned long driver_info; /* for internal use */
void *driver_ivar;
struct usb_device *device; /* current device */
struct usb_interface *iface; /* current interface */
enum usb_hc_mode usb_mode; /* host or device mode */
uint8_t port;
uint8_t dev_state;
#define UAA_DEV_READY 0
#define UAA_DEV_DISABLED 1
#define UAA_DEV_EJECTING 2
};
/*
* The following is a wrapper for the callout structure to ease
* porting the code to other platforms.
*/
struct usb_callout {
struct callout co;
};
#define usb_callout_init_mtx(c,m,f) callout_init_mtx(&(c)->co,m,f)
#define usb_callout_reset(c,t,f,d) callout_reset(&(c)->co,t,f,d)
#define usb_callout_stop(c) callout_stop(&(c)->co)
#define usb_callout_drain(c) callout_drain(&(c)->co)
#define usb_callout_pending(c) callout_pending(&(c)->co)
/* USB transfer states */
#define USB_ST_SETUP 0
#define USB_ST_TRANSFERRED 1
#define USB_ST_ERROR 2
/*
* The following macro will return the current state of an USB
* transfer like defined by the "USB_ST_XXX" enums.
*/
#define USB_GET_STATE(xfer) (usbd_xfer_state(xfer))
int usbd_lookup_id_by_uaa(const struct usb_device_id *id,
usb_size_t sizeof_id, struct usb_attach_arg *uaa);
void device_set_usb_desc(device_t dev);
const char *usbd_errstr(usb_error_t error);
void usb_pause_mtx(struct mtx *mtx, int _ticks);
usb_error_t usbd_do_request_flags(struct usb_device* udev, struct mtx *mtx,
struct usb_device_request* req, void* data, uint16_t flags,
uint16_t *actlen, usb_timeout_t timeout);
#define usbd_do_request(u,m,r,d) \
usbd_do_request_flags(u,m,r,d,0,NULL,USB_DEFAULT_TIMEOUT)
void usb_pause_mtx(struct mtx *mtx, int _ticks);
enum usb_dev_speed usbd_get_speed(struct usb_device* udev);
void* usbd_xfer_softc(struct usb_xfer *xfer);
uint8_t usbd_xfer_state(struct usb_xfer *xfer);
usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer);
void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex,
void *ptr, usb_frlength_t len);
void usbd_xfer_set_stall(struct usb_xfer *xfer);
void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen,
int *aframes, int *nframes);
void usbd_transfer_submit(struct usb_xfer *xfer);
void usbd_transfer_start(struct usb_xfer *xfer);
void usbd_transfer_stop(struct usb_xfer *xfer);
void usbd_transfer_drain(struct usb_xfer* xfer);
usb_error_t usbd_transfer_setup(struct usb_device *udev,
const uint8_t *ifaces, struct usb_xfer **pxfer,
const struct usb_config *setup_start, uint16_t n_setup,
void *priv_sc, struct mtx *priv_mtx);
void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup);
#endif /* _KERNEL */
#endif /* _FBSD_COMPAT_USB_USBDI_H_ */
+6
View File
@@ -77,6 +77,12 @@ void uninit_callout(void);
status_t init_pci();
void uninit_pci();
status_t init_usb();
void uninit_usb();
status_t get_next_usb_device(uint32* cookie, struct freebsd_usb_device* result);
status_t get_usb_device_attach_arg(struct freebsd_usb_device* device, struct usb_attach_arg* uaa);
device_t find_root_device(int);
pci_info* get_device_pci_info(device_t dev);
@@ -0,0 +1,90 @@
/* $FreeBSD$ */
/*-
* 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 <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/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#endif /* USB_GLOBAL_INCLUDE_FILE */
static const char* usb_errstr_table[USB_ERR_MAX] = {
[USB_ERR_NORMAL_COMPLETION] = "USB_ERR_NORMAL_COMPLETION",
[USB_ERR_PENDING_REQUESTS] = "USB_ERR_PENDING_REQUESTS",
[USB_ERR_NOT_STARTED] = "USB_ERR_NOT_STARTED",
[USB_ERR_INVAL] = "USB_ERR_INVAL",
[USB_ERR_NOMEM] = "USB_ERR_NOMEM",
[USB_ERR_CANCELLED] = "USB_ERR_CANCELLED",
[USB_ERR_BAD_ADDRESS] = "USB_ERR_BAD_ADDRESS",
[USB_ERR_BAD_BUFSIZE] = "USB_ERR_BAD_BUFSIZE",
[USB_ERR_BAD_FLAG] = "USB_ERR_BAD_FLAG",
[USB_ERR_NO_CALLBACK] = "USB_ERR_NO_CALLBACK",
[USB_ERR_IN_USE] = "USB_ERR_IN_USE",
[USB_ERR_NO_ADDR] = "USB_ERR_NO_ADDR",
[USB_ERR_NO_PIPE] = "USB_ERR_NO_PIPE",
[USB_ERR_ZERO_NFRAMES] = "USB_ERR_ZERO_NFRAMES",
[USB_ERR_ZERO_MAXP] = "USB_ERR_ZERO_MAXP",
[USB_ERR_SET_ADDR_FAILED] = "USB_ERR_SET_ADDR_FAILED",
[USB_ERR_NO_POWER] = "USB_ERR_NO_POWER",
[USB_ERR_TOO_DEEP] = "USB_ERR_TOO_DEEP",
[USB_ERR_IOERROR] = "USB_ERR_IOERROR",
[USB_ERR_NOT_CONFIGURED] = "USB_ERR_NOT_CONFIGURED",
[USB_ERR_TIMEOUT] = "USB_ERR_TIMEOUT",
[USB_ERR_SHORT_XFER] = "USB_ERR_SHORT_XFER",
[USB_ERR_STALLED] = "USB_ERR_STALLED",
[USB_ERR_INTERRUPTED] = "USB_ERR_INTERRUPTED",
[USB_ERR_DMA_LOAD_FAILED] = "USB_ERR_DMA_LOAD_FAILED",
[USB_ERR_BAD_CONTEXT] = "USB_ERR_BAD_CONTEXT",
[USB_ERR_NO_ROOT_HUB] = "USB_ERR_NO_ROOT_HUB",
[USB_ERR_NO_INTR_THREAD] = "USB_ERR_NO_INTR_THREAD",
[USB_ERR_NOT_LOCKED] = "USB_ERR_NOT_LOCKED",
};
/*------------------------------------------------------------------------*
* usbd_errstr
*
* This function converts an USB error code into a string.
*------------------------------------------------------------------------*/
const char *
usbd_errstr(usb_error_t err)
{
return (err < USB_ERR_MAX ? usb_errstr_table[err] : "USB_ERR_UNKNOWN");
}
@@ -0,0 +1,216 @@
/* $FreeBSD$ */
/*-
* 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 <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/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>
#include <sys/limits.h>
#include <sys/endian.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#endif /* USB_GLOBAL_INCLUDE_FILE */
/*------------------------------------------------------------------------*
* usbd_lookup_id_by_info
*
* This functions takes an array of "struct usb_device_id" and tries
* to match the entries with the information in "struct usbd_lookup_info".
*
* NOTE: The "sizeof_id" parameter must be a multiple of the
* usb_device_id structure size. Else the behaviour of this function
* is undefined.
*
* Return values:
* NULL: No match found.
* Else: Pointer to matching entry.
*------------------------------------------------------------------------*/
const struct usb_device_id *
usbd_lookup_id_by_info(const struct usb_device_id *id, usb_size_t sizeof_id,
const struct usbd_lookup_info *info)
{
const struct usb_device_id *id_end;
if (id == NULL) {
goto done;
}
id_end = (const void *)(((const uint8_t *)id) + sizeof_id);
/*
* Keep on matching array entries until we find a match or
* until we reach the end of the matching array:
*/
for (; id != id_end; id++) {
if ((id->match_flag_vendor) &&
(id->idVendor != info->idVendor)) {
continue;
}
if ((id->match_flag_product) &&
(id->idProduct != info->idProduct)) {
continue;
}
if ((id->match_flag_dev_lo) &&
(id->bcdDevice_lo > info->bcdDevice)) {
continue;
}
if ((id->match_flag_dev_hi) &&
(id->bcdDevice_hi < info->bcdDevice)) {
continue;
}
if ((id->match_flag_dev_class) &&
(id->bDeviceClass != info->bDeviceClass)) {
continue;
}
if ((id->match_flag_dev_subclass) &&
(id->bDeviceSubClass != info->bDeviceSubClass)) {
continue;
}
if ((id->match_flag_dev_protocol) &&
(id->bDeviceProtocol != info->bDeviceProtocol)) {
continue;
}
if ((id->match_flag_int_class) &&
(id->bInterfaceClass != info->bInterfaceClass)) {
continue;
}
if ((id->match_flag_int_subclass) &&
(id->bInterfaceSubClass != info->bInterfaceSubClass)) {
continue;
}
if ((id->match_flag_int_protocol) &&
(id->bInterfaceProtocol != info->bInterfaceProtocol)) {
continue;
}
/* We found a match! */
return (id);
}
done:
return (NULL);
}
/*------------------------------------------------------------------------*
* usbd_lookup_id_by_uaa - factored out code
*
* Return values:
* 0: Success
* Else: Failure
*------------------------------------------------------------------------*/
int
usbd_lookup_id_by_uaa(const struct usb_device_id *id, usb_size_t sizeof_id,
struct usb_attach_arg *uaa)
{
id = usbd_lookup_id_by_info(id, sizeof_id, &uaa->info);
if (id) {
/* copy driver info */
uaa->driver_info = id->driver_info;
return (0);
}
return (ENXIO);
}
/*------------------------------------------------------------------------*
* Export the USB device ID format we use to userspace tools.
*------------------------------------------------------------------------*/
#if BYTE_ORDER == LITTLE_ENDIAN
#define U16_XOR "0"
#else
#define U16_XOR "8"
#endif
#if defined(KLD_MODULE) && (USB_HAVE_ID_SECTION != 0)
static const char __section("bus_autoconf_format") __used usb_id_format[] = {
/* Declare that three different sections use the same format */
"usb_host_id{256,:}"
"usb_device_id{256,:}"
"usb_dual_id{256,:}"
/* List size of fields in the usb_device_id structure */
"mf_vendor{" U16_XOR ",1}"
"mf_product{" U16_XOR ",1}"
"mf_dev_lo{" U16_XOR ",1}"
"mf_dev_hi{" U16_XOR ",1}"
"mf_dev_class{" U16_XOR ",1}"
"mf_dev_subclass{" U16_XOR ",1}"
"mf_dev_protocol{" U16_XOR ",1}"
"mf_int_class{" U16_XOR ",1}"
"mf_int_subclass{" U16_XOR ",1}"
"mf_int_protocol{" U16_XOR ",1}"
"unused{" U16_XOR ",6}"
"idVendor[0]{" U16_XOR ",8}"
"idVendor[1]{" U16_XOR ",8}"
"idProduct[0]{" U16_XOR ",8}"
"idProduct[1]{" U16_XOR ",8}"
"bcdDevice_lo[0]{" U16_XOR ",8}"
"bcdDevice_lo[1]{" U16_XOR ",8}"
"bcdDevice_hi[0]{" U16_XOR ",8}"
"bcdDevice_hi[1]{" U16_XOR ",8}"
"bDeviceClass{0,8}"
"bDeviceSubClass{0,8}"
"bDeviceProtocol{0,8}"
"bInterfaceClass{0,8}"
"bInterfaceSubClass{0,8}"
"bInterfaceProtocol{0,8}"
#if USB_HAVE_COMPAT_LINUX
"mfl_vendor{" U16_XOR ",1}"
"mfl_product{" U16_XOR ",1}"
"mfl_dev_lo{" U16_XOR ",1}"
"mfl_dev_hi{" U16_XOR ",1}"
"mfl_dev_class{" U16_XOR ",1}"
"mfl_dev_subclass{" U16_XOR ",1}"
"mfl_dev_protocol{" U16_XOR ",1}"
"mfl_int_class{" U16_XOR ",1}"
"mfl_int_subclass{" U16_XOR ",1}"
"mfl_int_protocol{" U16_XOR ",1}"
"unused{" U16_XOR ",6}"
#endif
};
#endif
@@ -0,0 +1,107 @@
/* $FreeBSD$ */
/*-
* 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.
*/
#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/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_device.h>
/*------------------------------------------------------------------------*
* usb_pause_mtx - factored out code
*
* This function will delay the code by the passed number of system
* ticks. The passed mutex "mtx" will be dropped while waiting, if
* "mtx" is different from NULL.
*------------------------------------------------------------------------*/
void
usb_pause_mtx(struct mtx *mtx, int timo)
{
if (mtx != NULL)
mtx_unlock(mtx);
/*
* Add one tick to the timeout so that we don't return too
* early! Note that pause() will assert that the passed
* timeout is positive and non-zero!
*/
pause("USBWAIT", timo + 1);
if (mtx != NULL)
mtx_lock(mtx);
}
/*------------------------------------------------------------------------*
* usb_printbcd
*
* This function will print the version number "bcd" to the string
* pointed to by "p" having a maximum length of "p_len" bytes
* including the terminating zero.
*------------------------------------------------------------------------*/
void
usb_printbcd(char *p, uint16_t p_len, uint16_t bcd)
{
if (snprintf(p, p_len, "%x.%02x", bcd >> 8, bcd & 0xff)) {
/* ignore any errors */
}
}
/*------------------------------------------------------------------------*
* usb_trim_spaces
*
* This function removes spaces at the beginning and the end of the string
* pointed to by the "p" argument.
*------------------------------------------------------------------------*/
void
usb_trim_spaces(char *p)
{
char *q;
char *e;
if (p == NULL)
return;
q = e = p;
while (*q == ' ') /* skip leading spaces */
q++;
while ((*p = *q++)) /* copy string */
if (*p++ != ' ') /* remember last non-space */
e = p;
*e = 0; /* kill trailing spaces */
}
+485
View File
@@ -0,0 +1,485 @@
/*
* Copyright 2022, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#include <sys/condvar.h>
extern "C" {
#include <sys/mutex.h>
#include <sys/systm.h>
#include <sys/taskqueue.h>
#include <sys/priority.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_device.h>
#include "device.h"
}
// undo name remappings, so we can use both FreeBSD and Haiku ones in this file
#undef usb_device
#undef usb_interface
#undef usb_endpoint_descriptor
#include <USB3.h>
usb_module_info* sUSB = NULL;
struct taskqueue* sUSBTaskqueue = NULL;
status_t
init_usb()
{
if (sUSB != NULL)
return B_OK;
if (get_module(B_USB_MODULE_NAME, (module_info**)&sUSB) != B_OK) {
dprintf("cannot get module \"%s\"\n", B_USB_MODULE_NAME);
return B_ERROR;
}
return B_OK;
}
void
uninit_usb()
{
if (sUSB != NULL)
put_module(B_USB_MODULE_NAME);
if (sUSBTaskqueue != NULL)
taskqueue_free(sUSBTaskqueue);
sUSB = NULL;
sUSBTaskqueue = NULL;
}
status_t
get_next_usb_device(uint32* cookie, freebsd_usb_device* result)
{
// We cheat here: since USB IDs are sequential, instead of doing a
// complicated parent/child iteration dance, we simply request device
// descriptors and let the USB stack figure out the rest.
//
// It would be better if we used USB->register_driver, but that is not
// an option at present for a variety of reasons...
const usb_configuration_info* config;
usb_device current;
while (*cookie < 1024) {
current = *cookie;
*cookie = *cookie + 1;
config = sUSB->get_configuration(current);
if (config != NULL)
break;
}
if (config == NULL)
return ENODEV;
result->haiku_usb_device = current;
result->endpoints_max = 0;
for (size_t i = 0; i < config->interface_count; i++) {
usb_interface_info* iface = config->interface[i].active;
for (size_t j = 0; j < iface->endpoint_count; j++) {
const int rep = result->endpoints_max++;
result->endpoints[rep].iface_index = i;
static_assert(sizeof(freebsd_usb_endpoint_descriptor)
== sizeof(usb_endpoint_descriptor), "size mismatch");
if (result->endpoints[rep].edesc == NULL)
result->endpoints[rep].edesc = new freebsd_usb_endpoint_descriptor;
memcpy(result->endpoints[rep].edesc, iface->endpoint[j].descr,
sizeof(usb_endpoint_descriptor));
}
}
return B_OK;
}
status_t
get_usb_device_attach_arg(struct freebsd_usb_device* device, struct usb_attach_arg* uaa)
{
memset(uaa, 0, sizeof(struct usb_attach_arg));
const usb_device_descriptor* device_desc =
sUSB->get_device_descriptor(device->haiku_usb_device);
if (!device_desc)
return B_BAD_VALUE;
uaa->info.idVendor = device_desc->vendor_id;
uaa->info.idProduct = device_desc->product_id;
uaa->info.bcdDevice = device_desc->device_version;
uaa->info.bDeviceClass = device_desc->device_class;
uaa->info.bDeviceSubClass = device_desc->device_subclass;
uaa->info.bDeviceProtocol = device_desc->device_protocol;
const usb_configuration_info* config = sUSB->get_configuration(device->haiku_usb_device);
if (!device_desc)
return B_BAD_VALUE;
// TODO: represent more than just interface[0], but how?
usb_interface_info* iface = config->interface[0].active;
uaa->info.bInterfaceClass = iface->descr->interface_class;
uaa->info.bInterfaceSubClass = iface->descr->interface_subclass;
uaa->info.bInterfaceProtocol = iface->descr->interface_protocol;
// TODO: bIface{Index,Num}, bConfig{Index,Num}
uaa->device = device;
uaa->iface = NULL;
// TODO: fetch values for these?
uaa->usb_mode = USB_MODE_HOST;
uaa->port = 1;
uaa->dev_state = UAA_DEV_READY;
return B_OK;
}
void
usb_cleanup_device(freebsd_usb_device* udev)
{
for (int i = 0; i < USB_MAX_EP_UNITS; i++) {
delete udev->endpoints[i].edesc;
udev->endpoints[i].edesc = NULL;
}
}
static usb_error_t
map_usb_error(status_t err)
{
switch (err) {
case B_OK: return USB_ERR_NORMAL_COMPLETION;
case B_DEV_STALLED: return USB_ERR_STALLED;
case B_CANCELED: return USB_ERR_CANCELLED;
}
return USB_ERR_INVAL;
}
extern "C" usb_error_t
usbd_do_request_flags(struct freebsd_usb_device* udev, struct mtx* mtx,
struct usb_device_request* req, void* data, uint16_t flags,
uint16_t* actlen, usb_timeout_t timeout)
{
if (mtx != NULL)
mtx_unlock(mtx);
// FIXME: timeouts
// TODO: flags
size_t actualLen = 0;
status_t ret = sUSB->send_request((usb_device)udev->haiku_usb_device,
req->bmRequestType, req->bRequest,
UGETW(req->wValue), UGETW(req->wIndex), UGETW(req->wLength),
data, &actualLen);
if (actlen)
*actlen = actualLen;
if (mtx != NULL)
mtx_lock(mtx);
return map_usb_error(ret);
}
enum usb_dev_speed
usbd_get_speed(struct freebsd_usb_device* udev)
{
const usb_device_descriptor* descriptor = sUSB->get_device_descriptor(
(usb_device)udev->haiku_usb_device);
KASSERT(descriptor != NULL, ("no device"));
if (descriptor->usb_version >= 0x0300)
return USB_SPEED_SUPER;
else if (descriptor->usb_version >= 0x200)
return USB_SPEED_HIGH;
else if (descriptor->usb_version >= 0x110)
return USB_SPEED_FULL;
else if (descriptor->usb_version >= 0x100)
return USB_SPEED_LOW;
panic("unknown USB version!");
return (usb_dev_speed)-1;
}
struct usb_xfer {
struct mtx* mutex;
void* priv_sc;
usb_callback_t* callback;
usb_xfer_flags flags;
usb_frlength_t max_data_length;
usb_device device;
uint8 type;
usb_pipe pipe;
iovec* frames;
int nframes;
uint8 usb_state;
bool in_progress;
status_t result;
int transferred_length;
struct task invoker;
struct cv condition;
};
extern "C" usb_error_t
usbd_transfer_setup(struct freebsd_usb_device* udev,
const uint8_t* ifaces, struct usb_xfer** ppxfer,
const struct usb_config* setup_start, uint16_t n_setup,
void* priv_sc, struct mtx* xfer_mtx)
{
if (xfer_mtx == NULL)
xfer_mtx = &Giant;
// Make sure the taskqueue exists.
if (sUSBTaskqueue == NULL) {
mtx_lock(&Giant);
if (sUSBTaskqueue == NULL) {
sUSBTaskqueue = taskqueue_create("usb taskq", 0,
taskqueue_thread_enqueue, &sUSBTaskqueue);
taskqueue_start_threads(&sUSBTaskqueue, 1, PZERO, "usb taskq");
}
mtx_unlock(&Giant);
}
const usb_configuration_info* device_config = sUSB->get_configuration(
(usb_device)udev->haiku_usb_device);
for (const struct usb_config* setup = setup_start;
setup < (setup_start + n_setup); setup++) {
/* skip transfers w/o callbacks */
if (setup->callback == NULL)
continue;
struct usb_xfer* xfer = new usb_xfer;
xfer->mutex = xfer_mtx;
xfer->priv_sc = priv_sc;
xfer->callback = setup->callback;
xfer->flags = setup->flags;
xfer->max_data_length = setup->bufsize;
xfer->device = (usb_device)udev->haiku_usb_device;
xfer->type = setup->type;
xfer->pipe = -1;
uint8_t endpoint = setup->endpoint;
uint8_t iface_index = ifaces[setup->if_index];
if (endpoint == UE_ADDR_ANY) {
for (int i = 0; i < udev->endpoints_max; i++) {
if (UE_GET_XFERTYPE(udev->endpoints[i].edesc->bmAttributes) != xfer->type)
continue;
endpoint = udev->endpoints[i].edesc->bEndpointAddress;
break;
}
}
usb_interface_info* iface = device_config->interface[iface_index].active;
for (int i = 0; i < iface->endpoint_count; i++) {
if (iface->endpoint[i].descr->endpoint_address != endpoint)
continue;
xfer->pipe = iface->endpoint[i].handle;
break;
}
if (xfer->pipe == -1)
panic("failed to locate endpoint!");
xfer->nframes = setup->frames;
if (xfer->nframes == 0)
xfer->nframes = 1;
xfer->frames = (iovec*)calloc(xfer->nframes, sizeof(iovec));
xfer->usb_state = USB_ST_SETUP;
xfer->in_progress = false;
xfer->transferred_length = 0;
cv_init(&xfer->condition, "FreeBSD USB transfer");
if (xfer->flags.proxy_buffer)
panic("not yet supported");
ppxfer[setup - setup_start] = xfer;
}
return USB_ERR_NORMAL_COMPLETION;
}
extern "C" void
usbd_transfer_unsetup(struct usb_xfer** pxfer, uint16_t n_setup)
{
for (int i = 0; i < n_setup; i++) {
struct usb_xfer* xfer = pxfer[i];
usbd_transfer_drain(xfer);
cv_destroy(&xfer->condition);
free(xfer->frames);
delete xfer;
}
}
extern "C" usb_frlength_t
usbd_xfer_max_len(struct usb_xfer* xfer)
{
return xfer->max_data_length;
}
extern "C" void*
usbd_xfer_softc(struct usb_xfer* xfer)
{
return xfer->priv_sc;
}
extern "C" uint8_t
usbd_xfer_state(struct usb_xfer* xfer)
{
return xfer->usb_state;
}
extern "C" void
usbd_xfer_set_frame_data(struct usb_xfer* xfer,
usb_frcount_t frindex, void* ptr, usb_frlength_t len)
{
KASSERT(frindex < uint32_t(xfer->nframes), ("frame index overflow"));
xfer->frames[frindex].iov_base = ptr;
xfer->frames[frindex].iov_len = len;
}
extern "C" void
usbd_xfer_set_stall(struct usb_xfer *xfer)
{
// Not needed?
}
static void
usbd_invoker(void* arg, int pending)
{
struct usb_xfer* xfer = (struct usb_xfer*)arg;
mtx_lock(xfer->mutex);
xfer->in_progress = false;
xfer->usb_state = (xfer->result == B_OK) ? USB_ST_TRANSFERRED : USB_ST_ERROR;
xfer->callback(xfer, map_usb_error(xfer->result));
mtx_unlock(xfer->mutex);
cv_signal(&xfer->condition);
}
static void
usbd_callback(void* arg, status_t status, void* data, size_t actualLength)
{
struct usb_xfer* xfer = (struct usb_xfer*)arg;
xfer->result = status;
xfer->transferred_length = actualLength;
TASK_INIT(&xfer->invoker, 0, usbd_invoker, xfer);
taskqueue_enqueue(sUSBTaskqueue, &xfer->invoker);
}
extern "C" void
usbd_transfer_start(struct usb_xfer* xfer)
{
if (xfer->in_progress)
return;
xfer->usb_state = USB_ST_SETUP;
xfer->callback(xfer, USB_ERR_NOT_STARTED);
}
extern "C" void
usbd_transfer_submit(struct usb_xfer* xfer)
{
KASSERT(!xfer->in_progress, ("cannot submit in-progress transfer!"));
xfer->transferred_length = 0;
xfer->in_progress = true;
switch (xfer->type) {
case UE_BULK:
sUSB->queue_bulk_v(xfer->pipe, xfer->frames, xfer->nframes, usbd_callback, xfer);
break;
case UE_INTERRUPT:
KASSERT(xfer->nframes == 1, ("invalid frame count for interrupt transfer"));
sUSB->queue_interrupt(xfer->pipe, xfer->frames[0].iov_base, xfer->frames[0].iov_len,
usbd_callback, xfer);
break;
default:
panic("unhandled pipe type %d", xfer->type);
}
}
extern "C" void
usbd_transfer_stop(struct usb_xfer* xfer)
{
if (xfer == NULL)
return;
mtx_assert(xfer->mutex, MA_OWNED);
if (!xfer->in_progress)
return;
// Unfortunately we have no way of cancelling just one transfer.
sUSB->cancel_queued_transfers(xfer->pipe);
}
extern "C" void
usbd_transfer_drain(struct usb_xfer* xfer)
{
if (xfer == NULL)
return;
mtx_lock(xfer->mutex);
usbd_transfer_stop(xfer);
while (xfer->in_progress)
cv_wait(&xfer->condition, xfer->mutex);
mtx_unlock(xfer->mutex);
}
extern "C" void
usbd_xfer_status(struct usb_xfer* xfer, int* actlen, int* sumlen, int* aframes, int* nframes)
{
if (actlen)
*actlen = xfer->transferred_length;
if (sumlen) {
int sum = 0;
for (int i = 0; i < xfer->nframes; i++)
sum += xfer->frames[i].iov_len;
*sumlen = sum;
}
if (aframes) {
int length = xfer->transferred_length;
int frames = 0;
for (int i = 0; i < xfer->nframes && length > 0; i++) {
length -= xfer->frames[i].iov_len;
if (length >= 0)
frames++;
}
*aframes = frames;
}
if (nframes)
*nframes = xfer->nframes;
}
@@ -0,0 +1,44 @@
/*
* Copyright 2022, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#include <sys/cdefs.h>
#include <sys/callout.h>
#include <sys/bus.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
/** device_set_usb_desc
* This function can be called at probe or attach to set the USB
* device supplied textual description for the given device. */
void
device_set_usb_desc(device_t dev)
{
struct usb_attach_arg *uaa;
struct usb_device *udev;
struct usb_interface *iface;
usb_error_t err;
if (dev == NULL) {
/* should not happen */
return;
}
uaa = device_get_ivars(dev);
if (uaa == NULL) {
/* can happen if called at the wrong time */
return;
}
udev = uaa->device;
iface = uaa->iface;
if ((iface == NULL)) {
err = USB_ERR_INVAL;
} else {
err = 0;
}
/* TODO */
}