/* * Copyright (c) 2023, Leon Albrecht * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include namespace Kernel::USB { enum class CBWDirection : u8 { DataOut = 0, DataIn = 1 }; struct CommandBlockWrapper { LittleEndian signature { 0x43425355 }; LittleEndian tag { 0 }; LittleEndian transfer_length { 0 }; union { u8 flags { 0 }; struct { u8 flag_reserved : 6; u8 flag_obsolete : 1; CBWDirection direction : 1; }; }; u8 lun { 0 }; // only 4 bits u8 command_length { 0 }; // 5 bits, range 1-16 u8 command_block[16] { 0 }; template requires(sizeof(T) <= 16) void set_command(T const& command) { command_length = sizeof(command); memcpy(&command_block, &command, sizeof(command)); } }; static_assert(AssertSize()); enum class CSWStatus : u8 { Passed = 0x00, Failed = 0x01, PhaseError = 0x02 }; struct CommandStatusWrapper { LittleEndian signature; LittleEndian tag; LittleEndian data_residue; CSWStatus status; }; static_assert(AssertSize()); class BulkSCSIInterface : public StorageDevice { // https://www.usb.org/sites/default/files/usbmassbulk_10.pdf public: BulkSCSIInterface(LUNAddress logical_unit_number_address, u32 hardware_relative_controller_id, size_t sector_size, u64 max_addressable_block, USB::Device& device, NonnullOwnPtr in_pipe, NonnullOwnPtr out_pipe); USB::Device const& device() const { return m_device; } virtual void start_request(AsyncBlockDeviceRequest&) override; virtual CommandSet command_set() const override { return CommandSet::SCSI; } private: USB::Device& m_device; NonnullOwnPtr m_in_pipe; NonnullOwnPtr m_out_pipe; ErrorOr do_read(u32 block_index, u32 block_count, UserOrKernelBuffer& buffer, size_t buffer_size); ErrorOr do_write(u32 block_index, u32 block_count, UserOrKernelBuffer& buffer, size_t buffer_size); IntrusiveListNode> m_list_node; public: using List = IntrusiveList<&BulkSCSIInterface::m_list_node>; }; }