//! USB class-code decoding: turn the (class, subclass, protocol) triple a USB device or //! interface reports in its descriptors into typed values. The device descriptor carries one //! triple for the whole device, and each interface descriptor carries its own; a class code //! of zero at the device level defers entirely to the interfaces. Subclass and protocol //! codes are qualified by the class code — the same value means different things under //! different classes — so there is no single SubClass or Protocol enum: each class with //! spec-defined codes gets its own namespace below. Pure reference data (from the USB-IF //! defined class codes; see https://www.usb.org/defined-class-codes) — no hardware access — //! so it is shared by kernel discovery and any user-space tool (device naming, driver //! matching). // Base class codes (assigned by the USB-IF). The comment on each value notes where the code // may legally appear: in the device descriptor, in interface descriptors, or both. pub const Class = enum(u8) { // Use class information in the interface descriptors (device descriptor only). Each // interface within a configuration specifies its own class information and the various // interfaces operate independently. per_interface = 0x00, // Audio: speakers, microphones, sound cards (interface) audio = 0x01, // Communications and CDC control: modems, network adapters (both) communications = 0x02, // Human Interface Device: keyboards, mice, game controllers (interface) hid = 0x03, // Physical: force-feedback devices (interface) physical = 0x05, // Image: still-imaging cameras, scanners (interface) image = 0x06, // Printer (interface) printer = 0x07, // Mass storage: flash drives, external disks, card readers (interface) mass_storage = 0x08, // Hub (device descriptor only) hub = 0x09, // CDC-Data: the data interfaces paired with a communications control interface // (interface) cdc_data = 0x0A, // Smart card readers (interface) smart_card = 0x0B, // Content security (interface) content_security = 0x0D, // Video: webcams (interface) video = 0x0E, // Personal healthcare devices (interface) personal_healthcare = 0x0F, // Audio/Video devices (interface) audio_video = 0x10, // Billboard: describes alternate modes a USB Type-C device supports (device descriptor // only) billboard = 0x11, // USB Type-C bridge (interface) type_c_bridge = 0x12, // USB Bulk Display Protocol devices (interface) bulk_display = 0x13, // MCTP over USB protocol endpoint devices (interface) mctp = 0x14, // I3C devices (interface) i3c = 0x3C, // Diagnostic devices (both) diagnostic = 0xDC, // Wireless controllers: Bluetooth adapters (interface) wireless_controller = 0xE0, // Miscellaneous (both) miscellaneous = 0xEF, // Application-specific: firmware upgrade, IrDA bridges, test and measurement // (interface) application_specific = 0xFE, // Vendor-specific (both) vendor_specific = 0xFF, _, }; // Subclass and protocol codes qualified by Class.hub. Hubs have no subclass codes; the // protocol distinguishes the hub's transaction-translator arrangement. pub const hub = struct { pub const Protocol = enum(u8) { // Full-speed hub full_speed = 0x00, // Hi-speed hub with a single transaction translator hi_speed_single_tt = 0x01, // Hi-speed hub with multiple transaction translators hi_speed_multi_tt = 0x02, // SuperSpeed hub (USB 3) super_speed = 0x03, _, }; }; // Subclass and protocol codes qualified by Class.hid. pub const hid = struct { pub const SubClass = enum(u8) { // No subclass none = 0x00, // Boot interface: the device also supports the simplified boot protocol, usable by // firmware before a full HID report-descriptor parser is available boot = 0x01, _, }; // Only meaningful when the subclass is boot pub const Protocol = enum(u8) { none = 0x00, keyboard = 0x01, mouse = 0x02, _, }; }; // Subclass and protocol codes qualified by Class.mass_storage. The subclass identifies the // command set the device understands; the protocol identifies the transport used to carry // commands, data, and status over the bus. pub const mass_storage = struct { pub const SubClass = enum(u8) { // SCSI command set not reported; de facto, treat as scsi not_reported = 0x00, // Reduced Block Commands: typically flash devices rbc = 0x01, // MMC-5 (ATAPI): CD and DVD drives atapi = 0x02, // QIC-157 tape drives (obsolete) qic_157 = 0x03, // UFI: floppy disk drives ufi = 0x04, // SFF-8070i (obsolete) sff_8070i = 0x05, // Transparent SCSI command set: the common case for flash drives and disks scsi = 0x06, // LSD FS: negotiated access to large storage devices lsd_fs = 0x07, // IEEE 1667 ieee_1667 = 0x08, // Vendor-specific vendor_specific = 0xFF, _, }; pub const Protocol = enum(u8) { // Control/Bulk/Interrupt with command completion interrupt cbi_completion_interrupt = 0x00, // Control/Bulk/Interrupt without command completion interrupt cbi = 0x01, // Bulk-only transport: the common case for flash drives and disks bulk_only = 0x50, // USB attached SCSI uas = 0x62, // Vendor-specific vendor_specific = 0xFF, _, }; }; // Subclass and protocol codes qualified by Class.communications (CDC). The protocol codes // are model-specific; the useful invariant is the subclass, which selects the control model // the interface implements. pub const communications = struct { pub const SubClass = enum(u8) { // Direct line control model direct_line = 0x01, // Abstract control model: USB modems and serial adapters abstract_control = 0x02, // Telephone control model telephone = 0x03, // Multi-channel control model multi_channel = 0x04, // CAPI control model capi = 0x05, // Ethernet networking control model ethernet = 0x06, // ATM networking control model atm = 0x07, // Wireless handset control model wireless_handset = 0x08, // Device management device_management = 0x09, // Mobile direct line model mobile_direct_line = 0x0A, // OBEX obex = 0x0B, // Ethernet emulation model ethernet_emulation = 0x0C, // Network control model network_control = 0x0D, _, }; }; // Subclass and protocol codes qualified by Class.wireless_controller. pub const wireless_controller = struct { pub const SubClass = enum(u8) { // Radio frequency controllers radio_frequency = 0x01, _, }; // Only meaningful when the subclass is radio_frequency pub const Protocol = enum(u8) { // Bluetooth programming interface bluetooth = 0x01, // Ultra-wideband radio control ultra_wideband = 0x02, // Remote NDIS remote_ndis = 0x03, // Bluetooth AMP controller bluetooth_amp = 0x04, _, }; }; // Subclass and protocol codes qualified by Class.miscellaneous. pub const miscellaneous = struct { pub const SubClass = enum(u8) { // Common class common = 0x02, _, }; // Only meaningful when the subclass is common pub const Protocol = enum(u8) { // Interface association descriptor: at the device level, announces that the // configuration groups interfaces into functions with IADs interface_association = 0x01, _, }; }; // Subclass and protocol codes qualified by Class.application_specific. pub const application_specific = struct { pub const SubClass = enum(u8) { // Device firmware upgrade firmware_upgrade = 0x01, // IrDA bridge irda_bridge = 0x02, // Test and measurement test_and_measurement = 0x03, _, }; }; /// Pack a (class, subclass, protocol) triple into one 0xCCSSPP value — the /// bus-native identity a USB bus driver reports in `ChildAdded.identity` and the /// device manager matches on (the USB analog of a packed PCI class code). Mirrors /// `pci_class.ClassCode.pack`, so both sides build/decode the identical u64. pub fn packTriple(class: u8, subclass: u8, protocol: u8) u64 { return (@as(u64, class) << 16) | (@as(u64, subclass) << 8) | protocol; } /// The inverse of `packTriple`. pub fn unpackTriple(triple: u64) struct { class: u8, subclass: u8, protocol: u8 } { return .{ .class = @truncate(triple >> 16), .subclass = @truncate(triple >> 8), .protocol = @truncate(triple), }; } test "class codes match the USB-IF assignments" { const std = @import("std"); const expectEqual = std.testing.expectEqual; try expectEqual(0x03, @intFromEnum(Class.hid)); try expectEqual(0x09, @intFromEnum(Class.hub)); try expectEqual(0xFF, @intFromEnum(Class.vendor_specific)); // A typical flash drive: mass storage, transparent SCSI, bulk-only transport. try expectEqual(0x06, @intFromEnum(mass_storage.SubClass.scsi)); try expectEqual(0x50, @intFromEnum(mass_storage.Protocol.bulk_only)); // A boot keyboard: HID, boot subclass, keyboard protocol. try expectEqual(0x01, @intFromEnum(hid.SubClass.boot)); try expectEqual(0x01, @intFromEnum(hid.Protocol.keyboard)); // Class codes are non-exhaustive: unlisted values pass through undamaged. const unknown: Class = @enumFromInt(0x42); try expectEqual(0x42, @intFromEnum(unknown)); _ = hub.Protocol.hi_speed_multi_tt; _ = communications.SubClass.abstract_control; _ = wireless_controller.Protocol.bluetooth; _ = miscellaneous.Protocol.interface_association; _ = application_specific.SubClass.firmware_upgrade; } test "packTriple / unpackTriple round-trip the identity a bus driver reports" { const std = @import("std"); const expectEqual = std.testing.expectEqual; // A boot keyboard interface: HID / boot / keyboard. const keyboard = packTriple( @intFromEnum(Class.hid), @intFromEnum(hid.SubClass.boot), @intFromEnum(hid.Protocol.keyboard), ); try expectEqual(@as(u64, 0x03_01_01), keyboard); // A flash drive interface: mass storage / SCSI / bulk-only. const storage = packTriple( @intFromEnum(Class.mass_storage), @intFromEnum(mass_storage.SubClass.scsi), @intFromEnum(mass_storage.Protocol.bulk_only), ); try expectEqual(@as(u64, 0x08_06_50), storage); const parts = unpackTriple(storage); try expectEqual(@as(u8, 0x08), parts.class); try expectEqual(@as(u8, 0x06), parts.subclass); try expectEqual(@as(u8, 0x50), parts.protocol); }