From 1d96047ec1d7527d02fb23ab7626be54aef6a767 Mon Sep 17 00:00:00 2001 From: Markus Pfeiffer Date: Sun, 14 Oct 2012 11:00:18 +0000 Subject: [PATCH] Bring in USB4BSD userspace libraries and utilities. The following will now be built and installed if WANT_USB4BSD is defined. * libusb * usbconfig --- lib/Makefile | 3 + lib/libusb/Makefile | 219 +++++ lib/libusb/libusb.3 | 554 ++++++++++++ lib/libusb/libusb.h | 461 ++++++++++ lib/libusb/libusb01.c | 1015 +++++++++++++++++++++ lib/libusb/libusb10.c | 1522 ++++++++++++++++++++++++++++++++ lib/libusb/libusb10.h | 115 +++ lib/libusb/libusb10_desc.c | 498 +++++++++++ lib/libusb/libusb10_io.c | 738 ++++++++++++++++ lib/libusb/libusb20.3 | 1034 ++++++++++++++++++++++ lib/libusb/libusb20.c | 1320 +++++++++++++++++++++++++++ lib/libusb/libusb20.h | 309 +++++++ lib/libusb/libusb20_desc.c | 792 +++++++++++++++++ lib/libusb/libusb20_desc.h | 593 +++++++++++++ lib/libusb/libusb20_int.h | 238 +++++ lib/libusb/libusb20_ugen20.c | 1022 +++++++++++++++++++++ lib/libusb/usb.h | 313 +++++++ usr.sbin/Makefile | 7 +- usr.sbin/usbconfig/Makefile | 10 + usr.sbin/usbconfig/dump.c | 404 +++++++++ usr.sbin/usbconfig/dump.h | 40 + usr.sbin/usbconfig/usbconfig.8 | 100 +++ usr.sbin/usbconfig/usbconfig.c | 822 +++++++++++++++++ 23 files changed, 12127 insertions(+), 2 deletions(-) create mode 100644 lib/libusb/Makefile create mode 100644 lib/libusb/libusb.3 create mode 100644 lib/libusb/libusb.h create mode 100644 lib/libusb/libusb01.c create mode 100644 lib/libusb/libusb10.c create mode 100644 lib/libusb/libusb10.h create mode 100644 lib/libusb/libusb10_desc.c create mode 100644 lib/libusb/libusb10_io.c create mode 100644 lib/libusb/libusb20.3 create mode 100644 lib/libusb/libusb20.c create mode 100644 lib/libusb/libusb20.h create mode 100644 lib/libusb/libusb20_desc.c create mode 100644 lib/libusb/libusb20_desc.h create mode 100644 lib/libusb/libusb20_int.h create mode 100644 lib/libusb/libusb20_ugen20.c create mode 100644 lib/libusb/usb.h create mode 100644 usr.sbin/usbconfig/Makefile create mode 100644 usr.sbin/usbconfig/dump.c create mode 100644 usr.sbin/usbconfig/dump.h create mode 100644 usr.sbin/usbconfig/usbconfig.8 create mode 100644 usr.sbin/usbconfig/usbconfig.c diff --git a/lib/Makefile b/lib/Makefile index a25db71f83..b812092b3f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -85,6 +85,7 @@ SUBDIR= ${SUBDIR_ORDERED} \ libstand \ libtcplay \ libtelnet \ + ${_libusb} \ ${_libusbhid} \ libvgl \ libwrap \ @@ -116,8 +117,10 @@ _libnetgraph= libnetgraph .endif .if defined(WANT_USB4BSD) +_libusb= libusb _libusbhid= libu4bhid .else +_libusb= _libusbhid= libusbhid .endif diff --git a/lib/libusb/Makefile b/lib/libusb/Makefile new file mode 100644 index 0000000000..8d7861918d --- /dev/null +++ b/lib/libusb/Makefile @@ -0,0 +1,219 @@ +# +# $FreeBSD$ +# +# Makefile for the FreeBSD specific LibUSB 2.0 +# + +LIB= usb +SHLIB_MAJOR= 2 +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= + +WARNS?= 2 + +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_io.c + +.if defined(COMPAT_32BIT) +CFLAGS+= -DCOMPAT_32BIT +.endif + +.include + +# LibUSB v1.0 +MLINKS += libusb.3 libusb_init.3 +MLINKS += libusb.3 libusb_exit.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_device_address.3 +MLINKS += libusb.3 libusb_get_device_speed.3 +MLINKS += libusb.3 libusb_get_max_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_get_device_descriptor.3 +MLINKS += libusb.3 libsub_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_ascii.3 +MLINKS += libusb.3 libusb_parse_ss_endpoint_comp.3 +MLINKS += libusb.3 libusb_free_ss_endpoint_comp.3 +MLINKS += libusb.3 libusb_parse_bos_descriptor.3 +MLINKS += libusb.3 libusb_free_bos_descriptor.3 +MLINKS += libusb.3 libusb_alloc_transfer.3 +MLINKS += libusb.3 libusb_free_transfer.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_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.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 + +# 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_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_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 diff --git a/lib/libusb/libusb.3 b/lib/libusb/libusb.3 new file mode 100644 index 0000000000..4e4dd1012b --- /dev/null +++ b/lib/libusb/libusb.3 @@ -0,0 +1,554 @@ +.\" +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd November 9, 2011 +.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 / DEINITIALISATION +.Pp +.Ft int +.Fn libusb_init libusb_context **ctx +This function initialises libusb. +It must be called at the beginning +of the program, before other libusb routines are used. +This function returns 0 on success or LIBUSB_ERROR 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 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 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 . +.Pp +.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_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 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_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 int +.Fn libusb_get_configuration "libusb_device_handle *devh" "int *config" +Returns the bConfiguration 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 +.Sh USB DESCRIPTORS +.Pp +.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 libsub_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_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. +.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. +.Pp +.Sh USB ASYNCHRONOUS I/O +.Pp +.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. +.Pp +.Sh USB SYNCHRONOUS I/O +.Pp +.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. +.Pp +.Sh USB EVENTS +.Pp +.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 +.B 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_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 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 libusb_lock_event_waiters(). +This will +block until the timeout expires or a transfer completes or a thread releases +the event handling lock through 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 "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. +.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 desciptors, without checking if +another thread is already doing so. +Must be called with the event lock held. +.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. +.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" +Retrive 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. +.Sh LIBUSB VERSION 0.1 COMPATIBILITY +.Pp +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 +.Pa http://libusb.sourceforge.net/ +.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 0000000000..5a4d2df980 --- /dev/null +++ b/lib/libusb/libusb.h @@ -0,0 +1,461 @@ +/* $FreeBSD$ */ +/*- + * 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__ + +#include +#include + +#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_PTP = 6, + LIBUSB_CLASS_PRINTER = 7, + LIBUSB_CLASS_MASS_STORAGE = 8, + LIBUSB_CLASS_HUB = 9, + LIBUSB_CLASS_DATA = 10, + 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_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, +}; + +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, +}; + +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_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, +}; + +enum libusb_speed { + LIBUSB_SPEED_UNKNOWN = 0, + LIBUSB_SPEED_LOW = 1, + LIBUSB_SPEED_FULL = 2, + LIBUSB_SPEED_HIGH = 3, + LIBUSB_SPEED_SUPER = 4, +}; + +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_debug_level { + LIBUSB_DEBUG_NO=0, + LIBUSB_DEBUG_FUNCTION=1, + LIBUSB_DEBUG_TRANSFER=2, +}; + +/* libusb structures */ + +struct libusb_context; +struct libusb_device; +struct libusb_transfer; +struct libusb_device_handle; + +struct libusb_pollfd { + int fd; + short events; +}; + +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_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_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumDeviceCapabilities; + struct libusb_usb_2_0_device_capability_descriptor *usb_2_0_ext_cap; + struct libusb_ss_usb_device_capability_descriptor *ss_usb_cap; +} libusb_bos_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; + uint32_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; + void *os_priv; + 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 char *libusb_strerror(int code); +const char *libusb_error_name(int code); +int libusb_init(libusb_context ** context); +void libusb_exit(struct libusb_context *ctx); + +/* 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_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); +libusb_device *libusb_ref_device(libusb_device * dev); +void libusb_unref_device(libusb_device * dev); +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); +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_interface_alt_setting(libusb_device_handle * devh, int interface_number, int alternate_setting); + +/* 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_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); + +/* 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_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_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(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_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); +struct libusb_pollfd **libusb_get_pollfds(libusb_context * ctx); + +/* 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); + +#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 0000000000..17edb0e7df --- /dev/null +++ b/lib/libusb/libusb01.c @@ -0,0 +1,1015 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This file contains the emulation layer for LibUSB v0.1 from sourceforge. + */ + +#include + +#include +#include +#include + +#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 { + 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 0000000000..0be318a103 --- /dev/null +++ b/lib/libusb/libusb10.c @@ -0,0 +1,1522 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2009 Sylvestre Gallon. All rights reserved. + * Copyright (c) 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. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define libusb_device_handle libusb20_device + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" +#include "libusb.h" +#include "libusb10.h" + +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 */ + +void +libusb_set_debug(libusb_context *ctx, int level) +{ + ctx = GET_CONTEXT(ctx); + if (ctx) + 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); +} + +int +libusb_init(libusb_context **context) +{ + struct libusb_context *ctx; + char *debug; + int ret; + + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return (LIBUSB_ERROR_INVALID_PARAM); + + memset(ctx, 0, sizeof(*ctx)); + + debug = getenv("LIBUSB_DEBUG"); + if (debug != NULL) { + ctx->debug = atoi(debug); + if (ctx->debug != 0) + ctx->debug_fixed = 1; + } + TAILQ_INIT(&ctx->pollfds); + TAILQ_INIT(&ctx->tr_done); + + pthread_mutex_init(&ctx->ctx_lock, NULL); + pthread_cond_init(&ctx->ctx_cond, NULL); + + ctx->ctx_handler = NO_THREAD; + + ret = pipe(ctx->ctrl_pipe); + if (ret < 0) { + pthread_mutex_destroy(&ctx->ctx_lock); + pthread_cond_destroy(&ctx->ctx_cond); + free(ctx); + return (LIBUSB_ERROR_OTHER); + } + /* set non-blocking mode on the control pipe to avoid deadlock */ + libusb_set_nonblocking(ctx->ctrl_pipe[0]); + libusb_set_nonblocking(ctx->ctrl_pipe[1]); + + libusb10_add_pollfd(ctx, &ctx->ctx_poll, NULL, ctx->ctrl_pipe[0], 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_DEBUG_FUNCTION, "libusb_init complete"); + + return (0); +} + +void +libusb_exit(libusb_context *ctx) +{ + ctx = GET_CONTEXT(ctx); + + if (ctx == NULL) + return; + + /* XXX cleanup devices */ + + libusb10_remove_pollfd(ctx, &ctx->ctx_poll); + close(ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[1]); + pthread_mutex_destroy(&ctx->ctx_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; + struct libusb_device *dev; + int i; + + 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; + + /* link together the two structures */ + dev->os_priv = pdev; + pdev->privLuData = dev; + + (*list)[i] = libusb_ref_device(dev); + i++; + } + (*list)[i] = NULL; + + 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_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); + 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); +} + +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) { + 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; + uint8_t dummy; + 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, 16 * 4 /* number of endpoints */ ); + if (err) { + libusb_unref_device(dev); + return (LIBUSB_ERROR_NO_MEM); + } + 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 */ + dummy = 0; + err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); + if (err < (int)sizeof(dummy)) { + /* ignore error, if any */ + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open write failed!"); + } + *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_DEBUG_FUNCTION, "libusb_open_device_width_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_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid leave"); + return (pdev); +} + +void +libusb_close(struct libusb20_device *pdev) +{ + libusb_context *ctx; + struct libusb_device *dev; + uint8_t dummy; + int err; + + 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 */ + dummy = 0; + err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); + if (err < (int)sizeof(dummy)) { + /* ignore error, if any */ + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close write failed!"); + } +} + +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); + + CTX_LOCK(dev->ctx); + if (dev->claimed_interfaces & (1 << interface_number)) + err = LIBUSB_ERROR_BUSY; + + if (!err) + dev->claimed_interfaces |= (1 << interface_number); + CTX_UNLOCK(dev->ctx); + 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); + + CTX_LOCK(dev->ctx); + if (!(dev->claimed_interfaces & (1 << interface_number))) + err = LIBUSB_ERROR_NOT_FOUND; + + if (!err) + dev->claimed_interfaces &= ~(1 << interface_number); + CTX_UNLOCK(dev->ctx); + 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); +} + +static struct libusb20_transfer * +libusb10_get_transfer(struct libusb20_device *pdev, + uint8_t endpoint, uint8_t index) +{ + index &= 1; /* double buffering */ + + index |= (endpoint & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 4; + + if (endpoint & LIBUSB20_ENDPOINT_DIR_MASK) { + /* this is an IN endpoint */ + index |= 2; + } + return (libusb20_tr_get_pointer(pdev, 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); +} + +/* 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; + 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; + uint8_t flags; + + 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; + + flags = uxfer->flags; + + 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; + uint8_t dummy; + + 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_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(pxfer0, buffsize, maxframe, endpoint); + 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 */ + dummy = 0; + write(dev->ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); +} + +/* 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; + uint32_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; + + if (endpoint > 255) + return (LIBUSB_ERROR_INVALID_PARAM); + + dev = libusb_get_device(uxfer->dev_handle); + + DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "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 { + + /* 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_DEBUG_FUNCTION, "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; + uint32_t endpoint; + int retval; + + if (uxfer == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + /* check if not initialised */ + if (uxfer->dev_handle == NULL) + return (LIBUSB_ERROR_NOT_FOUND); + + endpoint = uxfer->endpoint; + + if (endpoint > 255) + return (LIBUSB_ERROR_INVALID_PARAM); + + dev = libusb_get_device(uxfer->dev_handle); + + DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer enter"); + + sxfer = (struct libusb_super_transfer *)( + (uint8_t *)uxfer - sizeof(*sxfer)); + + retval = 0; + + CTX_LOCK(dev->ctx); + + pxfer0 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 0); + pxfer1 = libusb10_get_transfer(uxfer->dev_handle, 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); + } 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); + libusb20_tr_stop(pxfer0); + /* make sure the queue doesn't stall */ + libusb10_submit_transfer_sub( + uxfer->dev_handle, endpoint); + } else if (libusb20_tr_get_priv_sc1(pxfer1) == sxfer) { + libusb10_complete_transfer(pxfer1, + sxfer, LIBUSB_TRANSFER_CANCELLED); + libusb20_tr_stop(pxfer1); + /* make sure the queue doesn't stall */ + libusb10_submit_transfer_sub( + uxfer->dev_handle, endpoint); + } else { + /* not started */ + retval = LIBUSB_ERROR_NOT_FOUND; + } + + CTX_UNLOCK(dev->ctx); + + DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer leave"); + + return (retval); +} + +UNEXPORTED void +libusb10_cancel_all_transfer(libusb_device *dev) +{ + /* TODO */ +} + +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) +{ + switch (code) { + case LIBUSB_SUCCESS: + return ("Success"); + case LIBUSB_ERROR_IO: + return ("I/O error"); + case LIBUSB_ERROR_INVALID_PARAM: + return ("Invalid parameter"); + case LIBUSB_ERROR_ACCESS: + return ("Permissions error"); + case LIBUSB_ERROR_NO_DEVICE: + return ("No device"); + case LIBUSB_ERROR_NOT_FOUND: + return ("Not found"); + case LIBUSB_ERROR_BUSY: + return ("Device busy"); + case LIBUSB_ERROR_TIMEOUT: + return ("Timeout"); + case LIBUSB_ERROR_OVERFLOW: + return ("Overflow"); + case LIBUSB_ERROR_PIPE: + return ("Pipe error"); + case LIBUSB_ERROR_INTERRUPTED: + return ("Interrupted"); + case LIBUSB_ERROR_NO_MEM: + return ("Out of memory"); + case LIBUSB_ERROR_NOT_SUPPORTED: + return ("Not supported"); + case LIBUSB_ERROR_OTHER: + return ("Other error"); + default: + return ("Unknown error"); + } +} + +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"); + } +} diff --git a/lib/libusb/libusb10.h b/lib/libusb/libusb10.h new file mode 100644 index 0000000000..d2a2bd7c15 --- /dev/null +++ b/lib/libusb/libusb10.h @@ -0,0 +1,115 @@ +/* $FreeBSD$ */ +/*- + * 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__ + +#include + +#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 DPRINTF(ctx, dbg, format, args...) do { \ + if ((ctx)->debug == dbg) { \ + switch (dbg) { \ + case LIBUSB_DEBUG_FUNCTION: \ + printf("LIBUSB_FUNCTION: " \ + format "\n", ## args); \ + break; \ + case LIBUSB_DEBUG_TRANSFER: \ + printf("LIBUSB_TRANSFER: " \ + format "\n", ## args); \ + break; \ + default: \ + break; \ + } \ + } \ +} while(0) + +/* 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; + uint8_t state; +#define LIBUSB_SUPER_XFER_ST_NONE 0 +#define LIBUSB_SUPER_XFER_ST_PEND 1 +}; + +struct libusb_context { + int debug; + int debug_fixed; + int ctrl_pipe[2]; + int tr_done_ref; + int tr_done_gen; + + pthread_mutex_t ctx_lock; + pthread_cond_t ctx_cond; + pthread_t ctx_handler; +#define NO_THREAD ((pthread_t)-1) + + TAILQ_HEAD(, libusb_super_pollfd) pollfds; + TAILQ_HEAD(, libusb_super_transfer) tr_done; + + 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; + + uint32_t claimed_interfaces; + + struct libusb_super_pollfd dev_poll; + + struct libusb_context *ctx; + + TAILQ_HEAD(, libusb_super_transfer) tr_head; + + struct libusb20_device *os_priv; +}; + +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); + +#endif /* __LIBUSB10_H__ */ diff --git a/lib/libusb/libusb10_desc.c b/lib/libusb/libusb10_desc.c new file mode 100644 index 0000000000..6d5822e8c3 --- /dev/null +++ b/lib/libusb/libusb10_desc.c @@ -0,0 +1,498 @@ +/* $FreeBSD$ */ +/*- + * 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. + */ + +#include + +#include +#include + +#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_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(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_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; + struct libusb_ss_usb_device_capability_descriptor *ss_cap; + + 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 = 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->bNumDeviceCapabilities = ((const uint8_t *)buf)[4]; + ptr->usb_2_0_ext_cap = NULL; + ptr->ss_usb_cap = NULL; + + dcap_20 = (void *)(ptr + 1); + ss_cap = (void *)(dcap_20 + 1); + } + if (dlen >= 3 && + ptr != NULL && + dtype == LIBUSB_DT_DEVICE_CAPABILITY) { + switch (((const uint8_t *)buf)[2]) { + case LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY: + if (ptr->usb_2_0_ext_cap != 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) + 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) + return (0); /* success */ + + return (LIBUSB_ERROR_IO); +} + +void +libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos) +{ + if (bos == NULL) + return; + + free(bos); +} diff --git a/lib/libusb/libusb10_io.c b/lib/libusb/libusb10_io.c new file mode 100644 index 0000000000..380e312bf6 --- /dev/null +++ b/lib/libusb/libusb10_io.c @@ -0,0 +1,738 @@ +/* $FreeBSD$ */ +/*- + * 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. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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_DEBUG_FUNCTION, "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 = 0; /* nothing to do */ + else + err = libusb20_dev_process(ppdev[i]); + + if (err) { + /* cancel all transfers - device is gone */ + libusb10_cancel_all_transfer(dev); + + /* remove USB device from polling loop */ + libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); + } + CTX_UNLOCK(ctx); + libusb_unref_device(dev); + CTX_LOCK(ctx); + + } else { + uint8_t dummy; + + while (1) { + if (read(fds[i].fd, &dummy, 1) != 1) + break; + } + } + } + + 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); + + 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_DEBUG_FUNCTION, "libusb_wait_for_event enter"); + + if (tv == NULL) { + pthread_cond_wait(&ctx->ctx_cond, + &ctx->ctx_lock); + return (0); + } + err = clock_gettime(CLOCK_REALTIME, &ts); + if (err < 0) + return (LIBUSB_ERROR_OTHER); + + 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); + + if (err == ETIMEDOUT) + return (1); + + return (0); +} + +int +libusb_handle_events_timeout(libusb_context *ctx, struct timeval *tv) +{ + int err; + + ctx = GET_CONTEXT(ctx); + + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_timeout enter"); + + libusb_lock_events(ctx); + + err = libusb_handle_events_locked(ctx, tv); + + libusb_unlock_events(ctx); + + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_timeout leave"); + + return (err); +} + +int +libusb_handle_events(libusb_context *ctx) +{ + return (libusb_handle_events_timeout(ctx, 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 { + libusb_wait_for_event(ctx, tv); + err = 0; + } + 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); +} + +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; +} + +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 (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 void +libusb10_do_transfer_cb(struct libusb_transfer *transfer) +{ + libusb_context *ctx; + int *pdone; + + ctx = GET_CONTEXT(NULL); + + DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "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; + volatile int complet; + 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 *)&complet; + xfer->callback = libusb10_do_transfer_cb; + complet = 0; + + if ((ret = libusb_submit_transfer(xfer)) < 0) { + libusb_free_transfer(xfer); + return (ret); + } + while (complet == 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 = GET_CONTEXT(NULL); + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_bulk_transfer enter"); + + ret = libusb10_do_transfer(devh, endpoint, data, length, transferred, + timeout, LIBUSB_TRANSFER_TYPE_BULK); + + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "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 = GET_CONTEXT(NULL); + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_interrupt_transfer enter"); + + ret = libusb10_do_transfer(devh, endpoint, data, length, transferred, + timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); + + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_interrupt_transfer leave"); + return (ret); +} + +uint8_t * +libusb_get_iso_packet_buffer(struct libusb_transfer *transfer, uint32_t index) +{ + uint8_t *ptr; + uint32_t n; + + if (transfer->num_iso_packets < 0) + return (NULL); + + if (index >= (uint32_t)transfer->num_iso_packets) + return (NULL); + + ptr = transfer->buffer; + if (ptr == NULL) + return (NULL); + + for (n = 0; n != index; n++) { + ptr += transfer->iso_packet_desc[n].length; + } + return (ptr); +} + +uint8_t * +libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, uint32_t index) +{ + uint8_t *ptr; + + if (transfer->num_iso_packets < 0) + return (NULL); + + if (index >= (uint32_t)transfer->num_iso_packets) + return (NULL); + + ptr = transfer->buffer; + if (ptr == NULL) + return (NULL); + + ptr += transfer->iso_packet_desc[0].length * index; + + 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_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; +} + diff --git a/lib/libusb/libusb20.3 b/lib/libusb/libusb20.3 new file mode 100644 index 0000000000..bd167b239d --- /dev/null +++ b/lib/libusb/libusb20.3 @@ -0,0 +1,1034 @@ +.\" +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd October 14, 2010 +.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" +.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_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_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 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 +. +.Pp +. +.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_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 +function 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 +.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 +. +.Pp +. +.Fn libusb20_dev_get_backend_name +returns a zero terminated string describing the backend used. +. +.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: " +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: " +. +.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 +.It LIBUSB20_POWER_OFF +.It LIBUSB20_POWER_ON +.It LIBUSB20_POWER_SAVE +.It LIBUSB20_POWER_SUSPEND +.It LIBUSB20_POWER_RESUME +.El +. +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_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 +.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 +.It LIBUSB20_SPEED_UNKNOWN +.It LIBUSB20_SPEED_LOW +.It LIBUSB20_SPEED_FULL +.It LIBUSB20_SPEED_HIGH +.It LIBUSB20_SPEED_VARIABLE +.It LIBUSB20_SPEED_SUPER +.El +. +.Pp +. +.Fn libusb20_dev_get_config_index +This function returns the currently select 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 +function 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 +This function 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 +This function 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 +.Pp +.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 +. +. +/dev/usb +.Sh SEE ALSO +.Xr usb 4 , +.Xr libusb 3 , +.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 0000000000..2aa38251d4 --- /dev/null +++ b/lib/libusb/libusb20.c @@ -0,0 +1,1320 @@ +/* $FreeBSD$ */ +/*- + * 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. + */ + +#include + +#include +#include +#include +#include +#include + +#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_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 = 0; + xfer->priv_sc1 = 0; + 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) +{ + 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; + 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); + + error = xfer->pdev->methods->tr_open(xfer, MaxBufSize, + MaxFrameCount, ep_no, pre_scale); + + 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] = libusb20_pass_ptr(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] = libusb20_pass_ptr(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] = libusb20_pass_ptr(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] = libusb20_pass_ptr(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] = libusb20_pass_ptr(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] = libusb20_pass_ptr(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; + + /* + * The following variable is only used by the libusb v0.1 + * compat layer: + */ + pdev->claimed_interface = 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_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; + + /* 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) { + return (error); + } + req.wLength = *(uint8_t *)ptr; /* bytes */ + if (req.wLength > len) { + /* partial string read */ + req.wLength = len; + } + error = libusb20_dev_request_sync(pdev, &req, + ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK); + + 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; + + 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 = 0 - 1; /* 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); +} + +/* 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) +{ + struct libusb20_backend *pbe; + +#ifdef __linux__ + pbe = libusb20_be_alloc(&libusb20_linux_backend); +#else + pbe = NULL; +#endif + return (pbe); +} + +struct libusb20_backend * +libusb20_be_alloc_ugen20(void) +{ + struct libusb20_backend *pbe; + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + pbe = libusb20_be_alloc(&libusb20_ugen20_backend); +#else + pbe = NULL; +#endif + return (pbe); +} + +struct libusb20_backend * +libusb20_be_alloc_default(void) +{ + struct libusb20_backend *pbe; + + pbe = libusb20_be_alloc_linux(); + if (pbe) { + return (pbe); + } + 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 0000000000..e4359fcb15 --- /dev/null +++ b/lib/libusb/libusb20.h @@ -0,0 +1,309 @@ +/* $FreeBSD$ */ +/*- + * 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_ + +#include +#include +#include + +#include +#include +#include + +#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, +}; + +/** \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]; +}; + +#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); +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_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_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 0000000000..0781067be7 --- /dev/null +++ b/lib/libusb/libusb20_desc.c @@ -0,0 +1,792 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#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; + uint8_t iface_no; + + ptr = config_desc; + if (ptr[1] != LIBUSB20_DT_CONFIG) { + return (NULL); /* not config descriptor */ + } + /* + * The first "bInterfaceNumber" should never have the value 0xff. + * Then it is corrupt. + */ + niface_no_alt = 0; + nendpoint = 0; + niface = 0; + iface_no = 0 - 1; + 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 inital config descriptor pointer! + */ + ptr = (void *)(lub_endpoint + nendpoint); + memcpy(LIBUSB20_ADD_BYTES(ptr, 0), config_desc, pcdesc.len); + pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0); + config_desc = LIBUSB20_ADD_BYTES(ptr, 0); + + /* init config structure */ + + ptr = config_desc; + + 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 = 0 - 1; + 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 */ + } + /* + * Sometimes USB devices have corrupt interface + * descriptors and we need to overwrite the provided + * interface number! + */ + 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 = 0 - 1; + } + 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, + 0 - 1 - 3, ps->ptr); + src_ptr = NULL; + break; + + default: /* empty structure */ + src_len = 0; + src_ptr = NULL; + break; + } + + if (src_len > 0xFE) { + if (src_len > (uint16_t)(0 - 1 - 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: + */ + uint16_t dummy; + + dummy = libusb20_me_encode(buf, + 0 - 1 - 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; + uint16_t dummy; + 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 + */ + dummy = 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 0000000000..a069ee96a6 --- /dev/null +++ b/lib/libusb/libusb20_desc.h @@ -0,0 +1,593 @@ +/* $FreeBSD$ */ +/*- + * 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_ + +#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); + +#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, bNumDeviceCapabilities, ) \ + +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, +}; + +/** \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 0000000000..bef4d02737 --- /dev/null +++ b/lib/libusb/libusb20_int.h @@ -0,0 +1,238 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This file describes internal structures. + */ + +#ifndef _LIBUSB20_INT_H_ +#define _LIBUSB20_INT_H_ + +#ifdef COMPAT_32BIT +#define libusb20_pass_ptr(ptr) ((uint64_t)(uintptr_t)(ptr)) +#else +#define libusb20_pass_ptr(ptr) (ptr) +#endif + +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_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, 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, 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: + */ +#ifdef COMPAT_32BIT + uint64_t *ppBuffer; +#else + void **ppBuffer; +#endif + /* + * 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; + + /* 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; + + char usb_desc[96]; +}; + +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 0000000000..37027173d4 --- /dev/null +++ b/lib/libusb/libusb20_ugen20.c @@ -0,0 +1,1022 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" + +#include +#include +#include + +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_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 (0 - 1); + } + 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; + 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, USB_GET_PLUGTIME, &plugtime)) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + /* store when the device was plugged */ + pdev->session_data.plugtime = plugtime; + + if (ioctl(f, 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, 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_product, + devinfo.udi_vendor, pdev->bus_number); + + 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 = libusb20_pass_ptr(st->buf); + st->urd.urd_maxlen = sizeof(st->buf); + st->nparsed = 0; + + if (ioctl(st->f, 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, 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 = libusb20_pass_ptr(pdev->privBeData); + fs_init.ep_index_max = nMaxTransfer; + + if (ioctl(pdev->file, 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, 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, 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 = libusb20_pass_ptr(&cdesc); + gen_desc.ugd_maxlen = sizeof(cdesc); + gen_desc.ugd_config_index = cfg_index; + + error = ioctl(pdev->file_ctrl, 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 = libusb20_pass_ptr(ptr); + gen_desc.ugd_maxlen = len; + + error = ioctl(pdev->file_ctrl, 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, 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, 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, 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, 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, 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, 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, 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_kernel_driver_active(struct libusb20_device *pdev, + uint8_t iface_index) +{ + int temp = iface_index; + + if (ioctl(pdev->file_ctrl, 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, USB_IFACE_DRIVER_DETACH, &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); /* kernel driver is active */ +} + +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 = libusb20_pass_ptr(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, USB_DO_REQUEST, &req)) { + return (LIBUSB20_ERROR_OTHER); + } + if (pactlen) { + /* get actual length */ + *pactlen = req.ucr_actlen; + } + return (0); /* kernel driver is active */ +} + +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, 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, uint8_t pre_scale) +{ + struct usb_fs_open 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.max_bufsize = MaxBufSize; + temp.max_frames = MaxFrameCount; + temp.ep_index = xfer->trIndex; + temp.ep_no = ep_no; + + if (ioctl(xfer->pdev->file, USB_FS_OPEN, &temp)) { + return (LIBUSB20_ERROR_INVALID_PARAM); + } + /* maximums might have changed - update */ + xfer->maxFrames = temp.max_frames; + + /* "max_bufsize" should be multiple of "max_packet_length" */ + xfer->maxTotalLength = temp.max_bufsize; + xfer->maxPacketLen = temp.max_packet_length; + + /* setup buffer and length lists using zero copy */ + fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer); + fsep->pLength = libusb20_pass_ptr(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, 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, 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, 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, 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 = libusb20_pass_ptr(buf); + ugd.ugd_maxlen = len; + ugd.ugd_iface_index = iface_index; + + if (ioctl(pdev->file, 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, 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(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(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(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(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(USB_SET_TEMPLATE, &temp)); +} + +static int +ugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp) +{ + return (ugen20_be_ioctl(USB_GET_TEMPLATE, ptemp)); +} diff --git a/lib/libusb/usb.h b/lib/libusb/usb.h new file mode 100644 index 0000000000..dc3959e17f --- /dev/null +++ b/lib/libusb/usb.h @@ -0,0 +1,313 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBUSB20_COMPAT_01_H_ +#define _LIBUSB20_COMPAT_01_H_ + +#include +#include + +#include + +/* 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_ */ diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index c685111083..c8bcec2928 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -124,6 +124,7 @@ SUBDIR= 802_11 \ traceroute6 \ trpt \ tzsetup \ + ${_usbconfig} \ ${_usbd} \ ${_usbdevs} \ vidcontrol \ @@ -142,8 +143,10 @@ SUBDIR= 802_11 \ zic .if !defined(WANT_USB4BSD) -_usbd= usbd -_usbdevs=usbdevs +_usbd= usbd +_usbdevs= usbdevs +.else +_usbconfig= usbconfig .endif .if !defined(NO_LPR) diff --git a/usr.sbin/usbconfig/Makefile b/usr.sbin/usbconfig/Makefile new file mode 100644 index 0000000000..0aa51fae17 --- /dev/null +++ b/usr.sbin/usbconfig/Makefile @@ -0,0 +1,10 @@ +# +# $FreeBSD$ +# +PROG= usbconfig +MAN= usbconfig.8 +SRCS= usbconfig.c dump.c +DPADD+= ${LIBUSB} +LDADD+= -lusb + +.include diff --git a/usr.sbin/usbconfig/dump.c b/usr.sbin/usbconfig/dump.c new file mode 100644 index 0000000000..7ee89dac99 --- /dev/null +++ b/usr.sbin/usbconfig/dump.c @@ -0,0 +1,404 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dump.h" + +#define DUMP0(n,type,field,...) dump_field(pdev, " ", #field, n->field); +#define DUMP1(n,type,field,...) dump_field(pdev, " ", #field, n->field); +#define DUMP2(n,type,field,...) dump_field(pdev, " ", #field, n->field); +#define DUMP3(n,type,field,...) dump_field(pdev, " ", #field, n->field); + +const char * +dump_mode(uint8_t value) +{ + if (value == LIBUSB20_MODE_HOST) + return ("HOST"); + return ("DEVICE"); +} + +const char * +dump_speed(uint8_t value) +{ + ; /* style fix */ + switch (value) { + case LIBUSB20_SPEED_LOW: + return ("LOW (1.5Mbps)"); + case LIBUSB20_SPEED_FULL: + return ("FULL (12Mbps)"); + case LIBUSB20_SPEED_HIGH: + return ("HIGH (480Mbps)"); + case LIBUSB20_SPEED_VARIABLE: + return ("VARIABLE (52-480Mbps)"); + case LIBUSB20_SPEED_SUPER: + return ("SUPER (5.0Gbps)"); + default: + break; + } + return ("UNKNOWN ()"); +} + +const char * +dump_power_mode(uint8_t value) +{ + ; /* style fix */ + switch (value) { + case LIBUSB20_POWER_OFF: + return ("OFF"); + case LIBUSB20_POWER_ON: + return ("ON"); + case LIBUSB20_POWER_SAVE: + return ("SAVE"); + case LIBUSB20_POWER_SUSPEND: + return ("SUSPEND"); + case LIBUSB20_POWER_RESUME: + return ("RESUME"); + default: + return ("UNKNOWN"); + } +} + +static void +dump_field(struct libusb20_device *pdev, const char *plevel, + const char *field, uint32_t value) +{ + uint8_t temp_string[256]; + + printf("%s%s = 0x%04x ", plevel, field, value); + + if (strlen(plevel) == 8) { + /* Endpoint Descriptor */ + + if (strcmp(field, "bEndpointAddress") == 0) { + if (value & 0x80) + printf(" \n"); + else + printf(" \n"); + return; + } + + if (strcmp(field, "bmAttributes") == 0) { + switch (value & 0x03) { + case 0: + printf(" \n"); + break; + case 1: + switch (value & 0x0C) { + case 0x00: + printf(" \n"); + break; + case 0x04: + printf(" \n"); + break; + case 0x08: + printf(" \n"); + break; + default: + printf(" \n"); + break; + } + break; + case 2: + printf(" \n"); + break; + default: + printf(" \n"); + break; + } + return; + } + } + + if ((field[0] == 'i') && (field[1] != 'd')) { + /* Indirect String Descriptor */ + if (value == 0) { + printf(" \n"); + return; + } + if (libusb20_dev_req_string_simple_sync(pdev, value, + temp_string, sizeof(temp_string))) { + printf(" \n"); + return; + } + printf(" <%s>\n", temp_string); + return; + } + + /* No additional information */ + printf("\n"); +} + +static void +dump_extra(struct libusb20_me_struct *str, const char *plevel) +{ + const uint8_t *ptr; + uint8_t x; + + ptr = NULL; + + while ((ptr = libusb20_desc_foreach(str, ptr))) { + printf("\n" "%sAdditional Descriptor\n\n", plevel); + printf("%sbLength = 0x%02x\n", plevel, ptr[0]); + printf("%sbDescriptorType = 0x%02x\n", plevel, ptr[1]); + if (ptr[0] > 1) + printf("%sbDescriptorSubType = 0x%02x\n", + plevel, ptr[2]); + printf("%s RAW dump: ", plevel); + for (x = 0; x != ptr[0]; x++) { + if ((x % 8) == 0) { + printf("\n%s 0x%02x | ", plevel, x); + } + printf("0x%02x%s", ptr[x], + (x != (ptr[0] - 1)) ? ", " : (x % 8) ? "\n" : ""); + } + printf("\n"); + } + return; +} + +static void +dump_endpoint(struct libusb20_device *pdev, + struct libusb20_endpoint *ep) +{ + struct LIBUSB20_ENDPOINT_DESC_DECODED *edesc; + + edesc = &ep->desc; + LIBUSB20_ENDPOINT_DESC(DUMP3, edesc); + dump_extra(&ep->extra, " " " " " "); + return; +} + +static void +dump_iface(struct libusb20_device *pdev, + struct libusb20_interface *iface) +{ + struct LIBUSB20_INTERFACE_DESC_DECODED *idesc; + uint8_t z; + + idesc = &iface->desc; + LIBUSB20_INTERFACE_DESC(DUMP2, idesc); + dump_extra(&iface->extra, " " " " " "); + + for (z = 0; z != iface->num_endpoints; z++) { + printf("\n Endpoint %u\n", z); + dump_endpoint(pdev, iface->endpoints + z); + } + return; +} + +void +dump_device_info(struct libusb20_device *pdev, uint8_t show_ifdrv) +{ + char buf[128]; + uint8_t n; + + printf("%s, cfg=%u md=%s spd=%s pwr=%s\n", + libusb20_dev_get_desc(pdev), + libusb20_dev_get_config_index(pdev), + dump_mode(libusb20_dev_get_mode(pdev)), + dump_speed(libusb20_dev_get_speed(pdev)), + dump_power_mode(libusb20_dev_get_power_mode(pdev))); + + if (!show_ifdrv) + return; + + for (n = 0; n != 255; n++) { + if (libusb20_dev_get_iface_desc(pdev, n, buf, sizeof(buf))) + break; + if (buf[0] == 0) + continue; + printf("ugen%u.%u.%u: %s\n", + libusb20_dev_get_bus_number(pdev), + libusb20_dev_get_address(pdev), n, buf); + } +} + +void +dump_be_quirk_names(struct libusb20_backend *pbe) +{ + struct libusb20_quirk q; + uint16_t x; + int error; + + memset(&q, 0, sizeof(q)); + + printf("\nDumping list of supported quirks:\n\n"); + + for (x = 0; x != 0xFFFF; x++) { + + error = libusb20_be_get_quirk_name(pbe, x, &q); + if (error) { + if (x == 0) { + printf("No quirk names - maybe the USB quirk " + "module has not been loaded.\n"); + } + break; + } + if (strcmp(q.quirkname, "UQ_NONE")) + printf("%s\n", q.quirkname); + } + printf("\n"); + return; +} + +void +dump_be_dev_quirks(struct libusb20_backend *pbe) +{ + struct libusb20_quirk q; + uint16_t x; + int error; + + memset(&q, 0, sizeof(q)); + + printf("\nDumping current device quirks:\n\n"); + + for (x = 0; x != 0xFFFF; x++) { + + error = libusb20_be_get_dev_quirk(pbe, x, &q); + if (error) { + if (x == 0) { + printf("No device quirks - maybe the USB quirk " + "module has not been loaded.\n"); + } + break; + } + if (strcmp(q.quirkname, "UQ_NONE")) { + printf("VID=0x%04x PID=0x%04x REVLO=0x%04x " + "REVHI=0x%04x QUIRK=%s\n", + q.vid, q.pid, q.bcdDeviceLow, + q.bcdDeviceHigh, q.quirkname); + } + } + printf("\n"); + return; +} + +void +dump_device_desc(struct libusb20_device *pdev) +{ + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + + ddesc = libusb20_dev_get_device_desc(pdev); + LIBUSB20_DEVICE_DESC(DUMP0, ddesc); + return; +} + +void +dump_config(struct libusb20_device *pdev, uint8_t all_cfg) +{ + struct LIBUSB20_CONFIG_DESC_DECODED *cdesc; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + struct libusb20_config *pcfg = NULL; + uint8_t cfg_index; + uint8_t cfg_index_end; + uint8_t x; + uint8_t y; + + ddesc = libusb20_dev_get_device_desc(pdev); + + if (all_cfg) { + cfg_index = 0; + cfg_index_end = ddesc->bNumConfigurations; + } else { + cfg_index = libusb20_dev_get_config_index(pdev); + cfg_index_end = cfg_index + 1; + } + + for (; cfg_index != cfg_index_end; cfg_index++) { + + pcfg = libusb20_dev_alloc_config(pdev, cfg_index); + if (!pcfg) { + continue; + } + printf("\n Configuration index %u\n\n", cfg_index); + cdesc = &(pcfg->desc); + LIBUSB20_CONFIG_DESC(DUMP1, cdesc); + dump_extra(&(pcfg->extra), " " " "); + + for (x = 0; x != pcfg->num_interface; x++) { + printf("\n Interface %u\n", x); + dump_iface(pdev, pcfg->interface + x); + printf("\n"); + for (y = 0; y != (pcfg->interface + x)->num_altsetting; y++) { + printf("\n Interface %u Alt %u\n", x, y + 1); + dump_iface(pdev, + (pcfg->interface + x)->altsetting + y); + printf("\n"); + } + } + printf("\n"); + free(pcfg); + } + return; +} + +void +dump_string_by_index(struct libusb20_device *pdev, uint8_t str_index) +{ + char *pbuf; + uint8_t n; + uint8_t len; + + pbuf = malloc(256); + if (pbuf == NULL) + err(1, "out of memory"); + + if (str_index == 0) { + /* language table */ + if (libusb20_dev_req_string_sync(pdev, + str_index, 0, pbuf, 256)) { + printf("STRING_0x%02x = \n", str_index); + } else { + printf("STRING_0x%02x = ", str_index); + len = (uint8_t)pbuf[0]; + for (n = 0; n != len; n++) { + printf("0x%02x%s", (uint8_t)pbuf[n], + (n != (len-1)) ? ", " : ""); + } + printf("\n"); + } + } else { + /* ordinary string */ + if (libusb20_dev_req_string_simple_sync(pdev, + str_index, pbuf, 256)) { + printf("STRING_0x%02x = \n", str_index); + } else { + printf("STRING_0x%02x = <%s>\n", str_index, pbuf); + } + } + free(pbuf); +} diff --git a/usr.sbin/usbconfig/dump.h b/usr.sbin/usbconfig/dump.h new file mode 100644 index 0000000000..581684a4cf --- /dev/null +++ b/usr.sbin/usbconfig/dump.h @@ -0,0 +1,40 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DUMP_H_ +#define _DUMP_H_ + +const char *dump_mode(uint8_t value); +const char *dump_speed(uint8_t value); +const char *dump_power_mode(uint8_t value); +void dump_string_by_index(struct libusb20_device *pdev, uint8_t index); +void dump_device_info(struct libusb20_device *pdev, uint8_t show_drv); +void dump_be_quirk_names(struct libusb20_backend *pbe); +void dump_be_dev_quirks(struct libusb20_backend *pbe); +void dump_device_desc(struct libusb20_device *pdev); +void dump_config(struct libusb20_device *pdev, uint8_t all_cfg); + +#endif /* _DUMP_H_ */ diff --git a/usr.sbin/usbconfig/usbconfig.8 b/usr.sbin/usbconfig/usbconfig.8 new file mode 100644 index 0000000000..ae6973711b --- /dev/null +++ b/usr.sbin/usbconfig/usbconfig.8 @@ -0,0 +1,100 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008-2010 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 January 6, 2010 +.Dt USBCONFIG 8 +.Os +.Sh NAME +.Nm usbconfig +.Nd configure the USB subsystem +.Sh SYNOPSIS +.Nm +.Op Fl u Ar unit +.Op Fl a Ar addr +.Op cmds... +.Nm +.Op Fl d Ar [ugen]. +.Op cmds... +.Sh DESCRIPTION +The +.Nm +utility is used to configure and dump information about the USB subsystem. +.Pp +The options are as follows: +.Bl -tag -width " " +.It Fl u Ar unit +Limit device range to USB devices connected to the given USBUS unit. +.It Fl a Ar addr +Limit device range to the given USB device index. +Should only be used in conjunction with the unit argument. +.It Fl d Ar [ugen]. +Limit device range to USB devices connected to the given unit and address. +The unit and address coordinates may be prefixed by the lowercased word "ugen". +.It Fl h +Show help and available commands. +.El +.Pp +When called without options, +.Nm +prints a list of all available USB devices. +.Sh EXAMPLES +Show information about the device on USB bus 1 at address 2: +.Pp +.Dl usbconfig -u 1 -a 2 dump_info +.Pp +Dump HID descriptor for device on USB bus 1 at address 2: +.Pp +.Dl usbconfig -u 1 -a 2 do_request 0x81 0x06 0x2200 0 0x100 +.Pp +Dump string descriptor at index Z for device on USB bus 1 at address 2: +.Pp +.Dl usbconfig -u 1 -a 2 dump_string Z +.Pp +Dump current configuration descriptor for device on USB bus 1 at address 2: +.Pp +.Dl usbconfig -u 1 -a 2 dump_curr_config_desc +.Pp +Dump device descriptor for device on USB bus 1 at address 2: +.Pp +.Dl usbconfig -u 1 -a 2 dump_device_desc +.Pp +Program the device on USB bus 1 at address 2 to suspend, resume, power off, go into power save, or power on: +.Pp +.Dl usbconfig -u 1 -a 2 suspend +.Dl usbconfig -u 1 -a 2 resume +.Dl usbconfig -u 1 -a 2 power_off +.Dl usbconfig -u 1 -a 2 power_save +.Dl usbconfig -u 1 -a 2 power_on +.Pp +Display a list of available quirk names: +.Pp +.Dl usbconfig dump_quirk_names +.Pp +See +.Xr usb_quirk 4 +for more information on quirks. +.Sh SEE ALSO +.Xr usb 4 , +.Xr usb_quirk 4 diff --git a/usr.sbin/usbconfig/usbconfig.c b/usr.sbin/usbconfig/usbconfig.c new file mode 100644 index 0000000000..a1fa694b2a --- /dev/null +++ b/usr.sbin/usbconfig/usbconfig.c @@ -0,0 +1,822 @@ +/* $FreeBSD$ */ +/*- + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dump.h" + +struct options { + const char *quirkname; + void *buffer; + int template; + gid_t gid; + uid_t uid; + mode_t mode; + uint32_t got_any; + struct LIBUSB20_CONTROL_SETUP_DECODED setup; + uint16_t bus; + uint16_t addr; + uint16_t iface; + uint16_t vid; + uint16_t pid; + uint16_t lo_rev; /* inclusive */ + uint16_t hi_rev; /* inclusive */ + uint8_t string_index; + uint8_t config_index; + uint8_t alt_index; + uint8_t got_list:1; + uint8_t got_bus:1; + uint8_t got_addr:1; + uint8_t got_iface:1; + uint8_t got_set_config:1; + uint8_t got_set_alt:1; + uint8_t got_set_template:1; + uint8_t got_get_template:1; + uint8_t got_suspend:1; + uint8_t got_resume:1; + uint8_t got_reset:1; + uint8_t got_power_off:1; + uint8_t got_power_save:1; + uint8_t got_power_on:1; + uint8_t got_dump_device_quirks:1; + uint8_t got_dump_quirk_names:1; + uint8_t got_dump_device_desc:1; + uint8_t got_dump_curr_config:1; + uint8_t got_dump_all_config:1; + uint8_t got_dump_info:1; + uint8_t got_show_iface_driver:1; + uint8_t got_remove_device_quirk:1; + uint8_t got_add_device_quirk:1; + uint8_t got_remove_quirk:1; + uint8_t got_add_quirk:1; + uint8_t got_dump_string:1; + uint8_t got_do_request:1; +}; + +struct token { + const char *name; + uint8_t value; + uint8_t narg; +}; + +enum { + T_UNIT, + T_ADDR, + T_UGEN, + T_IFACE, + T_SET_CONFIG, + T_SET_ALT, + T_SET_TEMPLATE, + T_GET_TEMPLATE, + T_ADD_DEVICE_QUIRK, + T_REMOVE_DEVICE_QUIRK, + T_ADD_QUIRK, + T_REMOVE_QUIRK, + T_SHOW_IFACE_DRIVER, + T_DUMP_QUIRK_NAMES, + T_DUMP_DEVICE_QUIRKS, + T_DUMP_DEVICE_DESC, + T_DUMP_CURR_CONFIG_DESC, + T_DUMP_ALL_CONFIG_DESC, + T_DUMP_STRING, + T_DUMP_INFO, + T_SUSPEND, + T_RESUME, + T_POWER_OFF, + T_POWER_SAVE, + T_POWER_ON, + T_RESET, + T_LIST, + T_DO_REQUEST, +}; + +static struct options options; + +static const struct token token[] = { + {"-u", T_UNIT, 1}, + {"-a", T_ADDR, 1}, + {"-d", T_UGEN, 1}, + {"-i", T_IFACE, 1}, + {"set_config", T_SET_CONFIG, 1}, + {"set_alt", T_SET_ALT, 1}, + {"set_template", T_SET_TEMPLATE, 1}, + {"get_template", T_GET_TEMPLATE, 0}, + {"add_dev_quirk_vplh", T_ADD_DEVICE_QUIRK, 5}, + {"remove_dev_quirk_vplh", T_REMOVE_DEVICE_QUIRK, 5}, + {"add_quirk", T_ADD_QUIRK, 1}, + {"remove_quirk", T_REMOVE_QUIRK, 1}, + {"dump_quirk_names", T_DUMP_QUIRK_NAMES, 0}, + {"dump_device_quirks", T_DUMP_DEVICE_QUIRKS, 0}, + {"dump_device_desc", T_DUMP_DEVICE_DESC, 0}, + {"dump_curr_config_desc", T_DUMP_CURR_CONFIG_DESC, 0}, + {"dump_all_config_desc", T_DUMP_ALL_CONFIG_DESC, 0}, + {"dump_string", T_DUMP_STRING, 1}, + {"dump_info", T_DUMP_INFO, 0}, + {"show_ifdrv", T_SHOW_IFACE_DRIVER, 0}, + {"suspend", T_SUSPEND, 0}, + {"resume", T_RESUME, 0}, + {"power_off", T_POWER_OFF, 0}, + {"power_save", T_POWER_SAVE, 0}, + {"power_on", T_POWER_ON, 0}, + {"reset", T_RESET, 0}, + {"list", T_LIST, 0}, + {"do_request", T_DO_REQUEST, 5}, +}; + +static void +be_dev_remove_quirk(struct libusb20_backend *pbe, + uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev, + const char *str) +{ + struct libusb20_quirk q; + int error; + + memset(&q, 0, sizeof(q)); + + q.vid = vid; + q.pid = pid; + q.bcdDeviceLow = lorev; + q.bcdDeviceHigh = hirev; + strlcpy(q.quirkname, str, sizeof(q.quirkname)); + + error = libusb20_be_remove_dev_quirk(pbe, &q); + if (error) { + fprintf(stderr, "Removing quirk '%s' failed, continuing.\n", str); + } + return; +} + +static void +be_dev_add_quirk(struct libusb20_backend *pbe, + uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev, + const char *str) +{ + struct libusb20_quirk q; + int error; + + memset(&q, 0, sizeof(q)); + + q.vid = vid; + q.pid = pid; + q.bcdDeviceLow = lorev; + q.bcdDeviceHigh = hirev; + strlcpy(q.quirkname, str, sizeof(q.quirkname)); + + error = libusb20_be_add_dev_quirk(pbe, &q); + if (error) { + fprintf(stderr, "Adding quirk '%s' failed, continuing.\n", str); + } + return; +} + +static uint8_t +get_token(const char *str, uint8_t narg) +{ + uint8_t n; + + for (n = 0; n != (sizeof(token) / sizeof(token[0])); n++) { + if (strcasecmp(str, token[n].name) == 0) { + if (token[n].narg > narg) { + /* too few arguments */ + break; + } + return (token[n].value); + } + } + return (0 - 1); +} + +static uid_t +num_id(const char *name, const char *type) +{ + uid_t val; + char *ep; + + errno = 0; + val = strtoul(name, &ep, 0); + if (errno) { + err(1, "%s", name); + } + if (*ep != '\0') { + errx(1, "%s: illegal %s name", name, type); + } + return (val); +} + +static int +get_int(const char *s) +{ + int val; + char *ep; + + errno = 0; + val = strtoul(s, &ep, 0); + if (errno) { + err(1, "%s", s); + } + if (*ep != '\0') { + errx(1, "illegal number: %s", s); + } + return val; +} + +static void +duplicate_option(const char *ptr) +{ + fprintf(stderr, "Syntax error: " + "Duplicate option: '%s'\n", ptr); + exit(1); +} + +static void +usage(void) +{ + fprintf(stderr, "" + "usbconfig - configure the USB subsystem" "\n" + "usage: usbconfig -u -a -i [cmds...]" "\n" + "usage: usbconfig -d [ugen]. -i [cmds...]" "\n" + "commands:" "\n" + " set_config " "\n" + " set_alt " "\n" + " set_template