Compare commits
17 Commits
aaff5a65e1
...
865061b492
Author | SHA1 | Date |
---|---|---|
Bananymous | 865061b492 | |
Bananymous | 39313e1737 | |
Bananymous | 70880636f4 | |
Bananymous | 96a5ba0ed3 | |
Bananymous | 8054f6c618 | |
Bananymous | 658ea68d04 | |
Bananymous | 5750f87396 | |
Bananymous | 1253e2a458 | |
Bananymous | 857b3e92f8 | |
Bananymous | 8bf14d542e | |
Bananymous | 31d2a39540 | |
Bananymous | 97718b4046 | |
Bananymous | c07fd265f0 | |
Bananymous | 1de50a2a94 | |
Bananymous | 627ca432cc | |
Bananymous | f8ef11b4d2 | |
Bananymous | 71563034a7 |
|
@ -70,15 +70,19 @@ namespace BAN
|
|||
template<integral T>
|
||||
struct LittleEndian
|
||||
{
|
||||
constexpr LittleEndian()
|
||||
: raw(0)
|
||||
{ }
|
||||
|
||||
constexpr LittleEndian(T value)
|
||||
{
|
||||
raw = host_to_little_endian(value);
|
||||
}
|
||||
: raw(host_to_little_endian(value))
|
||||
{ }
|
||||
|
||||
constexpr operator T() const
|
||||
{
|
||||
return host_to_little_endian(raw);
|
||||
}
|
||||
|
||||
private:
|
||||
T raw;
|
||||
};
|
||||
|
@ -86,15 +90,19 @@ namespace BAN
|
|||
template<integral T>
|
||||
struct BigEndian
|
||||
{
|
||||
constexpr BigEndian()
|
||||
: raw(0)
|
||||
{ }
|
||||
|
||||
constexpr BigEndian(T value)
|
||||
{
|
||||
raw = host_to_big_endian(value);
|
||||
}
|
||||
: raw(host_to_big_endian(value))
|
||||
{ }
|
||||
|
||||
constexpr operator T() const
|
||||
{
|
||||
return host_to_big_endian(raw);
|
||||
}
|
||||
|
||||
private:
|
||||
T raw;
|
||||
};
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <new>
|
||||
#if __has_include(<new>)
|
||||
#include <new>
|
||||
#else
|
||||
#include <stddef.h>
|
||||
|
||||
inline void* operator new(size_t, void* addr) { return addr; }
|
||||
inline void* operator new[](size_t, void* addr) { return addr; }
|
||||
#endif
|
||||
|
|
|
@ -83,5 +83,5 @@ command_line_enter_msg:
|
|||
command_line:
|
||||
# 100 character command line
|
||||
command_line_buffer:
|
||||
.ascii "root=/dev/sda2"
|
||||
.skip 100 - 28
|
||||
.ascii "root=UUID=9C87D2AF-566A-4517-971A-57BA86EEA88D"
|
||||
.skip 100 - (. - command_line_buffer)
|
||||
|
|
|
@ -89,6 +89,7 @@ set(KERNEL_SOURCES
|
|||
kernel/Storage/NVMe/Namespace.cpp
|
||||
kernel/Storage/NVMe/Queue.cpp
|
||||
kernel/Storage/Partition.cpp
|
||||
kernel/Storage/SCSI.cpp
|
||||
kernel/Storage/StorageDevice.cpp
|
||||
kernel/Syscall.cpp
|
||||
kernel/Terminal/FramebufferTerminal.cpp
|
||||
|
@ -105,6 +106,8 @@ set(KERNEL_SOURCES
|
|||
kernel/USB/HID/HIDDriver.cpp
|
||||
kernel/USB/HID/Keyboard.cpp
|
||||
kernel/USB/HID/Mouse.cpp
|
||||
kernel/USB/MassStorage/MassStorageDriver.cpp
|
||||
kernel/USB/MassStorage/SCSIDevice.cpp
|
||||
kernel/USB/USBManager.cpp
|
||||
kernel/USB/XHCI/Controller.cpp
|
||||
kernel/USB/XHCI/Device.cpp
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
#define DEBUG_USB_HID 0
|
||||
#define DEBUG_USB_KEYBOARD 0
|
||||
#define DEBUG_USB_MOUSE 0
|
||||
#define DEBUG_USB_MASS_STORAGE 0
|
||||
|
||||
|
||||
namespace Debug
|
||||
|
|
|
@ -14,7 +14,6 @@ namespace Kernel
|
|||
Ext2_NoInodes,
|
||||
Storage_Boundaries,
|
||||
Storage_GPTHeader,
|
||||
ATA_NoLBA,
|
||||
ATA_AMNF,
|
||||
ATA_TKZNF,
|
||||
ATA_ABRT,
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Array.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <kernel/Memory/Types.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
|
|
|
@ -35,11 +35,12 @@ namespace Kernel
|
|||
{}
|
||||
BAN::ErrorOr<void> initialize();
|
||||
|
||||
void select_device(bool secondary);
|
||||
BAN::ErrorOr<DeviceType> identify(bool secondary, BAN::Span<uint16_t> buffer);
|
||||
BAN::ErrorOr<void> send_command(ATADevice&, uint64_t lba, uint64_t sector_count, bool write);
|
||||
|
||||
void block_until_irq();
|
||||
//uint8_t device_index(const ATADevice&) const;
|
||||
void select_device(bool is_secondary);
|
||||
BAN::ErrorOr<DeviceType> identify(bool is_secondary, BAN::Span<uint16_t> buffer);
|
||||
|
||||
BAN::ErrorOr<void> block_until_irq();
|
||||
|
||||
uint8_t io_read(uint16_t);
|
||||
void io_write(uint16_t, uint8_t);
|
||||
|
|
|
@ -19,18 +19,19 @@ namespace Kernel
|
|||
};
|
||||
|
||||
public:
|
||||
virtual ~ATABaseDevice() {};
|
||||
virtual ~ATABaseDevice();
|
||||
|
||||
virtual uint32_t sector_size() const override { return m_sector_words * 2; }
|
||||
virtual uint64_t total_size() const override { return m_lba_count * sector_size(); }
|
||||
|
||||
uint32_t words_per_sector() const { return m_sector_words; }
|
||||
uint64_t sector_count() const { return m_lba_count; }
|
||||
bool has_lba() const { return m_has_lba; }
|
||||
|
||||
BAN::StringView model() const { return m_model; }
|
||||
BAN::StringView name() const { return m_name; }
|
||||
BAN::StringView name() const override { return m_name; }
|
||||
|
||||
virtual dev_t rdev() const override { return m_rdev; }
|
||||
dev_t rdev() const override { return m_rdev; }
|
||||
|
||||
protected:
|
||||
ATABaseDevice();
|
||||
|
@ -42,6 +43,7 @@ namespace Kernel
|
|||
uint32_t m_command_set;
|
||||
uint32_t m_sector_words;
|
||||
uint64_t m_lba_count;
|
||||
bool m_has_lba;
|
||||
char m_model[41];
|
||||
char m_name[4] {};
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
dev_t scsi_get_rdev();
|
||||
void scsi_free_rdev(dev_t);
|
||||
|
||||
}
|
|
@ -19,7 +19,9 @@ namespace Kernel
|
|||
USBClassDriver() = default;
|
||||
virtual ~USBClassDriver() = default;
|
||||
|
||||
virtual void handle_input_data(BAN::ConstByteSpan, uint8_t endpoint_id) = 0;
|
||||
virtual BAN::ErrorOr<void> initialize() { return {}; };
|
||||
|
||||
virtual void handle_input_data(size_t byte_count, uint8_t endpoint_id) = 0;
|
||||
};
|
||||
|
||||
class USBDevice
|
||||
|
@ -64,11 +66,12 @@ namespace Kernel
|
|||
|
||||
virtual BAN::ErrorOr<void> initialize_endpoint(const USBEndpointDescriptor&) = 0;
|
||||
virtual BAN::ErrorOr<size_t> send_request(const USBDeviceRequest&, paddr_t buffer) = 0;
|
||||
virtual void send_data_buffer(uint8_t endpoint_id, paddr_t buffer, size_t buffer_len) = 0;
|
||||
|
||||
static USB::SpeedClass determine_speed_class(uint64_t bits_per_second);
|
||||
|
||||
protected:
|
||||
void handle_input_data(BAN::ConstByteSpan, uint8_t endpoint_id);
|
||||
void handle_input_data(size_t byte_count, uint8_t endpoint_id);
|
||||
virtual BAN::ErrorOr<void> initialize_control_endpoint() = 0;
|
||||
|
||||
private:
|
||||
|
|
|
@ -75,15 +75,13 @@ namespace Kernel
|
|||
};
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::UniqPtr<USBHIDDriver>> create(USBDevice&, const USBDevice::InterfaceDescriptor&);
|
||||
|
||||
void handle_input_data(BAN::ConstByteSpan, uint8_t endpoint_id) override;
|
||||
void handle_input_data(size_t byte_count, uint8_t endpoint_id) override;
|
||||
|
||||
private:
|
||||
USBHIDDriver(USBDevice&, const USBDevice::InterfaceDescriptor&);
|
||||
~USBHIDDriver();
|
||||
|
||||
BAN::ErrorOr<void> initialize();
|
||||
BAN::ErrorOr<void> initialize() override;
|
||||
|
||||
BAN::ErrorOr<BAN::Vector<DeviceReport>> initializes_device_reports(const BAN::Vector<USBHID::Collection>&);
|
||||
|
||||
|
@ -94,6 +92,9 @@ namespace Kernel
|
|||
bool m_uses_report_id { false };
|
||||
BAN::Vector<DeviceReport> m_device_inputs;
|
||||
|
||||
uint8_t m_data_endpoint_id = 0;
|
||||
BAN::UniqPtr<DMARegion> m_data_buffer;
|
||||
|
||||
friend class BAN::UniqPtr<USBHIDDriver>;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Kernel::USBMassStorage
|
||||
{
|
||||
|
||||
struct CBW
|
||||
{
|
||||
uint32_t dCBWSignature;
|
||||
uint32_t dCBWTag;
|
||||
uint32_t dCBWDataTransferLength;
|
||||
uint8_t bmCBWFlags;
|
||||
uint8_t bCBWLUN;
|
||||
uint8_t bCBWCBLength;
|
||||
uint8_t CBWCB[16];
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(CBW) == 31);
|
||||
|
||||
struct CSW
|
||||
{
|
||||
uint32_t dCSWSignature;
|
||||
uint32_t dCSWTag;
|
||||
uint32_t dCSWDataResidue;
|
||||
uint8_t bmCSWStatus;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(CSW) == 13);
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Function.h>
|
||||
|
||||
#include <kernel/Lock/Mutex.h>
|
||||
#include <kernel/Storage/StorageDevice.h>
|
||||
#include <kernel/USB/Device.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class USBMassStorageDriver final : public USBClassDriver
|
||||
{
|
||||
BAN_NON_COPYABLE(USBMassStorageDriver);
|
||||
BAN_NON_MOVABLE(USBMassStorageDriver);
|
||||
|
||||
public:
|
||||
void handle_input_data(size_t byte_count, uint8_t endpoint_id) override;
|
||||
|
||||
BAN::ErrorOr<size_t> send_bytes(paddr_t, size_t count);
|
||||
BAN::ErrorOr<size_t> recv_bytes(paddr_t, size_t count);
|
||||
|
||||
void lock() { m_mutex.lock(); }
|
||||
void unlock() { m_mutex.unlock(); }
|
||||
|
||||
private:
|
||||
USBMassStorageDriver(USBDevice&, const USBDevice::InterfaceDescriptor&);
|
||||
~USBMassStorageDriver();
|
||||
|
||||
BAN::ErrorOr<void> initialize() override;
|
||||
|
||||
private:
|
||||
USBDevice& m_device;
|
||||
USBDevice::InterfaceDescriptor m_interface;
|
||||
|
||||
Mutex m_mutex;
|
||||
|
||||
uint8_t m_in_endpoint_id { 0 };
|
||||
BAN::Function<void(size_t)> m_in_callback;
|
||||
|
||||
uint8_t m_out_endpoint_id { 0 };
|
||||
BAN::Function<void(size_t)> m_out_callback;
|
||||
|
||||
BAN::Vector<BAN::RefPtr<StorageDevice>> m_storage_devices;
|
||||
|
||||
friend class BAN::UniqPtr<USBMassStorageDriver>;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/FS/DevFS/FileSystem.h>
|
||||
#include <kernel/Storage/StorageDevice.h>
|
||||
#include <kernel/USB/MassStorage/MassStorageDriver.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class USBSCSIDevice : public StorageDevice
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<USBSCSIDevice>> create(USBMassStorageDriver& driver, uint8_t lun, uint32_t max_packet_size);
|
||||
|
||||
uint32_t sector_size() const override { return m_block_size; }
|
||||
uint64_t total_size() const override { return m_block_size * m_block_count; }
|
||||
|
||||
dev_t rdev() const override { return m_rdev; }
|
||||
BAN::StringView name() const override { return m_name; }
|
||||
|
||||
private:
|
||||
USBSCSIDevice(USBMassStorageDriver& driver, uint8_t lun, BAN::UniqPtr<DMARegion>&&, uint64_t block_count, uint32_t block_size);
|
||||
~USBSCSIDevice();
|
||||
|
||||
static BAN::ErrorOr<size_t> send_scsi_command_impl(USBMassStorageDriver&, DMARegion& dma_region, uint8_t lun, BAN::ConstByteSpan command, BAN::ByteSpan data, bool in);
|
||||
BAN::ErrorOr<size_t> send_scsi_command(BAN::ConstByteSpan command, BAN::ByteSpan data, bool in);
|
||||
|
||||
BAN::ErrorOr<void> read_sectors_impl(uint64_t first_lba, uint64_t sector_count, BAN::ByteSpan buffer) override;
|
||||
BAN::ErrorOr<void> write_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ConstByteSpan buffer) override;
|
||||
|
||||
private:
|
||||
USBMassStorageDriver& m_driver;
|
||||
BAN::UniqPtr<DMARegion> m_dma_region;
|
||||
|
||||
const uint8_t m_lun;
|
||||
|
||||
const uint64_t m_block_count;
|
||||
const uint32_t m_block_size;
|
||||
|
||||
const dev_t m_rdev;
|
||||
const char m_name[4];
|
||||
|
||||
friend class BAN::RefPtr<USBSCSIDevice>;
|
||||
};
|
||||
|
||||
}
|
|
@ -27,7 +27,6 @@ namespace Kernel
|
|||
volatile uint32_t transfer_count { 0 };
|
||||
volatile XHCI::TRB completion_trb;
|
||||
|
||||
BAN::UniqPtr<DMARegion> data_region;
|
||||
void(XHCIDevice::*callback)(XHCI::TRB);
|
||||
};
|
||||
|
||||
|
@ -36,6 +35,7 @@ namespace Kernel
|
|||
|
||||
BAN::ErrorOr<void> initialize_endpoint(const USBEndpointDescriptor&) override;
|
||||
BAN::ErrorOr<size_t> send_request(const USBDeviceRequest&, paddr_t buffer) override;
|
||||
void send_data_buffer(uint8_t endpoint_id, paddr_t buffer, size_t buffer_size) override;
|
||||
|
||||
void on_transfer_event(const volatile XHCI::TRB&);
|
||||
|
||||
|
@ -47,7 +47,7 @@ namespace Kernel
|
|||
~XHCIDevice();
|
||||
BAN::ErrorOr<void> update_actual_max_packet_size();
|
||||
|
||||
void on_interrupt_endpoint_event(XHCI::TRB);
|
||||
void on_interrupt_or_bulk_endpoint_event(XHCI::TRB);
|
||||
|
||||
void advance_endpoint_enqueue(Endpoint&, bool chain);
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ namespace Kernel
|
|||
"Ext2 filesystem out of inodes",
|
||||
"Attempted to access outside of device boundaries",
|
||||
"Device has invalid GPT header",
|
||||
"Device does not support LBA addressing",
|
||||
"Address mark not found",
|
||||
"Track zero not found",
|
||||
"Aborted command",
|
||||
|
|
|
@ -543,7 +543,7 @@ namespace Kernel
|
|||
|
||||
BAN::ErrorOr<void> TmpDirectoryInode::link_inode(TmpInode& inode, BAN::StringView name)
|
||||
{
|
||||
static constexpr size_t directory_entry_alignment = 16;
|
||||
static constexpr size_t directory_entry_alignment = sizeof(TmpDirectoryEntry);
|
||||
|
||||
auto find_result = find_inode_impl(name);
|
||||
if (!find_result.is_error())
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <kernel/FS/VirtualFileSystem.h>
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Storage/Partition.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
|
@ -14,55 +15,105 @@ namespace Kernel
|
|||
|
||||
static BAN::RefPtr<VirtualFileSystem> s_instance;
|
||||
|
||||
static BAN::ErrorOr<BAN::RefPtr<BlockDevice>> find_partition_by_uuid(BAN::StringView uuid)
|
||||
{
|
||||
ASSERT(uuid.size() == 36);
|
||||
|
||||
BAN::RefPtr<BlockDevice> result;
|
||||
DevFileSystem::get().for_each_inode(
|
||||
[&result, uuid](BAN::RefPtr<Inode> inode) -> BAN::Iteration
|
||||
{
|
||||
if (!inode->is_device())
|
||||
return BAN::Iteration::Continue;
|
||||
if (!static_cast<Device*>(inode.ptr())->is_partition())
|
||||
return BAN::Iteration::Continue;
|
||||
auto* partition = static_cast<Partition*>(inode.ptr());
|
||||
if (partition->uuid() != uuid)
|
||||
return BAN::Iteration::Continue;
|
||||
result = partition;
|
||||
return BAN::Iteration::Break;
|
||||
}
|
||||
);
|
||||
|
||||
if (!result)
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
return result;
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<BAN::RefPtr<BlockDevice>> find_block_device_by_name(BAN::StringView name)
|
||||
{
|
||||
auto device_inode = TRY(DevFileSystem::get().root_inode()->find_inode(name));
|
||||
if (!device_inode->mode().ifblk())
|
||||
return BAN::Error::from_errno(ENOTBLK);
|
||||
return BAN::RefPtr<BlockDevice>(static_cast<BlockDevice*>(device_inode.ptr()));
|
||||
}
|
||||
|
||||
static BAN::RefPtr<BlockDevice> find_root_device(BAN::StringView root_path)
|
||||
{
|
||||
enum class RootType
|
||||
{
|
||||
PartitionUUID,
|
||||
BlockDeviceName,
|
||||
};
|
||||
|
||||
BAN::StringView entry;
|
||||
RootType type;
|
||||
|
||||
if (root_path.size() >= 5 && root_path.substring(0, 5) == "UUID="_sv)
|
||||
{
|
||||
entry = root_path.substring(5);
|
||||
if (entry.size() != 36)
|
||||
panic("Invalid UUID '{}'", entry);
|
||||
type = RootType::PartitionUUID;
|
||||
}
|
||||
else if (root_path.size() >= 5 && root_path.substring(0, 5) == "/dev/"_sv)
|
||||
{
|
||||
entry = root_path.substring(5);
|
||||
if (entry.empty() || entry.contains('/'))
|
||||
panic("Invalid root path '{}'", root_path);
|
||||
type = RootType::BlockDeviceName;
|
||||
}
|
||||
else
|
||||
{
|
||||
panic("Unsupported root path format '{}'", root_path);
|
||||
}
|
||||
|
||||
constexpr size_t timeout_ms = 10'000;
|
||||
constexpr size_t sleep_ms = 500;
|
||||
|
||||
for (size_t i = 0; i < timeout_ms / sleep_ms; i++)
|
||||
{
|
||||
BAN::ErrorOr<BAN::RefPtr<BlockDevice>> ret = BAN::Error::from_errno(EINVAL);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case RootType::PartitionUUID:
|
||||
ret = find_partition_by_uuid(entry);
|
||||
break;
|
||||
case RootType::BlockDeviceName:
|
||||
ret = find_block_device_by_name(entry);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret.is_error())
|
||||
return ret.release_value();
|
||||
|
||||
if (ret.error().get_error_code() != ENOENT)
|
||||
panic("could not open root device '{}': {}", root_path, ret.error());
|
||||
|
||||
SystemTimer::get().sleep_ms(sleep_ms);
|
||||
}
|
||||
|
||||
panic("could not find root device '{}' after {} ms", root_path, timeout_ms);
|
||||
}
|
||||
|
||||
void VirtualFileSystem::initialize(BAN::StringView root_path)
|
||||
{
|
||||
ASSERT(!s_instance);
|
||||
s_instance = MUST(BAN::RefPtr<VirtualFileSystem>::create());
|
||||
|
||||
BAN::RefPtr<BlockDevice> root_device;
|
||||
if (root_path.size() >= 5 && root_path.substring(0, 5) == "UUID="_sv)
|
||||
{
|
||||
auto uuid = root_path.substring(5);
|
||||
if (uuid.size() != 36)
|
||||
panic("Invalid UUID specified for root '{}'", uuid);
|
||||
|
||||
BAN::RefPtr<Partition> root_partition;
|
||||
DevFileSystem::get().for_each_inode(
|
||||
[&root_partition, uuid](BAN::RefPtr<Inode> inode) -> BAN::Iteration
|
||||
{
|
||||
if (!inode->is_device())
|
||||
return BAN::Iteration::Continue;
|
||||
if (!static_cast<Device*>(inode.ptr())->is_partition())
|
||||
return BAN::Iteration::Continue;
|
||||
auto* partition = static_cast<Partition*>(inode.ptr());
|
||||
if (partition->uuid() != uuid)
|
||||
return BAN::Iteration::Continue;
|
||||
root_partition = partition;
|
||||
return BAN::Iteration::Break;
|
||||
}
|
||||
);
|
||||
if (!root_partition)
|
||||
panic("Could not find partition with UUID '{}'", uuid);
|
||||
root_device = root_partition;
|
||||
}
|
||||
else if (root_path.size() >= 5 && root_path.substring(0, 5) == "/dev/"_sv)
|
||||
{
|
||||
auto device_name = root_path.substring(5);
|
||||
|
||||
auto device_result = DevFileSystem::get().root_inode()->find_inode(device_name);
|
||||
if (device_result.is_error())
|
||||
panic("Could not open root device '{}': {}", root_path, device_result.error());
|
||||
|
||||
auto device_inode = device_result.release_value();
|
||||
if (!device_inode->mode().ifblk())
|
||||
panic("Root inode '{}' is not an block device", root_path);
|
||||
|
||||
root_device = static_cast<BlockDevice*>(device_inode.ptr());
|
||||
}
|
||||
else
|
||||
{
|
||||
panic("Unknown root path format '{}' specified", root_path);
|
||||
}
|
||||
auto root_device = find_root_device(root_path);
|
||||
ASSERT(root_device);
|
||||
|
||||
auto filesystem_result = FileSystem::from_block_device(root_device);
|
||||
if (filesystem_result.is_error())
|
||||
|
|
|
@ -70,18 +70,19 @@ namespace Kernel::Input
|
|||
BAN::ErrorOr<void> PS2Controller::device_send_byte_and_wait_ack(uint8_t device_index, uint8_t byte)
|
||||
{
|
||||
LockGuard _(m_mutex);
|
||||
for (;;)
|
||||
for (size_t attempt = 0; attempt < 10; attempt++)
|
||||
{
|
||||
TRY(device_send_byte(device_index, byte));
|
||||
uint8_t response = TRY(read_byte());
|
||||
if (response == PS2::Response::RESEND)
|
||||
continue;
|
||||
if (response == PS2::Response::ACK)
|
||||
break;
|
||||
return {};
|
||||
dwarnln_if(DEBUG_PS2, "PS/2 device on port {} did not respond with expected ACK, got {2H}", device_index, byte);
|
||||
return BAN::Error::from_errno(EBADMSG);
|
||||
}
|
||||
return {};
|
||||
dwarnln_if(DEBUG_PS2, "PS/2 device on port {} is in resend loop", device_index, byte);
|
||||
return BAN::Error::from_errno(EBADMSG);
|
||||
}
|
||||
|
||||
uint8_t PS2Controller::get_device_index(PS2Device* device) const
|
||||
|
@ -244,7 +245,7 @@ namespace Kernel::Input
|
|||
if (fadt && fadt->revision > 1 && !(fadt->iapc_boot_arch & (1 << 1)))
|
||||
{
|
||||
dwarnln_if(DEBUG_PS2, "No PS/2 available");
|
||||
return {};
|
||||
return BAN::Error::from_errno(ENODEV);
|
||||
}
|
||||
|
||||
// Disable Devices
|
||||
|
|
|
@ -149,13 +149,9 @@ namespace Kernel
|
|||
const uintptr_t addr = argv_region->vaddr() + offset;
|
||||
TRY(argv_region->copy_data_to_region(i * sizeof(char*), reinterpret_cast<const uint8_t*>(&addr), sizeof(char*)));
|
||||
|
||||
dprintln("argv[{}] = {H}", i * sizeof(char*), addr);
|
||||
|
||||
const auto current = (i == 0) ? path : arguments[i - 1];
|
||||
TRY(argv_region->copy_data_to_region(offset, reinterpret_cast<const uint8_t*>(current.data()), current.size()));
|
||||
|
||||
dprintln(" argv[{}] = '{}'", offset, current);
|
||||
|
||||
const uint8_t zero = 0;
|
||||
TRY(argv_region->copy_data_to_region(offset + current.size(), &zero, 1));
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Kernel
|
|||
if (bus_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
auto bus = BAN::RefPtr<ATABus>::adopt(bus_ptr);
|
||||
if (bus->io_read(ATA_PORT_STATUS) == 0x00)
|
||||
if (bus->io_read(ATA_PORT_STATUS) == 0xFF)
|
||||
{
|
||||
dprintln("Floating ATA bus on IO port 0x{H}", base);
|
||||
return BAN::Error::from_errno(ENODEV);
|
||||
|
@ -69,94 +69,83 @@ namespace Kernel
|
|||
SystemTimer::get().sleep_ns(400);
|
||||
}
|
||||
|
||||
void ATABus::select_device(bool secondary)
|
||||
void ATABus::select_device(bool is_secondary)
|
||||
{
|
||||
io_write(ATA_PORT_DRIVE_SELECT, 0xA0 | ((uint8_t)secondary << 4));
|
||||
io_write(ATA_PORT_DRIVE_SELECT, 0xA0 | ((uint8_t)is_secondary << 4));
|
||||
select_delay();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<ATABus::DeviceType> ATABus::identify(bool secondary, BAN::Span<uint16_t> buffer)
|
||||
BAN::ErrorOr<ATABus::DeviceType> ATABus::identify(bool is_secondary, BAN::Span<uint16_t> buffer)
|
||||
{
|
||||
// Try to detect whether port contains device
|
||||
uint8_t status = io_read(ATA_PORT_STATUS);
|
||||
if (status & ATA_STATUS_BSY)
|
||||
{
|
||||
uint64_t timeout = SystemTimer::get().ms_since_boot() + s_ata_timeout_ms;
|
||||
while ((status = io_read(ATA_PORT_STATUS)) & ATA_STATUS_BSY)
|
||||
{
|
||||
if (SystemTimer::get().ms_since_boot() >= timeout)
|
||||
{
|
||||
dprintln("BSY flag clear timeout, assuming no drive on port");
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (__builtin_popcount(status) >= 4)
|
||||
{
|
||||
dprintln("STATUS contains garbage, assuming no drive on port");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
select_device(secondary);
|
||||
select_device(is_secondary);
|
||||
|
||||
// Disable interrupts
|
||||
io_write(ATA_PORT_CONTROL, ATA_CONTROL_nIEN);
|
||||
|
||||
io_write(ATA_PORT_SECTOR_COUNT, 0);
|
||||
io_write(ATA_PORT_LBA0, 0);
|
||||
io_write(ATA_PORT_LBA1, 0);
|
||||
io_write(ATA_PORT_LBA2, 0);
|
||||
io_write(ATA_PORT_COMMAND, ATA_COMMAND_IDENTIFY);
|
||||
SystemTimer::get().sleep_ms(1);
|
||||
|
||||
// No device on port
|
||||
if (io_read(ATA_PORT_STATUS) == 0)
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
return BAN::Error::from_errno(ENODEV);
|
||||
|
||||
DeviceType type = DeviceType::ATA;
|
||||
TRY(wait(false));
|
||||
|
||||
if (wait(true).is_error())
|
||||
const uint8_t lba1 = io_read(ATA_PORT_LBA1);
|
||||
const uint8_t lba2 = io_read(ATA_PORT_LBA2);
|
||||
|
||||
auto device_type = DeviceType::ATA;
|
||||
if (lba1 || lba2)
|
||||
{
|
||||
uint8_t lba1 = io_read(ATA_PORT_LBA1);
|
||||
uint8_t lba2 = io_read(ATA_PORT_LBA2);
|
||||
|
||||
if (lba1 == 0x14 && lba2 == 0xEB)
|
||||
type = DeviceType::ATAPI;
|
||||
device_type = DeviceType::ATAPI;
|
||||
else if (lba1 == 0x69 && lba2 == 0x96)
|
||||
type = DeviceType::ATAPI;
|
||||
device_type = DeviceType::ATAPI;
|
||||
else
|
||||
{
|
||||
dprintln("Unsupported device type");
|
||||
dprintln("Unsupported device type {2H} {2H}", lba1, lba2);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
io_write(ATA_PORT_COMMAND, ATA_COMMAND_IDENTIFY_PACKET);
|
||||
SystemTimer::get().sleep_ms(1);
|
||||
|
||||
if (auto res = wait(true); res.is_error())
|
||||
{
|
||||
dprintln("Fatal error: {}", res.error());
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
TRY(wait(true));
|
||||
|
||||
ASSERT(buffer.size() >= 256);
|
||||
read_buffer(ATA_PORT_DATA, buffer.data(), 256);
|
||||
return type;
|
||||
return device_type;
|
||||
}
|
||||
|
||||
void ATABus::handle_irq()
|
||||
{
|
||||
ASSERT(!m_has_got_irq);
|
||||
if (io_read(ATA_PORT_STATUS) & ATA_STATUS_ERR)
|
||||
dprintln("ATA Error: {}", error());
|
||||
m_has_got_irq.store(true);
|
||||
|
||||
bool expected { false };
|
||||
[[maybe_unused]] bool success = m_has_got_irq.compare_exchange(expected, true);
|
||||
ASSERT(success);
|
||||
}
|
||||
|
||||
void ATABus::block_until_irq()
|
||||
BAN::ErrorOr<void> ATABus::block_until_irq()
|
||||
{
|
||||
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + s_ata_timeout_ms;
|
||||
|
||||
bool expected { true };
|
||||
while (!m_has_got_irq.compare_exchange(expected, false))
|
||||
{
|
||||
if (SystemTimer::get().ms_since_boot() >= timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
Processor::pause();
|
||||
expected = true;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
uint8_t ATABus::io_read(uint16_t port)
|
||||
|
@ -256,29 +245,12 @@ namespace Kernel
|
|||
|
||||
LockGuard _(m_mutex);
|
||||
|
||||
if (lba < (1 << 28))
|
||||
{
|
||||
// LBA28
|
||||
io_write(ATA_PORT_DRIVE_SELECT, 0xE0 | ((uint8_t)device.is_secondary() << 4) | ((lba >> 24) & 0x0F));
|
||||
select_delay();
|
||||
io_write(ATA_PORT_CONTROL, 0);
|
||||
TRY(send_command(device, lba, sector_count, false));
|
||||
|
||||
io_write(ATA_PORT_SECTOR_COUNT, sector_count);
|
||||
io_write(ATA_PORT_LBA0, (uint8_t)(lba >> 0));
|
||||
io_write(ATA_PORT_LBA1, (uint8_t)(lba >> 8));
|
||||
io_write(ATA_PORT_LBA2, (uint8_t)(lba >> 16));
|
||||
io_write(ATA_PORT_COMMAND, ATA_COMMAND_READ_SECTORS);
|
||||
|
||||
for (uint32_t sector = 0; sector < sector_count; sector++)
|
||||
{
|
||||
block_until_irq();
|
||||
read_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector());
|
||||
}
|
||||
}
|
||||
else
|
||||
for (uint32_t sector = 0; sector < sector_count; sector++)
|
||||
{
|
||||
// LBA48
|
||||
ASSERT(false);
|
||||
TRY(block_until_irq());
|
||||
read_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector());
|
||||
}
|
||||
|
||||
return {};
|
||||
|
@ -293,33 +265,60 @@ namespace Kernel
|
|||
|
||||
LockGuard _(m_mutex);
|
||||
|
||||
if (lba < (1 << 28))
|
||||
{
|
||||
// LBA28
|
||||
io_write(ATA_PORT_DRIVE_SELECT, 0xE0 | ((uint8_t)device.is_secondary() << 4) | ((lba >> 24) & 0x0F));
|
||||
select_delay();
|
||||
io_write(ATA_PORT_CONTROL, 0);
|
||||
TRY(send_command(device, lba, sector_count, true));
|
||||
|
||||
io_write(ATA_PORT_SECTOR_COUNT, sector_count);
|
||||
io_write(ATA_PORT_LBA0, (uint8_t)(lba >> 0));
|
||||
io_write(ATA_PORT_LBA1, (uint8_t)(lba >> 8));
|
||||
io_write(ATA_PORT_LBA2, (uint8_t)(lba >> 16));
|
||||
io_write(ATA_PORT_COMMAND, ATA_COMMAND_WRITE_SECTORS);
|
||||
|
||||
for (uint32_t sector = 0; sector < sector_count; sector++)
|
||||
{
|
||||
write_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector());
|
||||
block_until_irq();
|
||||
}
|
||||
}
|
||||
else
|
||||
for (uint32_t sector = 0; sector < sector_count; sector++)
|
||||
{
|
||||
// LBA48
|
||||
ASSERT(false);
|
||||
write_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector());
|
||||
TRY(block_until_irq());
|
||||
}
|
||||
|
||||
io_write(ATA_PORT_COMMAND, ATA_COMMAND_CACHE_FLUSH);
|
||||
block_until_irq();
|
||||
TRY(block_until_irq());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> ATABus::send_command(ATADevice& device, uint64_t lba, uint64_t sector_count, bool write)
|
||||
{
|
||||
uint8_t io_select = 0;
|
||||
uint8_t io_lba0 = 0;
|
||||
uint8_t io_lba1 = 0;
|
||||
uint8_t io_lba2 = 0;
|
||||
|
||||
if (lba >= (1 << 28))
|
||||
{
|
||||
dwarnln("LBA48 addressing not supported");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
else if (device.has_lba())
|
||||
{
|
||||
io_select = 0xE0 | ((uint8_t)device.is_secondary() << 4) | ((lba >> 24) & 0x0F);
|
||||
io_lba0 = (lba >> 0) & 0xFF;
|
||||
io_lba1 = (lba >> 8) & 0xFF;
|
||||
io_lba2 = (lba >> 16) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint8_t sector = (lba % 63) + 1;
|
||||
const uint8_t head = (lba + 1 - sector) % (16 * 63) / 63;
|
||||
const uint16_t cylinder = (lba + 1 - sector) / (16 * 63);
|
||||
|
||||
io_select = 0xA0 | ((uint8_t)device.is_secondary() << 4) | head;
|
||||
io_lba0 = sector;
|
||||
io_lba1 = (cylinder >> 0) & 0xFF;
|
||||
io_lba2 = (cylinder >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
io_write(ATA_PORT_DRIVE_SELECT, io_select);
|
||||
select_delay();
|
||||
io_write(ATA_PORT_CONTROL, 0);
|
||||
|
||||
io_write(ATA_PORT_SECTOR_COUNT, sector_count);
|
||||
io_write(ATA_PORT_LBA0, io_lba0);
|
||||
io_write(ATA_PORT_LBA1, io_lba1);
|
||||
io_write(ATA_PORT_LBA2, io_lba2);
|
||||
io_write(ATA_PORT_COMMAND, write ? ATA_COMMAND_WRITE_SECTORS : ATA_COMMAND_READ_SECTORS);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -4,44 +4,43 @@
|
|||
#include <kernel/Storage/ATA/ATABus.h>
|
||||
#include <kernel/Storage/ATA/ATADefinitions.h>
|
||||
#include <kernel/Storage/ATA/ATADevice.h>
|
||||
#include <kernel/Storage/SCSI.h>
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
static dev_t get_ata_dev_minor()
|
||||
{
|
||||
static dev_t minor = 0;
|
||||
return minor++;
|
||||
}
|
||||
|
||||
detail::ATABaseDevice::ATABaseDevice()
|
||||
: m_rdev(makedev(DeviceNumber::SCSI, get_ata_dev_minor()))
|
||||
: m_rdev(scsi_get_rdev())
|
||||
{
|
||||
strcpy(m_name, "sda");
|
||||
m_name[2] += minor(m_rdev);
|
||||
}
|
||||
|
||||
detail::ATABaseDevice::~ATABaseDevice()
|
||||
{
|
||||
scsi_free_rdev(m_rdev);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> detail::ATABaseDevice::initialize(BAN::Span<const uint16_t> identify_data)
|
||||
{
|
||||
ASSERT(identify_data.size() >= 256);
|
||||
|
||||
m_signature = identify_data[ATA_IDENTIFY_SIGNATURE];
|
||||
m_signature = identify_data[ATA_IDENTIFY_SIGNATURE];
|
||||
m_capabilities = identify_data[ATA_IDENTIFY_CAPABILITIES];
|
||||
|
||||
m_command_set = 0;
|
||||
m_command_set |= (uint32_t)(identify_data[ATA_IDENTIFY_COMMAND_SET + 0] << 0);
|
||||
m_command_set |= (uint32_t)(identify_data[ATA_IDENTIFY_COMMAND_SET + 1] << 16);
|
||||
m_command_set = static_cast<uint32_t>(identify_data[ATA_IDENTIFY_COMMAND_SET + 0]) << 0;
|
||||
m_command_set |= static_cast<uint32_t>(identify_data[ATA_IDENTIFY_COMMAND_SET + 1]) << 16;
|
||||
|
||||
if (!(m_capabilities & ATA_CAPABILITIES_LBA))
|
||||
return BAN::Error::from_error_code(ErrorCode::ATA_NoLBA);
|
||||
m_has_lba = !!(m_capabilities & ATA_CAPABILITIES_LBA);
|
||||
|
||||
if ((identify_data[ATA_IDENTIFY_SECTOR_INFO] & (1 << 15)) == 0 &&
|
||||
(identify_data[ATA_IDENTIFY_SECTOR_INFO] & (1 << 14)) != 0 &&
|
||||
(identify_data[ATA_IDENTIFY_SECTOR_INFO] & (1 << 12)) != 0)
|
||||
{
|
||||
m_sector_words = *(uint32_t*)(identify_data.data() + ATA_IDENTIFY_SECTOR_WORDS);
|
||||
m_sector_words = static_cast<uint32_t>(identify_data[ATA_IDENTIFY_SECTOR_WORDS + 0]) << 0;
|
||||
m_sector_words |= static_cast<uint32_t>(identify_data[ATA_IDENTIFY_SECTOR_WORDS + 1]) << 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -50,9 +49,17 @@ namespace Kernel
|
|||
|
||||
m_lba_count = 0;
|
||||
if (m_command_set & ATA_COMMANDSET_LBA48_SUPPORTED)
|
||||
m_lba_count = *(uint64_t*)(identify_data.data() + ATA_IDENTIFY_LBA_COUNT_EXT);
|
||||
{
|
||||
m_lba_count = static_cast<uint64_t>(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 0]) << 0;
|
||||
m_lba_count |= static_cast<uint64_t>(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 1]) << 16;
|
||||
m_lba_count |= static_cast<uint64_t>(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 2]) << 32;
|
||||
m_lba_count |= static_cast<uint64_t>(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 3]) << 48;
|
||||
}
|
||||
if (m_lba_count < (1 << 28))
|
||||
m_lba_count = *(uint32_t*)(identify_data.data() + ATA_IDENTIFY_LBA_COUNT);
|
||||
{
|
||||
m_lba_count = static_cast<uint32_t>(identify_data[ATA_IDENTIFY_LBA_COUNT + 0]) << 0;
|
||||
m_lba_count |= static_cast<uint32_t>(identify_data[ATA_IDENTIFY_LBA_COUNT + 1]) << 16;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
#include <kernel/Device/DeviceNumbers.h>
|
||||
#include <kernel/Lock/SpinLock.h>
|
||||
#include <kernel/Storage/SCSI.h>
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
static uint64_t s_scsi_bitmap { 0 };
|
||||
static SpinLock s_scsi_spinlock;
|
||||
|
||||
static constexpr size_t s_scsi_bitmap_bits = sizeof(s_scsi_bitmap) * 8;
|
||||
|
||||
dev_t scsi_get_rdev()
|
||||
{
|
||||
SpinLockGuard _(s_scsi_spinlock);
|
||||
|
||||
uint64_t mask = 1;
|
||||
for (uint8_t minor = 0; minor < s_scsi_bitmap_bits; minor++, mask <<= 1)
|
||||
{
|
||||
if (s_scsi_bitmap & mask)
|
||||
continue;
|
||||
s_scsi_bitmap |= mask;
|
||||
|
||||
return makedev(DeviceNumber::SCSI, minor);
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
void scsi_free_rdev(dev_t rdev)
|
||||
{
|
||||
ASSERT(major(rdev) == static_cast<dev_t>(DeviceNumber::SCSI));
|
||||
|
||||
SpinLockGuard _(s_scsi_spinlock);
|
||||
|
||||
const uint64_t mask = static_cast<uint64_t>(1) << minor(rdev);
|
||||
ASSERT(s_scsi_bitmap & mask);
|
||||
|
||||
s_scsi_bitmap &= ~mask;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#include <kernel/Memory/DMARegion.h>
|
||||
#include <kernel/USB/Device.h>
|
||||
#include <kernel/USB/HID/HIDDriver.h>
|
||||
#include <kernel/USB/MassStorage/MassStorageDriver.h>
|
||||
|
||||
#define USB_DUMP_DESCRIPTORS 0
|
||||
|
||||
|
@ -154,7 +155,7 @@ namespace Kernel
|
|||
dprintln_if(DEBUG_USB, "Found CommunicationAndCDCControl interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::HID:
|
||||
if (auto result = USBHIDDriver::create(*this, interface); !result.is_error())
|
||||
if (auto result = BAN::UniqPtr<USBHIDDriver>::create(*this, interface); !result.is_error())
|
||||
TRY(m_class_drivers.push_back(result.release_value()));
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Physical:
|
||||
|
@ -167,7 +168,8 @@ namespace Kernel
|
|||
dprintln_if(DEBUG_USB, "Found Printer interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::MassStorage:
|
||||
dprintln_if(DEBUG_USB, "Found MassStorage interface");
|
||||
if (auto result = BAN::UniqPtr<USBMassStorageDriver>::create(*this, interface); !result.is_error())
|
||||
TRY(m_class_drivers.push_back(result.release_value()));
|
||||
break;
|
||||
case USB::InterfaceBaseClass::CDCData:
|
||||
dprintln_if(DEBUG_USB, "Found CDCData interface");
|
||||
|
@ -220,6 +222,15 @@ namespace Kernel
|
|||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_class_drivers.size(); i++)
|
||||
{
|
||||
if (auto ret = m_class_drivers[i]->initialize(); ret.is_error())
|
||||
{
|
||||
dwarnln("Could not initialize USB interface {}", ret.error());
|
||||
m_class_drivers.remove(i--);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_class_drivers.empty())
|
||||
{
|
||||
dprintln("Successfully initialized USB device with {}/{} interfaces",
|
||||
|
@ -317,10 +328,10 @@ namespace Kernel
|
|||
return BAN::move(configuration);
|
||||
}
|
||||
|
||||
void USBDevice::handle_input_data(BAN::ConstByteSpan data, uint8_t endpoint_id)
|
||||
void USBDevice::handle_input_data(size_t byte_count, uint8_t endpoint_id)
|
||||
{
|
||||
for (auto& driver : m_class_drivers)
|
||||
driver->handle_input_data(data, endpoint_id);
|
||||
driver->handle_input_data(byte_count, endpoint_id);
|
||||
}
|
||||
|
||||
USB::SpeedClass USBDevice::determine_speed_class(uint64_t bits_per_second)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <BAN/ByteSpan.h>
|
||||
#include <BAN/ScopeGuard.h>
|
||||
|
||||
#include <kernel/FS/DevFS/FileSystem.h>
|
||||
#include <kernel/USB/HID/HIDDriver.h>
|
||||
|
@ -68,13 +69,6 @@ namespace Kernel
|
|||
|
||||
static BAN::ErrorOr<BAN::Vector<Collection>> parse_report_descriptor(BAN::ConstByteSpan report_data, bool& out_use_report_id);
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<USBHIDDriver>> USBHIDDriver::create(USBDevice& device, const USBDevice::InterfaceDescriptor& interface)
|
||||
{
|
||||
auto result = TRY(BAN::UniqPtr<USBHIDDriver>::create(device, interface));
|
||||
TRY(result->initialize());
|
||||
return result;
|
||||
}
|
||||
|
||||
USBHIDDriver::USBHIDDriver(USBDevice& device, const USBDevice::InterfaceDescriptor& interface)
|
||||
: m_device(device)
|
||||
, m_interface(interface)
|
||||
|
@ -192,7 +186,29 @@ namespace Kernel
|
|||
m_device_inputs = TRY(initializes_device_reports(collections));
|
||||
|
||||
for (const auto& endpoint : m_interface.endpoints)
|
||||
TRY(m_device.initialize_endpoint(endpoint.descriptor));
|
||||
{
|
||||
const auto& desc = endpoint.descriptor;
|
||||
|
||||
if (!(desc.bEndpointAddress & 0x80))
|
||||
continue;
|
||||
if ((desc.bmAttributes & 0x03) != 0x03)
|
||||
continue;
|
||||
|
||||
TRY(m_device.initialize_endpoint(desc));
|
||||
m_data_buffer = TRY(DMARegion::create(desc.wMaxPacketSize & 0x07FF));
|
||||
|
||||
m_data_endpoint_id = (desc.bEndpointAddress & 0x0F) * 2 + !!(desc.bEndpointAddress & 0x80);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_data_endpoint_id == 0)
|
||||
{
|
||||
dwarnln("HID device does not an interrupt IN endpoints");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
m_device.send_data_buffer(m_data_endpoint_id, m_data_buffer->paddr(), m_data_buffer->size());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -256,23 +272,15 @@ namespace Kernel
|
|||
return BAN::move(result);
|
||||
}
|
||||
|
||||
void USBHIDDriver::handle_input_data(BAN::ConstByteSpan data, uint8_t endpoint_id)
|
||||
void USBHIDDriver::handle_input_data(size_t byte_count, uint8_t endpoint_id)
|
||||
{
|
||||
{
|
||||
bool found = false;
|
||||
for (const auto& endpoint : m_interface.endpoints)
|
||||
{
|
||||
const auto& desc = endpoint.descriptor;
|
||||
if (endpoint_id == (desc.bEndpointAddress & 0x0F) * 2 + !!(desc.bEndpointAddress & 0x80))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If this packet is not for us, skip it
|
||||
if (!found)
|
||||
return;
|
||||
}
|
||||
if (m_data_endpoint_id != endpoint_id)
|
||||
return;
|
||||
|
||||
auto data = BAN::ConstByteSpan(reinterpret_cast<uint8_t*>(m_data_buffer->vaddr()), byte_count);
|
||||
BAN::ScopeGuard _([&] {
|
||||
m_device.send_data_buffer(m_data_endpoint_id, m_data_buffer->paddr(), m_data_buffer->size());
|
||||
});
|
||||
|
||||
if constexpr(DEBUG_USB_HID)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
#include <BAN/Endianness.h>
|
||||
#include <BAN/ScopeGuard.h>
|
||||
#include <BAN/StringView.h>
|
||||
|
||||
#include <kernel/FS/VirtualFileSystem.h>
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
#include <kernel/USB/MassStorage/MassStorageDriver.h>
|
||||
#include <kernel/USB/MassStorage/SCSIDevice.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
USBMassStorageDriver::USBMassStorageDriver(USBDevice& device, const USBDevice::InterfaceDescriptor& interface)
|
||||
: m_device(device)
|
||||
, m_interface(interface)
|
||||
{ }
|
||||
|
||||
USBMassStorageDriver::~USBMassStorageDriver()
|
||||
{ }
|
||||
|
||||
BAN::ErrorOr<void> USBMassStorageDriver::initialize()
|
||||
{
|
||||
if (m_interface.descriptor.bInterfaceProtocol != 0x50)
|
||||
{
|
||||
dwarnln("Only USB Mass Storage BBB is supported");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
auto dma_region = TRY(DMARegion::create(PAGE_SIZE));
|
||||
|
||||
// Bulk-Only Mass Storage Reset
|
||||
{
|
||||
USBDeviceRequest reset_request {
|
||||
.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Class | USB::RequestType::Interface,
|
||||
.bRequest = 0xFF,
|
||||
.wValue = 0x0000,
|
||||
.wIndex = m_interface.descriptor.bInterfaceNumber,
|
||||
.wLength = 0x0000,
|
||||
};
|
||||
|
||||
TRY(m_device.send_request(reset_request, 0));
|
||||
}
|
||||
|
||||
// Get Max LUN
|
||||
{
|
||||
USBDeviceRequest lun_request {
|
||||
.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Interface,
|
||||
.bRequest = 0xFE,
|
||||
.wValue = 0x0000,
|
||||
.wIndex = m_interface.descriptor.bInterfaceNumber,
|
||||
.wLength = 0x0001,
|
||||
};
|
||||
|
||||
uint32_t max_lun = 0;
|
||||
const auto lun_result = m_device.send_request(lun_request, dma_region->paddr());
|
||||
if (!lun_result.is_error() && lun_result.value() == 1)
|
||||
max_lun = *reinterpret_cast<uint8_t*>(dma_region->vaddr());
|
||||
TRY(m_storage_devices.resize(max_lun + 1));
|
||||
}
|
||||
|
||||
uint32_t max_packet_size = -1;
|
||||
|
||||
// Initialize bulk-in and bulk-out endpoints
|
||||
{
|
||||
constexpr size_t invalid_index = -1;
|
||||
|
||||
size_t bulk_in_index = invalid_index;
|
||||
size_t bulk_out_index = invalid_index;
|
||||
|
||||
for (size_t i = 0; i < m_interface.endpoints.size(); i++)
|
||||
{
|
||||
const auto& endpoint = m_interface.endpoints[i].descriptor;
|
||||
if (endpoint.bmAttributes != 0x02)
|
||||
continue;
|
||||
((endpoint.bEndpointAddress & 0x80) ? bulk_in_index : bulk_out_index) = i;
|
||||
}
|
||||
|
||||
if (bulk_in_index == invalid_index || bulk_out_index == invalid_index)
|
||||
{
|
||||
dwarnln("USB Mass Storage device does not contain bulk-in and bulk-out endpoints");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
TRY(m_device.initialize_endpoint(m_interface.endpoints[bulk_in_index].descriptor));
|
||||
TRY(m_device.initialize_endpoint(m_interface.endpoints[bulk_out_index].descriptor));
|
||||
|
||||
{
|
||||
const auto& desc = m_interface.endpoints[bulk_in_index].descriptor;
|
||||
m_in_endpoint_id = (desc.bEndpointAddress & 0x0F) * 2 + !!(desc.bEndpointAddress & 0x80);
|
||||
max_packet_size = BAN::Math::min<uint32_t>(max_packet_size, desc.wMaxPacketSize);
|
||||
}
|
||||
|
||||
{
|
||||
const auto& desc = m_interface.endpoints[bulk_out_index].descriptor;
|
||||
m_out_endpoint_id = (desc.bEndpointAddress & 0x0F) * 2 + !!(desc.bEndpointAddress & 0x80);
|
||||
max_packet_size = BAN::Math::min<uint32_t>(max_packet_size, desc.wMaxPacketSize);
|
||||
}
|
||||
}
|
||||
|
||||
BAN::Function<BAN::ErrorOr<BAN::RefPtr<StorageDevice>>(USBMassStorageDriver&, uint8_t, uint32_t)> create_device_func;
|
||||
switch (m_interface.descriptor.bInterfaceSubClass)
|
||||
{
|
||||
case 0x06:
|
||||
create_device_func =
|
||||
[](USBMassStorageDriver& driver, uint8_t lun, uint32_t max_packet_size) -> BAN::ErrorOr<BAN::RefPtr<StorageDevice>>
|
||||
{
|
||||
auto ret = TRY(USBSCSIDevice::create(driver, lun, max_packet_size));
|
||||
return BAN::RefPtr<StorageDevice>(ret);
|
||||
};
|
||||
break;
|
||||
default:
|
||||
dwarnln("Unsupported command block {2H}", m_interface.descriptor.bInterfaceSubClass);
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
ASSERT(m_storage_devices.size() <= 0xFF);
|
||||
for (uint8_t lun = 0; lun < m_storage_devices.size(); lun++)
|
||||
m_storage_devices[lun] = TRY(create_device_func(*this, lun, max_packet_size));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> USBMassStorageDriver::send_bytes(paddr_t paddr, size_t count)
|
||||
{
|
||||
ASSERT(m_mutex.is_locked());
|
||||
|
||||
constexpr size_t invalid = -1;
|
||||
|
||||
static volatile size_t bytes_sent;
|
||||
bytes_sent = invalid;
|
||||
|
||||
ASSERT(!m_out_callback);
|
||||
m_out_callback = [](size_t bytes) { bytes_sent = bytes; };
|
||||
BAN::ScopeGuard _([this] { m_out_callback.clear(); });
|
||||
|
||||
m_device.send_data_buffer(m_out_endpoint_id, paddr, count);
|
||||
|
||||
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 100;
|
||||
while (bytes_sent == invalid)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(EIO);
|
||||
|
||||
return static_cast<size_t>(bytes_sent);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> USBMassStorageDriver::recv_bytes(paddr_t paddr, size_t count)
|
||||
{
|
||||
ASSERT(m_mutex.is_locked());
|
||||
|
||||
constexpr size_t invalid = -1;
|
||||
|
||||
static volatile size_t bytes_recv;
|
||||
bytes_recv = invalid;
|
||||
|
||||
ASSERT(!m_in_callback);
|
||||
m_in_callback = [](size_t bytes) { bytes_recv = bytes; };
|
||||
BAN::ScopeGuard _([this] { m_in_callback.clear(); });
|
||||
|
||||
m_device.send_data_buffer(m_in_endpoint_id, paddr, count);
|
||||
|
||||
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 100;
|
||||
while (bytes_recv == invalid)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(EIO);
|
||||
|
||||
m_in_callback.clear();
|
||||
|
||||
return static_cast<size_t>(bytes_recv);
|
||||
}
|
||||
|
||||
void USBMassStorageDriver::handle_input_data(size_t byte_count, uint8_t endpoint_id)
|
||||
{
|
||||
if (endpoint_id != m_in_endpoint_id && endpoint_id != m_out_endpoint_id)
|
||||
return;
|
||||
|
||||
dprintln_if(DEBUG_USB_MASS_STORAGE, "got {} bytes to {} endpoint", byte_count, endpoint_id == m_in_endpoint_id ? "IN" : "OUT");
|
||||
|
||||
if (endpoint_id == m_in_endpoint_id)
|
||||
{
|
||||
if (m_in_callback)
|
||||
m_in_callback(byte_count);
|
||||
else
|
||||
dwarnln("ignoring {} bytes to IN endpoint", byte_count);
|
||||
return;
|
||||
}
|
||||
|
||||
if (endpoint_id == m_out_endpoint_id)
|
||||
{
|
||||
if (m_out_callback)
|
||||
m_out_callback(byte_count);
|
||||
else
|
||||
dwarnln("ignoring {} bytes to OUT endpoint", byte_count);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
#include <BAN/Endianness.h>
|
||||
|
||||
#include <kernel/Storage/SCSI.h>
|
||||
#include <kernel/USB/MassStorage/Definitions.h>
|
||||
#include <kernel/USB/MassStorage/SCSIDevice.h>
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
namespace SCSI
|
||||
{
|
||||
|
||||
struct InquiryRes
|
||||
{
|
||||
uint8_t peripheral_device_type : 5;
|
||||
uint8_t peripheral_qualifier : 3;
|
||||
|
||||
uint8_t reserved0 : 7;
|
||||
uint8_t rmb : 1;
|
||||
|
||||
uint8_t version;
|
||||
|
||||
uint8_t response_data_format : 4;
|
||||
uint8_t hisup : 1;
|
||||
uint8_t normaca : 1;
|
||||
uint8_t obsolete0 : 1;
|
||||
uint8_t obsolete1 : 1;
|
||||
|
||||
uint8_t additional_length;
|
||||
|
||||
uint8_t protect : 1;
|
||||
uint8_t reserved1 : 2;
|
||||
uint8_t _3pc : 1;
|
||||
uint8_t tgps : 2;
|
||||
uint8_t acc : 1;
|
||||
uint8_t sccs : 1;
|
||||
|
||||
uint8_t obsolete2 : 1;
|
||||
uint8_t obsolete3 : 1;
|
||||
uint8_t obsolete4 : 1;
|
||||
uint8_t obsolete5 : 1;
|
||||
uint8_t multip : 1;
|
||||
uint8_t vs0 : 1;
|
||||
uint8_t encserv : 1;
|
||||
uint8_t obsolete6 : 1;
|
||||
|
||||
uint8_t vs1 : 1;
|
||||
uint8_t cmdque : 1;
|
||||
uint8_t obsolete7 : 1;
|
||||
uint8_t obsolete8 : 1;
|
||||
uint8_t obsolete9 : 1;
|
||||
uint8_t obsolete10 : 1;
|
||||
uint8_t obsolete11 : 1;
|
||||
uint8_t obsolete12 : 1;
|
||||
|
||||
uint8_t t10_vendor_identification[8];
|
||||
uint8_t product_identification[16];
|
||||
uint8_t product_revision_level[4];
|
||||
};
|
||||
static_assert(sizeof(InquiryRes) == 36);
|
||||
|
||||
struct ReadCapacity10
|
||||
{
|
||||
BAN::BigEndian<uint32_t> logical_block_address {};
|
||||
BAN::BigEndian<uint32_t> block_length;
|
||||
};
|
||||
static_assert(sizeof(ReadCapacity10) == 8);
|
||||
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<USBSCSIDevice>> USBSCSIDevice::create(USBMassStorageDriver& driver, uint8_t lun, uint32_t max_packet_size)
|
||||
{
|
||||
auto dma_region = TRY(DMARegion::create(max_packet_size));
|
||||
|
||||
dprintln("USB SCSI device");
|
||||
|
||||
{
|
||||
const uint8_t scsi_inquiry_req[6] {
|
||||
0x12,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00, sizeof(SCSI::InquiryRes),
|
||||
0x00
|
||||
};
|
||||
SCSI::InquiryRes inquiry_res;
|
||||
TRY(send_scsi_command_impl(driver, *dma_region, lun, BAN::ConstByteSpan::from(scsi_inquiry_req), BAN::ByteSpan::from(inquiry_res), true));
|
||||
|
||||
dprintln(" vendor: {}", BAN::StringView(reinterpret_cast<const char*>(inquiry_res.t10_vendor_identification), 8));
|
||||
dprintln(" product: {}", BAN::StringView(reinterpret_cast<const char*>(inquiry_res.product_identification), 16));
|
||||
dprintln(" revision: {}", BAN::StringView(reinterpret_cast<const char*>(inquiry_res.product_revision_level), 4));
|
||||
}
|
||||
|
||||
uint32_t block_count;
|
||||
uint32_t block_size;
|
||||
|
||||
{
|
||||
const uint8_t scsi_read_capacity_req[10] {
|
||||
0x25,
|
||||
0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00,
|
||||
0x00
|
||||
};
|
||||
SCSI::ReadCapacity10 read_capacity_res;
|
||||
TRY(send_scsi_command_impl(driver, *dma_region, lun, BAN::ConstByteSpan::from(scsi_read_capacity_req), BAN::ByteSpan::from(read_capacity_res), true));
|
||||
|
||||
block_count = read_capacity_res.logical_block_address + 1;
|
||||
block_size = read_capacity_res.block_length;
|
||||
|
||||
if (block_count == 0)
|
||||
{
|
||||
dwarnln("Too big USB storage");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
dprintln(" last LBA: {}", block_count);
|
||||
dprintln(" block size: {}", block_size);
|
||||
dprintln(" total size: {} MiB", block_count * block_size / 1024 / 1024);
|
||||
}
|
||||
|
||||
auto result = TRY(BAN::RefPtr<USBSCSIDevice>::create(driver, lun, BAN::move(dma_region), block_count, block_size));
|
||||
result->add_disk_cache();
|
||||
DevFileSystem::get().add_device(result);
|
||||
if (auto res = result->initialize_partitions(result->name()); res.is_error())
|
||||
dprintln("{}", res.error());
|
||||
return result;
|
||||
}
|
||||
|
||||
USBSCSIDevice::USBSCSIDevice(USBMassStorageDriver& driver, uint8_t lun, BAN::UniqPtr<DMARegion>&& dma_region, uint64_t block_count, uint32_t block_size)
|
||||
: m_driver(driver)
|
||||
, m_dma_region(BAN::move(dma_region))
|
||||
, m_lun(lun)
|
||||
, m_block_count(block_count)
|
||||
, m_block_size(block_size)
|
||||
, m_rdev(scsi_get_rdev())
|
||||
, m_name { 's', 'd', (char)('a' + minor(m_rdev)), '\0' }
|
||||
{ }
|
||||
|
||||
USBSCSIDevice::~USBSCSIDevice()
|
||||
{
|
||||
scsi_free_rdev(m_rdev);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> USBSCSIDevice::send_scsi_command(BAN::ConstByteSpan scsi_command, BAN::ByteSpan data, bool in)
|
||||
{
|
||||
return TRY(send_scsi_command_impl(m_driver, *m_dma_region, m_lun, scsi_command, data, in));
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> USBSCSIDevice::send_scsi_command_impl(USBMassStorageDriver& driver, DMARegion& dma_region, uint8_t lun, BAN::ConstByteSpan scsi_command, BAN::ByteSpan data, bool in)
|
||||
{
|
||||
ASSERT(scsi_command.size() <= 16);
|
||||
|
||||
LockGuard _(driver);
|
||||
|
||||
auto& cbw = *reinterpret_cast<USBMassStorage::CBW*>(dma_region.vaddr());
|
||||
cbw = {
|
||||
.dCBWSignature = 0x43425355,
|
||||
.dCBWTag = 0x00000000,
|
||||
.dCBWDataTransferLength = static_cast<uint32_t>(data.size()),
|
||||
.bmCBWFlags = static_cast<uint8_t>(in ? 0x80 : 0x00),
|
||||
.bCBWLUN = lun,
|
||||
.bCBWCBLength = static_cast<uint8_t>(scsi_command.size()),
|
||||
.CBWCB = {},
|
||||
};
|
||||
memcpy(cbw.CBWCB, scsi_command.data(), scsi_command.size());
|
||||
|
||||
if (TRY(driver.send_bytes(dma_region.paddr(), sizeof(USBMassStorage::CBW))) != sizeof(USBMassStorage::CBW))
|
||||
{
|
||||
dwarnln("failed to send full CBW");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
const size_t ntransfer =
|
||||
TRY([&]() -> BAN::ErrorOr<size_t>
|
||||
{
|
||||
if (data.empty())
|
||||
return 0;
|
||||
if (in)
|
||||
return TRY(driver.recv_bytes(dma_region.paddr(), data.size()));
|
||||
memcpy(reinterpret_cast<void*>(dma_region.vaddr()), data.data(), data.size());
|
||||
return TRY(driver.send_bytes(dma_region.paddr(), data.size()));
|
||||
}());
|
||||
|
||||
if (ntransfer > data.size())
|
||||
{
|
||||
dwarnln("device responded with more bytes than requested");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
if (in && !data.empty())
|
||||
memcpy(data.data(), reinterpret_cast<void*>(dma_region.vaddr()), ntransfer);
|
||||
|
||||
if (TRY(driver.recv_bytes(dma_region.paddr(), sizeof(USBMassStorage::CSW))) != sizeof(USBMassStorage::CSW))
|
||||
{
|
||||
dwarnln("could not receive full CSW");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
if (auto status = reinterpret_cast<USBMassStorage::CSW*>(dma_region.vaddr())->bmCSWStatus)
|
||||
{
|
||||
dwarnln("CSW status {2H}", status);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
return ntransfer;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> USBSCSIDevice::read_sectors_impl(uint64_t first_lba, uint64_t sector_count, BAN::ByteSpan buffer)
|
||||
{
|
||||
const size_t max_blocks_per_read = m_dma_region->size() / m_block_size;
|
||||
ASSERT(max_blocks_per_read <= 0xFFFF);
|
||||
|
||||
for (uint64_t i = 0; i < sector_count;)
|
||||
{
|
||||
const uint32_t lba = first_lba + i;
|
||||
const uint32_t count = BAN::Math::min<uint32_t>(max_blocks_per_read, sector_count - i);
|
||||
|
||||
const uint8_t scsi_read_req[10] {
|
||||
0x28,
|
||||
0x00,
|
||||
(uint8_t)(lba >> 24), (uint8_t)(lba >> 16), (uint8_t)(lba >> 8), (uint8_t)(lba >> 0),
|
||||
0x00,
|
||||
(uint8_t)(count >> 8), (uint8_t)(count >> 0),
|
||||
0x00
|
||||
};
|
||||
TRY(send_scsi_command(BAN::ConstByteSpan::from(scsi_read_req), buffer.slice(i * m_block_size, count * m_block_size), true));
|
||||
|
||||
i += count;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> USBSCSIDevice::write_sectors_impl(uint64_t first_lba, uint64_t sector_count, BAN::ConstByteSpan _buffer)
|
||||
{
|
||||
const size_t max_blocks_per_write = m_dma_region->size() / m_block_size;
|
||||
ASSERT(max_blocks_per_write <= 0xFFFF);
|
||||
|
||||
auto buffer = BAN::ByteSpan(const_cast<uint8_t*>(_buffer.data()), _buffer.size());
|
||||
|
||||
for (uint64_t i = 0; i < sector_count;)
|
||||
{
|
||||
const uint32_t lba = first_lba + i;
|
||||
const uint32_t count = BAN::Math::min<uint32_t>(max_blocks_per_write, sector_count - i);
|
||||
|
||||
const uint8_t scsi_write_req[10] {
|
||||
0x2A,
|
||||
0x00,
|
||||
(uint8_t)(lba >> 24), (uint8_t)(lba >> 16), (uint8_t)(lba >> 8), (uint8_t)(lba >> 0),
|
||||
0x00,
|
||||
(uint8_t)(count >> 8), (uint8_t)(count >> 0),
|
||||
0x00
|
||||
};
|
||||
TRY(send_scsi_command(BAN::ConstByteSpan::from(scsi_write_req), buffer.slice(i * m_block_size, count * m_block_size), false));
|
||||
|
||||
i += count;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
|
@ -159,7 +159,13 @@ namespace Kernel
|
|||
{
|
||||
auto& protocol = reinterpret_cast<volatile XHCI::SupportedPrococolCap&>(ext_cap);
|
||||
|
||||
if (protocol.name_string != *reinterpret_cast<const uint32_t*>("USB "))
|
||||
const uint32_t target_name_string {
|
||||
('U' << 0) |
|
||||
('S' << 8) |
|
||||
('B' << 16) |
|
||||
(' ' << 24)
|
||||
};
|
||||
if (protocol.name_string != target_name_string)
|
||||
{
|
||||
dwarnln("Invalid port protocol name string");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
@ -268,6 +274,7 @@ namespace Kernel
|
|||
// read and clear needed change flags
|
||||
const bool reset_change = op_port.portsc & XHCI::PORTSC::PRC;
|
||||
const bool connection_change = op_port.portsc & XHCI::PORTSC::CSC;
|
||||
const bool port_enabled = op_port.portsc & XHCI::PORTSC::PED;
|
||||
op_port.portsc = XHCI::PORTSC::CSC | XHCI::PORTSC::PRC | XHCI::PORTSC::PP;
|
||||
|
||||
if (!(op_port.portsc & XHCI::PORTSC::CCS))
|
||||
|
@ -284,18 +291,18 @@ namespace Kernel
|
|||
switch (my_port.revision_major)
|
||||
{
|
||||
case 2:
|
||||
if (!reset_change)
|
||||
{
|
||||
if (connection_change)
|
||||
op_port.portsc = XHCI::PORTSC::PR | XHCI::PORTSC::PP;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!connection_change)
|
||||
continue;
|
||||
dprintln_if(DEBUG_XHCI, "USB 3 devices not supported");
|
||||
// USB2 ports advance to Enabled state after a reset
|
||||
if (port_enabled && reset_change)
|
||||
break;
|
||||
// reset port
|
||||
if (connection_change)
|
||||
op_port.portsc = XHCI::PORTSC::PR | XHCI::PORTSC::PP;
|
||||
continue;
|
||||
case 3:
|
||||
if (!connection_change || !port_enabled)
|
||||
continue;
|
||||
// USB3 ports advance to Enabled state automatically
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -106,9 +106,7 @@ namespace Kernel
|
|||
TRY(m_controller.send_command(address_device));
|
||||
}
|
||||
|
||||
// NOTE: Full speed devices can have other max packet sizes than 8
|
||||
if (m_speed_class == USB::SpeedClass::FullSpeed)
|
||||
TRY(update_actual_max_packet_size());
|
||||
TRY(update_actual_max_packet_size());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -117,7 +115,7 @@ namespace Kernel
|
|||
{
|
||||
// FIXME: This is more or less generic USB code
|
||||
|
||||
dprintln_if(DEBUG_XHCI, "Retrieving actual max packet size of full speed device");
|
||||
dprintln_if(DEBUG_XHCI, "Retrieving actual max packet size");
|
||||
|
||||
BAN::Vector<uint8_t> buffer;
|
||||
TRY(buffer.resize(8, 0));
|
||||
|
@ -130,7 +128,13 @@ namespace Kernel
|
|||
request.wLength = 8;
|
||||
TRY(send_request(request, kmalloc_paddr_of((vaddr_t)buffer.data()).value()));
|
||||
|
||||
m_endpoints[0].max_packet_size = buffer.back();
|
||||
const bool is_usb3 = m_controller.port(m_port_id).revision_major == 3;
|
||||
const uint32_t new_max_packet_size = is_usb3 ? 1u << buffer.back() : buffer.back();
|
||||
|
||||
if (m_endpoints[0].max_packet_size == new_max_packet_size)
|
||||
return {};
|
||||
|
||||
m_endpoints[0].max_packet_size = new_max_packet_size;
|
||||
|
||||
const uint32_t context_size = m_controller.context_size_set() ? 64 : 32;
|
||||
|
||||
|
@ -200,20 +204,20 @@ namespace Kernel
|
|||
|
||||
BAN::ErrorOr<void> XHCIDevice::initialize_endpoint(const USBEndpointDescriptor& endpoint_descriptor)
|
||||
{
|
||||
ASSERT(m_controller.port(m_port_id).revision_major == 2);
|
||||
const bool is_control { (endpoint_descriptor.bmAttributes & 0x03) == 0x00 };
|
||||
const bool is_isoch { (endpoint_descriptor.bmAttributes & 0x03) == 0x01 };
|
||||
const bool is_bulk { (endpoint_descriptor.bmAttributes & 0x03) == 0x02 };
|
||||
const bool is_interrupt { (endpoint_descriptor.bmAttributes & 0x03) == 0x03 };
|
||||
|
||||
const uint32_t endpoint_id = (endpoint_descriptor.bEndpointAddress & 0x0F) * 2 + !!(endpoint_descriptor.bEndpointAddress & 0x80);
|
||||
const uint32_t max_packet_size = endpoint_descriptor.wMaxPacketSize & 0x07FF;
|
||||
const uint32_t max_burst_size = (endpoint_descriptor.wMaxPacketSize >> 11) & 0x0003;
|
||||
const uint32_t max_esit_payload = max_packet_size * (max_burst_size + 1);
|
||||
const uint32_t interval = determine_interval(endpoint_descriptor, m_speed_class);
|
||||
const uint32_t average_trb_length = ((endpoint_descriptor.bmAttributes & 3) == 0b00) ? 8 : max_esit_payload;
|
||||
const uint32_t error_count = ((endpoint_descriptor.bmAttributes & 3) == 0b01) ? 0 : 3;
|
||||
(void)is_control;
|
||||
(void)is_isoch;
|
||||
(void)is_bulk;
|
||||
(void)is_interrupt;
|
||||
|
||||
XHCI::EndpointType endpoint_type;
|
||||
switch ((endpoint_descriptor.bEndpointAddress & 0x80) | (endpoint_descriptor.bmAttributes & 0x03))
|
||||
{
|
||||
case 0x00:
|
||||
case 0x00: ASSERT_NOT_REACHED();
|
||||
case 0x80: endpoint_type = XHCI::EndpointType::Control; break;
|
||||
case 0x01: endpoint_type = XHCI::EndpointType::IsochOut; break;
|
||||
case 0x81: endpoint_type = XHCI::EndpointType::IsochIn; break;
|
||||
|
@ -224,6 +228,16 @@ namespace Kernel
|
|||
default: ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
// FIXME: Streams
|
||||
|
||||
const uint32_t endpoint_id = (endpoint_descriptor.bEndpointAddress & 0x0F) * 2 + !!(endpoint_descriptor.bEndpointAddress & 0x80);
|
||||
const uint32_t max_packet_size = (is_control || is_bulk) ? endpoint_descriptor.wMaxPacketSize : endpoint_descriptor.wMaxPacketSize & 0x07FF;
|
||||
const uint32_t max_burst_size = (is_control || is_bulk) ? 0 : (endpoint_descriptor.wMaxPacketSize & 0x1800) >> 11;
|
||||
const uint32_t max_esit_payload = max_packet_size * (max_burst_size + 1);
|
||||
const uint32_t interval = determine_interval(endpoint_descriptor, m_speed_class);
|
||||
const uint32_t average_trb_length = (is_control) ? 8 : max_esit_payload;
|
||||
const uint32_t error_count = (is_isoch) ? 0 : 3;
|
||||
|
||||
auto& endpoint = m_endpoints[endpoint_id - 1];
|
||||
ASSERT(!endpoint.transfer_ring);
|
||||
|
||||
|
@ -237,8 +251,7 @@ namespace Kernel
|
|||
endpoint.dequeue_index = 0;
|
||||
endpoint.enqueue_index = 0;
|
||||
endpoint.cycle_bit = 1;
|
||||
endpoint.callback = &XHCIDevice::on_interrupt_endpoint_event;
|
||||
endpoint.data_region = TRY(DMARegion::create(endpoint.max_packet_size));
|
||||
endpoint.callback = (is_interrupt || is_bulk) ? &XHCIDevice::on_interrupt_or_bulk_endpoint_event : nullptr;
|
||||
|
||||
memset(reinterpret_cast<void*>(endpoint.transfer_ring->vaddr()), 0, endpoint.transfer_ring->size());
|
||||
|
||||
|
@ -276,62 +289,27 @@ namespace Kernel
|
|||
configure_endpoint.configure_endpoint_command.slot_id = m_slot_id;
|
||||
TRY(m_controller.send_command(configure_endpoint));
|
||||
|
||||
if (endpoint_type == XHCI::EndpointType::InterruptIn)
|
||||
{
|
||||
auto& trb = *reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr());
|
||||
memset(const_cast<XHCI::TRB*>(&trb), 0, sizeof(XHCI::TRB));
|
||||
trb.normal.trb_type = XHCI::TRBType::Normal;
|
||||
trb.normal.data_buffer_pointer = endpoint.data_region->paddr();
|
||||
trb.normal.trb_transfer_length = endpoint.data_region->size();
|
||||
trb.normal.td_size = 0;
|
||||
trb.normal.interrupt_target = 0;
|
||||
trb.normal.cycle_bit = 1;
|
||||
trb.normal.interrupt_on_completion = 1;
|
||||
trb.normal.interrupt_on_short_packet = 1;
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
|
||||
m_controller.doorbell_reg(m_slot_id) = endpoint_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwarnln("Configured unsupported endpoint {2H}",
|
||||
(endpoint_descriptor.bEndpointAddress & 0x80) | (endpoint_descriptor.bmAttributes & 0x03)
|
||||
);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void XHCIDevice::on_interrupt_endpoint_event(XHCI::TRB trb)
|
||||
void XHCIDevice::on_interrupt_or_bulk_endpoint_event(XHCI::TRB trb)
|
||||
{
|
||||
ASSERT(trb.trb_type == XHCI::TRBType::TransferEvent);
|
||||
if (trb.transfer_event.completion_code != 1 && trb.transfer_event.completion_code != 13)
|
||||
{
|
||||
dwarnln("Interrupt endpoint got transfer event with completion code {}", +trb.transfer_event.completion_code);
|
||||
dwarnln("Interrupt or bulk endpoint got transfer event with completion code {}", +trb.transfer_event.completion_code);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t endpoint_id = trb.transfer_event.endpoint_id;
|
||||
auto& endpoint = m_endpoints[endpoint_id - 1];
|
||||
ASSERT(endpoint.transfer_ring && endpoint.data_region);
|
||||
|
||||
const uint32_t transfer_length = endpoint.max_packet_size - trb.transfer_event.trb_transfer_length;
|
||||
auto received_data = BAN::ConstByteSpan(reinterpret_cast<uint8_t*>(endpoint.data_region->vaddr()), transfer_length);
|
||||
handle_input_data(received_data, endpoint_id);
|
||||
const auto* transfer_trb_arr = reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr());
|
||||
const uint32_t transfer_trb_index = (trb.transfer_event.trb_pointer - endpoint.transfer_ring->paddr()) / sizeof(XHCI::TRB);
|
||||
const uint32_t original_len = transfer_trb_arr[transfer_trb_index].normal.trb_transfer_length;
|
||||
|
||||
auto& new_trb = *reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr() + endpoint.enqueue_index * sizeof(XHCI::TRB));
|
||||
memset(const_cast<XHCI::TRB*>(&new_trb), 0, sizeof(XHCI::TRB));
|
||||
new_trb.normal.trb_type = XHCI::TRBType::Normal;
|
||||
new_trb.normal.data_buffer_pointer = endpoint.data_region->paddr();
|
||||
new_trb.normal.trb_transfer_length = endpoint.max_packet_size;
|
||||
new_trb.normal.td_size = 0;
|
||||
new_trb.normal.interrupt_target = 0;
|
||||
new_trb.normal.cycle_bit = endpoint.cycle_bit;
|
||||
new_trb.normal.interrupt_on_completion = 1;
|
||||
new_trb.normal.interrupt_on_short_packet = 1;
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
|
||||
m_controller.doorbell_reg(m_slot_id) = endpoint_id;
|
||||
const uint32_t transfer_length = original_len - trb.transfer_event.trb_transfer_length;
|
||||
handle_input_data(transfer_length, endpoint_id);
|
||||
}
|
||||
|
||||
void XHCIDevice::on_transfer_event(const volatile XHCI::TRB& trb)
|
||||
|
@ -495,6 +473,28 @@ namespace Kernel
|
|||
return endpoint.transfer_count;
|
||||
}
|
||||
|
||||
void XHCIDevice::send_data_buffer(uint8_t endpoint_id, paddr_t buffer, size_t buffer_len)
|
||||
{
|
||||
ASSERT(endpoint_id != 0);
|
||||
auto& endpoint = m_endpoints[endpoint_id - 1];
|
||||
|
||||
ASSERT(buffer_len <= endpoint.max_packet_size);
|
||||
|
||||
auto& trb = *reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr() + endpoint.enqueue_index * sizeof(XHCI::TRB));
|
||||
memset(const_cast<XHCI::TRB*>(&trb), 0, sizeof(XHCI::TRB));
|
||||
trb.normal.trb_type = XHCI::TRBType::Normal;
|
||||
trb.normal.data_buffer_pointer = buffer;
|
||||
trb.normal.trb_transfer_length = buffer_len;
|
||||
trb.normal.td_size = 0;
|
||||
trb.normal.interrupt_target = 0;
|
||||
trb.normal.cycle_bit = endpoint.cycle_bit;
|
||||
trb.normal.interrupt_on_completion = 1;
|
||||
trb.normal.interrupt_on_short_packet = 1;
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
|
||||
m_controller.doorbell_reg(m_slot_id) = endpoint_id;
|
||||
}
|
||||
|
||||
void XHCIDevice::advance_endpoint_enqueue(Endpoint& endpoint, bool chain)
|
||||
{
|
||||
endpoint.enqueue_index++;
|
||||
|
|
|
@ -254,8 +254,6 @@ static void init2(void*)
|
|||
|
||||
TTY::initialize_devices();
|
||||
|
||||
MUST(Process::create_userspace({ 0, 0, 0, 0 }, "/usr/bin/init"_sv, {}));
|
||||
|
||||
auto console_path = MUST(BAN::String::formatted("/dev/{}", cmdline.console));
|
||||
auto console_path_sv = console_path.sv();
|
||||
MUST(Process::create_userspace({ 0, 0, 0, 0 }, "/usr/bin/init"_sv, BAN::Span<BAN::StringView>(&console_path_sv, 1)));
|
||||
|
|
|
@ -33,3 +33,5 @@ if [[ -z $BANAN_BOOTLOADER ]]; then
|
|||
fi
|
||||
|
||||
export BANAN_CMAKE=$BANAN_TOOLCHAIN_PREFIX/bin/cmake
|
||||
|
||||
export BANAN_ROOT_PART_UUID='9C87D2AF-566A-4517-971A-57BA86EEA88D'
|
||||
|
|
|
@ -46,6 +46,15 @@ else
|
|||
mkpart root ext2 2M 100%
|
||||
fi
|
||||
|
||||
fdisk "$BANAN_DISK_IMAGE_PATH" >/dev/null << EOF
|
||||
x
|
||||
u
|
||||
2
|
||||
$BANAN_ROOT_PART_UUID
|
||||
r
|
||||
w
|
||||
EOF
|
||||
|
||||
# create loop device
|
||||
LOOP_DEV=$(sudo losetup --show -fP "$BANAN_DISK_IMAGE_PATH" || exit 1 )
|
||||
PARTITION1=${LOOP_DEV}p1
|
||||
|
|
|
@ -44,7 +44,7 @@ install_grub_legacy() {
|
|||
--boot-directory="$MOUNT_DIR/boot" \
|
||||
$LOOP_DEV
|
||||
sudo mkdir -p "$MOUNT_DIR/boot/grub"
|
||||
sudo cp "$BANAN_TOOLCHAIN_DIR/grub-legacy-boot.cfg" "$MOUNT_DIR/boot/grub/grub.cfg"
|
||||
sed "s/<ROOT>/UUID=$BANAN_ROOT_PART_UUID/" "$BANAN_TOOLCHAIN_DIR/grub-legacy-boot.cfg" | sudo tee "$MOUNT_DIR/boot/grub/grub.cfg" >/dev/null
|
||||
sudo umount "$MOUNT_DIR"
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ install_grub_uefi() {
|
|||
|
||||
sudo mount $PARTITION2 "$MOUNT_DIR"
|
||||
sudo mkdir -p "$MOUNT_DIR/boot/grub"
|
||||
sudo cp "$BANAN_TOOLCHAIN_DIR/grub-uefi.cfg" "$MOUNT_DIR/boot/grub/grub.cfg"
|
||||
sed "s/<ROOT>/UUID=$BANAN_ROOT_PART_UUID/" "$BANAN_TOOLCHAIN_DIR/grub-uefi.cfg" | sudo tee "$MOUNT_DIR/boot/grub/grub.cfg" >/dev/null
|
||||
sudo umount "$MOUNT_DIR"
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,10 @@ fi
|
|||
|
||||
if [[ $BANAN_DISK_TYPE == "NVME" ]]; then
|
||||
DISK_ARGS="-device nvme,serial=deadbeef,drive=disk"
|
||||
elif [[ $BANAN_DISK_TYPE == "IDE" ]]; then
|
||||
DISK_ARGS="-device piix3-ide,id=ide -device ide-hd,drive=disk,bus=ide.0"
|
||||
elif [[ $BANAN_DISK_TYPE == "USB" ]]; then
|
||||
DISK_ARGS="-device usb-storage,drive=disk"
|
||||
else
|
||||
DISK_ARGS="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0"
|
||||
fi
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
menuentry "banan-os" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT>
|
||||
}
|
||||
|
||||
menuentry "banan-os (no serial)" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2 noserial
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT> noserial
|
||||
}
|
||||
|
||||
menuentry "banan-os (only serial)" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2 console=ttyS0
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT> console=ttyS0
|
||||
}
|
||||
|
||||
menuentry "banan-os (no apic)" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2 noapic
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT> noapic
|
||||
}
|
||||
|
||||
menuentry "banan-os (no apic, no serial)" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2 noapic noserial
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT> noapic noserial
|
||||
}
|
||||
|
||||
menuentry "banan-os (no apic, only serial)" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2 noapic console=ttyS0
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT> noapic console=ttyS0
|
||||
}
|
||||
|
|
|
@ -4,25 +4,25 @@ set root=(hd0,gpt2)
|
|||
insmod all_video
|
||||
|
||||
menuentry "banan-os" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT>
|
||||
}
|
||||
|
||||
menuentry "banan-os (no serial)" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2 noserial
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT> noserial
|
||||
}
|
||||
|
||||
menuentry "banan-os (only serial)" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2 console=ttyS0
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT> console=ttyS0
|
||||
}
|
||||
|
||||
menuentry "banan-os (no apic)" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2 noapic
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT> noapic
|
||||
}
|
||||
|
||||
menuentry "banan-os (no apic, no serial)" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2 noapic noserial
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT> noapic noserial
|
||||
}
|
||||
|
||||
menuentry "banan-os (no apic, only serial)" {
|
||||
multiboot2 /boot/banan-os.kernel root=/dev/sda2 noapic console=ttyS0
|
||||
multiboot2 /boot/banan-os.kernel root=<ROOT> noapic console=ttyS0
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue