diff options
Diffstat (limited to 'lib/libusb')
-rw-r--r-- | lib/libusb/Makefile | 276 | ||||
-rw-r--r-- | lib/libusb/Makefile.depend | 16 | ||||
-rw-r--r-- | lib/libusb/libusb-0.1.pc.in | 11 | ||||
-rw-r--r-- | lib/libusb/libusb-1.0.pc.in | 11 | ||||
-rw-r--r-- | lib/libusb/libusb-2.0.pc.in | 11 | ||||
-rw-r--r-- | lib/libusb/libusb.3 | 882 | ||||
-rw-r--r-- | lib/libusb/libusb.h | 647 | ||||
-rw-r--r-- | lib/libusb/libusb01.c | 1025 | ||||
-rw-r--r-- | lib/libusb/libusb10.c | 1927 | ||||
-rw-r--r-- | lib/libusb/libusb10.h | 155 | ||||
-rw-r--r-- | lib/libusb/libusb10_desc.c | 765 | ||||
-rw-r--r-- | lib/libusb/libusb10_hotplug.c | 434 | ||||
-rw-r--r-- | lib/libusb/libusb10_io.c | 872 | ||||
-rw-r--r-- | lib/libusb/libusb20.3 | 1079 | ||||
-rw-r--r-- | lib/libusb/libusb20.c | 1416 | ||||
-rw-r--r-- | lib/libusb/libusb20.h | 317 | ||||
-rw-r--r-- | lib/libusb/libusb20_desc.c | 796 | ||||
-rw-r--r-- | lib/libusb/libusb20_desc.h | 608 | ||||
-rw-r--r-- | lib/libusb/libusb20_int.h | 240 | ||||
-rw-r--r-- | lib/libusb/libusb20_ugen20.c | 1086 | ||||
-rw-r--r-- | lib/libusb/libusb_global_linux.h | 87 | ||||
-rw-r--r-- | lib/libusb/usb.h | 315 |
22 files changed, 12976 insertions, 0 deletions
diff --git a/lib/libusb/Makefile b/lib/libusb/Makefile new file mode 100644 index 000000000000..4a0c392f0d8f --- /dev/null +++ b/lib/libusb/Makefile @@ -0,0 +1,276 @@ +# +# +# Makefile for the FreeBSD specific LibUSB 2.0 +# + +LIB= usb +SHLIB_MAJOR= 3 +SHLIB_MINOR= 0 +SRCS= libusb20.c +SRCS+= libusb20_desc.c +SRCS+= libusb20_ugen20.c +INCS+= libusb20.h +INCS+= libusb20_desc.h +MAN= libusb.3 libusb20.3 +MKLINT= no +NOGCCERROR= +PTHREAD_LIBS?= -lpthread + +WARNS?= 2 + +LIBADD= pthread + +MLINKS+= libusb.3 usb.3 + +# libusb 0.1 compat +INCS+= usb.h +SRCS+= libusb01.c + +# libusb 1.0 compat +INCS+= libusb.h +SRCS+= libusb10.c +SRCS+= libusb10_desc.c +SRCS+= libusb10_hotplug.c +SRCS+= libusb10_io.c + +PCFILES= libusb-0.1.pc libusb-1.0.pc libusb-2.0.pc +.for pcfile in ${PCFILES} +${pcfile}: ${pcfile}.in + sed -e 's,@libdir@,${LIBDIR},g ; s,@sharedlibdir@,${SHLIBDIR},g ; \ + s,@includedir@,${INCLUDEDIR},g ;' ${.ALLSRC} > ${.TARGET} +.endfor +CLEANFILES+= ${PCFILES} + +# +# Cross platform support +# +# Examples: +# make LIBUSB_GLOBAL_INCLUDE_FILE=libusb_global_linux.h +# make \ +# LIBUSB_GLOBAL_INCLUDE_FILE=libusb_global_linux.h \ +# DEBUG_FLAGS="-g" +# +# From Ubuntu 10.04: +# freebsd-make LIBUSB_GLOBAL_INCLUDE_FILE=libusb_global_linux.h \ +# PTHREAD_LIBS="-lpthread -lrt" +# freebsd-make \ +# LIBUSB_GLOBAL_INCLUDE_FILE=libusb_global_linux.h \ +# PTHREAD_LIBS="-lpthread -lrt" +# +# +.if defined(LIBUSB_GLOBAL_INCLUDE_FILE) +CFLAGS+= -DLIBUSB_GLOBAL_INCLUDE_FILE=\"${LIBUSB_GLOBAL_INCLUDE_FILE}\" +CFLAGS+= -DUSB_GLOBAL_INCLUDE_FILE=\"${LIBUSB_GLOBAL_INCLUDE_FILE}\" +CFLAGS+= -I ../../sys +.endif + +# LibUSB v1.0 +MLINKS += libusb.3 libusb_get_version.3 +MLINKS += libusb.3 libusb_init.3 +MLINKS += libusb.3 libusb_init_context.3 +MLINKS += libusb.3 libusb_exit.3 +MLINKS += libusb.3 libusb_has_capability.3 +MLINKS += libusb.3 libusb_strerror.3 +MLINKS += libusb.3 libusb_error_name.3 +MLINKS += libusb.3 libusb_set_debug.3 +MLINKS += libusb.3 libusb_get_device_list.3 +MLINKS += libusb.3 libusb_free_device_list.3 +MLINKS += libusb.3 libusb_get_bus_number.3 +MLINKS += libusb.3 libusb_get_port_number.3 +MLINKS += libusb.3 libusb_get_device_address.3 +MLINKS += libusb.3 libusb_get_device_speed.3 +MLINKS += libusb.3 libusb_get_max_packet_size.3 +MLINKS += libusb.3 libusb_get_max_iso_packet_size.3 +MLINKS += libusb.3 libusb_ref_device.3 +MLINKS += libusb.3 libusb_unref_device.3 +MLINKS += libusb.3 libusb_open.3 +MLINKS += libusb.3 libusb_open_device_with_vid_pid.3 +MLINKS += libusb.3 libusb_close.3 +MLINKS += libusb.3 libusb_get_device.3 +MLINKS += libusb.3 libusb_get_configuration.3 +MLINKS += libusb.3 libusb_set_configuration.3 +MLINKS += libusb.3 libusb_claim_interface.3 +MLINKS += libusb.3 libusb_release_interface.3 +MLINKS += libusb.3 libusb_set_interface_alt_setting.3 +MLINKS += libusb.3 libusb_clear_halt.3 +MLINKS += libusb.3 libusb_reset_device.3 +MLINKS += libusb.3 libusb_check_connected.3 +MLINKS += libusb.3 libusb_kernel_driver_active.3 +MLINKS += libusb.3 libusb_get_driver.3 +MLINKS += libusb.3 libusb_get_driver_np.3 +MLINKS += libusb.3 libusb_detach_kernel_driver.3 +MLINKS += libusb.3 libusb_detach_kernel_driver_np.3 +MLINKS += libusb.3 libusb_attach_kernel_driver.3 +MLINKS += libusb.3 libusb_set_auto_detach_kernel_driver.3 +MLINKS += libusb.3 libusb_get_device_descriptor.3 +MLINKS += libusb.3 libusb_get_active_config_descriptor.3 +MLINKS += libusb.3 libusb_get_config_descriptor.3 +MLINKS += libusb.3 libusb_get_config_descriptor_by_value.3 +MLINKS += libusb.3 libusb_free_config_descriptor.3 +MLINKS += libusb.3 libusb_get_string_descriptor.3 +MLINKS += libusb.3 libusb_get_string_descriptor_ascii.3 +MLINKS += libusb.3 libusb_parse_ss_endpoint_comp.3 +MLINKS += libusb.3 libusb_free_ss_endpoint_comp.3 +MLINKS += libusb.3 libusb_get_ss_endpoint_companion_descriptor.3 +MLINKS += libusb.3 libusb_free_ss_endpoint_companion_descriptor.3 +MLINKS += libusb.3 libusb_parse_bos_descriptor.3 +MLINKS += libusb.3 libusb_free_bos_descriptor.3 +MLINKS += libusb.3 libusb_get_usb_2_0_extension_descriptor.3 +MLINKS += libusb.3 libusb_free_usb_2_0_extension_descriptor.3 +MLINKS += libusb.3 libusb_get_ss_usb_device_capability_descriptor.3 +MLINKS += libusb.3 libusb_free_ss_usb_device_capability_descriptor.3 +MLINKS += libusb.3 libusb_get_container_id_descriptor.3 +MLINKS += libusb.3 libusb_free_container_id_descriptor.3 +MLINKS += libusb.3 libusb_alloc_streams.3 +MLINKS += libusb.3 libusb_free_streams.3 +MLINKS += libusb.3 libusb_alloc_transfer.3 +MLINKS += libusb.3 libusb_free_transfer.3 +MLINKS += libusb.3 libusb_transfer_set_stream_id.3 +MLINKS += libusb.3 libusb_transfer_get_stream_id.3 +MLINKS += libusb.3 libusb_submit_transfer.3 +MLINKS += libusb.3 libusb_cancel_transfer.3 +MLINKS += libusb.3 libusb_control_transfer.3 +MLINKS += libusb.3 libusb_bulk_transfer.3 +MLINKS += libusb.3 libusb_interrupt_transfer.3 +MLINKS += libusb.3 libusb_try_lock_events.3 +MLINKS += libusb.3 libusb_lock_events.3 +MLINKS += libusb.3 libusb_unlock_events.3 +MLINKS += libusb.3 libusb_event_handling_ok.3 +MLINKS += libusb.3 libusb_event_handler_active.3 +MLINKS += libusb.3 libusb_interrupt_event_handler.3 +MLINKS += libusb.3 libusb_lock_event_waiters.3 +MLINKS += libusb.3 libusb_unlock_event_waiters.3 +MLINKS += libusb.3 libusb_wait_for_event.3 +MLINKS += libusb.3 libusb_handle_events_timeout_completed.3 +MLINKS += libusb.3 libusb_handle_events_completed.3 +MLINKS += libusb.3 libusb_handle_events_timeout.3 +MLINKS += libusb.3 libusb_handle_events.3 +MLINKS += libusb.3 libusb_handle_events_locked.3 +MLINKS += libusb.3 libusb_get_next_timeout.3 +MLINKS += libusb.3 libusb_set_pollfd_notifiers.3 +MLINKS += libusb.3 libusb_get_pollfds.3 +MLINKS += libusb.3 libusb_hotplug_register_callback.3 +MLINKS += libusb.3 libusb_hotplug_deregister_callback.3 + +# LibUSB v0.1 +MLINKS += libusb.3 usb_open.3 +MLINKS += libusb.3 usb_close.3 +MLINKS += libusb.3 usb_get_string.3 +MLINKS += libusb.3 usb_get_string_simple.3 +MLINKS += libusb.3 usb_get_descriptor_by_endpoint.3 +MLINKS += libusb.3 usb_get_descriptor.3 +MLINKS += libusb.3 usb_parse_descriptor.3 +MLINKS += libusb.3 usb_parse_configuration.3 +MLINKS += libusb.3 usb_destroy_configuration.3 +MLINKS += libusb.3 usb_fetch_and_parse_descriptors.3 +MLINKS += libusb.3 usb_bulk_write.3 +MLINKS += libusb.3 usb_bulk_read.3 +MLINKS += libusb.3 usb_interrupt_write.3 +MLINKS += libusb.3 usb_interrupt_read.3 +MLINKS += libusb.3 usb_control_msg.3 +MLINKS += libusb.3 usb_set_configuration.3 +MLINKS += libusb.3 usb_claim_interface.3 +MLINKS += libusb.3 usb_release_interface.3 +MLINKS += libusb.3 usb_set_altinterface.3 +MLINKS += libusb.3 usb_resetep.3 +MLINKS += libusb.3 usb_clear_halt.3 +MLINKS += libusb.3 usb_reset.3 +MLINKS += libusb.3 usb_strerror.3 +MLINKS += libusb.3 usb_init.3 +MLINKS += libusb.3 usb_set_debug.3 +MLINKS += libusb.3 usb_find_busses.3 +MLINKS += libusb.3 usb_find_devices.3 +MLINKS += libusb.3 usb_device.3 +MLINKS += libusb.3 usb_get_busses.3 +MLINKS += libusb.3 usb_check_connected.3 + +# LibUSB v2.0 +MLINKS += libusb20.3 libusb20_tr_close.3 +MLINKS += libusb20.3 libusb20_tr_open.3 +MLINKS += libusb20.3 libusb20_tr_get_pointer.3 +MLINKS += libusb20.3 libusb20_tr_get_time_complete.3 +MLINKS += libusb20.3 libusb20_tr_get_actual_frames.3 +MLINKS += libusb20.3 libusb20_tr_get_actual_length.3 +MLINKS += libusb20.3 libusb20_tr_get_max_frames.3 +MLINKS += libusb20.3 libusb20_tr_get_max_packet_length.3 +MLINKS += libusb20.3 libusb20_tr_get_max_total_length.3 +MLINKS += libusb20.3 libusb20_tr_get_status.3 +MLINKS += libusb20.3 libusb20_tr_pending.3 +MLINKS += libusb20.3 libusb20_tr_callback_wrapper.3 +MLINKS += libusb20.3 libusb20_tr_clear_stall_sync.3 +MLINKS += libusb20.3 libusb20_tr_drain.3 +MLINKS += libusb20.3 libusb20_tr_set_buffer.3 +MLINKS += libusb20.3 libusb20_tr_set_callback.3 +MLINKS += libusb20.3 libusb20_tr_set_flags.3 +MLINKS += libusb20.3 libusb20_tr_get_length.3 +MLINKS += libusb20.3 libusb20_tr_set_length.3 +MLINKS += libusb20.3 libusb20_tr_set_priv_sc0.3 +MLINKS += libusb20.3 libusb20_tr_set_priv_sc1.3 +MLINKS += libusb20.3 libusb20_tr_set_timeout.3 +MLINKS += libusb20.3 libusb20_tr_set_total_frames.3 +MLINKS += libusb20.3 libusb20_tr_setup_bulk.3 +MLINKS += libusb20.3 libusb20_tr_setup_control.3 +MLINKS += libusb20.3 libusb20_tr_setup_intr.3 +MLINKS += libusb20.3 libusb20_tr_setup_isoc.3 +MLINKS += libusb20.3 libusb20_tr_bulk_intr_sync.3 +MLINKS += libusb20.3 libusb20_tr_start.3 +MLINKS += libusb20.3 libusb20_tr_stop.3 +MLINKS += libusb20.3 libusb20_tr_submit.3 +MLINKS += libusb20.3 libusb20_tr_get_priv_sc0.3 +MLINKS += libusb20.3 libusb20_tr_get_priv_sc1.3 +MLINKS += libusb20.3 libusb20_dev_get_backend_name.3 +MLINKS += libusb20.3 libusb20_dev_get_info.3 +MLINKS += libusb20.3 libusb20_dev_get_iface_desc.3 +MLINKS += libusb20.3 libusb20_dev_get_desc.3 +MLINKS += libusb20.3 libusb20_dev_get_stats.3 +MLINKS += libusb20.3 libusb20_dev_close.3 +MLINKS += libusb20.3 libusb20_dev_detach_kernel_driver.3 +MLINKS += libusb20.3 libusb20_dev_set_config_index.3 +MLINKS += libusb20.3 libusb20_dev_get_debug.3 +MLINKS += libusb20.3 libusb20_dev_get_fd.3 +MLINKS += libusb20.3 libusb20_dev_kernel_driver_active.3 +MLINKS += libusb20.3 libusb20_dev_open.3 +MLINKS += libusb20.3 libusb20_dev_process.3 +MLINKS += libusb20.3 libusb20_dev_request_sync.3 +MLINKS += libusb20.3 libusb20_dev_req_string_sync.3 +MLINKS += libusb20.3 libusb20_dev_req_string_simple_sync.3 +MLINKS += libusb20.3 libusb20_dev_reset.3 +MLINKS += libusb20.3 libusb20_dev_check_connected.3 +MLINKS += libusb20.3 libusb20_dev_set_power_mode.3 +MLINKS += libusb20.3 libusb20_dev_get_power_mode.3 +MLINKS += libusb20.3 libusb20_dev_get_port_path.3 +MLINKS += libusb20.3 libusb20_dev_get_power_usage.3 +MLINKS += libusb20.3 libusb20_dev_set_alt_index.3 +MLINKS += libusb20.3 libusb20_dev_get_device_desc.3 +MLINKS += libusb20.3 libusb20_dev_alloc_config.3 +MLINKS += libusb20.3 libusb20_dev_alloc.3 +MLINKS += libusb20.3 libusb20_dev_get_address.3 +MLINKS += libusb20.3 libusb20_dev_get_parent_address.3 +MLINKS += libusb20.3 libusb20_dev_get_parent_port.3 +MLINKS += libusb20.3 libusb20_dev_get_bus_number.3 +MLINKS += libusb20.3 libusb20_dev_get_mode.3 +MLINKS += libusb20.3 libusb20_dev_get_speed.3 +MLINKS += libusb20.3 libusb20_dev_get_config_index.3 +MLINKS += libusb20.3 libusb20_dev_free.3 +MLINKS += libusb20.3 libusb20_dev_set_debug.3 +MLINKS += libusb20.3 libusb20_dev_wait_process.3 +MLINKS += libusb20.3 libusb20_be_get_template.3 +MLINKS += libusb20.3 libusb20_be_set_template.3 +MLINKS += libusb20.3 libusb20_be_get_dev_quirk.3 +MLINKS += libusb20.3 libusb20_be_get_quirk_name.3 +MLINKS += libusb20.3 libusb20_be_add_dev_quirk.3 +MLINKS += libusb20.3 libusb20_be_remove_dev_quirk.3 +MLINKS += libusb20.3 libusb20_be_alloc_default.3 +MLINKS += libusb20.3 libusb20_be_device_foreach.3 +MLINKS += libusb20.3 libusb20_be_dequeue_device.3 +MLINKS += libusb20.3 libusb20_be_enqueue_device.3 +MLINKS += libusb20.3 libusb20_be_free.3 +MLINKS += libusb20.3 libusb20_me_get_1.3 +MLINKS += libusb20.3 libusb20_me_get_2.3 +MLINKS += libusb20.3 libusb20_me_encode.3 +MLINKS += libusb20.3 libusb20_me_decode.3 +MLINKS += libusb20.3 libusb20_desc_foreach.3 +MLINKS += libusb20.3 libusb20_strerror.3 +MLINKS += libusb20.3 libusb20_error_name.3 + +.include <bsd.lib.mk> diff --git a/lib/libusb/Makefile.depend b/lib/libusb/Makefile.depend new file mode 100644 index 000000000000..577dc5747f1e --- /dev/null +++ b/lib/libusb/Makefile.depend @@ -0,0 +1,16 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/libthr \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/libusb/libusb-0.1.pc.in b/lib/libusb/libusb-0.1.pc.in new file mode 100644 index 000000000000..72b7a60eadd0 --- /dev/null +++ b/lib/libusb/libusb-0.1.pc.in @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=@libdir@ +sharedlibdir=@sharedlibdir@ +includedir=@includedir@ + +Name: libusb-0.1 +Description: Library that abstracts ways to access USB devices (v0.1) +Version: 0.1.0 +Libs: -L${libdir} -lusb +Cflags: -I${includedir} diff --git a/lib/libusb/libusb-1.0.pc.in b/lib/libusb/libusb-1.0.pc.in new file mode 100644 index 000000000000..3a2f27cc52b8 --- /dev/null +++ b/lib/libusb/libusb-1.0.pc.in @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=@libdir@ +sharedlibdir=@sharedlibdir@ +includedir=@includedir@ + +Name: libusb-1.0 +Description: Library that abstracts ways to access USB devices (v1.0) +Version: 1.0.16 +Libs: -L${libdir} -lusb +Cflags: -I${includedir} diff --git a/lib/libusb/libusb-2.0.pc.in b/lib/libusb/libusb-2.0.pc.in new file mode 100644 index 000000000000..4ac152bb09ff --- /dev/null +++ b/lib/libusb/libusb-2.0.pc.in @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=@libdir@ +sharedlibdir=@sharedlibdir@ +includedir=@includedir@ + +Name: libusb-2.0 +Description: Library that abstracts ways to access USB devices (v2.0) +Version: 2.0.0 +Libs: -L${libdir} -lusb +Cflags: -I${includedir} diff --git a/lib/libusb/libusb.3 b/lib/libusb/libusb.3 new file mode 100644 index 000000000000..607a7f645d95 --- /dev/null +++ b/lib/libusb/libusb.3 @@ -0,0 +1,882 @@ +.\" +.\" Copyright (c) 2009 Sylvestre Gallon +.\" +.\" 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. +.\" +.Dd July 9, 2025 +.Dt LIBUSB 3 +.Os +.Sh NAME +.Nm libusb +.Nd "USB access library" +.Sh LIBRARY +USB access library +.Pq libusb, -lusb +.Sh SYNOPSIS +.In libusb.h +.Sh DESCRIPTION +The +.Nm +library contains interfaces for directly managing a usb device. +The current implementation supports v1.0 of the libusb API. +.Sh LIBRARY INITIALISATION AND DEINITIALISATION +.Ft "const struct libusb_version *" +.Fn libusb_get_version "void" +This function returns version information about LibUSB. +.Pp +.Ft int +.Fn libusb_init "libusb_context **ctx" +Call this function before any other libusb v1.0 API function, to +initialise a valid libusb v1.0 context. +If the +.Fa ctx +argument is non-NULL, a pointer to the libusb context is stored at +the given location. +This function returns 0 upon success or LIBUSB_ERROR on failure. +.Pp +.Ft int +.Fn libusb_init_context "libusb_context **ctx" "const struct libusb_init_option []" "int num_options" +Call this function before any other libusb v1.0 API function, to +initialise a valid libusb v1.0 context. +If the +.Fa ctx +argument is non-NULL, a pointer to the libusb context is stored at +the given location. +Additional options, like the USB debug level, may be given using the +second and third argument. +If no options are needed, simply use libusb_init(). +This function returns 0 upon success or a LIBUSB_ERROR value on failure. +.Pp +.Ft void +.Fn libusb_exit "libusb_context *ctx" +Deinitialise libusb. +Must be called at the end of the application. +Other libusb routines may not be called after this function. +.Pp +.Ft int +.Fn libusb_has_capability "uint32_t capability" +This function checks the runtime capabilities of +.Nm . +This function will return non-zero if the given +.Fa capability +is supported, 0 if it is not supported. +The valid values for +.Fa capability +are: +.Bl -tag -width LIBUSB_CAP -offset indent +.It Va LIBUSB_CAP_HAS_CAPABILITY +.Nm +supports +.Fn libusb_has_capability . +.It Va LIBUSB_CAP_HAS_HOTPLUG +.Nm +supports hotplug notifications. +.It Va LIBUSB_CAP_HAS_HID_ACCESS +.Nm +can access HID devices without requiring user intervention. +.It Va LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER +.Nm +supports detaching of the default USB driver with +.Fn libusb_detach_kernel_driver . +.El +.Pp +.Ft const char * +.Fn libusb_strerror "int code" +Get the ASCII representation of the error given by the +.Fa code +argument. +This function does not return NULL. +.Pp +.Ft int +.Fn libusb_setlocale "const char *locale" +Set locale for the error message when using +.Fn libusb_strerror +to +.Ft locale . +Note other +.Nm +implementations only support the first two bytes, that means +.Ql en-US +is equivalent to +.Ql en-CA . +.Pp +.Ft const char * +.Fn libusb_error_name "int code" +Get the ASCII representation of the error enum given by the +.Fa code +argument. +This function does not return NULL. +.Pp +.Ft void +.Fn libusb_set_debug "libusb_context *ctx" "int level" +Set the debug level to +.Fa level . +.Sh DEVICE HANDLING AND ENUMERATION +.Ft ssize_t +.Fn libusb_get_device_list "libusb_context *ctx" "libusb_device ***list" +Populate +.Fa list +with the list of usb devices available, adding a reference to each +device in the list. +All the list entries created by this +function must have their reference counter +decremented when you are done with them, +and the list itself must be freed. +This +function returns the number of devices in the list or a LIBUSB_ERROR code. +.Pp +.Ft void +.Fn libusb_free_device_list "libusb_device **list" "int unref_devices" +Free the list of devices discovered by libusb_get_device_list. +If +.Fa unref_device +is set to 1 all devices in the list have their reference +counter decremented once. +.Pp +.Ft uint8_t +.Fn libusb_get_bus_number "libusb_device *dev" +Returns the number of the bus contained by the device +.Fa dev . +.Pp +.Ft uint8_t +.Fn libusb_get_port_number "libusb_device *dev" +Returns the port number which the device given by +.Fa dev +is attached to. +.Pp +.Ft int +.Fn libusb_get_port_numbers "libusb_device *dev" "uint8_t *buf" "uint8_t bufsize" +Stores, in the buffer +.Fa buf +of size +.Fa bufsize , +the list of all port numbers from root for the device +.Fa dev . +.Pp +.Ft int +.Fn libusb_get_port_path "libusb_context *ctx" "libusb_device *dev" "uint8_t *buf" "uint8_t bufsize" +Deprecated function equivalent to libusb_get_port_numbers. +.Pp +.Ft uint8_t +.Fn libusb_get_device_address "libusb_device *dev" +Returns the device_address contained by the device +.Fa dev . +.Pp +.Ft enum libusb_speed +.Fn libusb_get_device_speed "libusb_device *dev" +Returns the wire speed at which the device is connected. +See the LIBUSB_SPEED_XXX enums for more information. +LIBUSB_SPEED_UNKNOWN is returned in case of unknown wire speed. +.Pp +.Ft int +.Fn libusb_get_max_packet_size "libusb_device *dev" "unsigned char endpoint" +Returns the wMaxPacketSize value on success, LIBUSB_ERROR_NOT_FOUND if the +endpoint does not exist and LIBUSB_ERROR_OTHERS on other failure. +.Pp +.Ft int +.Fn libusb_get_max_iso_packet_size "libusb_device *dev" "unsigned char endpoint" +Returns the packet size multiplied by the packet multiplier on success, +LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist and +LIBUSB_ERROR_OTHERS on other failure. +.Pp +.Ft libusb_device * +.Fn libusb_ref_device "libusb_device *dev" +Increment the reference counter of the device +.Fa dev . +.Pp +.Ft void +.Fn libusb_unref_device "libusb_device *dev" +Decrement the reference counter of the device +.Fa dev . +.Pp +.Ft int +.Fn libusb_wrap_sys_device "libusb_context *ctx" "intptr_t sys_dev" "libusb_device_handle **dev_handle" +This function creates a libusb handler from a previously opened fd +.Fa sys_dev . +This function is provided for compatibility and is currently unimplemented. +It always returns +.Dv LIBUSB_ERROR_NOT_SUPPORTED . +.Pp +.Ft int +.Fn libusb_open "libusb_device *dev" "libusb_device_handle **devh" +Open a device and obtain a device_handle. +Returns 0 on success, +LIBUSB_ERROR_NO_MEM on memory allocation problems, LIBUSB_ERROR_ACCESS +on permissions problems, LIBUSB_ERROR_NO_DEVICE if the device has been +disconnected and a LIBUSB_ERROR code on other errors. +.Pp +.Ft libusb_device_handle * +.Fn libusb_open_device_with_vid_pid "libusb_context *ctx" "uint16_t vid" "uint16_t pid" +A convenience function to open a device by vendor and product IDs +.Fa vid +and +.Fa pid . +Returns NULL on error. +.Pp +.Ft void +.Fn libusb_close "libusb_device_handle *devh" +Close a device handle. +.Pp +.Ft libusb_device * +.Fn libusb_get_device "libusb_device_handle *devh" +Get the device contained by devh. +Returns NULL on error. +.Pp +.Ft libusb_device * +.Fn libusb_get_parent "libusb_device *dev" +Get dev's parent device. +Returns NULL if the device has no parent (i.e. is a root device). +.Pp +.Ft int +.Fn libusb_get_configuration "libusb_device_handle *devh" "int *config" +Returns the value of the current configuration. +Returns 0 +on success, LIBUSB_ERROR_NO_DEVICE if the device has been disconnected +and a LIBUSB_ERROR code on error. +.Pp +.Ft int +.Fn libusb_set_configuration "libusb_device_handle *devh" "int config" +Set the active configuration to +.Fa config +for the device contained by +.Fa devh . +This function returns 0 on success, LIBUSB_ERROR_NOT_FOUND if the requested +configuration does not exist, LIBUSB_ERROR_BUSY if the interfaces are currently +claimed, LIBUSB_ERROR_NO_DEVICE if the device has been disconnected and a +LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_claim_interface "libusb_device_handle *devh" "int interface_number" +Claim an interface in a given libusb_handle +.Fa devh . +This is a non-blocking function. +It returns 0 on success, LIBUSB_ERROR_NOT_FOUND +if the requested interface does not exist, LIBUSB_ERROR_BUSY if a program or +driver has claimed the interface, LIBUSB_ERROR_NO_DEVICE if the device has +been disconnected and a LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_release_interface "libusb_device_handle *devh" "int interface_number" +This function releases an interface. +All the claimed interfaces on a device must be released +before closing the device. +Returns 0 on success, LIBUSB_ERROR_NOT_FOUND if the +interface was not claimed, LIBUSB_ERROR_NO_DEVICE if the device has been +disconnected and LIBUSB_ERROR on failure. +.Pp +.Ft int +.Fn libusb_set_interface_alt_setting "libusb_device_handle *dev" "int interface_number" "int alternate_setting" +Activate an alternate setting for an interface. +Returns 0 on success, +LIBUSB_ERROR_NOT_FOUND if the interface was not claimed or the requested +setting does not exist, LIBUSB_ERROR_NO_DEVICE if the device has been +disconnected and a LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_clear_halt "libusb_device_handle *devh" "unsigned char endpoint" +Clear an halt/stall for a endpoint. +Returns 0 on success, LIBUSB_ERROR_NOT_FOUND +if the endpoint does not exist, LIBUSB_ERROR_NO_DEVICE if the device has been +disconnected and a LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_reset_device "libusb_device_handle *devh" +Perform an USB port reset for an usb device. +Returns 0 on success, +LIBUSB_ERROR_NOT_FOUND if re-enumeration is required or if the device has +been disconnected and a LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_check_connected "libusb_device_handle *devh" +Test if the USB device is still connected. +Returns 0 on success, +LIBUSB_ERROR_NO_DEVICE if it has been disconnected and a LIBUSB_ERROR +code on failure. +.Pp +.Ft int +.Fn libusb_kernel_driver_active "libusb_device_handle *devh" "int interface" +Determine if a driver is active on a interface. +Returns 0 if no kernel driver is active +and 1 if a kernel driver is active, LIBUSB_ERROR_NO_DEVICE +if the device has been disconnected and a LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_get_driver "libusb_device_handle *devh" "int interface" "char *name" "int namelen" +or +.Ft int +.Fn libusb_get_driver_np "libusb_device_handle *devh" "int interface" "char *name" "int namelen" +Copy the name of the driver attached to the given +.Fa device +and +.Fa interface +into the buffer +.Fa name +of length +.Fa namelen . +Returns 0 on success, LIBUSB_ERROR_NOT_FOUND if no kernel driver is attached +to the given interface and LIBUSB_ERROR_INVALID_PARAM if the interface does +not exist. +This function is non-portable. +The buffer pointed to by +.Fa name +is only zero terminated on success. +.Pp +.Ft int +.Fn libusb_detach_kernel_driver "libusb_device_handle *devh" "int interface" +or +.Ft int +.Fn libusb_detach_kernel_driver_np "libusb_device_handle *devh" "int interface" +Detach a kernel driver from an interface. +This is needed to claim an interface already claimed by a kernel driver. +Returns 0 on success, LIBUSB_ERROR_NOT_FOUND if no kernel driver was active, +LIBUSB_ERROR_INVALID_PARAM if the interface does not exist, +LIBUSB_ERROR_NO_DEVICE if the device has been disconnected +and a LIBUSB_ERROR code on failure. +This function is non-portable. +.Pp +.Ft int +.Fn libusb_attach_kernel_driver "libusb_device_handle *devh" "int interface" +Re-attach an interface kernel driver that was previously detached. +Returns 0 on success, +LIBUSB_ERROR_INVALID_PARAM if the interface does not exist, +LIBUSB_ERROR_NO_DEVICE +if the device has been disconnected, LIBUSB_ERROR_BUSY if the driver cannot be +attached because the interface is claimed by a program or driver and a +LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_set_auto_detach_kernel_driver "libusb_device_handle *devh" "int enable" +This function enables automatic kernel interface driver detach when an +interface is claimed. +When the interface is restored the kernel driver is allowed to be re-attached. +If the +.Fa enable +argument is non-zero the feature is enabled. +Else disabled. +Returns 0 on success and a LIBUSB_ERROR code on +failure. +.Pp +.Ft unsigned char * +.Fn libusb_dev_mem_alloc "libusb_device_handle *devh" +This function attempts to allocate a DMA memory block from the given +.Fa devh +so that we can enjoy the zero-copy transfer from kernel. +This function is provided for compatibility and is currently unimplemented and always returns NULL. +.Pp +.Ft int +.Fn libusb_dev_mem_free "libusb_device_handle *devh" "unsigned char *buffer" "size_t size" +This function frees the DMA memory in +.Fa devh +from the given +.Fa buffer +with +.Fa size . +This function is unimplemented and always returns LIBUSB_ERROR_NOT_SUPPORTED. +.Sh USB DESCRIPTORS +.Ft int +.Fn libusb_get_device_descriptor "libusb_device *dev" "libusb_device_descriptor *desc" +Get the USB device descriptor for the device +.Fa dev . +This is a non-blocking function. +Returns 0 on success and a LIBUSB_ERROR code on +failure. +.Pp +.Ft int +.Fn libusb_get_active_config_descriptor "libusb_device *dev" "struct libusb_config_descriptor **config" +Get the USB configuration descriptor for the active configuration. +Returns 0 on +success, LIBUSB_ERROR_NOT_FOUND if the device is in +an unconfigured state +and a LIBUSB_ERROR code on error. +.Pp +.Ft int +.Fn libusb_get_config_descriptor "libusb_device *dev" "uint8_t config_index" "libusb_config_descriptor **config" +Get a USB configuration descriptor based on its index +.Fa idx . +Returns 0 on success, LIBUSB_ERROR_NOT_FOUND if the configuration does not exist +and a LIBUSB_ERROR code on error. +.Pp +.Ft int +.Fn libusb_get_config_descriptor_by_value "libusb_device *dev" "uint8 bConfigurationValue" "libusb_config_descriptor **config" +Get a USB configuration descriptor with a specific bConfigurationValue. +This is +a non-blocking function which does not send a request through the device. +Returns 0 +on success, LIBUSB_ERROR_NOT_FOUND if the configuration +does not exist and a +LIBUSB_ERROR code on failure. +.Pp +.Ft void +.Fn libusb_free_config_descriptor "libusb_config_descriptor *config" +Free a configuration descriptor. +.Pp +.Ft int +.Fn libusb_get_string_descriptor "libusb_device_handle *devh" "uint8_t desc_idx" "uint16_t langid" "unsigned char *data" "int length" +Retrieve a string descriptor in raw format. +Returns the number of bytes actually transferred on success +or a negative LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_get_string_descriptor_ascii "libusb_device_handle *devh" "uint8_t desc_idx" "unsigned char *data" "int length" +Retrieve a string descriptor in C style ASCII. +Returns the positive number of bytes in the resulting ASCII string +on success and a LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_parse_ss_endpoint_comp "const void *buf" "int len" "libusb_ss_endpoint_companion_descriptor **ep_comp" +This function parses the USB 3.0 endpoint companion descriptor in host endian format pointed to by +.Fa buf +and having a length of +.Fa len . +Typically these arguments are the extra and extra_length fields of the +endpoint descriptor. +On success the pointer to resulting descriptor is stored at the location given by +.Fa ep_comp . +Returns zero on success and a LIBUSB_ERROR code on failure. +On success the parsed USB 3.0 endpoint companion descriptor must be +freed using the libusb_free_ss_endpoint_comp function. +.Pp +.Ft void +.Fn libusb_free_ss_endpoint_comp "libusb_ss_endpoint_companion_descriptor *ep_comp" +This function is NULL safe and frees a parsed USB 3.0 endpoint companion descriptor given by +.Fa ep_comp . +.Pp +.Ft int +.Fn libusb_get_ss_endpoint_companion_descriptor "struct libusb_context *ctx" "const struct libusb_endpoint_descriptor *endpoint" "struct libusb_ss_endpoint_companion_descriptor **ep_comp" +This function finds and parses the USB 3.0 endpoint companion descriptor given by +.Fa endpoint . +Returns zero on success and a LIBUSB_ERROR code on failure. +On success the parsed USB 3.0 endpoint companion descriptor must be +freed using the libusb_free_ss_endpoint_companion_descriptor function. +.Pp +.Ft void +.Fn libusb_free_ss_endpoint_companion_descriptor "struct libusb_ss_endpoint_companion_descriptor *ep_comp" +This function is NULL safe and frees a parsed USB 3.0 endpoint companion descriptor given by +.Fa ep_comp . +.Pp +.Ft int +.Fn libusb_get_bos_descriptor "libusb_device_handle *handle" "struct libusb_bos_descriptor **bos" +This function queries the USB device given by +.Fa handle +and stores a pointer to a parsed BOS descriptor into +.Fa bos . +Returns zero on success and a LIBUSB_ERROR code on failure. +On success the parsed BOS descriptor must be +freed using the libusb_free_bos_descriptor function. +.Pp +.Ft int +.Fn libusb_parse_bos_descriptor "const void *buf" "int len" "libusb_bos_descriptor **bos" +This function parses a Binary Object Store, BOS, descriptor into host endian format pointed to by +.Fa buf +and having a length of +.Fa len . +On success the pointer to resulting descriptor is stored at the location given by +.Fa bos . +Returns zero on success and a LIBUSB_ERROR code on failure. +On success the parsed BOS descriptor must be freed using the +libusb_free_bos_descriptor function. +.Pp +.Ft void +.Fn libusb_free_bos_descriptor "libusb_bos_descriptor *bos" +This function is NULL safe and frees a parsed BOS descriptor given by +.Fa bos . +.Pp +.Ft int +.Fn libusb_get_usb_2_0_extension_descriptor "struct libusb_context *ctx" "struct libusb_bos_dev_capability_descriptor *dev_cap" "struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension" +This function parses the USB 2.0 extension descriptor from the descriptor given by +.Fa dev_cap +and stores a pointer to the parsed descriptor into +.Fa usb_2_0_extension . +Returns zero on success and a LIBUSB_ERROR code on failure. +On success the parsed USB 2.0 extension descriptor must be freed using the +libusb_free_usb_2_0_extension_descriptor function. +.Pp +.Ft void +.Fn libusb_free_usb_2_0_extension_descriptor "struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension" +This function is NULL safe and frees a parsed USB 2.0 extension descriptor given by +.Fa usb_2_0_extension . +.Pp +.Ft int +.Fn libusb_get_ss_usb_device_capability_descriptor "struct libusb_context *ctx" "struct libusb_bos_dev_capability_descriptor *dev_cap" "struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_capability" +This function parses the SuperSpeed device capability descriptor from the descriptor given by +.Fa dev_cap +and stores a pointer to the parsed descriptor into +.Fa ss_usb_device_capability . +Returns zero on success and a LIBUSB_ERROR code on failure. +On success the parsed SuperSpeed device capability descriptor must be freed using the +libusb_free_ss_usb_device_capability_descriptor function. +.Pp +.Ft void +.Fn libusb_free_ss_usb_device_capability_descriptor "struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_capability" +This function is NULL safe and frees a parsed SuperSpeed device capability descriptor given by +.Fa ss_usb_device_capability . +.Pp +.Ft int +.Fn libusb_get_container_id_descriptor "struct libusb_context *ctx" "struct libusb_bos_dev_capability_descriptor *dev_cap" "struct libusb_container_id_descriptor **container_id" +This function parses the container ID descriptor from the descriptor given by +.Fa dev_cap +and stores a pointer to the parsed descriptor into +.Fa container_id . +Returns zero on success and a LIBUSB_ERROR code on failure. +On success the parsed container ID descriptor must be freed using the +libusb_free_container_id_descriptor function. +.Pp +.Ft void +.Fn libusb_free_container_id_descriptor "struct libusb_container_id_descriptor *container_id" +This function is NULL safe and frees a parsed container ID descriptor given by +.Fa container_id . +.Pp +.Ft int +.Fn libusb_get_platform_descriptor "struct libusb_context *ctx" "struct libusb_bos_dev_capability_descriptor *dev_cap" "struct libusb_platform_descriptor **platform_descriptor" +This function parses the platform descriptor from the descriptor given by +.Fa dev_cap +and stores a pointer to the parsed descriptor into +.Fa platform_descriptor . +Returns zero on success and a LIBUSB_ERROR code on failure. +On success the parsed platform descriptor must be freed using the +libusb_free_platform_descriptor function. +.Pp +.Ft void +.Fn libusb_free_platform_descriptor "struct libusb_platform_descriptor *platform_descriptor" +This function is NULL safe and frees a parsed platform descriptor given by +.Fa platform_descriptor . +.Sh USB ASYNCHRONOUS I/O +.Ft struct libusb_transfer * +.Fn libusb_alloc_transfer "int iso_packets" +Allocate a transfer with the number of isochronous packet descriptors +specified by +.Fa iso_packets . +Returns NULL on error. +.Pp +.Ft void +.Fn libusb_free_transfer "struct libusb_transfer *tr" +Free a transfer. +.Pp +.Ft int +.Fn libusb_submit_transfer "struct libusb_transfer *tr" +This function will submit a transfer and returns immediately. +Returns 0 on success, LIBUSB_ERROR_NO_DEVICE if +the device has been disconnected and a +LIBUSB_ERROR code on other failure. +.Pp +.Ft int +.Fn libusb_cancel_transfer "struct libusb_transfer *tr" +This function asynchronously cancels a transfer. +Returns 0 on success and a LIBUSB_ERROR code on failure. +.Sh USB SYNCHRONOUS I/O +.Ft int +.Fn libusb_control_transfer "libusb_device_handle *devh" "uint8_t bmRequestType" "uint8_t bRequest" "uint16_t wValue" "uint16_t wIndex" "unsigned char *data" "uint16_t wLength" "unsigned int timeout" +Perform a USB control transfer. +Returns the actual number of bytes +transferred on success, in the range from and including zero up to and +including +.Fa wLength . +On error a LIBUSB_ERROR code is returned, for example +LIBUSB_ERROR_TIMEOUT if the transfer timed out, LIBUSB_ERROR_PIPE if the +control request was not supported, LIBUSB_ERROR_NO_DEVICE if the +device has been disconnected and another LIBUSB_ERROR code on other failures. +The LIBUSB_ERROR codes are all negative. +.Pp +.Ft int +.Fn libusb_bulk_transfer "struct libusb_device_handle *devh" "unsigned char endpoint" "unsigned char *data" "int length" "int *transferred" "unsigned int timeout" +Perform an USB bulk transfer. +A timeout value of zero means no timeout. +The timeout value is given in milliseconds. +Returns 0 on success, LIBUSB_ERROR_TIMEOUT +if the transfer timed out, LIBUSB_ERROR_PIPE if the control request was not +supported, LIBUSB_ERROR_OVERFLOW if the device offered more data, +LIBUSB_ERROR_NO_DEVICE if the device has been disconnected and +a LIBUSB_ERROR code on other failure. +.Pp +.Ft int +.Fn libusb_interrupt_transfer "struct libusb_device_handle *devh" "unsigned char endpoint" "unsigned char *data" "int length" "int *transferred" "unsigned int timeout" +Perform an USB Interrupt transfer. +A timeout value of zero means no timeout. +The timeout value is given in milliseconds. +Returns 0 on success, LIBUSB_ERROR_TIMEOUT +if the transfer timed out, LIBUSB_ERROR_PIPE if the control request was not +supported, LIBUSB_ERROR_OVERFLOW if the device offered more data, +LIBUSB_ERROR_NO_DEVICE if the device has been disconnected and +a LIBUSB_ERROR code on other failure. +.Sh USB STREAMS SUPPORT +.Ft int +.Fn libusb_alloc_streams "libusb_device_handle *dev" "uint32_t num_streams" "unsigned char *endpoints" "int num_endpoints" +This function verifies that the given number of streams using the +given number of endpoints is allowed and allocates the resources +needed to use so-called USB streams. +Currently only a single stream per endpoint is supported to simplify +the internals of LibUSB. +This function returns 0 on success or a LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_free_streams "libusb_device_handle *dev" "unsigned char *endpoints" "int num_endpoints" +This function release resources needed for streams usage. +Returns 0 on success or a LIBUSB_ERROR code on failure. +.Pp +.Ft void +.Fn libusb_transfer_set_stream_id "struct libusb_transfer *transfer" "uint32_t stream_id" +This function sets the stream ID for the given USB transfer. +.Pp +.Ft uint32_t +.Fn libusb_transfer_get_stream_id "struct libusb_transfer *transfer" +This function returns the stream ID for the given USB transfer. +If no stream ID is used a value of zero is returned. +.Sh USB EVENTS +.Ft int +.Fn libusb_try_lock_events "libusb_context *ctx" +Try to acquire the event handling lock. +Returns 0 if the lock was obtained and 1 if not. +.Pp +.Ft void +.Fn libusb_lock_events "libusb_context *ctx" +Acquire the event handling lock. +This function is blocking. +.Pp +.Ft void +.Fn libusb_unlock_events "libusb_context *ctx" +Release the event handling lock. +This will wake up any thread blocked +on +.Fn libusb_wait_for_event . +.Pp +.Ft int +.Fn libusb_event_handling_ok "libusb_context *ctx" +Determine if it still OK for this thread to be doing event handling. +Returns 1 +if event handling can start or continue. +Returns 0 if this thread must give up +the events lock. +.Pp +.Ft int +.Fn libusb_event_handler_active "libusb_context *ctx" +Determine if an active thread is handling events. +Returns 1 if there is a thread handling events and 0 if there +are no threads currently handling events. +.Pp +.Ft void +.Fn libusb_interrupt_event_handler "libusb_context *ctx" +Causes the +.Fn libusb_handle_events +familiy of functions to return to the caller one time. +The +.Fn libusb_handle_events +functions may be called again after calling this function. +.Pp +.Ft void +.Fn libusb_lock_event_waiters "libusb_context *ctx" +Acquire the event_waiters lock. +This lock is designed to be obtained in the +situation where you want to be aware when events are completed, but some other +thread is event handling so calling +.Fn libusb_handle_events +is not allowed. +.Pp +.Ft void +.Fn libusb_unlock_event_waiters "libusb_context *ctx" +Release the event_waiters lock. +.Pp +.Ft int +.Fn libusb_wait_for_event "libusb_context *ctx" "struct timeval *tv" +Wait for another thread to signal completion of an event. +Must be called +with the event waiters lock held, see +.Fn libusb_lock_event_waiters . +This will +block until the timeout expires or a transfer completes or a thread releases +the event handling lock through +.Fn libusb_unlock_events . +Returns 0 after a +transfer completes or another thread stops event handling, and 1 if the +timeout expired. +.Pp +.Ft int +.Fn libusb_handle_events_timeout_completed "libusb_context *ctx" "struct timeval *tv" "int *completed" +Handle any pending events by checking if timeouts have expired and by +checking the set of file descriptors for activity. +If the +.Fa completed +argument is not equal to NULL, this function will +loop until a transfer completion callback sets the variable pointed to +by the +.Fa completed +argument to non-zero. +If the +.Fa tv +argument is not equal to NULL, this function will return +LIBUSB_ERROR_TIMEOUT after the given timeout. +Returns 0 on success, or a LIBUSB_ERROR code on failure or timeout. +.Pp +.Ft int +.Fn libusb_handle_events_completed "libusb_context *ctx" "int *completed" +Handle any pending events by checking the set of file descriptors for activity. +If the +.Fa completed +argument is not equal to NULL, this function will +loop until a transfer completion callback sets the variable pointed to +by the +.Fa completed +argument to non-zero. +Returns 0 on success, or a LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_handle_events_timeout "libusb_context *ctx" "struct timeval *tv" +Handle any pending events by checking if timeouts have expired and by +checking the set of file descriptors for activity. +Returns 0 on success, or a +LIBUSB_ERROR code on failure or timeout. +.Pp +.Ft int +.Fn libusb_handle_events "libusb_context *ctx" +Handle any pending events in blocking mode with a sensible timeout. +Returns 0 +on success and a LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_handle_events_locked "libusb_context *ctx" "struct timeval *tv" +Handle any pending events by polling file descriptors, without checking if +another thread is already doing so. +Must be called with the event lock held. +.Pp +.Ft int +.Fn libusb_pollfds_handle_timeouts "libusb_context *ctx" +This function determines whether applications maintaining libusb events using +.Fn libusb_get_pollfds +are responsible for handling timeout events themselves. +Returns 1 if libusb handles the timeout internally, 0 if the application +needs to set a dedicated timer to handle it. +.Pp +.Ft int +.Fn libusb_get_next_timeout "libusb_context *ctx" "struct timeval *tv" +Determine the next internal timeout that libusb needs to handle. +Returns 0 +if there are no pending timeouts, 1 if a timeout was returned, or a LIBUSB_ERROR +code on failure or timeout. +.Pp +.Ft void +.Fn libusb_set_pollfd_notifiers "libusb_context *ctx" "libusb_pollfd_added_cb added_cb" "libusb_pollfd_removed_cb remove_cb" "void *user_data" +Register notification functions for file descriptor additions/removals. +These functions will be invoked for every new or removed file descriptor +that libusb uses as an event source. +.Pp +.Ft const struct libusb_pollfd ** +.Fn libusb_get_pollfds "libusb_context *ctx" +Retrieve a list of file descriptors that should be polled by your main loop as +libusb event sources. +Returns a NULL-terminated list on success or NULL on failure. +.Pp +.Ft int +.Fn libusb_hotplug_register_callback "libusb_context *ctx" "libusb_hotplug_event events" "libusb_hotplug_flag flags" "int vendor_id" "int product_id" "int dev_class" "libusb_hotplug_callback_fn cb_fn" "void *user_data" "libusb_hotplug_callback_handle *handle" +This function registers a hotplug filter. +The +.Fa events +argument select which events makes the hotplug filter trigger. +Available event values are LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED and LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT. +One or more events must be specified. +The +.Fa vendor_id , +.Fa product_id +and +.Fa dev_class +arguments can be set to LIBUSB_HOTPLUG_MATCH_ANY to match any value in the USB device descriptor. +Else the specified value is used for matching. +If the +.Fa flags +argument is set to LIBUSB_HOTPLUG_ENUMERATE, all currently attached and matching USB devices will be passed to the hotplug filter, given by the +.Fa cb_fn +argument. +Else the +.Fa flags +argument should be set to LIBUSB_HOTPLUG_NO_FLAGS. +This function returns 0 upon success or a LIBUSB_ERROR code on failure. +.Pp +.Ft int +.Fn libusb_hotplug_callback_fn "libusb_context *ctx" "libusb_device *device" "libusb_hotplug_event event" "void *user_data" +The hotplug filter function. +If this function returns non-zero, the filter is removed. +Else the filter is kept and can receive more events. +The +.Fa user_data +argument is the same as given when the filter was registered. +The +.Fa event +argument can be either of LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED or LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT. +.Pp +.Ft void +.Fn libusb_hotplug_deregister_callback "libusb_context *ctx" "libusb_hotplug_callback_handle handle" +This function unregisters a hotplug filter. +.Pp +.Ft void +.Fn libusb_free_pollfds "const struct libusb_pollfd **pollfds" +This function releases the memory storage in +.Fa pollfds , +and is safe to call when the argument is NULL. +.Pp void * +.Fn libusb_hotplug_get_user_data "struct libusb_context *ctx" "libusb_hotplug_callback_handle callback_handle" +This function returns the user data from the opaque +.Fa callback_handle , +or returns NULL if no matching handle is found. +.Sh LIBUSB VERSION 0.1 COMPATIBILITY +The library is also compliant with LibUSB version 0.1.12. +.Pp +.Fn usb_open +.Fn usb_close +.Fn usb_get_string +.Fn usb_get_string_simple +.Fn usb_get_descriptor_by_endpoint +.Fn usb_get_descriptor +.Fn usb_parse_descriptor +.Fn usb_parse_configuration +.Fn usb_destroy_configuration +.Fn usb_fetch_and_parse_descriptors +.Fn usb_bulk_write +.Fn usb_bulk_read +.Fn usb_interrupt_write +.Fn usb_interrupt_read +.Fn usb_control_msg +.Fn usb_set_configuration +.Fn usb_claim_interface +.Fn usb_release_interface +.Fn usb_set_altinterface +.Fn usb_resetep +.Fn usb_clear_halt +.Fn usb_reset +.Fn usb_strerror +.Fn usb_init +.Fn usb_set_debug +.Fn usb_find_busses +.Fn usb_find_devices +.Fn usb_device +.Fn usb_get_busses +.Fn usb_check_connected +.Fn usb_get_driver_np +.Fn usb_detach_kernel_driver_np +.Sh SEE ALSO +.Xr libusb20 3 , +.Xr usb 4 , +.Xr usbconfig 8 , +.Xr usbdump 8 +.Pp +.Lk https://libusb.info/ +.Sh HISTORY +.Nm +support first appeared in +.Fx 8.0 . diff --git a/lib/libusb/libusb.h b/lib/libusb/libusb.h new file mode 100644 index 000000000000..623b56fb273b --- /dev/null +++ b/lib/libusb/libusb.h @@ -0,0 +1,647 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2009 Sylvestre Gallon. 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 __LIBUSB_H__ +#define __LIBUSB_H__ + +#ifndef LIBUSB_GLOBAL_INCLUDE_FILE +#include <stdint.h> +#include <sys/time.h> +#include <sys/types.h> +#endif + +#define LIBUSB_API_VERSION 0x01000102 + +#define LIBUSB_CALL + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* indent fix */ + +#endif + +/* libusb enums */ + +enum libusb_class_code { + LIBUSB_CLASS_PER_INTERFACE = 0, + LIBUSB_CLASS_AUDIO = 1, + LIBUSB_CLASS_COMM = 2, + LIBUSB_CLASS_HID = 3, + LIBUSB_CLASS_PHYSICAL = 5, + LIBUSB_CLASS_PTP = 6, + LIBUSB_CLASS_IMAGE = 6, + LIBUSB_CLASS_PRINTER = 7, + LIBUSB_CLASS_MASS_STORAGE = 8, + LIBUSB_CLASS_HUB = 9, + LIBUSB_CLASS_DATA = 10, + LIBUSB_CLASS_SMART_CARD = 11, + LIBUSB_CLASS_CONTENT_SECURITY = 13, + LIBUSB_CLASS_VIDEO = 14, + LIBUSB_CLASS_PERSONAL_HEALTHCARE = 15, + LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, + LIBUSB_CLASS_WIRELESS = 0xe0, + LIBUSB_CLASS_MISCELLANEOUS = 0xef, + LIBUSB_CLASS_APPLICATION = 0xfe, + LIBUSB_CLASS_VENDOR_SPEC = 0xff, +}; + +enum libusb_descriptor_type { + LIBUSB_DT_DEVICE = 0x01, + LIBUSB_DT_CONFIG = 0x02, + LIBUSB_DT_STRING = 0x03, + LIBUSB_DT_INTERFACE = 0x04, + LIBUSB_DT_ENDPOINT = 0x05, + LIBUSB_DT_HID = 0x21, + LIBUSB_DT_REPORT = 0x22, + LIBUSB_DT_PHYSICAL = 0x23, + LIBUSB_DT_HUB = 0x29, + LIBUSB_DT_BOS = 0x0f, + LIBUSB_DT_DEVICE_CAPABILITY = 0x10, + LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30, +}; + +enum libusb_device_capability_type { + LIBUSB_WIRELESS_USB_DEVICE_CAPABILITY = 0x1, + LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY = 0x2, + LIBUSB_SS_USB_DEVICE_CAPABILITY = 0x3, + LIBUSB_CONTAINER_ID_DEVICE_CAPABILITY = 0x4, +}; + +#define LIBUSB_DT_DEVICE_SIZE 18 +#define LIBUSB_DT_CONFIG_SIZE 9 +#define LIBUSB_DT_INTERFACE_SIZE 9 +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 +#define LIBUSB_DT_HUB_NONVAR_SIZE 7 +#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6 +#define LIBUSB_DT_BOS_SIZE 5 +#define LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE 7 +#define LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE 10 + +#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7 +#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10 +#define LIBUSB_BT_CONTAINER_ID_SIZE 20 +#define LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE 20 + +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f +#define LIBUSB_ENDPOINT_DIR_MASK 0x80 + +enum libusb_endpoint_direction { + LIBUSB_ENDPOINT_IN = 0x80, + LIBUSB_ENDPOINT_OUT = 0x00, +}; + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 + +enum libusb_transfer_type { + LIBUSB_TRANSFER_TYPE_CONTROL = 0, + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, + LIBUSB_TRANSFER_TYPE_BULK = 2, + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3, + LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4, +}; + +enum libusb_standard_request { + LIBUSB_REQUEST_GET_STATUS = 0x00, + LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, + LIBUSB_REQUEST_SET_FEATURE = 0x03, + LIBUSB_REQUEST_SET_ADDRESS = 0x05, + LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, + LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, + LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, + LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, + LIBUSB_REQUEST_GET_INTERFACE = 0x0A, + LIBUSB_REQUEST_SET_INTERFACE = 0x0B, + LIBUSB_REQUEST_SYNCH_FRAME = 0x0C, + LIBUSB_REQUEST_SET_SEL = 0x30, + LIBUSB_REQUEST_SET_ISOCH_DELAY = 0x31, +}; + +enum libusb_request_type { + LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), + LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), + LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), + LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5), +}; + +enum libusb_request_recipient { + LIBUSB_RECIPIENT_DEVICE = 0x00, + LIBUSB_RECIPIENT_INTERFACE = 0x01, + LIBUSB_RECIPIENT_ENDPOINT = 0x02, + LIBUSB_RECIPIENT_OTHER = 0x03, +}; + +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C + +enum libusb_iso_sync_type { + LIBUSB_ISO_SYNC_TYPE_NONE = 0, + LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, + LIBUSB_ISO_SYNC_TYPE_SYNC = 3, +}; + +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 + +enum libusb_iso_usage_type { + LIBUSB_ISO_USAGE_TYPE_DATA = 0, + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2, +}; + +enum libusb_usb_2_0_extension_attributes { + LIBUSB_BM_LPM_SUPPORT = 0x2, +}; + +enum libusb_supported_speed { + LIBUSB_LOW_SPEED_OPERATION = (1 << 0), + LIBUSB_FULL_SPEED_OPERATION = (1 << 1), + LIBUSB_HIGH_SPEED_OPERATION = (1 << 2), + LIBUSB_SUPER_SPEED_OPERATION = (1 << 3), +}; + +enum libusb_bos_type { + LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1, + LIBUSB_BT_USB_2_0_EXTENSION = 2, + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 3, + LIBUSB_BT_CONTAINER_ID = 4, + LIBUSB_BT_PLATFORM_DESCRIPTOR = 5, +}; + +enum libusb_capability { + /* libusb supports libusb_has_capability(). */ + LIBUSB_CAP_HAS_CAPABILITY = 0, + /* Hotplug support is available. */ + LIBUSB_CAP_HAS_HOTPLUG, + /* Can access HID devices without requiring user intervention. */ + LIBUSB_CAP_HAS_HID_ACCESS, + + /* + * Supports detaching of the default USB driver with + * libusb_detach_kernel_driver(). + */ + LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER, +}; + +enum libusb_error { + LIBUSB_SUCCESS = 0, + LIBUSB_ERROR_IO = -1, + LIBUSB_ERROR_INVALID_PARAM = -2, + LIBUSB_ERROR_ACCESS = -3, + LIBUSB_ERROR_NO_DEVICE = -4, + LIBUSB_ERROR_NOT_FOUND = -5, + LIBUSB_ERROR_BUSY = -6, + LIBUSB_ERROR_TIMEOUT = -7, + LIBUSB_ERROR_OVERFLOW = -8, + LIBUSB_ERROR_PIPE = -9, + LIBUSB_ERROR_INTERRUPTED = -10, + LIBUSB_ERROR_NO_MEM = -11, + LIBUSB_ERROR_NOT_SUPPORTED = -12, + LIBUSB_ERROR_OTHER = -99, +}; + +#define LIBUSB_ERROR_COUNT 14 + +enum libusb_speed { + LIBUSB_SPEED_UNKNOWN = 0, + LIBUSB_SPEED_LOW = 1, + LIBUSB_SPEED_FULL = 2, + LIBUSB_SPEED_HIGH = 3, + LIBUSB_SPEED_SUPER = 4, + LIBUSB_SPEED_SUPER_PLUS = 5, +}; + +enum libusb_transfer_status { + LIBUSB_TRANSFER_COMPLETED, + LIBUSB_TRANSFER_ERROR, + LIBUSB_TRANSFER_TIMED_OUT, + LIBUSB_TRANSFER_CANCELLED, + LIBUSB_TRANSFER_STALL, + LIBUSB_TRANSFER_NO_DEVICE, + LIBUSB_TRANSFER_OVERFLOW, +}; + +enum libusb_transfer_flags { + LIBUSB_TRANSFER_SHORT_NOT_OK = 1 << 0, + LIBUSB_TRANSFER_FREE_BUFFER = 1 << 1, + LIBUSB_TRANSFER_FREE_TRANSFER = 1 << 2, +}; + +enum libusb_log_level { + LIBUSB_LOG_LEVEL_NONE = 0, + LIBUSB_LOG_LEVEL_ERROR, + LIBUSB_LOG_LEVEL_WARNING, + LIBUSB_LOG_LEVEL_INFO, + LIBUSB_LOG_LEVEL_DEBUG +}; + +#define LIBUSB_HOTPLUG_MATCH_ANY -1 + +typedef enum { + LIBUSB_HOTPLUG_NO_FLAGS = 0, + LIBUSB_HOTPLUG_ENUMERATE = 1 << 0, +} libusb_hotplug_flag; + +typedef enum { + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 1, + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 2, +} libusb_hotplug_event; + +enum libusb_option { + LIBUSB_OPTION_LOG_LEVEL = 0, + LIBUSB_OPTION_USE_USBDK = 1, + LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2, + LIBUSB_OPTION_WEAK_AUTHORITY = 2, + LIBUSB_OPTION_MAX = 3, +}; + +/* libusb structures */ + +struct libusb_context; +struct libusb_device; +struct libusb_transfer; +struct libusb_device_handle; +struct libusb_hotplug_callback_handle_struct; + +struct libusb_pollfd { + int fd; + short events; +}; + +struct libusb_version { + const uint16_t major; + const uint16_t minor; + const uint16_t micro; + const uint16_t nano; + const char *rc; + const char *describe; +}; + +struct libusb_init_option { + enum libusb_option option; + union { + int64_t ival; + } value; +}; + +typedef struct libusb_context libusb_context; +typedef struct libusb_device libusb_device; +typedef struct libusb_device_handle libusb_device_handle; +typedef struct libusb_pollfd libusb_pollfd; +typedef void (*libusb_pollfd_added_cb) (int fd, short events, void *user_data); +typedef void (*libusb_pollfd_removed_cb) (int fd, void *user_data); +typedef struct libusb_hotplug_callback_handle_struct *libusb_hotplug_callback_handle; + +typedef struct libusb_device_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} libusb_device_descriptor; + +typedef struct libusb_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + uint8_t bRefresh; + uint8_t bSynchAddress; + uint8_t *extra; + int extra_length; +} libusb_endpoint_descriptor __aligned(sizeof(void *)); + +typedef struct libusb_ss_endpoint_companion_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bMaxBurst; + uint8_t bmAttributes; + uint16_t wBytesPerInterval; +} libusb_ss_endpoint_companion_descriptor __aligned(sizeof(void *)); + +typedef struct libusb_interface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; + struct libusb_endpoint_descriptor *endpoint; + uint8_t *extra; + int extra_length; +} libusb_interface_descriptor __aligned(sizeof(void *)); + +typedef struct libusb_interface { + struct libusb_interface_descriptor *altsetting; + int num_altsetting; +} libusb_interface __aligned(sizeof(void *)); + +typedef struct libusb_config_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t MaxPower; + struct libusb_interface *interface; + uint8_t *extra; + int extra_length; +} libusb_config_descriptor __aligned(sizeof(void *)); + +typedef struct libusb_usb_2_0_device_capability_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint32_t bmAttributes; +#define LIBUSB_USB_2_0_CAPABILITY_LPM_SUPPORT (1 << 1) +} libusb_usb_2_0_device_capability_descriptor __aligned(sizeof(void *)); + +typedef struct libusb_ss_usb_device_capability_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint8_t bmAttributes; +#define LIBUSB_SS_USB_CAPABILITY_LPM_SUPPORT (1 << 1) + uint16_t wSpeedSupported; +#define LIBUSB_CAPABILITY_LOW_SPEED_OPERATION (1) +#define LIBUSB_CAPABILITY_FULL_SPEED_OPERATION (1 << 1) +#define LIBUSB_CAPABILITY_HIGH_SPEED_OPERATION (1 << 2) +#define LIBUSB_CAPABILITY_5GBPS_OPERATION (1 << 3) + uint8_t bFunctionalitySupport; + uint8_t bU1DevExitLat; + uint16_t wU2DevExitLat; +} libusb_ss_usb_device_capability_descriptor __aligned(sizeof(void *)); + +typedef struct libusb_bos_dev_capability_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint8_t dev_capability_data[0]; +} libusb_bos_dev_capability_descriptor __aligned(sizeof(void *)); + +typedef struct libusb_bos_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; +#ifndef bNumDeviceCapabilities +#define bNumDeviceCapabilities bNumDeviceCaps +#endif + uint8_t bNumDeviceCaps; + struct libusb_usb_2_0_device_capability_descriptor *usb_2_0_ext_cap; + struct libusb_ss_usb_device_capability_descriptor *ss_usb_cap; + struct libusb_bos_dev_capability_descriptor **dev_capability; +} libusb_bos_descriptor __aligned(sizeof(void *)); + +typedef struct libusb_usb_2_0_extension_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint32_t bmAttributes; +} libusb_usb_2_0_extension_descriptor __aligned(sizeof(void *)); + +typedef struct libusb_container_id_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint8_t bReserved; + uint8_t ContainerID[16]; +} libusb_container_id_descriptor __aligned(sizeof(void *)); + +typedef struct libusb_platform_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint8_t bReserved; + uint8_t PlatformCapabilityUUID[16]; + uint8_t CapabilityData[]; +} libusb_platform_descriptor __aligned(sizeof(void *)); + +typedef struct libusb_control_setup { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} libusb_control_setup; + +#define LIBUSB_CONTROL_SETUP_SIZE 8 /* bytes */ + +typedef struct libusb_iso_packet_descriptor { + uint32_t length; + uint32_t actual_length; + enum libusb_transfer_status status; +} libusb_iso_packet_descriptor __aligned(sizeof(void *)); + +typedef void (*libusb_transfer_cb_fn) (struct libusb_transfer *transfer); + +typedef struct libusb_transfer { + libusb_device_handle *dev_handle; + uint8_t flags; + uint8_t endpoint; + uint8_t type; + uint32_t timeout; + enum libusb_transfer_status status; + int length; + int actual_length; + libusb_transfer_cb_fn callback; + void *user_data; + uint8_t *buffer; + int num_iso_packets; + struct libusb_iso_packet_descriptor iso_packet_desc[0]; +} libusb_transfer __aligned(sizeof(void *)); + +/* Library initialisation */ + +void libusb_set_debug(libusb_context * ctx, int level); +const struct libusb_version *libusb_get_version(void); +const char *libusb_strerror(int code); +const char *libusb_error_name(int code); +int libusb_init(libusb_context ** context); +int libusb_init_context(libusb_context **, const struct libusb_init_option [], int num_options); +void libusb_exit(struct libusb_context *ctx); +int libusb_has_capability(uint32_t capability); +int libusb_setlocale(const char *locale); + +/* Device handling and enumeration */ + +ssize_t libusb_get_device_list(libusb_context * ctx, libusb_device *** list); +void libusb_free_device_list(libusb_device ** list, int unref_devices); +uint8_t libusb_get_bus_number(libusb_device * dev); +uint8_t libusb_get_port_number(libusb_device * dev); +int libusb_get_port_numbers(libusb_device *dev, uint8_t *buf, uint8_t bufsize); +int libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *buf, uint8_t bufsize); +uint8_t libusb_get_device_address(libusb_device * dev); +enum libusb_speed libusb_get_device_speed(libusb_device * dev); +int libusb_clear_halt(libusb_device_handle *devh, uint8_t endpoint); +int libusb_get_max_packet_size(libusb_device * dev, uint8_t endpoint); +int libusb_get_max_iso_packet_size(libusb_device * dev, uint8_t endpoint); +libusb_device *libusb_ref_device(libusb_device * dev); +void libusb_unref_device(libusb_device * dev); +int libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle); +int libusb_open(libusb_device * dev, libusb_device_handle ** devh); +libusb_device_handle *libusb_open_device_with_vid_pid(libusb_context * ctx, uint16_t vendor_id, uint16_t product_id); +void libusb_close(libusb_device_handle * devh); +libusb_device *libusb_get_device(libusb_device_handle * devh); +libusb_device *libusb_get_parent(libusb_device * dev); +int libusb_get_configuration(libusb_device_handle * devh, int *config); +int libusb_set_configuration(libusb_device_handle * devh, int configuration); +int libusb_claim_interface(libusb_device_handle * devh, int interface_number); +int libusb_release_interface(libusb_device_handle * devh, int interface_number); +int libusb_reset_device(libusb_device_handle * devh); +int libusb_check_connected(libusb_device_handle * devh); +int libusb_kernel_driver_active(libusb_device_handle * devh, int interface); +int libusb_get_driver_np(libusb_device_handle * devh, int interface, char *name, int namelen); +int libusb_get_driver(libusb_device_handle * devh, int interface, char *name, int namelen); +int libusb_detach_kernel_driver_np(libusb_device_handle * devh, int interface); +int libusb_detach_kernel_driver(libusb_device_handle * devh, int interface); +int libusb_attach_kernel_driver(libusb_device_handle * devh, int interface); +int libusb_set_auto_detach_kernel_driver(libusb_device_handle *dev, int enable); +int libusb_set_interface_alt_setting(libusb_device_handle * devh, int interface_number, int alternate_setting); +unsigned char *libusb_dev_mem_alloc(libusb_device_handle *devh); +int libusb_dev_mem_free(libusb_device_handle *devh, unsigned char *buffer, + size_t size); + +/* USB Descriptors */ + +int libusb_get_device_descriptor(libusb_device * dev, struct libusb_device_descriptor *desc); +int libusb_get_active_config_descriptor(libusb_device * dev, struct libusb_config_descriptor **config); +int libusb_get_config_descriptor(libusb_device * dev, uint8_t config_index, struct libusb_config_descriptor **config); +int libusb_get_config_descriptor_by_value(libusb_device * dev, uint8_t bConfigurationValue, struct libusb_config_descriptor **config); +void libusb_free_config_descriptor(struct libusb_config_descriptor *config); +int libusb_get_ss_endpoint_companion_descriptor(struct libusb_context *ctx, const struct libusb_endpoint_descriptor *endpoint, struct libusb_ss_endpoint_companion_descriptor **ep_comp); +void libusb_free_ss_endpoint_companion_descriptor(struct libusb_ss_endpoint_companion_descriptor *ep_comp); +int libusb_get_string_descriptor(libusb_device_handle * devh, uint8_t desc_index, uint16_t langid, unsigned char *data, int length); +int libusb_get_string_descriptor_ascii(libusb_device_handle * devh, uint8_t desc_index, uint8_t *data, int length); +int libusb_get_descriptor(libusb_device_handle * devh, uint8_t desc_type, uint8_t desc_index, uint8_t *data, int length); +int libusb_parse_ss_endpoint_comp(const void *buf, int len, struct libusb_ss_endpoint_companion_descriptor **ep_comp); +void libusb_free_ss_endpoint_comp(struct libusb_ss_endpoint_companion_descriptor *ep_comp); +int libusb_parse_bos_descriptor(const void *buf, int len, struct libusb_bos_descriptor **bos); +void libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos); +int libusb_get_bos_descriptor(libusb_device_handle *handle, struct libusb_bos_descriptor **bos); +int libusb_get_usb_2_0_extension_descriptor(struct libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension); +void libusb_free_usb_2_0_extension_descriptor(struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension); +int libusb_get_ss_usb_device_capability_descriptor(struct libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_capability); +void libusb_free_ss_usb_device_capability_descriptor(struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_capability); +int libusb_get_container_id_descriptor(struct libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_container_id_descriptor **container_id); +void libusb_free_container_id_descriptor(struct libusb_container_id_descriptor *container_id); +int libusb_get_platform_descriptor(libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_platform_descriptor **platform_descriptor); +void libusb_free_platform_descriptor(struct libusb_platform_descriptor *platform_descriptor); + +/* Asynchronous device I/O */ + +struct libusb_transfer *libusb_alloc_transfer(int iso_packets); +void libusb_free_transfer(struct libusb_transfer *transfer); +int libusb_submit_transfer(struct libusb_transfer *transfer); +int libusb_cancel_transfer(struct libusb_transfer *transfer); +uint8_t *libusb_get_iso_packet_buffer(struct libusb_transfer *transfer, uint32_t index); +uint8_t *libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, uint32_t index); +void libusb_set_iso_packet_lengths(struct libusb_transfer *transfer, uint32_t length); +uint8_t *libusb_control_transfer_get_data(struct libusb_transfer *transfer); +struct libusb_control_setup *libusb_control_transfer_get_setup(struct libusb_transfer *transfer); +void libusb_fill_control_setup(uint8_t *buf, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength); +void libusb_fill_control_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t *buf, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); +void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); +void libusb_fill_bulk_stream_transfer(struct libusb_transfer *transfer, libusb_device_handle *dev_handle, unsigned char endpoint, uint32_t stream_id, unsigned char *buffer, int length, libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout); +void libusb_fill_interrupt_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); +void libusb_fill_iso_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, int npacket, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); + +/* Polling and timing */ + +int libusb_try_lock_events(libusb_context * ctx); +void libusb_lock_events(libusb_context * ctx); +void libusb_unlock_events(libusb_context * ctx); +int libusb_event_handling_ok(libusb_context * ctx); +int libusb_event_handler_active(libusb_context * ctx); +void libusb_interrupt_event_handler(libusb_context *ctx); +void libusb_lock_event_waiters(libusb_context * ctx); +void libusb_unlock_event_waiters(libusb_context * ctx); +int libusb_wait_for_event(libusb_context * ctx, struct timeval *tv); +int libusb_handle_events_timeout_completed(libusb_context * ctx, struct timeval *tv, int *completed); +int libusb_handle_events_completed(libusb_context * ctx, int *completed); +int libusb_handle_events_timeout(libusb_context * ctx, struct timeval *tv); +int libusb_handle_events(libusb_context * ctx); +int libusb_handle_events_locked(libusb_context * ctx, struct timeval *tv); +int libusb_pollfds_handle_timeouts(libusb_context *ctx); +int libusb_get_next_timeout(libusb_context * ctx, struct timeval *tv); +void libusb_set_pollfd_notifiers(libusb_context * ctx, libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, void *user_data); +const struct libusb_pollfd **libusb_get_pollfds(libusb_context *ctx); +void libusb_free_pollfds(const struct libusb_pollfd **pollfds); + +/* Synchronous device I/O */ + +int libusb_control_transfer(libusb_device_handle * devh, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t wLength, uint32_t timeout); +int libusb_bulk_transfer(libusb_device_handle * devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, uint32_t timeout); +int libusb_interrupt_transfer(libusb_device_handle * devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, uint32_t timeout); + +/* Byte-order */ + +uint16_t libusb_cpu_to_le16(uint16_t x); +uint16_t libusb_le16_to_cpu(uint16_t x); + +/* Hotplug support */ + +typedef int (*libusb_hotplug_callback_fn)(libusb_context *ctx, + libusb_device *device, libusb_hotplug_event event, void *user_data); + +int libusb_hotplug_register_callback(libusb_context *ctx, libusb_hotplug_event events, libusb_hotplug_flag flags, int vendor_id, int product_id, int dev_class, libusb_hotplug_callback_fn cb_fn, void *user_data, libusb_hotplug_callback_handle *handle); +void libusb_hotplug_deregister_callback(libusb_context *ctx, libusb_hotplug_callback_handle handle); +void *libusb_hotplug_get_user_data(struct libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle); + +/* Streams support */ + +int libusb_alloc_streams(libusb_device_handle *dev, uint32_t num_streams, unsigned char *endpoints, int num_endpoints); +int libusb_free_streams(libusb_device_handle *dev, unsigned char *endpoints, int num_endpoints); +void libusb_transfer_set_stream_id(struct libusb_transfer *transfer, uint32_t stream_id); +uint32_t libusb_transfer_get_stream_id(struct libusb_transfer *transfer); + +#if 0 +{ /* indent fix */ +#endif +#ifdef __cplusplus +} + +#endif + +#endif /* __LIBUSB_H__ */ diff --git a/lib/libusb/libusb01.c b/lib/libusb/libusb01.c new file mode 100644 index 000000000000..f246e74353a6 --- /dev/null +++ b/lib/libusb/libusb01.c @@ -0,0 +1,1025 @@ +/*- + * 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. + */ + +/* + * This file contains the emulation layer for LibUSB v0.1 from sourceforge. + */ + +#ifdef LIBUSB_GLOBAL_INCLUDE_FILE +#include LIBUSB_GLOBAL_INCLUDE_FILE +#else +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/queue.h> +#endif + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" +#include "usb.h" + +/* + * The two following macros were taken from the original LibUSB v0.1 + * for sake of compatibility: + */ +#define LIST_ADD(begin, ent) \ + do { \ + if (begin) { \ + ent->next = begin; \ + ent->next->prev = ent; \ + } else { \ + ent->next = NULL; \ + } \ + ent->prev = NULL; \ + begin = ent; \ + } while(0) + +#define LIST_DEL(begin, ent) \ + do { \ + if (ent->prev) { \ + ent->prev->next = ent->next; \ + } else { \ + begin = ent->next; \ + } \ + if (ent->next) { \ + ent->next->prev = ent->prev; \ + } \ + ent->prev = NULL; \ + ent->next = NULL; \ + } while (0) + +struct usb_bus *usb_busses = NULL; + +static struct usb_bus usb_global_bus = { + .dirname = {"/dev/usb"}, + .root_dev = NULL, + .devices = NULL, +}; + +static struct libusb20_backend *usb_backend = NULL; + +struct usb_parse_state { + + struct { + struct libusb20_endpoint *currep; + struct libusb20_interface *currifc; + struct libusb20_config *currcfg; + struct libusb20_me_struct *currextra; + } a; + + struct { + struct usb_config_descriptor *currcfg; + struct usb_interface_descriptor *currifc; + struct usb_endpoint_descriptor *currep; + struct usb_interface *currifcw; + uint8_t *currextra; + } b; + + uint8_t preparse; +}; + +static struct libusb20_transfer * +usb_get_transfer_by_ep_no(usb_dev_handle * dev, uint8_t ep_no) +{ + struct libusb20_device *pdev = (void *)dev; + struct libusb20_transfer *xfer; + int err; + uint32_t bufsize; + uint8_t x; + uint8_t speed; + + x = (ep_no & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 2; + + if (ep_no & LIBUSB20_ENDPOINT_DIR_MASK) { + /* this is an IN endpoint */ + x |= 1; + } + speed = libusb20_dev_get_speed(pdev); + + /* select a sensible buffer size */ + if (speed == LIBUSB20_SPEED_LOW) { + bufsize = 256; + } else if (speed == LIBUSB20_SPEED_FULL) { + bufsize = 4096; + } else if (speed == LIBUSB20_SPEED_SUPER) { + bufsize = 65536; + } else if (speed == LIBUSB20_SPEED_SUPER_PLUS) { + bufsize = 131072; + } else { + bufsize = 16384; + } + + xfer = libusb20_tr_get_pointer(pdev, x); + + if (xfer == NULL) + return (xfer); + + err = libusb20_tr_open(xfer, bufsize, 1, ep_no); + if (err == LIBUSB20_ERROR_BUSY) { + /* already opened */ + return (xfer); + } else if (err) { + return (NULL); + } + /* success */ + return (xfer); +} + +usb_dev_handle * +usb_open(struct usb_device *dev) +{ + int err; + + err = libusb20_dev_open(dev->dev, 16 * 2); + if (err == LIBUSB20_ERROR_BUSY) { + /* + * Workaround buggy USB applications which open the USB + * device multiple times: + */ + return (dev->dev); + } + if (err) + return (NULL); + + /* + * Dequeue USB device from backend queue so that it does not get + * freed when the backend is re-scanned: + */ + libusb20_be_dequeue_device(usb_backend, dev->dev); + + return (dev->dev); +} + +int +usb_close(usb_dev_handle * udev) +{ + struct usb_device *dev; + int err; + + err = libusb20_dev_close((void *)udev); + + if (err) + return (-1); + + if (usb_backend != NULL) { + /* + * Enqueue USB device to backend queue so that it gets freed + * when the backend is re-scanned: + */ + libusb20_be_enqueue_device(usb_backend, (void *)udev); + } else { + /* + * The backend is gone. Free device data so that we + * don't start leaking memory! + */ + dev = usb_device(udev); + libusb20_dev_free((void *)udev); + LIST_DEL(usb_global_bus.devices, dev); + free(dev); + } + return (0); +} + +int +usb_get_string(usb_dev_handle * dev, int strindex, + int langid, char *buf, size_t buflen) +{ + int err; + + if (dev == NULL) + return (-1); + + if (buflen > 65535) + buflen = 65535; + + err = libusb20_dev_req_string_sync((void *)dev, + strindex, langid, buf, buflen); + + if (err) + return (-1); + + return (0); +} + +int +usb_get_string_simple(usb_dev_handle * dev, int strindex, + char *buf, size_t buflen) +{ + int err; + + if (dev == NULL) + return (-1); + + if (buflen > 65535) + buflen = 65535; + + err = libusb20_dev_req_string_simple_sync((void *)dev, + strindex, buf, buflen); + + if (err) + return (-1); + + return (strlen(buf)); +} + +int +usb_get_descriptor_by_endpoint(usb_dev_handle * udev, int ep, uint8_t type, + uint8_t ep_index, void *buf, int size) +{ + memset(buf, 0, size); + + if (udev == NULL) + return (-1); + + if (size > 65535) + size = 65535; + + return (usb_control_msg(udev, ep | USB_ENDPOINT_IN, + USB_REQ_GET_DESCRIPTOR, (type << 8) + ep_index, 0, + buf, size, 1000)); +} + +int +usb_get_descriptor(usb_dev_handle * udev, uint8_t type, uint8_t desc_index, + void *buf, int size) +{ + memset(buf, 0, size); + + if (udev == NULL) + return (-1); + + if (size > 65535) + size = 65535; + + return (usb_control_msg(udev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, + (type << 8) + desc_index, 0, buf, size, 1000)); +} + +int +usb_parse_descriptor(uint8_t *source, char *description, void *dest) +{ + uint8_t *sp = source; + uint8_t *dp = dest; + uint16_t w; + uint32_t d; + char *cp; + + for (cp = description; *cp; cp++) { + switch (*cp) { + case 'b': /* 8-bit byte */ + *dp++ = *sp++; + break; + /* + * 16-bit word, convert from little endian to CPU + */ + case 'w': + w = (sp[1] << 8) | sp[0]; + sp += 2; + /* Align to word boundary */ + dp += ((dp - (uint8_t *)0) & 1); + *((uint16_t *)dp) = w; + dp += 2; + break; + /* + * 32-bit dword, convert from little endian to CPU + */ + case 'd': + d = (sp[3] << 24) | (sp[2] << 16) | + (sp[1] << 8) | sp[0]; + sp += 4; + /* Align to word boundary */ + dp += ((dp - (uint8_t *)0) & 1); + /* Align to double word boundary */ + dp += ((dp - (uint8_t *)0) & 2); + *((uint32_t *)dp) = d; + dp += 4; + break; + } + } + return (sp - source); +} + +static void +usb_parse_extra(struct usb_parse_state *ps, uint8_t **pptr, int *plen) +{ + void *ptr; + uint16_t len; + + ptr = ps->a.currextra->ptr; + len = ps->a.currextra->len; + + if (ps->preparse == 0) { + memcpy(ps->b.currextra, ptr, len); + *pptr = ps->b.currextra; + *plen = len; + } + ps->b.currextra += len; + return; +} + +static void +usb_parse_endpoint(struct usb_parse_state *ps) +{ + struct usb_endpoint_descriptor *bep; + struct libusb20_endpoint *aep; + + aep = ps->a.currep; + bep = ps->b.currep++; + + if (ps->preparse == 0) { + /* copy descriptor fields */ + bep->bLength = aep->desc.bLength; + bep->bDescriptorType = aep->desc.bDescriptorType; + bep->bEndpointAddress = aep->desc.bEndpointAddress; + bep->bmAttributes = aep->desc.bmAttributes; + bep->wMaxPacketSize = aep->desc.wMaxPacketSize; + bep->bInterval = aep->desc.bInterval; + bep->bRefresh = aep->desc.bRefresh; + bep->bSynchAddress = aep->desc.bSynchAddress; + } + ps->a.currextra = &aep->extra; + usb_parse_extra(ps, &bep->extra, &bep->extralen); + return; +} + +static void +usb_parse_iface_sub(struct usb_parse_state *ps) +{ + struct libusb20_interface *aifc; + struct usb_interface_descriptor *bifc; + uint8_t x; + + aifc = ps->a.currifc; + bifc = ps->b.currifc++; + + if (ps->preparse == 0) { + /* copy descriptor fields */ + bifc->bLength = aifc->desc.bLength; + bifc->bDescriptorType = aifc->desc.bDescriptorType; + bifc->bInterfaceNumber = aifc->desc.bInterfaceNumber; + bifc->bAlternateSetting = aifc->desc.bAlternateSetting; + bifc->bNumEndpoints = aifc->num_endpoints; + bifc->bInterfaceClass = aifc->desc.bInterfaceClass; + bifc->bInterfaceSubClass = aifc->desc.bInterfaceSubClass; + bifc->bInterfaceProtocol = aifc->desc.bInterfaceProtocol; + bifc->iInterface = aifc->desc.iInterface; + bifc->endpoint = ps->b.currep; + } + for (x = 0; x != aifc->num_endpoints; x++) { + ps->a.currep = aifc->endpoints + x; + usb_parse_endpoint(ps); + } + + ps->a.currextra = &aifc->extra; + usb_parse_extra(ps, &bifc->extra, &bifc->extralen); + return; +} + +static void +usb_parse_iface(struct usb_parse_state *ps) +{ + struct libusb20_interface *aifc; + struct usb_interface *bifc; + uint8_t x; + + aifc = ps->a.currifc; + bifc = ps->b.currifcw++; + + if (ps->preparse == 0) { + /* initialise interface wrapper */ + bifc->altsetting = ps->b.currifc; + bifc->num_altsetting = aifc->num_altsetting + 1; + } + usb_parse_iface_sub(ps); + + for (x = 0; x != aifc->num_altsetting; x++) { + ps->a.currifc = aifc->altsetting + x; + usb_parse_iface_sub(ps); + } + return; +} + +static void +usb_parse_config(struct usb_parse_state *ps) +{ + struct libusb20_config *acfg; + struct usb_config_descriptor *bcfg; + uint8_t x; + + acfg = ps->a.currcfg; + bcfg = ps->b.currcfg; + + if (ps->preparse == 0) { + /* initialise config wrapper */ + bcfg->bLength = acfg->desc.bLength; + bcfg->bDescriptorType = acfg->desc.bDescriptorType; + bcfg->wTotalLength = acfg->desc.wTotalLength; + bcfg->bNumInterfaces = acfg->num_interface; + bcfg->bConfigurationValue = acfg->desc.bConfigurationValue; + bcfg->iConfiguration = acfg->desc.iConfiguration; + bcfg->bmAttributes = acfg->desc.bmAttributes; + bcfg->MaxPower = acfg->desc.bMaxPower; + bcfg->interface = ps->b.currifcw; + } + for (x = 0; x != acfg->num_interface; x++) { + ps->a.currifc = acfg->interface + x; + usb_parse_iface(ps); + } + + ps->a.currextra = &acfg->extra; + usb_parse_extra(ps, &bcfg->extra, &bcfg->extralen); + return; +} + +int +usb_parse_configuration(struct usb_config_descriptor *config, + uint8_t *buffer) +{ + struct usb_parse_state ps; + uint8_t *ptr; + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + + if ((buffer == NULL) || (config == NULL)) { + return (-1); + } + memset(&ps, 0, sizeof(ps)); + + ps.a.currcfg = libusb20_parse_config_desc(buffer); + ps.b.currcfg = config; + if (ps.a.currcfg == NULL) { + /* could not parse config or out of memory */ + return (-1); + } + /* do the pre-parse */ + ps.preparse = 1; + usb_parse_config(&ps); + + a = ((uint8_t *)(ps.b.currifcw) - ((uint8_t *)0)); + b = ((uint8_t *)(ps.b.currifc) - ((uint8_t *)0)); + c = ((uint8_t *)(ps.b.currep) - ((uint8_t *)0)); + d = ((uint8_t *)(ps.b.currextra) - ((uint8_t *)0)); + + /* allocate memory for our configuration */ + ptr = malloc(a + b + c + d); + if (ptr == NULL) { + /* free config structure */ + free(ps.a.currcfg); + return (-1); + } + + /* "currifcw" must be first, hence this pointer is freed */ + ps.b.currifcw = (void *)(ptr); + ps.b.currifc = (void *)(ptr + a); + ps.b.currep = (void *)(ptr + a + b); + ps.b.currextra = (void *)(ptr + a + b + c); + + /* generate a libusb v0.1 compatible structure */ + ps.preparse = 0; + usb_parse_config(&ps); + + /* free config structure */ + free(ps.a.currcfg); + + return (0); /* success */ +} + +void +usb_destroy_configuration(struct usb_device *dev) +{ + uint8_t c; + + if (dev->config == NULL) { + return; + } + for (c = 0; c != dev->descriptor.bNumConfigurations; c++) { + struct usb_config_descriptor *cf = &dev->config[c]; + + if (cf->interface != NULL) { + free(cf->interface); + cf->interface = NULL; + } + } + + free(dev->config); + dev->config = NULL; + return; +} + +void +usb_fetch_and_parse_descriptors(usb_dev_handle * udev) +{ + struct usb_device *dev; + struct libusb20_device *pdev; + uint8_t *ptr; + int error; + uint32_t size; + uint16_t len; + uint8_t x; + + if (udev == NULL) { + /* be NULL safe */ + return; + } + dev = usb_device(udev); + pdev = (void *)udev; + + if (dev->descriptor.bNumConfigurations == 0) { + /* invalid device */ + return; + } + size = dev->descriptor.bNumConfigurations * + sizeof(struct usb_config_descriptor); + + dev->config = malloc(size); + if (dev->config == NULL) { + /* out of memory */ + return; + } + memset(dev->config, 0, size); + + for (x = 0; x != dev->descriptor.bNumConfigurations; x++) { + + error = (pdev->methods->get_config_desc_full) ( + pdev, &ptr, &len, x); + + if (error) { + usb_destroy_configuration(dev); + return; + } + usb_parse_configuration(dev->config + x, ptr); + + /* free config buffer */ + free(ptr); + } + return; +} + +static int +usb_std_io(usb_dev_handle * dev, int ep, char *bytes, int size, + int timeout, int is_intr) +{ + struct libusb20_transfer *xfer; + uint32_t temp; + uint32_t maxsize; + uint32_t actlen; + char *oldbytes; + + xfer = usb_get_transfer_by_ep_no(dev, ep); + if (xfer == NULL) + return (-1); + + if (libusb20_tr_pending(xfer)) { + /* there is already a transfer ongoing */ + return (-1); + } + maxsize = libusb20_tr_get_max_total_length(xfer); + oldbytes = bytes; + + /* + * We allow transferring zero bytes which is the same + * equivalent to a zero length USB packet. + */ + do { + + temp = size; + if (temp > maxsize) { + /* find maximum possible length */ + temp = maxsize; + } + if (is_intr) + libusb20_tr_setup_intr(xfer, bytes, temp, timeout); + else + libusb20_tr_setup_bulk(xfer, bytes, temp, timeout); + + libusb20_tr_start(xfer); + + while (1) { + + if (libusb20_dev_process((void *)dev) != 0) { + /* device detached */ + return (-1); + } + if (libusb20_tr_pending(xfer) == 0) { + /* transfer complete */ + break; + } + /* wait for USB event from kernel */ + libusb20_dev_wait_process((void *)dev, -1); + } + + switch (libusb20_tr_get_status(xfer)) { + case 0: + /* success */ + break; + case LIBUSB20_TRANSFER_TIMED_OUT: + /* transfer timeout */ + return (-ETIMEDOUT); + default: + /* other transfer error */ + return (-ENXIO); + } + actlen = libusb20_tr_get_actual_length(xfer); + + bytes += actlen; + size -= actlen; + + if (actlen != temp) { + /* short transfer */ + break; + } + } while (size > 0); + + return (bytes - oldbytes); +} + +int +usb_bulk_write(usb_dev_handle * dev, int ep, char *bytes, + int size, int timeout) +{ + return (usb_std_io(dev, ep & ~USB_ENDPOINT_DIR_MASK, + bytes, size, timeout, 0)); +} + +int +usb_bulk_read(usb_dev_handle * dev, int ep, char *bytes, + int size, int timeout) +{ + return (usb_std_io(dev, ep | USB_ENDPOINT_DIR_MASK, + bytes, size, timeout, 0)); +} + +int +usb_interrupt_write(usb_dev_handle * dev, int ep, char *bytes, + int size, int timeout) +{ + return (usb_std_io(dev, ep & ~USB_ENDPOINT_DIR_MASK, + bytes, size, timeout, 1)); +} + +int +usb_interrupt_read(usb_dev_handle * dev, int ep, char *bytes, + int size, int timeout) +{ + return (usb_std_io(dev, ep | USB_ENDPOINT_DIR_MASK, + bytes, size, timeout, 1)); +} + +int +usb_control_msg(usb_dev_handle * dev, int requesttype, int request, + int value, int wIndex, char *bytes, int size, int timeout) +{ + struct LIBUSB20_CONTROL_SETUP_DECODED req; + int err; + uint16_t actlen; + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); + + req.bmRequestType = requesttype; + req.bRequest = request; + req.wValue = value; + req.wIndex = wIndex; + req.wLength = size; + + err = libusb20_dev_request_sync((void *)dev, &req, bytes, + &actlen, timeout, 0); + + if (err) + return (-1); + + return (actlen); +} + +int +usb_set_configuration(usb_dev_handle * udev, int bConfigurationValue) +{ + struct usb_device *dev; + int err; + uint8_t i; + + /* + * Need to translate from "bConfigurationValue" to + * configuration index: + */ + + if (bConfigurationValue == 0) { + /* unconfigure */ + i = 255; + } else { + /* lookup configuration index */ + dev = usb_device(udev); + + /* check if the configuration array is not there */ + if (dev->config == NULL) { + return (-1); + } + for (i = 0;; i++) { + if (i == dev->descriptor.bNumConfigurations) { + /* "bConfigurationValue" not found */ + return (-1); + } + if ((dev->config + i)->bConfigurationValue == + bConfigurationValue) { + break; + } + } + } + + err = libusb20_dev_set_config_index((void *)udev, i); + + if (err) + return (-1); + + return (0); +} + +int +usb_claim_interface(usb_dev_handle * dev, int interface) +{ + struct libusb20_device *pdev = (void *)dev; + + pdev->claimed_interface = interface; + + return (0); +} + +int +usb_release_interface(usb_dev_handle * dev, int interface) +{ + /* do nothing */ + return (0); +} + +int +usb_set_altinterface(usb_dev_handle * dev, int alternate) +{ + struct libusb20_device *pdev = (void *)dev; + int err; + uint8_t iface; + + iface = pdev->claimed_interface; + + err = libusb20_dev_set_alt_index((void *)dev, iface, alternate); + + if (err) + return (-1); + + return (0); +} + +int +usb_resetep(usb_dev_handle * dev, unsigned int ep) +{ + /* emulate an endpoint reset through clear-STALL */ + return (usb_clear_halt(dev, ep)); +} + +int +usb_clear_halt(usb_dev_handle * dev, unsigned int ep) +{ + struct libusb20_transfer *xfer; + + xfer = usb_get_transfer_by_ep_no(dev, ep); + if (xfer == NULL) + return (-1); + + libusb20_tr_clear_stall_sync(xfer); + + return (0); +} + +int +usb_reset(usb_dev_handle * dev) +{ + int err; + + err = libusb20_dev_reset((void *)dev); + + if (err) + return (-1); + + /* + * Be compatible with LibUSB from sourceforge and close the + * handle after reset! + */ + return (usb_close(dev)); +} + +int +usb_check_connected(usb_dev_handle * dev) +{ + int err; + + err = libusb20_dev_check_connected((void *)dev); + + if (err) + return (-1); + + return (0); +} + +const char * +usb_strerror(void) +{ + /* TODO */ + return ("Unknown error"); +} + +void +usb_init(void) +{ + /* nothing to do */ + return; +} + +void +usb_set_debug(int level) +{ + /* use kernel UGEN debugging if you need to see what is going on */ + return; +} + +int +usb_find_busses(void) +{ + usb_busses = &usb_global_bus; + return (1); +} + +int +usb_find_devices(void) +{ + struct libusb20_device *pdev; + struct usb_device *udev; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + int devnum; + int err; + + /* cleanup after last device search */ + /* close all opened devices, if any */ + + while ((pdev = libusb20_be_device_foreach(usb_backend, NULL))) { + udev = pdev->privLuData; + libusb20_be_dequeue_device(usb_backend, pdev); + libusb20_dev_free(pdev); + if (udev != NULL) { + LIST_DEL(usb_global_bus.devices, udev); + free(udev); + } + } + + /* free old USB backend, if any */ + + libusb20_be_free(usb_backend); + + /* do a new backend device search */ + usb_backend = libusb20_be_alloc_default(); + if (usb_backend == NULL) { + return (-1); + } + /* iterate all devices */ + + devnum = 1; + pdev = NULL; + while ((pdev = libusb20_be_device_foreach(usb_backend, pdev))) { + udev = malloc(sizeof(*udev)); + if (udev == NULL) + break; + + memset(udev, 0, sizeof(*udev)); + + udev->bus = &usb_global_bus; + + snprintf(udev->filename, sizeof(udev->filename), + "/dev/ugen%u.%u", + libusb20_dev_get_bus_number(pdev), + libusb20_dev_get_address(pdev)); + + ddesc = libusb20_dev_get_device_desc(pdev); + + udev->descriptor.bLength = sizeof(udev->descriptor); + udev->descriptor.bDescriptorType = ddesc->bDescriptorType; + udev->descriptor.bcdUSB = ddesc->bcdUSB; + udev->descriptor.bDeviceClass = ddesc->bDeviceClass; + udev->descriptor.bDeviceSubClass = ddesc->bDeviceSubClass; + udev->descriptor.bDeviceProtocol = ddesc->bDeviceProtocol; + udev->descriptor.bMaxPacketSize0 = ddesc->bMaxPacketSize0; + udev->descriptor.idVendor = ddesc->idVendor; + udev->descriptor.idProduct = ddesc->idProduct; + udev->descriptor.bcdDevice = ddesc->bcdDevice; + udev->descriptor.iManufacturer = ddesc->iManufacturer; + udev->descriptor.iProduct = ddesc->iProduct; + udev->descriptor.iSerialNumber = ddesc->iSerialNumber; + udev->descriptor.bNumConfigurations = + ddesc->bNumConfigurations; + if (udev->descriptor.bNumConfigurations > USB_MAXCONFIG) { + /* truncate number of configurations */ + udev->descriptor.bNumConfigurations = USB_MAXCONFIG; + } + udev->devnum = devnum++; + /* link together the two structures */ + udev->dev = pdev; + pdev->privLuData = udev; + + err = libusb20_dev_open(pdev, 0); + if (err == 0) { + /* XXX get all config descriptors by default */ + usb_fetch_and_parse_descriptors((void *)pdev); + libusb20_dev_close(pdev); + } + LIST_ADD(usb_global_bus.devices, udev); + } + + return (devnum - 1); /* success */ +} + +struct usb_device * +usb_device(usb_dev_handle * dev) +{ + struct libusb20_device *pdev; + + pdev = (void *)dev; + + return (pdev->privLuData); +} + +struct usb_bus * +usb_get_busses(void) +{ + return (usb_busses); +} + +int +usb_get_driver_np(usb_dev_handle * dev, int interface, char *name, int namelen) +{ + struct libusb20_device *pdev; + char *ptr; + int err; + + pdev = (void *)dev; + + if (pdev == NULL) + return (-1); + if (namelen < 1) + return (-1); + if (namelen > 255) + namelen = 255; + + err = libusb20_dev_get_iface_desc(pdev, interface, name, namelen); + if (err != 0) + return (-1); + + /* we only want the driver name */ + ptr = strstr(name, ":"); + if (ptr != NULL) + *ptr = 0; + + return (0); +} + +int +usb_detach_kernel_driver_np(usb_dev_handle * dev, int interface) +{ + struct libusb20_device *pdev; + int err; + + pdev = (void *)dev; + + if (pdev == NULL) + return (-1); + + err = libusb20_dev_detach_kernel_driver(pdev, interface); + if (err != 0) + return (-1); + + return (0); +} diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c new file mode 100644 index 000000000000..e226def0b7b6 --- /dev/null +++ b/lib/libusb/libusb10.c @@ -0,0 +1,1927 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2009 Sylvestre Gallon. All rights reserved. + * Copyright (c) 2009-2023 Hans Petter Selasky + * Copyright (c) 2024 Aymeric Wibo + * Copyright (c) 2025 ShengYi Hung + * + * 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 LIBUSB_GLOBAL_INCLUDE_FILE +#include LIBUSB_GLOBAL_INCLUDE_FILE +#else +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <poll.h> +#include <pthread.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <sys/eventfd.h> +#include <sys/fcntl.h> +#include <sys/ioctl.h> +#include <sys/queue.h> +#include <sys/endian.h> +#endif + +#define libusb_device_handle libusb20_device +#define LIBUSB_LOG_BUFFER_SIZE 1024 + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" +#include "libusb.h" +#include "libusb10.h" + +#define LIBUSB_NUM_SW_ENDPOINTS (16 * 4) + +static pthread_mutex_t default_context_lock = PTHREAD_MUTEX_INITIALIZER; +struct libusb_context *usbi_default_context = NULL; + +/* Prototypes */ + +static struct libusb20_transfer *libusb10_get_transfer(struct libusb20_device *, uint8_t, uint8_t); +static int libusb10_get_buffsize(struct libusb20_device *, libusb_transfer *); +static int libusb10_convert_error(uint8_t status); +static void libusb10_complete_transfer(struct libusb20_transfer *, struct libusb_super_transfer *, int); +static void libusb10_isoc_proxy(struct libusb20_transfer *); +static void libusb10_bulk_intr_proxy(struct libusb20_transfer *); +static void libusb10_ctrl_proxy(struct libusb20_transfer *); +static void libusb10_submit_transfer_sub(struct libusb20_device *, uint8_t); + +/* Library initialisation / deinitialisation */ + +static const struct libusb_version libusb_version = { + .major = 1, + .minor = 0, + .micro = 0, + .nano = 2016, + .rc = "", + .describe = "https://www.freebsd.org" +}; + +static const struct libusb_language_context libusb_language_ctx[] = { + { + .lang_name = "en", + .err_strs = { + [-LIBUSB_SUCCESS] = "Success", + [-LIBUSB_ERROR_IO] = "I/O error", + [-LIBUSB_ERROR_INVALID_PARAM] = "Invalid parameter", + [-LIBUSB_ERROR_ACCESS] = "Permissions error", + [-LIBUSB_ERROR_NO_DEVICE] = "No device", + [-LIBUSB_ERROR_NOT_FOUND] = "Not found", + [-LIBUSB_ERROR_BUSY] = "Device busy", + [-LIBUSB_ERROR_TIMEOUT] = "Timeout", + [-LIBUSB_ERROR_OVERFLOW] = "Overflow", + [-LIBUSB_ERROR_PIPE] = "Pipe error", + [-LIBUSB_ERROR_INTERRUPTED] = "Interrupted", + [-LIBUSB_ERROR_NO_MEM] = "Out of memory", + [-LIBUSB_ERROR_NOT_SUPPORTED] ="Not supported", + [LIBUSB_ERROR_COUNT - 1] = "Other error", + [LIBUSB_ERROR_COUNT] = "Unknown error", + } + }, + { + .lang_name = "zh", + .err_strs = { + [-LIBUSB_SUCCESS] = "成功", + [-LIBUSB_ERROR_IO] = "I/O 錯誤", + [-LIBUSB_ERROR_INVALID_PARAM] = "不合法的參數", + [-LIBUSB_ERROR_ACCESS] = "權限錯誤", + [-LIBUSB_ERROR_NO_DEVICE] = "裝置不存在", + [-LIBUSB_ERROR_NOT_FOUND] = "不存在", + [-LIBUSB_ERROR_BUSY] = "裝置忙碌中", + [-LIBUSB_ERROR_TIMEOUT] = "逾時", + [-LIBUSB_ERROR_OVERFLOW] = "溢位", + [-LIBUSB_ERROR_PIPE] = "管道錯誤", + [-LIBUSB_ERROR_INTERRUPTED] = "被中斷", + [-LIBUSB_ERROR_NO_MEM] = "記憶體不足", + [-LIBUSB_ERROR_NOT_SUPPORTED] ="不支援", + [LIBUSB_ERROR_COUNT - 1] = "其他錯誤", + [LIBUSB_ERROR_COUNT] = "未知錯誤", + } + }, +}; + +static const struct libusb_language_context *default_language_context = + &libusb_language_ctx[0]; + +const struct libusb_version * +libusb_get_version(void) +{ + + return (&libusb_version); +} + +void +libusb_set_debug(libusb_context *ctx, int level) +{ + ctx = GET_CONTEXT(ctx); + /* debug_fixed is set when the environment overrides libusb_set_debug */ + if (ctx && ctx->debug_fixed == 0) + ctx->debug = level; +} + +static void +libusb_set_nonblocking(int f) +{ + int flags; + + /* + * We ignore any failures in this function, hence the + * non-blocking flag is not critical to the operation of + * libUSB. We use F_GETFL and F_SETFL to be compatible with + * Linux. + */ + + flags = fcntl(f, F_GETFL, NULL); + if (flags == -1) + return; + flags |= O_NONBLOCK; + fcntl(f, F_SETFL, flags); +} + +void +libusb_interrupt_event_handler(libusb_context *ctx) +{ + int err; + + if (ctx == NULL) + return; + + err = eventfd_write(ctx->event, 1); + if (err < 0) { + /* ignore error, if any */ + DPRINTF(ctx, LIBUSB_LOG_LEVEL_ERROR, "Waking up event loop failed!"); + } +} + +int +libusb_init(libusb_context **context) +{ + return (libusb_init_context(context, NULL, 0)); +} + +int +libusb_init_context(libusb_context **context, + const struct libusb_init_option option[], int num_options) +{ + struct libusb_context *ctx; + pthread_condattr_t attr; + char *debug, *ep; + + if (num_options < 0) + return (LIBUSB_ERROR_INVALID_PARAM); + + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return (LIBUSB_ERROR_INVALID_PARAM); + + memset(ctx, 0, sizeof(*ctx)); + ctx->devd_pipe = -1; + + debug = getenv("LIBUSB_DEBUG"); + if (debug != NULL) { + /* + * If LIBUSB_DEBUG is set, we'll honor that first and + * use it to override any future libusb_set_debug() + * calls or init options. + */ + errno = 0; + ctx->debug = strtol(debug, &ep, 10); + if (errno == 0 && *ep == '\0') { + ctx->debug_fixed = 1; + } else { + /* + * LIBUSB_DEBUG conversion failed for some reason, but + * we don't care about the specifics all that much. We + * can't use it either way. Force it to the default, + * 0, in case we had a partial number. + */ + ctx->debug = 0; + } + } else { + /* + * If the LIBUSB_OPTION_LOG_LEVEL is set, honor that. + */ + for (int i = 0; i != num_options; i++) { + if (option[i].option != LIBUSB_OPTION_LOG_LEVEL) + continue; + + ctx->debug = (int)option[i].value.ival; + if ((int64_t)ctx->debug == option[i].value.ival) { + ctx->debug_fixed = 1; + } else { + free(ctx); + return (LIBUSB_ERROR_INVALID_PARAM); + } + } + } + + TAILQ_INIT(&ctx->pollfds); + TAILQ_INIT(&ctx->tr_done); + TAILQ_INIT(&ctx->hotplug_cbh); + TAILQ_INIT(&ctx->hotplug_devs); + + if (pthread_mutex_init(&ctx->ctx_lock, NULL) != 0) { + free(ctx); + return (LIBUSB_ERROR_NO_MEM); + } + if (pthread_mutex_init(&ctx->hotplug_lock, NULL) != 0) { + pthread_mutex_destroy(&ctx->ctx_lock); + free(ctx); + return (LIBUSB_ERROR_NO_MEM); + } + if (pthread_condattr_init(&attr) != 0) { + pthread_mutex_destroy(&ctx->ctx_lock); + pthread_mutex_destroy(&ctx->hotplug_lock); + free(ctx); + return (LIBUSB_ERROR_NO_MEM); + } + if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) != 0) { + pthread_mutex_destroy(&ctx->ctx_lock); + pthread_mutex_destroy(&ctx->hotplug_lock); + pthread_condattr_destroy(&attr); + free(ctx); + return (LIBUSB_ERROR_OTHER); + } + if (pthread_cond_init(&ctx->ctx_cond, &attr) != 0) { + pthread_mutex_destroy(&ctx->ctx_lock); + pthread_mutex_destroy(&ctx->hotplug_lock); + pthread_condattr_destroy(&attr); + free(ctx); + return (LIBUSB_ERROR_NO_MEM); + } + pthread_condattr_destroy(&attr); + + ctx->ctx_handler = NO_THREAD; + ctx->hotplug_handler = NO_THREAD; + + ctx->event = eventfd(0, EFD_NONBLOCK); + if (ctx->event < 0) { + pthread_mutex_destroy(&ctx->ctx_lock); + pthread_mutex_destroy(&ctx->hotplug_lock); + pthread_cond_destroy(&ctx->ctx_cond); + free(ctx); + return (LIBUSB_ERROR_OTHER); + } + + libusb10_add_pollfd(ctx, &ctx->ctx_poll, NULL, ctx->event, POLLIN); + + pthread_mutex_lock(&default_context_lock); + if (usbi_default_context == NULL) { + usbi_default_context = ctx; + } + pthread_mutex_unlock(&default_context_lock); + + if (context) + *context = ctx; + + DPRINTF(ctx, LIBUSB_LOG_LEVEL_INFO, "libusb_init complete"); + + signal(SIGPIPE, SIG_IGN); + + return (0); +} + +void +libusb_exit(libusb_context *ctx) +{ + ctx = GET_CONTEXT(ctx); + + if (ctx == NULL) + return; + + /* stop hotplug thread, if any */ + + if (ctx->hotplug_handler != NO_THREAD) { + pthread_t td; + void *ptr; + + HOTPLUG_LOCK(ctx); + td = ctx->hotplug_handler; + ctx->hotplug_handler = NO_THREAD; + if (ctx->usb_event_mode == usb_event_devd) { + close(ctx->devd_pipe); + ctx->devd_pipe = -1; + } else if (ctx->usb_event_mode == usb_event_netlink) { + close(ctx->ss.fd); + ctx->ss.fd = -1; + } + HOTPLUG_UNLOCK(ctx); + + pthread_join(td, &ptr); + } + + /* XXX cleanup devices */ + + libusb10_remove_pollfd(ctx, &ctx->ctx_poll); + close(ctx->event); + pthread_mutex_destroy(&ctx->ctx_lock); + pthread_mutex_destroy(&ctx->hotplug_lock); + pthread_cond_destroy(&ctx->ctx_cond); + + pthread_mutex_lock(&default_context_lock); + if (ctx == usbi_default_context) { + usbi_default_context = NULL; + } + pthread_mutex_unlock(&default_context_lock); + + free(ctx); +} + +/* Device handling and initialisation. */ + +ssize_t +libusb_get_device_list(libusb_context *ctx, libusb_device ***list) +{ + struct libusb20_backend *usb_backend; + struct libusb20_device *pdev, *parent_dev; + struct libusb_device *dev; + int i, j, k; + + ctx = GET_CONTEXT(ctx); + + if (ctx == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (list == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + usb_backend = libusb20_be_alloc_default(); + if (usb_backend == NULL) + return (LIBUSB_ERROR_NO_MEM); + + /* figure out how many USB devices are present */ + pdev = NULL; + i = 0; + while ((pdev = libusb20_be_device_foreach(usb_backend, pdev))) + i++; + + /* allocate device pointer list */ + *list = malloc((i + 1) * sizeof(void *)); + if (*list == NULL) { + libusb20_be_free(usb_backend); + return (LIBUSB_ERROR_NO_MEM); + } + /* create libusb v1.0 compliant devices */ + i = 0; + while ((pdev = libusb20_be_device_foreach(usb_backend, NULL))) { + + dev = malloc(sizeof(*dev)); + if (dev == NULL) { + while (i != 0) { + libusb_unref_device((*list)[i - 1]); + i--; + } + free(*list); + *list = NULL; + libusb20_be_free(usb_backend); + return (LIBUSB_ERROR_NO_MEM); + } + /* get device into libUSB v1.0 list */ + libusb20_be_dequeue_device(usb_backend, pdev); + + memset(dev, 0, sizeof(*dev)); + + /* init transfer queues */ + TAILQ_INIT(&dev->tr_head); + + /* set context we belong to */ + dev->ctx = ctx; + + /* assume we have no parent by default */ + dev->parent_dev = NULL; + + /* link together the two structures */ + dev->os_priv = pdev; + pdev->privLuData = dev; + + (*list)[i] = libusb_ref_device(dev); + i++; + } + (*list)[i] = NULL; + + /* for each device, find its parent */ + for (j = 0; j < i; j++) { + pdev = (*list)[j]->os_priv; + + for (k = 0; k < i; k++) { + if (k == j) + continue; + + parent_dev = (*list)[k]->os_priv; + + if (parent_dev->bus_number != pdev->bus_number) + continue; + if (parent_dev->device_address == pdev->parent_address) { + (*list)[j]->parent_dev = libusb_ref_device((*list)[k]); + break; + } + } + } + + libusb20_be_free(usb_backend); + return (i); +} + +void +libusb_free_device_list(libusb_device **list, int unref_devices) +{ + int i; + + if (list == NULL) + return; /* be NULL safe */ + + if (unref_devices) { + for (i = 0; list[i] != NULL; i++) + libusb_unref_device(list[i]); + } + free(list); +} + +uint8_t +libusb_get_bus_number(libusb_device *dev) +{ + if (dev == NULL) + return (0); /* should not happen */ + return (libusb20_dev_get_bus_number(dev->os_priv)); +} + +uint8_t +libusb_get_port_number(libusb_device *dev) +{ + if (dev == NULL) + return (0); /* should not happen */ + return (libusb20_dev_get_parent_port(dev->os_priv)); +} + +int +libusb_get_port_numbers(libusb_device *dev, uint8_t *buf, uint8_t bufsize) +{ + return (libusb20_dev_get_port_path(dev->os_priv, buf, bufsize)); +} + +int +libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *buf, + uint8_t bufsize) +{ + return (libusb20_dev_get_port_path(dev->os_priv, buf, bufsize)); +} + +uint8_t +libusb_get_device_address(libusb_device *dev) +{ + if (dev == NULL) + return (0); /* should not happen */ + return (libusb20_dev_get_address(dev->os_priv)); +} + +enum libusb_speed +libusb_get_device_speed(libusb_device *dev) +{ + if (dev == NULL) + return (LIBUSB_SPEED_UNKNOWN); /* should not happen */ + + switch (libusb20_dev_get_speed(dev->os_priv)) { + case LIBUSB20_SPEED_LOW: + return (LIBUSB_SPEED_LOW); + case LIBUSB20_SPEED_FULL: + return (LIBUSB_SPEED_FULL); + case LIBUSB20_SPEED_HIGH: + return (LIBUSB_SPEED_HIGH); + case LIBUSB20_SPEED_SUPER: + return (LIBUSB_SPEED_SUPER); + case LIBUSB20_SPEED_SUPER_PLUS: + return (LIBUSB_SPEED_SUPER_PLUS); + default: + break; + } + return (LIBUSB_SPEED_UNKNOWN); +} + +int +libusb_get_max_packet_size(libusb_device *dev, uint8_t endpoint) +{ + struct libusb_config_descriptor *pdconf; + struct libusb_interface *pinf; + struct libusb_interface_descriptor *pdinf; + struct libusb_endpoint_descriptor *pdend; + int i; + int j; + int k; + int ret; + + if (dev == NULL) + return (LIBUSB_ERROR_NO_DEVICE); + + ret = libusb_get_active_config_descriptor(dev, &pdconf); + if (ret < 0) + return (ret); + + ret = LIBUSB_ERROR_NOT_FOUND; + for (i = 0; i < pdconf->bNumInterfaces; i++) { + pinf = &pdconf->interface[i]; + for (j = 0; j < pinf->num_altsetting; j++) { + pdinf = &pinf->altsetting[j]; + for (k = 0; k < pdinf->bNumEndpoints; k++) { + pdend = &pdinf->endpoint[k]; + if (pdend->bEndpointAddress == endpoint) { + ret = pdend->wMaxPacketSize; + goto out; + } + } + } + } + +out: + libusb_free_config_descriptor(pdconf); + return (ret); +} + +int +libusb_get_max_iso_packet_size(libusb_device *dev, uint8_t endpoint) +{ + int multiplier; + int ret; + + ret = libusb_get_max_packet_size(dev, endpoint); + + switch (libusb20_dev_get_speed(dev->os_priv)) { + case LIBUSB20_SPEED_LOW: + case LIBUSB20_SPEED_FULL: + break; + default: + if (ret > -1) { + multiplier = (1 + ((ret >> 11) & 3)); + if (multiplier > 3) + multiplier = 3; + ret = (ret & 0x7FF) * multiplier; + } + break; + } + return (ret); +} + +libusb_device * +libusb_ref_device(libusb_device *dev) +{ + if (dev == NULL) + return (NULL); /* be NULL safe */ + + CTX_LOCK(dev->ctx); + dev->refcnt++; + CTX_UNLOCK(dev->ctx); + + return (dev); +} + +void +libusb_unref_device(libusb_device *dev) +{ + if (dev == NULL) + return; /* be NULL safe */ + + CTX_LOCK(dev->ctx); + dev->refcnt--; + CTX_UNLOCK(dev->ctx); + + if (dev->refcnt == 0) { + libusb_unref_device(dev->parent_dev); + libusb20_dev_free(dev->os_priv); + free(dev); + } +} + +int +libusb_open(libusb_device *dev, libusb_device_handle **devh) +{ + libusb_context *ctx = dev->ctx; + struct libusb20_device *pdev = dev->os_priv; + int err; + + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + /* set default device handle value */ + *devh = NULL; + + dev = libusb_ref_device(dev); + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + err = libusb20_dev_open(pdev, LIBUSB_NUM_SW_ENDPOINTS); + if (err) { + libusb_unref_device(dev); + return (LIBUSB_ERROR_NO_MEM); + } + + /* + * Clear the device gone flag, in case the device was opened + * after a re-attach, to allow new transaction: + */ + CTX_LOCK(ctx); + dev->device_is_gone = 0; + CTX_UNLOCK(ctx); + + libusb10_add_pollfd(ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN | + POLLOUT | POLLRDNORM | POLLWRNORM); + + /* make sure our event loop detects the new device */ + libusb_interrupt_event_handler(ctx); + + *devh = pdev; + + return (0); +} + +libusb_device_handle * +libusb_open_device_with_vid_pid(libusb_context *ctx, uint16_t vendor_id, + uint16_t product_id) +{ + struct libusb_device **devs; + struct libusb20_device *pdev; + struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; + int i; + int j; + + ctx = GET_CONTEXT(ctx); + if (ctx == NULL) + return (NULL); /* be NULL safe */ + + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_open_device_with_vid_pid enter"); + + if ((i = libusb_get_device_list(ctx, &devs)) < 0) + return (NULL); + + pdev = NULL; + for (j = 0; j < i; j++) { + struct libusb20_device *tdev; + + tdev = devs[j]->os_priv; + pdesc = libusb20_dev_get_device_desc(tdev); + /* + * NOTE: The USB library will automatically swap the + * fields in the device descriptor to be of host + * endian type! + */ + if (pdesc->idVendor == vendor_id && + pdesc->idProduct == product_id) { + libusb_open(devs[j], &pdev); + break; + } + } + + libusb_free_device_list(devs, 1); + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_open_device_with_vid_pid leave"); + return (pdev); +} + +void +libusb_close(struct libusb20_device *pdev) +{ + libusb_context *ctx; + struct libusb_device *dev; + + if (pdev == NULL) + return; /* be NULL safe */ + + dev = libusb_get_device(pdev); + ctx = dev->ctx; + + libusb10_remove_pollfd(ctx, &dev->dev_poll); + + libusb20_dev_close(pdev); + + /* unref will free the "pdev" when the refcount reaches zero */ + libusb_unref_device(dev); + + /* make sure our event loop detects the closed device */ + libusb_interrupt_event_handler(ctx); +} + +libusb_device * +libusb_get_device(struct libusb20_device *pdev) +{ + if (pdev == NULL) + return (NULL); + return ((libusb_device *)pdev->privLuData); +} + +int +libusb_get_configuration(struct libusb20_device *pdev, int *config) +{ + struct libusb20_config *pconf; + + if (pdev == NULL || config == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + pconf = libusb20_dev_alloc_config(pdev, libusb20_dev_get_config_index(pdev)); + if (pconf == NULL) + return (LIBUSB_ERROR_NO_MEM); + + *config = pconf->desc.bConfigurationValue; + + free(pconf); + + return (0); +} + +int +libusb_set_configuration(struct libusb20_device *pdev, int configuration) +{ + struct libusb20_config *pconf; + struct libusb_device *dev; + int err; + uint8_t i; + + dev = libusb_get_device(pdev); + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (configuration < 1) { + /* unconfigure */ + i = 255; + } else { + for (i = 0; i != 255; i++) { + uint8_t found; + + pconf = libusb20_dev_alloc_config(pdev, i); + if (pconf == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + found = (pconf->desc.bConfigurationValue + == configuration); + free(pconf); + + if (found) + goto set_config; + } + return (LIBUSB_ERROR_INVALID_PARAM); + } + +set_config: + + libusb10_cancel_all_transfer(dev); + + libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); + + err = libusb20_dev_set_config_index(pdev, i); + + libusb10_add_pollfd(dev->ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN | + POLLOUT | POLLRDNORM | POLLWRNORM); + + return (err ? LIBUSB_ERROR_INVALID_PARAM : 0); +} + +int +libusb_claim_interface(struct libusb20_device *pdev, int interface_number) +{ + libusb_device *dev; + int err = 0; + + dev = libusb_get_device(pdev); + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (interface_number < 0 || interface_number > 31) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (pdev->auto_detach != 0) { + err = libusb_detach_kernel_driver(pdev, interface_number); + if (err != 0) + goto done; + } + + CTX_LOCK(dev->ctx); + dev->claimed_interfaces |= (1 << interface_number); + CTX_UNLOCK(dev->ctx); +done: + return (err); +} + +int +libusb_release_interface(struct libusb20_device *pdev, int interface_number) +{ + libusb_device *dev; + int err = 0; + + dev = libusb_get_device(pdev); + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (interface_number < 0 || interface_number > 31) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (pdev->auto_detach != 0) { + err = libusb_attach_kernel_driver(pdev, interface_number); + if (err != 0) + goto done; + } + + CTX_LOCK(dev->ctx); + if (!(dev->claimed_interfaces & (1 << interface_number))) + err = LIBUSB_ERROR_NOT_FOUND; + else + dev->claimed_interfaces &= ~(1 << interface_number); + CTX_UNLOCK(dev->ctx); +done: + return (err); +} + +int +libusb_set_interface_alt_setting(struct libusb20_device *pdev, + int interface_number, int alternate_setting) +{ + libusb_device *dev; + int err = 0; + + dev = libusb_get_device(pdev); + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (interface_number < 0 || interface_number > 31) + return (LIBUSB_ERROR_INVALID_PARAM); + + CTX_LOCK(dev->ctx); + if (!(dev->claimed_interfaces & (1 << interface_number))) + err = LIBUSB_ERROR_NOT_FOUND; + CTX_UNLOCK(dev->ctx); + + if (err) + return (err); + + libusb10_cancel_all_transfer(dev); + + libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); + + err = libusb20_dev_set_alt_index(pdev, + interface_number, alternate_setting); + + libusb10_add_pollfd(dev->ctx, &dev->dev_poll, + pdev, libusb20_dev_get_fd(pdev), + POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); + + return (err ? LIBUSB_ERROR_OTHER : 0); +} + +libusb_device * +libusb_get_parent(libusb_device *dev) +{ + return (dev->parent_dev); +} + +static struct libusb20_transfer * +libusb10_get_transfer(struct libusb20_device *pdev, + uint8_t endpoint, uint8_t xfer_index) +{ + xfer_index &= 1; /* double buffering */ + + xfer_index |= (endpoint & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 4; + + if (endpoint & LIBUSB20_ENDPOINT_DIR_MASK) { + /* this is an IN endpoint */ + xfer_index |= 2; + } + return (libusb20_tr_get_pointer(pdev, xfer_index)); +} + +int +libusb_clear_halt(struct libusb20_device *pdev, uint8_t endpoint) +{ + struct libusb20_transfer *xfer; + struct libusb_device *dev; + int err; + + xfer = libusb10_get_transfer(pdev, endpoint, 0); + if (xfer == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + dev = libusb_get_device(pdev); + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + CTX_LOCK(dev->ctx); + err = libusb20_tr_open(xfer, 0, 1, endpoint); + CTX_UNLOCK(dev->ctx); + + if (err != 0 && err != LIBUSB20_ERROR_BUSY) + return (LIBUSB_ERROR_OTHER); + + libusb20_tr_clear_stall_sync(xfer); + + /* check if we opened the transfer */ + if (err == 0) { + CTX_LOCK(dev->ctx); + libusb20_tr_close(xfer); + CTX_UNLOCK(dev->ctx); + } + return (0); /* success */ +} + +int +libusb_reset_device(struct libusb20_device *pdev) +{ + libusb_device *dev; + int err; + + dev = libusb_get_device(pdev); + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + libusb10_cancel_all_transfer(dev); + + libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); + + err = libusb20_dev_reset(pdev); + + libusb10_add_pollfd(dev->ctx, &dev->dev_poll, + pdev, libusb20_dev_get_fd(pdev), + POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); + + return (err ? LIBUSB_ERROR_OTHER : 0); +} + +int +libusb_check_connected(struct libusb20_device *pdev) +{ + libusb_device *dev; + int err; + + dev = libusb_get_device(pdev); + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + err = libusb20_dev_check_connected(pdev); + + return (err ? LIBUSB_ERROR_NO_DEVICE : 0); +} + +int +libusb_kernel_driver_active(struct libusb20_device *pdev, int interface) +{ + if (pdev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (libusb20_dev_kernel_driver_active(pdev, interface)) + return (0); /* no kernel driver is active */ + else + return (1); /* kernel driver is active */ +} + +int +libusb_get_driver_np(struct libusb20_device *pdev, int interface, + char *name, int namelen) +{ + return (libusb_get_driver(pdev, interface, name, namelen)); +} + +int +libusb_get_driver(struct libusb20_device *pdev, int interface, + char *name, int namelen) +{ + char *ptr; + int err; + + if (pdev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + if (namelen < 1) + return (LIBUSB_ERROR_INVALID_PARAM); + if (namelen > 255) + namelen = 255; + + err = libusb20_dev_get_iface_desc( + pdev, interface, name, namelen); + + if (err != 0) + return (LIBUSB_ERROR_OTHER); + + /* we only want the driver name */ + ptr = strstr(name, ":"); + if (ptr != NULL) + *ptr = 0; + + return (0); +} + +int +libusb_detach_kernel_driver_np(struct libusb20_device *pdev, int interface) +{ + return (libusb_detach_kernel_driver(pdev, interface)); +} + +int +libusb_detach_kernel_driver(struct libusb20_device *pdev, int interface) +{ + int err; + + if (pdev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + err = libusb20_dev_detach_kernel_driver( + pdev, interface); + + return (err ? LIBUSB_ERROR_OTHER : 0); +} + +int +libusb_attach_kernel_driver(struct libusb20_device *pdev, int interface) +{ + if (pdev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + /* stub - currently not supported by libusb20 */ + return (0); +} + +int +libusb_set_auto_detach_kernel_driver(libusb_device_handle *dev, int enable) +{ + dev->auto_detach = (enable ? 1 : 0); + return (0); +} + +/* Asynchronous device I/O */ + +struct libusb_transfer * +libusb_alloc_transfer(int iso_packets) +{ + struct libusb_transfer *uxfer; + struct libusb_super_transfer *sxfer; + int len; + + len = sizeof(struct libusb_transfer) + + sizeof(struct libusb_super_transfer) + + (iso_packets * sizeof(libusb_iso_packet_descriptor)); + + sxfer = malloc(len); + if (sxfer == NULL) + return (NULL); + + memset(sxfer, 0, len); + + uxfer = (struct libusb_transfer *)( + ((uint8_t *)sxfer) + sizeof(*sxfer)); + + /* set default value */ + uxfer->num_iso_packets = iso_packets; + + return (uxfer); +} + +void +libusb_free_transfer(struct libusb_transfer *uxfer) +{ + struct libusb_super_transfer *sxfer; + + if (uxfer == NULL) + return; /* be NULL safe */ + + /* check if we should free the transfer buffer */ + if (uxfer->flags & LIBUSB_TRANSFER_FREE_BUFFER) + free(uxfer->buffer); + + sxfer = (struct libusb_super_transfer *)( + (uint8_t *)uxfer - sizeof(*sxfer)); + + free(sxfer); +} + +static uint32_t +libusb10_get_maxframe(struct libusb20_device *pdev, libusb_transfer *xfer) +{ + uint32_t ret; + + switch (xfer->type) { + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + ret = 60 | LIBUSB20_MAX_FRAME_PRE_SCALE; /* 60ms */ + break; + case LIBUSB_TRANSFER_TYPE_CONTROL: + ret = 2; + break; + default: + ret = 1; + break; + } + return (ret); +} + +static int +libusb10_get_buffsize(struct libusb20_device *pdev, libusb_transfer *xfer) +{ + int ret; + int usb_speed; + + usb_speed = libusb20_dev_get_speed(pdev); + + switch (xfer->type) { + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + ret = 0; /* kernel will auto-select */ + break; + case LIBUSB_TRANSFER_TYPE_CONTROL: + ret = 1024; + break; + default: + switch (usb_speed) { + case LIBUSB20_SPEED_LOW: + ret = 256; + break; + case LIBUSB20_SPEED_FULL: + ret = 4096; + break; + case LIBUSB20_SPEED_SUPER: + ret = 65536; + break; + case LIBUSB20_SPEED_SUPER_PLUS: + ret = 131072; + break; + default: + ret = 16384; + break; + } + break; + } + return (ret); +} + +static int +libusb10_convert_error(uint8_t status) +{ + ; /* indent fix */ + + switch (status) { + case LIBUSB20_TRANSFER_START: + case LIBUSB20_TRANSFER_COMPLETED: + return (LIBUSB_TRANSFER_COMPLETED); + case LIBUSB20_TRANSFER_OVERFLOW: + return (LIBUSB_TRANSFER_OVERFLOW); + case LIBUSB20_TRANSFER_NO_DEVICE: + return (LIBUSB_TRANSFER_NO_DEVICE); + case LIBUSB20_TRANSFER_STALL: + return (LIBUSB_TRANSFER_STALL); + case LIBUSB20_TRANSFER_CANCELLED: + return (LIBUSB_TRANSFER_CANCELLED); + case LIBUSB20_TRANSFER_TIMED_OUT: + return (LIBUSB_TRANSFER_TIMED_OUT); + default: + return (LIBUSB_TRANSFER_ERROR); + } +} + +/* This function must be called locked */ + +static void +libusb10_complete_transfer(struct libusb20_transfer *pxfer, + struct libusb_super_transfer *sxfer, int status) +{ + struct libusb_transfer *uxfer; + struct libusb_device *dev; + + uxfer = (struct libusb_transfer *)( + ((uint8_t *)sxfer) + sizeof(*sxfer)); + + if (pxfer != NULL) + libusb20_tr_set_priv_sc1(pxfer, NULL); + + /* set transfer status */ + uxfer->status = status; + + /* update super transfer state */ + sxfer->state = LIBUSB_SUPER_XFER_ST_NONE; + + dev = libusb_get_device(uxfer->dev_handle); + + TAILQ_INSERT_TAIL(&dev->ctx->tr_done, sxfer, entry); +} + +/* This function must be called locked */ + +static void +libusb10_isoc_proxy(struct libusb20_transfer *pxfer) +{ + struct libusb_super_transfer *sxfer; + struct libusb_transfer *uxfer; + uint32_t actlen; + uint16_t iso_packets; + uint16_t i; + uint8_t status; + + status = libusb20_tr_get_status(pxfer); + sxfer = libusb20_tr_get_priv_sc1(pxfer); + actlen = libusb20_tr_get_actual_length(pxfer); + iso_packets = libusb20_tr_get_max_frames(pxfer); + + if (sxfer == NULL) + return; /* cancelled - nothing to do */ + + uxfer = (struct libusb_transfer *)( + ((uint8_t *)sxfer) + sizeof(*sxfer)); + + if (iso_packets > uxfer->num_iso_packets) + iso_packets = uxfer->num_iso_packets; + + if (iso_packets == 0) + return; /* nothing to do */ + + /* make sure that the number of ISOCHRONOUS packets is valid */ + uxfer->num_iso_packets = iso_packets; + + switch (status) { + case LIBUSB20_TRANSFER_COMPLETED: + /* update actual length */ + uxfer->actual_length = actlen; + for (i = 0; i != iso_packets; i++) { + uxfer->iso_packet_desc[i].actual_length = + libusb20_tr_get_length(pxfer, i); + } + libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); + break; + case LIBUSB20_TRANSFER_START: + /* setup length(s) */ + actlen = 0; + for (i = 0; i != iso_packets; i++) { + libusb20_tr_setup_isoc(pxfer, + &uxfer->buffer[actlen], + uxfer->iso_packet_desc[i].length, i); + actlen += uxfer->iso_packet_desc[i].length; + } + + /* no remainder */ + sxfer->rem_len = 0; + + libusb20_tr_set_total_frames(pxfer, iso_packets); + libusb20_tr_submit(pxfer); + + /* fork another USB transfer, if any */ + libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); + break; + default: + libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); + break; + } +} + +/* This function must be called locked */ + +static void +libusb10_bulk_intr_proxy(struct libusb20_transfer *pxfer) +{ + struct libusb_super_transfer *sxfer; + struct libusb_transfer *uxfer; + uint32_t max_bulk; + uint32_t actlen; + uint8_t status; + uint8_t flags; + + status = libusb20_tr_get_status(pxfer); + sxfer = libusb20_tr_get_priv_sc1(pxfer); + max_bulk = libusb20_tr_get_max_total_length(pxfer); + actlen = libusb20_tr_get_actual_length(pxfer); + + if (sxfer == NULL) + return; /* cancelled - nothing to do */ + + uxfer = (struct libusb_transfer *)( + ((uint8_t *)sxfer) + sizeof(*sxfer)); + + flags = uxfer->flags; + + switch (status) { + case LIBUSB20_TRANSFER_COMPLETED: + + uxfer->actual_length += actlen; + + /* check for short packet */ + if (sxfer->last_len != actlen) { + if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { + libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR); + } else { + libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); + } + break; + } + /* check for end of data */ + if (sxfer->rem_len == 0) { + libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); + break; + } + /* FALLTHROUGH */ + + case LIBUSB20_TRANSFER_START: + if (max_bulk > sxfer->rem_len) { + max_bulk = sxfer->rem_len; + } + /* setup new BULK or INTERRUPT transaction */ + libusb20_tr_setup_bulk(pxfer, + sxfer->curr_data, max_bulk, uxfer->timeout); + + /* update counters */ + sxfer->last_len = max_bulk; + sxfer->curr_data += max_bulk; + sxfer->rem_len -= max_bulk; + + libusb20_tr_submit(pxfer); + + /* check if we can fork another USB transfer */ + if (sxfer->rem_len == 0) + libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); + break; + + default: + libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); + break; + } +} + +/* This function must be called locked */ + +static void +libusb10_ctrl_proxy(struct libusb20_transfer *pxfer) +{ + struct libusb_super_transfer *sxfer; + struct libusb_transfer *uxfer; + uint32_t max_bulk; + uint32_t actlen; + uint8_t status; + uint8_t flags; + + status = libusb20_tr_get_status(pxfer); + sxfer = libusb20_tr_get_priv_sc1(pxfer); + max_bulk = libusb20_tr_get_max_total_length(pxfer); + actlen = libusb20_tr_get_actual_length(pxfer); + + if (sxfer == NULL) + return; /* cancelled - nothing to do */ + + uxfer = (struct libusb_transfer *)( + ((uint8_t *)sxfer) + sizeof(*sxfer)); + + flags = uxfer->flags; + + switch (status) { + case LIBUSB20_TRANSFER_COMPLETED: + + uxfer->actual_length += actlen; + + /* subtract length of SETUP packet, if any */ + actlen -= libusb20_tr_get_length(pxfer, 0); + + /* check for short packet */ + if (sxfer->last_len != actlen) { + if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { + libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR); + } else { + libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); + } + break; + } + /* check for end of data */ + if (sxfer->rem_len == 0) { + libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); + break; + } + /* FALLTHROUGH */ + + case LIBUSB20_TRANSFER_START: + if (max_bulk > sxfer->rem_len) { + max_bulk = sxfer->rem_len; + } + /* setup new CONTROL transaction */ + if (status == LIBUSB20_TRANSFER_COMPLETED) { + /* next fragment - don't send SETUP packet */ + libusb20_tr_set_length(pxfer, 0, 0); + } else { + /* first fragment - send SETUP packet */ + libusb20_tr_set_length(pxfer, 8, 0); + libusb20_tr_set_buffer(pxfer, uxfer->buffer, 0); + } + + if (max_bulk != 0) { + libusb20_tr_set_length(pxfer, max_bulk, 1); + libusb20_tr_set_buffer(pxfer, sxfer->curr_data, 1); + libusb20_tr_set_total_frames(pxfer, 2); + } else { + libusb20_tr_set_total_frames(pxfer, 1); + } + + /* update counters */ + sxfer->last_len = max_bulk; + sxfer->curr_data += max_bulk; + sxfer->rem_len -= max_bulk; + + libusb20_tr_submit(pxfer); + + /* check if we can fork another USB transfer */ + if (sxfer->rem_len == 0) + libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); + break; + + default: + libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); + break; + } +} + +/* The following function must be called locked */ + +static void +libusb10_submit_transfer_sub(struct libusb20_device *pdev, uint8_t endpoint) +{ + struct libusb20_transfer *pxfer0; + struct libusb20_transfer *pxfer1; + struct libusb_super_transfer *sxfer; + struct libusb_transfer *uxfer; + struct libusb_device *dev; + int err; + int buffsize; + int maxframe; + int temp; + + dev = libusb_get_device(pdev); + + pxfer0 = libusb10_get_transfer(pdev, endpoint, 0); + pxfer1 = libusb10_get_transfer(pdev, endpoint, 1); + + if (pxfer0 == NULL || pxfer1 == NULL) + return; /* shouldn't happen */ + + temp = 0; + if (libusb20_tr_pending(pxfer0)) + temp |= 1; + if (libusb20_tr_pending(pxfer1)) + temp |= 2; + + switch (temp) { + case 3: + /* wait till one of the transfers complete */ + return; + case 2: + sxfer = libusb20_tr_get_priv_sc1(pxfer1); + if (sxfer == NULL) + return; /* cancelling */ + if (sxfer->rem_len) + return; /* cannot queue another one */ + /* swap transfers */ + pxfer1 = pxfer0; + break; + case 1: + sxfer = libusb20_tr_get_priv_sc1(pxfer0); + if (sxfer == NULL) + return; /* cancelling */ + if (sxfer->rem_len) + return; /* cannot queue another one */ + /* swap transfers */ + pxfer0 = pxfer1; + break; + default: + break; + } + + /* find next transfer on same endpoint */ + TAILQ_FOREACH(sxfer, &dev->tr_head, entry) { + + uxfer = (struct libusb_transfer *)( + ((uint8_t *)sxfer) + sizeof(*sxfer)); + + if (uxfer->endpoint == endpoint) { + TAILQ_REMOVE(&dev->tr_head, sxfer, entry); + sxfer->entry.tqe_prev = NULL; + goto found; + } + } + return; /* success */ + +found: + + libusb20_tr_set_priv_sc0(pxfer0, pdev); + libusb20_tr_set_priv_sc1(pxfer0, sxfer); + + /* reset super transfer state */ + sxfer->rem_len = uxfer->length; + sxfer->curr_data = uxfer->buffer; + uxfer->actual_length = 0; + + switch (uxfer->type) { + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + libusb20_tr_set_callback(pxfer0, libusb10_isoc_proxy); + break; + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + libusb20_tr_set_callback(pxfer0, libusb10_bulk_intr_proxy); + break; + case LIBUSB_TRANSFER_TYPE_CONTROL: + libusb20_tr_set_callback(pxfer0, libusb10_ctrl_proxy); + if (sxfer->rem_len < 8) + goto failure; + + /* remove SETUP packet from data */ + sxfer->rem_len -= 8; + sxfer->curr_data += 8; + break; + default: + goto failure; + } + + buffsize = libusb10_get_buffsize(pdev, uxfer); + maxframe = libusb10_get_maxframe(pdev, uxfer); + + /* make sure the transfer is opened */ + err = libusb20_tr_open_stream(pxfer0, buffsize, maxframe, + endpoint, sxfer->stream_id); + if (err && (err != LIBUSB20_ERROR_BUSY)) { + goto failure; + } + libusb20_tr_start(pxfer0); + return; + +failure: + libusb10_complete_transfer(pxfer0, sxfer, LIBUSB_TRANSFER_ERROR); + /* make sure our event loop spins the done handler */ + libusb_interrupt_event_handler(dev->ctx); +} + +/* The following function must be called unlocked */ + +int +libusb_submit_transfer(struct libusb_transfer *uxfer) +{ + struct libusb20_transfer *pxfer0; + struct libusb20_transfer *pxfer1; + struct libusb_super_transfer *sxfer; + struct libusb_device *dev; + uint8_t endpoint; + int err; + + if (uxfer == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (uxfer->dev_handle == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + endpoint = uxfer->endpoint; + + dev = libusb_get_device(uxfer->dev_handle); + + DPRINTF(dev->ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_submit_transfer enter"); + + sxfer = (struct libusb_super_transfer *)( + (uint8_t *)uxfer - sizeof(*sxfer)); + + CTX_LOCK(dev->ctx); + + pxfer0 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 0); + pxfer1 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 1); + + if (pxfer0 == NULL || pxfer1 == NULL) { + err = LIBUSB_ERROR_OTHER; + } else if ((sxfer->entry.tqe_prev != NULL) || + (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) || + (libusb20_tr_get_priv_sc1(pxfer1) == sxfer)) { + err = LIBUSB_ERROR_BUSY; + } else if (dev->device_is_gone != 0) { + err = LIBUSB_ERROR_NO_DEVICE; + } else { + + /* set pending state */ + sxfer->state = LIBUSB_SUPER_XFER_ST_PEND; + + /* insert transfer into transfer head list */ + TAILQ_INSERT_TAIL(&dev->tr_head, sxfer, entry); + + /* start work transfers */ + libusb10_submit_transfer_sub( + uxfer->dev_handle, endpoint); + + err = 0; /* success */ + } + + CTX_UNLOCK(dev->ctx); + + DPRINTF(dev->ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_submit_transfer leave %d", err); + + return (err); +} + +/* Asynchronous transfer cancel */ + +int +libusb_cancel_transfer(struct libusb_transfer *uxfer) +{ + struct libusb20_transfer *pxfer0; + struct libusb20_transfer *pxfer1; + struct libusb_super_transfer *sxfer; + struct libusb_device *dev; + struct libusb_device_handle *devh; + uint8_t endpoint; + int retval; + + if (uxfer == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + /* check if not initialised */ + if ((devh = uxfer->dev_handle) == NULL) + return (LIBUSB_ERROR_NOT_FOUND); + + endpoint = uxfer->endpoint; + + dev = libusb_get_device(devh); + + DPRINTF(dev->ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_cancel_transfer enter"); + + sxfer = (struct libusb_super_transfer *)( + (uint8_t *)uxfer - sizeof(*sxfer)); + + retval = 0; + + CTX_LOCK(dev->ctx); + + pxfer0 = libusb10_get_transfer(devh, endpoint, 0); + pxfer1 = libusb10_get_transfer(devh, endpoint, 1); + + if (sxfer->state != LIBUSB_SUPER_XFER_ST_PEND) { + /* only update the transfer status */ + uxfer->status = LIBUSB_TRANSFER_CANCELLED; + retval = LIBUSB_ERROR_NOT_FOUND; + } else if (sxfer->entry.tqe_prev != NULL) { + /* we are lucky - transfer is on a queue */ + TAILQ_REMOVE(&dev->tr_head, sxfer, entry); + sxfer->entry.tqe_prev = NULL; + libusb10_complete_transfer(NULL, + sxfer, LIBUSB_TRANSFER_CANCELLED); + /* make sure our event loop spins the done handler */ + libusb_interrupt_event_handler(dev->ctx); + } else if (pxfer0 == NULL || pxfer1 == NULL) { + /* not started */ + retval = LIBUSB_ERROR_NOT_FOUND; + } else if (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) { + libusb10_complete_transfer(pxfer0, + sxfer, LIBUSB_TRANSFER_CANCELLED); + if (dev->device_is_gone != 0) { + /* clear transfer pointer */ + libusb20_tr_set_priv_sc1(pxfer0, NULL); + /* make sure our event loop spins the done handler */ + libusb_interrupt_event_handler(dev->ctx); + } else { + libusb20_tr_stop(pxfer0); + /* make sure the queue doesn't stall */ + libusb10_submit_transfer_sub(devh, endpoint); + } + } else if (libusb20_tr_get_priv_sc1(pxfer1) == sxfer) { + libusb10_complete_transfer(pxfer1, + sxfer, LIBUSB_TRANSFER_CANCELLED); + /* check if handle is still active */ + if (dev->device_is_gone != 0) { + /* clear transfer pointer */ + libusb20_tr_set_priv_sc1(pxfer1, NULL); + /* make sure our event loop spins the done handler */ + libusb_interrupt_event_handler(dev->ctx); + } else { + libusb20_tr_stop(pxfer1); + /* make sure the queue doesn't stall */ + libusb10_submit_transfer_sub(devh, endpoint); + } + } else { + /* not started */ + retval = LIBUSB_ERROR_NOT_FOUND; + } + + CTX_UNLOCK(dev->ctx); + + DPRINTF(dev->ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_cancel_transfer leave"); + + return (retval); +} + +UNEXPORTED void +libusb10_cancel_all_transfer(libusb_device *dev) +{ + struct libusb20_device *pdev = dev->os_priv; + unsigned x; + + for (x = 0; x != LIBUSB_NUM_SW_ENDPOINTS; x++) { + struct libusb20_transfer *xfer; + + xfer = libusb20_tr_get_pointer(pdev, x); + if (xfer == NULL) + continue; + libusb20_tr_close(xfer); + } +} + +UNEXPORTED void +libusb10_cancel_all_transfer_locked(struct libusb20_device *pdev, struct libusb_device *dev) +{ + struct libusb_super_transfer *sxfer; + unsigned x; + + for (x = 0; x != LIBUSB_NUM_SW_ENDPOINTS; x++) { + struct libusb20_transfer *xfer; + + xfer = libusb20_tr_get_pointer(pdev, x); + if (xfer == NULL) + continue; + if (libusb20_tr_pending(xfer) == 0) + continue; + sxfer = libusb20_tr_get_priv_sc1(xfer); + if (sxfer == NULL) + continue; + /* complete pending transfer */ + libusb10_complete_transfer(xfer, sxfer, LIBUSB_TRANSFER_ERROR); + } + + while ((sxfer = TAILQ_FIRST(&dev->tr_head))) { + TAILQ_REMOVE(&dev->tr_head, sxfer, entry); + + /* complete pending transfer */ + libusb10_complete_transfer(NULL, sxfer, LIBUSB_TRANSFER_ERROR); + } +} + +uint16_t +libusb_cpu_to_le16(uint16_t x) +{ + return (htole16(x)); +} + +uint16_t +libusb_le16_to_cpu(uint16_t x) +{ + return (le16toh(x)); +} + +const char * +libusb_strerror(int code) +{ + int entry = -code; + + if (code == LIBUSB_ERROR_OTHER) + entry = LIBUSB_ERROR_COUNT - 1; + /* + * The libusb upstream considers all code out of range a + * LIBUSB_ERROR_OTHER. In FreeBSD, it is a special unknown error. We + * preserve the FreeBSD implementation as I think it make sense. + */ + if (entry < 0 || entry >= LIBUSB_ERROR_COUNT) + entry = LIBUSB_ERROR_COUNT; + + /* + * Fall back to English one as the translation may be unimplemented + * when adding new error code. + */ + if (default_language_context->err_strs[entry] == NULL) + return (libusb_language_ctx[0].err_strs[entry]); + + return (default_language_context->err_strs[entry]); +} + +const char * +libusb_error_name(int code) +{ + switch (code) { + case LIBUSB_SUCCESS: + return ("LIBUSB_SUCCESS"); + case LIBUSB_ERROR_IO: + return ("LIBUSB_ERROR_IO"); + case LIBUSB_ERROR_INVALID_PARAM: + return ("LIBUSB_ERROR_INVALID_PARAM"); + case LIBUSB_ERROR_ACCESS: + return ("LIBUSB_ERROR_ACCESS"); + case LIBUSB_ERROR_NO_DEVICE: + return ("LIBUSB_ERROR_NO_DEVICE"); + case LIBUSB_ERROR_NOT_FOUND: + return ("LIBUSB_ERROR_NOT_FOUND"); + case LIBUSB_ERROR_BUSY: + return ("LIBUSB_ERROR_BUSY"); + case LIBUSB_ERROR_TIMEOUT: + return ("LIBUSB_ERROR_TIMEOUT"); + case LIBUSB_ERROR_OVERFLOW: + return ("LIBUSB_ERROR_OVERFLOW"); + case LIBUSB_ERROR_PIPE: + return ("LIBUSB_ERROR_PIPE"); + case LIBUSB_ERROR_INTERRUPTED: + return ("LIBUSB_ERROR_INTERRUPTED"); + case LIBUSB_ERROR_NO_MEM: + return ("LIBUSB_ERROR_NO_MEM"); + case LIBUSB_ERROR_NOT_SUPPORTED: + return ("LIBUSB_ERROR_NOT_SUPPORTED"); + case LIBUSB_ERROR_OTHER: + return ("LIBUSB_ERROR_OTHER"); + default: + return ("LIBUSB_ERROR_UNKNOWN"); + } +} + +int +libusb_has_capability(uint32_t capability) +{ + + switch (capability) { + case LIBUSB_CAP_HAS_CAPABILITY: + case LIBUSB_CAP_HAS_HOTPLUG: + case LIBUSB_CAP_HAS_HID_ACCESS: + case LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER: + return (1); + default: + return (0); + } +} + +void +libusb_log_va_args(struct libusb_context *ctx, enum libusb_log_level level, + const char *fmt, ...) +{ + static const char *log_prefix[5] = { + [LIBUSB_LOG_LEVEL_ERROR] = "LIBUSB_ERROR", + [LIBUSB_LOG_LEVEL_WARNING] = "LIBUSB_WARN", + [LIBUSB_LOG_LEVEL_INFO] = "LIBUSB_INFO", + [LIBUSB_LOG_LEVEL_DEBUG] = "LIBUSB_DEBUG", + }; + + char buffer[LIBUSB_LOG_BUFFER_SIZE]; + char new_fmt[LIBUSB_LOG_BUFFER_SIZE]; + va_list args; + + ctx = GET_CONTEXT(ctx); + + if (ctx->debug < level) + return; + + va_start(args, fmt); + + snprintf(new_fmt, sizeof(new_fmt), "%s: %s\n", log_prefix[level], fmt); + vsnprintf(buffer, sizeof(buffer), new_fmt, args); + fputs(buffer, stdout); + + va_end(args); +} + +/* + * Upstream code actually recognizes the first two characters to identify a + * language. We do so to provide API compatibility with setlocale. + */ +int +libusb_setlocale(const char *locale) +{ + size_t idx; + const char *lang; + + if (locale == NULL || strlen(locale) < 2 || + (locale[2] != '\0' && strchr("-_.", locale[2]) == NULL)) + return (LIBUSB_ERROR_INVALID_PARAM); + + for (idx = 0; idx < nitems(libusb_language_ctx); ++idx) { + lang = libusb_language_ctx[idx].lang_name; + if (tolower(locale[0]) == lang[0] && + tolower(locale[1]) == lang[1]) { + default_language_context = &libusb_language_ctx[idx]; + return (LIBUSB_SUCCESS); + } + } + + return (LIBUSB_ERROR_INVALID_PARAM); +} + +unsigned char * +libusb_dev_mem_alloc(libusb_device_handle *devh) +{ + return (NULL); +} + +int +libusb_dev_mem_free(libusb_device_handle *devh, unsigned char *buffer, + size_t size) +{ + return (LIBUSB_ERROR_NOT_SUPPORTED); +} + +int +libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, + libusb_device_handle **dev_handle) +{ + return (LIBUSB_ERROR_NOT_SUPPORTED); +} diff --git a/lib/libusb/libusb10.h b/lib/libusb/libusb10.h new file mode 100644 index 000000000000..eced364ef857 --- /dev/null +++ b/lib/libusb/libusb10.h @@ -0,0 +1,155 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2009 Sylvestre Gallon. 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 __LIBUSB10_H__ +#define __LIBUSB10_H__ + +#ifndef LIBUSB_GLOBAL_INCLUDE_FILE +#include <sys/cdefs.h> +#include <sys/queue.h> +#include <netlink/netlink.h> +#include <netlink/netlink_generic.h> +#include <netlink/netlink_snl.h> +#include <netlink/netlink_snl_generic.h> +#include <netlink/netlink_sysevent.h> + +#endif + +#define GET_CONTEXT(ctx) (((ctx) == NULL) ? usbi_default_context : (ctx)) +#define UNEXPORTED __attribute__((__visibility__("hidden"))) +#define CTX_LOCK(ctx) pthread_mutex_lock(&(ctx)->ctx_lock) +#define CTX_TRYLOCK(ctx) pthread_mutex_trylock(&(ctx)->ctx_lock) +#define CTX_UNLOCK(ctx) pthread_mutex_unlock(&(ctx)->ctx_lock) +#define HOTPLUG_LOCK(ctx) pthread_mutex_lock(&(ctx)->hotplug_lock) +#define HOTPLUG_UNLOCK(ctx) pthread_mutex_unlock(&(ctx)->hotplug_lock) + +void libusb_log_va_args(struct libusb_context *ctx, enum libusb_log_level level, + const char *fmt, ...) __printflike(3, 4); + +#define DPRINTF(ctx, dbg, format, ...) \ + libusb_log_va_args(ctx, dbg, format, ##__VA_ARGS__) + +/* internal structures */ + +struct libusb_super_pollfd { + TAILQ_ENTRY(libusb_super_pollfd) entry; + struct libusb20_device *pdev; + struct libusb_pollfd pollfd; +}; + +struct libusb_super_transfer { + TAILQ_ENTRY(libusb_super_transfer) entry; + uint8_t *curr_data; + uint32_t rem_len; + uint32_t last_len; + uint32_t stream_id; + uint8_t state; +#define LIBUSB_SUPER_XFER_ST_NONE 0 +#define LIBUSB_SUPER_XFER_ST_PEND 1 +}; + +struct libusb_hotplug_callback_handle_struct { + TAILQ_ENTRY(libusb_hotplug_callback_handle_struct) entry; + int events; + int vendor; + int product; + int devclass; + libusb_hotplug_callback_fn fn; + void *user_data; +}; + +TAILQ_HEAD(libusb_device_head, libusb_device); + +typedef enum { + usb_event_none, + usb_event_scan, + usb_event_devd, + usb_event_netlink +} usb_event_mode_t; + +struct libusb_context { + int debug; + int debug_fixed; + int event; + int tr_done_ref; + int tr_done_gen; + usb_event_mode_t usb_event_mode; + int devd_pipe; + struct snl_state ss; + + pthread_mutex_t ctx_lock; + pthread_mutex_t hotplug_lock; + pthread_cond_t ctx_cond; + pthread_t hotplug_handler; + pthread_t ctx_handler; +#define NO_THREAD ((pthread_t)-1) + + TAILQ_HEAD(, libusb_super_pollfd) pollfds; + TAILQ_HEAD(, libusb_super_transfer) tr_done; + TAILQ_HEAD(, libusb_hotplug_callback_handle_struct) hotplug_cbh; + struct libusb_device_head hotplug_devs; + + struct libusb_super_pollfd ctx_poll; + + libusb_pollfd_added_cb fd_added_cb; + libusb_pollfd_removed_cb fd_removed_cb; + void *fd_cb_user_data; +}; + +struct libusb_device { + int refcnt; + + int device_is_gone; + + uint32_t claimed_interfaces; + + struct libusb_super_pollfd dev_poll; + + struct libusb_context *ctx; + struct libusb_device *parent_dev; + + TAILQ_ENTRY(libusb_device) hotplug_entry; + + TAILQ_HEAD(, libusb_super_transfer) tr_head; + + struct libusb20_device *os_priv; +}; + +struct libusb_language_context { + const char *lang_name; + /* All error Plus 1 UNKNOWN */ + const char *err_strs[LIBUSB_ERROR_COUNT + 1]; +}; + +extern struct libusb_context *usbi_default_context; + +void libusb10_add_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd, struct libusb20_device *pdev, int fd, short events); +void libusb10_remove_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd); +void libusb10_cancel_all_transfer(libusb_device *dev); +void libusb10_cancel_all_transfer_locked(struct libusb20_device *pdev, struct libusb_device *dev); + +#endif /* __LIBUSB10_H__ */ diff --git a/lib/libusb/libusb10_desc.c b/lib/libusb/libusb10_desc.c new file mode 100644 index 000000000000..7da5c84f4ad2 --- /dev/null +++ b/lib/libusb/libusb10_desc.c @@ -0,0 +1,765 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2009 Sylvestre Gallon. 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 LIBUSB_GLOBAL_INCLUDE_FILE +#include LIBUSB_GLOBAL_INCLUDE_FILE +#else +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/queue.h> +#endif + +#define libusb_device_handle libusb20_device + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" +#include "libusb.h" +#include "libusb10.h" + +#define N_ALIGN(n) (-((-(n)) & (-8UL))) + +/* USB descriptors */ + +int +libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc) +{ + struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; + struct libusb20_device *pdev; + + if ((dev == NULL) || (desc == NULL)) + return (LIBUSB_ERROR_INVALID_PARAM); + + pdev = dev->os_priv; + pdesc = libusb20_dev_get_device_desc(pdev); + + desc->bLength = pdesc->bLength; + desc->bDescriptorType = pdesc->bDescriptorType; + desc->bcdUSB = pdesc->bcdUSB; + desc->bDeviceClass = pdesc->bDeviceClass; + desc->bDeviceSubClass = pdesc->bDeviceSubClass; + desc->bDeviceProtocol = pdesc->bDeviceProtocol; + desc->bMaxPacketSize0 = pdesc->bMaxPacketSize0; + desc->idVendor = pdesc->idVendor; + desc->idProduct = pdesc->idProduct; + desc->bcdDevice = pdesc->bcdDevice; + desc->iManufacturer = pdesc->iManufacturer; + desc->iProduct = pdesc->iProduct; + desc->iSerialNumber = pdesc->iSerialNumber; + desc->bNumConfigurations = pdesc->bNumConfigurations; + + return (0); +} + +int +libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config) +{ + struct libusb20_device *pdev; + uint8_t config_index; + + pdev = dev->os_priv; + config_index = libusb20_dev_get_config_index(pdev); + + return (libusb_get_config_descriptor(dev, config_index, config)); +} + +int +libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, + struct libusb_config_descriptor **config) +{ + struct libusb20_device *pdev; + struct libusb20_config *pconf; + struct libusb20_interface *pinf; + struct libusb20_endpoint *pend; + struct libusb_config_descriptor *pconfd; + struct libusb_interface_descriptor *ifd; + struct libusb_endpoint_descriptor *endd; + uint8_t *pextra; + uint16_t nextra; + uint8_t nif; + uint8_t nep; + uint8_t nalt; + uint8_t i; + uint8_t j; + uint8_t k; + + if (dev == NULL || config == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + *config = NULL; + + pdev = dev->os_priv; + pconf = libusb20_dev_alloc_config(pdev, config_index); + + if (pconf == NULL) + return (LIBUSB_ERROR_NOT_FOUND); + + nalt = nif = pconf->num_interface; + nep = 0; + nextra = N_ALIGN(pconf->extra.len); + + for (i = 0; i < nif; i++) { + + pinf = pconf->interface + i; + nextra += N_ALIGN(pinf->extra.len); + nep += pinf->num_endpoints; + k = pinf->num_endpoints; + pend = pinf->endpoints; + while (k--) { + nextra += N_ALIGN(pend->extra.len); + pend++; + } + + j = pinf->num_altsetting; + nalt += pinf->num_altsetting; + pinf = pinf->altsetting; + while (j--) { + nextra += N_ALIGN(pinf->extra.len); + nep += pinf->num_endpoints; + k = pinf->num_endpoints; + pend = pinf->endpoints; + while (k--) { + nextra += N_ALIGN(pend->extra.len); + pend++; + } + pinf++; + } + } + + nextra = nextra + + (1 * sizeof(libusb_config_descriptor)) + + (nif * sizeof(libusb_interface)) + + (nalt * sizeof(libusb_interface_descriptor)) + + (nep * sizeof(libusb_endpoint_descriptor)); + + nextra = N_ALIGN(nextra); + + pconfd = malloc(nextra); + + if (pconfd == NULL) { + free(pconf); + return (LIBUSB_ERROR_NO_MEM); + } + /* make sure memory is initialised */ + memset(pconfd, 0, nextra); + + pconfd->interface = (libusb_interface *) (pconfd + 1); + + ifd = (libusb_interface_descriptor *) (pconfd->interface + nif); + endd = (libusb_endpoint_descriptor *) (ifd + nalt); + pextra = (uint8_t *)(endd + nep); + + /* fill in config descriptor */ + + pconfd->bLength = pconf->desc.bLength; + pconfd->bDescriptorType = pconf->desc.bDescriptorType; + pconfd->wTotalLength = pconf->desc.wTotalLength; + pconfd->bNumInterfaces = pconf->desc.bNumInterfaces; + pconfd->bConfigurationValue = pconf->desc.bConfigurationValue; + pconfd->iConfiguration = pconf->desc.iConfiguration; + pconfd->bmAttributes = pconf->desc.bmAttributes; + pconfd->MaxPower = pconf->desc.bMaxPower; + + if (pconf->extra.len != 0) { + pconfd->extra_length = pconf->extra.len; + pconfd->extra = pextra; + memcpy(pextra, pconf->extra.ptr, pconfd->extra_length); + pextra += N_ALIGN(pconfd->extra_length); + } + /* setup all interface and endpoint pointers */ + + for (i = 0; i < nif; i++) { + + pconfd->interface[i].altsetting = ifd; + ifd->endpoint = endd; + endd += pconf->interface[i].num_endpoints; + ifd++; + + for (j = 0; j < pconf->interface[i].num_altsetting; j++) { + ifd->endpoint = endd; + endd += pconf->interface[i].altsetting[j].num_endpoints; + ifd++; + } + } + + /* fill in all interface and endpoint data */ + + for (i = 0; i < nif; i++) { + pinf = &pconf->interface[i]; + pconfd->interface[i].num_altsetting = pinf->num_altsetting + 1; + for (j = 0; j < pconfd->interface[i].num_altsetting; j++) { + if (j != 0) + pinf = &pconf->interface[i].altsetting[j - 1]; + ifd = &pconfd->interface[i].altsetting[j]; + ifd->bLength = pinf->desc.bLength; + ifd->bDescriptorType = pinf->desc.bDescriptorType; + ifd->bInterfaceNumber = pinf->desc.bInterfaceNumber; + ifd->bAlternateSetting = pinf->desc.bAlternateSetting; + ifd->bNumEndpoints = pinf->desc.bNumEndpoints; + ifd->bInterfaceClass = pinf->desc.bInterfaceClass; + ifd->bInterfaceSubClass = pinf->desc.bInterfaceSubClass; + ifd->bInterfaceProtocol = pinf->desc.bInterfaceProtocol; + ifd->iInterface = pinf->desc.iInterface; + if (pinf->extra.len != 0) { + ifd->extra_length = pinf->extra.len; + ifd->extra = pextra; + memcpy(pextra, pinf->extra.ptr, pinf->extra.len); + pextra += N_ALIGN(pinf->extra.len); + } + for (k = 0; k < pinf->num_endpoints; k++) { + pend = &pinf->endpoints[k]; + endd = &ifd->endpoint[k]; + endd->bLength = pend->desc.bLength; + endd->bDescriptorType = pend->desc.bDescriptorType; + endd->bEndpointAddress = pend->desc.bEndpointAddress; + endd->bmAttributes = pend->desc.bmAttributes; + endd->wMaxPacketSize = pend->desc.wMaxPacketSize; + endd->bInterval = pend->desc.bInterval; + endd->bRefresh = pend->desc.bRefresh; + endd->bSynchAddress = pend->desc.bSynchAddress; + if (pend->extra.len != 0) { + endd->extra_length = pend->extra.len; + endd->extra = pextra; + memcpy(pextra, pend->extra.ptr, pend->extra.len); + pextra += N_ALIGN(pend->extra.len); + } + } + } + } + + free(pconf); + + *config = pconfd; + + return (0); /* success */ +} + +int +libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config) +{ + struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; + struct libusb20_device *pdev; + int i; + int err; + + if (dev == NULL || config == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + pdev = dev->os_priv; + pdesc = libusb20_dev_get_device_desc(pdev); + + for (i = 0; i < pdesc->bNumConfigurations; i++) { + err = libusb_get_config_descriptor(dev, i, config); + if (err) + return (err); + + if ((*config)->bConfigurationValue == bConfigurationValue) + return (0); /* success */ + + libusb_free_config_descriptor(*config); + } + + *config = NULL; + + return (LIBUSB_ERROR_NOT_FOUND); +} + +void +libusb_free_config_descriptor(struct libusb_config_descriptor *config) +{ + free(config); +} + +int +libusb_get_string_descriptor(libusb_device_handle *pdev, + uint8_t desc_index, uint16_t langid, unsigned char *data, + int length) +{ + if (pdev == NULL || data == NULL || length < 1) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (length > 65535) + length = 65535; + + /* put some default data into the destination buffer */ + data[0] = 0; + + return (libusb_control_transfer(pdev, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc_index, + langid, data, length, 1000)); +} + +int +libusb_get_string_descriptor_ascii(libusb_device_handle *pdev, + uint8_t desc_index, unsigned char *data, int length) +{ + if (pdev == NULL || data == NULL || length < 1) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (length > 65535) + length = 65535; + + /* put some default data into the destination buffer */ + data[0] = 0; + + if (libusb20_dev_req_string_simple_sync(pdev, desc_index, + data, length) == 0) + return (strlen((char *)data)); + + return (LIBUSB_ERROR_OTHER); +} + +int +libusb_get_descriptor(libusb_device_handle * devh, uint8_t desc_type, + uint8_t desc_index, uint8_t *data, int length) +{ + if (devh == NULL || data == NULL || length < 1) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (length > 65535) + length = 65535; + + return (libusb_control_transfer(devh, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data, + length, 1000)); +} + +int +libusb_parse_ss_endpoint_comp(const void *buf, int len, + struct libusb_ss_endpoint_companion_descriptor **ep_comp) +{ + if (buf == NULL || ep_comp == NULL || len < 1) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (len > 65535) + len = 65535; + + *ep_comp = NULL; + + while (len != 0) { + uint8_t dlen; + uint8_t dtype; + + dlen = ((const uint8_t *)buf)[0]; + dtype = ((const uint8_t *)buf)[1]; + + if (dlen < 2 || dlen > len) + break; + + if (dlen >= LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE && + dtype == LIBUSB_DT_SS_ENDPOINT_COMPANION) { + struct libusb_ss_endpoint_companion_descriptor *ptr; + + ptr = malloc(sizeof(*ptr)); + if (ptr == NULL) + return (LIBUSB_ERROR_NO_MEM); + + ptr->bLength = LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE; + ptr->bDescriptorType = dtype; + ptr->bMaxBurst = ((const uint8_t *)buf)[2]; + ptr->bmAttributes = ((const uint8_t *)buf)[3]; + ptr->wBytesPerInterval = ((const uint8_t *)buf)[4] | + (((const uint8_t *)buf)[5] << 8); + + *ep_comp = ptr; + + return (0); /* success */ + } + + buf = ((const uint8_t *)buf) + dlen; + len -= dlen; + } + return (LIBUSB_ERROR_IO); +} + +void +libusb_free_ss_endpoint_comp(struct libusb_ss_endpoint_companion_descriptor *ep_comp) +{ + if (ep_comp == NULL) + return; + + free(ep_comp); +} + +int +libusb_get_ss_endpoint_companion_descriptor(struct libusb_context *ctx, + const struct libusb_endpoint_descriptor *endpoint, + struct libusb_ss_endpoint_companion_descriptor **ep_comp) +{ + if (endpoint == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + return (libusb_parse_ss_endpoint_comp(endpoint->extra, endpoint->extra_length, ep_comp)); +} + +void +libusb_free_ss_endpoint_companion_descriptor(struct libusb_ss_endpoint_companion_descriptor *ep_comp) +{ + + libusb_free_ss_endpoint_comp(ep_comp); +} + +int +libusb_parse_bos_descriptor(const void *buf, int len, + struct libusb_bos_descriptor **bos) +{ + struct libusb_bos_descriptor *ptr; + struct libusb_usb_2_0_device_capability_descriptor *dcap_20 = NULL; + struct libusb_ss_usb_device_capability_descriptor *ss_cap = NULL; + uint8_t index = 0; + + if (buf == NULL || bos == NULL || len < 1) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (len > 65535) + len = 65535; + + *bos = ptr = NULL; + + while (len != 0) { + uint8_t dlen; + uint8_t dtype; + + dlen = ((const uint8_t *)buf)[0]; + dtype = ((const uint8_t *)buf)[1]; + + if (dlen < 2 || dlen > len) + break; + + if (dlen >= LIBUSB_DT_BOS_SIZE && + dtype == LIBUSB_DT_BOS && + ptr == NULL) { + + ptr = malloc(sizeof(*ptr) + sizeof(*dcap_20) + + sizeof(*ss_cap)); + + if (ptr == NULL) + return (LIBUSB_ERROR_NO_MEM); + + *bos = ptr; + + ptr->bLength = LIBUSB_DT_BOS_SIZE; + ptr->bDescriptorType = dtype; + ptr->wTotalLength = ((const uint8_t *)buf)[2] | + (((const uint8_t *)buf)[3] << 8); + ptr->bNumDeviceCaps = ((const uint8_t *)buf)[4]; + ptr->usb_2_0_ext_cap = NULL; + ptr->ss_usb_cap = NULL; + ptr->dev_capability = calloc(ptr->bNumDeviceCaps, + sizeof(void *)); + if (ptr->dev_capability == NULL) { + free(ptr); + return (LIBUSB_ERROR_NO_MEM); + } + + dcap_20 = (void *)(ptr + 1); + ss_cap = (void *)(dcap_20 + 1); + } + if (dlen >= 3 && + ptr != NULL && + dtype == LIBUSB_DT_DEVICE_CAPABILITY) { + if (index != ptr->bNumDeviceCaps) { + ptr->dev_capability[index] = malloc(dlen); + if (ptr->dev_capability[index] == NULL) { + libusb_free_bos_descriptor(ptr); + return LIBUSB_ERROR_NO_MEM; + } + memcpy(ptr->dev_capability[index], buf, dlen); + index++; + } + switch (((const uint8_t *)buf)[2]) { + case LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY: + if (ptr->usb_2_0_ext_cap != NULL || dcap_20 == NULL) + break; + if (dlen < LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE) + break; + + ptr->usb_2_0_ext_cap = dcap_20; + + dcap_20->bLength = LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE; + dcap_20->bDescriptorType = dtype; + dcap_20->bDevCapabilityType = ((const uint8_t *)buf)[2]; + dcap_20->bmAttributes = ((const uint8_t *)buf)[3] | + (((const uint8_t *)buf)[4] << 8) | + (((const uint8_t *)buf)[5] << 16) | + (((const uint8_t *)buf)[6] << 24); + break; + + case LIBUSB_SS_USB_DEVICE_CAPABILITY: + if (ptr->ss_usb_cap != NULL || ss_cap == NULL) + break; + if (dlen < LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE) + break; + + ptr->ss_usb_cap = ss_cap; + + ss_cap->bLength = LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE; + ss_cap->bDescriptorType = dtype; + ss_cap->bDevCapabilityType = ((const uint8_t *)buf)[2]; + ss_cap->bmAttributes = ((const uint8_t *)buf)[3]; + ss_cap->wSpeedSupported = ((const uint8_t *)buf)[4] | + (((const uint8_t *)buf)[5] << 8); + ss_cap->bFunctionalitySupport = ((const uint8_t *)buf)[6]; + ss_cap->bU1DevExitLat = ((const uint8_t *)buf)[7]; + ss_cap->wU2DevExitLat = ((const uint8_t *)buf)[8] | + (((const uint8_t *)buf)[9] << 8); + break; + + default: + break; + } + } + + buf = ((const uint8_t *)buf) + dlen; + len -= dlen; + } + + if (ptr != NULL) { + ptr->bNumDeviceCaps = index; + return (0); /* success */ + } + + return (LIBUSB_ERROR_IO); +} + +void +libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos) +{ + uint8_t i; + + if (bos == NULL) + return; + + for (i = 0; i != bos->bNumDeviceCaps; i++) + free(bos->dev_capability[i]); + free(bos->dev_capability); + free(bos); +} + +int +libusb_get_bos_descriptor(libusb_device_handle *handle, + struct libusb_bos_descriptor **bos) +{ + uint8_t bos_header[LIBUSB_DT_BOS_SIZE] = {0}; + uint16_t wTotalLength; + uint8_t *bos_data; + int err; + + err = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0, + bos_header, sizeof(bos_header)); + if (err < 0) + return (err); + + wTotalLength = bos_header[2] | (bos_header[3] << 8); + if (wTotalLength < LIBUSB_DT_BOS_SIZE) + return (LIBUSB_ERROR_INVALID_PARAM); + + bos_data = calloc(wTotalLength, 1); + if (bos_data == NULL) + return (LIBUSB_ERROR_NO_MEM); + + err = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0, + bos_data, wTotalLength); + if (err < 0) + goto done; + + /* avoid descriptor length mismatches */ + bos_data[2] = (wTotalLength & 0xFF); + bos_data[3] = (wTotalLength >> 8); + + err = libusb_parse_bos_descriptor(bos_data, wTotalLength, bos); +done: + free(bos_data); + return (err); +} + +int +libusb_get_usb_2_0_extension_descriptor(struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension) +{ + struct libusb_usb_2_0_extension_descriptor *desc; + + if (dev_cap == NULL || usb_2_0_extension == NULL || + dev_cap->bDevCapabilityType != LIBUSB_BT_USB_2_0_EXTENSION) + return (LIBUSB_ERROR_INVALID_PARAM); + if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) + return (LIBUSB_ERROR_IO); + + desc = malloc(sizeof(*desc)); + if (desc == NULL) + return (LIBUSB_ERROR_NO_MEM); + + desc->bLength = LIBUSB_BT_USB_2_0_EXTENSION_SIZE; + desc->bDescriptorType = dev_cap->bDescriptorType; + desc->bDevCapabilityType = dev_cap->bDevCapabilityType; + desc->bmAttributes = + (dev_cap->dev_capability_data[0]) | + (dev_cap->dev_capability_data[1] << 8) | + (dev_cap->dev_capability_data[2] << 16) | + (dev_cap->dev_capability_data[3] << 24); + + *usb_2_0_extension = desc; + return (0); +} + +void +libusb_free_usb_2_0_extension_descriptor( + struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension) +{ + + free(usb_2_0_extension); +} + +int +libusb_get_ss_usb_device_capability_descriptor(struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_capability) +{ + struct libusb_ss_usb_device_capability_descriptor *desc; + + if (dev_cap == NULL || ss_usb_device_capability == NULL || + dev_cap->bDevCapabilityType != LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) + return (LIBUSB_ERROR_INVALID_PARAM); + if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) + return (LIBUSB_ERROR_IO); + + desc = malloc(sizeof(*desc)); + if (desc == NULL) + return (LIBUSB_ERROR_NO_MEM); + + desc->bLength = LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE; + desc->bDescriptorType = dev_cap->bDescriptorType; + desc->bDevCapabilityType = dev_cap->bDevCapabilityType; + desc->bmAttributes = dev_cap->dev_capability_data[0]; + desc->wSpeedSupported = dev_cap->dev_capability_data[1] | + (dev_cap->dev_capability_data[2] << 8); + desc->bFunctionalitySupport = dev_cap->dev_capability_data[3]; + desc->bU1DevExitLat = dev_cap->dev_capability_data[4]; + desc->wU2DevExitLat = dev_cap->dev_capability_data[5] | + (dev_cap->dev_capability_data[6] << 8); + + *ss_usb_device_capability = desc; + return (0); +} + +void +libusb_free_ss_usb_device_capability_descriptor( + struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_capability) +{ + + free(ss_usb_device_capability); +} + +int +libusb_get_container_id_descriptor(struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_container_id_descriptor **container_id) +{ + struct libusb_container_id_descriptor *desc; + + if (dev_cap == NULL || container_id == NULL || + dev_cap->bDevCapabilityType != LIBUSB_BT_CONTAINER_ID) + return (LIBUSB_ERROR_INVALID_PARAM); + if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) + return (LIBUSB_ERROR_IO); + + desc = malloc(sizeof(*desc)); + if (desc == NULL) + return (LIBUSB_ERROR_NO_MEM); + + desc->bLength = LIBUSB_BT_CONTAINER_ID_SIZE; + desc->bDescriptorType = dev_cap->bDescriptorType; + desc->bDevCapabilityType = dev_cap->bDevCapabilityType; + desc->bReserved = dev_cap->dev_capability_data[0]; + memcpy(desc->ContainerID, dev_cap->dev_capability_data + 1, + sizeof(desc->ContainerID)); + + *container_id = desc; + return (0); +} + +void +libusb_free_container_id_descriptor( + struct libusb_container_id_descriptor *container_id) +{ + free(container_id); +} + +int +libusb_get_platform_descriptor(libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *bos_cap, + struct libusb_platform_descriptor **pd) +{ + struct libusb_platform_descriptor *desc; + uint8_t *cap_data; + + if (bos_cap == NULL || + bos_cap->bDescriptorType != LIBUSB_BT_PLATFORM_DESCRIPTOR || + pd == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (bos_cap->bLength < LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE) + return (LIBUSB_ERROR_IO); + + cap_data = bos_cap->dev_capability_data; + desc = calloc(1, bos_cap->bLength); + if (desc == NULL) + return (LIBUSB_ERROR_NO_MEM); + + desc->bLength = bos_cap->bLength; + desc->bDescriptorType = LIBUSB_BT_PLATFORM_DESCRIPTOR; + desc->bDevCapabilityType = bos_cap->bDevCapabilityType; + desc->bReserved = cap_data[0]; + memcpy(desc->PlatformCapabilityUUID, cap_data + 1, + sizeof(desc->PlatformCapabilityUUID)); + + /* + * UUID (16 bytes) + bReserved + */ + cap_data += sizeof(desc->PlatformCapabilityUUID) + 1; + /* + * UUID (16 bytes) + bReserved + bLength + bDescriptortype + + * bDevCapabilitytype + */ + memcpy(desc->CapabilityData, cap_data, + bos_cap->bLength - (sizeof(desc->PlatformCapabilityUUID) + 4)); + *pd = desc; + + return (LIBUSB_SUCCESS); +} + +void +libusb_free_platform_descriptor( + struct libusb_platform_descriptor *platform_descriptor) +{ + free(platform_descriptor); +} diff --git a/lib/libusb/libusb10_hotplug.c b/lib/libusb/libusb10_hotplug.c new file mode 100644 index 000000000000..9c46d4926bfa --- /dev/null +++ b/lib/libusb/libusb10_hotplug.c @@ -0,0 +1,434 @@ +/*- + * Copyright (c) 2016-2019 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 <netlink/netlink_snl_generic.h> +#ifdef LIBUSB_GLOBAL_INCLUDE_FILE +#include LIBUSB_GLOBAL_INCLUDE_FILE +#else +#include <assert.h> +#include <errno.h> +#include <poll.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <sys/fcntl.h> +#include <sys/ioctl.h> +#include <sys/queue.h> +#include <sys/endian.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/module.h> +#include <sys/linker.h> +#endif + +#define libusb_device_handle libusb20_device + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" +#include "libusb.h" +#include "libusb10.h" + +#define DEVDPIPE "/var/run/devd.seqpacket.pipe" +#define DEVCTL_MAXBUF 1024 + +typedef enum { + broken_event, + invalid_event, + valid_event, +} event_t; + +static bool +netlink_init(libusb_context *ctx) +{ + uint32_t group; + + if (modfind("nlsysevent") < 0) + kldload("nlsysevent"); + if (modfind("nlsysevent") < 0) + return (false); + if (!snl_init(&ctx->ss, NETLINK_GENERIC) || (group = + snl_get_genl_mcast_group(&ctx->ss, "nlsysevent", "USB", NULL)) == 0) + return (false); + + if (setsockopt(ctx->ss.fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, + sizeof(group)) == -1) + return (false); + + ctx->usb_event_mode = usb_event_netlink; + return (true); +} + +static bool +devd_init(libusb_context *ctx) +{ + struct sockaddr_un devd_addr; + + bzero(&devd_addr, sizeof(devd_addr)); + if ((ctx->devd_pipe = socket(PF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK, 0)) < 0) + return (false); + + devd_addr.sun_family = PF_LOCAL; + strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path)); + if (connect(ctx->devd_pipe, (struct sockaddr *)&devd_addr, + sizeof(devd_addr)) == -1) { + close(ctx->devd_pipe); + ctx->devd_pipe = -1; + return (false); + } + + ctx->usb_event_mode = usb_event_devd; + return (true); +} + +struct nlevent { + const char *name; + const char *subsystem; + const char *type; + const char *data; +}; + +#define _OUT(_field) offsetof(struct nlevent, _field) +static struct snl_attr_parser ap_nlevent_get[] = { + { .type = NLSE_ATTR_SYSTEM, .off = _OUT(name), .cb = snl_attr_get_string }, + { .type = NLSE_ATTR_SUBSYSTEM, .off = _OUT(subsystem), .cb = snl_attr_get_string }, + { .type = NLSE_ATTR_TYPE, .off = _OUT(type), .cb = snl_attr_get_string }, + { .type = NLSE_ATTR_DATA, .off = _OUT(data), .cb = snl_attr_get_string }, +}; +#undef _OUT + +SNL_DECLARE_GENL_PARSER(nlevent_get_parser, ap_nlevent_get); + +static event_t +verify_event_validity(libusb_context *ctx) +{ + if (ctx->usb_event_mode == usb_event_netlink) { + struct nlmsghdr *hdr; + struct nlevent ne; + + hdr = snl_read_message(&ctx->ss); + if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR) { + memset(&ne, 0, sizeof(ne)); + if (!snl_parse_nlmsg(&ctx->ss, hdr, &nlevent_get_parser, &ne)) + return (broken_event); + if (strcmp(ne.subsystem, "DEVICE") == 0) + return (valid_event); + return (invalid_event); + } + if (errno == EBADF) + return (broken_event); + return (invalid_event); + } else if (ctx->usb_event_mode == usb_event_devd) { + char buf[DEVCTL_MAXBUF]; + ssize_t len; + + len = read(ctx->devd_pipe, buf, sizeof(buf)); + if (len == 0 || (len < 0 && errno != EWOULDBLOCK)) + return (broken_event); + if (len > 0 && strstr(buf, "system=USB") != NULL && + strstr(buf, "subsystem=DEVICE") != NULL) + return (valid_event); + return (invalid_event); + } + return (broken_event); +} + +static int +libusb_hotplug_equal(libusb_device *_adev, libusb_device *_bdev) +{ + struct libusb20_device *adev = _adev->os_priv; + struct libusb20_device *bdev = _bdev->os_priv; + + if (adev->bus_number != bdev->bus_number) + return (0); + if (adev->device_address != bdev->device_address) + return (0); + if (memcmp(&adev->ddesc, &bdev->ddesc, sizeof(adev->ddesc))) + return (0); + if (memcmp(&adev->session_data, &bdev->session_data, sizeof(adev->session_data))) + return (0); + return (1); +} + +static int +libusb_hotplug_filter(libusb_context *ctx, libusb_hotplug_callback_handle pcbh, + libusb_device *dev, libusb_hotplug_event event) +{ + if (!(pcbh->events & event)) + return (0); + if (pcbh->vendor != LIBUSB_HOTPLUG_MATCH_ANY && + pcbh->vendor != libusb20_dev_get_device_desc(dev->os_priv)->idVendor) + return (0); + if (pcbh->product != LIBUSB_HOTPLUG_MATCH_ANY && + pcbh->product != libusb20_dev_get_device_desc(dev->os_priv)->idProduct) + return (0); + if (pcbh->devclass != LIBUSB_HOTPLUG_MATCH_ANY && + pcbh->devclass != libusb20_dev_get_device_desc(dev->os_priv)->bDeviceClass) + return (0); + return (pcbh->fn(ctx, dev, event, pcbh->user_data)); +} + +static int +libusb_hotplug_enumerate(libusb_context *ctx, struct libusb_device_head *phead) +{ + libusb_device **ppdev; + ssize_t count; + ssize_t x; + + count = libusb_get_device_list(ctx, &ppdev); + if (count < 0) + return (-1); + + for (x = 0; x != count; x++) + TAILQ_INSERT_TAIL(phead, ppdev[x], hotplug_entry); + + libusb_free_device_list(ppdev, 0); + return (0); +} + +static void * +libusb_hotplug_scan(void *arg) +{ + struct pollfd pfd; + struct libusb_device_head hotplug_devs; + libusb_hotplug_callback_handle acbh; + libusb_hotplug_callback_handle bcbh; + libusb_context *ctx = arg; + libusb_device *temp; + libusb_device *adev; + libusb_device *bdev; + int timeout = INFTIM; + int nfds; + + memset(&pfd, 0, sizeof(pfd)); + if (ctx->usb_event_mode == usb_event_devd) { + pfd.fd = ctx->devd_pipe; + pfd.events = POLLIN | POLLERR; + nfds = 1; + } else if (ctx->usb_event_mode == usb_event_netlink) { + pfd.fd = ctx->ss.fd; + pfd.events = POLLIN | POLLERR; + nfds = 1; + } else { + nfds = 0; + timeout = 4000; + } + for (;;) { + pfd.revents = 0; + if (poll(&pfd, nfds, timeout) > 0) { + switch (verify_event_validity(ctx)) { + case invalid_event: + continue; + case valid_event: + break; + case broken_event: + /* There are 2 cases for broken events: + * - devd and netlink sockets are not available + * anymore (devd restarted, nlsysevent unloaded) + * - libusb_exit has been called as it sets NO_THREAD + * this will result in exiting this loop and this thread + * immediately + */ + nfds = 0; + if (ctx->usb_event_mode == usb_event_devd) { + if (ctx->devd_pipe != -1) + close(ctx->devd_pipe); + } else if (ctx->usb_event_mode == usb_event_netlink) { + if (ctx->ss.fd != -1) + close(ctx->ss.fd); + } + ctx->usb_event_mode = usb_event_scan; + timeout = 4000; + break; + } + } + + HOTPLUG_LOCK(ctx); + if (ctx->hotplug_handler == NO_THREAD) { + while ((adev = TAILQ_FIRST(&ctx->hotplug_devs)) != NULL) { + TAILQ_REMOVE(&ctx->hotplug_devs, adev, hotplug_entry); + libusb_unref_device(adev); + } + if (ctx->usb_event_mode == usb_event_devd) + close(ctx->devd_pipe); + else if (ctx->usb_event_mode == usb_event_netlink) + close(ctx->ss.fd); + HOTPLUG_UNLOCK(ctx); + break; + } + + TAILQ_INIT(&hotplug_devs); + + if (libusb_hotplug_enumerate(ctx, &hotplug_devs) < 0) { + HOTPLUG_UNLOCK(ctx); + continue; + } + + /* figure out which devices are gone */ + TAILQ_FOREACH_SAFE(adev, &ctx->hotplug_devs, hotplug_entry, temp) { + TAILQ_FOREACH(bdev, &hotplug_devs, hotplug_entry) { + if (libusb_hotplug_equal(adev, bdev)) + break; + } + if (bdev == NULL) { + TAILQ_REMOVE(&ctx->hotplug_devs, adev, hotplug_entry); + TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { + if (libusb_hotplug_filter(ctx, acbh, adev, + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) == 0) + continue; + TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); + free(acbh); + } + libusb_unref_device(adev); + } + } + + /* figure out which devices are new */ + TAILQ_FOREACH_SAFE(adev, &hotplug_devs, hotplug_entry, temp) { + TAILQ_FOREACH(bdev, &ctx->hotplug_devs, hotplug_entry) { + if (libusb_hotplug_equal(adev, bdev)) + break; + } + if (bdev == NULL) { + TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); + TAILQ_INSERT_TAIL(&ctx->hotplug_devs, adev, hotplug_entry); + TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { + if (libusb_hotplug_filter(ctx, acbh, adev, + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) + continue; + TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); + free(acbh); + } + } + } + HOTPLUG_UNLOCK(ctx); + + /* unref remaining devices */ + while ((adev = TAILQ_FIRST(&hotplug_devs)) != NULL) { + TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); + libusb_unref_device(adev); + } + } + return (NULL); +} + +int libusb_hotplug_register_callback(libusb_context *ctx, + libusb_hotplug_event events, libusb_hotplug_flag flags, + int vendor_id, int product_id, int dev_class, + libusb_hotplug_callback_fn cb_fn, void *user_data, + libusb_hotplug_callback_handle *phandle) +{ + libusb_hotplug_callback_handle handle; + struct libusb_device *adev; + + ctx = GET_CONTEXT(ctx); + + if (ctx->usb_event_mode == usb_event_none) { + HOTPLUG_LOCK(ctx); + if (!netlink_init(ctx) && !devd_init(ctx)) + ctx->usb_event_mode = usb_event_scan; + HOTPLUG_UNLOCK(ctx); + } + + if (ctx == NULL || cb_fn == NULL || events == 0 || + vendor_id < -1 || vendor_id > 0xffff || + product_id < -1 || product_id > 0xffff || + dev_class < -1 || dev_class > 0xff) + return (LIBUSB_ERROR_INVALID_PARAM); + + handle = malloc(sizeof(*handle)); + if (handle == NULL) + return (LIBUSB_ERROR_NO_MEM); + + HOTPLUG_LOCK(ctx); + if (ctx->hotplug_handler == NO_THREAD) { + libusb_hotplug_enumerate(ctx, &ctx->hotplug_devs); + + if (pthread_create(&ctx->hotplug_handler, NULL, + &libusb_hotplug_scan, ctx) != 0) + ctx->hotplug_handler = NO_THREAD; + } + handle->events = events; + handle->vendor = vendor_id; + handle->product = product_id; + handle->devclass = dev_class; + handle->fn = cb_fn; + handle->user_data = user_data; + + if (flags & LIBUSB_HOTPLUG_ENUMERATE) { + TAILQ_FOREACH(adev, &ctx->hotplug_devs, hotplug_entry) { + if (libusb_hotplug_filter(ctx, handle, adev, + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) + continue; + free(handle); + handle = NULL; + break; + } + } + if (handle != NULL) + TAILQ_INSERT_TAIL(&ctx->hotplug_cbh, handle, entry); + HOTPLUG_UNLOCK(ctx); + + if (phandle != NULL) + *phandle = handle; + return (LIBUSB_SUCCESS); +} + +void libusb_hotplug_deregister_callback(libusb_context *ctx, + libusb_hotplug_callback_handle handle) +{ + ctx = GET_CONTEXT(ctx); + + if (ctx == NULL || handle == NULL) + return; + + HOTPLUG_LOCK(ctx); + TAILQ_REMOVE(&ctx->hotplug_cbh, handle, entry); + libusb_interrupt_event_handler(ctx); + HOTPLUG_UNLOCK(ctx); + + free(handle); +} + +void * +libusb_hotplug_get_user_data(struct libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle) +{ + libusb_hotplug_callback_handle handle; + + ctx = GET_CONTEXT(ctx); + + HOTPLUG_LOCK(ctx); + TAILQ_FOREACH(handle, &ctx->hotplug_cbh, entry) { + if (handle == callback_handle) + break; + } + HOTPLUG_UNLOCK(ctx); + + return (handle); +} diff --git a/lib/libusb/libusb10_io.c b/lib/libusb/libusb10_io.c new file mode 100644 index 000000000000..2047712e9e39 --- /dev/null +++ b/lib/libusb/libusb10_io.c @@ -0,0 +1,872 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2009 Sylvestre Gallon. 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 LIBUSB_GLOBAL_INCLUDE_FILE +#include LIBUSB_GLOBAL_INCLUDE_FILE +#else +#include <errno.h> +#include <poll.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/eventfd.h> +#include <sys/queue.h> +#include <sys/endian.h> +#endif + +#define libusb_device_handle libusb20_device + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" +#include "libusb.h" +#include "libusb10.h" + +UNEXPORTED void +libusb10_add_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd, + struct libusb20_device *pdev, int fd, short events) +{ + if (ctx == NULL) + return; /* invalid */ + + if (pollfd->entry.tqe_prev != NULL) + return; /* already queued */ + + if (fd < 0) + return; /* invalid */ + + pollfd->pdev = pdev; + pollfd->pollfd.fd = fd; + pollfd->pollfd.events = events; + + CTX_LOCK(ctx); + TAILQ_INSERT_TAIL(&ctx->pollfds, pollfd, entry); + CTX_UNLOCK(ctx); + + if (ctx->fd_added_cb) + ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data); +} + +UNEXPORTED void +libusb10_remove_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd) +{ + if (ctx == NULL) + return; /* invalid */ + + if (pollfd->entry.tqe_prev == NULL) + return; /* already dequeued */ + + CTX_LOCK(ctx); + TAILQ_REMOVE(&ctx->pollfds, pollfd, entry); + pollfd->entry.tqe_prev = NULL; + CTX_UNLOCK(ctx); + + if (ctx->fd_removed_cb) + ctx->fd_removed_cb(pollfd->pollfd.fd, ctx->fd_cb_user_data); +} + +/* This function must be called locked */ + +static int +libusb10_handle_events_sub(struct libusb_context *ctx, struct timeval *tv) +{ + struct libusb_device *dev; + struct libusb20_device **ppdev; + struct libusb_super_pollfd *pfd; + struct pollfd *fds; + struct libusb_super_transfer *sxfer; + struct libusb_transfer *uxfer; + nfds_t nfds; + int timeout; + int i; + int err; + + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb10_handle_events_sub enter"); + + nfds = 0; + i = 0; + TAILQ_FOREACH(pfd, &ctx->pollfds, entry) + nfds++; + + fds = alloca(sizeof(*fds) * nfds); + if (fds == NULL) + return (LIBUSB_ERROR_NO_MEM); + + ppdev = alloca(sizeof(*ppdev) * nfds); + if (ppdev == NULL) + return (LIBUSB_ERROR_NO_MEM); + + TAILQ_FOREACH(pfd, &ctx->pollfds, entry) { + fds[i].fd = pfd->pollfd.fd; + fds[i].events = pfd->pollfd.events; + fds[i].revents = 0; + ppdev[i] = pfd->pdev; + if (pfd->pdev != NULL) + libusb_get_device(pfd->pdev)->refcnt++; + i++; + } + + if (tv == NULL) + timeout = -1; + else + timeout = (tv->tv_sec * 1000) + ((tv->tv_usec + 999) / 1000); + + CTX_UNLOCK(ctx); + err = poll(fds, nfds, timeout); + CTX_LOCK(ctx); + + if ((err == -1) && (errno == EINTR)) + err = LIBUSB_ERROR_INTERRUPTED; + else if (err < 0) + err = LIBUSB_ERROR_IO; + + if (err < 1) { + for (i = 0; i != (int)nfds; i++) { + if (ppdev[i] != NULL) { + CTX_UNLOCK(ctx); + libusb_unref_device(libusb_get_device(ppdev[i])); + CTX_LOCK(ctx); + } + } + goto do_done; + } + for (i = 0; i != (int)nfds; i++) { + if (ppdev[i] != NULL) { + dev = libusb_get_device(ppdev[i]); + + if (fds[i].revents != 0) { + err = libusb20_dev_process(ppdev[i]); + + if (err) { + /* + * When the device is opened + * set the "device_is_gone" + * flag. This prevents the + * client from submitting new + * USB transfers to a detached + * device. + */ + if (ppdev[i]->is_opened) + dev->device_is_gone = 1; + + /* remove USB device from polling loop */ + libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); + + /* cancel all pending transfers */ + libusb10_cancel_all_transfer_locked(ppdev[i], dev); + } + } + CTX_UNLOCK(ctx); + libusb_unref_device(dev); + CTX_LOCK(ctx); + + } else { + eventfd_read(fds[i].fd, &(eventfd_t){0}); + + } + } + + err = 0; + +do_done: + + /* Do all done callbacks */ + + while ((sxfer = TAILQ_FIRST(&ctx->tr_done))) { + uint8_t flags; + + TAILQ_REMOVE(&ctx->tr_done, sxfer, entry); + sxfer->entry.tqe_prev = NULL; + + ctx->tr_done_ref++; + + CTX_UNLOCK(ctx); + + uxfer = (struct libusb_transfer *)( + ((uint8_t *)sxfer) + sizeof(*sxfer)); + + /* Allow the callback to free the transfer itself. */ + flags = uxfer->flags; + + if (uxfer->callback != NULL) + (uxfer->callback) (uxfer); + + /* Check if the USB transfer should be automatically freed. */ + if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) + libusb_free_transfer(uxfer); + + CTX_LOCK(ctx); + + ctx->tr_done_ref--; + ctx->tr_done_gen++; + } + + /* Wakeup other waiters */ + pthread_cond_broadcast(&ctx->ctx_cond); + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb10_handle_events_sub complete"); + + return (err); +} + +/* Polling and timing */ + +int +libusb_try_lock_events(libusb_context *ctx) +{ + int err; + + ctx = GET_CONTEXT(ctx); + if (ctx == NULL) + return (1); + + err = CTX_TRYLOCK(ctx); + if (err) + return (1); + + err = (ctx->ctx_handler != NO_THREAD); + if (err) + CTX_UNLOCK(ctx); + else + ctx->ctx_handler = pthread_self(); + + return (err); +} + +void +libusb_lock_events(libusb_context *ctx) +{ + ctx = GET_CONTEXT(ctx); + CTX_LOCK(ctx); + if (ctx->ctx_handler == NO_THREAD) + ctx->ctx_handler = pthread_self(); +} + +void +libusb_unlock_events(libusb_context *ctx) +{ + ctx = GET_CONTEXT(ctx); + if (ctx->ctx_handler == pthread_self()) { + ctx->ctx_handler = NO_THREAD; + pthread_cond_broadcast(&ctx->ctx_cond); + } + CTX_UNLOCK(ctx); +} + +int +libusb_event_handling_ok(libusb_context *ctx) +{ + ctx = GET_CONTEXT(ctx); + return (ctx->ctx_handler == pthread_self()); +} + +int +libusb_event_handler_active(libusb_context *ctx) +{ + ctx = GET_CONTEXT(ctx); + return (ctx->ctx_handler != NO_THREAD); +} + +void +libusb_lock_event_waiters(libusb_context *ctx) +{ + ctx = GET_CONTEXT(ctx); + CTX_LOCK(ctx); +} + +void +libusb_unlock_event_waiters(libusb_context *ctx) +{ + ctx = GET_CONTEXT(ctx); + CTX_UNLOCK(ctx); +} + +int +libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) +{ + struct timespec ts; + int err; + + ctx = GET_CONTEXT(ctx); + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_wait_for_event enter"); + + if (tv == NULL) { + pthread_cond_wait(&ctx->ctx_cond, + &ctx->ctx_lock); + /* try to grab polling of actual events, if any */ + if (ctx->ctx_handler == NO_THREAD) + ctx->ctx_handler = pthread_self(); + return (0); + } + err = clock_gettime(CLOCK_MONOTONIC, &ts); + if (err < 0) + return (LIBUSB_ERROR_OTHER); + + /* + * The "tv" arguments points to a relative time structure and + * not an absolute time structure. + */ + ts.tv_sec += tv->tv_sec; + ts.tv_nsec += tv->tv_usec * 1000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + err = pthread_cond_timedwait(&ctx->ctx_cond, + &ctx->ctx_lock, &ts); + /* try to grab polling of actual events, if any */ + if (ctx->ctx_handler == NO_THREAD) + ctx->ctx_handler = pthread_self(); + + if (err == ETIMEDOUT) + return (1); + + return (0); +} + +int +libusb_handle_events_timeout_completed(libusb_context *ctx, + struct timeval *tv, int *completed) +{ + int err = 0; + + ctx = GET_CONTEXT(ctx); + + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_handle_events_timeout_completed enter"); + + libusb_lock_events(ctx); + + while (1) { + if (completed != NULL) { + if (*completed != 0 || err != 0) + break; + } + err = libusb_handle_events_locked(ctx, tv); + if (completed == NULL) + break; + } + + libusb_unlock_events(ctx); + + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_handle_events_timeout_completed exit"); + + return (err); +} + +int +libusb_handle_events_completed(libusb_context *ctx, int *completed) +{ + return (libusb_handle_events_timeout_completed(ctx, NULL, completed)); +} + +int +libusb_handle_events_timeout(libusb_context *ctx, struct timeval *tv) +{ + return (libusb_handle_events_timeout_completed(ctx, tv, NULL)); +} + +int +libusb_handle_events(libusb_context *ctx) +{ + return (libusb_handle_events_timeout_completed(ctx, NULL, NULL)); +} + +int +libusb_handle_events_locked(libusb_context *ctx, struct timeval *tv) +{ + int err; + + ctx = GET_CONTEXT(ctx); + + if (libusb_event_handling_ok(ctx)) { + err = libusb10_handle_events_sub(ctx, tv); + } else { + err = libusb_wait_for_event(ctx, tv); + if (err != 0) + err = LIBUSB_ERROR_TIMEOUT; + } + return (err); +} + +int +libusb_get_next_timeout(libusb_context *ctx, struct timeval *tv) +{ + /* all timeouts are currently being done by the kernel */ + timerclear(tv); + return (0); +} + +int +libusb_pollfds_handle_timeouts(libusb_context *ctx) +{ + return (1); +} + +void +libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data) +{ + ctx = GET_CONTEXT(ctx); + + ctx->fd_added_cb = added_cb; + ctx->fd_removed_cb = removed_cb; + ctx->fd_cb_user_data = user_data; +} + +const struct libusb_pollfd ** +libusb_get_pollfds(libusb_context *ctx) +{ + struct libusb_super_pollfd *pollfd; + libusb_pollfd **ret; + int i; + + ctx = GET_CONTEXT(ctx); + + CTX_LOCK(ctx); + + i = 0; + TAILQ_FOREACH(pollfd, &ctx->pollfds, entry) + i++; + + ret = calloc(i + 1, sizeof(struct libusb_pollfd *)); + if (ret == NULL) + goto done; + + i = 0; + TAILQ_FOREACH(pollfd, &ctx->pollfds, entry) + ret[i++] = &pollfd->pollfd; + ret[i] = NULL; + +done: + CTX_UNLOCK(ctx); + return ((const struct libusb_pollfd **)ret); +} + + +/* Synchronous device I/O */ + +int +libusb_control_transfer(libusb_device_handle *devh, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint8_t *data, uint16_t wLength, unsigned int timeout) +{ + struct LIBUSB20_CONTROL_SETUP_DECODED req; + int err; + uint16_t actlen; + + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if ((wLength != 0) && (data == NULL)) + return (LIBUSB_ERROR_INVALID_PARAM); + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); + + req.bmRequestType = bmRequestType; + req.bRequest = bRequest; + req.wValue = wValue; + req.wIndex = wIndex; + req.wLength = wLength; + + err = libusb20_dev_request_sync(devh, &req, data, + &actlen, timeout, 0); + + if (err == LIBUSB20_ERROR_PIPE) + return (LIBUSB_ERROR_PIPE); + else if (err == LIBUSB20_ERROR_TIMEOUT) + return (LIBUSB_ERROR_TIMEOUT); + else if (err) + return (LIBUSB_ERROR_NO_DEVICE); + + return (actlen); +} + +static libusb_context * +libusb10_get_context_by_device_handle(libusb_device_handle *devh) +{ + libusb_context *ctx; + + if (devh != NULL) + ctx = libusb_get_device(devh)->ctx; + else + ctx = NULL; + + return (GET_CONTEXT(ctx)); +} + +static void +libusb10_do_transfer_cb(struct libusb_transfer *transfer) +{ + libusb_context *ctx; + int *pdone; + + ctx = libusb10_get_context_by_device_handle(transfer->dev_handle); + + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "sync I/O done"); + + pdone = transfer->user_data; + *pdone = 1; +} + +/* + * TODO: Replace the following function. Allocating and freeing on a + * per-transfer basis is slow. --HPS + */ +static int +libusb10_do_transfer(libusb_device_handle *devh, + uint8_t endpoint, uint8_t *data, int length, + int *transferred, unsigned int timeout, int type) +{ + libusb_context *ctx; + struct libusb_transfer *xfer; + int done; + int ret; + + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if ((length != 0) && (data == NULL)) + return (LIBUSB_ERROR_INVALID_PARAM); + + xfer = libusb_alloc_transfer(0); + if (xfer == NULL) + return (LIBUSB_ERROR_NO_MEM); + + ctx = libusb_get_device(devh)->ctx; + + xfer->dev_handle = devh; + xfer->endpoint = endpoint; + xfer->type = type; + xfer->timeout = timeout; + xfer->buffer = data; + xfer->length = length; + xfer->user_data = (void *)&done; + xfer->callback = libusb10_do_transfer_cb; + done = 0; + + if ((ret = libusb_submit_transfer(xfer)) < 0) { + libusb_free_transfer(xfer); + return (ret); + } + while (done == 0) { + if ((ret = libusb_handle_events(ctx)) < 0) { + libusb_cancel_transfer(xfer); + usleep(1000); /* nice it */ + } + } + + *transferred = xfer->actual_length; + + switch (xfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + ret = 0; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + ret = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_OVERFLOW: + ret = LIBUSB_ERROR_OVERFLOW; + break; + case LIBUSB_TRANSFER_STALL: + ret = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + ret = LIBUSB_ERROR_NO_DEVICE; + break; + default: + ret = LIBUSB_ERROR_OTHER; + break; + } + + libusb_free_transfer(xfer); + return (ret); +} + +int +libusb_bulk_transfer(libusb_device_handle *devh, + uint8_t endpoint, uint8_t *data, int length, + int *transferred, unsigned int timeout) +{ + libusb_context *ctx; + int ret; + + ctx = libusb10_get_context_by_device_handle(devh); + + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_bulk_transfer enter"); + + ret = libusb10_do_transfer(devh, endpoint, data, length, transferred, + timeout, LIBUSB_TRANSFER_TYPE_BULK); + + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_bulk_transfer leave"); + return (ret); +} + +int +libusb_interrupt_transfer(libusb_device_handle *devh, + uint8_t endpoint, uint8_t *data, int length, + int *transferred, unsigned int timeout) +{ + libusb_context *ctx; + int ret; + + ctx = libusb10_get_context_by_device_handle(devh); + + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_interrupt_transfer enter"); + + ret = libusb10_do_transfer(devh, endpoint, data, length, transferred, + timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); + + DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_interrupt_transfer leave"); + return (ret); +} + +uint8_t * +libusb_get_iso_packet_buffer(struct libusb_transfer *transfer, uint32_t off) +{ + uint8_t *ptr; + uint32_t n; + + if (transfer->num_iso_packets < 0) + return (NULL); + + if (off >= (uint32_t)transfer->num_iso_packets) + return (NULL); + + ptr = transfer->buffer; + if (ptr == NULL) + return (NULL); + + for (n = 0; n != off; n++) { + ptr += transfer->iso_packet_desc[n].length; + } + return (ptr); +} + +uint8_t * +libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, uint32_t off) +{ + uint8_t *ptr; + + if (transfer->num_iso_packets < 0) + return (NULL); + + if (off >= (uint32_t)transfer->num_iso_packets) + return (NULL); + + ptr = transfer->buffer; + if (ptr == NULL) + return (NULL); + + ptr += transfer->iso_packet_desc[0].length * off; + + return (ptr); +} + +void +libusb_set_iso_packet_lengths(struct libusb_transfer *transfer, uint32_t length) +{ + int n; + + if (transfer->num_iso_packets < 0) + return; + + for (n = 0; n != transfer->num_iso_packets; n++) + transfer->iso_packet_desc[n].length = length; +} + +uint8_t * +libusb_control_transfer_get_data(struct libusb_transfer *transfer) +{ + if (transfer->buffer == NULL) + return (NULL); + + return (transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE); +} + +struct libusb_control_setup * +libusb_control_transfer_get_setup(struct libusb_transfer *transfer) +{ + return ((struct libusb_control_setup *)transfer->buffer); +} + +void +libusb_fill_control_setup(uint8_t *buf, uint8_t bmRequestType, + uint8_t bRequest, uint16_t wValue, + uint16_t wIndex, uint16_t wLength) +{ + struct libusb_control_setup *req = (struct libusb_control_setup *)buf; + + /* The alignment is OK for all fields below. */ + req->bmRequestType = bmRequestType; + req->bRequest = bRequest; + req->wValue = htole16(wValue); + req->wIndex = htole16(wIndex); + req->wLength = htole16(wLength); +} + +void +libusb_fill_control_transfer(struct libusb_transfer *transfer, + libusb_device_handle *devh, uint8_t *buf, + libusb_transfer_cb_fn callback, void *user_data, + uint32_t timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)buf; + + transfer->dev_handle = devh; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buf; + if (setup != NULL) + transfer->length = LIBUSB_CONTROL_SETUP_SIZE + + le16toh(setup->wLength); + else + transfer->length = 0; + transfer->user_data = user_data; + transfer->callback = callback; + +} + +void +libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, + int length, libusb_transfer_cb_fn callback, void *user_data, + uint32_t timeout) +{ + transfer->dev_handle = devh; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buf; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +void +libusb_fill_interrupt_transfer(struct libusb_transfer *transfer, + libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, + int length, libusb_transfer_cb_fn callback, void *user_data, + uint32_t timeout) +{ + transfer->dev_handle = devh; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buf; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +void +libusb_fill_bulk_stream_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + uint32_t stream_id, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, + length, callback, user_data, timeout); + transfer->type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; + + libusb_transfer_set_stream_id(transfer, stream_id); +} + +void +libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, + int length, int npacket, libusb_transfer_cb_fn callback, + void *user_data, uint32_t timeout) +{ + transfer->dev_handle = devh; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buf; + transfer->length = length; + transfer->num_iso_packets = npacket; + transfer->user_data = user_data; + transfer->callback = callback; +} + +int +libusb_alloc_streams(libusb_device_handle *dev, uint32_t num_streams, + unsigned char *endpoints, int num_endpoints) +{ + if (num_streams > 1) + return (LIBUSB_ERROR_INVALID_PARAM); + return (0); +} + +int +libusb_free_streams(libusb_device_handle *dev, unsigned char *endpoints, int num_endpoints) +{ + + return (0); +} + +void +libusb_transfer_set_stream_id(struct libusb_transfer *transfer, uint32_t stream_id) +{ + struct libusb_super_transfer *sxfer; + + if (transfer == NULL) + return; + + sxfer = (struct libusb_super_transfer *)( + ((uint8_t *)transfer) - sizeof(*sxfer)); + + /* set stream ID */ + sxfer->stream_id = stream_id; +} + +uint32_t +libusb_transfer_get_stream_id(struct libusb_transfer *transfer) +{ + struct libusb_super_transfer *sxfer; + + if (transfer == NULL) + return (0); + + sxfer = (struct libusb_super_transfer *)( + ((uint8_t *)transfer) - sizeof(*sxfer)); + + /* get stream ID */ + return (sxfer->stream_id); +} + +void +libusb_free_pollfds(const struct libusb_pollfd **pollfds) +{ + if (pollfds == NULL) + return; + + free(pollfds); +} diff --git a/lib/libusb/libusb20.3 b/lib/libusb/libusb20.3 new file mode 100644 index 000000000000..7854b0f8ed7e --- /dev/null +++ b/lib/libusb/libusb20.3 @@ -0,0 +1,1079 @@ +.\" +.\" Copyright (c) 2008-2019 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. +.\" +.Dd December 27, 2019 +.Dt LIBUSB20 3 +.Os +.Sh NAME +.Nm libusb20 +. +.Nd "USB access library" +. +. +.Sh LIBRARY +. +. +USB access library (libusb -lusb) +. +. +. +.Sh SYNOPSIS +.In libusb20.h +.Ft int +.Fn libusb20_tr_close "struct libusb20_transfer *xfer" +.Ft int +.Fn libusb20_tr_open "struct libusb20_transfer *xfer" "uint32_t max_buf_size" "uint32_t max_frame_count" "uint8_t ep_no" +.Fn libusb20_tr_open_stream "struct libusb20_transfer *xfer" "uint32_t max_buf_size" "uint32_t max_frame_count" "uint8_t ep_no" "uint16_t stream_id" +.Ft struct libusb20_transfer* +.Fn libusb20_tr_get_pointer "struct libusb20_device *pdev" "uint16_t tr_index" +.Ft uint16_t +.Fn libusb20_tr_get_time_complete "struct libusb20_transfer *xfer" +.Ft uint32_t +.Fn libusb20_tr_get_actual_frames "struct libusb20_transfer *xfer" +.Ft uint32_t +.Fn libusb20_tr_get_actual_length "struct libusb20_transfer *xfer" +.Ft uint32_t +.Fn libusb20_tr_get_max_frames "struct libusb20_transfer *xfer" +.Ft uint32_t +.Fn libusb20_tr_get_max_packet_length "struct libusb20_transfer *xfer" +.Ft uint32_t +.Fn libusb20_tr_get_max_total_length "struct libusb20_transfer *xfer" +.Ft uint8_t +.Fn libusb20_tr_get_status "struct libusb20_transfer *xfer" +.Ft uint8_t +.Fn libusb20_tr_pending "struct libusb20_transfer *xfer" +.Ft void +.Fn libusb20_tr_callback_wrapper "struct libusb20_transfer *xfer" +.Ft void +.Fn libusb20_tr_clear_stall_sync "struct libusb20_transfer *xfer" +.Ft void +.Fn libusb20_tr_drain "struct libusb20_transfer *xfer" +.Ft void +.Fn libusb20_tr_set_buffer "struct libusb20_transfer *xfer" "void *buffer" "uint16_t fr_index" +.Ft void +.Fn libusb20_tr_set_callback "struct libusb20_transfer *xfer" "libusb20_tr_callback_t *cb" +.Ft void +.Fn libusb20_tr_set_flags "struct libusb20_transfer *xfer" "uint8_t flags" +.Ft uint32_t +.Fn libusb20_tr_get_length "struct libusb20_transfer *xfer" "uint16_t fr_index" +.Ft void +.Fn libusb20_tr_set_length "struct libusb20_transfer *xfer" "uint32_t length" "uint16_t fr_index" +.Ft void +.Fn libusb20_tr_set_priv_sc0 "struct libusb20_transfer *xfer" "void *sc0" +.Ft void +.Fn libusb20_tr_set_priv_sc1 "struct libusb20_transfer *xfer" "void *sc1" +.Ft void +.Fn libusb20_tr_set_timeout "struct libusb20_transfer *xfer" "uint32_t timeout" +.Ft void +.Fn libusb20_tr_set_total_frames "struct libusb20_transfer *xfer" "uint32_t nframes" +.Ft void +.Fn libusb20_tr_setup_bulk "struct libusb20_transfer *xfer" "void *pbuf" "uint32_t length" "uint32_t timeout" +.Ft void +.Fn libusb20_tr_setup_control "struct libusb20_transfer *xfer" "void *psetup" "void *pbuf" "uint32_t timeout" +.Ft void +.Fn libusb20_tr_setup_intr "struct libusb20_transfer *xfer" "void *pbuf" "uint32_t length" "uint32_t timeout" +.Ft void +.Fn libusb20_tr_setup_isoc "struct libusb20_transfer *xfer" "void *pbuf" "uint32_t length" "uint61_t fr_index" +.Ft uint8_t +.Fn libusb20_tr_bulk_intr_sync "struct libusb20_transfer *xfer" "void *pbuf" "uint32_t length" "uint32_t *pactlen" "uint32_t timeout" +.Ft void +.Fn libusb20_tr_start "struct libusb20_transfer *xfer" +.Ft void +.Fn libusb20_tr_stop "struct libusb20_transfer *xfer" +.Ft void +.Fn libusb20_tr_submit "struct libusb20_transfer *xfer" +.Ft void * +.Fn libusb20_tr_get_priv_sc0 "struct libusb20_transfer *xfer" +.Ft void * +.Fn libusb20_tr_get_priv_sc1 "struct libusb20_transfer *xfer" +.Ft const char * +.Fn libusb20_dev_get_backend_name "struct libusb20_device *" +.Ft int +.Fn libusb20_dev_get_port_path "struct libusb20_device *pdev" "uint8_t *buf" "uint8_t bufsize" +.Ft int +.Fn libusb20_dev_get_info "struct libusb20_device *pdev" "struct usb_device_info *pinfo" +.Ft int +.Fn libusb20_dev_get_iface_desc "struct libusb20_device *pdev" "uint8_t iface_index" "char *buf" "uint8_t len" +.Ft const char * +.Fn libusb20_dev_get_desc "struct libusb20_device *pdev" +.Ft int +.Fn libusb20_dev_get_stats "struct libusb20_device *pdev" "struct libusb20_device_stats *pstats" +.Ft int +.Fn libusb20_dev_close "struct libusb20_device *pdev" +.Ft int +.Fn libusb20_dev_detach_kernel_driver "struct libusb20_device *pdev" "uint8_t iface_index" +.Ft int +.Fn libusb20_dev_set_config_index "struct libusb20_device *pdev" "uint8_t configIndex" +.Ft int +.Fn libusb20_dev_get_debug "struct libusb20_device *pdev" +.Ft int +.Fn libusb20_dev_get_fd "struct libusb20_device *pdev" +.Ft int +.Fn libusb20_dev_kernel_driver_active "struct libusb20_device *pdev" "uint8_t iface_index" +.Ft int +.Fn libusb20_dev_open "struct libusb20_device *pdev" "uint16_t transfer_max" +.Ft int +.Fn libusb20_dev_process "struct libusb20_device *pdev" +.Ft int +.Fn libusb20_dev_request_sync "struct libusb20_device *pdev" "struct LIBUSB20_CONTROL_SETUP_DECODED *setup" "void *data" "uint16_t *pactlen" "uint32_t timeout" "uint8_t flags" +.Ft int +.Fn libusb20_dev_req_string_sync "struct libusb20_device *pdev" "uint8_t index" "uint16_t langid" "void *ptr" "uint16_t len" +.Ft int +.Fn libusb20_dev_req_string_simple_sync "struct libusb20_device *pdev" "uint8_t index" "void *ptr" "uint16_t len" +.Ft int +.Fn libusb20_dev_reset "struct libusb20_device *pdev" +.Ft int +.Fn libusb20_dev_check_connected "struct libusb20_device *pdev" +.Ft int +.Fn libusb20_dev_set_power_mode "struct libusb20_device *pdev" "uint8_t power_mode" +.Ft uint8_t +.Fn libusb20_dev_get_power_mode "struct libusb20_device *pdev" +.Ft uint16_t +.Fn libusb20_dev_get_power_usage "struct libusb20_device *pdev" +.Ft int +.Fn libusb20_dev_set_alt_index "struct libusb20_device *pdev" "uint8_t iface_index" "uint8_t alt_index" +.Ft struct LIBUSB20_DEVICE_DESC_DECODED * +.Fn libusb20_dev_get_device_desc "struct libusb20_device *pdev" +.Ft struct libusb20_config * +.Fn libusb20_dev_alloc_config "struct libusb20_device *pdev" "uint8_t config_index" +.Ft struct libusb20_device * +.Fn libusb20_dev_alloc "void" +.Ft uint8_t +.Fn libusb20_dev_get_address "struct libusb20_device *pdev" +.Ft uint8_t +.Fn libusb20_dev_get_parent_address "struct libusb20_device *pdev" +.Ft uint8_t +.Fn libusb20_dev_get_parent_port "struct libusb20_device *pdev" +.Ft uint8_t +.Fn libusb20_dev_get_bus_number "struct libusb20_device *pdev" +.Ft uint8_t +.Fn libusb20_dev_get_mode "struct libusb20_device *pdev" +.Ft uint8_t +.Fn libusb20_dev_get_speed "struct libusb20_device *pdev" +.Ft uint8_t +.Fn libusb20_dev_get_config_index "struct libusb20_device *pdev" +.Ft void +.Fn libusb20_dev_free "struct libusb20_device *pdev" +.Ft void +.Fn libusb20_dev_set_debug "struct libusb20_device *pdev" "int debug" +.Ft void +.Fn libusb20_dev_wait_process "struct libusb20_device *pdev" "int timeout" +.Ft int +.Fn libusb20_be_get_template "struct libusb20_backend *pbe" "int *ptemp" +.Ft int +.Fn libusb20_be_set_template "struct libusb20_backend *pbe" "int temp" +.Ft int +.Fn libusb20_be_get_dev_quirk "struct libusb20_backend *pber" "uint16_t index" "struct libusb20_quirk *pq" +.Ft int +.Fn libusb20_be_get_quirk_name "struct libusb20_backend *pbe" "uint16_t index" "struct libusb20_quirk *pq" +.Ft int +.Fn libusb20_be_add_dev_quirk "struct libusb20_backend *pbe" "struct libusb20_quirk *pq" +.Ft int +.Fn libusb20_be_remove_dev_quirk "struct libusb20_backend *pbe" "struct libusb20_quirk *pq" +.Ft struct libusb20_backend * +.Fn libusb20_be_alloc_default "void" +.Ft struct libusb20_backend * +.Fn libusb20_be_alloc_freebsd "void" +.Ft struct libusb20_backend * +.Fn libusb20_be_alloc_linux "void" +.Ft struct libusb20_device * +.Fn libusb20_be_device_foreach "struct libusb20_backend *pbe" "struct libusb20_device *pdev" +.Ft void +.Fn libusb20_be_dequeue_device "struct libusb20_backend *pbe" "struct libusb20_device *pdev" +.Ft void +.Fn libusb20_be_enqueue_device "struct libusb20_backend *pbe" "struct libusb20_device *pdev" +.Ft void +.Fn libusb20_be_free "struct libusb20_backend *pbe" +.Ft uint8_t +.Fn libusb20_me_get_1 "const struct libusb20_me_struct *me" "uint16_t off" +.Ft uint16_t +.Fn libusb20_me_get_2 "const struct libusb20_me_struct *me" "uint16_t off" +.Ft uint16_t +.Fn libusb20_me_encode "void *pdata" "uint16_t len" "const void *pdecoded" +.Ft uint16_t +.Fn libusb20_me_decode "const void *pdata" "uint16_t len" "void *pdecoded" +.Ft "const uint8_t *" +.Fn libusb20_desc_foreach "const struct libusb20_me_struct *me" "const uint8_t *pdesc" +.Ft "const char *" +.Fn libusb20_strerror "int code" +.Ft "const char *" +.Fn libusb20_error_name "int code" +. +. +.Sh DESCRIPTION +. +The +.Nm +library implements functions to be able to easily access and control +USB through the USB file system interface. +The +.Nm +interfaces are specific to the +.Fx +usb stack and are not available on other operating systems, portable +applications should consider using +.Xr libusb 3 . +. +. +.Sh USB TRANSFER OPERATIONS +. +. +.Fn libusb20_tr_close +will release all kernel resources associated with an USB +.Fa xfer . +. +This function returns zero upon success. +. +Non-zero return values indicate a LIBUSB20_ERROR value. +. +.Pp +. +.Fn libusb20_tr_open +will allocate kernel buffer resources according to +.Fa max_buf_size +and +.Fa max_frame_count +associated with an USB +.Fa pxfer +and bind the transfer to the specified +.Fa ep_no . +.Fa max_buf_size +is the minimum buffer size which the data transport layer has to support. +If +.Fa max_buf_size +is zero, the +.Nm +library will use wMaxPacketSize to compute the buffer size. +This can be useful for isochronous transfers. +The actual buffer size can be greater than +.Fa max_buf_size +and is returned by +.Fn libusb20_tr_get_max_total_length . +. +If +.Fa max_frame_count +is OR'ed with LIBUSB20_MAX_FRAME_PRE_SCALE the remaining part of the +argument is converted from milliseconds into the actual number of +frames rounded up, when this function returns. +This flag is only valid for ISOCHRONOUS transfers and has no effect +for other transfer types. +The actual number of frames setup is found by calling +.Fn libusb20_tr_get_max_frames . +. +This function returns zero upon success. +. +Non-zero return values indicate a LIBUSB20_ERROR value. +. +.Pp +. +.Fn libusb20_tr_open_stream +is identical to +.Fn libusb20_tr_open +except that a stream ID can be specified for BULK endpoints having +such a feature. +.Fn libusb20_tr_open +can be used to open stream ID zero. +. +.Pp +. +.Fn libusb20_tr_get_pointer +will return a pointer to the allocated USB transfer according to the +.Fa pdev +and +.Fa tr_index +arguments. +. +This function returns NULL in case of failure. +. +.Pp +. +.Fn libusb20_tr_get_time_complete +will return the completion time of an USB transfer in +millisecond units. +This function is most useful for isochronous USB transfers when doing echo +cancelling. +. +.Pp +. +.Fn libusb20_tr_get_actual_frames +will return the actual number of USB frames after an USB +transfer completed. +A value of zero means that no data was transferred. +.Pp +. +.Fn libusb20_tr_get_actual_length +will return the sum of the actual length for all +transferred USB frames for the given USB transfer. +. +.Pp +. +.Fn libusb20_tr_get_max_frames +will return the maximum number of USB frames that were +allocated when an USB transfer was setup for the given USB transfer. +. +.Pp +. +.Fn libusb20_tr_get_max_packet_length +will return the maximum packet length in bytes +associated with the given USB transfer. +. +The packet length can be used round up buffer sizes so that short USB +packets are avoided for proxy buffers. +. +. +.Pp +. +.Fn libusb20_tr_get_max_total_length +will return the maximum value for the data length sum of all USB +frames associated with an USB transfer. +In case of control transfers the value returned does not include the +length of the SETUP packet, 8 bytes, which is part of frame zero. +The returned value of this function is always aligned to the maximum +packet size, wMaxPacketSize, of the endpoint which the USB transfer is +bound to. +. +.Pp +. +.Fn libusb20_tr_get_status +will return the status of an USB transfer. +. +Status values are defined by a set of LIBUSB20_TRANSFER_XXX enums. +. +.Pp +. +.Fn libusb20_tr_pending +will return non-zero if the given USB transfer is +pending for completion. +. +Else this function returns zero. +. +.Pp +. +.Fn libusb20_tr_callback_wrapper +This is an internal function used to wrap asynchronous USB callbacks. +. +.Pp +. +.Fn libusb20_tr_clear_stall_sync +This is an internal function used to synchronously clear the stall on +the given USB transfer. +. +Please see the USB specification for more information on stall +clearing. +. +If the given USB transfer is pending when this function is called, the +USB transfer will complete with an error after that this function has +been called. +. +.Pp +. +.Fn libusb20_tr_drain +will stop the given USB transfer and will not return +until the USB transfer has been stopped in hardware. +. +.Pp +. +.Fn libusb20_tr_set_buffer +is used to set the +.Fa buffer +pointer for the given USB transfer and +.Fa fr_index . +. +Typically the frame index is zero. +. +. +.Pp +. +.Fn libusb20_tr_set_callback +is used to set the USB callback for asynchronous USB +transfers. +. +The callback type is defined by libusb20_tr_callback_t. +. +.Pp +. +.Fn libusb20_tr_set_flags +is used to set various USB flags for the given USB transfer. +.Bl -tag -width "LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK" +.It LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK +Report a short frame as error. +.It LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK +Multiple short frames are not allowed. +.It LIBUSB20_TRANSFER_FORCE_SHORT +All transmitted frames are short terminated. +.It LIBUSB20_TRANSFER_DO_CLEAR_STALL +Will do a clear-stall before starting the transfer. +.El +. +.Pp +. +.Fn libusb20_tr_get_length +returns the length of the given USB frame by index. +After an USB transfer is complete the USB frame length will get updated to the actual transferred length. +. +.Pp +. +.Fn libusb20_tr_set_length +sets the length of the given USB frame by index. +. +.Pp +. +.Fn libusb20_tr_set_priv_sc0 +sets private driver pointer number zero. +. +.Pp +. +.Fn libusb20_tr_set_priv_sc1 +sets private driver pointer number one. +. +.Pp +. +.Fn libusb20_tr_set_timeout +sets the timeout for the given USB transfer. +. +A timeout value of zero means no timeout. +. +The timeout is given in milliseconds. +. +.Pp +. +.Fn libusb20_tr_set_total_frames +sets the total number of frames that should be executed when the USB transfer is submitted. +. +The total number of USB frames must be less than the maximum number of USB frames associated with the given USB transfer. +. +.Pp +. +.Fn libusb20_tr_setup_bulk +is a helper function for setting up a single frame USB BULK transfer. +. +.Pp +. +.Fn libusb20_tr_setup_control +is a helper function for setting up a single or dual +frame USB CONTROL transfer depending on the control transfer length. +. +.Pp +. +.Fn libusb20_tr_setup_intr +is a helper function for setting up a single frame USB INTERRUPT transfer. +. +.Pp +. +.Fn libusb20_tr_setup_isoc +is a helper function for setting up a multi frame USB ISOCHRONOUS transfer. +. +.Pp +. +.Fn libusb20_tr_bulk_intr_sync +will perform a synchronous BULK or INTERRUPT transfer having length given by the +.Fa length +argument and buffer pointer given by the +.Fa pbuf +argument on the USB transfer given by the +.Fa xfer +argument. +. +If the +.Fa pactlen +argument is non-NULL the actual transfer length will be stored at the given pointer destination. +. +If the +.Fa timeout +argument is non-zero the transfer will timeout after the given value in milliseconds. +. +This function does not change the transfer flags, like short packet not ok. +. +This function returns zero on success else a LIBUSB20_TRANSFER_XXX value is returned. +. +.Pp +. +.Fn libusb20_tr_start +will get the USB transfer started, if not already +started. +. +This function will not get the transfer queued in hardware. +. +This function is non-blocking. +. +.Pp +. +.Fn libusb20_tr_stop +will get the USB transfer stopped, if not already stopped. +. +This function is non-blocking, which means that the actual stop can +happen after the return of this function. +. +.Pp +. +.Fn libusb20_tr_submit +will get the USB transfer queued in hardware. +. +. +.Pp +. +.Fn libusb20_tr_get_priv_sc0 +returns private driver pointer number zero associated +with an USB transfer. +. +. +.Pp +. +.Fn libusb20_tr_get_priv_sc1 +returns private driver pointer number one associated +with an USB transfer. +. +. +.Sh USB DEVICE OPERATIONS +. +. +.Fn libusb20_dev_get_backend_name +returns a zero terminated string describing the backend used. +. +.Pp +. +.Fn libusb20_dev_get_port_path +retrieves the list of USB port numbers which the datastream for a given USB device follows. +The first port number is the Root HUB port number. +Then children port numbers follow. +The Root HUB device itself has a port path length of zero. +Valid port numbers start at one and range until and including 255. +Typically there should not be more than 16 levels, due to electrical and protocol limitations. +This functions returns the number of actual port levels upon success +else a LIBUSB20_ERROR value is returned which are always negative. +If the actual number of port levels is greater than the maximum +specified, a LIBUSB20_ERROR value is returned. +. +.Pp +. +.Fn libusb20_dev_get_info +retrieves the BSD specific usb_device_info structure into the memory location given by +.Fa pinfo . +The USB device given by +.Fa pdev +must be opened before this function will succeed. +This function returns zero on success else a LIBUSB20_ERROR value is returned. +. +.Pp +. +.Fn libusb20_dev_get_iface_desc +retrieves the kernel interface description for the given USB +.Fa iface_index . +The format of the USB interface description is: "drivername<unit>: <description>" +The description string is always zero terminated. +A zero length string is written in case no driver is attached to the given interface. +The USB device given by +.Fa pdev +must be opened before this function will succeed. +This function returns zero on success else a LIBUSB20_ERROR value is returned. +. +.Pp +. +.Fn libusb20_dev_get_desc +returns a zero terminated string describing the given USB device. +The format of the string is: "drivername<unit>: <description>" +. +.Pp +. +.Fn libusb20_dev_get_stats +retrieves the device statistics into the structure pointed to by the +.Fa pstats +argument. +This function returns zero on success else a LIBUSB20_ERROR value is returned. +. +.Pp +. +.Fn libusb20_dev_close +will close the given USB device. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_detach_kernel_driver +will try to detach the kernel driver for the USB interface given by +.Fa iface_index . +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_set_config_index +will try to set the configuration index on an USB +device. +. +The first configuration index is zero. +. +The un-configure index is 255. +. +This function returns zero on success else a LIBUSB20_ERROR value is returned. +. +.Pp +. +.Fn libusb20_dev_get_debug +returns the debug level of an USB device. +. +.Pp +. +.Fn libusb20_dev_get_fd +returns the file descriptor of the given USB device. +. +A negative value is returned when no file descriptor is present. +. +The file descriptor can be used for polling purposes. +. +.Pp +. +.Fn libusb20_dev_kernel_driver_active +returns zero if a kernel driver is active on the given USB interface. +. +Else a LIBUSB20_ERROR value is returned. +. +.Pp +. +.Fn libusb20_dev_open +opens an USB device so that setting up USB transfers +becomes possible. +. +The number of USB transfers can be zero which means only control +transfers are allowed. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +A return value of LIBUSB20_ERROR_BUSY means that the device is already +opened. +. +.Pp +. +.Fn libusb20_dev_process +is called to sync kernel USB transfers with userland USB +transfers. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned typically indicating that the given USB device has been +detached. +. +.Pp +. +.Fn libusb20_dev_request_sync +will perform a synchronous control request on the given +USB device. +. +Before this call will succeed the USB device must be opened. +. +.Fa setup +is a pointer to a decoded and host endian SETUP packet. +.Fa data +is a pointer to a data transfer buffer associated with the control transaction. +This argument can be NULL. +.Fa pactlen +is a pointer to a variable that will hold the actual transfer length after the +control transaction is complete. +.Fa timeout +is the transaction timeout given in milliseconds. +A timeout of zero means no timeout. +.Fa flags +is used to specify transaction flags, for example LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_req_string_sync +will synchronously request an USB string by language ID +and string index into the given buffer limited by a maximum length. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_req_string_simple_sync +will synchronously request an USB string using the +default language ID and convert the string into ASCII before storing +the string into the given buffer limited by a maximum length which +includes the terminating zero. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +. +.Pp +. +.Fn libusb20_dev_reset +will try to BUS reset the given USB device and restore +the last set USB configuration. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +. +.Pp +. +.Fn libusb20_dev_check_connected +will check if an opened USB device is still connected. +. +This function returns zero if the device is still connected else a LIBUSB20_ERROR value is returned. +. +. +.Pp +. +.Fn libusb20_dev_set_power_mode +sets the power mode of the USB device. +. +Valid power modes: +.Bl -tag -width "LIBUSB20_POWER_OFF" +.It LIBUSB20_POWER_OFF +.It LIBUSB20_POWER_ON +.It LIBUSB20_POWER_SAVE +.It LIBUSB20_POWER_SUSPEND +.It LIBUSB20_POWER_RESUME +.El +.Pp +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_get_power_mode +returns the currently selected power mode for the given +USB device. +. +.Pp +. +.Fn libusb20_dev_get_power_usage +returns the reported power usage in milliamps for the given USB device. +A power usage of zero typically means that the device is self powered. +. +.Pp +. +.Fn libusb20_dev_set_alt_index +will try to set the given alternate index for the given +USB interface index. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_get_device_desc +returns a pointer to the decoded and host endian version +of the device descriptor. +. +The USB device need not be opened when calling this function. +. +.Pp +. +.Fn libusb20_dev_alloc_config +will read out and decode the USB config descriptor for the given USB device +and config index. +This function returns a pointer to the decoded configuration which must eventually +be passed to free(). +NULL is returned in case of failure. +. +.Pp +. +.Fn libusb20_dev_alloc +is an internal function to allocate a new USB device. +. +.Pp +. +.Fn libusb20_dev_get_address +returns the internal and not necessarily the real +hardware address of the given USB device. +Valid addresses start at one. +. +.Pp +. +.Fn libusb20_dev_get_parent_address +returns the internal and not necessarily the real hardware address of +the given parent USB HUB device. +This value is zero for the root HUB which usually has a device address +equal to one. +Valid addresses start at one. +. +.Pp +. +.Fn libusb20_dev_get_parent_port +returns the port number on the parent USB HUB device. +This value is zero for the root HUB which usually has a device address +equal to one. +Valid port numbers start at one. +. +.Pp +. +.Fn libusb20_dev_get_bus_number +returns the internal bus number which the given USB +device belongs to. +Valid bus numbers start at zero. +. +.Pp +. +.Fn libusb20_dev_get_mode +returns the current operation mode of the USB entity. +. +Valid return values are: +.Bl -tag -width "LIBUSB20_MODE_DEVICE" +.It LIBUSB20_MODE_HOST +.It LIBUSB20_MODE_DEVICE +.El +. +.Pp +. +.Fn libusb20_dev_get_speed +returns the current speed of the given USB device. +. +.Bl -tag -width "LIBUSB20_SPEED_VARIABLE" +.It LIBUSB20_SPEED_UNKNOWN +.It LIBUSB20_SPEED_LOW +.It LIBUSB20_SPEED_FULL +.It LIBUSB20_SPEED_HIGH +.It LIBUSB20_SPEED_VARIABLE +.It LIBUSB20_SPEED_SUPER +.It LIBUSB20_SPEED_SUPER_PLUS +.El +. +.Pp +. +.Fn libusb20_dev_get_config_index +returns the currently selected config index for the given +USB device. +. +.Pp +. +.Fn libusb20_dev_free +will free the given USB device and all associated USB +transfers. +. +.Pp +. +.Fn libusb20_dev_set_debug +will set the debug level for the given USB device. +. +.Pp +. +.Fn libusb20_dev_wait_process +will wait until a pending USB transfer has completed on +the given USB device. +. +A timeout value can be specified which is passed on to the +.Xr poll 2 +function. +. +.Sh USB BACKEND OPERATIONS +. +.Fn libusb20_be_get_template +will return the currently selected global USB device +side mode template into the integer pointer +.Fa ptemp . +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_be_set_template +will set the global USB device side mode template to +.Fa temp . +The new template is not activated until after the next USB +enumeration. +The template number decides how the USB device will present itself to +the USB Host, like Mass Storage Device, USB Ethernet Device. +Also see the +.Xr usb2_template 4 +module. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_be_get_dev_quirk +will return the device quirk according to +.Fa index +into the libusb20_quirk structure pointed to by +.Fa pq . +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +If the given quirk does not exist LIBUSB20_ERROR_NOT_FOUND is +returned. +. +.Pp +. +.Fn libusb20_be_get_quirk_name +will return the quirk name according to +.Fa index +into the libusb20_quirk structure pointed to by +.Fa pq . +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +If the given quirk does not exist LIBUSB20_ERROR_NOT_FOUND is +returned. +. +.Pp +. +.Fn libusb20_be_add_dev_quirk +will add the libusb20_quirk structure pointed to by the +.Fa pq +argument into the device quirk list. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +If the given quirk cannot be added LIBUSB20_ERROR_NO_MEM is +returned. +. +.Pp +. +.Fn libusb20_be_remove_dev_quirk +will remove the quirk matching the libusb20_quirk structure pointed to by the +.Fa pq +argument from the device quirk list. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +If the given quirk does not exist LIBUSB20_ERROR_NOT_FOUND is +returned. +. +.Pp +. +.Fn libusb20_be_alloc_default +.Fn libusb20_be_alloc_freebsd +.Fn libusb20_be_alloc_linux +These functions are used to allocate a specific USB backend or the operating system +default USB backend. +Allocating a backend is a way to scan for currently present USB devices. +.Pp +. +.Fn libusb20_be_device_foreach +is used to iterate USB devices present in a USB backend. +. +The starting value of +.Fa pdev +is NULL. +. +This function returns the next USB device in the list. +. +If NULL is returned the end of the USB device list has been reached. +. +.Pp +. +.Fn libusb20_be_dequeue_device +will dequeue the given USB device pointer from the +backend USB device list. +. +Dequeued USB devices will not be freed when the backend is freed. +. +.Pp +. +.Fn libusb20_be_enqueue_device +will enqueue the given USB device pointer in the backend USB device list. +. +Enqueued USB devices will get freed when the backend is freed. +. +.Pp +. +.Fn libusb20_be_free +will free the given backend and all USB devices in its device list. +. +. +.Sh USB DESCRIPTOR PARSING +. +.Fn libusb20_me_get_1 pie offset +This function will return a byte at the given byte offset of a message +entity. +. +This function is safe against invalid offsets. +. +.Pp +. +.Fn libusb20_me_get_2 pie offset +This function will return a little endian 16-bit value at the given byte offset of a message +entity. +. +This function is safe against invalid offsets. +. +.Pp +. +.Fn libusb20_me_encode pbuf len pdecoded +This function will encode a so-called *DECODED structure into binary +format. +. +The total encoded length that will fit in the given buffer is +returned. +. +If the buffer pointer is NULL no data will be written to the buffer +location. +. +.Pp +. +.Fn libusb20_me_decode pbuf len pdecoded +This function will decode a binary structure into a so-called *DECODED +structure. +. +The total decoded length is returned. +. +The buffer pointer cannot be NULL. +. +. +.Sh USB DEBUGGING +.Ft const char * +.Fn libusb20_strerror "int code" +Get the ASCII representation of the error given by the +.Fa code +argument. +This function does not return NULL. +.Pp +.Ft const char * +.Fn libusb20_error_name "int code" +Get the ASCII representation of the error enum given by the +.Fa code +argument. +This function does not return NULL. +. +.Sh FILES +.Bl -tag -width Pa +.It Pa /dev/usb +.El +.Sh SEE ALSO +.Xr libusb 3 , +.Xr usb 4 , +.Xr usbconfig 8 , +.Xr usbdump 8 +. +. +.Sh HISTORY +. +. +Some parts of the +.Nm +API derives from the libusb project at sourceforge. diff --git a/lib/libusb/libusb20.c b/lib/libusb/libusb20.c new file mode 100644 index 000000000000..25c95adc27ff --- /dev/null +++ b/lib/libusb/libusb20.c @@ -0,0 +1,1416 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008-2009 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 LIBUSB_GLOBAL_INCLUDE_FILE +#include LIBUSB_GLOBAL_INCLUDE_FILE +#else +#include <ctype.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/queue.h> +#endif + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" + +static int +dummy_int(void) +{ + return (LIBUSB20_ERROR_NOT_SUPPORTED); +} + +static void +dummy_void(void) +{ + return; +} + +static void +dummy_callback(struct libusb20_transfer *xfer) +{ + ; /* style fix */ + switch (libusb20_tr_get_status(xfer)) { + case LIBUSB20_TRANSFER_START: + libusb20_tr_submit(xfer); + break; + default: + /* complete or error */ + break; + } + return; +} + +#define dummy_get_config_desc_full (void *)dummy_int +#define dummy_get_config_index (void *)dummy_int +#define dummy_set_config_index (void *)dummy_int +#define dummy_set_alt_index (void *)dummy_int +#define dummy_reset_device (void *)dummy_int +#define dummy_check_connected (void *)dummy_int +#define dummy_set_power_mode (void *)dummy_int +#define dummy_get_power_mode (void *)dummy_int +#define dummy_get_power_usage (void *)dummy_int +#define dummy_get_stats (void *)dummy_int +#define dummy_kernel_driver_active (void *)dummy_int +#define dummy_detach_kernel_driver (void *)dummy_int +#define dummy_do_request_sync (void *)dummy_int +#define dummy_tr_open (void *)dummy_int +#define dummy_tr_close (void *)dummy_int +#define dummy_tr_clear_stall_sync (void *)dummy_int +#define dummy_process (void *)dummy_int +#define dummy_dev_info (void *)dummy_int +#define dummy_dev_get_iface_driver (void *)dummy_int + +#define dummy_tr_submit (void *)dummy_void +#define dummy_tr_cancel_async (void *)dummy_void + +static const struct libusb20_device_methods libusb20_dummy_methods = { + LIBUSB20_DEVICE(LIBUSB20_DECLARE, dummy) +}; + +void +libusb20_tr_callback_wrapper(struct libusb20_transfer *xfer) +{ + ; /* style fix */ + +repeat: + + if (!xfer->is_pending) { + xfer->status = LIBUSB20_TRANSFER_START; + } else { + xfer->is_pending = 0; + } + + xfer->callback(xfer); + + if (xfer->is_restart) { + xfer->is_restart = 0; + goto repeat; + } + if (xfer->is_draining && + (!xfer->is_pending)) { + xfer->is_draining = 0; + xfer->status = LIBUSB20_TRANSFER_DRAINED; + xfer->callback(xfer); + } + return; +} + +int +libusb20_tr_close(struct libusb20_transfer *xfer) +{ + int error; + + if (!xfer->is_opened) { + return (LIBUSB20_ERROR_OTHER); + } + error = xfer->pdev->methods->tr_close(xfer); + + if (xfer->pLength) { + free(xfer->pLength); + } + if (xfer->ppBuffer) { + free(xfer->ppBuffer); + } + /* reset variable fields in case the transfer is opened again */ + xfer->priv_sc0 = NULL; + xfer->priv_sc1 = NULL; + xfer->is_opened = 0; + xfer->is_pending = 0; + xfer->is_cancel = 0; + xfer->is_draining = 0; + xfer->is_restart = 0; + xfer->status = 0; + xfer->flags = 0; + xfer->nFrames = 0; + xfer->aFrames = 0; + xfer->timeout = 0; + xfer->maxFrames = 0; + xfer->maxTotalLength = 0; + xfer->maxPacketLen = 0; + return (error); +} + +int +libusb20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize, + uint32_t MaxFrameCount, uint8_t ep_no) +{ + return (libusb20_tr_open_stream(xfer, MaxBufSize, MaxFrameCount, ep_no, 0)); +} + +int +libusb20_tr_open_stream(struct libusb20_transfer *xfer, uint32_t MaxBufSize, + uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id) +{ + uint32_t size; + uint8_t pre_scale; + int error; + + if (xfer->is_opened) + return (LIBUSB20_ERROR_BUSY); + if (MaxFrameCount & LIBUSB20_MAX_FRAME_PRE_SCALE) { + MaxFrameCount &= ~LIBUSB20_MAX_FRAME_PRE_SCALE; + /* + * The kernel can setup 8 times more frames when + * pre-scaling ISOCHRONOUS transfers. Make sure the + * length and pointer buffers are big enough: + */ + MaxFrameCount *= 8; + pre_scale = 1; + } else { + pre_scale = 0; + } + if (MaxFrameCount == 0) + return (LIBUSB20_ERROR_INVALID_PARAM); + + xfer->maxFrames = MaxFrameCount; + + size = MaxFrameCount * sizeof(xfer->pLength[0]); + xfer->pLength = malloc(size); + if (xfer->pLength == NULL) { + return (LIBUSB20_ERROR_NO_MEM); + } + memset(xfer->pLength, 0, size); + + size = MaxFrameCount * sizeof(xfer->ppBuffer[0]); + xfer->ppBuffer = malloc(size); + if (xfer->ppBuffer == NULL) { + free(xfer->pLength); + return (LIBUSB20_ERROR_NO_MEM); + } + memset(xfer->ppBuffer, 0, size); + + if (pre_scale) { + error = xfer->pdev->methods->tr_open(xfer, MaxBufSize, + MaxFrameCount / 8, ep_no, stream_id, 1); + } else { + error = xfer->pdev->methods->tr_open(xfer, MaxBufSize, + MaxFrameCount, ep_no, stream_id, 0); + } + + if (error) { + free(xfer->ppBuffer); + free(xfer->pLength); + } else { + xfer->is_opened = 1; + } + return (error); +} + +struct libusb20_transfer * +libusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t trIndex) +{ + if (trIndex >= pdev->nTransfer) { + return (NULL); + } + return (pdev->pTransfer + trIndex); +} + +uint32_t +libusb20_tr_get_actual_frames(struct libusb20_transfer *xfer) +{ + return (xfer->aFrames); +} + +uint16_t +libusb20_tr_get_time_complete(struct libusb20_transfer *xfer) +{ + return (xfer->timeComplete); +} + +uint32_t +libusb20_tr_get_actual_length(struct libusb20_transfer *xfer) +{ + uint32_t x; + uint32_t actlen = 0; + + for (x = 0; x != xfer->aFrames; x++) { + actlen += xfer->pLength[x]; + } + return (actlen); +} + +uint32_t +libusb20_tr_get_max_frames(struct libusb20_transfer *xfer) +{ + return (xfer->maxFrames); +} + +uint32_t +libusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer) +{ + /* + * Special Case NOTE: If the packet multiplier is non-zero for + * High Speed USB, the value returned is equal to + * "wMaxPacketSize * multiplier" ! + */ + return (xfer->maxPacketLen); +} + +uint32_t +libusb20_tr_get_max_total_length(struct libusb20_transfer *xfer) +{ + return (xfer->maxTotalLength); +} + +uint8_t +libusb20_tr_get_status(struct libusb20_transfer *xfer) +{ + return (xfer->status); +} + +uint8_t +libusb20_tr_pending(struct libusb20_transfer *xfer) +{ + return (xfer->is_pending); +} + +void * +libusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer) +{ + return (xfer->priv_sc0); +} + +void * +libusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer) +{ + return (xfer->priv_sc1); +} + +void +libusb20_tr_stop(struct libusb20_transfer *xfer) +{ + if (!xfer->is_opened) { + /* transfer is not opened */ + return; + } + if (!xfer->is_pending) { + /* transfer not pending */ + return; + } + if (xfer->is_cancel) { + /* already cancelling */ + return; + } + xfer->is_cancel = 1; /* we are cancelling */ + + xfer->pdev->methods->tr_cancel_async(xfer); + return; +} + +void +libusb20_tr_drain(struct libusb20_transfer *xfer) +{ + if (!xfer->is_opened) { + /* transfer is not opened */ + return; + } + /* make sure that we are cancelling */ + libusb20_tr_stop(xfer); + + if (xfer->is_pending) { + xfer->is_draining = 1; + } + return; +} + +void +libusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer) +{ + xfer->pdev->methods->tr_clear_stall_sync(xfer); + return; +} + +void +libusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t frIndex) +{ + xfer->ppBuffer[frIndex] = buffer; + return; +} + +void +libusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb) +{ + xfer->callback = cb; + return; +} + +void +libusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags) +{ + xfer->flags = flags; + return; +} + +uint32_t +libusb20_tr_get_length(struct libusb20_transfer *xfer, uint16_t frIndex) +{ + return (xfer->pLength[frIndex]); +} + +void +libusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t frIndex) +{ + xfer->pLength[frIndex] = length; + return; +} + +void +libusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0) +{ + xfer->priv_sc0 = sc0; + return; +} + +void +libusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1) +{ + xfer->priv_sc1 = sc1; + return; +} + +void +libusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout) +{ + xfer->timeout = timeout; + return; +} + +void +libusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames) +{ + if (nFrames > xfer->maxFrames) { + /* should not happen */ + nFrames = xfer->maxFrames; + } + xfer->nFrames = nFrames; + return; +} + +void +libusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout) +{ + xfer->ppBuffer[0] = pBuf; + xfer->pLength[0] = length; + xfer->timeout = timeout; + xfer->nFrames = 1; + return; +} + +void +libusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pBuf, uint32_t timeout) +{ + uint16_t len; + + xfer->ppBuffer[0] = psetup; + xfer->pLength[0] = 8; /* fixed */ + xfer->timeout = timeout; + + len = ((uint8_t *)psetup)[6] | (((uint8_t *)psetup)[7] << 8); + + if (len != 0) { + xfer->nFrames = 2; + xfer->ppBuffer[1] = pBuf; + xfer->pLength[1] = len; + } else { + xfer->nFrames = 1; + } + return; +} + +void +libusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout) +{ + xfer->ppBuffer[0] = pBuf; + xfer->pLength[0] = length; + xfer->timeout = timeout; + xfer->nFrames = 1; + return; +} + +void +libusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint16_t frIndex) +{ + if (frIndex >= xfer->maxFrames) { + /* should not happen */ + return; + } + xfer->ppBuffer[frIndex] = pBuf; + xfer->pLength[frIndex] = length; + return; +} + +uint8_t +libusb20_tr_bulk_intr_sync(struct libusb20_transfer *xfer, + void *pbuf, uint32_t length, uint32_t *pactlen, + uint32_t timeout) +{ + struct libusb20_device *pdev = xfer->pdev; + uint32_t transfer_max; + uint32_t transfer_act; + uint8_t retval; + + /* set some sensible default value */ + if (pactlen != NULL) + *pactlen = 0; + + /* check for error condition */ + if (libusb20_tr_pending(xfer)) + return (LIBUSB20_ERROR_OTHER); + + do { + /* compute maximum transfer length */ + transfer_max = + libusb20_tr_get_max_total_length(xfer); + + if (transfer_max > length) + transfer_max = length; + + /* setup bulk or interrupt transfer */ + libusb20_tr_setup_bulk(xfer, pbuf, + transfer_max, timeout); + + /* start the transfer */ + libusb20_tr_start(xfer); + + /* wait for transfer completion */ + while (libusb20_dev_process(pdev) == 0) { + + if (libusb20_tr_pending(xfer) == 0) + break; + + libusb20_dev_wait_process(pdev, -1); + } + + transfer_act = libusb20_tr_get_actual_length(xfer); + + /* update actual length, if any */ + if (pactlen != NULL) + pactlen[0] += transfer_act; + + /* check transfer status */ + retval = libusb20_tr_get_status(xfer); + if (retval) + break; + + /* check for short transfer */ + if (transfer_act != transfer_max) + break; + + /* update buffer pointer and length */ + pbuf = ((uint8_t *)pbuf) + transfer_max; + length = length - transfer_max; + + } while (length != 0); + + return (retval); +} + +void +libusb20_tr_submit(struct libusb20_transfer *xfer) +{ + if (!xfer->is_opened) { + /* transfer is not opened */ + return; + } + if (xfer->is_pending) { + /* should not happen */ + return; + } + xfer->is_pending = 1; /* we are pending */ + xfer->is_cancel = 0; /* not cancelling */ + xfer->is_restart = 0; /* not restarting */ + + xfer->pdev->methods->tr_submit(xfer); + return; +} + +void +libusb20_tr_start(struct libusb20_transfer *xfer) +{ + if (!xfer->is_opened) { + /* transfer is not opened */ + return; + } + if (xfer->is_pending) { + if (xfer->is_cancel) { + /* cancelling - restart */ + xfer->is_restart = 1; + } + /* transfer not pending */ + return; + } + /* get into the callback */ + libusb20_tr_callback_wrapper(xfer); + return; +} + +/* USB device operations */ + +int +libusb20_dev_close(struct libusb20_device *pdev) +{ + struct libusb20_transfer *xfer; + uint16_t x; + int error = 0; + + if (!pdev->is_opened) { + return (LIBUSB20_ERROR_OTHER); + } + for (x = 0; x != pdev->nTransfer; x++) { + xfer = pdev->pTransfer + x; + + if (!xfer->is_opened) { + /* transfer is not opened */ + continue; + } + + libusb20_tr_drain(xfer); + + libusb20_tr_close(xfer); + } + + if (pdev->pTransfer != NULL) { + free(pdev->pTransfer); + pdev->pTransfer = NULL; + } + error = pdev->beMethods->close_device(pdev); + + pdev->methods = &libusb20_dummy_methods; + + pdev->is_opened = 0; + + /* + * Make sure libusb20_tr_get_pointer() fails: + */ + pdev->nTransfer = 0; + + /* + * The following variable is only used by the libusb v0.1 + * compat layer: + */ + pdev->claimed_interface = 0; + + /* + * The following variable is only used by the libusb v1.0 + * compat layer: + */ + pdev->auto_detach = 0; + + return (error); +} + +int +libusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t ifaceIndex) +{ + int error; + + error = pdev->methods->detach_kernel_driver(pdev, ifaceIndex); + return (error); +} + +struct LIBUSB20_DEVICE_DESC_DECODED * +libusb20_dev_get_device_desc(struct libusb20_device *pdev) +{ + return (&(pdev->ddesc)); +} + +int +libusb20_dev_get_fd(struct libusb20_device *pdev) +{ + return (pdev->file); +} + +int +libusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t ifaceIndex) +{ + int error; + + error = pdev->methods->kernel_driver_active(pdev, ifaceIndex); + return (error); +} + +int +libusb20_dev_open(struct libusb20_device *pdev, uint16_t nTransferMax) +{ + struct libusb20_transfer *xfer; + uint32_t size; + uint16_t x; + int error; + + if (pdev->is_opened) { + return (LIBUSB20_ERROR_BUSY); + } + if (nTransferMax >= 256) { + return (LIBUSB20_ERROR_INVALID_PARAM); + } else if (nTransferMax != 0) { + size = sizeof(pdev->pTransfer[0]) * nTransferMax; + pdev->pTransfer = malloc(size); + if (pdev->pTransfer == NULL) { + return (LIBUSB20_ERROR_NO_MEM); + } + memset(pdev->pTransfer, 0, size); + } + /* initialise all transfers */ + for (x = 0; x != nTransferMax; x++) { + + xfer = pdev->pTransfer + x; + + xfer->pdev = pdev; + xfer->trIndex = x; + xfer->callback = &dummy_callback; + } + + /* set "nTransfer" early */ + pdev->nTransfer = nTransferMax; + + error = pdev->beMethods->open_device(pdev, nTransferMax); + + if (error) { + if (pdev->pTransfer != NULL) { + free(pdev->pTransfer); + pdev->pTransfer = NULL; + } + pdev->file = -1; + pdev->file_ctrl = -1; + pdev->nTransfer = 0; + } else { + pdev->is_opened = 1; + } + return (error); +} + +int +libusb20_dev_reset(struct libusb20_device *pdev) +{ + int error; + + error = pdev->methods->reset_device(pdev); + return (error); +} + +int +libusb20_dev_check_connected(struct libusb20_device *pdev) +{ + int error; + + error = pdev->methods->check_connected(pdev); + return (error); +} + +int +libusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode) +{ + int error; + + error = pdev->methods->set_power_mode(pdev, power_mode); + return (error); +} + +uint8_t +libusb20_dev_get_power_mode(struct libusb20_device *pdev) +{ + int error; + uint8_t power_mode; + + error = pdev->methods->get_power_mode(pdev, &power_mode); + if (error) + power_mode = LIBUSB20_POWER_ON; /* fake power mode */ + return (power_mode); +} + +int +libusb20_dev_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize) +{ + + if (pdev->port_level == 0) { + /* + * Fallback for backends without port path: + */ + if (bufsize < 2) + return (LIBUSB20_ERROR_OVERFLOW); + buf[0] = pdev->parent_address; + buf[1] = pdev->parent_port; + return (2); + } + + /* check if client buffer is too small */ + if (pdev->port_level > bufsize) + return (LIBUSB20_ERROR_OVERFLOW); + + /* copy port number information */ + memcpy(buf, pdev->port_path, pdev->port_level); + + return (pdev->port_level); /* success */ +} + +uint16_t +libusb20_dev_get_power_usage(struct libusb20_device *pdev) +{ + int error; + uint16_t power_usage; + + error = pdev->methods->get_power_usage(pdev, &power_usage); + if (error) + power_usage = 0; + return (power_usage); +} + +int +libusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t ifaceIndex, uint8_t altIndex) +{ + int error; + + error = pdev->methods->set_alt_index(pdev, ifaceIndex, altIndex); + return (error); +} + +int +libusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex) +{ + int error; + + error = pdev->methods->set_config_index(pdev, configIndex); + return (error); +} + +int +libusb20_dev_request_sync(struct libusb20_device *pdev, + struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, + uint16_t *pactlen, uint32_t timeout, uint8_t flags) +{ + int error; + + error = pdev->methods->do_request_sync(pdev, + setup, data, pactlen, timeout, flags); + return (error); +} + +int +libusb20_dev_req_string_sync(struct libusb20_device *pdev, + uint8_t str_index, uint16_t langid, void *ptr, uint16_t len) +{ + struct LIBUSB20_CONTROL_SETUP_DECODED req; + int error; + int flags; + + /* make sure memory is initialised */ + memset(ptr, 0, len); + + if (len < 4) { + /* invalid length */ + return (LIBUSB20_ERROR_INVALID_PARAM); + } + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); + + /* + * We need to read the USB string in two steps else some USB + * devices will complain. + */ + req.bmRequestType = + LIBUSB20_REQUEST_TYPE_STANDARD | + LIBUSB20_RECIPIENT_DEVICE | + LIBUSB20_ENDPOINT_IN; + req.bRequest = LIBUSB20_REQUEST_GET_DESCRIPTOR; + req.wValue = (LIBUSB20_DT_STRING << 8) | str_index; + req.wIndex = langid; + req.wLength = 4; /* bytes */ + + error = libusb20_dev_request_sync(pdev, &req, + ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK); + if (error) { + /* try to request full string */ + req.wLength = 255; + flags = 0; + } else { + /* extract length and request full string */ + req.wLength = *(uint8_t *)ptr; + flags = LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK; + } + if (req.wLength > len) { + /* partial string read */ + req.wLength = len; + } + error = libusb20_dev_request_sync(pdev, &req, ptr, NULL, 1000, flags); + if (error) + return (error); + + if (((uint8_t *)ptr)[1] != LIBUSB20_DT_STRING) + return (LIBUSB20_ERROR_OTHER); + return (0); /* success */ +} + +int +libusb20_dev_req_string_simple_sync(struct libusb20_device *pdev, + uint8_t str_index, void *ptr, uint16_t len) +{ + char *buf; + int error; + uint16_t langid; + uint16_t n; + uint16_t i; + uint16_t c; + uint8_t temp[255]; + uint8_t swap; + + /* the following code derives from the FreeBSD USB kernel */ + + if ((len < 1) || (ptr == NULL)) { + /* too short buffer */ + return (LIBUSB20_ERROR_INVALID_PARAM); + } + error = libusb20_dev_req_string_sync(pdev, + 0, 0, temp, sizeof(temp)); + if (error < 0) { + *(uint8_t *)ptr = 0; /* zero terminate */ + return (error); + } + langid = temp[2] | (temp[3] << 8); + + error = libusb20_dev_req_string_sync(pdev, str_index, + langid, temp, sizeof(temp)); + if (error < 0) { + *(uint8_t *)ptr = 0; /* zero terminate */ + return (error); + } + if (temp[0] < 2) { + /* string length is too short */ + *(uint8_t *)ptr = 0; /* zero terminate */ + return (LIBUSB20_ERROR_OTHER); + } + /* reserve one byte for terminating zero */ + len--; + + /* find maximum length */ + n = (temp[0] / 2) - 1; + if (n > len) { + n = len; + } + /* reset swap state */ + swap = 3; + + /* setup output buffer pointer */ + buf = ptr; + + /* convert and filter */ + for (i = 0; (i != n); i++) { + c = temp[(2 * i) + 2] | (temp[(2 * i) + 3] << 8); + + /* convert from Unicode, handle buggy strings */ + if (((c & 0xff00) == 0) && (swap & 1)) { + /* Little Endian, default */ + *buf = c; + swap = 1; + } else if (((c & 0x00ff) == 0) && (swap & 2)) { + /* Big Endian */ + *buf = c >> 8; + swap = 2; + } else { + /* skip invalid character */ + continue; + } + /* + * Filter by default - we don't allow greater and less than + * signs because they might confuse the dmesg printouts! + */ + if ((*buf == '<') || (*buf == '>') || (!isprint(*buf))) { + /* skip invalid character */ + continue; + } + buf++; + } + *buf = 0; /* zero terminate string */ + + return (0); +} + +struct libusb20_config * +libusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t configIndex) +{ + struct libusb20_config *retval = NULL; + uint8_t *ptr; + uint16_t len; + uint8_t do_close; + int error; + + /* + * Catch invalid configuration descriptor reads early on to + * avoid issues with devices that don't check for a valid USB + * configuration read request. + */ + if (configIndex >= pdev->ddesc.bNumConfigurations) + return (NULL); + + if (!pdev->is_opened) { + error = libusb20_dev_open(pdev, 0); + if (error) { + return (NULL); + } + do_close = 1; + } else { + do_close = 0; + } + error = pdev->methods->get_config_desc_full(pdev, + &ptr, &len, configIndex); + + if (error) { + goto done; + } + /* parse new config descriptor */ + retval = libusb20_parse_config_desc(ptr); + + /* free config descriptor */ + free(ptr); + +done: + if (do_close) { + error = libusb20_dev_close(pdev); + } + return (retval); +} + +struct libusb20_device * +libusb20_dev_alloc(void) +{ + struct libusb20_device *pdev; + + pdev = malloc(sizeof(*pdev)); + if (pdev == NULL) { + return (NULL); + } + memset(pdev, 0, sizeof(*pdev)); + + pdev->file = -1; + pdev->file_ctrl = -1; + pdev->methods = &libusb20_dummy_methods; + return (pdev); +} + +uint8_t +libusb20_dev_get_config_index(struct libusb20_device *pdev) +{ + int error; + uint8_t cfg_index; + uint8_t do_close; + + if (!pdev->is_opened) { + error = libusb20_dev_open(pdev, 0); + if (error == 0) { + do_close = 1; + } else { + do_close = 0; + } + } else { + do_close = 0; + } + + error = pdev->methods->get_config_index(pdev, &cfg_index); + if (error) + cfg_index = 0xFF; /* current config index */ + if (do_close) { + if (libusb20_dev_close(pdev)) { + /* ignore */ + } + } + return (cfg_index); +} + +uint8_t +libusb20_dev_get_mode(struct libusb20_device *pdev) +{ + return (pdev->usb_mode); +} + +uint8_t +libusb20_dev_get_speed(struct libusb20_device *pdev) +{ + return (pdev->usb_speed); +} + +int +libusb20_dev_get_stats(struct libusb20_device *pdev, struct libusb20_device_stats *pstats) +{ + uint8_t do_close; + int error; + + if (!pdev->is_opened) { + error = libusb20_dev_open(pdev, 0); + if (error == 0) { + do_close = 1; + } else { + do_close = 0; + } + } else { + do_close = 0; + } + + error = pdev->methods->get_stats(pdev, pstats); + + if (do_close) + (void) libusb20_dev_close(pdev); + + return (error); +} + +/* if this function returns an error, the device is gone */ +int +libusb20_dev_process(struct libusb20_device *pdev) +{ + int error; + + error = pdev->methods->process(pdev); + return (error); +} + +void +libusb20_dev_wait_process(struct libusb20_device *pdev, int timeout) +{ + struct pollfd pfd[1]; + + if (!pdev->is_opened) { + return; + } + pfd[0].fd = pdev->file; + pfd[0].events = (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); + pfd[0].revents = 0; + + if (poll(pfd, 1, timeout)) { + /* ignore any error */ + } + return; +} + +void +libusb20_dev_free(struct libusb20_device *pdev) +{ + if (pdev == NULL) { + /* be NULL safe */ + return; + } + if (pdev->is_opened) { + if (libusb20_dev_close(pdev)) { + /* ignore any errors */ + } + } + free(pdev); + return; +} + +int +libusb20_dev_get_info(struct libusb20_device *pdev, + struct usb_device_info *pinfo) +{ + if (pinfo == NULL) + return (LIBUSB20_ERROR_INVALID_PARAM); + + return (pdev->beMethods->dev_get_info(pdev, pinfo)); +} + +const char * +libusb20_dev_get_backend_name(struct libusb20_device *pdev) +{ + return (pdev->beMethods->get_backend_name()); +} + +const char * +libusb20_dev_get_desc(struct libusb20_device *pdev) +{ + return (pdev->usb_desc); +} + +void +libusb20_dev_set_debug(struct libusb20_device *pdev, int debug) +{ + pdev->debug = debug; + return; +} + +int +libusb20_dev_get_debug(struct libusb20_device *pdev) +{ + return (pdev->debug); +} + +uint8_t +libusb20_dev_get_address(struct libusb20_device *pdev) +{ + return (pdev->device_address); +} + +uint8_t +libusb20_dev_get_parent_address(struct libusb20_device *pdev) +{ + return (pdev->parent_address); +} + +uint8_t +libusb20_dev_get_parent_port(struct libusb20_device *pdev) +{ + return (pdev->parent_port); +} + +uint8_t +libusb20_dev_get_bus_number(struct libusb20_device *pdev) +{ + return (pdev->bus_number); +} + +int +libusb20_dev_get_iface_desc(struct libusb20_device *pdev, + uint8_t iface_index, char *buf, uint8_t len) +{ + if ((buf == NULL) || (len == 0)) + return (LIBUSB20_ERROR_INVALID_PARAM); + + buf[0] = 0; /* set default string value */ + + return (pdev->beMethods->dev_get_iface_desc( + pdev, iface_index, buf, len)); +} + +/* USB backend operations */ + +int +libusb20_be_get_dev_quirk(struct libusb20_backend *pbe, + uint16_t quirk_index, struct libusb20_quirk *pq) +{ + return (pbe->methods->root_get_dev_quirk(pbe, quirk_index, pq)); +} + +int +libusb20_be_get_quirk_name(struct libusb20_backend *pbe, + uint16_t quirk_index, struct libusb20_quirk *pq) +{ + return (pbe->methods->root_get_quirk_name(pbe, quirk_index, pq)); +} + +int +libusb20_be_add_dev_quirk(struct libusb20_backend *pbe, + struct libusb20_quirk *pq) +{ + return (pbe->methods->root_add_dev_quirk(pbe, pq)); +} + +int +libusb20_be_remove_dev_quirk(struct libusb20_backend *pbe, + struct libusb20_quirk *pq) +{ + return (pbe->methods->root_remove_dev_quirk(pbe, pq)); +} + +int +libusb20_be_set_template(struct libusb20_backend *pbe, int temp) +{ + return (pbe->methods->root_set_template(pbe, temp)); +} + +int +libusb20_be_get_template(struct libusb20_backend *pbe, int *ptemp) +{ + int temp; + + if (ptemp == NULL) + ptemp = &temp; + + return (pbe->methods->root_get_template(pbe, ptemp)); +} + +struct libusb20_device * +libusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev) +{ + if (pbe == NULL) { + pdev = NULL; + } else if (pdev == NULL) { + pdev = TAILQ_FIRST(&(pbe->usb_devs)); + } else { + pdev = TAILQ_NEXT(pdev, dev_entry); + } + return (pdev); +} + +struct libusb20_backend * +libusb20_be_alloc(const struct libusb20_backend_methods *methods) +{ + struct libusb20_backend *pbe; + + pbe = malloc(sizeof(*pbe)); + if (pbe == NULL) { + return (NULL); + } + memset(pbe, 0, sizeof(*pbe)); + + TAILQ_INIT(&(pbe->usb_devs)); + + pbe->methods = methods; /* set backend methods */ + + /* do the initial device scan */ + if (pbe->methods->init_backend) { + pbe->methods->init_backend(pbe); + } + return (pbe); +} + +struct libusb20_backend * +libusb20_be_alloc_linux(void) +{ + return (NULL); +} + +struct libusb20_backend * +libusb20_be_alloc_ugen20(void) +{ + return (libusb20_be_alloc(&libusb20_ugen20_backend)); +} + +struct libusb20_backend * +libusb20_be_alloc_default(void) +{ + struct libusb20_backend *pbe; + +#ifdef __linux__ + pbe = libusb20_be_alloc_linux(); + if (pbe) { + return (pbe); + } +#endif + pbe = libusb20_be_alloc_ugen20(); + if (pbe) { + return (pbe); + } + return (NULL); /* no backend found */ +} + +void +libusb20_be_free(struct libusb20_backend *pbe) +{ + struct libusb20_device *pdev; + + if (pbe == NULL) { + /* be NULL safe */ + return; + } + while ((pdev = libusb20_be_device_foreach(pbe, NULL))) { + libusb20_be_dequeue_device(pbe, pdev); + libusb20_dev_free(pdev); + } + if (pbe->methods->exit_backend) { + pbe->methods->exit_backend(pbe); + } + /* free backend */ + free(pbe); +} + +void +libusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev) +{ + pdev->beMethods = pbe->methods; /* copy backend methods */ + TAILQ_INSERT_TAIL(&(pbe->usb_devs), pdev, dev_entry); +} + +void +libusb20_be_dequeue_device(struct libusb20_backend *pbe, + struct libusb20_device *pdev) +{ + TAILQ_REMOVE(&(pbe->usb_devs), pdev, dev_entry); +} + +const char * +libusb20_strerror(int code) +{ + switch (code) { + case LIBUSB20_SUCCESS: + return ("Success"); + case LIBUSB20_ERROR_IO: + return ("I/O error"); + case LIBUSB20_ERROR_INVALID_PARAM: + return ("Invalid parameter"); + case LIBUSB20_ERROR_ACCESS: + return ("Permissions error"); + case LIBUSB20_ERROR_NO_DEVICE: + return ("No device"); + case LIBUSB20_ERROR_NOT_FOUND: + return ("Not found"); + case LIBUSB20_ERROR_BUSY: + return ("Device busy"); + case LIBUSB20_ERROR_TIMEOUT: + return ("Timeout"); + case LIBUSB20_ERROR_OVERFLOW: + return ("Overflow"); + case LIBUSB20_ERROR_PIPE: + return ("Pipe error"); + case LIBUSB20_ERROR_INTERRUPTED: + return ("Interrupted"); + case LIBUSB20_ERROR_NO_MEM: + return ("Out of memory"); + case LIBUSB20_ERROR_NOT_SUPPORTED: + return ("Not supported"); + case LIBUSB20_ERROR_OTHER: + return ("Other error"); + default: + return ("Unknown error"); + } +} + +const char * +libusb20_error_name(int code) +{ + switch (code) { + case LIBUSB20_SUCCESS: + return ("LIBUSB20_SUCCESS"); + case LIBUSB20_ERROR_IO: + return ("LIBUSB20_ERROR_IO"); + case LIBUSB20_ERROR_INVALID_PARAM: + return ("LIBUSB20_ERROR_INVALID_PARAM"); + case LIBUSB20_ERROR_ACCESS: + return ("LIBUSB20_ERROR_ACCESS"); + case LIBUSB20_ERROR_NO_DEVICE: + return ("LIBUSB20_ERROR_NO_DEVICE"); + case LIBUSB20_ERROR_NOT_FOUND: + return ("LIBUSB20_ERROR_NOT_FOUND"); + case LIBUSB20_ERROR_BUSY: + return ("LIBUSB20_ERROR_BUSY"); + case LIBUSB20_ERROR_TIMEOUT: + return ("LIBUSB20_ERROR_TIMEOUT"); + case LIBUSB20_ERROR_OVERFLOW: + return ("LIBUSB20_ERROR_OVERFLOW"); + case LIBUSB20_ERROR_PIPE: + return ("LIBUSB20_ERROR_PIPE"); + case LIBUSB20_ERROR_INTERRUPTED: + return ("LIBUSB20_ERROR_INTERRUPTED"); + case LIBUSB20_ERROR_NO_MEM: + return ("LIBUSB20_ERROR_NO_MEM"); + case LIBUSB20_ERROR_NOT_SUPPORTED: + return ("LIBUSB20_ERROR_NOT_SUPPORTED"); + case LIBUSB20_ERROR_OTHER: + return ("LIBUSB20_ERROR_OTHER"); + default: + return ("LIBUSB20_ERROR_UNKNOWN"); + } +} diff --git a/lib/libusb/libusb20.h b/lib/libusb/libusb20.h new file mode 100644 index 000000000000..c132c58a9f69 --- /dev/null +++ b/lib/libusb/libusb20.h @@ -0,0 +1,317 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008-2009 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2007-2008 Daniel Drake. All rights reserved. + * Copyright (c) 2001 Johannes Erdfelt. 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 _LIBUSB20_H_ +#define _LIBUSB20_H_ + +#ifndef LIBUSB_GLOBAL_INCLUDE_FILE +#include <stdint.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +}; /* style */ + +#endif + +/** \ingroup misc + * Error codes. Most libusb20 functions return 0 on success or one of + * these codes on failure. + */ +enum libusb20_error { + /** Success (no error) */ + LIBUSB20_SUCCESS = 0, + + /** Input/output error */ + LIBUSB20_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB20_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB20_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB20_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB20_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB20_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB20_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB20_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB20_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB20_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB20_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB20_ERROR_NOT_SUPPORTED = -12, + + /** Other error */ + LIBUSB20_ERROR_OTHER = -99, +}; + +/** \ingroup asyncio + * libusb20_tr_get_status() values */ +enum libusb20_transfer_status { + /** Transfer completed without error. Note that this does not + * indicate that the entire amount of requested data was + * transferred. */ + LIBUSB20_TRANSFER_COMPLETED, + + /** Callback code to start transfer */ + LIBUSB20_TRANSFER_START, + + /** Drain complete callback code */ + LIBUSB20_TRANSFER_DRAINED, + + /** Transfer failed */ + LIBUSB20_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB20_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB20_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected + * (endpoint stalled). For control endpoints: control request + * not supported. */ + LIBUSB20_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB20_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB20_TRANSFER_OVERFLOW, +}; + +/** \ingroup asyncio + * libusb20_tr_set_flags() values */ +enum libusb20_transfer_flags { + /** Report a short frame as error */ + LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK = 0x0001, + + /** Multiple short frames are not allowed */ + LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK = 0x0002, + + /** All transmitted frames are short terminated */ + LIBUSB20_TRANSFER_FORCE_SHORT = 0x0004, + + /** Will do a clear-stall before xfer */ + LIBUSB20_TRANSFER_DO_CLEAR_STALL = 0x0008, +}; + +/** \ingroup misc + * libusb20_dev_get_mode() values + */ +enum libusb20_device_mode { + LIBUSB20_MODE_HOST, /* default */ + LIBUSB20_MODE_DEVICE, +}; + +/** \ingroup misc + * libusb20_dev_get_speed() values + */ +enum { + LIBUSB20_SPEED_UNKNOWN, /* default */ + LIBUSB20_SPEED_LOW, + LIBUSB20_SPEED_FULL, + LIBUSB20_SPEED_HIGH, + LIBUSB20_SPEED_VARIABLE, + LIBUSB20_SPEED_SUPER, + LIBUSB20_SPEED_SUPER_PLUS, +}; + +/** \ingroup misc + * libusb20_dev_set_power() values + */ +enum { + LIBUSB20_POWER_OFF, + LIBUSB20_POWER_ON, + LIBUSB20_POWER_SAVE, + LIBUSB20_POWER_SUSPEND, + LIBUSB20_POWER_RESUME, +}; + +struct usb_device_info; +struct libusb20_transfer; +struct libusb20_backend; +struct libusb20_backend_methods; +struct libusb20_device; +struct libusb20_device_methods; +struct libusb20_config; +struct LIBUSB20_CONTROL_SETUP_DECODED; +struct LIBUSB20_DEVICE_DESC_DECODED; + +typedef void (libusb20_tr_callback_t)(struct libusb20_transfer *xfer); + +struct libusb20_quirk { + uint16_t vid; /* vendor ID */ + uint16_t pid; /* product ID */ + uint16_t bcdDeviceLow; /* low revision value, inclusive */ + uint16_t bcdDeviceHigh; /* high revision value, inclusive */ + uint16_t reserved[2]; /* for the future */ + /* quirk name, UQ_XXX, including terminating zero */ + char quirkname[64 - 12]; +}; + +struct libusb20_device_stats { + uint64_t xfer_ok[4]; /* sorted by USB transfer type, UE_XXX */ + uint64_t xfer_fail[4]; /* sorted by USB transfer type, UE_XXX */ + uint64_t xfer_reserved[24]; /* reserved */ +}; + +#define LIBUSB20_MAX_FRAME_PRE_SCALE (1U << 31) + +/* USB transfer operations */ +int libusb20_tr_close(struct libusb20_transfer *xfer); +int libusb20_tr_open(struct libusb20_transfer *xfer, uint32_t max_buf_size, uint32_t max_frame_count, uint8_t ep_no); +int libusb20_tr_open_stream(struct libusb20_transfer *xfer, uint32_t max_buf_size, uint32_t max_frame_count, uint8_t ep_no, uint16_t stream_id); +struct libusb20_transfer *libusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t tr_index); +uint16_t libusb20_tr_get_time_complete(struct libusb20_transfer *xfer); +uint32_t libusb20_tr_get_actual_frames(struct libusb20_transfer *xfer); +uint32_t libusb20_tr_get_actual_length(struct libusb20_transfer *xfer); +uint32_t libusb20_tr_get_max_frames(struct libusb20_transfer *xfer); +uint32_t libusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer); +uint32_t libusb20_tr_get_max_total_length(struct libusb20_transfer *xfer); +uint8_t libusb20_tr_get_status(struct libusb20_transfer *xfer); +uint8_t libusb20_tr_pending(struct libusb20_transfer *xfer); +void libusb20_tr_callback_wrapper(struct libusb20_transfer *xfer); +void libusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer); +void libusb20_tr_drain(struct libusb20_transfer *xfer); +void libusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t fr_index); +void libusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb); +void libusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags); +uint32_t libusb20_tr_get_length(struct libusb20_transfer *xfer, uint16_t fr_index); +void libusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t fr_index); +void libusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0); +void libusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1); +void libusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout); +void libusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames); +void libusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t timeout); +void libusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pbuf, uint32_t timeout); +void libusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t timeout); +void libusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint16_t fr_index); +uint8_t libusb20_tr_bulk_intr_sync(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t *pactlen, uint32_t timeout); +void libusb20_tr_start(struct libusb20_transfer *xfer); +void libusb20_tr_stop(struct libusb20_transfer *xfer); +void libusb20_tr_submit(struct libusb20_transfer *xfer); +void *libusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer); +void *libusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer); + + +/* USB device operations */ + +const char *libusb20_dev_get_backend_name(struct libusb20_device *pdev); +const char *libusb20_dev_get_desc(struct libusb20_device *pdev); +int libusb20_dev_close(struct libusb20_device *pdev); +int libusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t iface_index); +int libusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex); +int libusb20_dev_get_debug(struct libusb20_device *pdev); +int libusb20_dev_get_fd(struct libusb20_device *pdev); +int libusb20_dev_get_stats(struct libusb20_device *pdev, struct libusb20_device_stats *pstat); +int libusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t iface_index); +int libusb20_dev_open(struct libusb20_device *pdev, uint16_t transfer_max); +int libusb20_dev_process(struct libusb20_device *pdev); +int libusb20_dev_request_sync(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags); +int libusb20_dev_req_string_sync(struct libusb20_device *pdev, uint8_t index, uint16_t langid, void *ptr, uint16_t len); +int libusb20_dev_req_string_simple_sync(struct libusb20_device *pdev, uint8_t index, void *ptr, uint16_t len); +int libusb20_dev_reset(struct libusb20_device *pdev); +int libusb20_dev_check_connected(struct libusb20_device *pdev); +int libusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode); +uint8_t libusb20_dev_get_power_mode(struct libusb20_device *pdev); +int libusb20_dev_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize); +uint16_t libusb20_dev_get_power_usage(struct libusb20_device *pdev); +int libusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t iface_index, uint8_t alt_index); +int libusb20_dev_get_info(struct libusb20_device *pdev, struct usb_device_info *pinfo); +int libusb20_dev_get_iface_desc(struct libusb20_device *pdev, uint8_t iface_index, char *buf, uint8_t len); + +struct LIBUSB20_DEVICE_DESC_DECODED *libusb20_dev_get_device_desc(struct libusb20_device *pdev); +struct libusb20_config *libusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t config_index); +struct libusb20_device *libusb20_dev_alloc(void); +uint8_t libusb20_dev_get_address(struct libusb20_device *pdev); +uint8_t libusb20_dev_get_parent_address(struct libusb20_device *pdev); +uint8_t libusb20_dev_get_parent_port(struct libusb20_device *pdev); +uint8_t libusb20_dev_get_bus_number(struct libusb20_device *pdev); +uint8_t libusb20_dev_get_mode(struct libusb20_device *pdev); +uint8_t libusb20_dev_get_speed(struct libusb20_device *pdev); +uint8_t libusb20_dev_get_config_index(struct libusb20_device *pdev); +void libusb20_dev_free(struct libusb20_device *pdev); +void libusb20_dev_set_debug(struct libusb20_device *pdev, int debug); +void libusb20_dev_wait_process(struct libusb20_device *pdev, int timeout); + +/* USB global operations */ + +int libusb20_be_get_dev_quirk(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); +int libusb20_be_get_quirk_name(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); +int libusb20_be_add_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq); +int libusb20_be_remove_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq); +int libusb20_be_get_template(struct libusb20_backend *pbe, int *ptemp); +int libusb20_be_set_template(struct libusb20_backend *pbe, int temp); + +/* USB backend operations */ + +struct libusb20_backend *libusb20_be_alloc(const struct libusb20_backend_methods *methods); +struct libusb20_backend *libusb20_be_alloc_default(void); +struct libusb20_backend *libusb20_be_alloc_freebsd(void); +struct libusb20_backend *libusb20_be_alloc_linux(void); +struct libusb20_backend *libusb20_be_alloc_ugen20(void); +struct libusb20_device *libusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev); +void libusb20_be_dequeue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev); +void libusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev); +void libusb20_be_free(struct libusb20_backend *pbe); + +/* USB debugging */ + +const char *libusb20_strerror(int); +const char *libusb20_error_name(int); + +#if 0 +{ /* style */ +#endif +#ifdef __cplusplus +} + +#endif + +#endif /* _LIBUSB20_H_ */ diff --git a/lib/libusb/libusb20_desc.c b/lib/libusb/libusb20_desc.c new file mode 100644 index 000000000000..89c01d5afeff --- /dev/null +++ b/lib/libusb/libusb20_desc.c @@ -0,0 +1,796 @@ +/*- + * 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 LIBUSB_GLOBAL_INCLUDE_FILE +#include LIBUSB_GLOBAL_INCLUDE_FILE +#else +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/queue.h> +#endif + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" + +static const uint32_t libusb20_me_encode_empty[2]; /* dummy */ + +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_USB_20_DEVCAP_DESC); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_USB_DEVCAP_DESC); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_BOS_DESCRIPTOR); + +/*------------------------------------------------------------------------* + * libusb20_parse_config_desc + * + * Return values: + * NULL: Out of memory. + * Else: A valid config structure pointer which must be passed to "free()" + *------------------------------------------------------------------------*/ +struct libusb20_config * +libusb20_parse_config_desc(const void *config_desc) +{ + struct libusb20_config *lub_config; + struct libusb20_interface *lub_interface; + struct libusb20_interface *lub_alt_interface; + struct libusb20_interface *last_if; + struct libusb20_endpoint *lub_endpoint; + struct libusb20_endpoint *last_ep; + + struct libusb20_me_struct pcdesc; + const uint8_t *ptr; + uint32_t size; + uint16_t niface_no_alt; + uint16_t niface; + uint16_t nendpoint; + uint16_t iface_no; + + ptr = config_desc; + if (ptr[1] != LIBUSB20_DT_CONFIG) { + return (NULL); /* not config descriptor */ + } + + /* + * The first "bInterfaceNumber" cannot start at 0xFFFF + * because the field is 8-bit. + */ + niface_no_alt = 0; + nendpoint = 0; + niface = 0; + iface_no = 0xFFFF; + ptr = NULL; + + /* get "wTotalLength" and setup "pcdesc" */ + pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0); + pcdesc.len = + ((const uint8_t *)config_desc)[2] | + (((const uint8_t *)config_desc)[3] << 8); + pcdesc.type = LIBUSB20_ME_IS_RAW; + + /* descriptor pre-scan */ + while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) { + if (ptr[1] == LIBUSB20_DT_ENDPOINT) { + nendpoint++; + } else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) { + niface++; + /* check "bInterfaceNumber" */ + if (ptr[2] != iface_no) { + iface_no = ptr[2]; + niface_no_alt++; + } + } + } + + /* sanity checking */ + if (niface >= 256) { + return (NULL); /* corrupt */ + } + if (nendpoint >= 256) { + return (NULL); /* corrupt */ + } + size = sizeof(*lub_config) + + (niface * sizeof(*lub_interface)) + + (nendpoint * sizeof(*lub_endpoint)) + + pcdesc.len; + + lub_config = malloc(size); + if (lub_config == NULL) { + return (NULL); /* out of memory */ + } + /* make sure memory is initialised */ + memset(lub_config, 0, size); + + lub_interface = (void *)(lub_config + 1); + lub_alt_interface = (void *)(lub_interface + niface_no_alt); + lub_endpoint = (void *)(lub_interface + niface); + + /* + * Make a copy of the config descriptor, so that the caller can free + * the initial config descriptor pointer! + */ + memcpy((void *)(lub_endpoint + nendpoint), config_desc, pcdesc.len); + + ptr = (const void *)(lub_endpoint + nendpoint); + pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0); + + /* init config structure */ + + LIBUSB20_INIT(LIBUSB20_CONFIG_DESC, &lub_config->desc); + + if (libusb20_me_decode(ptr, ptr[0], &lub_config->desc)) { + /* ignore */ + } + lub_config->num_interface = 0; + lub_config->interface = lub_interface; + lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]); + lub_config->extra.len = -ptr[0]; + lub_config->extra.type = LIBUSB20_ME_IS_RAW; + + /* reset states */ + niface = 0; + iface_no = 0xFFFF; + ptr = NULL; + lub_interface--; + lub_endpoint--; + last_if = NULL; + last_ep = NULL; + + /* descriptor pre-scan */ + while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) { + if (ptr[1] == LIBUSB20_DT_ENDPOINT) { + if (last_if) { + lub_endpoint++; + last_ep = lub_endpoint; + last_if->num_endpoints++; + + LIBUSB20_INIT(LIBUSB20_ENDPOINT_DESC, &last_ep->desc); + + if (libusb20_me_decode(ptr, ptr[0], &last_ep->desc)) { + /* ignore */ + } + last_ep->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]); + last_ep->extra.len = 0; + last_ep->extra.type = LIBUSB20_ME_IS_RAW; + } else { + lub_config->extra.len += ptr[0]; + } + + } else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) { + if (ptr[2] != iface_no) { + /* new interface */ + iface_no = ptr[2]; + lub_interface++; + lub_config->num_interface++; + last_if = lub_interface; + niface++; + } else { + /* one more alternate setting */ + lub_interface->num_altsetting++; + last_if = lub_alt_interface; + lub_alt_interface++; + } + + LIBUSB20_INIT(LIBUSB20_INTERFACE_DESC, &last_if->desc); + + if (libusb20_me_decode(ptr, ptr[0], &last_if->desc)) { + /* ignore */ + } + + /* detect broken USB descriptors when USB debugging is enabled */ + if (last_if->desc.bInterfaceNumber != (uint8_t)(niface - 1)) { + const char *str = getenv("LIBUSB_DEBUG"); + if (str != NULL && str[0] != '\0' && str[0] != '0') { + printf("LIBUSB_DEBUG: bInterfaceNumber(%u) is not sequential(%u)\n", + last_if->desc.bInterfaceNumber, niface - 1); + } + } + last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]); + last_if->extra.len = 0; + last_if->extra.type = LIBUSB20_ME_IS_RAW; + last_if->endpoints = lub_endpoint + 1; + last_if->altsetting = lub_alt_interface; + last_if->num_altsetting = 0; + last_if->num_endpoints = 0; + last_ep = NULL; + } else { + /* unknown descriptor */ + if (last_if) { + if (last_ep) { + last_ep->extra.len += ptr[0]; + } else { + last_if->extra.len += ptr[0]; + } + } else { + lub_config->extra.len += ptr[0]; + } + } + } + return (lub_config); +} + +/*------------------------------------------------------------------------* + * libusb20_desc_foreach + * + * Safe traversal of USB descriptors. + * + * Return values: + * NULL: End of descriptors + * Else: Pointer to next descriptor + *------------------------------------------------------------------------*/ +const uint8_t * +libusb20_desc_foreach(const struct libusb20_me_struct *pdesc, + const uint8_t *psubdesc) +{ + const uint8_t *start; + const uint8_t *end; + const uint8_t *desc_next; + + /* be NULL safe */ + if (pdesc == NULL) + return (NULL); + + start = (const uint8_t *)pdesc->ptr; + end = LIBUSB20_ADD_BYTES(start, pdesc->len); + + /* get start of next descriptor */ + if (psubdesc == NULL) + psubdesc = start; + else + psubdesc = psubdesc + psubdesc[0]; + + /* check that the next USB descriptor is within the range */ + if ((psubdesc < start) || (psubdesc >= end)) + return (NULL); /* out of range, or EOD */ + + /* check start of the second next USB descriptor, if any */ + desc_next = psubdesc + psubdesc[0]; + if ((desc_next < start) || (desc_next > end)) + return (NULL); /* out of range */ + + /* check minimum descriptor length */ + if (psubdesc[0] < 3) + return (NULL); /* too short descriptor */ + + return (psubdesc); /* return start of next descriptor */ +} + +/*------------------------------------------------------------------------* + * libusb20_me_get_1 - safety wrapper to read out one byte + *------------------------------------------------------------------------*/ +uint8_t +libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset) +{ + if (offset < ie->len) { + return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset))); + } + return (0); +} + +/*------------------------------------------------------------------------* + * libusb20_me_get_2 - safety wrapper to read out one word + *------------------------------------------------------------------------*/ +uint16_t +libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset) +{ + return (libusb20_me_get_1(ie, offset) | + (libusb20_me_get_1(ie, offset + 1) << 8)); +} + +/*------------------------------------------------------------------------* + * libusb20_me_encode - encode a message structure + * + * Description of parameters: + * "len" - maximum length of output buffer + * "ptr" - pointer to output buffer. If NULL, no data will be written + * "pd" - source structure + * + * Return values: + * 0..65535 - Number of bytes used, limited by the "len" input parameter. + *------------------------------------------------------------------------*/ +uint16_t +libusb20_me_encode(void *ptr, uint16_t len, const void *pd) +{ + const uint8_t *pf; /* pointer to format data */ + uint8_t *buf; /* pointer to output buffer */ + + uint32_t pd_offset; /* decoded structure offset */ + uint16_t len_old; /* old length */ + uint16_t pd_count; /* decoded element count */ + uint8_t me; /* message element */ + + /* initialise */ + + len_old = len; + buf = ptr; + pd_offset = sizeof(void *); + pf = (*((struct libusb20_me_format *const *)pd))->format; + + /* scan */ + + while (1) { + + /* get information element */ + + me = (pf[0]) & LIBUSB20_ME_MASK; + pd_count = pf[1] | (pf[2] << 8); + pf += 3; + + /* encode the message element */ + + switch (me) { + case LIBUSB20_ME_INT8: + while (pd_count--) { + uint8_t temp; + + if (len < 1) /* overflow */ + goto done; + if (buf) { + temp = *((const uint8_t *) + LIBUSB20_ADD_BYTES(pd, pd_offset)); + buf[0] = temp; + buf += 1; + } + pd_offset += 1; + len -= 1; + } + break; + + case LIBUSB20_ME_INT16: + pd_offset = -((-pd_offset) & ~1); /* align */ + while (pd_count--) { + uint16_t temp; + + if (len < 2) /* overflow */ + goto done; + + if (buf) { + temp = *((const uint16_t *) + LIBUSB20_ADD_BYTES(pd, pd_offset)); + buf[1] = (temp >> 8) & 0xFF; + buf[0] = temp & 0xFF; + buf += 2; + } + pd_offset += 2; + len -= 2; + } + break; + + case LIBUSB20_ME_INT32: + pd_offset = -((-pd_offset) & ~3); /* align */ + while (pd_count--) { + uint32_t temp; + + if (len < 4) /* overflow */ + goto done; + if (buf) { + temp = *((const uint32_t *) + LIBUSB20_ADD_BYTES(pd, pd_offset)); + buf[3] = (temp >> 24) & 0xFF; + buf[2] = (temp >> 16) & 0xFF; + buf[1] = (temp >> 8) & 0xFF; + buf[0] = temp & 0xFF; + buf += 4; + } + pd_offset += 4; + len -= 4; + } + break; + + case LIBUSB20_ME_INT64: + pd_offset = -((-pd_offset) & ~7); /* align */ + while (pd_count--) { + uint64_t temp; + + if (len < 8) /* overflow */ + goto done; + if (buf) { + + temp = *((const uint64_t *) + LIBUSB20_ADD_BYTES(pd, pd_offset)); + buf[7] = (temp >> 56) & 0xFF; + buf[6] = (temp >> 48) & 0xFF; + buf[5] = (temp >> 40) & 0xFF; + buf[4] = (temp >> 32) & 0xFF; + buf[3] = (temp >> 24) & 0xFF; + buf[2] = (temp >> 16) & 0xFF; + buf[1] = (temp >> 8) & 0xFF; + buf[0] = temp & 0xFF; + buf += 8; + } + pd_offset += 8; + len -= 8; + } + break; + + case LIBUSB20_ME_STRUCT: + pd_offset = -((-pd_offset) & + ~(LIBUSB20_ME_STRUCT_ALIGN - 1)); /* align */ + while (pd_count--) { + void *src_ptr; + uint16_t src_len; + struct libusb20_me_struct *ps; + + ps = LIBUSB20_ADD_BYTES(pd, pd_offset); + + switch (ps->type) { + case LIBUSB20_ME_IS_RAW: + src_len = ps->len; + src_ptr = ps->ptr; + break; + + case LIBUSB20_ME_IS_ENCODED: + if (ps->len == 0) { + /* + * Length is encoded + * in the data itself + * and should be + * correct: + */ + ps->len = 0xFFFF; + } + src_len = libusb20_me_get_1(pd, 0); + src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1); + if (src_len == 0xFF) { + /* length is escaped */ + src_len = libusb20_me_get_2(pd, 1); + src_ptr = + LIBUSB20_ADD_BYTES(ps->ptr, 3); + } + break; + + case LIBUSB20_ME_IS_DECODED: + /* reserve 3 length bytes */ + src_len = libusb20_me_encode(NULL, + 0xFFFF - 3, ps->ptr); + src_ptr = NULL; + break; + + default: /* empty structure */ + src_len = 0; + src_ptr = NULL; + break; + } + + if (src_len > 0xFE) { + if (src_len > (0xFFFF - 3)) + /* overflow */ + goto done; + + if (len < (src_len + 3)) + /* overflow */ + goto done; + + if (buf) { + buf[0] = 0xFF; + buf[1] = (src_len & 0xFF); + buf[2] = (src_len >> 8) & 0xFF; + buf += 3; + } + len -= (src_len + 3); + } else { + if (len < (src_len + 1)) + /* overflow */ + goto done; + + if (buf) { + buf[0] = (src_len & 0xFF); + buf += 1; + } + len -= (src_len + 1); + } + + /* check for buffer and non-zero length */ + + if (buf && src_len) { + if (ps->type == LIBUSB20_ME_IS_DECODED) { + /* + * Repeat encode + * procedure - we have + * room for the + * complete structure: + */ + (void) libusb20_me_encode(buf, + 0xFFFF - 3, ps->ptr); + } else { + bcopy(src_ptr, buf, src_len); + } + buf += src_len; + } + pd_offset += sizeof(struct libusb20_me_struct); + } + break; + + default: + goto done; + } + } +done: + return (len_old - len); +} + +/*------------------------------------------------------------------------* + * libusb20_me_decode - decode a message into a decoded structure + * + * Description of parameters: + * "ptr" - message pointer + * "len" - message length + * "pd" - pointer to decoded structure + * + * Returns: + * "0..65535" - number of bytes decoded, limited by "len" + *------------------------------------------------------------------------*/ +uint16_t +libusb20_me_decode(const void *ptr, uint16_t len, void *pd) +{ + const uint8_t *pf; /* pointer to format data */ + const uint8_t *buf; /* pointer to input buffer */ + + uint32_t pd_offset; /* decoded structure offset */ + uint16_t len_old; /* old length */ + uint16_t pd_count; /* decoded element count */ + uint8_t me; /* message element */ + + /* initialise */ + + len_old = len; + buf = ptr; + pd_offset = sizeof(void *); + pf = (*((struct libusb20_me_format **)pd))->format; + + /* scan */ + + while (1) { + + /* get information element */ + + me = (pf[0]) & LIBUSB20_ME_MASK; + pd_count = pf[1] | (pf[2] << 8); + pf += 3; + + /* decode the message element by type */ + + switch (me) { + case LIBUSB20_ME_INT8: + while (pd_count--) { + uint8_t temp; + + if (len < 1) { + len = 0; + temp = 0; + } else { + len -= 1; + temp = buf[0]; + buf++; + } + *((uint8_t *)LIBUSB20_ADD_BYTES(pd, + pd_offset)) = temp; + pd_offset += 1; + } + break; + + case LIBUSB20_ME_INT16: + pd_offset = -((-pd_offset) & ~1); /* align */ + while (pd_count--) { + uint16_t temp; + + if (len < 2) { + len = 0; + temp = 0; + } else { + len -= 2; + temp = buf[1] << 8; + temp |= buf[0]; + buf += 2; + } + *((uint16_t *)LIBUSB20_ADD_BYTES(pd, + pd_offset)) = temp; + pd_offset += 2; + } + break; + + case LIBUSB20_ME_INT32: + pd_offset = -((-pd_offset) & ~3); /* align */ + while (pd_count--) { + uint32_t temp; + + if (len < 4) { + len = 0; + temp = 0; + } else { + len -= 4; + temp = buf[3] << 24; + temp |= buf[2] << 16; + temp |= buf[1] << 8; + temp |= buf[0]; + buf += 4; + } + + *((uint32_t *)LIBUSB20_ADD_BYTES(pd, + pd_offset)) = temp; + pd_offset += 4; + } + break; + + case LIBUSB20_ME_INT64: + pd_offset = -((-pd_offset) & ~7); /* align */ + while (pd_count--) { + uint64_t temp; + + if (len < 8) { + len = 0; + temp = 0; + } else { + len -= 8; + temp = ((uint64_t)buf[7]) << 56; + temp |= ((uint64_t)buf[6]) << 48; + temp |= ((uint64_t)buf[5]) << 40; + temp |= ((uint64_t)buf[4]) << 32; + temp |= buf[3] << 24; + temp |= buf[2] << 16; + temp |= buf[1] << 8; + temp |= buf[0]; + buf += 8; + } + + *((uint64_t *)LIBUSB20_ADD_BYTES(pd, + pd_offset)) = temp; + pd_offset += 8; + } + break; + + case LIBUSB20_ME_STRUCT: + pd_offset = -((-pd_offset) & + ~(LIBUSB20_ME_STRUCT_ALIGN - 1)); /* align */ + while (pd_count--) { + uint16_t temp; + struct libusb20_me_struct *ps; + + ps = LIBUSB20_ADD_BYTES(pd, pd_offset); + + if (ps->type == LIBUSB20_ME_IS_ENCODED) { + /* + * Pre-store a de-constified + * pointer to the raw + * structure: + */ + ps->ptr = LIBUSB20_ADD_BYTES(buf, 0); + + /* + * Get the correct number of + * length bytes: + */ + if (len != 0) { + if (buf[0] == 0xFF) { + ps->len = 3; + } else { + ps->len = 1; + } + } else { + ps->len = 0; + } + } + /* get the structure length */ + + if (len != 0) { + if (buf[0] == 0xFF) { + if (len < 3) { + len = 0; + temp = 0; + } else { + len -= 3; + temp = buf[1] | + (buf[2] << 8); + buf += 3; + } + } else { + len -= 1; + temp = buf[0]; + buf += 1; + } + } else { + len = 0; + temp = 0; + } + /* check for invalid length */ + + if (temp > len) { + len = 0; + temp = 0; + } + /* check wanted structure type */ + + switch (ps->type) { + case LIBUSB20_ME_IS_ENCODED: + /* check for zero length */ + if (temp == 0) { + /* + * The pointer must + * be valid: + */ + ps->ptr = LIBUSB20_ADD_BYTES( + libusb20_me_encode_empty, 0); + ps->len = 1; + } else { + ps->len += temp; + } + break; + + case LIBUSB20_ME_IS_RAW: + /* update length and pointer */ + ps->len = temp; + ps->ptr = LIBUSB20_ADD_BYTES(buf, 0); + break; + + case LIBUSB20_ME_IS_EMPTY: + case LIBUSB20_ME_IS_DECODED: + /* check for non-zero length */ + if (temp != 0) { + /* update type */ + ps->type = LIBUSB20_ME_IS_DECODED; + ps->len = 0; + /* + * Recursivly decode + * the next structure + */ + (void) libusb20_me_decode(buf, + temp, ps->ptr); + } else { + /* update type */ + ps->type = LIBUSB20_ME_IS_EMPTY; + ps->len = 0; + } + break; + + default: + /* + * nothing to do - should + * not happen + */ + ps->ptr = NULL; + ps->len = 0; + break; + } + buf += temp; + len -= temp; + pd_offset += sizeof(struct libusb20_me_struct); + } + break; + + default: + goto done; + } + } +done: + return (len_old - len); +} diff --git a/lib/libusb/libusb20_desc.h b/lib/libusb/libusb20_desc.h new file mode 100644 index 000000000000..0f7c9294ebc8 --- /dev/null +++ b/lib/libusb/libusb20_desc.h @@ -0,0 +1,608 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2007-2008 Daniel Drake. All rights reserved. + * Copyright (c) 2001 Johannes Erdfelt. 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. + */ + +/* + * NOTE: This file contains the definition of some standard USB + * structures. All structures which name ends by *DECODED use host byte + * order. + */ + +/* + * NOTE: This file uses a lot of macros. If you want to see what the + * macros become when they are expanded then run the following + * commands from your shell: + * + * cpp libusb20_desc.h > temp.h + * indent temp.h + * less temp.h + */ + +#ifndef _LIBUSB20_DESC_H_ +#define _LIBUSB20_DESC_H_ + +#ifndef LIBUSB_GLOBAL_INCLUDE_FILE +#include <stdint.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +}; /* style */ + +#endif +/* basic macros */ + +#define LIBUSB20__NOT(...) __VA_ARGS__ +#define LIBUSB20_NOT(arg) LIBUSB20__NOT(LIBUSB20_YES arg(() LIBUSB20_NO)) +#define LIBUSB20_YES(...) __VA_ARGS__ +#define LIBUSB20_NO(...) +#define LIBUSB20_END(...) __VA_ARGS__ +#define LIBUSB20_MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define LIBUSB20_MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#define LIBUSB20_ADD_BYTES(ptr,off) \ + ((void *)(((const uint8_t *)(ptr)) + (off) - ((const uint8_t *)0))) + +/* basic message elements */ +enum { + LIBUSB20_ME_INT8, + LIBUSB20_ME_INT16, + LIBUSB20_ME_INT32, + LIBUSB20_ME_INT64, + LIBUSB20_ME_STRUCT, + LIBUSB20_ME_MAX, /* used to indicate end */ +}; + +/* basic message element modifiers */ +enum { + LIBUSB20_ME_IS_UNSIGNED = 0x00, + LIBUSB20_ME_IS_SIGNED = 0x80, + LIBUSB20_ME_MASK = 0x7F, +}; + +enum { + LIBUSB20_ME_IS_RAW, /* structure excludes length field + * (hardcoded value) */ + LIBUSB20_ME_IS_ENCODED, /* structure includes length field */ + LIBUSB20_ME_IS_EMPTY, /* no structure */ + LIBUSB20_ME_IS_DECODED, /* structure is recursive */ +}; + +/* basic helper structures and macros */ + +#define LIBUSB20_ME_STRUCT_ALIGN sizeof(void *) + +struct libusb20_me_struct { + void *ptr; /* data pointer */ + uint16_t len; /* defaults to zero */ + uint16_t type; /* defaults to LIBUSB20_ME_IS_EMPTY */ +} __aligned(LIBUSB20_ME_STRUCT_ALIGN); + +struct libusb20_me_format { + const uint8_t *format; /* always set */ + const char *desc; /* optionally set */ + const char *fields; /* optionally set */ +}; + +#define LIBUSB20_ME_STRUCT(n, field, arg, ismeta) \ + ismeta ( LIBUSB20_ME_STRUCT, 1, 0, ) \ + LIBUSB20_NOT(ismeta) ( struct libusb20_me_struct field; ) + +#define LIBUSB20_ME_STRUCT_ARRAY(n, field, arg, ismeta) \ + ismeta ( LIBUSB20_ME_STRUCT , (arg) & 0xFF, \ + ((arg) / 0x100) & 0xFF, ) \ + LIBUSB20_NOT(ismeta) ( struct libusb20_me_struct field [arg]; ) + +#define LIBUSB20_ME_INTEGER(n, field, ismeta, un, u, bits, a, size) \ + ismeta ( LIBUSB20_ME_INT##bits | \ + LIBUSB20_ME_IS_##un##SIGNED , \ + (size) & 0xFF, ((size) / 0x100) & 0xFF, ) \ + LIBUSB20_NOT(ismeta) ( u##int##bits##_t \ + __aligned((bits) / 8) field a; ) + +#define LIBUSB20_ME_UINT8_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 8, , 1) + +#define LIBUSB20_ME_UINT8_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 8, [arg], arg) + +#define LIBUSB20_ME_SINT8_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 8, , 1) + +#define LIBUSB20_ME_SINT8_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 8, [arg], arg) + +#define LIBUSB20_ME_UINT16_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 16, , 1) + +#define LIBUSB20_ME_UINT16_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 16, [arg], arg) + +#define LIBUSB20_ME_SINT16_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 16, , 1) + +#define LIBUSB20_ME_SINT16_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 16, [arg], arg) + +#define LIBUSB20_ME_UINT32_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 32, , 1) + +#define LIBUSB20_ME_UINT32_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 32, [arg], arg) + +#define LIBUSB20_ME_SINT32_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 32, , 1) + +#define LIBUSB20_ME_SINT32_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 32, [arg], arg) + +#define LIBUSB20_ME_UINT64_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 64, , 1) + +#define LIBUSB20_ME_UINT64_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 64, [arg], arg) + +#define LIBUSB20_ME_SINT64_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 64, , 1) + +#define LIBUSB20_ME_SINT64_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 64, [arg], arg) + +#define LIBUSB20_MAKE_DECODED_FIELD(n, type, field, arg) \ + LIBUSB20_ME_##type (n, field, arg, LIBUSB20_NO) + +#define LIBUSB20_MAKE_STRUCT(name) \ + extern const struct libusb20_me_format \ + name##_FORMAT[1]; \ + struct name##_DECODED { \ + const struct libusb20_me_format *name##_FORMAT; \ + name (LIBUSB20_MAKE_DECODED_FIELD,) \ + } + +#define LIBUSB20_MAKE_STRUCT_FORMAT(name) \ + const struct libusb20_me_format \ + name##_FORMAT[1] = {{ \ + .format = LIBUSB20_MAKE_FORMAT(name), \ + .desc = #name, \ + .fields = NULL, \ + }} + +#define LIBUSB20_MAKE_FORMAT_SUB(n, type, field, arg) \ + LIBUSB20_ME_##type (n, field, arg, LIBUSB20_YES) + +#define LIBUSB20_MAKE_FORMAT(what) (const uint8_t []) \ + { what (LIBUSB20_MAKE_FORMAT_SUB, ) LIBUSB20_ME_MAX, 0, 0 } + +#define LIBUSB20_INIT(what, ptr) do { \ + memset(ptr, 0, sizeof(*(ptr))); \ + (ptr)->what##_FORMAT = what##_FORMAT; \ +} while (0) + +#define LIBUSB20_DEVICE_DESC(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT16_T, bcdUSB, ) \ + m(n, UINT8_T, bDeviceClass, ) \ + m(n, UINT8_T, bDeviceSubClass, ) \ + m(n, UINT8_T, bDeviceProtocol, ) \ + m(n, UINT8_T, bMaxPacketSize0, ) \ + m(n, UINT16_T, idVendor, ) \ + m(n, UINT16_T, idProduct, ) \ + m(n, UINT16_T, bcdDevice, ) \ + m(n, UINT8_T, iManufacturer, ) \ + m(n, UINT8_T, iProduct, ) \ + m(n, UINT8_T, iSerialNumber, ) \ + m(n, UINT8_T, bNumConfigurations, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_DEVICE_DESC); + +#define LIBUSB20_ENDPOINT_DESC(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT8_T, bEndpointAddress, ) \ + m(n, UINT8_T, bmAttributes, ) \ + m(n, UINT16_T, wMaxPacketSize, ) \ + m(n, UINT8_T, bInterval, ) \ + m(n, UINT8_T, bRefresh, ) \ + m(n, UINT8_T, bSynchAddress, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_ENDPOINT_DESC); + +#define LIBUSB20_INTERFACE_DESC(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT8_T, bInterfaceNumber, ) \ + m(n, UINT8_T, bAlternateSetting, ) \ + m(n, UINT8_T, bNumEndpoints, ) \ + m(n, UINT8_T, bInterfaceClass, ) \ + m(n, UINT8_T, bInterfaceSubClass, ) \ + m(n, UINT8_T, bInterfaceProtocol, ) \ + m(n, UINT8_T, iInterface, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_INTERFACE_DESC); + +#define LIBUSB20_CONFIG_DESC(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT16_T, wTotalLength, ) \ + m(n, UINT8_T, bNumInterfaces, ) \ + m(n, UINT8_T, bConfigurationValue, ) \ + m(n, UINT8_T, iConfiguration, ) \ + m(n, UINT8_T, bmAttributes, ) \ + m(n, UINT8_T, bMaxPower, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_CONFIG_DESC); + +#define LIBUSB20_CONTROL_SETUP(m,n) \ + m(n, UINT8_T, bmRequestType, ) \ + m(n, UINT8_T, bRequest, ) \ + m(n, UINT16_T, wValue, ) \ + m(n, UINT16_T, wIndex, ) \ + m(n, UINT16_T, wLength, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_CONTROL_SETUP); + +#define LIBUSB20_SS_ENDPT_COMP_DESC(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT8_T, bMaxBurst, ) \ + m(n, UINT8_T, bmAttributes, ) \ + m(n, UINT16_T, wBytesPerInterval, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_SS_ENDPT_COMP_DESC); + +#define LIBUSB20_USB_20_DEVCAP_DESC(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT8_T, bDevCapabilityType, ) \ + m(n, UINT32_T, bmAttributes, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_USB_20_DEVCAP_DESC); + +#define LIBUSB20_SS_USB_DEVCAP_DESC(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT8_T, bDevCapabilityType, ) \ + m(n, UINT8_T, bmAttributes, ) \ + m(n, UINT16_T, wSpeedSupported, ) \ + m(n, UINT8_T, bFunctionalitySupport, ) \ + m(n, UINT8_T, bU1DevExitLat, ) \ + m(n, UINT16_T, wU2DevExitLat, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_SS_USB_DEVCAP_DESC); + +#ifndef bNumDeviceCapabilities +#define bNumDeviceCapabilities bNumDeviceCaps +#endif + +#define LIBUSB20_BOS_DESCRIPTOR(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT16_T, wTotalLength, ) \ + m(n, UINT8_T, bNumDeviceCaps, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_BOS_DESCRIPTOR); + +/* standard USB stuff */ + +/** \ingroup desc + * Device and/or Interface Class codes */ +enum libusb20_class_code { + /** In the context of a \ref LIBUSB20_DEVICE_DESC "device + * descriptor", this bDeviceClass value indicates that each + * interface specifies its own class information and all + * interfaces operate independently. + */ + LIBUSB20_CLASS_PER_INTERFACE = 0, + + /** Audio class */ + LIBUSB20_CLASS_AUDIO = 1, + + /** Communications class */ + LIBUSB20_CLASS_COMM = 2, + + /** Human Interface Device class */ + LIBUSB20_CLASS_HID = 3, + + /** Printer dclass */ + LIBUSB20_CLASS_PRINTER = 7, + + /** Picture transfer protocol class */ + LIBUSB20_CLASS_PTP = 6, + + /** Mass storage class */ + LIBUSB20_CLASS_MASS_STORAGE = 8, + + /** Hub class */ + LIBUSB20_CLASS_HUB = 9, + + /** Data class */ + LIBUSB20_CLASS_DATA = 10, + + /** Class is vendor-specific */ + LIBUSB20_CLASS_VENDOR_SPEC = 0xff, +}; + +/** \ingroup desc + * Descriptor types as defined by the USB specification. */ +enum libusb20_descriptor_type { + /** Device descriptor. See LIBUSB20_DEVICE_DESC. */ + LIBUSB20_DT_DEVICE = 0x01, + + /** Configuration descriptor. See LIBUSB20_CONFIG_DESC. */ + LIBUSB20_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB20_DT_STRING = 0x03, + + /** Interface descriptor. See LIBUSB20_INTERFACE_DESC. */ + LIBUSB20_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See LIBUSB20_ENDPOINT_DESC. */ + LIBUSB20_DT_ENDPOINT = 0x05, + + /** HID descriptor */ + LIBUSB20_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB20_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB20_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB20_DT_HUB = 0x29, + + /** Binary Object Store, BOS */ + LIBUSB20_DT_BOS = 0x0f, + + /** Device Capability */ + LIBUSB20_DT_DEVICE_CAPABILITY = 0x10, + + /** SuperSpeed endpoint companion */ + LIBUSB20_DT_SS_ENDPOINT_COMPANION = 0x30, +}; + +/** \ingroup desc + * Device capability types as defined by the USB specification. */ +enum libusb20_device_capability_type { + LIBUSB20_WIRELESS_USB_DEVICE_CAPABILITY = 0x1, + LIBUSB20_USB_2_0_EXTENSION_DEVICE_CAPABILITY = 0x2, + LIBUSB20_SS_USB_DEVICE_CAPABILITY = 0x3, + LIBUSB20_CONTAINER_ID_DEVICE_CAPABILITY = 0x4, +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB20_DT_DEVICE_SIZE 18 +#define LIBUSB20_DT_CONFIG_SIZE 9 +#define LIBUSB20_DT_INTERFACE_SIZE 9 +#define LIBUSB20_DT_ENDPOINT_SIZE 7 +#define LIBUSB20_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB20_DT_HUB_NONVAR_SIZE 7 +#define LIBUSB20_DT_SS_ENDPOINT_COMPANION_SIZE 6 +#define LIBUSB20_DT_BOS_SIZE 5 +#define LIBUSB20_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE 7 +#define LIBUSB20_SS_USB_DEVICE_CAPABILITY_SIZE 10 + +#define LIBUSB20_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB20_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup desc + * Endpoint direction. Values for bit 7 of the + * \ref LIBUSB20_ENDPOINT_DESC::bEndpointAddress "endpoint address" scheme. + */ +enum libusb20_endpoint_direction { + /** In: device-to-host */ + LIBUSB20_ENDPOINT_IN = 0x80, + + /** Out: host-to-device */ + LIBUSB20_ENDPOINT_OUT = 0x00, +}; + +#define LIBUSB20_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref LIBUSB20_ENDPOINT_DESC::bmAttributes "endpoint attributes" field. + */ +enum libusb20_transfer_type { + /** Control endpoint */ + LIBUSB20_TRANSFER_TYPE_CONTROL = 0, + + /** Isochronous endpoint */ + LIBUSB20_TRANSFER_TYPE_ISOCHRONOUS = 1, + + /** Bulk endpoint */ + LIBUSB20_TRANSFER_TYPE_BULK = 2, + + /** Interrupt endpoint */ + LIBUSB20_TRANSFER_TYPE_INTERRUPT = 3, +}; + +/** \ingroup misc + * Standard requests, as defined in table 9-3 of the USB2 specifications */ +enum libusb20_standard_request { + /** Request status of the specific recipient */ + LIBUSB20_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB20_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB20_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB20_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB20_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB20_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB20_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB20_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified + * interface */ + LIBUSB20_REQUEST_GET_INTERFACE = 0x0A, + + /** Select an alternate interface for the specified interface */ + LIBUSB20_REQUEST_SET_INTERFACE = 0x0B, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB20_REQUEST_SYNCH_FRAME = 0x0C, + + /** Set U1 and U2 system exit latency */ + LIBUSB20_REQUEST_SET_SEL = 0x30, + + /** Set isochronous delay */ + LIBUSB20_REQUEST_SET_ISOCH_DELAY = 0x31, +}; + +/** \ingroup misc + * Request type bits of the + * \ref libusb20_control_setup::bmRequestType "bmRequestType" field in + * control transfers. */ +enum libusb20_request_type { + /** Standard */ + LIBUSB20_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB20_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB20_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB20_REQUEST_TYPE_RESERVED = (0x03 << 5), +}; + +/** \ingroup misc + * Recipient bits of the + * \ref libusb20_control_setup::bmRequestType "bmRequestType" field in + * control transfers. Values 4 through 31 are reserved. */ +enum libusb20_request_recipient { + /** Device */ + LIBUSB20_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB20_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB20_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB20_RECIPIENT_OTHER = 0x03, +}; + +#define LIBUSB20_ISO_SYNC_TYPE_MASK 0x0C + +/** \ingroup desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 + * of the \ref LIBUSB20_ENDPOINT_DESC::bmAttributes "bmAttributes" + * field in LIBUSB20_ENDPOINT_DESC. + */ +enum libusb20_iso_sync_type { + /** No synchronization */ + LIBUSB20_ISO_SYNC_TYPE_NONE = 0, + + /** Asynchronous */ + LIBUSB20_ISO_SYNC_TYPE_ASYNC = 1, + + /** Adaptive */ + LIBUSB20_ISO_SYNC_TYPE_ADAPTIVE = 2, + + /** Synchronous */ + LIBUSB20_ISO_SYNC_TYPE_SYNC = 3, +}; + +#define LIBUSB20_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref LIBUSB20_ENDPOINT_DESC::bmAttributes "bmAttributes" field in + * LIBUSB20_ENDPOINT_DESC. + */ +enum libusb20_iso_usage_type { + /** Data endpoint */ + LIBUSB20_ISO_USAGE_TYPE_DATA = 0, + + /** Feedback endpoint */ + LIBUSB20_ISO_USAGE_TYPE_FEEDBACK = 1, + + /** Implicit feedback Data endpoint */ + LIBUSB20_ISO_USAGE_TYPE_IMPLICIT = 2, +}; + +struct libusb20_endpoint { + struct LIBUSB20_ENDPOINT_DESC_DECODED desc; + struct libusb20_me_struct extra; +} __aligned(sizeof(void *)); + +struct libusb20_interface { + struct LIBUSB20_INTERFACE_DESC_DECODED desc; + struct libusb20_me_struct extra; + struct libusb20_interface *altsetting; + struct libusb20_endpoint *endpoints; + uint8_t num_altsetting; + uint8_t num_endpoints; +} __aligned(sizeof(void *)); + +struct libusb20_config { + struct LIBUSB20_CONFIG_DESC_DECODED desc; + struct libusb20_me_struct extra; + struct libusb20_interface *interface; + uint8_t num_interface; +} __aligned(sizeof(void *)); + +uint8_t libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset); +uint16_t libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset); +uint16_t libusb20_me_encode(void *ptr, uint16_t len, const void *pd); +uint16_t libusb20_me_decode(const void *ptr, uint16_t len, void *pd); +const uint8_t *libusb20_desc_foreach(const struct libusb20_me_struct *pdesc, const uint8_t *psubdesc); +struct libusb20_config *libusb20_parse_config_desc(const void *config_desc); + +#if 0 +{ /* style */ +#endif +#ifdef __cplusplus +} + +#endif + +#endif /* _LIBUSB20_DESC_H_ */ diff --git a/lib/libusb/libusb20_int.h b/lib/libusb/libusb20_int.h new file mode 100644 index 000000000000..4b0cef442c36 --- /dev/null +++ b/lib/libusb/libusb20_int.h @@ -0,0 +1,240 @@ +/*- + * 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. + */ + +/* + * This file describes internal structures. + */ + +#ifndef _LIBUSB20_INT_H_ +#define _LIBUSB20_INT_H_ + +struct libusb20_device; +struct libusb20_backend; +struct libusb20_transfer; +struct libusb20_quirk; + +union libusb20_session_data { + unsigned long session_data; + struct timespec tv; + uint32_t plugtime; +}; + +/* USB backend specific */ +typedef const char *(libusb20_get_backend_name_t)(void); +typedef int (libusb20_root_get_dev_quirk_t)(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); +typedef int (libusb20_root_get_quirk_name_t)(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); +typedef int (libusb20_root_add_dev_quirk_t)(struct libusb20_backend *pbe, struct libusb20_quirk *pq); +typedef int (libusb20_root_remove_dev_quirk_t)(struct libusb20_backend *pbe, struct libusb20_quirk *pq); +typedef int (libusb20_close_device_t)(struct libusb20_device *pdev); +typedef int (libusb20_dev_get_info_t)(struct libusb20_device *pdev, struct usb_device_info *pinfo); +typedef int (libusb20_dev_get_iface_desc_t)(struct libusb20_device *pdev, uint8_t iface_index, char *buf, uint8_t len); +typedef int (libusb20_init_backend_t)(struct libusb20_backend *pbe); +typedef int (libusb20_open_device_t)(struct libusb20_device *pdev, uint16_t transfer_count_max); +typedef void (libusb20_exit_backend_t)(struct libusb20_backend *pbe); +typedef int (libusb20_root_set_template_t)(struct libusb20_backend *pbe, int temp); +typedef int (libusb20_root_get_template_t)(struct libusb20_backend *pbe, int *ptemp); + +#define LIBUSB20_DEFINE(n,field) \ + libusb20_##field##_t *field; + +#define LIBUSB20_DECLARE(n,field) \ + /* .field = */ n##_##field, + +#define LIBUSB20_BACKEND(m,n) \ + /* description of this backend */ \ + m(n, get_backend_name) \ + /* optional backend methods */ \ + m(n, init_backend) \ + m(n, exit_backend) \ + m(n, dev_get_info) \ + m(n, dev_get_iface_desc) \ + m(n, root_get_dev_quirk) \ + m(n, root_get_quirk_name) \ + m(n, root_add_dev_quirk) \ + m(n, root_remove_dev_quirk) \ + m(n, root_set_template) \ + m(n, root_get_template) \ + /* mandatory device methods */ \ + m(n, open_device) \ + m(n, close_device) \ + +struct libusb20_backend_methods { + LIBUSB20_BACKEND(LIBUSB20_DEFINE,) +}; + +/* USB dummy methods */ +typedef int (libusb20_dummy_int_t)(void); +typedef void (libusb20_dummy_void_t)(void); + +/* USB device specific */ +typedef int (libusb20_detach_kernel_driver_t)(struct libusb20_device *pdev, uint8_t iface_index); +typedef int (libusb20_do_request_sync_t)(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags); +typedef int (libusb20_get_config_desc_full_t)(struct libusb20_device *pdev, uint8_t **ppbuf, uint16_t *plen, uint8_t index); +typedef int (libusb20_get_config_index_t)(struct libusb20_device *pdev, uint8_t *pindex); +typedef int (libusb20_kernel_driver_active_t)(struct libusb20_device *pdev, uint8_t iface_index); +typedef int (libusb20_process_t)(struct libusb20_device *pdev); +typedef int (libusb20_reset_device_t)(struct libusb20_device *pdev); +typedef int (libusb20_set_power_mode_t)(struct libusb20_device *pdev, uint8_t power_mode); +typedef int (libusb20_get_power_mode_t)(struct libusb20_device *pdev, uint8_t *power_mode); +typedef int (libusb20_get_power_usage_t)(struct libusb20_device *pdev, uint16_t *power_usage); +typedef int (libusb20_get_stats_t)(struct libusb20_device *pdev, struct libusb20_device_stats *pstats); +typedef int (libusb20_set_alt_index_t)(struct libusb20_device *pdev, uint8_t iface_index, uint8_t alt_index); +typedef int (libusb20_set_config_index_t)(struct libusb20_device *pdev, uint8_t index); +typedef int (libusb20_check_connected_t)(struct libusb20_device *pdev); + + +/* USB transfer specific */ +typedef int (libusb20_tr_open_t)(struct libusb20_transfer *xfer, uint32_t MaxBufSize, uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id, uint8_t pre_scale); +typedef int (libusb20_tr_close_t)(struct libusb20_transfer *xfer); +typedef int (libusb20_tr_clear_stall_sync_t)(struct libusb20_transfer *xfer); +typedef void (libusb20_tr_submit_t)(struct libusb20_transfer *xfer); +typedef void (libusb20_tr_cancel_async_t)(struct libusb20_transfer *xfer); + +#define LIBUSB20_DEVICE(m,n) \ + m(n, detach_kernel_driver) \ + m(n, do_request_sync) \ + m(n, get_config_desc_full) \ + m(n, get_config_index) \ + m(n, kernel_driver_active) \ + m(n, process) \ + m(n, reset_device) \ + m(n, check_connected) \ + m(n, set_power_mode) \ + m(n, get_power_mode) \ + m(n, get_power_usage) \ + m(n, get_stats) \ + m(n, set_alt_index) \ + m(n, set_config_index) \ + m(n, tr_cancel_async) \ + m(n, tr_clear_stall_sync) \ + m(n, tr_close) \ + m(n, tr_open) \ + m(n, tr_submit) \ + +struct libusb20_device_methods { + LIBUSB20_DEVICE(LIBUSB20_DEFINE,) +}; + +struct libusb20_backend { + TAILQ_HEAD(, libusb20_device) usb_devs; + const struct libusb20_backend_methods *methods; +}; + +struct libusb20_transfer { + struct libusb20_device *pdev; /* the USB device we belong to */ + libusb20_tr_callback_t *callback; + void *priv_sc0; /* private client data */ + void *priv_sc1; /* private client data */ + /* + * Pointer to a list of buffer pointers: + */ + void **ppBuffer; + /* + * Pointer to frame lengths, which are updated to actual length + * after the USB transfer completes: + */ + uint32_t *pLength; + uint32_t maxTotalLength; + uint32_t maxFrames; /* total number of frames */ + uint32_t nFrames; /* total number of frames */ + uint32_t aFrames; /* actual number of frames */ + uint32_t timeout; + /* isochronous completion time in milliseconds */ + uint16_t timeComplete; + uint16_t trIndex; + uint16_t maxPacketLen; + uint8_t flags; /* see LIBUSB20_TRANSFER_XXX */ + uint8_t status; /* see LIBUSB20_TRANSFER_XXX */ + uint8_t is_opened; + uint8_t is_pending; + uint8_t is_cancel; + uint8_t is_draining; + uint8_t is_restart; +}; + +struct libusb20_device { + + /* device descriptor */ + struct LIBUSB20_DEVICE_DESC_DECODED ddesc; + + /* device timestamp */ + union libusb20_session_data session_data; + + /* our device entry */ + TAILQ_ENTRY(libusb20_device) dev_entry; + + /* device methods */ + const struct libusb20_device_methods *methods; + + /* backend methods */ + const struct libusb20_backend_methods *beMethods; + + /* list of USB transfers */ + struct libusb20_transfer *pTransfer; + + /* private backend data */ + void *privBeData; + + /* libUSB v0.1 and v1.0 compat data */ + void *privLuData; + + /* claimed interface */ + uint8_t claimed_interface; + + /* auto detach kernel driver */ + uint8_t auto_detach; + + /* device file handle */ + int file; + + /* device file handle (control transfers only) */ + int file_ctrl; + + /* debugging level */ + int debug; + + /* number of USB transfers */ + uint16_t nTransfer; + + uint8_t bus_number; + uint8_t device_address; + uint8_t usb_mode; + uint8_t usb_speed; + uint8_t is_opened; + uint8_t parent_address; + uint8_t parent_port; + uint8_t port_level; + + char usb_desc[96]; +#define LIBUSB20_DEVICE_PORT_PATH_MAX 32 + uint8_t port_path[LIBUSB20_DEVICE_PORT_PATH_MAX]; +}; + +extern const struct libusb20_backend_methods libusb20_ugen20_backend; +extern const struct libusb20_backend_methods libusb20_linux_backend; + +#endif /* _LIBUSB20_INT_H_ */ diff --git a/lib/libusb/libusb20_ugen20.c b/lib/libusb/libusb20_ugen20.c new file mode 100644 index 000000000000..11064d2644d6 --- /dev/null +++ b/lib/libusb/libusb20_ugen20.c @@ -0,0 +1,1086 @@ +/*- + * 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 LIBUSB_GLOBAL_INCLUDE_FILE +#include LIBUSB_GLOBAL_INCLUDE_FILE +#else +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <sys/queue.h> +#include <sys/types.h> +#endif + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usb_ioctl.h> + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" + +#ifndef IOUSB +#define IOUSB(a) a +#endif + +static libusb20_init_backend_t ugen20_init_backend; +static libusb20_open_device_t ugen20_open_device; +static libusb20_close_device_t ugen20_close_device; +static libusb20_get_backend_name_t ugen20_get_backend_name; +static libusb20_exit_backend_t ugen20_exit_backend; +static libusb20_dev_get_iface_desc_t ugen20_dev_get_iface_desc; +static libusb20_dev_get_info_t ugen20_dev_get_info; +static libusb20_root_get_dev_quirk_t ugen20_root_get_dev_quirk; +static libusb20_root_get_quirk_name_t ugen20_root_get_quirk_name; +static libusb20_root_add_dev_quirk_t ugen20_root_add_dev_quirk; +static libusb20_root_remove_dev_quirk_t ugen20_root_remove_dev_quirk; +static libusb20_root_set_template_t ugen20_root_set_template; +static libusb20_root_get_template_t ugen20_root_get_template; + +const struct libusb20_backend_methods libusb20_ugen20_backend = { + LIBUSB20_BACKEND(LIBUSB20_DECLARE, ugen20) +}; + +/* USB device specific */ +static libusb20_get_config_desc_full_t ugen20_get_config_desc_full; +static libusb20_get_config_index_t ugen20_get_config_index; +static libusb20_set_config_index_t ugen20_set_config_index; +static libusb20_set_alt_index_t ugen20_set_alt_index; +static libusb20_reset_device_t ugen20_reset_device; +static libusb20_check_connected_t ugen20_check_connected; +static libusb20_set_power_mode_t ugen20_set_power_mode; +static libusb20_get_power_mode_t ugen20_get_power_mode; +static libusb20_get_power_usage_t ugen20_get_power_usage; +static libusb20_get_stats_t ugen20_get_stats; +static libusb20_kernel_driver_active_t ugen20_kernel_driver_active; +static libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver; +static libusb20_do_request_sync_t ugen20_do_request_sync; +static libusb20_process_t ugen20_process; + +/* USB transfer specific */ +static libusb20_tr_open_t ugen20_tr_open; +static libusb20_tr_close_t ugen20_tr_close; +static libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync; +static libusb20_tr_submit_t ugen20_tr_submit; +static libusb20_tr_cancel_async_t ugen20_tr_cancel_async; + +static const struct libusb20_device_methods libusb20_ugen20_device_methods = { + LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20) +}; + +static const char * +ugen20_get_backend_name(void) +{ + return ("FreeBSD UGEN 2.0"); +} + +static uint32_t +ugen20_path_convert_one(const char **pp) +{ + const char *ptr; + uint32_t temp = 0; + + ptr = *pp; + + while ((*ptr >= '0') && (*ptr <= '9')) { + temp *= 10; + temp += (*ptr - '0'); + if (temp >= 1000000) { + /* catch overflow early */ + return (0xFFFFFFFF); + } + ptr++; + } + + if (*ptr == '.') { + /* skip dot */ + ptr++; + } + *pp = ptr; + + return (temp); +} + +static int +ugen20_enumerate(struct libusb20_device *pdev, const char *id) +{ + const char *tmp = id; + struct usb_device_descriptor ddesc; + struct usb_device_info devinfo; + struct usb_device_port_path udpp; + uint32_t plugtime; + char buf[64]; + int f; + int error; + + pdev->bus_number = ugen20_path_convert_one(&tmp); + pdev->device_address = ugen20_path_convert_one(&tmp); + + snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u", + pdev->bus_number, pdev->device_address); + + f = open(buf, O_RDWR); + if (f < 0) { + return (LIBUSB20_ERROR_OTHER); + } + if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + /* store when the device was plugged */ + pdev->session_data.plugtime = plugtime; + + if (ioctl(f, IOUSB(USB_GET_DEVICE_DESC), &ddesc)) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + LIBUSB20_INIT(LIBUSB20_DEVICE_DESC, &(pdev->ddesc)); + + libusb20_me_decode(&ddesc, sizeof(ddesc), &(pdev->ddesc)); + + if (pdev->ddesc.bNumConfigurations == 0) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } else if (pdev->ddesc.bNumConfigurations >= 8) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + if (ioctl(f, IOUSB(USB_GET_DEVICEINFO), &devinfo)) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + switch (devinfo.udi_mode) { + case USB_MODE_DEVICE: + pdev->usb_mode = LIBUSB20_MODE_DEVICE; + break; + default: + pdev->usb_mode = LIBUSB20_MODE_HOST; + break; + } + + switch (devinfo.udi_speed) { + case USB_SPEED_LOW: + pdev->usb_speed = LIBUSB20_SPEED_LOW; + break; + case USB_SPEED_FULL: + pdev->usb_speed = LIBUSB20_SPEED_FULL; + break; + case USB_SPEED_HIGH: + pdev->usb_speed = LIBUSB20_SPEED_HIGH; + break; + case USB_SPEED_VARIABLE: + pdev->usb_speed = LIBUSB20_SPEED_VARIABLE; + break; + case USB_SPEED_SUPER: + pdev->usb_speed = LIBUSB20_SPEED_SUPER; + break; + default: + pdev->usb_speed = LIBUSB20_SPEED_UNKNOWN; + break; + } + + /* get parent HUB index and port */ + + pdev->parent_address = devinfo.udi_hubindex; + pdev->parent_port = devinfo.udi_hubport; + + /* generate a nice description for printout */ + + snprintf(pdev->usb_desc, sizeof(pdev->usb_desc), + USB_GENERIC_NAME "%u.%u: <%s %s> at usbus%u", pdev->bus_number, + pdev->device_address, devinfo.udi_vendor, + devinfo.udi_product, pdev->bus_number); + + /* get device port path, if any */ + if (ioctl(f, IOUSB(USB_GET_DEV_PORT_PATH), &udpp) == 0 && + udpp.udp_port_level < LIBUSB20_DEVICE_PORT_PATH_MAX) { + memcpy(pdev->port_path, udpp.udp_port_no, udpp.udp_port_level); + pdev->port_level = udpp.udp_port_level; + } + + error = 0; +done: + close(f); + return (error); +} + +struct ugen20_urd_state { + struct usb_read_dir urd; + uint32_t nparsed; + int f; + uint8_t *ptr; + const char *src; + const char *dst; + uint8_t buf[256]; + uint8_t dummy_zero[1]; +}; + +static int +ugen20_readdir(struct ugen20_urd_state *st) +{ + ; /* style fix */ +repeat: + if (st->ptr == NULL) { + st->urd.urd_startentry += st->nparsed; + st->urd.urd_data = st->buf; + st->urd.urd_maxlen = sizeof(st->buf); + st->nparsed = 0; + + if (ioctl(st->f, IOUSB(USB_READ_DIR), &st->urd)) { + return (EINVAL); + } + st->ptr = st->buf; + } + if (st->ptr[0] == 0) { + if (st->nparsed) { + st->ptr = NULL; + goto repeat; + } else { + return (ENXIO); + } + } + st->src = (void *)(st->ptr + 1); + st->dst = st->src + strlen(st->src) + 1; + st->ptr = st->ptr + st->ptr[0]; + st->nparsed++; + + if ((st->ptr < st->buf) || + (st->ptr > st->dummy_zero)) { + /* invalid entry */ + return (EINVAL); + } + return (0); +} + +static int +ugen20_init_backend(struct libusb20_backend *pbe) +{ + struct ugen20_urd_state state; + struct libusb20_device *pdev; + + memset(&state, 0, sizeof(state)); + + state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY); + if (state.f < 0) + return (LIBUSB20_ERROR_OTHER); + + while (ugen20_readdir(&state) == 0) { + + if ((state.src[0] != 'u') || + (state.src[1] != 'g') || + (state.src[2] != 'e') || + (state.src[3] != 'n')) { + continue; + } + pdev = libusb20_dev_alloc(); + if (pdev == NULL) { + continue; + } + if (ugen20_enumerate(pdev, state.src + 4)) { + libusb20_dev_free(pdev); + continue; + } + /* put the device on the backend list */ + libusb20_be_enqueue_device(pbe, pdev); + } + close(state.f); + return (0); /* success */ +} + +static void +ugen20_tr_release(struct libusb20_device *pdev) +{ + struct usb_fs_uninit fs_uninit; + + if (pdev->nTransfer == 0) { + return; + } + /* release all pending USB transfers */ + if (pdev->privBeData != NULL) { + memset(&fs_uninit, 0, sizeof(fs_uninit)); + if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) { + /* ignore any errors of this kind */ + } + } + return; +} + +static int +ugen20_tr_renew(struct libusb20_device *pdev) +{ + struct usb_fs_init fs_init; + struct usb_fs_endpoint *pfse; + int error; + uint32_t size; + uint16_t nMaxTransfer; + + nMaxTransfer = pdev->nTransfer; + error = 0; + + if (nMaxTransfer == 0) { + goto done; + } + size = nMaxTransfer * sizeof(*pfse); + + if (pdev->privBeData == NULL) { + pfse = malloc(size); + if (pfse == NULL) { + error = LIBUSB20_ERROR_NO_MEM; + goto done; + } + pdev->privBeData = pfse; + } + /* reset endpoint data */ + memset(pdev->privBeData, 0, size); + + memset(&fs_init, 0, sizeof(fs_init)); + + fs_init.pEndpoints = pdev->privBeData; + fs_init.ep_index_max = nMaxTransfer; + + if (ioctl(pdev->file, IOUSB(USB_FS_INIT), &fs_init)) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } +done: + return (error); +} + +static int +ugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer) +{ + uint32_t plugtime; + char buf[64]; + int f; + int g; + int error; + + snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u", + pdev->bus_number, pdev->device_address); + + /* + * We need two file handles, one for the control endpoint and one + * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised + * kernel locking. + */ + g = open(buf, O_RDWR); + if (g < 0) { + return (LIBUSB20_ERROR_NO_DEVICE); + } + f = open(buf, O_RDWR); + if (f < 0) { + close(g); + return (LIBUSB20_ERROR_NO_DEVICE); + } + if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + /* check that the correct device is still plugged */ + if (pdev->session_data.plugtime != plugtime) { + error = LIBUSB20_ERROR_NO_DEVICE; + goto done; + } + /* need to set this before "tr_renew()" */ + pdev->file = f; + pdev->file_ctrl = g; + + /* renew all USB transfers */ + error = ugen20_tr_renew(pdev); + if (error) { + goto done; + } + /* set methods */ + pdev->methods = &libusb20_ugen20_device_methods; + +done: + if (error) { + if (pdev->privBeData) { + /* cleanup after "tr_renew()" */ + free(pdev->privBeData); + pdev->privBeData = NULL; + } + pdev->file = -1; + pdev->file_ctrl = -1; + close(f); + close(g); + } + return (error); +} + +static int +ugen20_close_device(struct libusb20_device *pdev) +{ + struct usb_fs_uninit fs_uninit; + + if (pdev->privBeData) { + memset(&fs_uninit, 0, sizeof(fs_uninit)); + if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) { + /* ignore this error */ + } + free(pdev->privBeData); + } + pdev->nTransfer = 0; + pdev->privBeData = NULL; + close(pdev->file); + close(pdev->file_ctrl); + pdev->file = -1; + pdev->file_ctrl = -1; + return (0); /* success */ +} + +static void +ugen20_exit_backend(struct libusb20_backend *pbe) +{ + return; /* nothing to do */ +} + +static int +ugen20_get_config_desc_full(struct libusb20_device *pdev, + uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index) +{ + struct usb_gen_descriptor gen_desc; + struct usb_config_descriptor cdesc; + uint8_t *ptr; + uint16_t len; + int error; + + /* make sure memory is initialised */ + memset(&cdesc, 0, sizeof(cdesc)); + memset(&gen_desc, 0, sizeof(gen_desc)); + + gen_desc.ugd_data = &cdesc; + gen_desc.ugd_maxlen = sizeof(cdesc); + gen_desc.ugd_config_index = cfg_index; + + error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc); + if (error) { + return (LIBUSB20_ERROR_OTHER); + } + len = UGETW(cdesc.wTotalLength); + if (len < sizeof(cdesc)) { + /* corrupt descriptor */ + return (LIBUSB20_ERROR_OTHER); + } + ptr = malloc(len); + if (!ptr) { + return (LIBUSB20_ERROR_NO_MEM); + } + + /* make sure memory is initialised */ + memset(ptr, 0, len); + + gen_desc.ugd_data = ptr; + gen_desc.ugd_maxlen = len; + + error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc); + if (error) { + free(ptr); + return (LIBUSB20_ERROR_OTHER); + } + /* make sure that the device doesn't fool us */ + memcpy(ptr, &cdesc, sizeof(cdesc)); + + *ppbuf = ptr; + *plen = len; + + return (0); /* success */ +} + +static int +ugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex) +{ + int temp; + + if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_CONFIG), &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + *pindex = temp; + + return (0); +} + +static int +ugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index) +{ + int temp = cfg_index; + + /* release all active USB transfers */ + ugen20_tr_release(pdev); + + if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_CONFIG), &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (ugen20_tr_renew(pdev)); +} + +static int +ugen20_set_alt_index(struct libusb20_device *pdev, + uint8_t iface_index, uint8_t alt_index) +{ + struct usb_alt_interface alt_iface; + + memset(&alt_iface, 0, sizeof(alt_iface)); + + alt_iface.uai_interface_index = iface_index; + alt_iface.uai_alt_index = alt_index; + + /* release all active USB transfers */ + ugen20_tr_release(pdev); + + if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_ALTINTERFACE), &alt_iface)) { + return (LIBUSB20_ERROR_OTHER); + } + return (ugen20_tr_renew(pdev)); +} + +static int +ugen20_reset_device(struct libusb20_device *pdev) +{ + int temp = 0; + + /* release all active USB transfers */ + ugen20_tr_release(pdev); + + if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICEENUMERATE), &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (ugen20_tr_renew(pdev)); +} + +static int +ugen20_check_connected(struct libusb20_device *pdev) +{ + uint32_t plugtime; + int error = 0; + + if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_PLUGTIME), &plugtime)) { + error = LIBUSB20_ERROR_NO_DEVICE; + goto done; + } + + if (pdev->session_data.plugtime != plugtime) { + error = LIBUSB20_ERROR_NO_DEVICE; + goto done; + } +done: + return (error); +} + +static int +ugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode) +{ + int temp; + + switch (power_mode) { + case LIBUSB20_POWER_OFF: + temp = USB_POWER_MODE_OFF; + break; + case LIBUSB20_POWER_ON: + temp = USB_POWER_MODE_ON; + break; + case LIBUSB20_POWER_SAVE: + temp = USB_POWER_MODE_SAVE; + break; + case LIBUSB20_POWER_SUSPEND: + temp = USB_POWER_MODE_SUSPEND; + break; + case LIBUSB20_POWER_RESUME: + temp = USB_POWER_MODE_RESUME; + break; + default: + return (LIBUSB20_ERROR_INVALID_PARAM); + } + if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_POWER_MODE), &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); +} + +static int +ugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode) +{ + int temp; + + if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_MODE), &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + switch (temp) { + case USB_POWER_MODE_OFF: + temp = LIBUSB20_POWER_OFF; + break; + case USB_POWER_MODE_ON: + temp = LIBUSB20_POWER_ON; + break; + case USB_POWER_MODE_SAVE: + temp = LIBUSB20_POWER_SAVE; + break; + case USB_POWER_MODE_SUSPEND: + temp = LIBUSB20_POWER_SUSPEND; + break; + case USB_POWER_MODE_RESUME: + temp = LIBUSB20_POWER_RESUME; + break; + default: + temp = LIBUSB20_POWER_ON; + break; + } + *power_mode = temp; + return (0); /* success */ +} + +static int +ugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage) +{ + int temp; + + if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_USAGE), &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + *power_usage = temp; + return (0); /* success */ +} + +static int +ugen20_get_stats(struct libusb20_device *pdev, struct libusb20_device_stats *pstats) +{ + struct usb_device_stats st; + + if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICESTATS), &st)) + return (LIBUSB20_ERROR_OTHER); + + memset(pstats, 0, sizeof(*pstats)); + + pstats->xfer_ok[0] = st.uds_requests_ok[0]; + pstats->xfer_ok[1] = st.uds_requests_ok[1]; + pstats->xfer_ok[2] = st.uds_requests_ok[2]; + pstats->xfer_ok[3] = st.uds_requests_ok[3]; + + pstats->xfer_fail[0] = st.uds_requests_fail[0]; + pstats->xfer_fail[1] = st.uds_requests_fail[1]; + pstats->xfer_fail[2] = st.uds_requests_fail[2]; + pstats->xfer_fail[3] = st.uds_requests_fail[3]; + + return (0); /* success */ +} + +static int +ugen20_kernel_driver_active(struct libusb20_device *pdev, + uint8_t iface_index) +{ + int temp = iface_index; + + if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_ACTIVE), &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); /* kernel driver is active */ +} + +static int +ugen20_detach_kernel_driver(struct libusb20_device *pdev, + uint8_t iface_index) +{ + int temp = iface_index; + + if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_DETACH), &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); /* kernel driver is detached */ +} + +static int +ugen20_do_request_sync(struct libusb20_device *pdev, + struct LIBUSB20_CONTROL_SETUP_DECODED *setup, + void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags) +{ + struct usb_ctl_request req; + + memset(&req, 0, sizeof(req)); + + req.ucr_data = data; + if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) { + req.ucr_flags |= USB_SHORT_XFER_OK; + } + if (libusb20_me_encode(&req.ucr_request, + sizeof(req.ucr_request), setup)) { + /* ignore */ + } + if (ioctl(pdev->file_ctrl, IOUSB(USB_DO_REQUEST), &req)) { + return (LIBUSB20_ERROR_OTHER); + } + if (pactlen) { + /* get actual length */ + *pactlen = req.ucr_actlen; + } + return (0); /* request was successful */ +} + +static int +ugen20_process(struct libusb20_device *pdev) +{ + struct usb_fs_complete temp; + struct usb_fs_endpoint *fsep; + struct libusb20_transfer *xfer; + + while (1) { + + if (ioctl(pdev->file, IOUSB(USB_FS_COMPLETE), &temp)) { + if (errno == EBUSY) { + break; + } else { + /* device detached */ + return (LIBUSB20_ERROR_OTHER); + } + } + fsep = pdev->privBeData; + xfer = pdev->pTransfer; + fsep += temp.ep_index; + xfer += temp.ep_index; + + /* update transfer status */ + + if (fsep->status == 0) { + xfer->aFrames = fsep->aFrames; + xfer->timeComplete = fsep->isoc_time_complete; + xfer->status = LIBUSB20_TRANSFER_COMPLETED; + } else if (fsep->status == USB_ERR_CANCELLED) { + xfer->aFrames = 0; + xfer->timeComplete = 0; + xfer->status = LIBUSB20_TRANSFER_CANCELLED; + } else if (fsep->status == USB_ERR_STALLED) { + xfer->aFrames = 0; + xfer->timeComplete = 0; + xfer->status = LIBUSB20_TRANSFER_STALL; + } else if (fsep->status == USB_ERR_TIMEOUT) { + xfer->aFrames = 0; + xfer->timeComplete = 0; + xfer->status = LIBUSB20_TRANSFER_TIMED_OUT; + } else { + xfer->aFrames = 0; + xfer->timeComplete = 0; + xfer->status = LIBUSB20_TRANSFER_ERROR; + } + libusb20_tr_callback_wrapper(xfer); + } + return (0); /* done */ +} + +static int +ugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize, + uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id, + uint8_t pre_scale) +{ + union { + struct usb_fs_open fs_open; + struct usb_fs_open_stream fs_open_stream; + } temp; + struct usb_fs_endpoint *fsep; + + if (pre_scale) + MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE; + + memset(&temp, 0, sizeof(temp)); + + fsep = xfer->pdev->privBeData; + fsep += xfer->trIndex; + + temp.fs_open.max_bufsize = MaxBufSize; + temp.fs_open.max_frames = MaxFrameCount; + temp.fs_open.ep_index = xfer->trIndex; + temp.fs_open.ep_no = ep_no; + + if (stream_id != 0) { + temp.fs_open_stream.stream_id = stream_id; + + if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN_STREAM), &temp.fs_open_stream)) + return (LIBUSB20_ERROR_INVALID_PARAM); + } else { + if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN), &temp.fs_open)) + return (LIBUSB20_ERROR_INVALID_PARAM); + } + /* maximums might have changed - update */ + xfer->maxFrames = temp.fs_open.max_frames; + + /* "max_bufsize" should be multiple of "max_packet_length" */ + xfer->maxTotalLength = temp.fs_open.max_bufsize; + xfer->maxPacketLen = temp.fs_open.max_packet_length; + + /* setup buffer and length lists using zero copy */ + fsep->ppBuffer = xfer->ppBuffer; + fsep->pLength = xfer->pLength; + + return (0); /* success */ +} + +static int +ugen20_tr_close(struct libusb20_transfer *xfer) +{ + struct usb_fs_close temp; + + memset(&temp, 0, sizeof(temp)); + + temp.ep_index = xfer->trIndex; + + if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLOSE), &temp)) { + return (LIBUSB20_ERROR_INVALID_PARAM); + } + return (0); /* success */ +} + +static int +ugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer) +{ + struct usb_fs_clear_stall_sync temp; + + memset(&temp, 0, sizeof(temp)); + + /* if the transfer is active, an error will be returned */ + + temp.ep_index = xfer->trIndex; + + if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLEAR_STALL_SYNC), &temp)) { + return (LIBUSB20_ERROR_INVALID_PARAM); + } + return (0); /* success */ +} + +static void +ugen20_tr_submit(struct libusb20_transfer *xfer) +{ + struct usb_fs_start temp; + struct usb_fs_endpoint *fsep; + + memset(&temp, 0, sizeof(temp)); + + fsep = xfer->pdev->privBeData; + fsep += xfer->trIndex; + + fsep->nFrames = xfer->nFrames; + fsep->flags = 0; + if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) { + fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK; + } + if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) { + fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK; + } + if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) { + fsep->flags |= USB_FS_FLAG_FORCE_SHORT; + } + if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) { + fsep->flags |= USB_FS_FLAG_CLEAR_STALL; + } + /* NOTE: The "fsep->timeout" variable is 16-bit. */ + if (xfer->timeout > 65535) + fsep->timeout = 65535; + else + fsep->timeout = xfer->timeout; + + temp.ep_index = xfer->trIndex; + + if (ioctl(xfer->pdev->file, IOUSB(USB_FS_START), &temp)) { + /* ignore any errors - should never happen */ + } + return; /* success */ +} + +static void +ugen20_tr_cancel_async(struct libusb20_transfer *xfer) +{ + struct usb_fs_stop temp; + + memset(&temp, 0, sizeof(temp)); + + temp.ep_index = xfer->trIndex; + + if (ioctl(xfer->pdev->file, IOUSB(USB_FS_STOP), &temp)) { + /* ignore any errors - should never happen */ + } + return; +} + +static int +ugen20_be_ioctl(uint32_t cmd, void *data) +{ + int f; + int error; + + f = open("/dev/" USB_DEVICE_NAME, O_RDONLY); + if (f < 0) + return (LIBUSB20_ERROR_OTHER); + error = ioctl(f, cmd, data); + if (error == -1) { + if (errno == EPERM) { + error = LIBUSB20_ERROR_ACCESS; + } else { + error = LIBUSB20_ERROR_OTHER; + } + } + close(f); + return (error); +} + +static int +ugen20_dev_get_iface_desc(struct libusb20_device *pdev, + uint8_t iface_index, char *buf, uint8_t len) +{ + struct usb_gen_descriptor ugd; + + memset(&ugd, 0, sizeof(ugd)); + + ugd.ugd_data = buf; + ugd.ugd_maxlen = len; + ugd.ugd_iface_index = iface_index; + + if (ioctl(pdev->file, IOUSB(USB_GET_IFACE_DRIVER), &ugd)) { + return (LIBUSB20_ERROR_INVALID_PARAM); + } + return (0); +} + +static int +ugen20_dev_get_info(struct libusb20_device *pdev, + struct usb_device_info *pinfo) +{ + if (ioctl(pdev->file, IOUSB(USB_GET_DEVICEINFO), pinfo)) { + return (LIBUSB20_ERROR_INVALID_PARAM); + } + return (0); +} + +static int +ugen20_root_get_dev_quirk(struct libusb20_backend *pbe, + uint16_t quirk_index, struct libusb20_quirk *pq) +{ + struct usb_gen_quirk q; + int error; + + memset(&q, 0, sizeof(q)); + + q.index = quirk_index; + + error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_GET), &q); + + if (error) { + if (errno == EINVAL) { + return (LIBUSB20_ERROR_NOT_FOUND); + } + } else { + pq->vid = q.vid; + pq->pid = q.pid; + pq->bcdDeviceLow = q.bcdDeviceLow; + pq->bcdDeviceHigh = q.bcdDeviceHigh; + strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname)); + } + return (error); +} + +static int +ugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index, + struct libusb20_quirk *pq) +{ + struct usb_gen_quirk q; + int error; + + memset(&q, 0, sizeof(q)); + + q.index = quirk_index; + + error = ugen20_be_ioctl(IOUSB(USB_QUIRK_NAME_GET), &q); + + if (error) { + if (errno == EINVAL) { + return (LIBUSB20_ERROR_NOT_FOUND); + } + } else { + strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname)); + } + return (error); +} + +static int +ugen20_root_add_dev_quirk(struct libusb20_backend *pbe, + struct libusb20_quirk *pq) +{ + struct usb_gen_quirk q; + int error; + + memset(&q, 0, sizeof(q)); + + q.vid = pq->vid; + q.pid = pq->pid; + q.bcdDeviceLow = pq->bcdDeviceLow; + q.bcdDeviceHigh = pq->bcdDeviceHigh; + strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname)); + + error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_ADD), &q); + if (error) { + if (errno == ENOMEM) { + return (LIBUSB20_ERROR_NO_MEM); + } + } + return (error); +} + +static int +ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe, + struct libusb20_quirk *pq) +{ + struct usb_gen_quirk q; + int error; + + memset(&q, 0, sizeof(q)); + + q.vid = pq->vid; + q.pid = pq->pid; + q.bcdDeviceLow = pq->bcdDeviceLow; + q.bcdDeviceHigh = pq->bcdDeviceHigh; + strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname)); + + error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_REMOVE), &q); + if (error) { + if (errno == EINVAL) { + return (LIBUSB20_ERROR_NOT_FOUND); + } + } + return (error); +} + +static int +ugen20_root_set_template(struct libusb20_backend *pbe, int temp) +{ + return (ugen20_be_ioctl(IOUSB(USB_SET_TEMPLATE), &temp)); +} + +static int +ugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp) +{ + return (ugen20_be_ioctl(IOUSB(USB_GET_TEMPLATE), ptemp)); +} diff --git a/lib/libusb/libusb_global_linux.h b/lib/libusb/libusb_global_linux.h new file mode 100644 index 000000000000..1964f19c6e5e --- /dev/null +++ b/lib/libusb/libusb_global_linux.h @@ -0,0 +1,87 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2013 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 _LIBUSB_GLOBAL_LINUX_H_ +#define _LIBUSB_GLOBAL_LINUX_H_ + +#define _XOPEN_SOURCE +#define _BSD_SOURCE +#ifdef __linux__ +#define _POSIX_SOURCE +#endif +#define _POSIX_C_SOURCE 200809 + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <time.h> +#include <unistd.h> +#ifdef __linux__ +#include <alloca.h> +#endif +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <setjmp.h> +#include <signal.h> +#include <pthread.h> +#include <sys/queue.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <sys/time.h> +#include <dev/usb/usb_endian.h> +#include <dev/usb/usb_freebsd.h> + +#include <compat/linux/linux_ioctl.h> + +#define IOUSB(a) FBSD_L##a + +#ifndef __aligned +#define __aligned(x) __attribute__((__aligned__(x))) +#endif + +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif + +#ifndef strlcpy +#define strlcpy(d,s,len) do { \ + strncpy(d,s,len); \ + ((char *)d)[(len) - 1] = 0; \ +} while (0) +#endif + +#ifndef TAILQ_FOREACH_SAFE +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) +#endif + +#endif /* _LIBUSB_GLOBAL_LINUX_H_ */ diff --git a/lib/libusb/usb.h b/lib/libusb/usb.h new file mode 100644 index 000000000000..ab225060c55e --- /dev/null +++ b/lib/libusb/usb.h @@ -0,0 +1,315 @@ +/*- + * 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 _LIBUSB20_COMPAT_01_H_ +#define _LIBUSB20_COMPAT_01_H_ + +#ifndef LIBUSB_GLOBAL_INCLUDE_FILE +#include <stdint.h> +#include <sys/param.h> +#include <sys/endian.h> +#endif + +/* USB interface class codes */ + +#define USB_CLASS_PER_INTERFACE 0 +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_PTP 6 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_VENDOR_SPEC 0xff + +/* USB descriptor types */ + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +#define USB_DT_HID 0x21 +#define USB_DT_REPORT 0x22 +#define USB_DT_PHYSICAL 0x23 +#define USB_DT_HUB 0x29 + +/* USB descriptor type sizes */ + +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 +#define USB_DT_HUB_NONVAR_SIZE 7 + +/* USB descriptor header */ +struct usb_descriptor_header { + uint8_t bLength; + uint8_t bDescriptorType; +}; + +/* USB string descriptor */ +struct usb_string_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wData[1]; +}; + +/* USB HID descriptor */ +struct usb_hid_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdHID; + uint8_t bCountryCode; + uint8_t bNumDescriptors; + /* uint8_t bReportDescriptorType; */ + /* uint16_t wDescriptorLength; */ + /* ... */ +}; + +/* USB endpoint descriptor */ +#define USB_MAXENDPOINTS 32 +struct usb_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; +#define USB_ENDPOINT_ADDRESS_MASK 0x0f +#define USB_ENDPOINT_DIR_MASK 0x80 + uint8_t bmAttributes; +#define USB_ENDPOINT_TYPE_MASK 0x03 +#define USB_ENDPOINT_TYPE_CONTROL 0 +#define USB_ENDPOINT_TYPE_ISOCHRONOUS 1 +#define USB_ENDPOINT_TYPE_BULK 2 +#define USB_ENDPOINT_TYPE_INTERRUPT 3 + uint16_t wMaxPacketSize; + uint8_t bInterval; + uint8_t bRefresh; + uint8_t bSynchAddress; + + uint8_t *extra; /* Extra descriptors */ + int extralen; +}; + +/* USB interface descriptor */ +#define USB_MAXINTERFACES 32 +struct usb_interface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; + + struct usb_endpoint_descriptor *endpoint; + + uint8_t *extra; /* Extra descriptors */ + int extralen; +}; + +#define USB_MAXALTSETTING 128 /* Hard limit */ +struct usb_interface { + struct usb_interface_descriptor *altsetting; + + int num_altsetting; +}; + +/* USB configuration descriptor */ +#define USB_MAXCONFIG 8 +struct usb_config_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t MaxPower; + + struct usb_interface *interface; + + uint8_t *extra; /* Extra descriptors */ + int extralen; +}; + +/* USB device descriptor */ +struct usb_device_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +}; + +/* USB setup packet */ +struct usb_ctrl_setup { + uint8_t bRequestType; +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) +#define USB_ENDPOINT_IN 0x80 +#define USB_ENDPOINT_OUT 0x00 + uint8_t bRequest; +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +}; + +/* Error codes */ +#define USB_ERROR_BEGIN 500000 + +/* Byte swapping */ +#define USB_LE16_TO_CPU(x) le16toh(x) + +/* Data types */ +struct usb_device; +struct usb_bus; + +/* + * To maintain compatibility with applications already built with libusb, + * we must only add entries to the end of this structure. NEVER delete or + * move members and only change types if you really know what you're doing. + */ +struct usb_device { + struct usb_device *next; + struct usb_device *prev; + + char filename[PATH_MAX + 1]; + + struct usb_bus *bus; + + struct usb_device_descriptor descriptor; + struct usb_config_descriptor *config; + + void *dev; + + uint8_t devnum; + + uint8_t num_children; + struct usb_device **children; +}; + +struct usb_bus { + struct usb_bus *next; + struct usb_bus *prev; + + char dirname[PATH_MAX + 1]; + + struct usb_device *devices; + uint32_t location; + + struct usb_device *root_dev; +}; + +struct usb_dev_handle; +typedef struct usb_dev_handle usb_dev_handle; + +/* Variables */ +extern struct usb_bus *usb_busses; + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* style */ + +#endif + +/* Function prototypes from "libusb20_compat01.c" */ + +usb_dev_handle *usb_open(struct usb_device *dev); +int usb_close(usb_dev_handle * dev); +int usb_get_string(usb_dev_handle * dev, int index, int langid, char *buf, size_t buflen); +int usb_get_string_simple(usb_dev_handle * dev, int index, char *buf, size_t buflen); +int usb_get_descriptor_by_endpoint(usb_dev_handle * udev, int ep, uint8_t type, uint8_t index, void *buf, int size); +int usb_get_descriptor(usb_dev_handle * udev, uint8_t type, uint8_t index, void *buf, int size); +int usb_parse_descriptor(uint8_t *source, char *description, void *dest); +int usb_parse_configuration(struct usb_config_descriptor *config, uint8_t *buffer); +void usb_destroy_configuration(struct usb_device *dev); +void usb_fetch_and_parse_descriptors(usb_dev_handle * udev); +int usb_bulk_write(usb_dev_handle * dev, int ep, char *bytes, int size, int timeout); +int usb_bulk_read(usb_dev_handle * dev, int ep, char *bytes, int size, int timeout); +int usb_interrupt_write(usb_dev_handle * dev, int ep, char *bytes, int size, int timeout); +int usb_interrupt_read(usb_dev_handle * dev, int ep, char *bytes, int size, int timeout); +int usb_control_msg(usb_dev_handle * dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout); +int usb_set_configuration(usb_dev_handle * dev, int configuration); +int usb_claim_interface(usb_dev_handle * dev, int interface); +int usb_release_interface(usb_dev_handle * dev, int interface); +int usb_set_altinterface(usb_dev_handle * dev, int alternate); +int usb_resetep(usb_dev_handle * dev, unsigned int ep); +int usb_clear_halt(usb_dev_handle * dev, unsigned int ep); +int usb_reset(usb_dev_handle * dev); +int usb_check_connected(usb_dev_handle * dev); +const char *usb_strerror(void); +void usb_init(void); +void usb_set_debug(int level); +int usb_find_busses(void); +int usb_find_devices(void); +struct usb_device *usb_device(usb_dev_handle * dev); +struct usb_bus *usb_get_busses(void); +int usb_get_driver_np(usb_dev_handle * dev, int interface, char *name, int namelen); +int usb_detach_kernel_driver_np(usb_dev_handle * dev, int interface); + +#if 0 +{ /* style */ +#endif +#ifdef __cplusplus +} + +#endif + +#endif /* _LIBUSB20_COMPAT01_H_ */ |