package libusb import "core:c" // ============================================================================= // Foreign import — platform-selected linking // ============================================================================= // Set to false to statically link libusb on Windows. // On Linux/macOS, the system linker decides static vs dynamic based on what's // available; use pkg-config or linker flags to control it externally. LIBUSB_SHARED :: #config(LIBUSB_SHARED, true) when ODIN_OS == .Windows { when ODIN_ARCH == .amd64 { when LIBUSB_SHARED { foreign import lib "windows-x64/libusb-1.0.lib" } else { foreign import lib { "windows-x64/libusb-1.0-static.lib", "system:Advapi32.lib", "system:Ole32.lib", "system:Setupapi.lib", } } } else when ODIN_ARCH == .i386 { when LIBUSB_SHARED { foreign import lib "windows-x86/libusb-1.0.lib" } else { foreign import lib { "windows-x86/libusb-1.0-static.lib", "system:Advapi32.lib", "system:Ole32.lib", "system:Setupapi.lib", } } } else { #panic("Unsupported Windows architecture for libusb. Only amd64 and i386 are supported.") } } else { // Linux, macOS, BSD — link against system-installed libusb. // Install via: apt install libusb-1.0-0-dev / brew install libusb / etc. foreign import lib "system:usb-1.0" } // libusb API version (1.0.30). API_VERSION :: 0x0100010C // Descriptor sizes per descriptor type. DT_DEVICE_SIZE :: 18 DT_CONFIG_SIZE :: 9 DT_INTERFACE_SIZE :: 9 DT_INTERFACE_ASSOCIATION_SIZE :: 8 DT_ENDPOINT_SIZE :: 7 DT_ENDPOINT_AUDIO_SIZE :: 9 DT_HUB_NONVAR_SIZE :: 7 DT_SS_ENDPOINT_COMPANION_SIZE :: 6 DT_BOS_SIZE :: 5 DT_DEVICE_CAPABILITY_SIZE :: 3 // BOS descriptor sizes. BT_USB_2_0_EXTENSION_SIZE :: 7 BT_SS_USB_DEVICE_CAPABILITY_SIZE :: 10 BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE :: 12 BT_CONTAINER_ID_SIZE :: 20 BT_PLATFORM_DESCRIPTOR_MIN_SIZE :: 20 // Maximum BOS descriptor size (sum of all sub-descriptors). DT_BOS_MAX_SIZE :: DT_BOS_SIZE + BT_USB_2_0_EXTENSION_SIZE + BT_SS_USB_DEVICE_CAPABILITY_SIZE + BT_CONTAINER_ID_SIZE // Endpoint address / direction masks. ENDPOINT_ADDRESS_MASK :: 0x0f ENDPOINT_DIR_MASK :: 0x80 // Transfer type mask in bmAttributes. TRANSFER_TYPE_MASK :: 0x03 // Iso sync type mask in bmAttributes. ISO_SYNC_TYPE_MASK :: 0x0c // Iso usage type mask in bmAttributes. ISO_USAGE_TYPE_MASK :: 0x30 // Control setup packet size (8 bytes). CONTROL_SETUP_SIZE :: 8 // Total number of error codes in the Error enum. ERROR_COUNT :: 14 // Hotplug convenience constants. HOTPLUG_NO_FLAGS :: Hotplug_Flags{} HOTPLUG_MATCH_ANY :: c.int(-1) // Maximum length for a device string descriptor in UTF-8 (libusb 1.0.30+). DEVICE_STRING_BYTES_MAX :: 384 // Poll event constants matching values. POLLIN :: c.short(0x0001) POLLPRI :: c.short(0x0002) POLLOUT :: c.short(0x0004) POLLERR :: c.short(0x0008) POLLHUP :: c.short(0x0010) // Device and/or Interface Class codes. Class_Code :: enum c.int { PER_INTERFACE = 0x00, AUDIO = 0x01, COMM = 0x02, HID = 0x03, PHYSICAL = 0x05, IMAGE = 0x06, PRINTER = 0x07, MASS_STORAGE = 0x08, HUB = 0x09, DATA = 0x0a, SMART_CARD = 0x0b, CONTENT_SECURITY = 0x0d, VIDEO = 0x0e, PERSONAL_HEALTHCARE = 0x0f, AUDIO_VIDEO = 0x10, BILLBOARD = 0x11, TYPE_C_BRIDGE = 0x12, BULK_DISPLAY = 0x13, MCTP = 0x14, I3C = 0x3c, DIAGNOSTIC_DEVICE = 0xdc, WIRELESS = 0xe0, MISCELLANEOUS = 0xef, APPLICATION = 0xfe, VENDOR_SPEC = 0xff, } // Legacy alias from libusb-0.1. CLASS_PTP :: Class_Code.IMAGE // Descriptor types as defined by the USB specification. Descriptor_Type :: enum c.int { DEVICE = 0x01, CONFIG = 0x02, STRING = 0x03, INTERFACE = 0x04, ENDPOINT = 0x05, INTERFACE_ASSOCIATION = 0x0b, BOS = 0x0f, DEVICE_CAPABILITY = 0x10, HID = 0x21, REPORT = 0x22, PHYSICAL = 0x23, HUB = 0x29, SUPERSPEED_HUB = 0x2a, SS_ENDPOINT_COMPANION = 0x30, } // Endpoint direction. Values for bit 7 of the endpoint address. Endpoint_Direction :: enum c.int { OUT = 0x00, IN = 0x80, } // Endpoint transfer type. Values for bits 0:1 of bmAttributes. Endpoint_Transfer_Type :: enum c.int { CONTROL = 0x0, ISOCHRONOUS = 0x1, BULK = 0x2, INTERRUPT = 0x3, } // Standard requests, as defined in table 9-5 of the USB 3.0 specifications. Standard_Request :: enum c.int { GET_STATUS = 0x00, CLEAR_FEATURE = 0x01, SET_FEATURE = 0x03, SET_ADDRESS = 0x05, GET_DESCRIPTOR = 0x06, SET_DESCRIPTOR = 0x07, GET_CONFIGURATION = 0x08, SET_CONFIGURATION = 0x09, GET_INTERFACE = 0x0a, SET_INTERFACE = 0x0b, SYNCH_FRAME = 0x0c, SET_SEL = 0x30, SET_ISOCH_DELAY = 0x31, } // Request type bits of the bmRequestType field in control transfers. Request_Type :: enum c.int { STANDARD = 0x00 << 5, CLASS = 0x01 << 5, VENDOR = 0x02 << 5, RESERVED = 0x03 << 5, } // Recipient bits of the bmRequestType field in control transfers. // Values 4 through 31 are reserved. Request_Recipient :: enum c.int { DEVICE = 0x00, INTERFACE = 0x01, ENDPOINT = 0x02, OTHER = 0x03, } // Synchronization type for isochronous endpoints. // Values for bits 2:3 of bmAttributes. Iso_Sync_Type :: enum c.int { NONE = 0x0, ASYNC = 0x1, ADAPTIVE = 0x2, SYNC = 0x3, } // Usage type for isochronous endpoints. // Values for bits 4:5 of bmAttributes. Iso_Usage_Type :: enum c.int { DATA = 0x0, FEEDBACK = 0x1, IMPLICIT = 0x2, } // Supported speeds (wSpeedSupported) — bitmask flags. Supported_Speed_Bit :: enum u16 { LOW = 0, FULL = 1, HIGH = 2, SUPER = 3, } Supported_Speeds :: bit_set[Supported_Speed_Bit;u16] // USB 2.0 Extension bmAttributes flags. Usb2_Ext_Attribute_Bit :: enum u32 { LPM_SUPPORT = 1, } Usb2_Ext_Attributes :: bit_set[Usb2_Ext_Attribute_Bit;u32] // SuperSpeed USB Device Capability bmAttributes flags. Ss_Dev_Cap_Attribute_Bit :: enum u8 { LTM_SUPPORT = 1, } Ss_Dev_Cap_Attributes :: bit_set[Ss_Dev_Cap_Attribute_Bit;u8] // USB capability types (BOS). Bos_Type :: enum c.int { WIRELESS_USB_DEVICE_CAPABILITY = 0x01, USB_2_0_EXTENSION = 0x02, SS_USB_DEVICE_CAPABILITY = 0x03, CONTAINER_ID = 0x04, PLATFORM_DESCRIPTOR = 0x05, SUPERSPEED_PLUS_CAPABILITY = 0x0A, } // Speed codes. Indicates the speed at which the device is operating. Speed :: enum c.int { UNKNOWN = 0, LOW = 1, FULL = 2, HIGH = 3, SUPER = 4, SUPER_PLUS = 5, SUPER_PLUS_X2 = 6, } // Error codes. Most libusb functions return 0 on success or one of these // codes on failure. Use `error_name()` for a string representation or // `strerror()` for an end-user description. Error :: enum c.int { SUCCESS = 0, IO = -1, INVALID_PARAM = -2, ACCESS = -3, NO_DEVICE = -4, NOT_FOUND = -5, BUSY = -6, TIMEOUT = -7, OVERFLOW = -8, PIPE = -9, INTERRUPTED = -10, NO_MEM = -11, NOT_SUPPORTED = -12, OTHER = -99, } // Transfer type (for the Transfer struct's `type` field). // Stored as u8 to match libusb_transfer.type which is `unsigned char`. Transfer_Type :: enum u8 { CONTROL = 0, ISOCHRONOUS = 1, BULK = 2, INTERRUPT = 3, BULK_STREAM = 4, } // Transfer status codes. Transfer_Status :: enum c.int { COMPLETED, ERROR, TIMED_OUT, CANCELLED, STALL, NO_DEVICE, OVERFLOW, } // Transfer flag bits for use in a `Transfer_Flags` bit_set. Transfer_Flag_Bit :: enum u8 { // Report short frames as errors. SHORT_NOT_OK, // Automatically free() transfer buffer during `free_transfer()`. // Do not use with buffers from `dev_mem_alloc()`. FREE_BUFFER, // Automatically call `free_transfer()` after callback returns. // Do not call `free_transfer()` from your callback if this is set. FREE_TRANSFER, // Terminate transfers that are a multiple of the endpoint's // wMaxPacketSize with an extra zero length packet. Currently // only supported on Linux. Available since libusb-1.0.9. ADD_ZERO_PACKET, } Transfer_Flags :: bit_set[Transfer_Flag_Bit;u8] // Capabilities supported by this instance of libusb. // Test with `has_capability()`. Capability :: enum u32 { HAS_CAPABILITY = 0x0000, HAS_HOTPLUG = 0x0001, HAS_HID_ACCESS = 0x0100, SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101, } // Log message levels. Log_Level :: enum c.int { NONE = 0, ERROR = 1, WARNING = 2, INFO = 3, DEBUG = 4, } // Log callback mode bits. Since version 1.0.23. Log_Cb_Mode_Bit :: enum c.int { GLOBAL = 0, CONTEXT = 1, } Log_Cb_Mode :: bit_set[Log_Cb_Mode_Bit;c.int] // Available option values for `set_option()` and `init_context()`. Option :: enum c.int { // Set the log message verbosity. Argument: `Log_Level` (as c.int). LOG_LEVEL = 0, // Use the UsbDk backend (Windows only, ignored elsewhere). USE_USBDK = 1, // Do not scan for devices in `init_context()` (Linux only). // Alias: WEAK_AUTHORITY. NO_DEVICE_DISCOVERY = 2, // Set the context log callback. Argument: `Log_Cb`. LOG_CB = 3, MAX = 4, } // Alias for NO_DEVICE_DISCOVERY. OPTION_WEAK_AUTHORITY :: Option.NO_DEVICE_DISCOVERY // Hotplug event bits. Since version 1.0.16. Hotplug_Event_Bit :: enum c.int { // A device has been plugged in and is ready to use. DEVICE_ARRIVED = 0, // A device has left and is no longer available. It is the user's // responsibility to call `close()` on any associated handle. DEVICE_LEFT = 1, } Hotplug_Events :: bit_set[Hotplug_Event_Bit;c.int] // Hotplug flag bits. Since version 1.0.16. Hotplug_Flag_Bit :: enum c.int { // Arm the callback and fire it for all matching currently attached devices. ENUMERATE = 0, } Hotplug_Flags :: bit_set[Hotplug_Flag_Bit;c.int] // Device string type (libusb 1.0.30+). Device_String_Type :: enum c.int { MANUFACTURER, PRODUCT, SERIAL_NUMBER, COUNT, } // SuperSpeedPlus sublink attribute enums (libusb 1.0.28+). Ssplus_Sublink_Type :: enum c.int { SYM = 0, ASYM = 1, } Ssplus_Sublink_Direction :: enum c.int { RX = 0, TX = 1, } Ssplus_Sublink_Exponent :: enum c.int { BPS = 0, KBS = 1, MBS = 2, GBS = 3, } Ssplus_Sublink_Protocol :: enum c.int { SS = 0, SSPLUS = 1, } // Setup packet for control transfers (8 bytes, packed). // Use `fill_control_setup()` to populate. The 16-bit fields // are stored in little-endian byte order on the wire. Control_Setup :: struct #packed { bmRequestType: u8, bRequest: u8, wValue: u16, wIndex: u16, wLength: u16, } // Standard USB device descriptor (USB 3.0, section 9.6.1). // All multi-byte fields in host-endian format. Device_Descriptor :: struct { // Size of this descriptor (in bytes). bLength: u8, // Descriptor type (`Descriptor_Type.DEVICE`). bDescriptorType: u8, // USB spec release number in BCD. 0x0200 = USB 2.0, etc. bcdUSB: u16, // USB-IF class code for the device. See `Class_Code`. bDeviceClass: u8, // USB-IF subclass code, qualified by bDeviceClass. bDeviceSubClass: u8, // USB-IF protocol code, qualified by class and subclass. bDeviceProtocol: u8, // Maximum packet size for endpoint 0. bMaxPacketSize0: u8, // USB-IF vendor ID. idVendor: u16, // USB-IF product ID. idProduct: u16, // Device release number in BCD. bcdDevice: u16, // Index of manufacturer string descriptor. iManufacturer: u8, // Index of product string descriptor. iProduct: u8, // Index of serial number string descriptor. iSerialNumber: u8, // Number of possible configurations. bNumConfigurations: u8, } // Standard USB endpoint descriptor (USB 3.0, section 9.6.6). // All multi-byte fields in host-endian format. Endpoint_Descriptor :: struct { // Size of this descriptor (in bytes). bLength: u8, // Descriptor type (`Descriptor_Type.ENDPOINT`). bDescriptorType: u8, // Endpoint address. Bits 0:3 = endpoint number, bit 7 = direction. bEndpointAddress: u8, // Endpoint attributes. Bits 0:1 = transfer type, 2:3 = iso sync type, // 4:5 = iso usage type. bmAttributes: u8, // Maximum packet size this endpoint can send/receive. wMaxPacketSize: u16, // Polling interval for data transfers. bInterval: u8, // Audio devices only: rate of synchronization feedback. bRefresh: u8, // Audio devices only: address of the synch endpoint. bSynchAddress: u8, // Extra descriptors. Stored here if libusb encounters unknown descriptors. extra: [^]u8, // Length of extra descriptors in bytes. extra_length: c.int, } // Standard USB interface association descriptor (USB 3.0, section 9.6.4). Interface_Association_Descriptor :: struct { bLength: u8, bDescriptorType: u8, bFirstInterface: u8, bInterfaceCount: u8, bFunctionClass: u8, bFunctionSubClass: u8, bFunctionProtocol: u8, iFunction: u8, } // Array of 0 or more interface association descriptors. Interface_Association_Descriptor_Array :: struct { iad: [^]Interface_Association_Descriptor, length: c.int, } // Standard USB interface descriptor (USB 3.0, section 9.6.5). Interface_Descriptor :: struct { bLength: u8, bDescriptorType: u8, bInterfaceNumber: u8, bAlternateSetting: u8, bNumEndpoints: u8, bInterfaceClass: u8, bInterfaceSubClass: u8, bInterfaceProtocol: u8, iInterface: u8, // Array of endpoint descriptors (length = bNumEndpoints). endpoint: [^]Endpoint_Descriptor, extra: [^]u8, extra_length: c.int, } // A collection of alternate settings for a particular USB interface. Interface :: struct { // Array of interface descriptors (length = num_altsetting). altsetting: [^]Interface_Descriptor, num_altsetting: c.int, } // Standard USB configuration descriptor (USB 3.0, section 9.6.3). Config_Descriptor :: struct { bLength: u8, bDescriptorType: u8, wTotalLength: u16, bNumInterfaces: u8, bConfigurationValue: u8, iConfiguration: u8, bmAttributes: u8, // Max power consumption. Units of 2 mA (high-speed) or 8 mA (super-speed). MaxPower: u8, // Array of interfaces (length = bNumInterfaces). interface: [^]Interface, extra: [^]u8, extra_length: c.int, } // SuperSpeed endpoint companion descriptor (USB 3.0, section 9.6.7). Ss_Endpoint_Companion_Descriptor :: struct { bLength: u8, bDescriptorType: u8, bMaxBurst: u8, bmAttributes: u8, wBytesPerInterval: u16, } // Generic BOS Device Capability descriptor. Check bDevCapabilityType and // call the matching get_*_descriptor function for a fully-typed struct. Bos_Dev_Capability_Descriptor :: struct { bLength: u8, bDescriptorType: u8, bDevCapabilityType: u8, // Device Capability data (bLength - 3 bytes). Flexible array member. dev_capability_data: [0]u8, } // Binary Device Object Store (BOS) descriptor (USB 3.0, section 9.6.2). Bos_Descriptor :: struct { bLength: u8, bDescriptorType: u8, wTotalLength: u16, bNumDeviceCaps: u8, // Flexible array of device capability descriptor pointers. dev_capability: [0]^Bos_Dev_Capability_Descriptor, } // USB 2.0 Extension descriptor (USB 3.0, section 9.6.2.1). Usb2_Extension_Descriptor :: struct { bLength: u8, bDescriptorType: u8, bDevCapabilityType: u8, // Bitmap of supported device level features. // See `Usb2_Ext_Attributes`. bmAttributes: u32, } // SuperSpeed USB Device Capability descriptor (USB 3.0, section 9.6.2.2). Ss_Usb_Device_Capability_Descriptor :: struct { bLength: u8, bDescriptorType: u8, bDevCapabilityType: u8, // See `Ss_Dev_Cap_Attributes`. bmAttributes: u8, // Bitmap of supported speeds. See `Supported_Speeds`. wSpeedSupported: u16, // Lowest speed at which full functionality is available. bFunctionalitySupport: u8, bU1DevExitLat: u8, bU2DevExitLat: u16, } // Container ID descriptor (USB 3.0, section 9.6.2.3). Container_Id_Descriptor :: struct { bLength: u8, bDescriptorType: u8, bDevCapabilityType: u8, bReserved: u8, // 128-bit UUID as a byte array. ContainerID: [16]u8, } // Platform descriptor (USB 3.2, section 9.6.2.4). // Contains a flexible array member — all memory must be managed manually. Platform_Descriptor :: struct { bLength: u8, bDescriptorType: u8, bDevCapabilityType: u8, bReserved: u8, // 128-bit UUID as a byte array. PlatformCapabilityUUID: [16]u8, // Capability data (bLength - 20 bytes). Flexible array member. CapabilityData: [0]u8, } // SuperSpeedPlus sublink attribute (libusb 1.0.28+, USB 3.1, section 9.6.2.5). Ssplus_Sublink_Attribute :: struct { ssid: u8, exponent: Ssplus_Sublink_Exponent, type: Ssplus_Sublink_Type, direction: Ssplus_Sublink_Direction, protocol: Ssplus_Sublink_Protocol, mantissa: u16, } // SuperSpeedPlus USB Device Capability descriptor (libusb 1.0.28+). // This is a parsed/computed representation, not the raw USB descriptor. Ssplus_Usb_Device_Capability_Descriptor :: struct { numSublinkSpeedAttributes: u8, numSublinkSpeedIDs: u8, ssid: u8, minRxLaneCount: u8, minTxLaneCount: u8, // Flexible array of sublink attributes (length = numSublinkSpeedAttributes). sublinkSpeedAttributes: [0]Ssplus_Sublink_Attribute, } // Version of the libusb runtime. Version :: struct { major: u16, minor: u16, micro: u16, nano: u16, // Release candidate suffix, e.g. "-rc4". rc: cstring, // For ABI compatibility only. describe: cstring, } // Isochronous packet descriptor. Iso_Packet_Descriptor :: struct { length: c.uint, actual_length: c.uint, status: Transfer_Status, } // Generic USB transfer structure. Transfer :: struct { // Handle of the device that this transfer will be submitted to. dev_handle: Device_Handle, // Bitwise OR of `Transfer_Flags`. flags: Transfer_Flags, // Address of the endpoint where this transfer will be sent. endpoint: u8, // Type of the transfer from `Transfer_Type`. type: Transfer_Type, // Timeout for this transfer in milliseconds. 0 = no timeout. timeout: c.uint, // Status of the transfer. Read-only, valid only within the callback. status: Transfer_Status, // Length of the data buffer. length: c.int, // Actual length of data transferred. Read-only, valid only within callback. actual_length: c.int, // Callback invoked when the transfer completes, fails, or is cancelled. callback: Transfer_Cb, // User context data accessible from within the callback. user_data: rawptr, // Data buffer. buffer: [^]u8, // Number of isochronous packets. Only for isochronous endpoints. num_iso_packets: c.int, // Isochronous packet descriptors. Flexible array member. iso_packet_desc: [0]Iso_Packet_Descriptor, } // Platform-specific timeval struct matching the system's struct timeval. // libusb uses this for event-loop timeouts. when ODIN_OS == .Darwin { Timeval :: struct { tv_sec: c.long, tv_usec: i32, } } else { Timeval :: struct { tv_sec: c.long, tv_usec: c.long, } } // File descriptor for polling. Poll_Fd :: struct { fd: c.int, // Event flags from . POLLIN for read-ready, POLLOUT for write-ready. events: c.short, } // Option value union for `Init_Option`. Init_Option_Value :: struct #raw_union { ival: c.int, log_cb: Log_Cb, } // Option structure for `init_context()`. Init_Option :: struct { option: Option, value: Init_Option_Value, } //----- Opaque handles ---------------------------------- Context :: distinct rawptr Device :: distinct rawptr Device_Handle :: distinct rawptr // Hotplug callback handle, returned by `hotplug_register_callback()`. Callback_Handle :: distinct c.int //----- Callback types ---------------------------------- // Asynchronous transfer completion callback. Transfer_Cb :: #type proc "c" (transfer: ^Transfer) // Log message callback. Since version 1.0.23. Log_Cb :: #type proc "c" (ctx: Context, level: Log_Level, str: cstring) // Hotplug event callback. Return 1 to deregister this callback. Hotplug_Callback_Fn :: #type proc "c" ( ctx: Context, device: Device, event: Hotplug_Events, user_data: rawptr, ) -> c.int // Callback invoked when a new file descriptor should be monitored for events. Pollfd_Added_Cb :: #type proc "c" (fd: c.int, events: c.short, user_data: rawptr) // Callback invoked when a file descriptor should no longer be monitored. Pollfd_Removed_Cb :: #type proc "c" (fd: c.int, user_data: rawptr) @(default_calling_convention = "c", link_prefix = "libusb_") foreign lib { // ---- Library initialization/deinitialization ---- set_log_cb :: proc(ctx: Context, cb: Log_Cb, mode: Log_Cb_Mode) --- set_option :: proc(ctx: Context, option: Option, #c_vararg args: ..any) -> Error --- init :: proc(ctx: ^Context) -> Error --- init_context :: proc(ctx: ^Context, options: [^]Init_Option, num_options: c.int) -> Error --- exit :: proc(ctx: Context) --- // Deprecated: use `set_option(.LOG_LEVEL, level)` instead. set_debug :: proc(ctx: Context, level: c.int) --- // ---- Device handling and enumeration ---- // Returns device count (positive) or a negative Error code. get_device_list :: proc(ctx: Context, list: ^[^]Device) -> int --- free_device_list :: proc(list: [^]Device, unref_devices: c.int) --- get_bus_number :: proc(dev: Device) -> u8 --- get_port_number :: proc(dev: Device) -> u8 --- // Returns number of elements filled or negative Error code. get_port_numbers :: proc(dev: Device, port_numbers: [^]u8, port_numbers_len: c.int) -> c.int --- // Deprecated: use `get_port_numbers()` instead. get_port_path :: proc(ctx: Context, dev: Device, path: [^]u8, path_length: u8) -> c.int --- get_parent :: proc(dev: Device) -> Device --- get_device_address :: proc(dev: Device) -> u8 --- get_device_speed :: proc(dev: Device) -> Speed --- get_session_data :: proc(dev: Device) -> c.ulong --- // Returns max packet size or negative Error code. get_max_packet_size :: proc(dev: Device, endpoint: u8) -> c.int --- // Returns max iso packet size or negative Error code. get_max_iso_packet_size :: proc(dev: Device, endpoint: u8) -> c.int --- get_max_alt_packet_size :: proc(dev: Device, interface_number: c.int, alternate_setting: c.int, endpoint: u8) -> c.int --- ref_device :: proc(dev: Device) -> Device --- unref_device :: proc(dev: Device) --- wrap_sys_device :: proc(ctx: Context, sys_dev: c.intptr_t, dev_handle: ^Device_Handle) -> Error --- open :: proc(dev: Device, dev_handle: ^Device_Handle) -> Error --- open_device_with_vid_pid :: proc(ctx: Context, vendor_id: u16, product_id: u16) -> Device_Handle --- close :: proc(dev_handle: Device_Handle) --- get_device :: proc(dev_handle: Device_Handle) -> Device --- get_configuration :: proc(dev_handle: Device_Handle, config: ^c.int) -> Error --- set_configuration :: proc(dev_handle: Device_Handle, configuration: c.int) -> Error --- claim_interface :: proc(dev_handle: Device_Handle, interface_number: c.int) -> Error --- release_interface :: proc(dev_handle: Device_Handle, interface_number: c.int) -> Error --- set_interface_alt_setting :: proc(dev_handle: Device_Handle, interface_number: c.int, alternate_setting: c.int) -> Error --- clear_halt :: proc(dev_handle: Device_Handle, endpoint: u8) -> Error --- reset_device :: proc(dev_handle: Device_Handle) -> Error --- // Returns 0 if no kernel driver active, 1 if active, or negative Error code. kernel_driver_active :: proc(dev_handle: Device_Handle, interface_number: c.int) -> c.int --- detach_kernel_driver :: proc(dev_handle: Device_Handle, interface_number: c.int) -> Error --- attach_kernel_driver :: proc(dev_handle: Device_Handle, interface_number: c.int) -> Error --- set_auto_detach_kernel_driver :: proc(dev_handle: Device_Handle, enable: c.int) -> Error --- // ---- Miscellaneous ---- has_capability :: proc(capability: Capability) -> c.int --- error_name :: proc(errcode: Error) -> cstring --- get_version :: proc() -> ^Version --- setlocale :: proc(locale: cstring) -> Error --- strerror :: proc(errcode: Error) -> cstring --- // ---- USB descriptors ---- get_device_descriptor :: proc(dev: Device, desc: ^Device_Descriptor) -> Error --- get_active_config_descriptor :: proc(dev: Device, config: ^^Config_Descriptor) -> Error --- get_config_descriptor :: proc(dev: Device, config_index: u8, config: ^^Config_Descriptor) -> Error --- get_config_descriptor_by_value :: proc(dev: Device, bConfigurationValue: u8, config: ^^Config_Descriptor) -> Error --- free_config_descriptor :: proc(config: ^Config_Descriptor) --- get_ss_endpoint_companion_descriptor :: proc(ctx: Context, endpoint: ^Endpoint_Descriptor, ep_comp: ^^Ss_Endpoint_Companion_Descriptor) -> Error --- free_ss_endpoint_companion_descriptor :: proc(ep_comp: ^Ss_Endpoint_Companion_Descriptor) --- get_bos_descriptor :: proc(dev_handle: Device_Handle, bos: ^^Bos_Descriptor) -> Error --- free_bos_descriptor :: proc(bos: ^Bos_Descriptor) --- get_usb2_extension_descriptor :: proc(ctx: Context, dev_cap: ^Bos_Dev_Capability_Descriptor, usb2_extension: ^^Usb2_Extension_Descriptor) -> Error --- free_usb2_extension_descriptor :: proc(usb2_extension: ^Usb2_Extension_Descriptor) --- get_ss_usb_device_capability_descriptor :: proc(ctx: Context, dev_cap: ^Bos_Dev_Capability_Descriptor, ss_usb_device_cap: ^^Ss_Usb_Device_Capability_Descriptor) -> Error --- free_ss_usb_device_capability_descriptor :: proc(ss_usb_device_cap: ^Ss_Usb_Device_Capability_Descriptor) --- get_ssplus_usb_device_capability_descriptor :: proc(ctx: Context, dev_cap: ^Bos_Dev_Capability_Descriptor, ssplus_usb_device_cap: ^^Ssplus_Usb_Device_Capability_Descriptor) -> Error --- free_ssplus_usb_device_capability_descriptor :: proc(ssplus_usb_device_cap: ^Ssplus_Usb_Device_Capability_Descriptor) --- get_container_id_descriptor :: proc(ctx: Context, dev_cap: ^Bos_Dev_Capability_Descriptor, container_id: ^^Container_Id_Descriptor) -> Error --- free_container_id_descriptor :: proc(container_id: ^Container_Id_Descriptor) --- get_platform_descriptor :: proc(ctx: Context, dev_cap: ^Bos_Dev_Capability_Descriptor, platform_descriptor: ^^Platform_Descriptor) -> Error --- free_platform_descriptor :: proc(platform_descriptor: ^Platform_Descriptor) --- // Returns number of bytes written or negative Error code. `data` is an output buffer. get_string_descriptor_ascii :: proc(dev_handle: Device_Handle, desc_index: u8, data: [^]u8, length: c.int) -> c.int --- get_interface_association_descriptors :: proc(dev: Device, config_index: u8, iad_array: ^^Interface_Association_Descriptor_Array) -> Error --- get_active_interface_association_descriptors :: proc(dev: Device, iad_array: ^^Interface_Association_Descriptor_Array) -> Error --- free_interface_association_descriptors :: proc(iad_array: ^Interface_Association_Descriptor_Array) --- // ---- Device string (libusb 1.0.30+) ---- // Retrieve a device string descriptor as UTF-8. // Returns number of bytes written or negative Error code. get_device_string :: proc(dev: Device, string_type: Device_String_Type, data: [^]u8, length: c.int) -> c.int --- // ---- Raw I/O (libusb 1.0.30+) ---- // Returns 1 if supported, 0 if not, or negative Error code. endpoint_supports_raw_io :: proc(dev_handle: Device_Handle, endpoint: u8) -> c.int --- endpoint_set_raw_io :: proc(dev_handle: Device_Handle, endpoint: u8, enable: c.int) -> c.int --- get_max_raw_io_transfer_size :: proc(dev_handle: Device_Handle, endpoint: u8) -> c.int --- // ---- Device hotplug event notification ---- hotplug_register_callback :: proc(ctx: Context, events: Hotplug_Events, flags: Hotplug_Flags, vendor_id: c.int, product_id: c.int, dev_class: c.int, cb_fn: Hotplug_Callback_Fn, user_data: rawptr, callback_handle: ^Callback_Handle) -> Error --- hotplug_deregister_callback :: proc(ctx: Context, callback_handle: Callback_Handle) --- hotplug_get_user_data :: proc(ctx: Context, callback_handle: Callback_Handle) -> rawptr --- // ---- Asynchronous device I/O ---- // Returns number of streams allocated or negative Error code. alloc_streams :: proc(dev_handle: Device_Handle, num_streams: u32, endpoints: [^]u8, num_endpoints: c.int) -> c.int --- free_streams :: proc(dev_handle: Device_Handle, endpoints: [^]u8, num_endpoints: c.int) -> Error --- dev_mem_alloc :: proc(dev_handle: Device_Handle, length: c.size_t) -> [^]u8 --- dev_mem_free :: proc(dev_handle: Device_Handle, buffer: [^]u8, length: c.size_t) -> Error --- alloc_transfer :: proc(iso_packets: c.int) -> ^Transfer --- free_transfer :: proc(transfer: ^Transfer) --- submit_transfer :: proc(transfer: ^Transfer) -> Error --- cancel_transfer :: proc(transfer: ^Transfer) -> Error --- transfer_set_stream_id :: proc(transfer: ^Transfer, stream_id: u32) --- transfer_get_stream_id :: proc(transfer: ^Transfer) -> u32 --- // ---- Polling and timing ---- try_lock_events :: proc(ctx: Context) -> c.int --- lock_events :: proc(ctx: Context) --- unlock_events :: proc(ctx: Context) --- event_handling_ok :: proc(ctx: Context) -> c.int --- event_handler_active :: proc(ctx: Context) -> c.int --- interrupt_event_handler :: proc(ctx: Context) --- lock_event_waiters :: proc(ctx: Context) --- unlock_event_waiters :: proc(ctx: Context) --- wait_for_event :: proc(ctx: Context, tv: ^Timeval) -> c.int --- handle_events :: proc(ctx: Context) -> Error --- handle_events_timeout :: proc(ctx: Context, tv: ^Timeval) -> Error --- handle_events_timeout_completed :: proc(ctx: Context, tv: ^Timeval, completed: ^c.int) -> Error --- handle_events_completed :: proc(ctx: Context, completed: ^c.int) -> Error --- handle_events_locked :: proc(ctx: Context, tv: ^Timeval) -> Error --- pollfds_handle_timeouts :: proc(ctx: Context) -> c.int --- // Returns 0 if no timeout, 1 if a timeout was returned, or negative Error code. get_next_timeout :: proc(ctx: Context, tv: ^Timeval) -> c.int --- set_pollfd_notifiers :: proc(ctx: Context, added_cb: Pollfd_Added_Cb, removed_cb: Pollfd_Removed_Cb, user_data: rawptr) --- // Returns a NULL-terminated list of `Poll_Fd` pointers. Free with `free_pollfds()`. get_pollfds :: proc(ctx: Context) -> [^]^Poll_Fd --- free_pollfds :: proc(pollfds: [^]^Poll_Fd) --- // ---- Synchronous device I/O ---- // Returns number of bytes transferred or negative Error code. control_transfer :: proc(dev_handle: Device_Handle, bmRequestType: u8, bRequest: u8, wValue: u16, wIndex: u16, data: [^]u8, wLength: u16, timeout: c.uint) -> c.int --- bulk_transfer :: proc(dev_handle: Device_Handle, endpoint: u8, data: [^]u8, length: c.int, transferred: ^c.int, timeout: c.uint) -> Error --- interrupt_transfer :: proc(dev_handle: Device_Handle, endpoint: u8, data: [^]u8, length: c.int, transferred: ^c.int, timeout: c.uint) -> Error --- } // --------------------------------------------------------------------------------------------------------------------- // ----- Helper Procedures (from libusb static inline in libusb.h) ------------------------ // --------------------------------------------------------------------------------------------------------------------- // Convert a 16-bit value from host-endian to little-endian format. cpu_to_le16 :: #force_inline proc "contextless" (x: u16) -> u16 { return transmute(u16)u16le(x) } // Convert a 16-bit value from little-endian to host-endian format. // Identical to `cpu_to_le16` since the conversion is its own inverse. le16_to_cpu :: cpu_to_le16 // Get the data section of a control transfer. The data starts 8 bytes into // the buffer, after the setup packet. control_transfer_get_data :: #force_inline proc "contextless" (transfer: ^Transfer) -> [^]u8 { return ([^]u8)(&transfer.buffer[CONTROL_SETUP_SIZE]) } // Get the control setup packet from the start of a transfer's data buffer. control_transfer_get_setup :: #force_inline proc "contextless" (transfer: ^Transfer) -> ^Control_Setup { return (^Control_Setup)(transfer.buffer) } // Populate the setup packet (first 8 bytes of the data buffer) for a control // transfer. The wValue, wIndex, and wLength values should be given in // host-endian byte order; they will be converted to little-endian. fill_control_setup :: proc "contextless" ( buffer: [^]u8, bmRequestType: u8, bRequest: u8, wValue: u16, wIndex: u16, wLength: u16, ) { setup := (^Control_Setup)(buffer) setup.bmRequestType = bmRequestType setup.bRequest = bRequest setup.wValue = cpu_to_le16(wValue) setup.wIndex = cpu_to_le16(wIndex) setup.wLength = cpu_to_le16(wLength) } // Populate the required fields for a control transfer. If buffer is non-nil, // the first 8 bytes are interpreted as a setup packet and the transfer length // is inferred from the setup's wLength field. fill_control_transfer :: proc "contextless" ( transfer: ^Transfer, dev_handle: Device_Handle, buffer: [^]u8, callback: Transfer_Cb, user_data: rawptr, timeout: c.uint, ) { transfer.dev_handle = dev_handle transfer.endpoint = 0 transfer.type = .CONTROL transfer.timeout = timeout transfer.buffer = buffer if buffer != nil { setup := (^Control_Setup)(buffer) transfer.length = c.int(CONTROL_SETUP_SIZE) + c.int(le16_to_cpu(setup.wLength)) } transfer.user_data = user_data transfer.callback = callback } // Populate the required fields for a bulk transfer. fill_bulk_transfer :: proc "contextless" ( transfer: ^Transfer, dev_handle: Device_Handle, endpoint: u8, buffer: [^]u8, length: c.int, callback: Transfer_Cb, user_data: rawptr, timeout: c.uint, ) { transfer.dev_handle = dev_handle transfer.endpoint = endpoint transfer.type = .BULK transfer.timeout = timeout transfer.buffer = buffer transfer.length = length transfer.user_data = user_data transfer.callback = callback } // Populate the required fields for a bulk stream transfer. // Since version 1.0.19. fill_bulk_stream_transfer :: proc "contextless" ( transfer: ^Transfer, dev_handle: Device_Handle, endpoint: u8, stream_id: u32, buffer: [^]u8, length: c.int, callback: Transfer_Cb, user_data: rawptr, timeout: c.uint, ) { fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length, callback, user_data, timeout) transfer.type = .BULK_STREAM transfer_set_stream_id(transfer, stream_id) } // Populate the required fields for an interrupt transfer. fill_interrupt_transfer :: proc "contextless" ( transfer: ^Transfer, dev_handle: Device_Handle, endpoint: u8, buffer: [^]u8, length: c.int, callback: Transfer_Cb, user_data: rawptr, timeout: c.uint, ) { transfer.dev_handle = dev_handle transfer.endpoint = endpoint transfer.type = .INTERRUPT transfer.timeout = timeout transfer.buffer = buffer transfer.length = length transfer.user_data = user_data transfer.callback = callback } // Populate the required fields for an isochronous transfer. fill_iso_transfer :: proc "contextless" ( transfer: ^Transfer, dev_handle: Device_Handle, endpoint: u8, buffer: [^]u8, length: c.int, num_iso_packets: c.int, callback: Transfer_Cb, user_data: rawptr, timeout: c.uint, ) { transfer.dev_handle = dev_handle transfer.endpoint = endpoint transfer.type = .ISOCHRONOUS transfer.timeout = timeout transfer.buffer = buffer transfer.length = length transfer.num_iso_packets = num_iso_packets transfer.user_data = user_data transfer.callback = callback } // Set the length of all packets in an isochronous transfer to the same value. set_iso_packet_lengths :: proc "contextless" (transfer: ^Transfer, length: c.uint) { descs := ([^]Iso_Packet_Descriptor)(&transfer.iso_packet_desc) for i in 0 ..< int(transfer.num_iso_packets) { descs[i].length = length } } // Locate the position of an isochronous packet within the buffer by // accumulating lengths of all preceding packets. get_iso_packet_buffer :: proc "contextless" (transfer: ^Transfer, packet: c.uint) -> [^]u8 { if c.int(packet) >= transfer.num_iso_packets do return nil descs := ([^]Iso_Packet_Descriptor)(&transfer.iso_packet_desc) offset: uint = 0 for i in 0 ..< int(packet) { offset += uint(descs[i].length) } return ([^]u8)(&transfer.buffer[offset]) } // Locate the position of an isochronous packet within the buffer, assuming // all packets are the same size as the first packet. get_iso_packet_buffer_simple :: proc "contextless" (transfer: ^Transfer, packet: c.uint) -> [^]u8 { if c.int(packet) >= transfer.num_iso_packets do return nil descs := ([^]Iso_Packet_Descriptor)(&transfer.iso_packet_desc) offset := uint(descs[0].length) * uint(packet) return ([^]u8)(&transfer.buffer[offset]) } // Retrieve a descriptor from the default control pipe. Convenience wrapper // around `control_transfer()`. get_descriptor :: proc( dev_handle: Device_Handle, desc_type: u8, desc_index: u8, data: [^]u8, length: c.int, ) -> c.int { return control_transfer( dev_handle, u8(Endpoint_Direction.IN), u8(Standard_Request.GET_DESCRIPTOR), u16(desc_type) << 8 | u16(desc_index), 0, data, u16(length), 1000, ) } // Retrieve a string descriptor from a device. The string returned is Unicode // (UTF-16LE) as per the USB spec. Convenience wrapper around `control_transfer()`. get_string_descriptor :: proc( dev_handle: Device_Handle, desc_index: u8, langid: u16, data: [^]u8, length: c.int, ) -> c.int { return control_transfer( dev_handle, u8(Endpoint_Direction.IN), u8(Standard_Request.GET_DESCRIPTOR), u16(u16(Descriptor_Type.STRING) << 8 | u16(desc_index)), langid, data, u16(length), 1000, ) } // ============================================================================= // Tests // ============================================================================= import "core:testing" @(test) init_test :: proc(t: ^testing.T) { result := init(nil) testing.expect_value(t, result, Error.SUCCESS) if result == .SUCCESS { exit(nil) } }