diff --git a/vendor/libusb/libusb.odin b/vendor/libusb/libusb.odin index c517094..93fbb95 100644 --- a/vendor/libusb/libusb.odin +++ b/vendor/libusb/libusb.odin @@ -1,1167 +1,823 @@ package libusb import "core:c" -import "core:fmt" -//TODO: Make multiplatform -import "core:sys/posix" -//TODO: Probably want to switch this to being statically linked -foreign import lib "system:usb-1.0" +// ============================================================================= +// Foreign import — platform-selected linking +// ============================================================================= -/** \ingroup libusb_desc - * Device and/or Interface Class codes */ -Class_Code :: enum c.int { - /** In the context of a \ref libusb_device_descriptor "device descriptor", - * this bDeviceClass value indicates that each interface specifies its - * own class information and all interfaces operate independently. - */ - CLASS_PER_INTERFACE = 0x00, - /** Audio class */ - CLASS_AUDIO = 0x01, - /** Communications class */ - CLASS_COMM = 0x02, - /** Human Interface Device class */ - CLASS_HID = 0x03, - /** Physical */ - CLASS_PHYSICAL = 0x05, - /** Image class */ - CLASS_IMAGE = 0x06, - CLASS_PTP = 0x06, /* legacy name from libusb-0.1 usb.h */ - /** Printer class */ - CLASS_PRINTER = 0x07, - /** Mass storage class */ - CLASS_MASS_STORAGE = 0x08, - /** Hub class */ - CLASS_HUB = 0x09, - /** Data class */ - CLASS_DATA = 0x0a, - /** Smart Card */ - CLASS_SMART_CARD = 0x0b, - /** Content Security */ - CLASS_CONTENT_SECURITY = 0x0d, - /** Video */ - CLASS_VIDEO = 0x0e, - /** Personal Healthcare */ - CLASS_PERSONAL_HEALTHCARE = 0x0f, - /** Diagnostic Device */ - CLASS_DIAGNOSTIC_DEVICE = 0xdc, - /** Wireless class */ - CLASS_WIRELESS = 0xe0, - /** Miscellaneous class */ - CLASS_MISCELLANEOUS = 0xef, - /** Application class */ - CLASS_APPLICATION = 0xfe, - /** Class is vendor-specific */ - CLASS_VENDOR_SPEC = 0xff, +// 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" } -/** \ingroup libusb_desc - * Descriptor types as defined by the USB specification. */ -Descriptor_Type :: enum c.int { - /** Device descriptor. See libusb_device_descriptor. */ - DEVICE = 0x01, - /** Configuration descriptor. See libusb_config_descriptor. */ - CONFIG = 0x02, - /** String descriptor */ - STRING = 0x03, - /** Interface descriptor. See libusb_interface_descriptor. */ - INTERFACE = 0x04, - /** Endpoint descriptor. See libusb_endpoint_descriptor. */ - ENDPOINT = 0x05, - /** Interface Association Descriptor. - * See libusb_interface_association_descriptor */ - INTERFACE_ASSOCIATION = 0x0b, - /** BOS descriptor */ - BOS = 0x0f, - /** Device Capability descriptor */ - DEVICE_CAPABILITY = 0x10, - /** HID descriptor */ - HID = 0x21, - /** HID report descriptor */ - REPORT = 0x22, - /** Physical descriptor */ - PHYSICAL = 0x23, - /** Hub descriptor */ - HUB = 0x29, - /** SuperSpeed Hub descriptor */ - SUPERSPEED_HUB = 0x2a, - /** SuperSpeed Endpoint Companion descriptor */ - SS_ENDPOINT_COMPANION = 0x30, -} +// libusb API version (1.0.30). +API_VERSION :: 0x0100010C -/* Descriptor sizes per descriptor type */ +// 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 /* Audio extension */ +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 */ +// 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 -/* We unwrap the BOS => define its max size */ -DT_BOS_MAX_SIZE :: 42 +// 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_MASK :: 0x0f /* in bEndpointAddress */ +// Endpoint address / direction masks. +ENDPOINT_ADDRESS_MASK :: 0x0f ENDPOINT_DIR_MASK :: 0x80 -/** \ingroup libusb_desc - * Endpoint direction. Values for bit 7 of the - * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. - */ -Endpoint_Direction :: enum c.int { - /** Out: host-to-device */ - ENDPOINT_OUT = 0x00, - /** In: device-to-host */ - ENDPOINT_IN = 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, } -TRANSFER_TYPE_MASK :: 0x03 /* in bmAttributes */ +// Legacy alias from libusb-0.1. +CLASS_PTP :: Class_Code.IMAGE -/** \ingroup libusb_desc - * Endpoint transfer type. Values for bits 0:1 of the - * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. - */ +// 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 endpoint */ CONTROL = 0x0, - /** Isochronous endpoint */ ISOCHRONOUS = 0x1, - /** Bulk endpoint */ BULK = 0x2, - /** Interrupt endpoint */ INTERRUPT = 0x3, } -/** \ingroup libusb_misc - * Standard requests, as defined in table 9-5 of the USB 3.0 specifications */ +// Standard requests, as defined in table 9-5 of the USB 3.0 specifications. Standard_Request :: enum c.int { - /** Request status of the specific recipient */ GET_STATUS = 0x00, - /** Clear or disable a specific feature */ CLEAR_FEATURE = 0x01, - - /* 0x02 is reserved */ - - /** Set or enable a specific feature */ SET_FEATURE = 0x03, - - /* 0x04 is reserved */ - - /** Set device address for all future accesses */ SET_ADDRESS = 0x05, - /** Get the specified descriptor */ GET_DESCRIPTOR = 0x06, - /** Used to update existing descriptors or add new descriptors */ SET_DESCRIPTOR = 0x07, - /** Get the current device configuration value */ GET_CONFIGURATION = 0x08, - /** Set device configuration */ SET_CONFIGURATION = 0x09, - /** Return the selected alternate setting for the specified interface */ GET_INTERFACE = 0x0a, - /** Select an alternate interface for the specified interface */ SET_INTERFACE = 0x0b, - /** Set then report an endpoint's synchronization frame */ SYNCH_FRAME = 0x0c, - /** Sets both the U1 and U2 Exit Latency */ SET_SEL = 0x30, - /** Delay from the time a host transmits a packet to the time it is - * received by the device. */ SET_ISOCH_DELAY = 0x31, } -/** \ingroup libusb_misc - * Request type bits of the - * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control - * transfers. */ +// Request type bits of the bmRequestType field in control transfers. Request_Type :: enum c.int { - /** Standard */ - STANDARD = (0x00 << 5), - /** Class */ - CLASS = (0x01 << 5), - /** Vendor */ - VENDOR = (0x02 << 5), - /** Reserved */ - RESERVED = (0x03 << 5), + STANDARD = 0x00 << 5, + CLASS = 0x01 << 5, + VENDOR = 0x02 << 5, + RESERVED = 0x03 << 5, } -/** \ingroup libusb_misc - * Recipient bits of the - * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control - * transfers. Values 4 through 31 are reserved. */ +// Recipient bits of the bmRequestType field in control transfers. +// Values 4 through 31 are reserved. Request_Recipient :: enum c.int { - /** Device */ DEVICE = 0x00, - /** Interface */ INTERFACE = 0x01, - /** Endpoint */ ENDPOINT = 0x02, - /** Other */ OTHER = 0x03, } -ISO_SYNC_TYPE_MASK :: 0x0c - -/** \ingroup libusb_desc - * Synchronization type for isochronous endpoints. Values for bits 2:3 of the - * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in - * libusb_endpoint_descriptor. - */ +// Synchronization type for isochronous endpoints. +// Values for bits 2:3 of bmAttributes. Iso_Sync_Type :: enum c.int { - /** No synchronization */ NONE = 0x0, - /** Asynchronous */ ASYNC = 0x1, - /** Adaptive */ ADAPTIVE = 0x2, - /** Synchronous */ SYNC = 0x3, } -ISO_USAGE_TYPE_MASK :: 0x30 - -/** \ingroup libusb_desc - * Usage type for isochronous endpoints. Values for bits 4:5 of the - * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in - * libusb_endpoint_descriptor. - */ +// Usage type for isochronous endpoints. +// Values for bits 4:5 of bmAttributes. Iso_Usage_Type :: enum c.int { - /** Data endpoint */ DATA = 0x0, - /** Feedback endpoint */ FEEDBACK = 0x1, - /** Implicit feedback Data endpoint */ IMPLICIT = 0x2, } -/** \ingroup libusb_desc - * Supported speeds (wSpeedSupported) bitfield. Indicates what - * speeds the device supports. - */ -Supported_Speed :: enum c.int { - /** Low speed operation supported (1.5MBit/s). */ - LOW_SPEED_OPERATION = (1 << 0), - /** Full speed operation supported (12MBit/s). */ - FULL_SPEED_OPERATION = (1 << 1), - /** High speed operation supported (480MBit/s). */ - HIGH_SPEED_OPERATION = (1 << 2), - /** Superspeed operation supported (5000MBit/s). */ - SUPER_SPEED_OPERATION = (1 << 3), +// 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] -/** \ingroup libusb_desc - * Masks for the bits of the - * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field - * of the USB 2.0 Extension descriptor. - */ -Usb2_Extension_Attributes :: enum c.int { - /** Supports Link Power Management (LPM) */ - BM_LPM_SUPPORT = (1 << 1), +// 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] -/** \ingroup libusb_desc - * Masks for the bits of the - * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field - * field of the SuperSpeed USB Device Capability descriptor. - */ -Ss_Usb_Device_Capability_Attributes :: enum c.int { - /** Supports Latency Tolerance Messages (LTM) */ - BM_LTM_SUPPORT = (1 << 1), +// 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] -/** \ingroup libusb_desc - * USB capability types - */ +// USB capability types (BOS). Bos_Type :: enum c.int { - /** Wireless USB device capability */ WIRELESS_USB_DEVICE_CAPABILITY = 0x01, - /** USB 2.0 extensions */ USB_2_0_EXTENSION = 0x02, - /** SuperSpeed USB device capability */ SS_USB_DEVICE_CAPABILITY = 0x03, - /** Container ID type */ CONTAINER_ID = 0x04, - /** Platform descriptor */ PLATFORM_DESCRIPTOR = 0x05, + SUPERSPEED_PLUS_CAPABILITY = 0x0A, } -/** \ingroup libusb_desc - * A structure representing the standard USB device descriptor. This - * descriptor is documented in section 9.6.1 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -Device_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this - * context. */ - bDescriptorType: u8, - /** USB specification release number in binary-coded decimal. A value of - * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ - bcdUSB: u16, - /** USB-IF class code for the device. See \ref libusb_class_code. */ - bDeviceClass: u8, - /** USB-IF subclass code for the device, qualified by the bDeviceClass - * value */ - bDeviceSubClass: u8, - /** USB-IF protocol code for the device, qualified by the bDeviceClass and - * bDeviceSubClass values */ - 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 binary-coded decimal */ - bcdDevice: u16, - /** Index of string descriptor describing manufacturer */ - iManufacturer: u8, - /** Index of string descriptor describing product */ - iProduct: u8, - /** Index of string descriptor containing device serial number */ - iSerialNumber: u8, - /** Number of possible configurations */ - bNumConfigurations: u8, -} - -/** \ingroup libusb_desc - * A structure representing the standard USB endpoint descriptor. This - * descriptor is documented in section 9.6.6 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -Endpoint_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in - * this context. */ - bDescriptorType: u8, - /** The address of the endpoint described by this descriptor. Bits 0:3 are - * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, - * see \ref libusb_endpoint_direction. */ - bEndpointAddress: u8, - /** Attributes which apply to the endpoint when it is configured using - * the bConfigurationValue. Bits 0:1 determine the transfer type and - * correspond to \ref libusb_endpoint_transfer_type. Bits 2:3 are only used - * for isochronous endpoints and correspond to \ref libusb_iso_sync_type. - * Bits 4:5 are also only used for isochronous endpoints and correspond to - * \ref libusb_iso_usage_type. Bits 6:7 are reserved. */ - bmAttributes: u8, - /** Maximum packet size this endpoint is capable of sending/receiving. */ - wMaxPacketSize: u16, - /** Interval for polling endpoint for data transfers. */ - bInterval: u8, - /** For audio devices only: the rate at which synchronization feedback - * is provided. */ - bRefresh: u8, - /** For audio devices only: the address if the synch endpoint */ - bSynchAddress: u8, - /** Extra descriptors. If libusb encounters unknown endpoint descriptors, - * it will store them here, should you wish to parse them. */ - extra: [^]u8, - /** Length of the extra descriptors, in bytes. Must be non-negative. */ - extra_length: c.int, -} - -/** \ingroup libusb_desc - * A structure representing the standard USB interface association descriptor. - * This descriptor is documented in section 9.6.4 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -Interface_Association_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE_ASSOCIATION - * LIBUSB_DT_INTERFACE_ASSOCIATION in this context. */ - bDescriptorType: u8, - /** Interface number of the first interface that is associated - * with this function */ - bFirstInterface: u8, - /** Number of contiguous interfaces that are associated with - * this function */ - bInterfaceCount: u8, - /** USB-IF class code for this function. - * A value of zero is not allowed in this descriptor. - * If this field is 0xff, the function class is vendor-specific. - * All other values are reserved for assignment by the USB-IF. - */ - bFunctionClass: u8, - /** USB-IF subclass code for this function. - * If this field is not set to 0xff, all values are reserved - * for assignment by the USB-IF - */ - bFunctionSubClass: u8, - /** USB-IF protocol code for this function. - * These codes are qualified by the values of the bFunctionClass - * and bFunctionSubClass fields. - */ - bFunctionProtocol: u8, - /** Index of string descriptor describing this function */ - iFunction: u8, -} - -/** \ingroup libusb_desc - * Structure containing an array of 0 or more interface association - * descriptors - */ -Interface_Association_Descriptor_Array :: struct { - /** Array of interface association descriptors. The size of this array - * is determined by the length field. - */ - iad: [^]Interface_Association_Descriptor, - /** Number of interface association descriptors contained. Read-only. */ - length: c.int, -} - -/** \ingroup libusb_desc - * A structure representing the standard USB interface descriptor. This - * descriptor is documented in section 9.6.5 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -Interface_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE - * in this context. */ - bDescriptorType: u8, - /** Number of this interface */ - bInterfaceNumber: u8, - /** Value used to select this alternate setting for this interface */ - bAlternateSetting: u8, - /** Number of endpoints used by this interface (excluding the control - * endpoint). */ - bNumEndpoints: u8, - /** USB-IF class code for this interface. See \ref libusb_class_code. */ - bInterfaceClass: u8, - /** USB-IF subclass code for this interface, qualified by the - * bInterfaceClass value */ - bInterfaceSubClass: u8, - /** USB-IF protocol code for this interface, qualified by the - * bInterfaceClass and bInterfaceSubClass values */ - bInterfaceProtocol: u8, - /** Index of string descriptor describing this interface */ - iInterface: u8, - /** Array of endpoint descriptors. This length of this array is determined - * by the bNumEndpoints field. */ - endpoint: [^]Endpoint_Descriptor, - /** Extra descriptors. If libusb encounters unknown interface descriptors, - * it will store them here, should you wish to parse them. */ - extra: [^]u8, - /** Length of the extra descriptors, in bytes. Must be non-negative. */ - extra_length: c.int, -} - -/** \ingroup libusb_desc - * A collection of alternate settings for a particular USB interface. - */ -Interface :: struct { - /** Array of interface descriptors. The length of this array is determined - * by the num_altsetting field. */ - altsetting: [^]Interface_Descriptor, - /** The number of alternate settings that belong to this interface. - * Must be non-negative. */ - num_altsetting: c.int, -} - -/** \ingroup libusb_desc - * A structure representing the standard USB configuration descriptor. This - * descriptor is documented in section 9.6.3 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -Config_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG - * in this context. */ - bDescriptorType: u8, - /** Total length of data returned for this configuration */ - wTotalLength: u16, - /** Number of interfaces supported by this configuration */ - bNumInterfaces: u8, - /** Identifier value for this configuration */ - bConfigurationValue: u8, - /** Index of string descriptor describing this configuration */ - iConfiguration: u8, - /** Configuration characteristics */ - bmAttributes: u8, - /** Maximum power consumption of the USB device from this bus in this - * configuration when the device is fully operation. Expressed in units - * of 2 mA when the device is operating in high-speed mode and in units - * of 8 mA when the device is operating in super-speed mode. */ - MaxPower: u8, - /** Array of interfaces supported by this configuration. The length of - * this array is determined by the bNumInterfaces field. */ - interface: [^]Interface, - /** Extra descriptors. If libusb encounters unknown configuration - * descriptors, it will store them here, should you wish to parse them. */ - extra: [^]u8, - /** Length of the extra descriptors, in bytes. Must be non-negative. */ - extra_length: c.int, -} - -/** \ingroup libusb_desc - * A structure representing the superspeed endpoint companion - * descriptor. This descriptor is documented in section 9.6.7 of - * the USB 3.0 specification. All multiple-byte fields are represented in - * host-endian format. - */ -Ss_Endpoint_Companion_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in - * this context. */ - bDescriptorType: u8, - /** The maximum number of packets the endpoint can send or - * receive as part of a burst. */ - bMaxBurst: u8, - /** In bulk EP: bits 4:0 represents the maximum number of - * streams the EP supports. In isochronous EP: bits 1:0 - * represents the Mult - a zero based value that determines - * the maximum number of packets within a service interval */ - bmAttributes: u8, - /** The total number of bytes this EP will transfer every - * service interval. Valid only for periodic EPs. */ - wBytesPerInterval: u16, -} - -/** \ingroup libusb_desc - * A generic representation of a BOS Device Capability descriptor. It is - * advised to check bDevCapabilityType and call the matching - * libusb_get_*_descriptor function to get a structure fully matching the type. - */ -Bos_Dev_Capability_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY - * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ - bDescriptorType: u8, - /** Device Capability type */ - bDevCapabilityType: u8, - /** Device Capability data (bLength - 3 bytes) */ - dev_capability_data: [0]u8, -} - -/** \ingroup libusb_desc - * A structure representing the Binary Device Object Store (BOS) descriptor. - * This descriptor is documented in section 9.6.2 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -Bos_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS - * in this context. */ - bDescriptorType: u8, - /** Length of this descriptor and all of its sub descriptors */ - wTotalLength: u16, - /** The number of separate device capability descriptors in - * the BOS */ - bNumDeviceCaps: u8, - /** bNumDeviceCap Device Capability Descriptors - * Isochronous packet descriptors, for isochronous transfers only. - * This is a C flexible array member and memory must be handled completely manually if it's used.*/ - dev_capability: [0]^Bos_Dev_Capability_Descriptor, -} - -/** \ingroup libusb_desc - * A structure representing the USB 2.0 Extension descriptor - * This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -Usb2_Extension_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY - * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ - bDescriptorType: u8, - /** Capability type. Will have value - * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION - * LIBUSB_BT_USB_2_0_EXTENSION in this context. */ - bDevCapabilityType: u8, - /** Bitmap encoding of supported device level features. - * A value of one in a bit location indicates a feature is - * supported; a value of zero indicates it is not supported. - * See \ref libusb_usb_2_0_extension_attributes. */ - bmAttributes: u32, -} - -/** \ingroup libusb_desc - * A structure representing the SuperSpeed USB Device Capability descriptor - * This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -Ss_Usb_Device_Capability_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY - * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ - bDescriptorType: u8, - /** Capability type. Will have value - * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY - * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY in this context. */ - bDevCapabilityType: u8, - /** Bitmap encoding of supported device level features. - * A value of one in a bit location indicates a feature is - * supported; a value of zero indicates it is not supported. - * See \ref libusb_ss_usb_device_capability_attributes. */ - bmAttributes: u8, - /** Bitmap encoding of the speed supported by this device when - * operating in SuperSpeed mode. See \ref libusb_supported_speed. */ - wSpeedSupported: u16, - /** The lowest speed at which all the functionality supported - * by the device is available to the user. For example if the - * device supports all its functionality when connected at - * full speed and above then it sets this value to 1. */ - bFunctionalitySupport: u8, - /** U1 Device Exit Latency. */ - bU1DevExitLat: u8, - /** U2 Device Exit Latency. */ - bU2DevExitLat: u16, -} - -/** \ingroup libusb_desc - * A structure representing the Container ID descriptor. - * This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification. - * All multiple-byte fields, except UUIDs, are represented in host-endian format. - */ -Container_Id_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY - * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ - bDescriptorType: u8, - /** Capability type. Will have value - * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID - * LIBUSB_BT_CONTAINER_ID in this context. */ - bDevCapabilityType: u8, - /** Reserved field */ - bReserved: u8, - /** 128 bit UUID */ - ContainerID: u128, -} - -/** \ingroup libusb_desc - * A structure representing a Platform descriptor. - * This descriptor is documented in section 9.6.2.4 of the USB 3.2 specification. - * This struct contains a mandatory C flexible array member all of it's memory must be handled completely manually. - */ -Platform_Descriptor :: struct { - /** Size of this descriptor (in bytes) */ - bLength: u8, - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY - * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ - bDescriptorType: u8, - /** Capability type. Will have value - * \ref libusb_capability_type::LIBUSB_BT_PLATFORM_DESCRIPTOR - * LIBUSB_BT_CONTAINER_ID in this context. */ - bDevCapabilityType: u8, - /** Reserved field */ - bReserved: u8, - /** 128 bit UUID */ - PlatformCapabilityUUID: u128, - /** Capability data (bLength - 20) - * This is a C flexible array member and memory must be handled completely manually.*/ - CapabilityData: [0]u8, -} - -//TODO: Add `libusb_control_setup` - -Context :: distinct rawptr -Device :: distinct rawptr -Device_Handle :: distinct rawptr - -/** \ingroup libusb_lib - * Structure providing the version of the libusb runtime - */ -Version :: struct { - /** Library major version. */ - major: u16, - /** Library minor version. */ - minor: u16, - /** Library micro version. */ - micro: u16, - /** Library nano version. */ - nano: u16, - /** Library release candidate suffix string, e.g. "-rc4". */ - rc: cstring, - /** For ABI compatibility only. */ - describe: cstring, -} - -/** \ingroup libusb_dev - * Speed codes. Indicates the speed at which the device is operating. - */ +// Speed codes. Indicates the speed at which the device is operating. Speed :: enum c.int { - /** The OS doesn't report or know the device speed. */ - UNKNOWN = 0, - /** The device is operating at low speed (1.5MBit/s). */ - LOW = 1, - /** The device is operating at full speed (12MBit/s). */ - FULL = 2, - /** The device is operating at high speed (480MBit/s). */ - HIGH = 3, - /** The device is operating at super speed (5000MBit/s). */ - SUPER = 4, - /** The device is operating at super speed plus (10000MBit/s). */ - SUPER_PLUS = 5, + UNKNOWN = 0, + LOW = 1, + FULL = 2, + HIGH = 3, + SUPER = 4, + SUPER_PLUS = 5, + SUPER_PLUS_X2 = 6, } -/** \ingroup libusb_misc - * Error codes. Most libusb functions return 0 on success or one of these - * codes on failure. - * You can call libusb_error_name() to retrieve a string representation of an - * error code or libusb_strerror() to get an end-user suitable description of - * an error code. - */ +// 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 (no error) */ SUCCESS = 0, - /** Input/output error */ IO = -1, - /** Invalid parameter */ INVALID_PARAM = -2, - /** Access denied (insufficient permissions) */ ACCESS = -3, - /** No such device (it may have been disconnected) */ NO_DEVICE = -4, - /** Entity not found */ NOT_FOUND = -5, - /** Resource busy */ BUSY = -6, - /** Operation timed out */ TIMEOUT = -7, - /** Overflow */ OVERFLOW = -8, - /** Pipe error */ PIPE = -9, - /** System call interrupted (perhaps due to signal) */ INTERRUPTED = -10, - /** Insufficient memory */ NO_MEM = -11, - /** Operation not supported or unimplemented on this platform */ NOT_SUPPORTED = -12, - /* NB: Remember to update LIBUSB_ERROR_COUNT below as well as the - message strings in strerror.c when adding new error codes here. */ - - /** Other error */ OTHER = -99, } -/* Total number of error codes in enum libusb_error */ -ERROR_COUNT :: 14 - -/** \ingroup libusb_asyncio - * Transfer type */ -TransferType :: enum c.int { - /** Control transfer */ +// 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 transfer */ ISOCHRONOUS = 1, - /** Bulk transfer */ BULK = 2, - /** Interrupt transfer */ INTERRUPT = 3, - /** Bulk stream transfer */ BULK_STREAM = 4, } -/** \ingroup libusb_asyncio - * Transfer status codes */ +// Transfer status codes. Transfer_Status :: enum c.int { - /** Transfer completed without error. Note that this does not indicate - * that the entire amount of requested data was transferred. */ COMPLETED, - /** Transfer failed */ ERROR, - /** Transfer timed out */ TIMED_OUT, - /** Transfer was cancelled */ CANCELLED, - /** For bulk/interrupt endpoints: halt condition detected (endpoint - * stalled). For control endpoints: control request not supported. */ STALL, - /** Device was disconnected */ NO_DEVICE, - /** Device sent more data than requested */ OVERFLOW, } -Transfer_Flag_Bits :: enum u8 { - /** Report short frames as errors */ +// 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 libusb_free_transfer(). - * Note that buffers allocated with libusb_dev_mem_alloc() should not - * be attempted freed in this way, since free() is not an appropriate - * way to release such memory. */ + // Automatically free() transfer buffer during `free_transfer()`. + // Do not use with buffers from `dev_mem_alloc()`. FREE_BUFFER, - /** Automatically call libusb_free_transfer() after callback returns. - * If this flag is set, it is illegal to call libusb_free_transfer() - * from your transfer callback, as this will result in a double-free - * when this flag is acted upon. */ + // 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. This is useful - * when a device protocol mandates that each logical request is - * terminated by an incomplete packet (i.e. the logical requests are - * not separated by other means). - * - * This flag only affects host-to-device transfers to bulk and interrupt - * endpoints. In other situations, it is ignored. - * - * This flag only affects transfers with a length that is a multiple of - * the endpoint's wMaxPacketSize. On transfers of other lengths, this - * flag has no effect. Therefore, if you are working with a device that - * needs a ZLP whenever the end of the logical request falls on a packet - * boundary, then it is sensible to set this flag on every - * transfer (you do not have to worry about only setting it on transfers - * that end on the boundary). - * - * This flag is currently only supported on Linux. - * On other systems, libusb_submit_transfer() will return - * \ref LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this - * flag is set. - * - * Available since libusb-1.0.9. - */ + // 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] -Transfer_Flag :: bit_set[Transfer_Flag_Bits;u8] - -/** \ingroup libusb_asyncio - * Isochronous packet descriptor. */ -Iso_Packet_Descriptor :: struct { - /** Length of data to request in this packet */ - length: c.uint, - /** Amount of data that was actually transferred */ - actual_length: c.uint, - /** Status code for this packet */ - status: Transfer_Status, -} - -Transfer_Cb :: #type proc "c" (transfer: ^Transfer) - -/** \ingroup libusb_asyncio - * The generic USB transfer structure. The user populates this structure and - * then submits it in order to request a transfer. After the transfer has - * completed, the library populates the transfer with the results and passes - * it back to the user. - */ -Transfer :: struct { - /** Handle of the device that this transfer will be submitted to */ - dev_handle: Device_Handle, - /** A bitwise OR combination of \ref libusb_transfer_flags. */ - flags: Transfer_Flag, - /** Address of the endpoint where this transfer will be sent. */ - endpoint: u8, - /** Type of the transfer from \ref libusb_transfer_type */ - type: u8, - /** Timeout for this transfer in milliseconds. A value of 0 indicates no - * timeout. */ - timeout: c.uint, - /** The status of the transfer. Read-only, and only for use within - * transfer callback function. - * - * If this is an isochronous transfer, this field may read COMPLETED even - * if there were errors in the frames. Use the - * \ref libusb_iso_packet_descriptor::status "status" field in each packet - * to determine if errors occurred. */ - status: Transfer_Status, - /** Length of the data buffer. Must be non-negative. */ - length: c.int, - /** Actual length of data that was transferred. Read-only, and only for - * use within transfer callback function. Not valid for isochronous - * endpoint transfers. */ - actual_length: c.int, - /** Callback function. This will be invoked when the transfer completes, - * fails, or is cancelled. */ - callback: Transfer_Cb, - /** User context data. Useful for associating specific data to a transfer - * that can be accessed from within the callback function. - * - * This field may be set manually or is taken as the `user_data` parameter - * of the following functions: - * - libusb_fill_bulk_transfer() - * - libusb_fill_bulk_stream_transfer() - * - libusb_fill_control_transfer() - * - libusb_fill_interrupt_transfer() - * - libusb_fill_iso_transfer() */ - user_data: rawptr, - /** Data buffer */ - buffer: [^]u8, - /** Number of isochronous packets. Only used for I/O with isochronous - * endpoints. Must be non-negative. */ - num_iso_packets: c.int, - /** Isochronous packet descriptors, for isochronous transfers only. - * This is a C flexible array member and memory must be handled completely manually if it's used.*/ - iso_packet_desc: [0]Iso_Packet_Descriptor, -} - -/** \ingroup libusb_misc - * Capabilities supported by an instance of libusb on the current running - * platform. Test if the loaded library supports a given capability by calling - * \ref libusb_has_capability(). - */ -Capability :: enum c.uint { - /** The libusb_has_capability() API is available. */ +// Capabilities supported by this instance of libusb. +// Test with `has_capability()`. +Capability :: enum u32 { HAS_CAPABILITY = 0x0000, - /** Hotplug support is available on this platform. */ HAS_HOTPLUG = 0x0001, - /** The library can access HID devices without requiring user intervention. - * Note that before being able to actually access an HID device, you may - * still have to call additional libusb functions such as - * \ref libusb_detach_kernel_driver(). */ HAS_HID_ACCESS = 0x0100, - /** The library supports detaching of the default USB driver, using - * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */ SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101, } -/** \ingroup libusb_lib - * Log message levels. - */ +// Log message levels. Log_Level :: enum c.int { - /** (0) : No messages ever emitted by the library (default) */ NONE = 0, - /** (1) : Error messages are emitted */ ERROR = 1, - /** (2) : Warning and error messages are emitted */ WARNING = 2, - /** (3) : Informational, warning and error messages are emitted */ INFO = 3, - /** (4) : All messages are emitted */ DEBUG = 4, } -/** \ingroup libusb_lib - * Log callback mode. - * - * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107 - * - * \see libusb_set_log_cb() - */ -Log_Cb_Mode :: enum c.int { - /** Callback function handling all log messages. */ - GLOBAL = (1 << 0), - /** Callback function handling context related log messages. */ - CONTEXT = (1 << 1), +// 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] -/** \ingroup libusb_lib - * Available option values for libusb_set_option() and libusb_init_context(). - */ +// Available option values for `set_option()` and `init_context()`. Option :: enum c.int { - /** Set the log message verbosity. - * - * This option must be provided an argument of type \ref libusb_log_level. - * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever - * printed. If you choose to increase the message verbosity level, ensure - * that your application does not close the stderr file descriptor. - * - * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative - * with its message logging and most of the time, will only log messages that - * explain error conditions and other oddities. This will help you debug - * your software. - * - * If the LIBUSB_DEBUG environment variable was set when libusb was - * initialized, this option does nothing: the message verbosity is fixed - * to the value in the environment variable. - * - * If libusb was compiled without any message logging, this option does - * nothing: you'll never get any messages. - * - * If libusb was compiled with verbose debug message logging, this option - * does nothing: you'll always get messages from all levels. - */ + // Set the log message verbosity. Argument: `Log_Level` (as c.int). LOG_LEVEL = 0, - /** Use the UsbDk backend for a specific context, if available. - * - * This option should be set at initialization with libusb_init_context() - * otherwise unspecified behavior may occur. - * - * Only valid on Windows. Ignored on all other platforms. - */ + // Use the UsbDk backend (Windows only, ignored elsewhere). USE_USBDK = 1, - /** Do not scan for devices - * - * With this option set, libusb will skip scanning devices in - * libusb_init_context(). - * - * Hotplug functionality will also be deactivated. - * - * The option is useful in combination with libusb_wrap_sys_device(), - * which can access a device directly without prior device scanning. - * - * This is typically needed on Android, where access to USB devices - * is limited. - * - * This option should only be used with libusb_init_context() - * otherwise unspecified behavior may occur. - * - * Only valid on Linux. Ignored on all other platforms. - */ + // Do not scan for devices in `init_context()` (Linux only). + // Alias: WEAK_AUTHORITY. NO_DEVICE_DISCOVERY = 2, - /** Set the context log callback function. - * - * Set the log callback function either on a context or globally. This - * option must be provided an argument of type \ref libusb_log_cb. - * Using this option with a NULL context is equivalent to calling - * libusb_set_log_cb() with mode \ref LIBUSB_LOG_CB_GLOBAL. - * Using it with a non-NULL context is equivalent to calling - * libusb_set_log_cb() with mode \ref LIBUSB_LOG_CB_CONTEXT. - */ + // Set the context log callback. Argument: `Log_Cb`. LOG_CB = 3, MAX = 4, } -Log_Cb :: #type proc "c" (ctx: ^Context, level: Log_Level, str: cstring) +// Alias for NO_DEVICE_DISCOVERY. +OPTION_WEAK_AUTHORITY :: Option.NO_DEVICE_DISCOVERY -Init_Option_Value :: struct #raw_union { - ival: c.int, - libusb_log_cb: Log_Cb, +// 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, } -/** \ingroup libusb_hotplug - * - * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 - * - * Hotplug events */ -Hotplug_Event :: enum c.int { - /** A device has been plugged in and is ready to use */ - DEVICE_ARRIVED = (1 << 0), - /** A device has left and is no longer available. - * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device. - * It is safe to call libusb_get_device_descriptor on a device that has left */ - DEVICE_LEFT = (1 << 1), -} +//----- Opaque handles ---------------------------------- -/** \ingroup libusb_hotplug - * - * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 - * - * Hotplug flags */ -Hotplug_Flag_Bits :: enum c.int { - /** Arm the callback and fire it for all matching currently attached devices. */ - ENUMERATE = 0, -} +Context :: distinct rawptr +Device :: distinct rawptr +Device_Handle :: distinct rawptr -Hotplug_Flag :: bit_set[Hotplug_Flag_Bits;c.int] +// Hotplug callback handle, returned by `hotplug_register_callback()`. +Callback_Handle :: distinct c.int -/** \ingroup libusb_hotplug - * Convenience macro when not using any flags */ -HOTPLUG_NO_FLAGS :: 0 -/** \ingroup libusb_hotplug - * Wildcard matching for hotplug events */ -HOTPLUG_MATCH_ANY :: -1 +//----- 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_Event, + event: Hotplug_Events, user_data: rawptr, ) -> c.int -Callback_Handle :: distinct c.int -Transfer_Type :: enum c.int { - CONTROL = 0, - ISOCHRONOUS = 1, - BULK = 2, - INTERRUPT = 3, - STREAM = 4, -} +// 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) -Poll_Fd :: struct { - /** Numeric file descriptor */ - fd: c.int, - /** Event flags to poll for from . POLLIN indicates that you - * should monitor this file descriptor for becoming ready to read from, - * and POLLOUT indicates that you should monitor this file descriptor for - * nonblocking write readiness. */ - events: posix.Poll_Event, -} - -Poll_FD_Added_CB :: #type proc "c" (fd: posix.FD, events: posix.Poll_Event, user_data: rawptr) - -Poll_FD_Removed_CB :: #type proc "c" (fd: posix.FD, 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 ---------------------------------- + + // ---- Library initialization/deinitialization ---- + set_log_cb :: proc(ctx: Context, cb: Log_Cb, mode: Log_Cb_Mode) --- - set_option :: proc(ctx: Context, option: Option, args: c.va_list) -> Error --- + 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) --- - //----- Device handling and enumeration ---------------------------------- + // 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(device: [^]Device, unref_devices: c.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 --- - get_port_numbers :: proc(dev: Device, port_numbers: [^]u8, port_numbers_len: c.int) -> Error --- + // 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_max_iso_packet_size :: proc(dev: Device, endpoint: c.char) -> c.int --- + 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: rawptr, dev_handle: ^Device_Handle) -> Error --- + 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: Device_Handle, config: ^c.int) -> Error --- + 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 --- - interface_alt_setting :: proc(dev_handle: Device_Handle, interface_number: c.int, alternate_setting: 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 --- - kernel_driver_active :: proc(dev_handle: Device_Handle, interface_number: c.int) -> 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 ---------------------------------- + // ---- Miscellaneous ---- + has_capability :: proc(capability: Capability) -> c.int --- error_name :: proc(errcode: Error) -> cstring --- - get_version :: proc() -> Version --- + get_version :: proc() -> ^Version --- setlocale :: proc(locale: cstring) -> Error --- strerror :: proc(errcode: Error) -> cstring --- - //----- USB descriptors ---------------------------------- + // ---- 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_descriptor: ^^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_Direction, ep_comp: ^^Ss_Endpoint_Companion_Descriptor) -> Error --- + 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) --- @@ -1169,33 +825,53 @@ foreign lib { 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) --- - get_string_descriptor_ascii :: proc(dev_handle: Device_Handle, desc_index: u8, data: cstring, 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) -> Error --- + // 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 hotplug event notification ---------------------------------- - hotplug_register_callback :: proc(ctx: Context, events: c.int, flags: Hotplug_Flag, 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, hotplug_callback_handle: Callback_Handle) --- - hotplug_get_user_data :: proc(ctx: Context, hotplug_callback_handle: Callback_Handle) -> rawptr --- + // ---- Device string (libusb 1.0.30+) ---- - //----- Asynchronous device I/O ---------------------------------- + // 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 = 0) -> ^Transfer --- + 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 ---------------------------------- + // ---- Polling and timing ---- + try_lock_events :: proc(ctx: Context) -> c.int --- lock_events :: proc(ctx: Context) --- unlock_events :: proc(ctx: Context) --- @@ -1204,30 +880,260 @@ foreign lib { 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: ^posix.timeval) -> c.int --- - handle_events_timeout_completed :: proc(ctx: Context, tv: ^posix.timeval, completed: ^c.int) -> Error --- + 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: ^posix.timeval) -> Error --- + handle_events_locked :: proc(ctx: Context, tv: ^Timeval) -> Error --- pollfds_handle_timeouts :: proc(ctx: Context) -> c.int --- - get_next_timeout :: proc(ctx: Context, tv: ^posix.timeval) --- - set_pollfd_notifiers :: proc(ctx: Context, added_cb: Poll_FD_Added_CB, removed_cb: Poll_FD_Removed_CB, user_data: rawptr) --- - get_pollfds :: proc(ctx: Context) -> [^][^]Poll_Fd --- - free_fds :: proc(pollfds: [^][^]Poll_Fd) --- + // 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 ---------------------------------- - control_transfer :: proc(dev_handle: Device_Handle, bmRequestType: u8, bRequest: u8, wValue: u16, wIndex: u16, data: [^]u8, wLength: u16, timeout: c.uint) -> Error --- + // ---- 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 --- } // --------------------------------------------------------------------------------------------------------------------- -// ----- Tests ------------------------ +// ----- 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) + } } diff --git a/vendor/libusb/windows-x64/libusb-1.0-static.lib b/vendor/libusb/windows-x64/libusb-1.0-static.lib new file mode 100644 index 0000000..705e939 Binary files /dev/null and b/vendor/libusb/windows-x64/libusb-1.0-static.lib differ diff --git a/vendor/libusb/windows-x64/libusb-1.0.dll b/vendor/libusb/windows-x64/libusb-1.0.dll new file mode 100644 index 0000000..26808bb Binary files /dev/null and b/vendor/libusb/windows-x64/libusb-1.0.dll differ diff --git a/vendor/libusb/windows-x64/libusb-1.0.lib b/vendor/libusb/windows-x64/libusb-1.0.lib new file mode 100644 index 0000000..9e653ea Binary files /dev/null and b/vendor/libusb/windows-x64/libusb-1.0.lib differ diff --git a/vendor/libusb/windows-x86/libusb-1.0-static.lib b/vendor/libusb/windows-x86/libusb-1.0-static.lib new file mode 100644 index 0000000..120ebbf Binary files /dev/null and b/vendor/libusb/windows-x86/libusb-1.0-static.lib differ diff --git a/vendor/libusb/windows-x86/libusb-1.0.dll b/vendor/libusb/windows-x86/libusb-1.0.dll new file mode 100644 index 0000000..231d0e0 Binary files /dev/null and b/vendor/libusb/windows-x86/libusb-1.0.dll differ diff --git a/vendor/libusb/windows-x86/libusb-1.0.lib b/vendor/libusb/windows-x86/libusb-1.0.lib new file mode 100644 index 0000000..be683c2 Binary files /dev/null and b/vendor/libusb/windows-x86/libusb-1.0.lib differ diff --git a/vendor/lmdb/examples/examples.odin b/vendor/lmdb/examples/examples.odin index 846df61..1df4e87 100644 --- a/vendor/lmdb/examples/examples.odin +++ b/vendor/lmdb/examples/examples.odin @@ -15,29 +15,28 @@ main :: proc() { // Create environment for lmdb mdb.panic_on_err(mdb.env_create(&environment)) // Create directory for databases. Won't do anything if it already exists. - // 0o774 gives all permissions for owner and group, read for everyone else. - os.make_directory(DB_PATH, 0o774) + os.make_directory(DB_PATH) // Open the database files (creates them if they don't already exist) - mdb.panic_on_err(mdb.env_open(environment, DB_PATH, 0, DB_MODE)) + mdb.panic_on_err(mdb.env_open(environment, DB_PATH, {}, DB_MODE)) // Transactions txn_handle: ^mdb.Txn db_handle: mdb.Dbi // Put transaction key := 7 - key_val := mdb.autoval(&key) + key_val := mdb.blittable_val(&key) put_data := 12 - put_data_val := mdb.autoval(&put_data) - mdb.panic_on_err(mdb.txn_begin(environment, nil, 0, &txn_handle)) - mdb.panic_on_err(mdb.dbi_open(txn_handle, nil, 0, &db_handle)) - mdb.panic_on_err(mdb.put(txn_handle, db_handle, &key_val.raw, &put_data_val.raw, 0)) + put_data_val := mdb.blittable_val(&put_data) + mdb.panic_on_err(mdb.txn_begin(environment, nil, {}, &txn_handle)) + mdb.panic_on_err(mdb.dbi_open(txn_handle, nil, {}, &db_handle)) + mdb.panic_on_err(mdb.put(txn_handle, db_handle, &key_val, &put_data_val, {})) mdb.panic_on_err(mdb.txn_commit(txn_handle)) // Get transaction - get_data_val := mdb.nil_autoval(int) - mdb.panic_on_err(mdb.txn_begin(environment, nil, 0, &txn_handle)) - mdb.panic_on_err(mdb.get(txn_handle, db_handle, &key_val.raw, &get_data_val.raw)) + data_val: mdb.Val + mdb.panic_on_err(mdb.txn_begin(environment, nil, {}, &txn_handle)) + mdb.panic_on_err(mdb.get(txn_handle, db_handle, &key_val, &data_val)) + data_cpy := mdb.blittable_copy(&data_val, int) mdb.panic_on_err(mdb.txn_commit(txn_handle)) - data_cpy := mdb.autoval_get_data(&get_data_val)^ fmt.println("Get result:", data_cpy) } diff --git a/vendor/lmdb/lmdb.odin b/vendor/lmdb/lmdb.odin index 7292c5c..a7131ae 100644 --- a/vendor/lmdb/lmdb.odin +++ b/vendor/lmdb/lmdb.odin @@ -164,24 +164,123 @@ */ package lmdb -foreign import lib "system:lmdb" - import "core:c" import "core:fmt" +import "core:reflect" import "core:sys/posix" +// --------------------------------------------------------------------------------------------------------------------- +// ----- Added Odin Helpers ------------------------ +// --------------------------------------------------------------------------------------------------------------------- + +// Wrap a blittable value's bytes as an LMDB Val. +// T must be a contiguous type with no indirection (no pointers, slices, strings, maps, etc.). +blittable_val :: #force_inline proc(val_ptr: ^$T) -> Val { + fmt.assertf( + reflect.has_no_indirections(type_info_of(T)), + "blitval: type '%v' contains indirection and cannot be stored directly in LMDB", + typeid_of(T), + ) + return Val{size_of(T), val_ptr} +} + +// Reads a blittable T out of the LMDB memory map by copying it into caller +// storage. The returned T has no lifetime tie to the transaction. +blittable_copy :: #force_inline proc(val: ^Val, $T: typeid) -> T { + fmt.assertf( + reflect.has_no_indirections(type_info_of(T)), + "blitval_copy: type '%v' contains indirection and cannot be read directly from LMDB", + typeid_of(T), + ) + return (cast(^T)val.data)^ +} + +// Zero-copy pointer view into the LMDB memory map as a ^T. +// Useful for large blittable types where you want to read individual fields +// without copying the entire value (e.g. ptr.timestamp, ptr.flags). +// MUST NOT be written through — writes either segfault (default env mode) +// or silently corrupt the database (ENV_WRITEMAP). +// MUST NOT be retained past txn_commit, txn_abort, or any subsequent write +// operation on the same env — the pointer is invalidated. +blittable_view :: #force_inline proc(val: ^Val, $T: typeid) -> ^T { + fmt.assertf( + reflect.has_no_indirections(type_info_of(T)), + "blitval_view: type '%v' contains indirection and cannot be viewed directly from LMDB", + typeid_of(T), + ) + return cast(^T)val.data +} + +// Wrap a slice of blittable elements as an LMDB Val for use with put/get. +// T must be a contiguous type with no indirection. +// The caller's slice must remain valid (not freed, not resized) for the +// duration of the put call that consumes this Val. +slice_val :: #force_inline proc(s: []$T) -> Val { + fmt.assertf( + reflect.has_no_indirections(type_info_of(T)), + "slice_val: element type '%v' contains indirection and cannot be stored directly in LMDB", + typeid_of(T), + ) + return Val{uint(len(s) * size_of(T)), raw_data(s)} +} + +// Zero-copy slice view into the LMDB memory map. +// T must match the element type that was originally stored. +// MUST NOT be modified — writes through this slice either segfault (default +// env mode) or silently corrupt the database (ENV_WRITEMAP). +// MUST be copied (e.g. slice.clone) if it needs to outlive the current +// transaction; the view is invalidated by txn_commit, txn_abort, or any +// subsequent write operation on the same env. +slice_view :: #force_inline proc(val: ^Val, $T: typeid) -> []T { + fmt.assertf( + reflect.has_no_indirections(type_info_of(T)), + "slice_view: element type '%v' contains indirection and cannot be read directly from LMDB", + typeid_of(T), + ) + return (cast([^]T)val.data)[:val.size / size_of(T)] +} + +// Wrap a string's bytes as an LMDB Val for use with put/get. +// The caller's string must remain valid (backing memory not freed) for the +// duration of the put call that consumes this Val. +string_val :: #force_inline proc(s: string) -> Val { + return Val{uint(len(s)), raw_data(s)} +} + +// Zero-copy string view into the LMDB memory map. +// MUST NOT be modified — writes through the underlying bytes either segfault +// (default env mode) or silently corrupt the database (ENV_WRITEMAP). +// MUST be copied (e.g. strings.clone) if it needs to outlive the current +// transaction; the view is invalidated by txn_commit, txn_abort, or any +// subsequent write operation on the same env. +string_view :: #force_inline proc(val: ^Val) -> string { + return string((cast([^]u8)val.data)[:val.size]) +} + +// Panic if there is an error +panic_on_err :: #force_inline proc(error: Error, loc := #caller_location) { + if error != .NONE { + fmt.panicf("LMDB error %v: %s", error, strerror(i32(error)), loc = loc) + } +} + +// --------------------------------------------------------------------------------------------------------------------- +// ----- Bindings ------------------------ +// --------------------------------------------------------------------------------------------------------------------- + _ :: c when ODIN_OS == .Windows { + #panic("TODO: Compile windows .lib for lmdb") mode_t :: c.int -} else { - mode_t :: posix.mode_t -} - -when ODIN_OS == .Windows { filehandle_t :: rawptr -} else { +} else when ODIN_OS == + .Linux || ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { + foreign import lib "system:lmdb" + mode_t :: posix.mode_t filehandle_t :: c.int +} else { + #panic("levlib/vendor/lmdb: unsupported OS target") } Env :: struct {} @@ -189,7 +288,7 @@ Env :: struct {} Txn :: struct {} /** @brief A handle for an individual database in the DB environment. */ -Dbi :: u32 +Dbi :: c.uint Cursor :: struct {} @@ -205,33 +304,8 @@ Cursor :: struct {} * Other data items can in theory be from 0 to 0xffffffff bytes long. */ Val :: struct { - mv_size: uint, /**< size of the data item */ - mv_data: rawptr, /**< address of the data item */ -} - -// Automatic `Val` handling for a given type 'T'. -// Will not traverse pointers. If `T` stores pointers, you probably don't want to use this. -Auto_Val :: struct($T: typeid) { - raw: Val, -} - -autoval :: #force_inline proc "contextless" (val_ptr: ^$T) -> Auto_Val(T) { - return Auto_Val(T){Val{size_of(T), val_ptr}} -} - -nil_autoval :: #force_inline proc "contextless" ($T: typeid) -> Auto_Val(T) { - return Auto_Val(T){Val{size_of(T), nil}} -} - -autoval_get_data :: #force_inline proc "contextless" (val: ^Auto_Val($T)) -> ^T { - return cast(^T)val.raw.mv_data -} - -// Panic if there is an error -panic_on_err :: #force_inline proc(error: Error) { - if error != .NONE { - fmt.panicf("Irrecoverable LMDB error", strerror(i32(error))) - } + size: uint, /**< size of the data item */ + data: rawptr, /**< address of the data item */ } /** @brief A callback function used to compare two keys in a database */ @@ -253,85 +327,65 @@ Cmp_Func :: #type proc "c" (_: ^Val, _: ^Val) -> i32 */ Rel_Func :: #type proc "c" (item: ^Val, oldptr, newptr, relctx: rawptr) -/** @defgroup mdb_env Environment Flags +/** @defgroup mdb_env Environment Flags * @{ */ -/** mmap at a fixed address (experimental) */ -ENV_FIXEDMAP :: 0x01 -/** no environment directory */ -ENV_NOSUBDIR :: 0x4000 -/** don't fsync after commit */ -ENV_NOSYNC :: 0x10000 -/** read only */ -ENV_RDONLY :: 0x20000 -/** don't fsync metapage after commit */ -ENV_NOMETASYNC :: 0x40000 -/** use writable mmap */ -ENV_WRITEMAP :: 0x80000 -/** use asynchronous msync when #MDB_WRITEMAP is used */ -ENV_MAPASYNC :: 0x100000 -/** tie reader locktable slots to #MDB_txn objects instead of to threads */ -ENV_NOTLS :: 0x200000 -/** don't do any locking, caller must manage their own locks */ -ENV_NOLOCK :: 0x400000 -/** don't do readahead (no effect on Windows) */ -ENV_NORDAHEAD :: 0x800000 -/** don't initialize malloc'd memory before writing to datafile */ -ENV_NOMEMINIT :: 0x1000000 +Env_Flag :: enum u32 { + FIXEDMAP = 0, /**< mmap at a fixed address (experimental) */ + NOSUBDIR = 14, /**< no environment directory */ + NOSYNC = 16, /**< don't fsync after commit */ + RDONLY = 17, /**< read only */ + NOMETASYNC = 18, /**< don't fsync metapage after commit */ + WRITEMAP = 19, /**< use writable mmap */ + MAPASYNC = 20, /**< use asynchronous msync when WRITEMAP is used */ + NOTLS = 21, /**< tie reader locktable slots to Txn objects instead of to threads */ + NOLOCK = 22, /**< don't do any locking, caller must manage their own locks */ + NORDAHEAD = 23, /**< don't do readahead (no effect on Windows) */ + NOMEMINIT = 24, /**< don't initialize malloc'd memory before writing to datafile */ + PREVSNAPSHOT = 25, /**< use the previous snapshot rather than the latest one */ +} +Env_Flags :: distinct bit_set[Env_Flag;c.uint] /** @} */ -/** @defgroup mdb_dbi_open Database Flags +/** @defgroup mdb_dbi_open Database Flags * @{ */ -/** use reverse string keys */ -DB_REVERSEKEY :: 0x02 -/** use sorted duplicates */ -DB_DUPSORT :: 0x04 -/** numeric keys in native byte order: either unsigned int or size_t. - * The keys must all be of the same size. */ -DB_INTEGERKEY :: 0x08 -/** with #MDB_DUPSORT, sorted dup items have fixed size */ -DB_DUPFIXED :: 0x10 -/** with #MDB_DUPSORT, dups are #MDB_INTEGERKEY-style integers */ -DB_INTEGERDUP :: 0x20 -/** with #MDB_DUPSORT, use reverse string dups */ -DB_REVERSEDUP :: 0x40 -/** create DB if not already existing */ -DB_CREATE :: 0x40000 +Db_Flag :: enum u32 { + REVERSEKEY = 1, /**< use reverse string keys */ + DUPSORT = 2, /**< use sorted duplicates */ + INTEGERKEY = 3, /**< numeric keys in native byte order */ + DUPFIXED = 4, /**< with DUPSORT, sorted dup items have fixed size */ + INTEGERDUP = 5, /**< with DUPSORT, dups are INTEGERKEY-style integers */ + REVERSEDUP = 6, /**< with DUPSORT, use reverse string dups */ + CREATE = 18, /**< create DB if not already existing */ +} +Db_Flags :: distinct bit_set[Db_Flag;c.uint] /** @} */ -/** @defgroup mdb_put Write Flags +/** @defgroup mdb_put Write Flags * @{ */ -/** For put: Don't write if the key already exists. */ -WRITE_NOOVERWRITE :: 0x10 -/** Only for #MDB_DUPSORT
- * For put: don't write if the key and data pair already exist.
- * For mdb_cursor_del: remove all duplicate data items. - */ -WRITE_NODUPDATA :: 0x20 -/** For mdb_cursor_put: overwrite the current key/data pair */ -WRITE_CURRENT :: 0x40 -/** For put: Just reserve space for data, don't copy it. Return a - * pointer to the reserved space. - */ -WRITE_RESERVE :: 0x10000 -/** Data is being appended, don't split full pages. */ -WRITE_APPEND :: 0x20000 -/** Duplicate data is being appended, don't split full pages. */ -WRITE_APPENDDUP :: 0x40000 -/** Store multiple data items in one call. Only for #MDB_DUPFIXED. */ -WRITE_MULTIPLE :: 0x80000 -/* @} */ +Write_Flag :: enum u32 { + NOOVERWRITE = 4, /**< For put: Don't write if the key already exists */ + NODUPDATA = 5, /**< For DUPSORT: don't write if the key and data pair already exist. + For mdb_cursor_del: remove all duplicate data items. */ + CURRENT = 6, /**< For mdb_cursor_put: overwrite the current key/data pair */ + RESERVE = 16, /**< For put: Just reserve space for data, don't copy it */ + APPEND = 17, /**< Data is being appended, don't split full pages */ + APPENDDUP = 18, /**< Duplicate data is being appended, don't split full pages */ + MULTIPLE = 19, /**< Store multiple data items in one call. Only for DUPFIXED. */ +} +Write_Flags :: distinct bit_set[Write_Flag;c.uint] +/** @} */ -/** @defgroup mdb_copy Copy Flags +/** @defgroup mdb_copy Copy Flags * @{ */ -/** Compacting copy: Omit free space from copy, and renumber all - * pages sequentially. - */ -CP_COMPACT :: 0x01 -/* @} */ +Copy_Flag :: enum u32 { + COMPACT = 0, /**< Compacting copy: Omit free space from copy, and renumber all pages sequentially. */ +} +Copy_Flags :: distinct bit_set[Copy_Flag;c.uint] +/** @} */ /** @brief Cursor Get operations. * @@ -340,33 +394,24 @@ CP_COMPACT :: 0x01 */ Cursor_Op :: enum c.int { FIRST, /**< Position at first key/data item */ - FIRST_DUP, /**< Position at first data item of current key. - Only for #MDB_DUPSORT */ - GET_BOTH, /**< Position at key/data pair. Only for #MDB_DUPSORT */ - GET_BOTH_RANGE, /**< position at key, nearest data. Only for #MDB_DUPSORT */ + FIRST_DUP, /**< Position at first data item of current key. Only for DUPSORT */ + GET_BOTH, /**< Position at key/data pair. Only for DUPSORT */ + GET_BOTH_RANGE, /**< Position at key, nearest data. Only for DUPSORT */ GET_CURRENT, /**< Return key/data at current cursor position */ - GET_MULTIPLE, /**< Return up to a page of duplicate data items - from current cursor position. Move cursor to prepare - for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */ + GET_MULTIPLE, /**< Return up to a page of duplicate data items from current cursor position. Only for DUPFIXED */ LAST, /**< Position at last key/data item */ - LAST_DUP, /**< Position at last data item of current key. - Only for #MDB_DUPSORT */ + LAST_DUP, /**< Position at last data item of current key. Only for DUPSORT */ NEXT, /**< Position at next data item */ - NEXT_DUP, /**< Position at next data item of current key. - Only for #MDB_DUPSORT */ - NEXT_MULTIPLE, /**< Return up to a page of duplicate data items - from next cursor position. Move cursor to prepare - for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */ + NEXT_DUP, /**< Position at next data item of current key. Only for DUPSORT */ + NEXT_MULTIPLE, /**< Return up to a page of duplicate data items from next cursor position. Only for DUPFIXED */ NEXT_NODUP, /**< Position at first data item of next key */ PREV, /**< Position at previous data item */ - PREV_DUP, /**< Position at previous data item of current key. - Only for #MDB_DUPSORT */ + PREV_DUP, /**< Position at previous data item of current key. Only for DUPSORT */ PREV_NODUP, /**< Position at last data item of previous key */ SET, /**< Position at specified key */ SET_KEY, /**< Position at specified key, return key + data */ - SET_RANGE, /**< Position at first key greater than or equal to specified key. */ - PREV_MULTIPLE, /**< Position at previous page and return up to - a page of duplicate data items. Only for #MDB_DUPFIXED */ + SET_RANGE, /**< Position at first key greater than or equal to specified key */ + PREV_MULTIPLE, /**< Position at previous page and return up to a page of duplicate data items. Only for DUPFIXED */ } Error :: enum c.int { @@ -419,33 +464,28 @@ Error :: enum c.int { BAD_VALSIZE = -30781, /** The specified DBI was changed unexpectedly */ BAD_DBI = -30780, + /** Unexpected problem - txn should abort */ + PROBLEM = -30779, } /** @brief Statistics for a database in the environment */ Stat :: struct { - ms_psize: u32, - /**< Size of a database page. - This is currently the same for all databases. */ - ms_depth: u32, - /**< Depth (height) of the B-tree */ - ms_branch_pages: uint, - /**< Number of internal (non-leaf) pages */ - ms_leaf_pages: uint, - /**< Number of leaf pages */ - ms_overflow_pages: uint, - /**< Number of overflow pages */ - ms_entries: uint, - /**< Number of data items */ + psize: u32, /**< Size of a database page. This is currently the same for all databases. */ + depth: u32, /**< Depth (height) of the B-tree */ + branch_pages: uint, /**< Number of internal (non-leaf) pages */ + leaf_pages: uint, /**< Number of leaf pages */ + overflow_pages: uint, /**< Number of overflow pages */ + entries: uint, /**< Number of data items */ } /** @brief Information about the environment */ Env_Info :: struct { - me_mapaddr: rawptr, /**< Address of map, if fixed */ - me_mapsize: uint, /**< Size of the data memory map */ - me_last_pgno: uint, /**< ID of the last used page */ - me_last_txnid: uint, /**< ID of the last committed transaction */ - me_maxreaders: u32, /**< max reader slots in the environment */ - me_numreaders: u32, /**< max reader slots used in the environment */ + mapaddr: rawptr, /**< Address of map, if fixed */ + mapsize: uint, /**< Size of the data memory map */ + last_pgno: uint, /**< ID of the last used page */ + last_txnid: uint, /**< ID of the last committed transaction */ + maxreaders: u32, /**< max reader slots in the environment */ + numreaders: u32, /**< max reader slots used in the environment */ } /** @brief A callback function for most LMDB assert() failures, @@ -454,7 +494,7 @@ Env_Info :: struct { * @param[in] env An environment handle returned by #mdb_env_create(). * @param[in] msg The assertion message, not including newline. */ -Assert_Func :: proc "c" (_: ^Env, _: cstring) +Assert_Func :: #type proc "c" (_: ^Env, _: cstring) /** @brief A callback function used to print a message from the library. * @@ -462,7 +502,7 @@ Assert_Func :: proc "c" (_: ^Env, _: cstring) * @param[in] ctx An arbitrary context pointer for the callback. * @return < 0 on failure, >= 0 on success. */ -Msg_Func :: proc "c" (_: cstring, _: rawptr) -> i32 +Msg_Func :: #type proc "c" (_: cstring, _: rawptr) -> i32 @(default_calling_convention = "c", link_prefix = "mdb_") foreign lib { @@ -623,7 +663,7 @@ foreign lib { * */ @(require_results) - env_open :: proc(env: ^Env, path: cstring, flags: u32, mode: mode_t) -> Error --- + env_open :: proc(env: ^Env, path: cstring, flags: Env_Flags, mode: mode_t) -> Error --- /** @brief Copy an LMDB environment to the specified path. * @@ -682,7 +722,7 @@ foreign lib { * @return A non-zero error value on failure and 0 on success. */ @(require_results) - env_copy2 :: proc(env: ^Env, path: cstring, flags: u32) -> Error --- + env_copy2 :: proc(env: ^Env, path: cstring, flags: Copy_Flags) -> Error --- /** @brief Copy an LMDB environment to the specified file descriptor, * with options. @@ -702,7 +742,7 @@ foreign lib { * @return A non-zero error value on failure and 0 on success. */ @(require_results) - env_copyfd2 :: proc(env: ^Env, fd: filehandle_t, flags: u32) -> Error --- + env_copyfd2 :: proc(env: ^Env, fd: filehandle_t, flags: Copy_Flags) -> Error --- /** @brief Return statistics about the LMDB environment. * @@ -767,7 +807,7 @@ foreign lib { * */ @(require_results) - env_set_flags :: proc(env: ^Env, flags: u32, onoff: i32) -> Error --- + env_set_flags :: proc(env: ^Env, flags: Env_Flags, onoff: i32) -> Error --- /** @brief Get environment flags. * @@ -780,7 +820,7 @@ foreign lib { * */ @(require_results) - env_get_flags :: proc(env: ^Env, flags: ^u32) -> Error --- + env_get_flags :: proc(env: ^Env, flags: ^Env_Flags) -> Error --- /** @brief Return the path that was used in #mdb_env_open(). * @@ -973,7 +1013,7 @@ foreign lib { * */ @(require_results) - txn_begin :: proc(env: ^Env, parent: ^Txn, flags: u32, txn: ^^Txn) -> Error --- + txn_begin :: proc(env: ^Env, parent: ^Txn, flags: Env_Flags, txn: ^^Txn) -> Error --- /** @brief Returns the transaction's #MDB_env * @@ -1126,7 +1166,7 @@ foreign lib { * */ @(require_results) - dbi_open :: proc(txn: ^Txn, name: cstring, flags: u32, dbi: ^Dbi) -> Error --- + dbi_open :: proc(txn: ^Txn, name: cstring, flags: Db_Flags, dbi: ^Dbi) -> Error --- /** @brief Retrieve statistics for a database. * @@ -1151,7 +1191,7 @@ foreign lib { * @return A non-zero error value on failure and 0 on success. */ @(require_results) - dbi_flags :: proc(txn: ^Txn, dbi: Dbi, flags: ^u32) -> Error --- + dbi_flags :: proc(txn: ^Txn, dbi: Dbi, flags: ^Db_Flags) -> Error --- /** @brief Close a database handle. Normally unnecessary. Use with care: * @@ -1229,6 +1269,7 @@ foreign lib { @(require_results) set_dupsort :: proc(txn: ^Txn, dbi: Dbi, cmp: Cmp_Func) -> Error --- + // NOTE: Unimplemented in current LMDB — this function has no effect. /** @brief Set a relocation function for a #MDB_FIXEDMAP database. * * @todo The relocation function is called whenever it is necessary to move the data @@ -1250,6 +1291,7 @@ foreign lib { @(require_results) set_relfunc :: proc(txn: ^Txn, dbi: Dbi, rel: Rel_Func) -> Error --- + // NOTE: Unimplemented in current LMDB — this function has no effect. /** @brief Set a context pointer for a #MDB_FIXEDMAP database's relocation function. * * See #mdb_set_relfunc and #MDB_rel_func for more details. @@ -1344,7 +1386,7 @@ foreign lib { * */ @(require_results) - put :: proc(txn: ^Txn, dbi: Dbi, key: ^Val, data: ^Val, flags: u32) -> Error --- + put :: proc(txn: ^Txn, dbi: Dbi, key: ^Val, data: ^Val, flags: Write_Flags) -> Error --- /** @brief Delete items from a database. * @@ -1517,7 +1559,7 @@ foreign lib { * */ @(require_results) - cursor_put :: proc(cursor: ^Cursor, key: ^Val, data: ^Val, flags: u32) -> Error --- + cursor_put :: proc(cursor: ^Cursor, key: ^Val, data: ^Val, flags: Write_Flags) -> Error --- /** @brief Delete current key/data pair * @@ -1541,7 +1583,7 @@ foreign lib { * */ @(require_results) - cursor_del :: proc(cursor: ^Cursor, flags: u32) -> Error --- + cursor_del :: proc(cursor: ^Cursor, flags: Write_Flags) -> Error --- /** @brief Return count of duplicates for current key. *