/*
* Copyright (c) 2024, Leon Albrecht <leon.a@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Endian.h>
#include <AK/Types.h>
#include <Kernel/Devices/Storage/USB/SCSICodes.h>
namespace Kernel::USB {
// According to ISO/IEC 14776-251 or the t10 UAS specification
// 5.3.3.5 Pipe Usage Descriptor
// Note: This should be 5.2.3.5, but the 2014 revision of the spec has a typo
// Table 8 Pipe ID
enum class PipeID : u8 {
// Reserved: 0x00
CommandPipe = 0x01,
StatusPipe = 0x02,
DataInPipe = 0x03,
DataOutPipe = 0x04,
// Reserved: 0x05 - 0xDF
// Vendor Specific: 0xE0 - 0xEF
// Reserved: 0xF0 - 0xFF
};
// Table 7
constexpr u8 UAS_PIPE_USAGE_DESCRIPTOR = 0x24;
struct PipeUsageDescriptor {
u8 descriptor_length;
u8 descriptor_type;
PipeID pipe_id;
u8 reserved;
};
// 6.2 IUs
// Table 9
enum class IUID : u8 {
// Reserved: 0x00
Command = 0x01,
// Reserved: 0x02
Sense = 0x03,
Response = 0x04,
TaskManagement = 0x05,
ReadReady = 0x06,
WriteReady = 0x07,
// Reserved: 0x08 - 0x0F
};
// Table 10
struct InformationUnitHeader {
IUID iu_id;
u8 reserved { 0 };
BigEndian<u16> tag;
};
static_assert(AssertSize<InformationUnitHeader, 4>());
// 6.2.2 Command IU
// Table 12
enum class TaskAttribute : u8 {
Simple = 0b000,
HeadOfQueue = 0b001,
Ordered = 0b010,
// Reserved: 0b011
ACA = 0b100,
// Reserved: 0b101 - 0b111
};
// Table 11
struct CommandIU {
InformationUnitHeader header;
struct {
TaskAttribute attribute : 3;
u8 priority : 4;
u8 reserved : 1;
} task_info;
u8 reserved_0 { 0 };
u8 additional_cbd_length; // must be multiple of 4
u8 reserved_1 { 0 };
BigEndian<u64> lun;
u8 cdb[16];
// Note: See SenseIU
// FIXME: Look up the technical maximum size of the additional_cbd_bytes
u8 additional_cbd_bytes[512 - 32]; // indicated by additional_cbd_length -> multiple dwords
template<typename Command>
void set_command(Command const& command)
{
// FIXME:
static_assert(sizeof(command) <= sizeof(cdb), "Command too large for CommandIU without additional_cbd_bytes");
memcpy(cdb, &command, min(sizeof(command), sizeof(cdb)));
additional_cbd_length = 0;
}
};
static_assert(AssertSize<CommandIU, 512>());
// 6.2.3 Read Ready IU
// Table 13
struct ReadReadyIU {
InformationUnitHeader header;
};
static_assert(AssertSize<ReadReadyIU, 4>());
// 6.2.4 Write Ready IU
// Table 14
struct WriteReadyIU {
InformationUnitHeader header;
};
static_assert(AssertSize<WriteReadyIU, 4>());
// 6.2.5 Sense IU
// Table 15
struct SenseIU {
InformationUnitHeader header;
BigEndian<u16> status_qualifier; // See SAM-4
SCSI::StatusCode status;
u8 reserved[7];
BigEndian<u16> length; // FIXME: The spec does not actually state the endianness of this?
// Note: As a Sense IU is of technically of flexible size
// we need to forcefully allocate some data here, to be able to put it
// into an ErrorOr later.
// The maximum size of the sense data the device may send is controlled by the
// MAXIMUM SENSE DATA LENGTH field in the Control extension mode page of the device.
// In theory the maximum size of the sense data is 252 bytes,
// meaning the maximum size of the IU is 252+16=268 bytes.
// But the spec also states that the SenseIU must will always be transmitted
// in it's own USB packet, so we can just use the maximum packet size
// of a packet instead, which is 512 bytes (USB3 allows up to 1024 bytes)
u8 sense_data[512 - 16];
};
static_assert(AssertSize<SenseIU, 512>());
// 6.2.6 Response IU
// Table 17
enum class ResponseCode : u8 {
// TM: On Task Management IUs
// Command: On Command IUs
TaskManagementFunctionComplete = 0x00, // TM
// Reserved: 0x01
InvalidIU = 0x02, // TM, Command
// Reserved: 0x03
TaskManagementFunctionNotSupported = 0x04, // TM
TaskManagementFunctionFailed = 0x05, // TM
// Reserved: 0x06 - 0x07
TaskManagementFunctionSucceeded = 0x08, // TM
IncorrectLUN = 0x09, // TM
OverlappedTagAttempted = 0x0A, // TM, Command
// Reserved: 0x0B - 0x0F
};
// Table 16
struct ResponseIU {
InformationUnitHeader header;
u8 additional_response_info[3];
ResponseCode response_code;
};
static_assert(AssertSize<ResponseIU, 8>());
// 6.2.7 Task Management IU
// Table 19
enum class TaskManagementFunction : u8 {
// LUN: lun field used
// TOTTBM: tag_of_task_to_be_managed field used
// Reserved: 0x00
AbortTask = 0x01, // LUN, TOTTBM
AbortTaskSet = 0x02, // LUN
// Reserved: 0x03
ClearTaskSet = 0x04, // LUN
// Reserved: 0x05 - 0x07
LogicalUnitReset = 0x08, // LUN
// Reserved: 0x09 - 0x0F
ITNexusReset = 0x10,
// Reserved: 0x11 - 0x3F
ClearACA = 0x40, // LUN
// Reserved: 0x41 - 0x7F
QueryTask = 0x80, // LUN, TOTTBM
QueryTaskSet = 0x81, // LUN
QueryAsynchrounousEvent = 0x82, // LUN
// Reserved: 0x83 - 0xFF
};
// Table 18
struct TaskManagementIU {
InformationUnitHeader header;
u8 function;
u8 reserved { 0 };
BigEndian<u16> tag_of_task_to_be_managed;
BigEndian<u64> lun;
};
}