//! USB Mass Storage Bulk-Only Transport (BOT) wire structures — the Command and
//! Command Status Wrappers that bracket every command (USB MSC BOT §5). Pure data
//! definitions, host-testable in isolation. The command inside the CBW is a SCSI
//! CDB (see scsi.zig); the transport here just carries it and reports status.
//!
//! One command is three bulk transfers: CBW out, an optional data stage, CSW in.

const std = @import("std");

/// "USBC" — the signature at the head of every Command Block Wrapper.
pub const cbw_signature: u32 = 0x43425355;
/// "USBS" — the signature at the head of every Command Status Wrapper.
pub const csw_signature: u32 = 0x53425355;

/// CBW `flags`: set for a device-to-host (IN) data stage, clear for OUT.
pub const flag_data_in: u8 = 0x80;

/// The 31-byte Command Block Wrapper, sent on the bulk-OUT endpoint.
pub const CommandBlockWrapper = extern struct {
    signature: u32 align(1) = cbw_signature,
    tag: u32 align(1),
    data_transfer_length: u32 align(1),
    flags: u8,
    lun: u8,
    cdb_length: u8,
    cdb: [16]u8 = [_]u8{0} ** 16,
};

/// A device's answer to a command (the CSW `status` byte).
pub const CommandStatus = enum(u8) {
    passed = 0,
    failed = 1,
    phase_error = 2,
    _,
};

/// The 13-byte Command Status Wrapper, read from the bulk-IN endpoint.
pub const CommandStatusWrapper = extern struct {
    signature: u32 align(1) = csw_signature,
    tag: u32 align(1),
    data_residue: u32 align(1),
    status: u8,
};

comptime {
    std.debug.assert(@sizeOf(CommandBlockWrapper) == 31);
    std.debug.assert(@sizeOf(CommandStatusWrapper) == 13);
}

test "wrapper sizes and signatures match the specification" {
    const cbw = CommandBlockWrapper{
        .tag = 0x11223344,
        .data_transfer_length = 512,
        .flags = flag_data_in,
        .lun = 0,
        .cdb_length = 10,
    };
    const bytes = std.mem.asBytes(&cbw);
    try std.testing.expectEqual(@as(usize, 31), bytes.len);
    // "USBC" little-endian.
    try std.testing.expectEqualSlices(u8, "USBC", bytes[0..4]);
    try std.testing.expectEqual(flag_data_in, bytes[12]);

    const csw = std.mem.bytesToValue(CommandStatusWrapper, &[_]u8{
        0x55, 0x53, 0x42, 0x53, // "USBS"
        0x44, 0x33, 0x22, 0x11, // tag
        0x00, 0x00, 0x00, 0x00, // residue
        0x00, // passed
    });
    try std.testing.expectEqual(csw_signature, csw.signature);
    try std.testing.expectEqual(@as(u32, 0x11223344), csw.tag);
    try std.testing.expectEqual(@as(u8, @intFromEnum(CommandStatus.passed)), csw.status);
}
