USB descriptor parsing.
Parses the raw descriptor bytes that usbfs returns (device descriptor followed by the configuration descriptor sets) into Elixir structs, plus string descriptor decoding.
Parsing is total and defensive: for any input binary, parse/1 returns
either {:ok, %Device{}} or {:error, reason} with a typed reason, and never
raises. That is what lets the library survive malformed descriptors from a
hostile or buggy device (truncated, oversized, zero-length, wrong bLength,
...).
Summary
Functions
Decode a string descriptor's bytes (<<bLength, 0x03, utf16le...>>) to a
UTF-8 string. Index 0 is not a string (its payload is the LANGID array).
Parse a full descriptor blob (device descriptor + configuration sets), as
returned by reading a usbfs node. Returns {:ok, %Device{}} or {:error, reason}.
Parse a single configuration descriptor set (config + interfaces + endpoints).
Parse just the 18-byte device descriptor (ignores any trailing bytes).
Types
@type reason() :: :short_device_descriptor | :not_a_device_descriptor | {:invalid_device_length, non_neg_integer()} | :zero_length_descriptor | {:invalid_descriptor_length, non_neg_integer()} | :truncated
Functions
Decode a string descriptor's bytes (<<bLength, 0x03, utf16le...>>) to a
UTF-8 string. Index 0 is not a string (its payload is the LANGID array).
@spec parse(binary()) :: {:ok, BodgeUSB.Descriptor.Device.t()} | {:error, reason()}
Parse a full descriptor blob (device descriptor + configuration sets), as
returned by reading a usbfs node. Returns {:ok, %Device{}} or {:error, reason}.
@spec parse_configuration(binary()) :: {:ok, BodgeUSB.Descriptor.Configuration.t()} | {:error, reason()}
Parse a single configuration descriptor set (config + interfaces + endpoints).
@spec parse_device(binary()) :: {:ok, BodgeUSB.Descriptor.Device.t()} | {:error, reason()}
Parse just the 18-byte device descriptor (ignores any trailing bytes).
Note: reading a usbfs node returns the device descriptor with its multibyte
fields (bcdUSB, idVendor, idProduct, bcdDevice) already byte-swapped to
host order by the kernel; we read them little-endian, which is correct on
little-endian hosts (the common case). Configuration descriptors are raw
little-endian and parse correctly everywhere.